25. FMC

您所在的位置:网站首页 fanucladder3扩展外部工件检索信号 25. FMC

25. FMC

#25. FMC| 来源: 网络整理| 查看: 265

25.8. FMC—扩展外部SDRAM实验¶

本小节以型号为“W9825G6KH”的SDRAM芯片为STM32扩展内存。它的行地址宽度为13位,列地址宽度为9位,内部含有4个Bank,数据线宽度为16位,容量大小为32MB,使用了两片SDRAM共64MB容量。

学习本小节内容时,请打开配套的“FMC—读写SDRAM”工程配合阅读。本实验仅讲解基本的SDRAM驱动,不涉及内存管理的内容, 在本书的《MDK编译过程及文件类型全解》章节将会讲解使用更简单的方法从SDRAM中分配变量,以及使用C语言标准库的malloc函数来分配SDRAM的空间。

25.8.1. 硬件设计¶

SDRAM与STM32相连的引脚非常多,主要是地址线和数据线,这些具有特定FMC功能的GPIO引脚可查询《STM32H743xI规格书》中的说明来了解。

关于该SDRAM芯片的更多信息,请参考其规格书《W9825G6KH》了解。若您使用的实验板FLASH的型号或控制引脚不一样,可在我们工程的基础上修改,程序的控制原理相同。

25.8.2. 软件设计¶

为了使工程更加有条理,我们把SDRAM初始化相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_sdram.c”及“bsp_sdram.h”文件, 这些文件也可根据您的喜好命名,它们不属于STM32 HAL库的内容,是由我们自己根据应用需要编写的。

25.8.2.1. 编程要点¶

(1) 初始化通讯使用的目标引脚及端口时钟;

(2) 使能FMC外设的时钟;

(3) 配置FMC SDRAM的时序、工作模式;

(4) 根据SDRAM的初始化流程编写初始化函数;

(5) 建立机制访问外部SDRAM存储器;

(6) 编写测试程序,对读写数据进行校验。

25.8.2.2. 代码分析¶

FMC硬件相关宏定义

我们把FMC SDRAM硬件相关的配置都以宏的形式定义到 “bsp_sdram.h”文件中,见 代码清单:FMC-4 。

代码清单:FMC-4 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 57 58 59 60/*地址信号线*/ #define FMC_A0_GPIO_PORT GPIOF #define FMC_A0_GPIO_CLK() __GPIOF_CLK_ENABLE() #define FMC_A0_GPIO_PIN GPIO_PIN_0 /*省略一些引脚*/ #define FMC_A12_GPIO_PORT GPIOG #define FMC_A12_GPIO_CLK() __GPIOG_CLK_ENABLE() #define FMC_A12_GPIO_PIN GPIO_PIN_2 /*数据信号线*/ #define FMC_D0_GPIO_PORT GPIOD #define FMC_D0_GPIO_CLK() __GPIOD_CLK_ENABLE() #define FMC_D0_GPIO_PIN GPIO_PIN_14 /*省略一些引脚*/ #define FMC_D31_GPIO_PORT GPIOI #define FMC_D31_GPIO_CLK() __GPIOI_CLK_ENABLE() #define FMC_D31_GPIO_PIN GPIO_PIN_10 /*控制信号线*/ #define FMC_CS_GPIO_PORT GPIOH #define FMC_CS_GPIO_CLK() __GPIOH_CLK_ENABLE() #define FMC_CS_GPIO_PIN GPIO_PIN_6 #define FMC_BA0_GPIO_PORT GPIOG #define FMC_BA0_GPIO_CLK() __GPIOG_CLK_ENABLE() #define FMC_BA0_GPIO_PIN GPIO_PIN_4 #define FMC_BA1_GPIO_PORT GPIOG #define FMC_BA1_GPIO_CLK() __GPIOG_CLK_ENABLE() #define FMC_BA1_GPIO_PIN GPIO_PIN_5 #define FMC_WE_GPIO_PORT GPIOC #define FMC_WE_GPIO_CLK() __GPIOC_CLK_ENABLE() #define FMC_WE_GPIO_PIN GPIO_PIN_0 #define FMC_RAS_GPIO_PORT GPIOF #define FMC_RAS_GPIO_CLK() __GPIOF_CLK_ENABLE() #define FMC_RAS_GPIO_PIN GPIO_PIN_11 #define FMC_CAS_GPIO_PORT GPIOG #define FMC_CAS_GPIO_CLK() __GPIOG_CLK_ENABLE() #define FMC_CAS_GPIO_PIN GPIO_PIN_15 #define FMC_CLK_GPIO_PORT GPIOG #define FMC_CLK_GPIO_CLK() __GPIOG_CLK_ENABLE() #define FMC_CLK_GPIO_PIN GPIO_PIN_8 #define FMC_CKE_GPIO_PORT GPIOH #define FMC_CKE_GPIO_CLK() __GPIOH_CLK_ENABLE() #define FMC_CKE_GPIO_PIN GPIO_PIN_7 /*UDQM LDQM*/ #define FMC_UDQM_GPIO_PORT GPIOE #define FMC_UDQM_GPIO_CLK() __GPIOE_CLK_ENABLE() #define FMC_UDQM_GPIO_PIN GPIO_PIN_1 #define FMC_LDQM_GPIO_PORT GPIOE #define FMC_LDQM_GPIO_CLK() __GPIOE_CLK_ENABLE() #define FMC_LDQM_GPIO_PIN GPIO_PIN_0

以上代码根据硬件的连接,把与SDRAM通讯使用的引脚号、引脚源以及复用功能映射都以宏封装起来。其中FMC_CKE和FMC_CLK引脚对应的是FMC的存储区域2, 所以后面我们对SDRAM的寻址空间也是要指向存储区域2的。

初始化FMC的 GPIO

利用上面的宏,编写FMC的GPIO引脚初始化函数,见 代码清单:FMC-5 。

代码清单:FMC-5 FMC的GPIO初始化函数(省略了大部分数据线)¶ 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/** * @brief 初始化控制SDRAM的IO * @param 无 * @retval 无 */ static void SDRAM_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*此处省略大量地址线、数据线以及控制信号线, 它们的时钟配置都相同,具体请查看工程中的代码*/ /* 使能SDRAM相关的GPIO时钟 */ /*地址信号线*/ FMC_A0_GPIO_CLK();FMC_A1_GPIO_CLK(); FMC_A2_GPIO_CLK(); /*数据信号线*/ /*控制信号线*/ FMC_UDQM_GPIO_CLK();FMC_LDQM_GPIO_CLK(); /*--所有GPIO的配置都相同,此处省略大量引脚初始化,具体请查看工程中的代码*/ /* 通用 GPIO 配置 */ GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//配置为复用功能 GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_InitStructure.Alternate = GPIO_AF12_FMC; /*A行列地址信号线 针对引脚配置*/ GPIO_InitStructure.Pin = FMC_A0_GPIO_PIN; HAL_GPIO_Init(FMC_A0_GPIO_PORT, &GPIO_InitStructure); /*...*/ /*DQ数据信号线 针对引脚配置*/ GPIO_InitStructure.Pin = FMC_D0_GPIO_PIN; HAL_GPIO_Init(FMC_D0_GPIO_PORT, &GPIO_InitStructure); /*...*/ /*控制信号线*/ GPIO_InitStructure.Pin = FMC_CS_GPIO_PIN; HAL_GPIO_Init(FMC_CS_GPIO_PORT, &GPIO_InitStructure); /*...*/ }

与所有使用到GPIO的外设一样,都要先把使用到的GPIO引脚模式初始化,以上代码把FMC SDRAM的所有信号线全都初始化为FMC复用功能,所有引脚配置都是一样的。

配置FMC的模式

接下来需要配置FMC SDRAM的工作模式,这个函数的主体是根据硬件连接的SDRAM特性,对时序结构体以及初始化结构体进行赋值。见 代码清单:FMC-6 配置FMC的模式。

代码清单:FMC-6 配置FMC的模式¶ 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 54void SDRAM_Init(void) { FMC_SDRAM_TimingTypeDef SdramTiming; RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; /* 配置FMC接口相关的 GPIO*/ SDRAM_GPIO_Config(); /* 配置SDRAM时钟源*/ RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FMC; RCC_PeriphClkInit.PLL2.PLL2M = 5; RCC_PeriphClkInit.PLL2.PLL2N = 144; RCC_PeriphClkInit.PLL2.PLL2P = 2; RCC_PeriphClkInit.PLL2.PLL2Q = 2; RCC_PeriphClkInit.PLL2.PLL2R = 3; RCC_PeriphClkInit.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2; RCC_PeriphClkInit.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; RCC_PeriphClkInit.PLL2.PLL2FRACN = 0; RCC_PeriphClkInit.FmcClockSelection = RCC_FMCCLKSOURCE_PLL2; if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) { while (1); } /* 使能 FMC 时钟 */ __FMC_CLK_ENABLE(); /*执行SDRAM1的内存初始化序列 */ hsdram1.Instance = FMC_SDRAM_DEVICE; /* hsdram1结构体初始化*/ hsdram1.Init.SDBank = FMC_SDRAM_BANK2; hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;//SDRAM列数 hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;//SDRAM行数 hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;//总线数据宽度为16位 hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;//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;//SDRAM时钟120MHz hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; //使能突发传输模式 hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; //读通道延时 /* SDRAM时序 */ SdramTiming.LoadToActiveDelay = 2; //加载模式寄存器命令与行有效或刷新命令之间的延迟 SdramTiming.ExitSelfRefreshDelay = 8;//退出自我刷新到行有效命令之间的延迟 SdramTiming.SelfRefreshTime = 5;//行有效与预充电命令之间的延迟 SdramTiming.RowCycleDelay = 8;//两个刷新命令或两个行有效命令之间的延迟 SdramTiming.WriteRecoveryTime = 2;//写入命令到预充电命令之间的延迟 SdramTiming.RPDelay = 2;//预充电与行有效命令之间的延迟 SdramTiming.RCDDelay = 2;//行有效与列读写命令之间的延迟 HAL_SDRAM_Init(&hsdram1, &SdramTiming); /* FMC SDRAM 设备时序初始化 */ SDRAM_InitSequence(); }

这个函数的执行流程如下:

(1) 初始化GPIO引脚以及FMC时钟

函数开头调用了前面定义的SDRAM_GPIO_Config函数对FMC用到的GPIO进行初始化,使用库函数HAL_RCCEx_PeriphCLKConfig配置FMC时钟输出的来源为PLL2R, 频率为120MHz。调用__FMC_CLK_ENABLE函数使能FMC外设时钟。

(2) 时序结构体赋值

接下来对时序结构体hsdram1和SdramTiming赋值。在前面我们了解到时序结构体各个成员值的单位是同步时钟SDCLK的周期数,而根据我们使用的SDRAM芯片, 可查询得它对这些时序要求,见表 SDRAM的延时参数 。

部分时间参数以ns为单位,因此我们需要进行单位转换,而以SDCLK时钟周期数(cycle)为单位的时间参数,直接赋值到时序结构体成员里即可。

由于我们配置FMC输出的SDCLK时钟频率为PLL2R的1/2(在后面的程序里配置的),即FSDCLK=120MHz, 可得1个SDCLK时钟周期长度为TSDCLK=1/FSDCLK=8.3ns,然后设置各个成员的时候,只要保证时间大于以上SDRAM延时参数表的要求即可。 如trc要求大于60ns,而8.3ns x 8=66.4ns,所以FMC_RowCycleDelay(TRC)成员值被设置为8个时钟周期,依葫芦画瓢完成时序参数的设置。

(3) 配置FMC初始化结构体

函数接下来对FMC SDRAM的初始化结构体赋值。包括行列地址线宽度、数据线宽度、SDRAM内部Bank数量以及CL长度,这些都是根据外接的SDRAM的特性设置的, 其中CL长度要与后面初始化流程中给SDRAM模式寄存器中的赋值一致。

设置存储区域

Bank成员设置FMC的SDRAM存储区域映射选择为FMC_SDRAM_BANK2,这是由于我们的SDRAM硬件连接到FMC_CKE1和FMC_CLK1,所以对应到存储区域2;

行地址、列地址、数据线宽度及内部Bank数量

这些结构体成员都是根据SDRAM芯片的特性配置的,行地址宽度为9位,列地址宽度为13位,数据线宽度为16位,SDRAM内部有4个Bank;

CL长度

CL的长度这里被设置为2个同步时钟周期,它需要与后面SDRAM模式寄存器中的配置一样;

写保护

WriteProtection用于设置写保护,如果使能了这个功能是无法向SDRAM写入数据的,所以我们关闭这个功能;

同步时钟参数

SDClockPeriod成员被设置为FMC_SDRAM_CLOCK_PERIOD_2 ,所以同步时钟的频率就被设置为PLL2R的1/2了;

突发读模式及读延迟

为了加快读取速度,我们使能突发读功能,且读延迟周期为0;

时序参数

最后向SdramTiming赋值为前面的时序结构体,包含了我们设定的SDRAM时间参数。

赋值完成后调用库函数HAL_SDRAM_Init把初始化结构体配置的各种参数写入到FMC_SDCR控制寄存器及FMC_SDTR时序寄存器中。 函数的最后调用SDRAM_InitSequence函数实现执行SDRAM的上电初始化时序。

实现SDRAM的初始化时序

在上面配置完成STM32的FMC外设参数后,在读写SDRAM前还需要执行前面介绍的SDRAM上电初始化时序, 它就是由SDRAM_InitSequence函数实现的,见 代码清单:FMC-7 。

代码清单:FMC-7 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 57 58static void SDRAM_InitSequence(void) { uint32_t tmpr = 0; /* Step 1 ----------------------------------------------------------------*/ /* 配置命令:开启提供给SDRAM的时钟 */ Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; Command.CommandTarget = FMC_COMMAND_TARGET_BANK; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; /* 发送配置命令 */ HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT); /* Step 2: 延时100us */ SDRAM_delay(1); /* Step 3 ----------------------------------------------------------------*/ /* 配置命令:对所有的bank预充电 */ Command.CommandMode = FMC_SDRAM_CMD_PALL; Command.CommandTarget = FMC_COMMAND_TARGET_BANK; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; /* 发送配置命令 */ HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT); /* Step 4 ----------------------------------------------------------------*/ /* 配置命令:自动刷新 */ Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; Command.CommandTarget = FMC_COMMAND_TARGET_BANK; Command.AutoRefreshNumber = 8; Command.ModeRegisterDefinition = 0; /* 发送配置命令 */ HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT); /* Step 5 ----------------------------------------------------------------*/ /* 设置sdram寄存器配置 */ tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | 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_COMMAND_TARGET_BANK; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = tmpr; /* 发送配置命令 */ HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT); /* Step 6 ----------------------------------------------------------------*/ /* 设置刷新计数器 */ /* 刷新周期=64ms/8192行=7.8125us */ /* COUNT=(7.8125us x Freq) - 20 */ /* 设置自刷新速率 */ HAL_SDRAM_ProgramRefreshRate(&sdramHandle, 824); }

SDRAM的初始化流程实际上是发送一系列控制命令,利用命令结构体FMC_SDRAM_CommandTypeDef及库函数HAL_SDRAM_SendCommand配合即可发送各种命令。 函数中按次序发送了使能CLK命令、预充电命令、2个自动刷新命令以及加载模式寄存器命令。

其中发送加载模式寄存器命令时使用了一些自定义的宏,使用这些宏组合起来然后赋值到命令结构体的FMC_ModeRegisterDefinition成员中,这些宏定义见 代码清单:FMC-8 。

代码清单:FMC-8 加载模式寄存器命令相关的宏¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14/** * @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)

这些宏是根据“SDRAM的模式寄存器”的位定义的,例如突发长度、突发模式、CL长度、SDRAM工作模式以及突发写模式,其中的CL长度注意要与前面FMC SDRAN初始化结构体中定义的一致。

设置自动刷新周期

在上面SDRAM_InitSequence函数的最后,我们还调用了库函数FMC_SetRefreshCount设置FMC自动刷新周期,这个函数会向刷新定时寄存器FMC_SDRTR写入计数值, 这个计数值每个SDCLK周期自动减1,减至0时FMC会自动向SDRAM发出自动刷新命令,控制SDRAM刷新,SDRAM每次收到刷新命令后,刷新一行, 对同一行进行刷新操作的时间间隔称为SDRAM的刷新周期。

根据STM32H743xx参考手册的说明,COUNT值的计算公式如下:

刷新速率 = (COUNT + 1) x SDRAM 频率时钟

COUNT =( SDRAM 刷新周期/行数) – 20

而查询我们的SDRAM芯片规格书,可知它的SDRAM刷新周期为64ms,行数为8192,可算出它的SDRAM刷新要求:

TRefresh = 64ms/8192=7.8125us

即每隔7.8125us需要收到一次自动刷新命令。

所以:

COUNTA = TRefresh/TSDCLK=7.8125x108=844

但是根据要求,如果SDRAM在接受读请求后出现内部刷新请求,则必须将刷新速率增加 20 个 SDRAM 时钟周期以获得重充足的裕量。

最后计算出:COUNT=COUNTA-20=824。

以上就是函数FMC_SetRefreshCount参数值的计算过程。

使用指针的方式访问SDRAM存储器

完成初始化SDRAM后,我们就可以利用它存储数据了,由于SDRAM的存储空间是被映射到内核的寻址区域的,我们可以通过映射的地址直接访问SDRAM, 访问这些地址时,FMC外设自动读写SDRAM,程序上无需额外操作。

通过地址访问内存,最直接的方式就是使用C语言的指针方式了,见 代码清单:FMC-9 。

代码清单:FMC-9 使用指针的方式访问SDRAM¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19/*SDRAM起始地址 存储空间2的起始地址*/ #define SDRAM_BANK_ADDR ((uint32_t)0xD0000000) /*SDRAM大小,64M字节*/ #define W9825G6KH_SIZE 0x4000000 uint32_t temp; /*向SDRAM写入8位数据*/ *( uint8_t*) (SDRAM_BANK_ADDR ) = (uint8_t)0xAA; /*从SDRAM读取数据*/ temp = *( uint8_t*) (SDRAM_BANK_ADDR ); /*写/读 16位数据*/ *( uint16_t*) (SDRAM_BANK_ADDR+10 ) = (uint16_t)0xBBBB; temp = *( uint16_t*) (SDRAM_BANK_ADDR+10 ); /*写/读 32位数据*/ *( uint32_t*) (SDRAM_BANK_ADDR+20 ) = (uint32_t)0xCCCCCCCC; temp = *( uint32_t*) (SDRAM_BANK_ADDR+20 );

为方便使用,代码中首先定义了宏SDRAM_BANK_ADDR表示SDRAM的起始地址,该地址即FMC映射的存储区域2的首地址;宏W9825G6KH_SIZE表示SDRAM的大小, 所以从地址(SDRAM_BANK_ADDR)到(SDRAM_BANK_ADDR+W9825G6KH_SIZE)都表示在SDRAM的存储空间,访问这些地址,直接就能访问SDRAM。

配合这些宏,使用指针的强制转换以及取指针操作即可读写SDRAM的数据,使用上跟普通的变量无异。

直接指定变量存储到SDRAM空间

每次存取数据都使用指针来访问太麻烦了,为了简化操作,可以直接指定变量存储到SDRAM空间,见 代码清单:FMC-10 。

代码清单:FMC-10 直接指定变量地址的方式访问SDRAM¶ 1 2 3 4 5/*SDRAM起始地址 存储空间2的起始地址*/ #define SDRAM_BANK_ADDR ((uint32_t)0xD0000000) /*绝对定位方式访问SDRAM,这种方式必须定义成全局变量*/ uint8_t testValue __attribute__((at(SDRAM_BANK_ADDR))); testValue = 0xDD;

这种方式使用关键字“__attribute__((at()))”来指定变量的地址,代码中指定testValue存储到SDRAM的起始地址,从而实现把变量存储到SDRAM上。 要注意使用这种方法定义变量时,必须在函数外把它定义成全局变量,才可以存储到指定地址上。

更常见的是利用这种方法定义一个很大的数组,整个数组都指定到SDRAM地址上,然后就像使用malloc函数一样,用户自定义一些内存管理函数,动态地使用SDRAM的内存, 我们在使用emWin写GUI应用的时候就是这样做的。

在本书的《MDK编译过程及文件类型全解》章节将会讲解使用更简单的方法从SDRAM中分配变量,以及使用C语言标准库的malloc函数来分配SDRAM的空间,更有效地进行内存管理。

25.8.2.3. main函数¶

最后我们来编写main函数,进行SDRAM芯片读写校验,见 代码清单:FMC-11 main函数(main.c文件)。

代码清单:FMC-11 main函数¶ 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 46int main(void) { RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; /* 系统时钟初始化成480MHz */ SystemClock_Config(); LED_GPIO_Config(); /* 配置串口1为:115200 8-N-1 */ UARTx_Config(); printf("\r\n 欢迎使用野火 STM32 H743 开发板。\r\n"); printf("\r\n野火STM32H743 SDRAM 读写测试例程\r\n"); /*初始化SDRAM模块*/ SDRAM_Init(); /*蓝灯亮,表示正在读写SDRAM测试*/ LED_BLUE; /*选择PLL输出作为RNG时钟源 */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RNG; PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLL; HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); /*使能RNG时钟*/ __HAL_RCC_RNG_CLK_ENABLE(); /*初始化RNG模块产生随机数*/ hrng.Instance = RNG; HAL_RNG_Init(&hrng); printf("开始生成10000个SDRAM测试随机数\r\n"); for (count=0; count


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3