### 背景
一张800x480x分辨率的bmp文件足有1.09MB大小,即使是16位的bmp文件也有187KB。再看STM32F769芯片片上SRAM比较大,但也仅有512KB的容量大小。如果在GUI显示应用上面,以太网应用场景里负荷较重下,则相当吃紧。这里需要SDRAM芯片的协助了。
### SDRAM
SDRAM(Synchronous Dynamic Random Access Memory)是同步动态随机存取存储器,是一种常用的内存类型。它是在时钟信号的控制下进行数据读取和写入的,具有高速、高效的特点。SDRAM的特点是高速、高效、灵活性强。它可以在一个时钟周期内进行一次数据读写操作,比传统的DRAM操作更快。此外,SDRAM的功耗相对较低,体积较小,质量也轻,适用于各种电子产品中。
### 配置SDRAM
从原理图上可以看到在STM32F769IDISCO开发板上面,SDRAM与STM32F769的FMC外设相连接。
阅读芯片手册,可以看到通过FMC外设驱动SDRAM内存,其地址空间可以选择2个bank。我选择了bank1,即SDRAM的地址空间起始为0xC000_0000。
在本次实验中,我们仍然通过STM32CubeMX工具软件来生成硬件配置代码。
### 测试代码
在main.c中,我们再编写简单的测试代码。本次我设计通过读写SDRAM内存中某个地址,把读写结果进行简单的对比,再将读写操作的结果通过串口输出出来。如果写入数据与读取数据相同,则代表SDRAM配置以及读写成功。
~~~c
main.c
static void sdram_init(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
uint32_t tmpmrd = 0;
/* Step 3:Configure a clock configuration enable command */
Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 4: Insert 100 us minimum delay */
/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
HAL_Delay(1);
/* Step 5: Configure a PALL (precharge all) command */
Command->CommandMode = FMC_SDRAM_CMD_PALL;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 6 : Configure a Auto-Refresh command */
Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 8;
Command->ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 7: Program the external memory mode register */
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_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = tmpmrd;
/* Send the command */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/* Step 8: Set the refresh rate counter */
/* (15.62 us x Freq) - 20 */
/* Set the device refresh counter */
hsdram->Instance->SDRTR |= ((uint32_t)((1292) << 1));
}
void sdram_write_reg(uint32_t addr, uint32_t val)
{
volatile uint32_t *ptr = (volatile uint32_t *)addr;
*ptr = val;
}
uint32_t sdram_read_reg(uint32_t addr)
{
volatile uint32_t *ptr = (volatile uint32_t *)addr;
return (*ptr);
}
/* FMC initialization function */
static void MX_FMC_Init(void)
{
/* USER CODE BEGIN FMC_Init 0 */
FMC_SDRAM_CommandTypeDef command;
/* USER CODE END FMC_Init 0 */
FMC_SDRAM_TimingTypeDef SdramTiming = {0};
/* USER CODE BEGIN FMC_Init 1 */
/* USER CODE END FMC_Init 1 */
/** Perform the SDRAM1 memory initialization sequence
*/
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 6;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 6;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN FMC_Init 2 */
sdram_init(&hsdram1, &command);
/* USER CODE END FMC_Init 2 */
}
main.c
sdram_write_reg(SDRAM_BANK_ADDR, 0x12345678);
rx_buf[0] = sdram_read_reg(SDRAM_BANK_ADDR);
JDEBUG_PRINTF("rx=%08X\r\n", rx_buf[0]);
~~~
### 结果展示
串口输出结果显示SDRAM读写操作正确。