STM32F767 FMC-SDRAM的基本用法

 本文介绍STM32F767 FMC-SDRAM的基本用法。

开发环境

硬件环境

  • 电脑:Windows 10 Home x64
  • Apollo STM32F767开发板(ST-LINK V2仿真器)

软件环境

  • Keil Version 5.24.1 (Pack Installer:Keil.STM32F7xx_DFP.2.9.0.pack)
  • STM32CubeMX Version 4.25.0(Packages Manager:STM32CubeF7)

FMC功能简介

主要特性

 本例使用FMC的SDRAM控制器,其主要特性如下:

  • 两个 SDRAM 存储区域,可独立配置
  • 8 位、16 位和 32 位数据总线宽度
  • 13 位地址行,11 位地址列,4 个内部存储区域:4x16Mx32bit (256 MB)、4x16Mx16bit(128 MB)、4x16Mx8bit (64 MB)
  • 支持字、半字和字节访问
  • SDRAM 时钟可以是 HCLK/2 或 HCLK/3
  • 自动进行行和存储区域边界管理
  • 多存储区域乒乓访问
  • 可编程时序参数
  • 支持自动刷新操作,可编程刷新速率
  • 自刷新模式
  • 掉电模式
  • 通过软件进行 SDRAM 上电初始化
  • CAS 延迟 1,2,3
  • 读 FIFO 可缓存,支持 6 行 x 32 位深度(6 x14 位地址标记)

SDRAM外部存储器接口信号

SDRAM初始化流程

 该部分直接以软件形式介绍。

SDRAM的使用

SDRAM初始化函数

 SDRAM的初始化函数如下所示。

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
/* FMC initialization function */
void MX_FMC_Init(void)
{
FMC_SDRAM_TimingTypeDef SdramTiming;
FMC_SDRAM_CommandTypeDef Command;
/** 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_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 = 8;
SdramTiming.SelfRefreshTime = 7;
SdramTiming.RowCycleDelay = 7;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
SDRAM_Initialization_Sequence(&hsdram1, &Command);//发送SDRAM初始化序列
}

 其中,使用hsdram1.Init设置SDRAM的基本参数,使用SdramTiming设置的为SDRAM的时序参数,SDRAM_Initialization_Sequence()函数为SDRAM的初始化序列。以下将针对每个模块进行介绍。

SDRAM的基本参数

 本例中使用的SDRAM为W9825G66KH-6,其基本参数如下:

  • 4M x 4Banks x 16Bits = 32MByte
  • 最高速率166MHz@CL3
  • CAS Latency:2和3
  • 8K Refresh Cycles/64ms
  • 行(Row)地址A0-A12,列(Column)地址A0-A8

 涉及SDRAM基本参数的初始化函数如下表所示。

参数 说明 可选值 本例取值
SDBank SDRAM存储器区域 FMC_SDRAM_BANK1/2 FMC_SDRAM_BANK1
ColumnBitsNumber 列地址位数 FMC_SDRAM_COLUMN_BITS_NUM_8/9/10/11 FMC_SDRAM_COLUMN_BITS_NUM_9
RowBitsNumber 行地址位数 FMC_SDRAM_ROW_BITS_NUM_11/12/13 FMC_SDRAM_ROW_BITS_NUM_13
MemoryDataWidth 数据线位数 FMC_SDRAM_MEM_BUS_WIDTH_8/16/32 FMC_SDRAM_MEM_BUS_WIDTH_16
InternalBankNumber bank数目 FMC_SDRAM_INTERN_BANKS_NUM_2/4 FMC_SDRAM_INTERN_BANKS_NUM_4
CASLatency CAS延迟 FMC_SDRAM_CAS_LATENCY_1/2/3 FMC_SDRAM_CAS_LATENCY_3
WriteProtection 写保护控制 FMC_SDRAM_WRITE_PROTECTION_DISABLE/ENABLE FMC_SDRAM_WRITE_PROTECTION_DISABLE
SDClockPeriod SDRAM时钟周期 FMC_SDRAM_CLOCK_DISABLE/PERIOD_2/PERIOD_3 FMC_SDRAM_CLOCK_PERIOD_2
ReadBurst Read_Burst控制 FMC_SDRAM_RBURST_DISABLE/ENABLE FMC_SDRAM_RBURST_ENABLE
ReadPipeDelay Read_Pipe_Delay控制 FMC_SDRAM_RPIPE_DELAY_0/1/2 FMC_SDRAM_RPIPE_DELAY_1
  • SDBank对应SDRAM存储器区域,STM32F7可以外接2片SDRAM,本例仅在区域1接SDRAM;
  • 列地址、行地址、数据线位数、bank位数根据SDRAM相应参数选择;
  • CAS延迟:本例中SDRAM的时钟频率为108MHz,CAS延迟可以设置为2;
  • WriteProtection:禁用写保护以提高写入效率;
  • SDClockPeriod:设置SDCLK相对于HCLK的分频数,本例中HCLK等于系统时钟频率,为216MHz,SDCLK为HCLK二分频得到;
  • ReadBurst:定义在 CAS 延迟后延后多少个 HCLK 时钟周期读取数据,本例延时为0;

SDRAM的时序参数

 SdramTiming结构体中定义SDRAM的时序参数,其计算方法如下。注意所有的时间均按照SDRAM时钟周期计算。

 本例使用的SDRAM型号为:W9825G6KH-6,其时序参数也记录在表中。

参数 定义 W9825G6KH-6要求 最终取值
LoadToActiveDelay 加载模式寄存器到激活:加载模式寄存器命令和激活或刷新命令之间的延迟 $t{RSC}=2 \cdot t{CK}$ 2
ExitSelfRefreshDelay 退出自刷新延迟:自刷新命令到发出激活命令之间的延迟 $t_{XSR}=72ns$ 7.7≈8
SelfRefreshTime 自刷新时间:最短的自刷新周期 $t_{RC}=60ns$ 6.5≈7
RowCycleDelay 行循环延迟:刷新命令和激活命令之间的延迟,以及两个相邻刷新命令之间的延迟 $t_{RC}=60ns$ 6.5≈7
WriteRecoveryTime 恢复延迟:写命令和预充电命令之间的延迟 $t{WR}=2 \cdot t{CK}$ 2
RPDelay 行预充电延迟:预充电命令与其它命令之间的延迟 $t_{RP}=15ns$ 2
RCDDelay 行到列延迟:激活命令与读/写命令之间的延迟 $t_{RCD}=15ns$ 2

SDRAM的初始化序列

 SDRAM的初始化序列如下所示。

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
/**
* @brief Perform the SDRAM exernal memory inialization sequence
* @param hsdram: SDRAM handle
* @param Command: Pointer to SDRAM command structure
* @retval None
*/
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
vuint32 tmpmrd = 0;
/* Step 1: 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, 0x1000);
/* Step 2: Insert 100 us minimum delay */
SYS_Delay_US(500);
/* Step 3: 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, 0x1000);
/* Step 4 : 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, 0x1000);
/* Step 5: Program the external memory mode register */
tmpmrd=(uint32)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;
printf("tmpmrd is: %x\n", tmpmrd);
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, 0x1000);
/* Step 6: Set the refresh rate counter */
HAL_SDRAM_ProgramRefreshRate(&hsdram1,824);
}
  • step 1:使能SDRAM时钟;
  • step 2:上电后,在precharge all banks前延时500us,本例中W9825G6KH-6要求最低200us;
  • step 3:precharge all banks;
  • step 4:设置自刷新cycle数,本例中要求至少8个Auto Refresh cycles(CBR);
  • step 5:设置SDRAM模式寄存器,在本例中,设置burst长度为1(STM32F767要求burst length为1,数据手册中原文说明为:the Burst Length (BL) of 1 must be selected by configuring the M[2:0] bits to 000 in the mode register)、burst类型为Sequential、CAS_LATENCY为2(与FMC中SDRAM控制器设置相对应)、运行模式为标准、Write Burst模式为Single Location Access;
  • step 6:设置FMC_SDRTR 寄存器中的刷新速率,计算方法为COUNT = (SDRAM refresh period ⁄ Number of rows) – 20,本例为824;
  • 至此,SDRAM配置完毕。

SDRAM的读写实现

 SDRAM的读写非常简单,其被处理器映射为一段起始地址为0XC0000000的存储区域,其读写实现如下所示。

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
#define Bank5_SDRAM_ADDR ((uint32)(0XC0000000)) /* SDRAM开始地址 */
void FMC_SDRAM_WriteBuffer16b(uint16 *pSrcBuffer,uint32 WriteAddr,uint32 BufferSize)
{
__IO uint16_t *pSdramAddress = (uint16_t *)(Bank5_SDRAM_ADDR + WriteAddr);
/* Write data to memory */
for(; BufferSize != 0; BufferSize--)
{
*(__IO uint16_t *)pSdramAddress = *pSrcBuffer;
pSrcBuffer++;
pSdramAddress++;
}
}
void FMC_SDRAM_ReadBuffer16b(uint16 *pDstBuffer,uint32 pAddress,uint32 BufferSize)
{
__IO uint16_t *pSdramAddress = (uint16_t *)(Bank5_SDRAM_ADDR + pAddress);
/* Read data from source */
for(; BufferSize != 0; BufferSize--)
{
*pDstBuffer = *(__IO uint16_t *)pSdramAddress;
pDstBuffer++;
pSdramAddress++;
}
}

SDRAM的读写测试

 对SDRAM进行读写测试,测试代码如下。

1
2
3
4
5
6
#define BUFFER_SIZE (64*1024)
uint32 test_addr = 0x00000000;
uint32 test_index = 0;
uint16 test_writeBuff[BUFFER_SIZE];
uint16 test_readBuff[BUFFER_SIZE];
uint16 testErrorNum = 0;
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
printf("----SDRAM write and read test----\n");
for (test_index = 0; test_index < BUFFER_SIZE; test_index++)
{
test_writeBuff[test_index] = test_index % UINT16_MAX;
}
printf("SDRAM write start...\n");
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
FMC_SDRAM_WriteBuffer16b(test_writeBuff,test_addr,BUFFER_SIZE);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
printf("SDRAM write finished\n\n");
printf("SDRAM read start...\n");
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
FMC_SDRAM_ReadBuffer16b(test_readBuff,test_addr,BUFFER_SIZE);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
printf("SDRAM read finished\n\n");
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
for (test_index = 0; test_index < BUFFER_SIZE; test_index++)
{
if (test_readBuff[test_index] != test_writeBuff[test_index])
{
//printf("readBuff is NOT equal with writeBuff at: %d\n", test_index);
testErrorNum++;
}
}
printf("Total test number is: %d, error number is: %d\n", test_index, testErrorNum);

 测试结果表示读写无误。

文章目录
  1. 1. 开发环境
    1. 1.1. 硬件环境
    2. 1.2. 软件环境
  2. 2. FMC功能简介
    1. 2.1. 主要特性
    2. 2.2. SDRAM外部存储器接口信号
    3. 2.3. SDRAM初始化流程
  3. 3. SDRAM的使用
    1. 3.1. SDRAM初始化函数
    2. 3.2. SDRAM的基本参数
    3. 3.3. SDRAM的时序参数
    4. 3.4. SDRAM的初始化序列
    5. 3.5. SDRAM的读写实现
  4. 4. SDRAM的读写测试
|