欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于STM32F429的SDRAM使用

程序员文章站 2022-04-01 16:21:38
...

使用ST的HAL库进行开发,SDRAM使用的是W9825G6KH-6。

W9825G6KH-6共有4个Bank,13位行地址,9位列地址,位宽是16位,

所以芯片的容量是:4x8192x512x16=256Mbits=32MBytes。

W9825G6KH-6的原理图如下:

基于STM32F429的SDRAM使用

FMC_D0~15:16位数据线;

FMC_A0~12:13位地址线,行地址与列地址是公用的,作为行地址时使用了0~12位,作为列地址时使用了0~8位;

FMC_SDNWE:低电平时写,高电平时读;

FMC_SDNCAS:列地址选通信号,低电平有效;

FMC_SDNRAS:行地址选通信号,低电平有效;

FMC_SDNE0:片选信号,低电平有效;

FMC_BA0~1:Bank选择信号;

FMC_SDCKE0:时钟使能信号;

FMC_SDCLK:时钟信号;

FMC_NBL0~1:写访问的输出字节屏蔽。


STM32F429自带FMC外设,可以对多种外部存储器进行控制,存储区域对应如下:

基于STM32F429的SDRAM使用

我们使用的是Bank5,也就是SDRAM Bank1。

我们要做的就是对SDRAM进行初始化配置,初始化成功后即可对指定的内存进行访问,单片机和外部SDRAM之间的读写时序

是由外设自动产生的,不需要程序进行控制,非常方便。

STM32F4xx参考手册中对SDRAM的初始化过程如下:

基于STM32F429的SDRAM使用

使用STM32CubeMx生成FMC的初始化代码,如下:

void MX_FMC_Init(void)
{
    FMC_SDRAM_TimingTypeDef SdramTiming;

    /** Perform the SDRAM1 memory initialization sequence
    */
    hsdram1.Instance = FMC_SDRAM_DEVICE;
    /* hsdram1.Init */
    hsdram1.Init.SDBank = FMC_SDRAM_BANK1;                              //使用SDRAM Bank1
    hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;        //列宽度9位
    hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;             //行宽度13位
    hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;          //数据总线为16位
    hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;     //SDRAM内部Bank数为4个
    hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;                  //CAS为3,表示发送完读时序后延迟3个时钟周期返回数据
    hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;  //忽略写保护
    hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;              //SDRAM时钟SDCLK = HCLK/2 = 180MHz / 2 = 90MHz
    hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;                   //失能突然读模式
    hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1;               //读通道延时1个时钟周期,在CAS延时后延时读取数据的时钟个数
    /* SdramTiming */
    SdramTiming.LoadToActiveDelay = 2;                                  //TMRD/TRSC,2个时钟周期
    SdramTiming.ExitSelfRefreshDelay = 8;                               //TXSR,8个时钟周期
    SdramTiming.SelfRefreshTime = 6;                                    //TRAS,6个时钟周期
    SdramTiming.RowCycleDelay = 6;                                      //TRC,6个时钟周期
    SdramTiming.WriteRecoveryTime = 4;                                  //TWR,4个时钟周期
    SdramTiming.RPDelay = 2;                                            //TRP,2个时钟周期
    SdramTiming.RCDDelay = 2;                                           //TRCD,2个时钟周期
    
    if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
}

上述FMC的初始化代码完成了SDRAM初始化中的1、2两个步骤;
接下来添加SDRAM初始化中的步骤3~8:

uint8_t SDRAM_SendCommand(uint32_t CommandMode, uint32_t Bank, uint32_t RefreshNum, uint32_t RegVal)
{
    uint32_t CommandTarget;
    FMC_SDRAM_CommandTypeDef Command;
    
    if(Bank == 1)
        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    else if(Bank == 2)
        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
    
    Command.CommandMode = CommandMode;
    Command.CommandTarget = CommandTarget;
    Command.AutoRefreshNumber = RefreshNum;
    Command.ModeRegisterDefinition = RegVal;
    if(HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000) == HAL_OK)
        return 1;
    else
        return 0;
}
void SDRAM_Init(void)
{
    uint32_t temp;
    
    SDRAM_SendCommand(FMC_SDRAM_CMD_CLK_ENABLE, 1, 1, 0);           //步骤3:使能时钟信号,SDCKE0 = 1
    Delay_us(500);                                                  //步骤4:至少延时200us
    SDRAM_SendCommand(FMC_SDRAM_CMD_PALL, 1, 1, 0);                 //步骤5:发送全部预充电命令
    SDRAM_SendCommand(FMC_SDRAM_CMD_AUTOREFRESH_MODE, 1, 8, 0);     //步骤6:设置自动刷新次数
    
    temp = SDRAM_MODEREG_BURST_LENGTH_1     |                       //设置突发长度:1
    SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL     |                       //设置突发类型:连续
    SDRAM_MODEREG_CAS_LATENCY_3             |                       //设置CAS值:3
    SDRAM_MODEREG_OPERATING_MODE_STANDARD   |                       //设置操作模式:标准
    SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;                           //设置突发写模式:单点访问
    SDRAM_SendCommand(FMC_SDRAM_CMD_LOAD_MODE, 1, 1, temp);         //步骤7:装载模式寄存器的值
    
    //SDRAM刷新周期是64ms,行数是8192行,时钟频率是180MHz/2=90MHz
    //所有COUNT = (64ms/8192)/(1/90us)-20 = 64000*90/8192-20 = 683
    HAL_SDRAM_ProgramRefreshRate(&hsdram1, 683);                    //步骤8:设置刷新速率
}

函数SDRAM_SendCommand()用来发送命令,内部调用了HAL的库函数HAL_SDRAM_SendCommand()发送配置命令;

函数SDRAM_Init()完成了SDRAM初始化中的步骤3~8。

至此,我们就完成了对SDRAM的初始化操作,此时外部SDRAM已经被映射到了相应的内存地址;需要注意的是,
SDRAM Bank1的地址是从0xC0000000~0xCFFFFFFF,SDRAM Bank2的地址是从0xD0000000~0xDFFFFFFF,
我们使用的是SDRAM Bank1,并且外部SDRAM的大小是32M字节,所以对应的内存地址范围是0xC0000000~0xC1FFFFFF。
接下来就测试一下SDRAM的读写:

#define     SDRAM_BANK1_BASE_ADDRESS    0xC0000000                                  //SDRAM Bank1起始地址
#define     SDRAM_HALF_WORD_SIZE        0x1000000                                   //定义16M个的16bits数据,共32M字节

__no_init uint16_t uhSdramArray[SDRAM_HALF_WORD_SIZE] @SDRAM_BANK1_BASE_ADDRESS;    //强制定义数组在SDRAM的内存中

void SDRAM_Test(void)
{
    uint32_t i;
    
    for(i = 0; i < SDRAM_HALF_WORD_SIZE; i++)
    {
        uhSdramArray[i] = i;                                        //对整个数组进行赋值
    }
    
    for(i = 0; i < SDRAM_HALF_WORD_SIZE; i += 4096)
    {
        printf("uhSdramArray[%d] = %d\r\n", i, uhSdramArray[i]);    //对数组中的数据间隔4096个进行串口打印
    }
}
串口打印结果如下,数据太多,没有全部显示,

基于STM32F429的SDRAM使用

基于STM32F429的SDRAM使用

SDRAM测试结束。

相关标签: SDRAM STM32F429