- 简述:
- SDRAM全称(Synchronous Dynamic Random Access Memory),即同步动态随机存储器,与SRAM(静态存储器)区别:容量大、价格便宜;
- STM32控制器芯片内部有一定大小的SRAM和FLASH作为内存和程序存储空间,但当程序教大RAM/ROM不够用时,就需要外扩存储器(例如8031需要外扩ROM一样);
- STM32F767IGT6自带了512K字节的SRAM,扩展SDRAM主要用来跑算法、驱动led屏、GUI;
- F7有2个SDRAM存储驱动,可独立配置;
- SDNE0+SDCLKE0:存储区域1
- SDNE1+SDCLKE1:存储区域2
- 实验板采用的芯片为:W9825G6KH,32M; 4Bank; 行:A0-A12;列:A0-A8;16位数据传输;
- 芯片存储结构为:
- 行地址:8192个
- 列地址:512个
- bank数目:4个
- 位宽:16位
- 芯片容量:8192*512*16 = 64Mbit = 32MByte;
- FMC介绍:
- 简述:STM32F767使用FMC外设来管理扩展的存储器,FMC(Flexible Memory Controller),可变存储控制器,用于驱动SRAM、SDRAM、NOR FLASH、NAND FLASH类型的存储器(不支持DDR类型的SDRAM);FSMC,可变静态存储控制,不能用于驱动SDRAM这样的动态存储器,因为驱动SDRAM需要定时刷新;
- 存储区域:
- bank1 : 0xc000 0000 ------ 0xcfff ffff;
- bank2 : 0xd000 0000 ------ 0xdfff ffff;
- 初始化常用寄存器设置
- FMC_SDCRx: 设置SDRAM芯片功能
- FMC_SDCMR:设置时序;
- FMC_SDRTT:设置刷新频率;
- 框图
- W9825G6KH框图
- 电气连接
- FMC:A0-12 ------> SDRAM:A0 ------>A12;地址线:行/列
- FMC:BA0,BA1(或者叫A14,A15) ------> SDRAM:BA0,BA1; bank地址线
- FMC:D0-D15 ------>SDRAM:D0 ------>D15; 数据线
- FMC:SDCKE0 ------>SDRAM:CKE 时钟使能
- FMC:SDNE0 ------>SDRAM:CS(4、5决定用哪个bank)芯片使能
- FMC:SDCLK ------>SDRAM:CLK时钟线 同步时钟信号
- FMC:SDNWE ------>SDRAM:WE 写入使能
- FMC:SDNCAS ------>SDRAM:CAS 列选通
- FMC:SDNRAS ------>SDRAM:RAS 行选通
- FMC:NBL1 ------>SDRAM:UDQM
- FMC:NBL0 ------>SDRAM:LDQM 10、11 数据掩码,表示DQ的有效部分
- 存储单元
- SDRAM的存储单元(bank)是以阵列的形式排列的; 如下图所示:
- 存储框图
- 功能分析:
- SDRAM寻址原理:将存储阵列看成一个表格,通过行/列地址确定唯一位置(矩阵键盘);
- 常见的SDRAM都有4个bank,寻址时要先指定bank号、行地址、列地址,才能找到目标地址;
- 寻址过程:
- 首先RAS拉低,行地址有效,地址线A0~A12所表示的地址会被锁存在行地址译码器里面,bank地址线上的BS0,BS1表示选择的bank地址,对应相应的bank;
- CAS拉低,列地址有效,地址线A0~A12所表示的地址会被锁存在列地址译码器里面; 这样就完成一次寻址操作;
- 数据传输
- 是用8位、16位数据传输
- LDQM(DQM0):低电平时D[7:0]数据有效;
- UDQM(DQM1):低电平时D[15:0]数据有效;
- SDRAM常用命令
- 命令禁止:CS高电平,停止新命令,不能停止当前正在执行的命令;
- 空操作:用于选中SDRAM,防止SDRAM接受错误指令,以便接下来发送命令;
- 行有效:
- 列有效:
- 读:WE:高,
- 写:WE:低
- 预充电(PRECHARGE):用于关闭bank中所打开的行地址,由于SDRAM的寻址具有独占性,所以在进行完读写操作后,如果要对同一bank的另一行进行寻址,就要将原来有效(打开)的行关闭,重新发送行/列地址; bank关闭现有行,准备打开新行的操作就叫做预充电(precharge)
- 刷新(refresh),用于刷新一行数据; SDRAM里面存储的数据,需要不断的进行刷新操作才能保留住,刷新命令对于SDRAM尤为重要;预充电和刷新都可以实现对SDRAM数据的刷新,不过预充电仅对当前打开的行有效(仅刷新当前行); 刷新命令:可以依次对所有的行进行刷新操作
- 自动刷新:
- 自我刷新:休眠模式低功耗状态下的数据保存,CKE(低电平)进入自我刷新模式; CKE(高)退出自我刷新;
- 模式寄存器(Mode Register Set):每次SDRAM初始化都要用到;
- 突发传输技术:只要指定起始列地址和突发长度 ,内存就会依次地自动对后面相应数量的存储单元进行读/写操作,而不需要控制器连续地提供列地址 ;这样除了第一个数据的传输需要若干个周期外,其后面每个数据只需要一个周期即可获得;
- 突发长度(Burst Length):BL : 可选项:1、2、4、8 Full Page; 常见设定1;
- 突发访问地址模式(Addresssing Mode):可以设置为顺序(sequential)和交错(interleave,地址是乱序的);一般选择顺序
- 列地址选通延迟(CAS Latency)CL:一般设置2/3个时钟周期,注意列地址选通延迟(CL)仅在读命令有效,写命令不需要;
- 写模式(Wrete Mode):设置单次写的模式,可以选择突发写入或者单次写入;
- 软件流程
- SDRAM初始化
- 框图
- 上电,提供不少于100us的时钟
- 发送nop命令
- 发送预充电(precharge)命令,控制所有bank进行预充电,并等待Trp(依据芯片手册设置为:2,芯片是15ns,时钟周期:9.25ns)时间;
- 发送至少2个自动刷新(auto refresh)命令,每个命令后需等待Trc时间,通常设置为7;
- 发送加载模式寄存器(load mode register )命令,配置SDRAM的工作参数,并等待Tmrd时间,表示加载模式寄存器命令行后的延迟; 通常设置为:2;
- 初始化完成,下一步读写初始化; 上述参数可通过数据手册找到;
-
/********************************************************* * 函数名:BSP_SDRAM_Init() * 描述 : * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void BSP_SDRAM_Init(void) { /*##-1- Configure the SDRAM device #########################################*/ /* SDRAM device configuration */ FMC_SDRAM_TimingTypeDef FMC_SDRAM_TimingInitStructure; FMC_SDRAM_TimingInitStructure.LoadToActiveDelay = 2; //加载模式寄存器到激活时间的延迟为2个时钟周期 FMC_SDRAM_TimingInitStructure.ExitSelfRefreshDelay = 8; //退出自刷新延迟为8个时钟周期 FMC_SDRAM_TimingInitStructure.SelfRefreshTime = 6; //自刷新时间为6个时钟周期 FMC_SDRAM_TimingInitStructure.RowCycleDelay = 6; //行循环延迟为6个时钟周期 FMC_SDRAM_TimingInitStructure.WriteRecoveryTime = 2; //恢复延迟为2个时钟周期 FMC_SDRAM_TimingInitStructure.RPDelay = 2; //行预充电延迟为2个时钟周期 FMC_SDRAM_TimingInitStructure.RCDDelay = 2; //行到列延迟为2个时钟周期 SDRAM_Handle_InitStructure.Instance = FMC_SDRAM_DEVICE; //SDRAM在BANK5,6 SDRAM_Handle_InitStructure.Init.SDBank = FMC_SDRAM_BANK1; //SDRAM接在BANK5上 SDRAM_Handle_InitStructure.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; //列数量 SDRAM_Handle_InitStructure.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; //行数量 SDRAM_Handle_InitStructure.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; //数据宽度为16位 SDRAM_Handle_InitStructure.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; //一共4个BANK SDRAM_Handle_InitStructure.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; //CAS为3 SDRAM_Handle_InitStructure.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; //失能写保护 SDRAM_Handle_InitStructure.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2; //SDRAM时钟为HCLK/2=216M/2=108M=9.3ns SDRAM_Handle_InitStructure.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; //使能突发 SDRAM_Handle_InitStructure.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; //读通道延时 /* Initialize the SDRAM controller */ HAL_SDRAM_Init(&SDRAM_Handle_InitStructure, &FMC_SDRAM_TimingInitStructure); /* Program the SDRAM external device */ BSP_SDRAM_Initialization_Sequence(&SDRAM_Handle_InitStructure); //发送初始化序列命令,设置模式寄存器; } /********************************************************* * 函数名:BSP_SDRAM_Initialization_Sequence() * 描述 :发送初始化序列命令,设置模式寄存器 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void BSP_SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram) { u32 temp = 0; //初始化完成,还需设置操作序列; 模式寄存器; BSP_SDRAM_SendCmd(0,FMC_SDRAM_CMD_CLK_ENABLE,1,0);//bank1,时钟使能,刷新次数, delay_us(500); //不低于100us; BSP_SDRAM_SendCmd(0,FMC_SDRAM_CMD_PALL,1,0);//对所有存储区预充电 BSP_SDRAM_SendCmd(0,FMC_SDRAM_CMD_AUTOREFRESH_MODE,8,0);//设置自刷新次数; /*配置:1:访问长度; [2:0] 2:访问类型 [3] 3:CAS周期值 [6:4] 4:运行模式 [8:7] 5:突发写模式 [9] 6:保留位 [11:10] */ temp=(u32)SDRAM_MODEREG_BURST_LENGTH_1 | //设置突发长度:1(可以是1/2/4/8) SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | //设置突发类型:连续(可以是连续/交错) SDRAM_MODEREG_CAS_LATENCY_3 | //设置CAS值:3(可以是2/3) SDRAM_MODEREG_OPERATING_MODE_STANDARD | //设置操作模式:0,标准模式 SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; //设置突发写模式:1,单点访问 BSP_SDRAM_SendCmd(0,FMC_SDRAM_CMD_LOAD_MODE,1,temp); //设置SDRAM的模式寄存器 //刷新频率计数器(以SDCLK频率计数),计算方法: //COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数 //我们使用的SDRAM刷新周期为64ms,SDCLK=216/2=108Mhz,行数为8192(2^13). //所以,COUNT=64*1000*108/8192-20=825.75; HAL_SDRAM_ProgramRefreshRate(&SDRAM_Handle_InitStructure,824); //中文参考手册P365; } /********************************************************* * 函数名:BSP_SDRAM_SendCmd(u8 bankx, u8 cmd , u8 refresh, u16 regval) * 描述 :简化设置模式寄存器程序; 1、bankx:0: bank5发送命令 1: bank6发送命令 2、cmd指令: 第一次调用:SDRAM时钟使能 二 :所有存储区预充电 三 :自动刷新次数 四 :设置模式寄存器 五 :自刷新 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ u8 BSP_SDRAM_SendCmd(u8 bankx, u8 cmd , u8 refresh, u16 regval) { u32 target_bank = 0; FMC_SDRAM_CommandTypeDef FMC_SDRAM_CommandInitStructure; if(bankx == 0) target_bank = FMC_SDRAM_CMD_TARGET_BANK1; if(bankx == 1) target_bank = FMC_SDRAM_CMD_TARGET_BANK2; FMC_SDRAM_CommandInitStructure.CommandMode = cmd; //命令 FMC_SDRAM_CommandInitStructure.CommandTarget = target_bank; //目标bank FMC_SDRAM_CommandInitStructure.AutoRefreshNumber = refresh; //自刷新次数 FMC_SDRAM_CommandInitStructure.ModeRegisterDefinition = regval; //要写入模式寄存器的值 if(HAL_SDRAM_SendCommand(&SDRAM_Handle_InitStructure,&FMC_SDRAM_CommandInitStructure,0x1000) == HAL_OK) return 0; else return 1; }
- 框图
- 写操作
- 框图
- 发送激活命令:此命令同时发送行地址和bank地址,发送该命令后,需要等待Trcd(据芯片手册设置为:2,芯片是15ns,时钟周期:9.25ns)时间,才可以发送写命令;
- 写命令:发送写命令的同时,设置列地址,完成对SDRAM的寻址,同时将数据 通过DQ数据线,存入SDRAM;
- 使能自动预充电:发送写命令的同时,拉高A10地址线,使能自动预充电以提高读写效率
- 执行预充电:预充电在发送激活命令的Tras时间后启动,并且需要等待Trp时间,来完成
- 完成一次数据 写入:最后发送第二个激活命令,启动下一次数据传输,这样就完成了一次数据的读取
- 框图
- 读操作
- 框图
- 发送激活命令:此命令同时发送行地址和bank地址,发送该命令后,需要等待Trcd(据芯片手册设置为:2,芯片是15ns,时钟周期:9.25ns)时间,才可以发送写命令;
- 读命令:发送读命令的同时,设置列地址,完成对SDRAM的寻址; 读操作还有一个CL延迟(CAS Latency),所以需要等待给定的CL延迟(2或3个clk)后,再从DQ数据线上读取数据;
- 使能自动预充电:发送写命令的同时,拉高A10地址线,使能自动预充电以提高读写效率
- 执行预充电:预充电在发送激活命令的Tras时间后启动,并且需要等待Trp时间,来完成
- 完成一次数据 写入:最后发送第二个激活命令,启动下一次数据传输,这样就完成了一次数据的读取;
- 框图
- SDRAM初始化
- 读/写例程:
/********************************************************* * 函数名:BSP_FMC_SDRAM_WriteBuffer(u8 *pBuffer,u32 WriteAddr,u32 n); * 描述 :在指定地址(SDRAM_BANK_ADDR + WriteAddr)处连续写入数据 1、pBuffer:字节地址 2、WriteAddr:要写入的地址 3、写入的字节数 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void BSP_FMC_SDRAM_WriteBuffer(u8 *pBuffer,u32 WriteAddr,u32 n) { for(;n!=0;n--) { *(vu8*)(BSP_SDRAM_BANK5_ADDR + WriteAddr) = *pBuffer; WriteAddr ++; pBuffer ++; } } /********************************************************* * 函数名:BSP_FMC_SDRAM_ReadBuffer(u8 *pBuffer,u32 ReadAddr,u32 n); * 描述 :在指定地址(SDRAM_BANK_ADDR + WriteAddr)处连续读出数据 1、pBuffer:字节地址 2、WriteAddr:要读出的地址 3、写入的字节数 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void BSP_FMC_SDRAM_ReadBuffer(u8 *pBuffer,u32 ReadAddr,u32 n) { for(;n!=0;n--) { *pBuffer++ = *(vu8*)(BSP_SDRAM_BANK5_ADDR + ReadAddr); ReadAddr++; } }
- 总结:仍需努力;
- 参考资料
- 正点原子:STM32F7 开发指南(HAL 库版)
- 野火:STM32 HAL 库开发实战指南
- 《高手进阶,终极内存技术指南——完整/进阶版》免费下载地址http://www.armbbs.cn/forum.php?mod=viewthread&tid=1930
- 官方例程:STM32Cube_FW_F7_V1.4.0
【10】STM32F767——————>SDRAM之W9825G6HK-6 附件有《高手进阶,终极内存技术指南——完整/进阶版》下载地址
最新推荐文章于 2023-06-26 16:21:04 发布