欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > STM32——“扩展动态随机存储器SDRAM”

STM32——“扩展动态随机存储器SDRAM”

2025/6/13 16:06:13 来源:https://blog.csdn.net/2301_79972085/article/details/148615371  浏览:    关键词:STM32——“扩展动态随机存储器SDRAM”

引入

        随着单片机的性能提高,我们想要在单片机上实现的功能有时需要外扩彩色屏幕,但是单片机的内存太小,而屏幕需要的缓存又太大,因此扩展出了外置的RAM,RAM又分为静态随机存储器和动态随机存储器,在这里我介绍动态随机存储器SDRAM在STM32上的配置。

一、SDRAM简介

        SDRAM是动态随机存储器(Synchronous Dynamic Random-Access Memory)的简称,相比较SRAM,他的读写速率可能没有SRAM那么快,因为需要留时间给刷新,但是他的造价低,容量一般可以做的很大。本章节用的是基于野火的STM32F429-V1开发板。

二、SDRAM在STM32上的配置

        SDRAM的使用需要用到FMC,即“可变存储控制器”,本章节以STM32F429IGT6为例(FMC在F4系列中,支持的型号仅仅只有“STM32F42xxx 和 STM32F43xxx”),且在STM32F4的中文草考手册中没有,反而在另外一个F4的参考手册中提及。、

        本章节代码采用HAL库,要用FMC驱动SDRAM就得进行初始化。

        初始化代码大概分为以下步骤:

        1.初始化GPIO,开启时钟,并且复用为FMC

        GPIO的初始化可以由CubeMX配置,因为引脚很多,这里配置BANK2。

static void SDRAM_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* Peripheral clock enable */__HAL_RCC_FMC_CLK_ENABLE();__HAL_RCC_GPIOF_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOG_CLK_ENABLE();__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/* GPIO_InitStruct *//** FMC GPIO ConfigurationPF0   ------> FMC_A0PF1   ------> FMC_A1PF2   ------> FMC_A2PF3   ------> FMC_A3PF4   ------> FMC_A4PF5   ------> FMC_A5PC0   ------> FMC_SDNWEPF11   ------> FMC_SDNRASPF12   ------> FMC_A6PF13   ------> FMC_A7PF14   ------> FMC_A8PF15   ------> FMC_A9PG0   ------> FMC_A10PG1   ------> FMC_A11PE7   ------> FMC_D4PE8   ------> FMC_D5PE9   ------> FMC_D6PE10   ------> FMC_D7PE11   ------> FMC_D8PE12   ------> FMC_D9PE13   ------> FMC_D10PE14   ------> FMC_D11PE15   ------> FMC_D12PH6   ------> FMC_SDNE1PH7   ------> FMC_SDCKE1PD8   ------> FMC_D13PD9   ------> FMC_D14PD10   ------> FMC_D15PD14   ------> FMC_D0PD15   ------> FMC_D1PG4   ------> FMC_BA0PG5   ------> FMC_BA1PG8   ------> FMC_SDCLKPD0   ------> FMC_D2PD1   ------> FMC_D3PG15   ------> FMC_SDNCASPE0   ------> FMC_NBL0PE1   ------> FMC_NBL1*/GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);}

        2.初始化SDRAM控制器

        SDRAM控制器的相关参数可以在手册中找到:

    SDRAM_HandleTypeDef hsdram2;FMC_SDRAM_TimingTypeDef SdramTiming = {0};hsdram2.Instance = FMC_SDRAM_DEVICE;/* hsdram2.Init */hsdram2.Init.SDBank = FMC_SDRAM_BANK2;									// 初始化 BANK2hsdram2.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;			// 列地址位 8hsdram2.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;					//行地址位   12hsdram2.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;				//数据宽度 16位hsdram2.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;			//SDRAM的BANK的数量 4hsdram2.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;						//CAS延时 3 个周期hsdram2.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;		//失能写保护hsdram2.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;					//SDRAM时钟分频 2 分频 180/2 < 143hsdram2.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;						//使能读突发hsdram2.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;					//管道延时 0/* SdramTiming */SdramTiming.LoadToActiveDelay = 2;											//退出自刷新延时 tXSRSdramTiming.SelfRefreshTime = 5;									//加载模式寄存器到激活 tMRDSdramTiming.ExitSelfRefreshDelay = 7;									//自刷新延时tRASSdramTiming.RowCycleDelay = 7;											//行循环延时 tRCSdramTiming.WriteRecoveryTime = 3;										//写恢复时间 tWRSdramTiming.RPDelay = 2;												//行预充电延时 tRPSdramTiming.RCDDelay = 2;												//行到列延时 tRCDHAL_SDRAM_Init(&hsdram2, &SdramTiming);//初始化SDRAM外设

        此图的Row Address 代表行地址引脚,一共12个,Column Address代表列地址的引脚,一共8个他们是分时复用的,第一个参数页提示了这个SDRAM有四个bank,以及数据宽度。

        图一显示的是不同芯片的最大时钟以及其他配置选项,429的始终是180MHz,而核心板的芯片是-7,因此最大频率不超过143(CAS为3,这里配置为3),因此配置 :

hsdram2.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;   //SDRAM时钟分频 2 分频 180/2 < 143 

       90MHz对应的时间大致为11ns

        此外的其他配置,如写保护给失能,管道延时(跟在CAS延时后边)为0,并且使能突发读。

此外还需配置“7个时间”,用来配合SDRAM控制器和SDRAM的通信。

        1.退出自刷新时间 tXSR

他在手册中定义如下(从左到右依次是-5 -6 -7):

        这里为不小于70ns,那就配置为7

SdramTiming.ExitSelfRefreshDelay = 7;

        2.加载模式寄存器到激活时间 tMRD

这里为两个时钟周期,那么就为2

SdramTiming.LoadToActiveDelay = 2;

        3.自刷新时间tRAS

这里为不小于42ns,就填为5

SdramTiming.SelfRefreshTime = 5;

        4.行循环延时 tRC

        这里最小为63ns,则配置为7

SdramTiming.RowCycleDelay = 7;	

        5.写恢复时间 tWR

        这里最小为两个时钟,则配置为3(满足某个特定条件: tWR >= tRAS - tRCD)

SdramTiming.WriteRecoveryTime = 3;

        6.行预充电延时 tRP

这里不低于15ns,则配置为2

SdramTiming.RPDelay = 2;

        7.行到列延时 tRCD

这里不低于15ns,则配置为2

SdramTiming.RCDDelay = 2;

最后初始化SDRAM控制器

        3.发送控制命令到SDRAM

发送命令由函数HAL_SDRAM_SendCommand完成

                a.给 SDRAM 提供时钟

                b.延时至少100us

                c.给 SDRAM 的所有 BANK 预充电

                d.插入8个自动刷新周期

                e.配置加载模式寄存器

                f.配置 SDRAM 自动刷新周期

发送命令代码如下:


static void SDRAM_Cmmand(void)
{FMC_SDRAM_CommandTypeDef FMC_SDRAM_CommandStruct = {0};//1 给 SDRAM 提供时钟FMC_SDRAM_CommandStruct.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;			//启用时钟FMC_SDRAM_CommandStruct.AutoRefreshNumber = 1;							//自动刷新数(无关命令,写默认值FMC_SDRAM_CommandStruct.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;		//发送到BANK2FMC_SDRAM_CommandStruct.ModeRegisterDefinition = 0;						//加载模式寄存器值(无关命令,写默认值HAL_SDRAM_SendCommand(&hsdram2,&FMC_SDRAM_CommandStruct,TIME_OUT);
//2 延时至少100usHAL_Delay(1);//3 给 SDRAM 的所有 BANK 预充电FMC_SDRAM_CommandStruct.CommandMode = FMC_SDRAM_CMD_PALL;				//预充电FMC_SDRAM_CommandStruct.AutoRefreshNumber = 1;							//自动刷新数(无关命令,写默认值FMC_SDRAM_CommandStruct.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;		//发送到BANK2FMC_SDRAM_CommandStruct.ModeRegisterDefinition = 0;						//加载模式寄存器值(无关命令,写默认值HAL_SDRAM_SendCommand(&hsdram2,&FMC_SDRAM_CommandStruct,TIME_OUT);//4 插入8个自动刷新周期FMC_SDRAM_CommandStruct.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;	//自动刷新FMC_SDRAM_CommandStruct.AutoRefreshNumber = 4;							//自动刷新数FMC_SDRAM_CommandStruct.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;		//发送到BANK2FMC_SDRAM_CommandStruct.ModeRegisterDefinition = 0;						//加载模式寄存器值(无关命令,写默认值HAL_SDRAM_SendCommand(&hsdram2,&FMC_SDRAM_CommandStruct,TIME_OUT);
//5 配置加载模式寄存器uint32_t ModeRegister = SDRAM_MODEREG_BURST_LENGTH_4 \| SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL\| SDRAM_MODEREG_CAS_LATENCY_3\| SDRAM_MODEREG_OPERATING_MODE_STANDARD\| SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;FMC_SDRAM_CommandStruct.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;			//加载模式寄存器FMC_SDRAM_CommandStruct.AutoRefreshNumber = 1;							//自动刷新数(无关命令,写默认值FMC_SDRAM_CommandStruct.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;		//发送到BANK2FMC_SDRAM_CommandStruct.ModeRegisterDefinition = ModeRegister;			//加载模式寄存器值HAL_SDRAM_SendCommand(&hsdram2,&FMC_SDRAM_CommandStruct,TIME_OUT);//6 配置 SDRAM 自动刷新周期  HAL_SDRAM_ProgramRefreshRate(&hsdram2,1386); 							//(64x10^-6 s /4096) x 90x10^6Hz - 20}

                其中,初始阿虎过程可以在手册中找到:

翻译大致如下(可能不准确,以英文原文为主)

初始化(Initialization)

SDRAM 必须按照预定义的方式上电并初始化。

        当电源同时加到 VDD 和 VDDQ上,并且时钟稳定(DQM 为高电平,CKE 为高电平)之后,64Mb 的 SDRAM 才会开始初始化。

        在发送除 COMMAND INHIBIT 或 NOP 命令之外的任何命令之前,需要等待 100 微秒。在这 100 微秒期间,可以持续发送 COMMAND INHIBIT 或 NOP 命令,并应至少持续到 100 微秒结束。

        在至少发送一次 COMMAND INHIBIT 或 NOP 命令之后,一旦 100 微秒延时完成,就应发送 PRECHARGE 命令,所有的 bank 必须被预充电(Precharged)。这将使所有 bank 进入空闲状态。

        随后,至少执行两次自动刷新(AUTO REFRESH)命令。自动刷新命令完成之后,SDRAM 就可以准备好进行模式寄存器(Mode Register)的设置。

        在执行任何操作命令之前,应先加载模式寄存器,因为上电后 SDRAM 处于未知状态。执行完 Load Mode Register 命令之后,至少要执行一次 NOP 命令,然后才可以执行其他命令。

        总结起来和以上配置并无二异,不过最后还得配置 SDRAM 自动刷新周期 ,他的计算公式如下图(手册中):

由SDRAM手册可知:

        行数为4096行,SDRAM的时钟已经被设置为90MHz,刷新4096次需要64ms

那么最后的结果就是:

(64x10^-6 s /4096) x 90x10^6Hz - 20 = 1386

整个初始化代码就是:

void SDRAM_Init(void)
{SDRAM_GPIO_Init();//初始化GPIOFMC_SDRAM_TimingTypeDef SdramTiming = {0};hsdram2.Instance = FMC_SDRAM_DEVICE;/* hsdram2.Init */hsdram2.Init.SDBank = FMC_SDRAM_BANK2;									// 初始化 BANK2hsdram2.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;			// 列地址位 8hsdram2.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;					//行地址位   12hsdram2.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;				//数据宽度 16位hsdram2.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;			//SDRAM的BANK的数量 4hsdram2.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;						//CAS延时 3 个周期hsdram2.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;		//失能写保护hsdram2.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;					//SDRAM时钟分频 2 分频 180/2 < 133hsdram2.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;						//使能读突发hsdram2.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;					//管道延时 0/* SdramTiming */SdramTiming.LoadToActiveDelay = 2;										//加载模式寄存器到激活 tMRDSdramTiming.ExitSelfRefreshDelay = 7;									//退出自刷新延时 tXSRSdramTiming.SelfRefreshTime = 5;										//自刷新延时tRASSdramTiming.RowCycleDelay = 7;											//行循环延时 tRCSdramTiming.WriteRecoveryTime = 3;										//写恢复时间 tWRSdramTiming.RPDelay = 2;												//行预充电延时 tRPSdramTiming.RCDDelay = 2;												//行到列延时 tRCDHAL_SDRAM_Init(&hsdram2, &SdramTiming);//初始化SDRAM外设SDRAM_Cmmand();//初始化SDRAM
}

        在主函数调用,即可通过指针访问SDRAM,由于这里是用的BANK2,因此它的起始地址是0xD0000000 。

        在主函数中将一张150kb的图片复制到SDRAM,然后读出来显示在屏幕中:

int main()
{HAL_Init();SystemClock_Config();UsartInit(115200);SDRAM_Init();SPI_LCD_Init();LCD_SetColor(COLOR_LIME);LCD_SetBackColor(COLOR_BLACK);LCD_Clear();LCD_SetDirection(Direction_H_Flip);LCD_SetTextFont(&CH_Font32);uint32_t* add1 = (uint32_t*)0xD0000000;memcpy(add1,gImage_cat,153600);LCD_DrawColorImage(0,0,320,240,(uint8_t*)add1);while(1){}
}

        最后的结果:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词