本帖最后由 netlhx 于 2015-12-20 13:23 编辑
感谢21IC举办的这次试用活动,有幸入围,评估板前几天已收到,今天正式开始学习及使用高大上的开发板。
各位热情的网友已陆续有开箱及DEMO测试**,本文就不再重复开箱照片,只上图片一张,以飨各位!
SDRAM概述
为了更好的体验显示效果,板载一块16MB的美光内存,型号为MT48LC4M32B2B5-6A,构成4 Meg x 32 (1 Meg x 32 x 4 banks),12位行地址,8位列地址。主要特性参考数据手册如下:
关于SDRAM的使用,详细情况请参考对应的手册,这里只介绍几个重要的参数,包括TRCD, TRP, CL。
- RCD 在发送列读写命令时必须要与行有效命令有一个间隔,这个间隔被定义为tRCD,即RAS to CASDelay(RAS至CAS延迟),大家也可以理解为行选通周期,这应该是根据芯片存储阵列电子元件响应时间(从一种状态到另一种状态变化的过程)所制定的延迟。tRCD是SDRAM的一个重要时序参数,可以通过主板BIOS经过北桥芯片进行调整,但不能超过厂商的预定范围。广义的tRCD以时钟周期(tCK,Clock Time)数为单位,比如tRCD=2,就代表延迟周期为两个时钟周期,具体到确切的时间,则要根据时钟频率而定,对于PC100SDRAM,tRCD=2,代表20ns的延迟,对于PC133则为15ns
- RP 在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开新的工作行,这个间隔被称为tRP(Precharge command Period,预充电有效周期)。和tRCD、CL一样,tRP的单位也是时钟周期数,具体值视时钟频率而定。
- CL 在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据I/O通道(DQ)输出到内存总线上了。但是在CAS发出之后,仍要经过一定的时间才能有数据输出,从CAS与读取命令发出到第一笔数据输出的这段时间,被定义为CL(CAS Latency,CAS潜伏期)。由于CL只在读取时出现,所以CL又被称为读取潜伏期(RL,Read Latency)。CL的单位与tRCD一样,为时钟周期数,具体耗时由时钟频率决定。
这些参数都可以参考上面的表1,需要注意的是这个值一般是指时钟周期,具体到不同的SDRAM时钟上的话,需要进行换算。数据手册上给出了具体的时间数,其值对应为18-18-18NS,单位是纳秒。
具体来说,在F469-DISCO开发板上,SDRAM的时钟周期最大可以达到HCLK/2,如果配置HCLK为180MHZ的话,则2分频后,SDRAM的时钟频率为90MHZ,一个周期长度约为11.1纳秒,所以在初始化的时候应该分别指定这些值为2, 2, 2。
GPIO配置
参考官方原理图,GPIO用到的主要包括地址线,这些是复用的,还是数据线,BANK选择等。见下图
只不过这些引脚有些多,可以参考官方例程查看具体用到的引脚。
FMC控制器配置
F469MCU内置FMC控制器,可以访问SRAM, SDRAM, NORFLASH等常见存储器件,这里主要指定一些基本的参数,先看代码,再做一个简单的说明。
/* SDRAM device configuration */
hsdram.Instance = FMC_SDRAM_DEVICE;
SDRAM_Timing.LoadToActiveDelay = 2;
SDRAM_Timing.ExitSelfRefreshDelay = 6;
SDRAM_Timing.SelfRefreshTime = 4;
SDRAM_Timing.RowCycleDelay = 6;
SDRAM_Timing.WriteRecoveryTime = 2;
SDRAM_Timing.RPDelay = 2;
SDRAM_Timing.RCDDelay = 2;
hsdram.Init.SDBank = FMC_SDRAM_BANK1;
hsdram.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
hsdram.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
hsdram.Init.MemoryDataWidth = SDRAM_MEMORY_WIDTH;
hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram.Init.SDClockPeriod = SDCLOCK_PERIOD;
hsdram.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* Initialize the SDRAM controller */
HAL_SDRAM_Init(&hsdram, &SDRAM_Timing);
注意看TRP, TRCD及CL的配置,这里指定的都是2,因为SDRAM工作在90MHZ频率之下,2个时钟周期大约为20NS,刚好满足要求,不能再小了。
后面的一些参数,如行列地址线的数目,BANK的个数,基本都可以从SDRAM的数据手册上获取。
SDRAM的初始化序列
按官方手册上的介绍,在使用SDRAM之前,先要执行一系列的初始化命令,然后才能正常访问SDRAM。基本步骤如下:
- 同时给VDD及VDDQ上电
- 提供稳定的时钟信号
- 在发送命令前至少等待100US,在此期间拉高CKE,直到结束
- 发送PRECHARGE ALL命令
- 等待至少TRP指定的时间,直到预充电完成
- 发磅AUTO REFRESH命令,等待TRFC指定的时间
- 现在SDRAM可以进入模式选择阶段,这里如果不指定的话,SDRAM会进入默认的工作模式,也许不是用户需要的状态。
- 等待TMRD指定的时间后,SDRAM就可以正常访问了。
结合下面的代码可以看看
__IO uint32_t tmpmrd =0;
/* Step 3: Configure a clock configuration enable command */
Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 4: Insert 100 us minimum delay */
/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
HAL_Delay(1);
/* Step 5: Configure a PALL (precharge all) command */
Command->CommandMode = FMC_SDRAM_CMD_PALL;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 6 : Configure a Auto-Refresh command */
Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 8;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 7: Program the external memory mode register */
tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = tmpmrd;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 8: Set the refresh rate counter */
/* (15.62 us x Freq) - 20 */
/* Set the device refresh counter */
hsdram->Instance->SDRTR |= ((uint32_t)((1292)<< 1));
至此,SDRAM的初始化就全部完成了,现在可以正常访问SDRAM了。
SDRAM的映射地址
SDRAM在MCU的地址空间中的映射地址为0xC0000000到0xC0FFFFFF,可随机线性访问。
代码测试
这里设计了一段简单的代码来测试所有的内存是否正常。首先将数据写入到SDRAM中,然后读出来,进行比较后判断是否正常。
SDRAM_Init();
HAL_UART_Transmit(&huart3, (uint8_t *)msg1, strlen(msg1), 100);
for(uwIndex = 0; uwIndex < uwCount; uwIndex += 4)
{
*(__IO uint32_t*) (SDRAM_BANK_ADDR + 4*uwIndex) = 0x01234567;
if(*(__IO uint32_t*) (SDRAM_BANK_ADDR + 4*uwIndex) == 0x01234567)
{
percent2 = uwIndex * 100 / uwCount;
if(percent1 != percent2)
{
sprintf(msg, "PASSED: %3d%%\r\n", percent2);
HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), 100);
}
percent1 = percent2;
}
else
while(1);
}
下面是测试结果
结束!
附件:
SDRAM数据手册:
MT48LC4M32B2B5-6A.pdf
(3.52 MB)
工程文件:
sdram.zip
(3.71 MB)
|