最近在做一个小项目,用的是STM32H743,遇到了几个大数组需要存放,正好画的板子上预留了SDRAM,就想把大数组放在SDRAM。这篇文章作为自己笔记,供以后参考。
使用的SDRAM为:MT48LC16M16A2P-6A,从立创商城购入。该模块的原理图如下图所示:
打开STM32CUBEMX,进入FMC-SDRAM1,按如图所示配置。
时钟树配置如图所示,主频480MHz,给到FMC的分频是240MHz。
在项目管理中,勾选为每个外设单独创建.c和.h文件
导出项目进入Keil软件,新建两个文件 SDRAM.h和SDRAM.c
#include "SDRAM.h"
#include "fmc.h"
#include <stdio.h>
#define SDRAM_TIMEOUT 1000
void SDRAM_InitSequence(void)
{
uint32_t tmpr = 0;
FMC_SDRAM_CommandTypeDef Command;
/* Step 3 -----------------------------------------------------*/
/* 配置命令:开启提供给SDRAM的时钟 */
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(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 2: Insert 100 us minimum delay */
/* Inserted delay is equal to 1 ms due to systick time base unit (ms)*/
HAL_Delay(1);
/* Step 5 ----------------------------------------------------*/
/* 配置命令:对所有的bank预充电 */
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(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 6 ------------------------------------------------------*/
/* 配置命令:自动刷新 */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command.AutoRefreshNumber = 4;
Command.ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 7 ------------------------------------------------------------------*/
/* 设置sdram寄存器配置 */
tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 |
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_SDRAM_CMD_TARGET_BANK1;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = tmpr;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 8 ------------------------------------------------------------------*/
/* 设置刷新计数器 */
/* (7.8125 us x Freq) - 20 */
/* Step 6: Set the refresh rate counter */
/* Set the device refresh rate */
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 824);
// FMC_SetRefreshCount(1386);
// /* 发送上述命令*/
// while(FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET)
// {
// }
}
/*
*********************************************************************************************************
* 函 数 名: WriteSpeedTest
* 功能说明: 写SDRAM速度测试
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void WriteSpeedTest(void)
{
uint32_t i, j;
int32_t iTime1, iTime2;
uint32_t *pBuf;
/* 设置初始化值并记下开始时间 */
j = 0;
pBuf = (uint32_t *)EXT_SDRAM_ADDR;
iTime1 = HAL_GetTick();
/* 以递增的方式写数据到SDRAM所有空间 */
for (i = 1024*1024/4; i >0 ; i--)
{
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
*pBuf++ = j++;
}
iTime2 = HAL_GetTick(); /* 记下结束时间 */
/* 读取写入的是否出错 */
j = 0;
pBuf = (uint32_t *)EXT_SDRAM_ADDR;
for (i = 0; i < 1024*1024*8; i++)
{
if(*pBuf++ != j++)
{
printf("写入出错 j=%d\r\n", j);
break;
}
}
/* 打印速度 */
printf("32MB数据写耗时:%dms, 写速度:%dMB/s\r\n",
iTime2 - iTime1, (EXT_SDRAM_SIZE / 1024 /1024 * 1000) / (iTime2 - iTime1));
}
/*
*********************************************************************************************************
* 函 数 名: ReadSpeedTest
* 功能说明: 读SDRAM速度测试
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void ReadSpeedTest(void)
{
uint32_t i;
int32_t iTime1, iTime2;
uint32_t *pBuf;
__IO uint32_t ulTemp; /* 设置为__IO类型,防止被MDK优化 */
/* 设置初始化值并记下开始时间 */
pBuf = (uint32_t *)EXT_SDRAM_ADDR;
iTime1 = HAL_GetTick();
/* 读取SDRAM所有空间数据 */
for (i = 1024*1024/4; i >0 ; i--)
{
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
ulTemp = *pBuf++;
}
iTime2 = HAL_GetTick(); /* 记下结束时间 */
/* 打印速度 */
printf("32MB数据读耗时:%dms, 读速度:%dMB/s\r\n",
iTime2 - iTime1, (EXT_SDRAM_SIZE / 1024 /1024 * 1000) / (iTime2 - iTime1));
}
SDRAM.h文件
#ifndef __SDRAM_H
#define __SDRAM_H
#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000)
#define EXT_SDRAM_SIZE (32 * 1024 * 1024)
//宏定义存放变量至SDRAM 用例:char a[2048]SDRAM_AREA_ATTRIBUTE;
#define SDRAM_AREA_ATTRIBUTE __attribute__ ((at(0xC0000000)))
/**
* @brief FMC SDRAM 模式配置的寄存器相关定义
*/
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
void SDRAM_InitSequence(void);
void ReadSpeedTest(void);
void WriteSpeedTest(void);
#endif
在自动生成的fmc.h文件中,找到如下代码
/** 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_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_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;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 8;
SdramTiming.SelfRefreshTime = 6;
SdramTiming.RowCycleDelay = 6;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 6;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FMC_Init 2 */
SDRAM_InitSequence(); //在此处添加
/* USER CODE END FMC_Init 2 */
注意在/* USER CODE BEGIN FMC_Init 2 */后加入SDRAM初始化的函数。
/* USER CODE BEGIN FMC_Init 2 */
SDRAM_InitSequence();
/* USER CODE END FMC_Init 2 */
最后在main.c文件中,可以调用
ReadSpeedTest();
WriteSpeedTest();
来测试SDRAM速度