| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-07-12 | gingko | 初次建立 | ===== 实验三十七:SDRAM实验——读写SDRAM ===== ==== 一、 实验目的与意义 ==== - 了解STM32 SDRAM结构。 - 了解STM32 SDRAM特征。 - 掌握SDRAM的使用方法。 - 掌握STM32 HAL库中SDRAM属性的配置方法。 - 掌握KEILMDK 集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.15.5923532fsFrHiE&id=551864196684|点击购买]]。 - JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、 实验原理 ==== === 1、SDRAM简介 === * SDRAM,英文名是:Synchronous Dynamic Random Access Memory,即同步动态随机存储器,相较于SRAM(静态存储器),SDRAM具有:容量大和价格便宜的特点。STM32F767支持SDRAM,因此,我们可以外挂SDRAM,从而大大降低外扩内存的成本。 * 同步动态随机存取内存(SDRAM)是有一个同步接口的动态随机存取内存(DRAM)。通常DRAM是有一个异步接口的,这样它可以随时响应控制输入的变化。而SDRAM有一个同步接口,在响应控制输入前会等待一个时钟信号,这样就能和计算机的系统总线同步。时钟被用来驱动一个有限状态机,对进入的指令进行管线(Pipeline)操作。这使得SDRAM与没有同步接口的异DRAM(asynchronous DRAM)相比,可以有一个更复杂的操作模式。管线意味着芯片可以在处理完之前的指令前,接受一个新的指令。在一个写入的管线中,写入命令在另一个指令执行完之后可以立刻执行,而不需要等待数据写入存储队列的时间。在一个读取的流水线中,需要的数据在读取指令发出之后固定数量的时钟频率后到达,而这个等待的过程可以发出其它附加指令。 * SDRAM是多Bank结构,例如在一个具有两个Bank的SDRAM的模组中,其中一个Bank在进行预充电期间,另一个Bank却马上可以被读取,这样当进行一次读取后,又马上去读取已经预充电Bank的数据时,就无需等待而是可以直接读取了,这也就大大提高了存储器的访问速度。为了实现这个功能,SDRAM需要增加对多个Bank的管理,实现控制其中的Bank进行预充电。在一个具有2个以上Bank的SDRAM中,一般会多一根叫做BAn的引脚,用来实现在多个Bank之间的选择。 * SDRAM具有多种工作模式,内部操作是一个复杂的状态机。SDRAM器件的引脚分为以下几类。 * (1)控制信号:包括片选、时钟、时钟使能、行列地址选择、读写有效及数据有效。 * (2)地址信号:时分复用引脚,根据行列地址选择引脚,控制输入的地址为行地址或列地址。 * (3)数据信号:双向引脚,受数据有效控制。 * SDRAM的所有操作都同步于时钟。根据时钟上升沿控制管脚和地址输入的状态,可以产生多种输入命令: * 模式寄存器设置命令 * 激活命令 * 预充命令 * 读命令 * 写命令 * 带预充的读命令 * 带预充的写命令 * 自动刷新命令 * 自我刷新命令 * 突发停命令 * 空操作命令 * 根据输入命令,SDRAM状态在内部状态间转移。内部状态包括模式寄存器设置状态、激活状态、预充状态、写状态、读状态、预充读状态、预充写状态、自动刷新状态及自我刷新状态。 * SDRAM支持的操作命令有初始化配置、预充电、行激活、读操作、写操作、自动刷新、自刷新等。所有的操作命令通过控制线CS#、RAS#、CAS#、WE#和地址线、体选地址BA输入。 === 2、SDRAM信号线 === * SDRAM的信号线如图所示: {{ :icore4:icore4_arm_hal_37_1.png?direct&750 |}} === 3、SDRAM存储单元 === * SDRAM的存储单元(称之为:BANK)是以阵列的形式排列的,每个存储单元的结构示意图,如图所示: {{ :icore4:icore4_arm_hal_37_2.png?direct&600 |}} * 对于这个存储阵列,我们可以将其看成是一个表格,只需要给定行地址和列地址,就可以确定其唯一位置,这就是SDRAM寻址的基本原理。而一个SDRAM芯片内部,一般又有4个这样的存储单元(BANK),所以,在SDRAM内部寻址的时候,先指定BANK号和行地址,然后再指定列地址,就可以查找到目标地址。 * SDRAM的存储结构示意图,如图下所示,寻址的时候,首先RAS信号为低电平,选通行地址,地址线A0~A12所表示的地址,会被传输并锁存到行地址译码器里面,最为行地址,同时BANK地址线上面的BS0,BS1所表示的BANK地址,也会被锁存,选中对应的BANK,然后,CAS信号为低电平,选通列地址,地址线A0~A12所表示的地址,会被传输并锁存到列地址译码器里面,作为列地址,这样,就完成了一次寻址。 {{ :icore4:icore4_arm_hal_37_3.png?direct |}} === 4、数据传输 === * 在完成寻址以后,数据线DQ0~DQ15上面的数据会通过数据控制逻辑写入(或读出)存储阵列。特别注意:因为SDRAM的位宽,可以达到32位,也就是最多有32条数据线,在实际使用的时候,我们可能会以:8位、16位、24位和32位等宽度来读写数据,这样的话,并不是每条数据线,都会被使用到,未被用到的数据线上面的数据,必须被忽略,这个时候就需要用到数据掩码(DQM)线来控制了,每一个数据掩码线,对应8个位的数据,低电平表示对应数据位有效,高电平表示对应数据位无效。 * SDRAM的驱动需要用到一些命令,常用的命令如图所示: {{ :icore4:icore4_arm_hal_37_4.png?direct |}} === 5、原理图 === {{ :icore4:icore4_arm_hal_37_5.png?direct |}} ==== 四、 实验程序 ==== === 1、主函数 === int main(void) { int i,j; /* MCU 配置*/ /* 重置所有外设, 初始化Flash 接口和Systick. */ HAL_Init(); /* 系统时钟配置 */ SystemClock_Config(); /* 初始化所有已配置的外设 */ MX_GPIO_Init(); //SDRAM初始化 BSP_SDRAM_Init(); LED_BLUE_ON; //向SDRAM中写入0x5555并读取校验 for(i = 0;i < SDRAM_SIZE;i++){ write_sdram(i,0x5555); } for(i = 0;i < SDRAM_SIZE;i++){ if(0x5555 != read_sdram(i)){ while(1){ LED_RED_ON; HAL_Delay(500); LED_RED_OFF; HAL_Delay(500); } } } //向SDRAM中写入0xAAAA并读取校验 for(i = 0;i < SDRAM_SIZE;i++){ write_sdram(i,0xAAAA); } for(i = 0;i < SDRAM_SIZE;i++){ if(0xAAAA != read_sdram(i)){ while(1){ LED_RED_ON; HAL_Delay(500); LED_RED_OFF; HAL_Delay(500); } } } //向SDRAM中写入0x0000~0xFFFF并读取校验 for(j = 0; j < 256; j++){ for(i = 0;i < 65536;i++){ write_sdram((65536 * j + i),i); } } for(j = 0; j < 256; j ++){ for(i = 0;i < 65536;i++){ if(i != read_sdram((65536 * j + i))){ while(1){ LED_RED_ON; HAL_Delay(500); LED_RED_OFF; HAL_Delay(500); } } } } //测试成功 LED_BLUE_OFF; LED_GREEN_ON; while (1) { } } === 2、SDRAM初始化 === uint8_t BSP_SDRAM_Init(void) { static uint8_t sdramstatus = SDRAM_ERROR; /* SDRAM设备配置 */ /* SDRAM时钟频率为100Mhz的定时配置(系统时钟最高为200Mhz) */ Timing.LoadToActiveDelay = 2; Timing.ExitSelfRefreshDelay = 7; Timing.SelfRefreshTime = 4; Timing.RowCycleDelay = 7; Timing.WriteRecoveryTime = 2; Timing.RPDelay = 2; Timing.RCDDelay = 2; sdramHandle.Init.SDBank = FMC_SDRAM_BANK1; sdramHandle.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; sdramHandle.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; sdramHandle.Init.MemoryDataWidth = SDRAM_MEMORY_WIDTH; sdramHandle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; sdramHandle.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; sdramHandle.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; sdramHandle.Init.SDClockPeriod = SDCLOCK_PERIOD; sdramHandle.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; sdramHandle.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* SDRAM控制器初始化 */ BSP_SDRAM_MspInit(&sdramHandle, NULL); /* __weak函数可以被应用程序重写*/ if(HAL_SDRAM_Init(&sdramHandle, &Timing) != HAL_OK) { sdramstatus = SDRAM_ERROR; } else { sdramstatus = SDRAM_OK; } /* SDRAM初始化顺序 */ BSP_SDRAM_Initialization_sequence(REFRESH_COUNT); return sdramstatus; } === 3、SDRAM读写函数 === #define SDRAM_SIZE SDRAM_DEVICE_SIZE #define write_sdram(offset,data) *(volatile unsigned short int *)(SDRAM_DEVICE_ADDR + (offset << 1)) = data #define read_sdram(offset) *(volatile unsigned short int *)(SDRAM_DEVICE_ADDR + (offset << 1)) ==== 五、 实验步骤 ==== - 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4通过Micro USB线与计算机相连,为iCore4供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4上; - 也可以进入Debug模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== * 上电即开始读写SDRAM测试,测试过程中,蓝色LED点亮,如果出现错误,红色LED闪烁,测试成功,绿色LED点亮。