正点原子F429开发板使用STM32CubeMX驱动板载SDRAM

正点原子F429开发板使用STM32CubeMX驱动板载SDRAM

发现最近随便转载的很多啊,未经授权禁止转载!抄袭!!否则转载者死全家!!另外这是我的笔记,不是教程,难免会有错误,不具有很高的参考性,望周知。

由于我需要使用到SDRAM作为显存来使用,使用内部的SRAM空间作为显存显然不是明智的,因为屏幕的像素点很多,这样将会占用大量的内存,导致无法运行其他的东西。

STM32CubeMX配置工程

废话不多说,首先打开STM32CubeMX,找到FMC选项,对其进行设置。这里的参数不是随便设置的,需要根据SDRAM芯片的参数进行设置。

这部分是时序相关的参数,根据SDRAM芯片手册并计算进行确定。

为了测试SDRAM是否正常工作,这里打开USART1,使用串口对SDRAM的读写进行测试。

这里只用到了串口发送部分,因此不需要进行其他设置。

使用Keil修改代码

增加SDRAM命令函数

参考正点原子例程代码,在fmc.c文件中添加如下代码,具体参数计算已在注释中说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* USER CODE BEGIN 0 */

#define Bank5_SDRAM_ADDR ((uint32_t)(0XC0000000)) //SDRAM开始地址

//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)

uint8_t SDRAM_Send_Cmd(SDRAM_HandleTypeDef *SDRAM_Handler, uint8_t bankx,uint8_t cmd,uint8_t refresh,uint16_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(SDRAM_Handler,&Command,0X1000)==HAL_OK) //向SDRAM发送命令
{
return 0;
}
else return 1;
}

void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *SDRAM_Handler)
{
uint32_t temp=0;
//SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
SDRAM_Send_Cmd(SDRAM_Handler, 0,FMC_SDRAM_CMD_CLK_ENABLE,1,0); //时钟配置使能
HAL_Delay(1); //至少延时200us
SDRAM_Send_Cmd(SDRAM_Handler, 0,FMC_SDRAM_CMD_PALL,1,0); //对所有存储区预充电
SDRAM_Send_Cmd(SDRAM_Handler, 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_Send_Cmd(SDRAM_Handler, 0,FMC_SDRAM_CMD_LOAD_MODE,1,temp); //设置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(SDRAM_Handler,683);

}
/* USER CODE END 0 */

接着,在MX_FMC_Init函数中,添加如下代码,在FMC外设初始化完成后,发送SDRAM初始化序列,对SDRAM进行初始化。

1
2
3
 /* USER CODE BEGIN FMC_Init 2 */
SDRAM_Initialization_Sequence(&hsdram1);
/* USER CODE END FMC_Init 2 */

进行SDRAM读写测试

在main.c文件中,添加对串口Handle的外部声明:

1
2
3
/* USER CODE BEGIN PV */
extern UART_HandleTypeDef huart1;
/* USER CODE END PV */

在main函数的while循环中,添加测试代码:

1
2
3
4
5
6
7
8
9
10
11
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
sprintf((__IO uint8_t*) (0XC0000000), "SDRAM test success!\r\n"); //将字符串写入SDRAM空间内
HAL_UART_Transmit(&huart1, (__IO uint8_t*) (0XC0000000), 21, 0xff); //用串口直接打印SDRAM空间内的字符串
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

如果SDRAM功能正常,打开串口调试助手,能够看到不断有字符串输出,表明SDRAM已经能够正常使用了。

测试SDRAM的空间大小

正点原子提供了一个简单测试SDRAM空间大小的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void fsmc_sdram_test()
{
uint32_t i=0;
uint32_t temp=0;
uint32_t sval=0; //在地址0读到的数据
//每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节
for(i=0;i<32*1024*1024;i+=16*1024)
{
*(__IO uint32_t*)(0xc0000000+i)=temp;
temp++;
}
//依次读出之前写入的数据,进行校验
for(i=0;i<32*1024*1024;i+=16*1024)
{
temp=*(__IO uint32_t*)(0xc0000000+i);
if(i==0)sval=temp;
else if(temp<=sval)break;//后面读出的数据一定要比第一次读到的数据大.
printf("SDRAM Capacity:%dKB\r\n",(uint16_t)(temp-sval+1)*16);//打印SDRAM容量
}
}

该函数利用不断向SDRAM内写入递增的值,并依次读取写入过的值,判断后面的数据是否比前一次读取的大,并通过printf函数输出当前测试的SDRAM的大小。使用printf函数注意需要在工程选项中勾选USE MicroLIB并增大堆栈空间。测试结果如图。

坚持原创技术分享,您的支持是我前进的动力!