| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-07-28 | gingko | 初次建立 | \\ \\ \\ \\ =====STM32CubeMX教程十四——SDRAM实验 ===== \\ \\ \\ 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR。 {{ :icore4tx:icore4tx_cube_14_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4tx:icore4tx_cube_14_2.png?direct |}} 3.配置RCC,使用外部时钟源。 {{ :icore4tx:icore4tx_cube_14_3.png?direct |}} 4.时基源选择SysTick。 {{ :icore4tx:icore4tx_cube_14_4.png?direct |}} 5.将PA10,PB7,PB8设置为GPIO_Output。 {{ :icore4tx:icore4tx_cube_14_5.png?direct |}} 6.引脚模式配置。 {{ :icore4tx:icore4tx_cube_14_6.png?direct |}} {{ :icore4tx:icore4tx_cube_14_7.png?direct |}} 7.设置串口。 {{ :icore4tx:icore4tx_cube_14_8.png?direct |}} 8.配置FMC {{ :icore4tx:icore4tx_cube_14_9.png?direct |}} 9.在 NVIC Settings 一栏使能接收中断。 {{ :icore4tx:icore4tx_cube_14_10.png?direct |}} 10.时钟源设置,选择外部高速时钟源,配置为最大主频。 {{ :icore4tx:icore4tx_cube_14_11.png?direct |}} 11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可。IDE我们使用的是MDK V5.27。 {{ :icore4tx:icore4tx_cube_14_12.png?direct |}} 12.点击Code Generator,进行进一步配置。 {{ :icore4tx:icore4tx_cube_14_13.png?direct |}} * **Copy all used libraries into the project folder** * **将HAL库的所有.C和.H都复制到所建工程中** * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * **只复制所需要的.C和.H(推荐)** * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * **不复制文件,直接从软件包存放位置导入.C和.H** * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 13.然后点击GENERATE CODE创建工程。 {{ :icore4tx:icore4tx_cube_14_14.png?direct |}} 创建成功,打开工程。 \\ \\ ===== 实验十四:SDRAM实验——读写测试SDRAM ===== ==== 一、 实验目的与意义 ==== - 了解STM32 SDRAM结构。 - 了解STM32 SDRAM特征。 - 掌握SDRAM的使用方法。 - 掌握STM32 HAL库中SDRAM属性的配置方法。 - 掌握KEILMDK集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。 - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、 实验原理 ==== === 1.SDRAM简介 === * 同步动态随机存取内存(synchronous dynamic random-access memory,简称 SDRAM)是有一个同步接口的动态随机存取内存(DRAM)。通常 DRAM 是有一个异步接口的,这样它可以随时响应控制输入的变化。而 SDRAM 有一个同步接口,在响应控制输入前会等待一个时钟信号,这样就能和计算机的系统总线同步。时钟被用来驱动一个有限状态机,对进入的指令进行管线(Pipeline)操作。这使得 SDRAM 与没有同步接口的异 DRAM(asynchronousDRAM)相比,可以有一个更复杂的操作模式。管线意味着芯片可以在处理完之前的指令前,接受一个新的指令。在一个写入的管线中,写入命令在另一个指令执行完之后可以立刻执行,而不需要等待数据写入存储队列的时间。在一个读取的流水线中,需要的数据在读取指令发出之后固定数量的时钟频率后到达,而这个等待的过程可以发出其它附加指令。 * SDRAM 是多 Bank 结构,例如在一个具有两个 Bank 的 SDRAM 的模组中,其中一个Bank 在进行预充电期间,另一个 Bank 却马上可以被读取,这样当进行一次读取后,又马上去读取已经预充电 Bank 的数据时,就无需等待而是可以直接读取了,这也就大大提高了存储器的访问速度。为了实现这个功能,SDRAM 需要增加对多个 Bank 的管理,实现控制其中的 Bank 进行预充电。在一个具有 2 个以上 Bank 的 SDRAM 中,一般会多一根叫做 BAn的引脚,用来实现在多个 Bank 之间的选择。 * SDRAM 具有多种工作模式,内部操作是一个复杂的状态机。SDRAM 器件的引脚分为以下几类。 * (1)控制信号:包括片选、时钟、时钟使能、行列地址选择、读写有效及数据有效。 * (2)地址信号:时分复用引脚,根据行列地址选择引脚,控制输入的地址为行地址或列地址。 * (3)数据信号:双向引脚,受数据有效控制。 * SDRAM的所有操作都同步于时钟。根据时钟上升沿控制管脚和地址输入的状态,可以产生多种输入命令:模式寄存器设置命令、激活命令、预充命令、读命令、写命令、带预充的读命令、带预充的写命令、自动刷新命令、自我刷新命令、突发停命令、空操作命令。根据输入命令,SDRAM状态在内部状态间转移。内部状态包括模式寄存器设置状态、激活状态、预充状态、写状态、读状态、预充读状态、预充写状态、自动刷新状态及自我刷新状态。 * SDRAM 支持的操作命令有初始化配置、预充电、行激活、读操作、写操作、自动刷新、自刷新等。所有的操作命令通过控制线 CS#、RAS#、CAS#、WE#和地址线、体选地址BA输入。 === 2.W9825G6JB命令表 === {{ :icore4tx:icore4tx_arm_hal_14_1.png?direct |}} === 3.FMC简介 === * STM32H750使用FMC外设来管理扩展的存储器,FMC是Flexible Memory Controller的缩写,译为可变存储控制器。它可以用于驱动包括 SRAM、SDRAM、NOR FLASH以及NAND FLSAH类型的存储器。 * FMC 有 6 个存储区域,每个区域支持 256MB 的寻址空间。 * (1) 存储区域 1 可连接多达 4 个 NOR Flash 或 PSRAM 设备。此存储区域被划分为如下4个NOR/PSRAM子区域,带4个专用片选信号:存储区域 1 NOR/PSRAM 1、存储区域 1 NOR/PSRAM 2、存储区域 1 NOR/PSRAM 3、存储区域 1 NOR/PSRAM 4。 * (2) 存储区域 2 用于 SDRAM 器件,具体是 SDRAM 存储区域 1 还是 SDRAM 存储区域 2 取决于 BMAP 位配置。 * (3) 存储区域 3 用于连接 NAND Flash 器件。此空间的 MPU 存储器特性必须通过软件重新配置到器件中。 * (4) 存储区域 5 和 6 用于连接 SDRAM 器件(每个存储区域 1 个器件)。 * 对于每个存储区域,所要使用的存储器类型可由用户应用程序通过配置寄存器配置。 * 本实验使用 FMC 控制 SDRAM。启动时,必须通过用户应用程序对用于连接 FMC SDRAM 控制器与外部 SDRAM 设备的 SDRAM I/O 引脚进行配置。应用程序未使用的 SDRAM 控制器 I/O 引脚可用于其它用途。 * FMC 的存储区域如图所示: {{ :icore4tx:icore4tx_arm_hal_14_2.png?direct |}} * FMC 框图如下: {{ :icore4tx:icore4tx_arm_hal_14_3.png?direct |}} === 4.SDRAM的地址映射 === * 两个可用的 SDRAM 存储区域如图: {{ :icore4tx:icore4tx_arm_hal_14_4.png?direct |}} * 下表显示了 13 位行和 11 位列配置的 SDRAM 映射。 {{ :icore4tx:icore4tx_arm_hal_14_5.png?direct |}} * (1) 连接 16 位存储器时, FMC 内部使用 ADDR[11:1]内部地址线进行外部寻址。连接32 位存储器时,FMC 内部使用 ADDR[12:2]地址线进行外部寻址。无论外部存储器的宽度是多少,FMC_A[0]都必须连接到外部存储器地址 A[0]。 * (2) 不支持 AutoPrecharge。FMC_A[10]必须连接到外部存储器地址 A[10],但始终为低电平。 === 5.SDRAM 控制寄存器 === * 控制 SDRAM 的有 FMC_SDCR1/FMC_SDCR2 控制寄存器、FMC_SDTR1/FMC_SDTR2时序寄存器、FMC_SDCMR 命令模式寄存器以及FMC_SDRTR 刷新定时器寄存器。其中控制寄存器及时序寄存器各有 2 个,分别对应于 SDRAM 存储区域 1 和存储区域 2 的配置。 * FMC_SDCR 控制寄存器可配置 SDCLK 的同步时钟频率、突发读使能、写保护、CAS延迟、行列地址位数以及数据总线宽度等。 * FMC_SDTR 时序寄存器用于配置 SDRAM 访问时的各种时间延迟,如 TRP 行预充电延迟、TMRD 加载模式寄存器激活延迟等。 * FMC_SDCMR 命令模式寄存器用于存储要发送到 SDRAM 模式寄存器的配置,以及要向 SDRAM 芯片发送的命令。 * FMC_SDRTR 刷新定时器寄存器用于配置 SDRAM 的自动刷新周期。 === 6.原理图 === {{ :icore4tx:icore4tx_arm_hal_14_6.png?direct |}} ==== 四、 实验程序 ==== === 1.主函数 === int main(void) { int i,j; HAL_Init(); i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA] axp152.set_dcdc2(1200);//[FPGA INT] axp152.set_dcdc3(3300);//[DCOUT3] axp152.set_dcdc4(3300);//[DCOUT4] axp152.set_aldo1(3300);//[BK3] axp152.set_aldo2(3300);//[ALDOOUT2] axp152.set_dldo1(3300);//[BK0] axp152.set_dldo2(3300);//[BK1] HAL_Delay(200); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); MX_FMC_Init(); BSP_SDRAM_Init(); usart2.initialize(115200); usart2.printf("\x0c"); //清屏 usart2.printf("\033[1;32;40m"); //设置终端字体为绿色 usart2.printf("Hello,I am iCore4TX!\r\n\r\n"); //向 SDRAM 中写入 0x0000~0xFFFF 并读取校验 for(j = 0; j < 256; j++){ for(i = 0;i < 65536;i++){ write_sdram((65536 * j + i),i); } } for(j = 0; j < 256; j ++){ for(i = 0;i < 65536;i++){ if(i != read_sdram((65536 * j + i))){ usart2.printf("SDRAM ERROR!\r\n"); while(1){ //测试失败 LED 灯闪烁 LED_ON; HAL_Delay(500); LED_OFF; HAL_Delay(500); } usart2.printf("SDRAM TEST OK!\r\n"); HAL_Delay(1000); LED_ON; //测试成功 LED 灯常亮 while (1) { } } === 2.SDRAM 初始化函数 === uint8_t BSP_SDRAM_Init(void) { static uint8_t sdramstatus = SDRAM_OK; /* SDRAM 驱动配置 */ sdramHandle.Instance = FMC_SDRAM_DEVICE; Timing.LoadToActiveDelay = 2; Timing.ExitSelfRefreshDelay = 7; Timing.SelfRefreshTime = 4; Timing.RowCycleDelay = 7; Timing.WriteRecoveryTime = 2; Timing.RPDelay = 2; Timing.RCDDelay = 2; sdramHandle.Init.SDBank = FMC_SDRAM_BANK1; sdramHandle.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; sdramHandle.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; sdramHandle.Init.MemoryDataWidth = SDRAM_MEMORY_WIDTH; sdramHandle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; sdramHandle.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; sdramHandle.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; sdramHandle.Init.SDClockPeriod = SDCLOCK_PERIOD; sdramHandle.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; sdramHandle.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* SDRAM 控制器初始化 */ BSP_SDRAM_MspInit(&sdramHandle, NULL); if(HAL_SDRAM_Init(&sdramHandle, &Timing) != HAL_OK) { sdramstatus = SDRAM_ERROR; } else { /* SDRAM 初始化顺序 */ BSP_SDRAM_Initialization_sequence(REFRESH_COUNT); } return sdramstatus; } === 3.SDRAM 读写函数 === #define write_sdram(offset,data) *(volatile unsigned short int *)(SDRAM_DEVICE_ADDR + (offset << 1)) = data #define read_sdram(offset) *(volatile unsigned short int *)(SDRAM_DEVICE_ADDR + (offset << 1)) 定义 SDRAM 读写函数 uint8_t BSP_SDRAM_ReadData(uint32_t uwStartAddress, uint32_t *pData, uint32_t uwDataSize) { if(HAL_SDRAM_Read_32b(&sdramHandle, (uint32_t *)uwStartAddress, pData, uwDataSize) != HAL_OK) { return SDRAM_ERROR; } else { return SDRAM_OK; } } 函数功能:在轮询模式下从 SDRAM 存储器中读取大量数据。 uwStartAddress:读取起始地址。 pData:指向要读取的数据的指针。 uwDataSize:从存储器读取的数据的大小。 uint8_t BSP_SDRAM_WriteData(uint32_t uwStartAddress, uint32_t *pData,uint32_t uwDataSize) { if(HAL_SDRAM_Write_32b(&sdramHandle, (uint32_t *)uwStartAddress, pData, uwDataSize) != HAL_OK) { return SDRAM_ERROR; } else { return SDRAM_OK; } } 功能介绍:在轮询模式下将大量数据写入 SDRAM 存储器。 uwStartAddress:写入起始地址。 pData:指向要写入数据的指针。 uwDataSize:向存储器写入的数据大小。 uint8_t BSP_SDRAM_Sendcmd(FMC_SDRAM_CommandTypeDef *SdramCmd) { if(HAL_SDRAM_SendCommand(&sdramHandle, SdramCmd, SDRAM_TIMEOUT) != HAL_OK) { return SDRAM_ERROR; } else { return SDRAM_OK; } } 功能介绍:向 SDRAM bank 发送命令。 SdramCmd:指向 SDRAM 命令结构的指针 === 4.FMC初始化函数 === void MX_FMC_Init(void) { //本实验中我们只用到了 FMC 的引脚,时序配置使用官方提供的 SDRAM 驱动 FMC_SDRAM_TimingTypeDef SdramTiming = {0}; /* 执行 SDRAM1 存储器初始化序列 */ hsdram1.Instance = FMC_SDRAM_DEVICE; /* hsdram1 初始化 */ hsdram1.Init.SDBank = FMC_SDRAM_BANK1; hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; 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_1; hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_DISABLE; hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE; hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0; /* Sdram 时序 */ SdramTiming.LoadToActiveDelay = 16; SdramTiming.ExitSelfRefreshDelay = 16; SdramTiming.SelfRefreshTime = 16; SdramTiming.RowCycleDelay = 16; SdramTiming.WriteRecoveryTime = 16; SdramTiming.RPDelay = 16; SdramTiming.RCDDelay = 16; if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK) { Error_Handler( ); } } ==== 五、 实验步骤 ==== - 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4TX通过Micro USB线与计算机相连,为iCore4TX供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4TX上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== SDRAM 读写测试成功,LED 常亮,并在终端显示出“SDRAM TEST OK!”。测试失败 LED 灯闪烁,并在终端显示“SDRAM ERROR!” {{ :icore4tx:icore4tx_arm_hal_14_7.png?direct |}}