查看: 11400 | 回复: 0

阿哲
发表于: 2016-1-8 10:20:01 | 显示全部楼层

测试平台:SK-1788开发板

CPU:Cortex-M3 LPC1788FBD208

SDRAM:H57V2562GTR-75C (两片)


H57V2562GTR-75C简介:

16位数据总线(D0-D15),13位地址总线(A0-A12),两片,构成32位数据总线,26位地址总线,每片4个bank,由BA0,BA1两个管脚选择寻址,CS为片选管脚。

存储器类型:动态

HY57V2562GTR大小:  

2片32M组成的64MB的内存, 32*2=64MB

每一块SDRAM:8192行(2^13)*512列(2^9)*4个bank  = 16M * 16位宽 = 256Mb = 32MB


连接方式

lpc1788 的封装是208PIN(所以ECC配置应该使用208的配置)

HY57V2562GTR挂到LPC1788的EMC_DYCS0,起始地址为0xA0000000,片选管脚EMC_CS0 地址范围:0xA0000000-0xA3FFFFFF

连接图如下:



2.jpg


实现原理:

研究过SDRAM的人都知道,我们的sdram是动态的,它要保存数据,依靠的是里面一个一个的微小电容排成的电容阵列(姑且这么认为),数据在每次的读写都会让电容的电荷流失,即使不对数据操作,间隔一定的时间,电容上的电荷也会流失。因此,sdram需要不断的刷新数据,为电容充电,以保证上面的数据不丢失。

不少sdram芯片datasheet上都会标出 3.jpg 等字样,(有的是4096)这就是说:每隔64ms刷新8192行。Sdram刷新是以“行”进行的,我们的sdram是8192行,因此刷新一次需要64ms。如果刷新的这个值比64ms大,那么我们的数据将得不到保存。64ms也是晶体存储容量的极限。因此在配置寄存器的时候,需要注意。


时钟配置:

为了使整个系统运行流畅,有必要说一下该开发板对于时钟的配置。

■  CPU上电复位,晶振稳定工作之后,通过配置CLKSRCSEL[0]=1,选择芯片的震荡源为外部12MHz晶振。

■  通过配置PLL0寄存器,将时钟倍频到108MHz输出,此时pll_clk为108MHz,sysclk为12MHz。(PLL1模块是单独一个给USB提供时钟的模块,占时不用管。)

■  通过配置CCLKSEL[8]=1,选择输入为108MHz,将108MHz送到CPU模块,EMC(SDRAM)模块和外设模块。

■  通过配置CPU Clock Divider 来选择时钟分频(不分频,108MHz),即cclk=108MHz,

■  通过配置EMC Clock Divider 来选择时钟(和CPU一致,108MHz),即emc_clk=108MHz,

■  通过配置Peripheral Clock Divider 来配置外设时钟分频,暂时不涉及到。

该系统的时钟部分工作原理如下图

56.jpg

下图是sdram模块的原理框图。

15.jpg

这里有两个比较重要的地方:

CMDDLY(EMCDLYCTL[4:0]),命令延迟模式下EMC 输出的可编程延迟值

FBCLKDLY(EMCDLYCTL[12:8]),控制输入数据采样的反馈时钟的可编程延迟值

这两个延时是非常重要的,1788芯片的SDRAM有一个很重要的寄存器, EMCDLYCTL 寄存器延时的设置,就算你和官方所使用芯片一样,只要电路板有差异,这个寄存器的设置将有可能导致SDRAM在使用过程中出现错误。

我在配置这两个寄存器的时候,花费了很长的时间才把延时配好。具体怎么配,下面再说。


初始化EMC模块和sdram

初始化SDRAM之后,就需要我们编写测试程序对sdram进行测试,具体实现为:按照8位,16位,32位数据写入,然后延时一段时间(也可以不作延时),将数据读出,看看与写入的值是否一致,如果一致,则sdram的驱动没有问题,可以使用。

下面是测试程序(返回值:0 测试通过;返回值1:测试失败)

  1. #define SDRAM_BASE                 0xA0000000

  2. #define EMC_BASE_ADDR           0x2009C000

  3. #define EMC_RS_BASE_ADDR     0x400FC1DC

  4. #define SDRAM_SIZE                   0x4000000       // 512Mbit=64MB



  5. #define EMCControl_ENABLE                (1<<0)

  6. #define EMCConfig_SMALL                    (0<<0)

  7. #define EMCConfig_PERC_OF_CCLK     (0<<8)

  8. #define EMCDLYCTL_CMDDLY               (0x0D<<0)

  9. #define EMCDLYCTL_FBCLKDLY             (0x10<<8)

  10. #define EMCDLYCTL_CLKOUT0DLY           (0<<16)

  11. #define EMCDLYCTL_CLKOUT1DLY           (0<<24)

  12. #define EMC_DRC_CLKDELAY               (1<<0)

  13. #define EMC_Ras_late_3                        (3<<0)   

  14. #define EMC_Cas_late_3                        (3<<8)



  15. #define EMCDCF0_BUF_EN                    (1<<19)

  16. #define EMCDCF0_AM                            (1<<7)|(3<<9)|(1<<14)

  17. #define EMCDC_DIS_CE                      (0<<0)

  18. #define EMCDC_CE                              (1<<0)

  19. #define EMCDC_DIS_CS                      (0<<1)

  20. #define EMCDC_CS                              (1<<1)

  21. #define EMCDC_SDRAM_INIT_NOP           (3<<7)

  22. #define EMCDC_SDRAM_INIT_PALL          (2<<7)

  23. #define EMCDC_SDRAM_INIT_MODE          (1<<7)

  24. #define EMCDC_SDRAM_INIT_NORMAL    (0<<7)



  25. void EMC_Init(void)

  26. {

  27. //  float SDRAM_PERIOD;

  28.     int    i;

  29. //  volatile unsigned long Dummy;

  30.     volatile unsigned int mhz = 0;

  31.     volatile unsigned int nsPerClk = 0;

  32.     unsigned int wtemp = wtemp;

  33.   unsigned int dwtemp = dwtemp;

  34. //  unsigned int j = 0;

  35.     lpc178x_emc_typedef         *emc       = get_emc_addr(EMC_BASE_ADDR);

  36.     lpc178x_emc_rs_typedef  *emc_rs = get_emc_rs_addr(EMC_RS_BASE_ADDR);

  37. /*PCONP 开启EMC*/

  38.     lpc178x_periph_enable(LPC178X_SCC_PCONP_PCEMC_MSK,ENABLE);

  39.    

  40.     /*配置延时寄存器,这个延时寄存器配置比较麻烦,后面还要讲到,先设置一个例程里面的初始值,CMD-DELAY:4.25ns,FBCLK-DELAY:4.25ns*/

  41.     emc_rs->EMCDLYCTL   = EMCDLYCTL_CMDDLY | EMCDLYCTL_FBCLKDLY | EMCDLYCTL_CLKOUT0DLY | EMCDLYCTL_CLKOUT1DLY;


  42.     emc->EMCControl      = EMCControl_ENABLE;  /*ECC 使能*/


  43.     /*配置sdram为小端模式,时钟和CPU一致,CCLK:CLKOUT = 1:1*/

  44.     emc->EMCConfig       = EMCConfig_SMALL | EMCConfig_PERC_OF_CCLK;
复制代码

后记:在调试sdram的过程中,遇到了很多问题,有些问题是网上出现的,但是没有具体的答案,自己摸索出来的,有些问题是网上没有遇到过的。

问题1:

开始调试1788,芯片硬件仿真时,调试SDRAM寄存器配置错误,导致1788芯片无法进入仿真状态,只能用Flash Magic才能擦除。

599.jpg

问题2:

设定sdram参数的时候,由于没有深入的研究datasheet,导致后来测试的时候,跑一万年,调死了也不正确,后来才发现原来芯片datasheet上的参数跟例程上面的不一样,改了之后,就可以了。(果然datasheet是王道啊,信datasheet,得永生)

  1. #define EMC_NS2CLK(ns, nsPerClk) ((ns +nsPerClk - 1) / nsPerClk)

  2. #define SDRAM_REFRESH         7513

  3. #define SDRAM_TRP                 20

  4. #define SDRAM_TRAS               42

  5. #define SDRAM_TRRD              15

  6. #define SDRAM_TMRD              2

  7. #define SDRAM_TDAL               22

  8. #define SDRAM_TRC                 63

  9. #define SDRAM_SREX              1

  10. #define SDRAM_TAPR            1

  11. #define SDRAM_TXSR            0xF

  12. #define SDRAM_TWR             1

  13. #define SDRAM_TRFC             70
复制代码

问题3:

配置芯片的管脚出错:

sdram用到的芯片管脚为:

  1. P2_16:列地址选通

  2. P2_17:行地址选通

  3. P2_18 :EMC_CLK0,sdram的时钟管脚0

  4. P2_20:EMC_DYCS0选通线(片选)

  5. P2_24: EMC_CKE0—SDRAM时钟使能0

  6. P2_28 - P2_31:数据屏蔽线0,1,2,3

  7. P3_0 - P3_31: 数据线D0-D31

  8. P4_0 - P4_12: 地址线A0-A12

  9. P4_13 – P4_14:BA0,BA1,bank选择管脚

  10. P4_25: EMC_WE,写使能,低电平有效写入使能信号
复制代码

管脚配置值为0x21


问题4:

sdram的延时设置错误(即EMCDLYCTL),具体表现为:写入的数据和读出的数据有一位乃至更多位的数据出错,我的方法是通过官方的测试程序,将EMCDLYCTL的值调试到最优情况,可以使数据不出错。

下图是EMC的延时模块,从图中我们可以看出,EMC的延时可以以一个很小的尺度(0.25ns)增大和减小,范围在0.25-8ns之间变化,通过配置EMCDLYCTL的值可以配置延时。

下表有关延时详细的配置信息:

例如:如果我需要配置CMDDLY为5ns,根据下面的公式

Delay_time = (CMDDLY+1) * 250ps

即:5ns=(CMDDLY+1)*250ps =>  CMDDLY = 19=0x13

根据上面的原理,我们可以设计程序,来找到最佳的延时时间。不过网上有官方的调试的程序,我们可以参照着来调试。

具体做法是:

设置一个延时最小值,看看测试的时候是否出错;

逐渐增大延时,当测试成功时,记录这个值,然后继续增大,

当再次增大,测试数据出现错误的时候,记下最大值,然后通过最大值和最小值计算平均,得出比较稳定的延时。

  1. static void FindDelay(int DelayType) {

  2.    uint32_t Delay;

  3.    uint32_t Min;

  4.    uint32_t Max;

  5.    uint32_t v;

  6.    Delay = 0x00;

  7.    Min   = 0xFF;

  8.    Max   = 0xFF;

  9.    

  10.    // Test for DLY min./max. values

  11.    

  12.    while (Delay < 32) {

  13.      
  14.      // Setup new DLY value to test   

  15.      if (DelayType == 0) {

  16.                          v = LPC_SC->EMCDLYCTL & ~0x001Ful;

  17.        LPC_SC->EMCDLYCTL = v | Delay;

  18.      } else {

  19.                          v = LPC_SC->EMCDLYCTL & ~0x1F00ul;

  20.        LPC_SC->EMCDLYCTL = v | (Delay << 8);

  21.      }

  22.      //

  23.      // Test configured DLY value and find out min./max. values that will work

  24.      //

  25.      if (sdram_test() == 0) {

  26.        //

  27.        // Test passed, remember min. DLY value if not done yet

  28.        //

  29.        if (Min == 0xFF) {

  30.          Min = Delay;

  31.        }

  32.      } else {

  33.        //

  34. // Test failed, if a min. value has been found before, remember the current value for max.

  35.        //

  36.        if (Min != 0xFF) {

  37.          Max = Delay;

  38.        }

  39.      }

  40.      Delay++;

  41.    }

  42.    //

  43.    // Calc DLY value

  44.    //

  45.    if        (Max != 0xFF) {  // If we found a min. and max. value we use the average of the min. and max. values to get an optimal DQSIN delay

  46.      Delay = (Min + Max) / 2;

  47.    } else if (Min != 0xFF) {  // If we found only a min. value we use the average of the min. value and the longest DLY value to get an optimal DQSIN delay

  48.      Delay = (Min + 0x1F) / 2;

  49.    } else {                   // No working max. and/or min. values found

  50.      while (1);  // Fatal error

  51.    }

  52.    //

  53.    // Setup DLY value to work with

  54.    //

  55.   if (DelayType == 0) {

  56.      v                 = LPC_SC->EMCDLYCTL & ~0x001Ful;

  57.      LPC_SC->EMCDLYCTL = v | Delay;

  58.    } else {

  59.      v                 = LPC_SC->EMCDLYCTL & ~0x1F00ul;

  60.      LPC_SC->EMCDLYCTL = v | (Delay << 8);

  61.    }

  62. }





  63. static uint32_t CalibrateOsc(void) {

  64.    uint32_t Cnt;

  65.    uint32_t v;

  66.    uint32_t i;



  67.    //

  68.    // Init start values

  69.    //

  70.    Cnt = 0;

  71.    //

  72.    // Calibrate osc.

  73.    //

  74.    for (i = 0; i < 10; i++) {

  75.      LPC_SC->EMCCAL = (1 << 14);     // Start calibration

  76.      v = LPC_SC->EMCCAL;

  77.      while ((v & (1 << 15)) == 0) {  // Wait for calibration done

  78.        v = LPC_SC->EMCCAL;

  79.      }

  80.      Cnt += (v & 0xFF);

  81.    }

  82.    return (Cnt / 10);

  83. }



  84. static void AdjustEMCTiming(uint32_t Delay) {

  85.    uint32_t v;

  86.    uint32_t CmdDly;

  87.    uint32_t FBDelay;

  88.    uint32_t FBClkDly;



  89.    FBDelay = CalibrateOsc();



  90.    v = LPC_SC->EMCDLYCTL;

  91.    CmdDly            = ((v &  0x001Ful) * Delay / FBDelay) & 0x1F;

  92.    FBClkDly          = ((v &  0x1F00ul) * Delay / FBDelay) & 0x1F00;

  93.    LPC_SC->EMCDLYCTL =  (v & ~0x1F1Ful) | FBClkDly | CmdDly;

  94. }
复制代码


跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 53 | 回复: 76



手机版|

GMT+8, 2024-5-2 12:58 , Processed in 0.049572 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表