1、驱动平台
开发板使用的是野火的STM32H750,需要驱动核心板上的SDRAM(2片装),SDRAM使用的是W9825G6KH-6,32MB@,组合一起成为64M。驱动方式使用STM32H750XB上的FMC进行驱动。通过使用HAL库上的stm32h7xx_hal_sdram.c里面的API进行配置。
2、SDRAM配置方式
1、初始化FMC对应的GPIO
1、开启FMC对应的GPIO时钟
2、将GPIO复用成FMC模式
3、使用HAL库初始化
代码如下:
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//配置为复用功能
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = GPIO_AF12_FMC;
GPIO_InitStructure.Pin = FMC_A0_GPIO_PIN;
HAL_GPIO_Init(FMC_A0_GPIO_PORT, &GPIO_InitStructure);
.....
因为管脚太多了,就不一一列举了。
2、初始化SDRAM的时钟源
通过PLL分频,可以获得SDRAM的控制时钟,具体配置代码如下:
RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;
RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FMC;
RCC_PeriphClkInit.PLL2.PLL2M = 5;
RCC_PeriphClkInit.PLL2.PLL2N = 144;
RCC_PeriphClkInit.PLL2.PLL2P = 2;
RCC_PeriphClkInit.PLL2.PLL2Q = 2;
RCC_PeriphClkInit.PLL2.PLL2R = 3;
RCC_PeriphClkInit.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;
RCC_PeriphClkInit.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
RCC_PeriphClkInit.PLL2.PLL2FRACN = 0;
RCC_PeriphClkInit.FmcClockSelection = RCC_FMCCLKSOURCE_PLL2;
3 、使能FMC时钟,并配置FMC的控制参数,写入SDRAM的时序参数
代码如下:
__FMC_CLK_ENABLE(); //使能FMC时钟
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1结构体初始化*/
hsdram1.Init.SDBank = FMC_SDRAM_BANK2;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;//SDRAM列数
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;//SDRAM行数
hsdram1.Init.MemoryDataWidth = SDRAM_MEMORY_WIDTH;//总线数据宽度为32位
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;//4个扇区
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;//列地址选通信延时
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;//禁止写保护
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;//SDRAM时钟120MHz
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; //使能突发传输模式
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; //读通道延时
/* SDRAM时序 */
SdramTiming.LoadToActiveDelay = 2;//加载模式寄存器命令与行有效或刷新命令之间的延迟
SdramTiming.ExitSelfRefreshDelay = 8;//退出自我刷新到行有效命令之间的延迟
SdramTiming.SelfRefreshTime = 5;//行有效与预充电命令之间的延迟
SdramTiming.RowCycleDelay = 8;//两个刷新命令或两个行有效命令之间的延迟
SdramTiming.WriteRecoveryTime = 2;//写入命令到预充电命令之间的延迟
SdramTiming.RPDelay = 2;//预充电与行有效命令之间的延迟
SdramTiming.RCDDelay = 2;//行有效与列读写命令之间的延迟
HAL_SDRAM_Init(&hsdram1, &SdramTiming);
4、初始化SDRAM,并进行预充电等处理
代码如下:
uint32_t tmpr = 0;
/* 配置命令:开启提供给SDRAM的时钟 */
Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* Step 2: 延时100us */
SDRAM_delay(1);
/* 配置命令:对所有的bank预充电 */
Command.CommandMode = FMC_SDRAM_CMD_PALL;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 配置命令:自动刷新 */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 8;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 设置sdram寄存器配置 */
tmpr = (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;
/* 配置命令:设置SDRAM寄存器 */
Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = tmpr;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 设置刷新计数器 */
/* 刷新周期=64ms/8192行=7.8125us */
/* COUNT=(7.8125us x Freq) - 20 */
/* 设置自刷新速率 */
HAL_SDRAM_ProgramRefreshRate(&sdramHandle, 824);
配置参数来自于芯片手册,并通过自己配置的时钟进行计算得到的,注释上有些写。
完成后基本上SDRAM的初始化就完成了,剩下就是自行配置SDRAM相关的接口函数,比如:
uint32_t tmpr = 0;
/* 配置命令:开启提供给SDRAM的时钟 */
Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* Step 2: 延时100us */
SDRAM_delay(1);
/* 配置命令:对所有的bank预充电 */
Command.CommandMode = FMC_SDRAM_CMD_PALL;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 配置命令:自动刷新 */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 8;
Command.ModeRegisterDefinition = 0;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 设置sdram寄存器配置 */
tmpr = (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;
/* 配置命令:设置SDRAM寄存器 */
Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command.CommandTarget = FMC_COMMAND_TARGET_BANK;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = tmpr;
/* 发送配置命令 */
HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);
/* 设置刷新计数器 */
/* 刷新周期=64ms/8192行=7.8125us */
/* COUNT=(7.8125us x Freq) - 20 */
/* 设置自刷新速率 */
HAL_SDRAM_ProgramRefreshRate(&sdramHandle, 824);
等等、看个人需求。
5、遇坑提醒
在没有使用STM32CubeMX配置的工程中,很多人都会将stm32h7xx_ll_xxx.c等文件去掉,我在移植使用的时候,编译时出现了这么几个错误:
Error[Li005]: no definition for "FMC_SDRAM_ProgramRefreshRate" [referenced from
Error[Li005]: no definition for "FMC_SDRAM_SendCommand" [referenced from
Error[Li005]: no definition for "FMC_SDRAM_Init" [referenced from
Error[Li005]: no definition for "FMC_SDRAM_Timing_Init" [referenced from
出现这问题就是源于没有将stm32h7xx_ll_fmc.c这个文件包含进来,因为这个文件包含了FMC的底层驱动,而stm32h7xx_hal_sdram.c是依赖stm32h7xx_ll_fmc.c里面的底层驱动来编写的。