立即注册 找回密码

微雪课堂

搜索
微雪课堂 首页 STM32 STM32CubeMX系列教程 查看内容

STM32CubeMX系列教程21:SDRAM

2016-5-23 13:53| 发布者: waveshare-admin| 查看: 26801| 评论: 3

摘要: 本章讲解通过FMS接口控制Nand Flash K9F1G08U0E (1G Bit)
在看下面教程之前,如果你之前没有使用过SDRAM,建议先看以下文档,以对SDRAM的原理和控制有一定的了解。

一、SDRAM简介

SDRAM(Synchronous Dynamic Random Access Memory)同步动态随机存取存储器
·同步是指存储器工作需要同步时钟,内部命令的发送与数据传输都以它为基准
·动态是指存储阵列需要不断的刷新来保证数据不丢失
·随机存取是指存储器的内容可以以任意顺序访问,而不管前一次访问的是哪一个位置

开发板使用的SDRAM型号是IC42S16400J-7TL或IS42S16400J-7TL(两个型号仅产地不同,性能相同),它是一颗8M字节(1 Meg Bits × 16Bits × 4 Banks = 67108864 bits = 64-Mbit)内存芯片。
顺便说下:“-7TL”中,7表示速度等级,T表示封装 TSOPII,L表示无铅(符合RoHS标准),属于商用系列,工作温度:0℃ ~ 70℃。如下为芯片数据手册:


SDRAM的内部是一个存储阵列,阵列就如同表格一样,先指定一个行(Row),再指定一个列(Column),我们就可以准确地找到所需要的单元格,这就是内存芯片寻址的基本原理。对于内存,这个单元格可称为存储单元,那么这个表格就是逻辑Bank(Logical Bank,下文简称L-Bank)。SDRAM内部分割成多个L-Bank。IC42S16400J分为四个Bank。


二、SDRAM硬件简介


上图是SDRAM内部结构图,引脚(Pin)所对应的功能简单翻译如下:

A0~A11:时分复用地址总线(发送地址时,先行后列,12行,8列)
DQ0~DQ15:双向数据总线
BA0/BA1:Bank地址(两条总线,可以选通4个Bank)
CS#:片选信号(低电平有效)
WE#:写使能信号(低电平有效)
RAS#:行地址信号(低电平有效)
CAS#:列地址信号(低电平有效)
CLK:同步时钟
CKE:时钟使能信号
UDQM/LDQM:数据掩码
VDD/VDDQ:工作电压/DQ电压
GND/GNDQ:相应电压接地

SDRAM与Open746I-C开发板连接如下图:



对于SDRAM来说,有三个参数对其性能影响至关重要,它们是tRCDCLtRP,对于这三个参数的详细描述可以查看“高手进阶,终极内存技术指南——完整.doc”。
下面是对三个参数的一些简要摘录(顺便加多了一个tWR):
tRCD:
在发送列读写命令时必须要与行有效命令有一个间隔,这个间隔被定义为tRCD,即RAS to CAS Delay(RAS至CAS延迟),可以理解为行选通周期,这应该是根据芯片存储阵列电子元件响
应时间(从一种状态到另一种状态变化的过程)所制定的延迟。广义的tRCD以时钟周期tCKClock Time)数为单位,比如tRCD=2,就代表延迟周期为两个时钟周期,具体到确切的时,则要根据时钟频率而定,对于IS42S16400J-7TL,tRCD为15ns
CL(CAS Latency):
在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据I/O通道(DQ)输出到内存总线上了。但是在CAS发出之后,仍要经过一定的时间才能有数据输出,从CAS与读取命令发出到第一笔数据输出的这段时间,被定义为CL(CAS Latency,CAS潜伏期)。由于CL只在读取时出现,以CL又被称为读取潜伏期(RL,Read Latency)。CL的单位与tRCD一样,为时钟周期数,具体耗时时钟频率决定。
数据写入的操作也是在tRCD之后进行,但此时没有了CL(CL只出现在读取操作中)。对于IS42S16400J-7TL,CL可取23个周期
tRP:
在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开新的工作行,这个间隔被称为tRP(Precharge command Period,预充电有效周期)。和tRCDCL一样,tRP的单位也是时钟周期数,具体值视时钟频率而定。对于IS42S16400J-7TL,tRP为15ns
tWR:
数据并不是即时地写入存储电容,因为选通三极管(就如读取时一样)与电容的充电必须要有一段时间,所以数据的真正写入需要一定的周期。为了保证数据的可靠写入,都会留出足够的写入/校正时间tWRWriteRecovery Time),这个操作也被称作写回(Write Back)。对于IS42S16400J-7TL,tWR为2个周期

三、FMC之SDRAM控制器简介

这里贴一下SDRAM控制器的特性,详细可查看数据手册:


操作SDRAM,需要先知道它的地址映射区域。从下图可知,SDRAM在STM32内部是连续的地址空间,我们使用的是区域2,所以地址应该在0xD0000000 ~ 0xDFFFFFFF之间。


由于我们的SDRAM是4Bank,12行,8列,由下图知,对于32位的地址空间,其23~31位的地址是固定了。为0xD0000000。0~22位的地址是实际的SDRAM可变地址,即地址映射为:0xD0000000 ~ 0xD07FFFFF


控制SDRAM涉及到以下几个寄存器,之后在对SDRAM配置时,使用到的位会有简单的介绍,详细描述请查看数据手册。
SDRAM控制寄存器1,2(FMC_SDCR1,FMC_SDCR2)
SDRAM时序寄存器1,2(FMC_SDTR1,FMC_SDTR2)
SDRAM命令模式寄存器(FMC_SDCMR)
SDRAM刷新定时器寄存器(FMC_SDRTR)

总的SDRAM初始化步骤如下,读者可以在对SDRAM配置完成后,返回对照下面的步骤在看一下相应的代码,相信会有一定的收获。


前面对SDRAM和STM32的SDRAM控制器有了一些简单的介绍,对于使用cube库的用户来说,基本是足够使用了。由于有SDRAM控制器,我们只需要简单的对控制器相应位进行配置后,就可以像操作内部的SRAM一样去操作SDRAM。

四、stm32CubeMX配置与说明

        复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,选择SDRAM 1,配置为4 banks,地址线12 bits,数据线 16 bits。
    

FMC之SDRAM引脚映射配置如下(注意PH5是FMC_SDNWE):



FMC配置如下


下面详细说明,各个选项的配置:

1.Bank 由硬件连接决定需要选择SDRAM bank 2
2.Column bit number表示列数,8位
3.Row bit number表示行数,12位
4.CAS latency表CAS潜伏期,即上面说的CL,该配置需要与之后的SDRAM模式寄存器的配置相同,这里先配置为2 memory clock cycles(对于SDRAM时钟超过133MHz的,则需要配置为3 memory clock cycles)
5.Write protection 写保护,一般配置为Disabled
6.SDRAM common clock为SDRAM 时钟配置,可选HCLK的2分频\3分频\不使能SDCLK时钟
前面主频配置为216MHz,SDRAM common clock设置为2分频,那SDCLK时钟为108MHz,每个时钟周期为9.25ns
7.SDRAM common burst read 表示突发读,这里选择使能
8.SDRAM common read pipe delay 表示CAS潜伏期后延迟多少个时钟在进行读数据,这里选择0 HCLK clock cycle
前面这8项主要是对SDRAM控制寄存器1,2(FMC_SDCR1,FMC_SDCR2)相关位进行的配

接下来的7项是对SDRAM时序寄存器1,2(FMC_SDTR1,FMC_SDTR2)相关位的配置
9.Load mode register to active delay : 加载模式寄存器命令和激活或刷新命令之间的延迟,按存储器时钟周期计


10.Exit self-refresh delay : 从发出自刷新命令到发出激活命令之间的延迟,按存储器时钟周期数计
查数据手册知道其最小值为70ns,由于我们每个时钟周期为9.25ns,所以设为8 (70÷9.25,向上取整)


11.Self refresh time : 最短的自刷新周期,按存储器时钟周期数计
查数据手册知道其最小值为42ns,最大值为100000ns,由于我们每个时钟周期为9.25ns,所以设为5 (40÷9.25,向上取整)


12.SDRAM common row cycle delay :  刷新命令和激活命令之间的延迟,以及两个相邻刷新命令之间的延迟, 以存储器时钟周期
数表示
查数据手册知道其最小值为63ns,由于我们每个时钟周期为9.25ns,所以设为7 (63÷9.25,向上取整)


13.Write recovery 
time : 写命令和预充电命令之间的延迟,按存储器时钟周期数计


14.SDRAM common row precharge delay : 预充电命令与其它命令之间的延迟,按存储器时钟周期数计
查数据手册知道其最小值为15ns,由于我们每个时钟周期为9.25ns,所以设为2 (15÷9.25,向上取整)


15.Row to column delay : 激活命令与读/写命令之间的延迟,按存储器时钟周期数计
查数据手册知道其最小值为15ns,由于我们每个时钟周期为9.25ns,所以这里本应该设为2 (15÷9.25,向上取整)
但要注意,时序必须满足以下式子:
TWR ≥ TRAS - TRCD
TWR ≥ TRC - TRCD - TRP
其中:TWR = Write recovery time = 2
TRAS = Self refresh time = 5
TRC = SDRAM common row cycle delay = 7
TRP = SDRAM common row precharge delay = 2
TRCD = Row to column delay
所以这里Row to column delay应该取3



生成报告以及代码,编译程序。在fmc.c文件中可以看到初始化函数。在stm32f7xx_hal_sdram.h头文件中可以看到sdram的操作函数。

五、应用程序编写

下载这个应用文件解压并添加到工程中:


在main.c中包含头文件"stm32746g_sdram.h"

/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
/* USER CODE END Includes */


添加变量,aRxBuffer,aTxbuffer为读写缓存,我uwWriteReadStatus存储读写状态。

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
#define BUFFER_SIZE         ((uint32_t)0x0100)
#define WRITE_READ_ADDR     ((uint32_t)0x0800)
/* Read/Write Buffers */
uint32_t aTxBuffer[BUFFER_SIZE];
uint32_t aRxBuffer[BUFFER_SIZE];
/* Status variables */
__IO uint32_t uwWriteReadStatus = 0;
int i;
/* USER CODE END PFP */


在main函数中添加以下测试代码:

/* USER CODE BEGIN 2 */
    BSP_SDRAM_Initialization_sequence(&hsdram1, REFRESH_COUNT);
    /*##-2- SDRAM memory read/write access #####################################*/
    /* Fill the buffer to write */
    for(i=0; i<BUFFER_SIZE; i++)
    {
            aTxBuffer[i]=0xC178A562+i;     /* TxBuffer init */
    }
  
    /* Write data to the SDRAM memory */
    BSP_SDRAM_WriteData(&hsdram1, SDRAM_DEVICE_ADDR+WRITE_READ_ADDR,aTxBuffer, BUFFER_SIZE);
    printf("\r\n/* Write data to the SDRAM memory */\r\n\r\n");
    for(i=0;i< BUFFER_SIZE;i++)
    {
            printf("%02X:0x%08X ",i,aTxBuffer[i]);
    }
    printf("\r\n");
  
    /* Read back data from the SDRAM memory */
    BSP_SDRAM_ReadData(&hsdram1, SDRAM_DEVICE_ADDR+WRITE_READ_ADDR, aRxBuffer, BUFFER_SIZE);
    printf("\r\n/* Read back data from the SDRAM memory */\r\n\r\n");
    for(i=0;i< BUFFER_SIZE;i++)
    {
            printf("%02X:0x%08X ",i,aRxBuffer[i]);
    }
    printf("\r\n");
  
    /*##-3- Checking data integrity ############################################*/    
    for (i = 0; (i < BUFFER_SIZE); i++)
    {
            if (aRxBuffer[i] != aTxBuffer[i])
                    uwWriteReadStatus++;
    }    
    if(uwWriteReadStatus == 0 ) /* check date */
            printf("\r\n SDRAM Test OK\r\n");
    else
            printf("\r\n SDRAM Test False\r\n");
    /* USER CODE END 2 */
这里详细说一下BSP_SDRAM_Initialization_sequence函数,主要是实现上SDRAM初始化步骤3~8

void BSP_SDRAM_Initialization_sequence(uint32_t RefreshCount)
{
  __IO uint32_t tmpmrd = 0;
    
  /* 时钟配置使能,对应STM32初始化SDRAM步骤3 */
  Command.CommandMode            = FMC_SDRAM_CMD_CLK_ENABLE;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK2;
  Command.AutoRefreshNumber      = 1;
  Command.ModeRegisterDefinition = 0;
  
  /* Send the command */
  HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
  
  /* 等待指定延迟周期,对应STM32初始化SDRAM步骤4 */ 
  /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
  HAL_Delay(1);
      
  /* PALL(“预充电所有存储区域”)命令,对应STM32初始化SDRAM步骤5 */ 
  Command.CommandMode            = FMC_SDRAM_CMD_PALL;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK2;
  Command.AutoRefreshNumber      = 1;
  Command.ModeRegisterDefinition = 0;
  
  /* Send the command */
  HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);  
    
  /* 自刷新命令,8个自刷新周期,对应STM32初始化SDRAM步骤6 */ 
  Command.CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK2;
  Command.AutoRefreshNumber      = 8;
  Command.ModeRegisterDefinition = 0;
  
  /* Send the command */
  HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
    
  /* 配置SDRAM模式寄存器,对应STM32初始化SDRAM步骤7 */
  /* 突发长度:1
     突发传输方式:顺序
     CAS潜伏期:2
     操作模式:标准
     操作模式:突发读/单一写
  */
  tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |\
                     SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |\
                     SDRAM_MODEREG_CAS_LATENCY_2           |\
                     SDRAM_MODEREG_OPERATING_MODE_STANDARD |\
                     SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
    
  Command.CommandMode            = FMC_SDRAM_CMD_LOAD_MODE;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK2;
  Command.AutoRefreshNumber      = 1;
  Command.ModeRegisterDefinition = tmpmrd;
  
  /* Send the command */
  HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
    
  /* 刷新率设置,对应STM32初始化SDRAM步骤8 */
  HAL_SDRAM_ProgramRefreshRate(&sdramHandle, RefreshCount); 
}
步骤7对应于设置下图中的各个位,以配置SDRAM模式寄存器

步骤8刷新率设置
由于我们使用的SDRAM芯片是4096行,所以这里的刷新率是64ms ÷(4096行) = 15.7us 
SDRAM使用108MHz,刷新周期为:15.7us × 108MHz = 1695.6
COUNT = 1695.6 - 20 = 1675


在执行完BSP_SDRAM_Initialization_sequence函数后,就可以像操作SRAM一样,操作SDRAM了

六、实验现象
编译下载,正常可以看到串口输出如下信息(波特率115200)



329

顶一下

刚表态过的朋友 (329 人)

相关阅读

发表评论

最新评论

引用 nobuta 2019-4-10 15:25
: 好用,用429试了下, 需要将 BSP_SDRAM_Initialization_sequence(&hsdram1, REFRESH_COUNT)的 &hsdram1 删除
引用 游客 2018-10-27 23:40
好用,用429试了下, 需要将 BSP_SDRAM_Initialization_sequence(&hsdram1, REFRESH_COUNT)的 &hsdram1 删除
引用 游客 2017-3-28 21:53
谢谢分享

查看全部评论(3)

CubeMX教程

微雪官网|产品资料|手机版|小黑屋|微雪课堂. ( 粤ICP备05067009号 )

GMT+8, 2019-11-17 10:07 , Processed in 0.019053 second(s), 19 queries .

Powered by Discuz! X3.2 © 2001-2013 Comsenz Inc & Style Design

返回顶部