正点原子--阿波罗开发板STM32F429IGT6
CLK 时钟信号,在该时钟的上升沿采集输入信号
CKE 时钟使能,禁止时钟时,SDRAM 会进入自刷新模式
CS# 片选信号,低电平有效
RAS# 行地址选通信号,低电平时,表示行地址
CAS# 列地址选通信号,低电平时,表示列地址
WE# 写使能信号,低电平有效
A0~A12 地址线(行/列)
BS0,BS1 BANK 地址线
DQ0~15 数据线
LDQM,UDQM 数据掩码,表示 DQ 的有效部分
A[0:12]接 FMC_A[0:12]
BA[0:1]接 FMC_BA[0:1]
D[0:15]接 FMC_D[0:15]
CKE 接 FMC_SDCKE0
CLK 接 FMC_SDCLK
UDQM 接 FMC_NBL1
LDQM 接 FMC_NBL0
WE 接 FMC_SDNWE
CAS 接 FMC_SDNCAS
RAS 接 FMC_SDNRAS
CS 接 FMC_SDNE0
注意:正点原子 HAL库开发指南 Byte-enable 选择了disable,这个是不对,他自己写的驱动里有初始化PE0,PE1 , Byte-enable 只有选择了16bit-byte enable ,生成的代码才会初始PE0,PE1
/* USER CODE BEGIN 0 */
//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)
static SDRAM_HandleTypeDef * psdram = &hsdram1;
//=============================================================
uint8_t SDRAM_SendCmd(uint8_t bankx,uint8_t cmd,uint8_t refresh,uint8_t regval);
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram);
//SDRAM 序列初始化
int SDRAM_AutoInit(void)
{
SDRAM_Initialization_Sequence(psdram);//发送SDRAM初始化序列
//刷新频率计数器(以SDCLK频率计数),计算方法:
//COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
//我们使用的SDRAM刷新周期为64ms,SDCLK=180/2=90Mhz,行数为8192(2^13).
//所以,COUNT=64*1000*90/8192-20=683
HAL_SDRAM_ProgramRefreshRate(psdram,683);//设置刷新频率
return 0;
}
//=============================================================
//发送SDRAM初始化序列
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{
uint32_t temp=0;
//SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
SDRAM_SendCmd (0,FMC_SDRAM_CMD_CLK_ENABLE,1,0); //时钟配置使能
for(volatile uint32_t i=0;i<0xFFFFFF;i++){} //最少延时200us
SDRAM_SendCmd (0,FMC_SDRAM_CMD_PALL,1,0); //对所有存储区预充电
SDRAM_SendCmd (0,FMC_SDRAM_CMD_AUTOREFRESH_MODE,8,0); //设置自刷新次数
//配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度,
//bit3为指定突发访问的类型,bit4~bit6为CAS值,bit7和bit8为运行模式
//bit9为指定的写突发模式,bit10和bit11位保留位
temp =(uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | //设置突发长度:1(可以是1/2/4/8)
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | //设置突发类型:连续(可以是连续/交错)
SDRAM_MODEREG_CAS_LATENCY_3 | //设置CAS值:3(可以是2/3)
SDRAM_MODEREG_OPERATING_MODE_STANDARD | //设置操作模式:0,标准模式
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; //设置突发写模式:1,单点访问
SDRAM_SendCmd (0,FMC_SDRAM_CMD_LOAD_MODE,1,temp); //设置SDRAM的模式寄存器
}
/**
* @brief :函数作用 : SDRAM配置
* @note :附加说明 :
* @param :函数入口 : bankx: 0(向BANK5上面的SDRAM发送指令) 1(向BANK6上面的SDRAM发送指令)
* cmd :指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
* refresh:自刷新次数
* regval :模式寄存器的定义
* @retval:函数返回 : 0,正常;1,失败.
*/
uint8_t SDRAM_SendCmd(uint8_t bankx,uint8_t cmd,uint8_t refresh,uint8_t regval)
{
uint32_t target_bank=0;
FMC_SDRAM_CommandTypeDef Command;
if (bankx==0) target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
else if(bankx==1) target_bank = FMC_SDRAM_CMD_TARGET_BANK2;
Command.CommandMode = cmd; //命令
Command.CommandTarget = target_bank; //目标SDRAM存储区域
Command.AutoRefreshNumber = refresh; //自刷新次数
Command.ModeRegisterDefinition = regval; //要写入模式寄存器的值
if(HAL_SDRAM_SendCommand(psdram,&Command,0X1000)==HAL_OK) //向SDRAM发送命令
{
return 0;
}
else{
//COM_Printf("SDRAM_SendCmd Err = %d\r\n",cmd);
return 1;
}
}
/* USER CODE END 0 */
初始化完FMC后, 调用这个函数初始化SDRAM
int SDRAM_AutoInit(void); //SDRAM 序列初始化
//uint16_t testsram[250000] __attribute__((at(0XC0000000)));//测试用数组 编译器V5 版本
uint16_t testsram[250000] __attribute__((section(".ARM.__at_0xC0000000"))); //编译器V6版本
//SDRAM内存测试
void fsmc_sdram_test(void)
{
static volatile uint32_t i =0;
static volatile uint32_t temp=0;
static volatile uint32_t sval=0; //在地址0读到的数据
i =0;
temp=0;
sval=0;
COM_Printf("Ex Memory Test: 0KB \r\n");
//每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节
for(i=0;i<32*1024*1024;i+=16*1024)
{
*(volatile uint32_t*)(Bank5_SDRAM_ADDR+i)=temp;
temp++;
}
//依次读出之前写入的数据,进行校验
for(i=0;i<32*1024*1024;i+=16*1024)
{
temp=*(volatile uint32_t*)(Bank5_SDRAM_ADDR+i);
if(i==0)sval=temp;
else if(temp<=sval)break;//后面读出的数据一定要比第一次读到的数据大.
COM_Printf("SDRAM Capacity:%dKB\r\n",(uint16_t)(temp-sval+1)*16);//打印SDRAM容量
}
}