023 - STM32学习笔记 - 扩展外部SDRAM(二) - 扩展外部SDRAM实验
023- STM32学习笔记 - 扩展外部SDRAM(一) - 扩展外部SDRAM实验
本节内容中要配置的引脚很多,如果你用的开发板跟我的不一样,请详细参照STM32规格书中说明对相关GPIO引脚进行配置。
先提前对本届内容的变成步骤进行总结如下:
- 初始化通讯使用的目标引脚及端口时钟;(再次强调,只要使用外设,一定要看时钟时候配置正确并开启!)
- 是能FMC外设时钟;(再再次强调,只要使用外设,一定要看时钟时候配置正确并开启!)
- 配置FMC SDRAM的时序和工作模式;
- 根据SDRAM的初始化流程编写初始化函数;
- 访问外部SDRAM存储器;
- 编写测试程序,校验读写的数据。
OK,参照如上步骤,实战之前先将我用的F429开发板中SDRAM部分贴出来。

一、相关GPIO宏定义
这次使用到的GPIO相当多,这里我们把FMC SDRAM相关的GPIO配置都宏定义到“bsp_sdram.h”中,相关的配置步骤参考之前的工程配置。这里我把我配置好的贴出来,如果我们使用的开发板不一致,请参考自己开发板的硬件原理图。
/* A行列地址信号线 */
/* A0 PF0 */
#define FMC_A0_GPIO_PORT GPIOF
#define FMC_A0_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A0_GPIO_PIN GPIO_Pin_0
#define FMC_A0_PINSOURCE GPIO_PinSource0
#define FMC_A0_AF GPIO_AF_FMC/* A1 PF1 */
#define FMC_A1_GPIO_PORT GPIOF
#define FMC_A1_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A1_GPIO_PIN GPIO_Pin_1
#define FMC_A1_PINSOURCE GPIO_PinSource1
#define FMC_A1_AF GPIO_AF_FMC/* A2 PF2 */
#define FMC_A2_GPIO_PORT GPIOF
#define FMC_A2_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A2_GPIO_PIN GPIO_Pin_2
#define FMC_A2_PINSOURCE GPIO_PinSource2
#define FMC_A2_AF GPIO_AF_FMC/* A3 PF3 */
#define FMC_A3_GPIO_PORT GPIOF
#define FMC_A3_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A3_GPIO_PIN GPIO_Pin_3
#define FMC_A3_PINSOURCE GPIO_PinSource3
#define FMC_A3_AF GPIO_AF_FMC/* A4 PF4 */
#define FMC_A4_GPIO_PORT GPIOF
#define FMC_A4_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A4_GPIO_PIN GPIO_Pin_4
#define FMC_A4_PINSOURCE GPIO_PinSource4
#define FMC_A4_AF GPIO_AF_FMC/* A5 PF5*/
#define FMC_A5_GPIO_PORT GPIOF
#define FMC_A5_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A5_GPIO_PIN GPIO_Pin_5
#define FMC_A5_PINSOURCE GPIO_PinSource5
#define FMC_A5_AF GPIO_AF_FMC/* A6 PF12 */
#define FMC_A6_GPIO_PORT GPIOF
#define FMC_A6_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A6_GPIO_PIN GPIO_Pin_12
#define FMC_A6_PINSOURCE GPIO_PinSource12
#define FMC_A6_AF GPIO_AF_FMC/* A7 PF13 */
#define FMC_A7_GPIO_PORT GPIOF
#define FMC_A7_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A7_GPIO_PIN GPIO_Pin_13
#define FMC_A7_PINSOURCE GPIO_PinSource13
#define FMC_A7_AF GPIO_AF_FMC/* A8 PF14 */
#define FMC_A8_GPIO_PORT GPIOF
#define FMC_A8_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A8_GPIO_PIN GPIO_Pin_14
#define FMC_A8_PINSOURCE GPIO_PinSource14
#define FMC_A8_AF GPIO_AF_FMC/* A9 PF15 */
#define FMC_A9_GPIO_PORT GPIOF
#define FMC_A9_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_A9_GPIO_PIN GPIO_Pin_15
#define FMC_A9_PINSOURCE GPIO_PinSource15
#define FMC_A9_AF GPIO_AF_FMC/* A10 PG0 */
#define FMC_A10_GPIO_PORT GPIOG
#define FMC_A10_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_A10_GPIO_PIN GPIO_Pin_0
#define FMC_A10_PINSOURCE GPIO_PinSource0
#define FMC_A10_AF GPIO_AF_FMC/* A11 PG1 */
#define FMC_A11_GPIO_PORT GPIOG
#define FMC_A11_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_A11_GPIO_PIN GPIO_Pin_1
#define FMC_A11_PINSOURCE GPIO_PinSource1
#define FMC_A11_AF GPIO_AF_FMC/*BA0 地址线 PG4*/
#define FMC_BA0_GPIO_PORT GPIOG
#define FMC_BA0_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_BA0_GPIO_PIN GPIO_Pin_4
#define FMC_BA0_PINSOURCE GPIO_PinSource4
#define FMC_BA0_AF GPIO_AF_FMC/*BA1 地址线 PG5 */
#define FMC_BA1_GPIO_PORT GPIOG
#define FMC_BA1_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_BA1_GPIO_PIN GPIO_Pin_5
#define FMC_BA1_PINSOURCE GPIO_PinSource5
#define FMC_BA1_AF GPIO_AF_FMC/*DQ 数据信号线*/
/*DQ0 数据线 PD14 */
#define FMC_D0_GPIO_PORT GPIOD
#define FMC_D0_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D0_GPIO_PIN GPIO_Pin_14
#define FMC_D0_PINSOURCE GPIO_PinSource14
#define FMC_D0_AF GPIO_AF_FMC/*DQ1 数据线*/
#define FMC_D1_GPIO_PORT GPIOD
#define FMC_D1_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D1_GPIO_PIN GPIO_Pin_15
#define FMC_D1_PINSOURCE GPIO_PinSource15
#define FMC_D1_AF GPIO_AF_FMC/*DQ2 数据线*/
#define FMC_D2_GPIO_PORT GPIOD
#define FMC_D2_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D2_GPIO_PIN GPIO_Pin_0
#define FMC_D2_PINSOURCE GPIO_PinSource0
#define FMC_D2_AF GPIO_AF_FMC/*DQ3 数据线*/
#define FMC_D3_GPIO_PORT GPIOD
#define FMC_D3_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D3_GPIO_PIN GPIO_Pin_1
#define FMC_D3_PINSOURCE GPIO_PinSource1
#define FMC_D3_AF GPIO_AF_FMC/*DQ4 数据线*/
#define FMC_D4_GPIO_PORT GPIOE
#define FMC_D4_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D4_GPIO_PIN GPIO_Pin_7
#define FMC_D4_PINSOURCE GPIO_PinSource7
#define FMC_D4_AF GPIO_AF_FMC/*DQ5 数据线*/
#define FMC_D5_GPIO_PORT GPIOE
#define FMC_D5_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D5_GPIO_PIN GPIO_Pin_8
#define FMC_D5_PINSOURCE GPIO_PinSource8
#define FMC_D5_AF GPIO_AF_FMC/*DQ6 数据线*/
#define FMC_D6_GPIO_PORT GPIOE
#define FMC_D6_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D6_GPIO_PIN GPIO_Pin_9
#define FMC_D6_PINSOURCE GPIO_PinSource9
#define FMC_D6_AF GPIO_AF_FMC/*DQ7 数据线*/
#define FMC_D7_GPIO_PORT GPIOE
#define FMC_D7_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D7_GPIO_PIN GPIO_Pin_10
#define FMC_D7_PINSOURCE GPIO_PinSource10
#define FMC_D7_AF GPIO_AF_FMC/*DQ8 数据线*/
#define FMC_D8_GPIO_PORT GPIOE
#define FMC_D8_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D8_GPIO_PIN GPIO_Pin_11
#define FMC_D8_PINSOURCE GPIO_PinSource11
#define FMC_D8_AF GPIO_AF_FMC/*DQ9 数据线*/
#define FMC_D9_GPIO_PORT GPIOE
#define FMC_D9_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D9_GPIO_PIN GPIO_Pin_12
#define FMC_D9_PINSOURCE GPIO_PinSource12
#define FMC_D9_AF GPIO_AF_FMC/*DQ10 数据线*/
#define FMC_D10_GPIO_PORT GPIOE
#define FMC_D10_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D10_GPIO_PIN GPIO_Pin_13
#define FMC_D10_PINSOURCE GPIO_PinSource13
#define FMC_D10_AF GPIO_AF_FMC/*DQ11 数据线*/
#define FMC_D11_GPIO_PORT GPIOE
#define FMC_D11_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D11_GPIO_PIN GPIO_Pin_14
#define FMC_D11_PINSOURCE GPIO_PinSource14
#define FMC_D11_AF GPIO_AF_FMC/*DQ12 数据线*/
#define FMC_D12_GPIO_PORT GPIOE
#define FMC_D12_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_D12_GPIO_PIN GPIO_Pin_15
#define FMC_D12_PINSOURCE GPIO_PinSource15
#define FMC_D12_AF GPIO_AF_FMC/*DQ13 数据线*/
#define FMC_D13_GPIO_PORT GPIOD
#define FMC_D13_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D13_GPIO_PIN GPIO_Pin_8
#define FMC_D13_PINSOURCE GPIO_PinSource8
#define FMC_D13_AF GPIO_AF_FMC/*DQ14 数据线*/
#define FMC_D14_GPIO_PORT GPIOD
#define FMC_D14_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D14_GPIO_PIN GPIO_Pin_9
#define FMC_D14_PINSOURCE GPIO_PinSource9
#define FMC_D14_AF GPIO_AF_FMC/*DQ15 数据线*/
#define FMC_D15_GPIO_PORT GPIOD
#define FMC_D15_GPIO_CLK RCC_AHB1Periph_GPIOD
#define FMC_D15_GPIO_PIN GPIO_Pin_10
#define FMC_D15_PINSOURCE GPIO_PinSource10
#define FMC_D15_AF GPIO_AF_FMC/*控制信号线*/
/*CS 片选*/
#define FMC_CS_GPIO_PORT GPIOH
#define FMC_CS_GPIO_CLK RCC_AHB1Periph_GPIOH
#define FMC_CS_GPIO_PIN GPIO_Pin_6
#define FMC_CS_PINSOURCE GPIO_PinSource6
#define FMC_CS_AF GPIO_AF_FMC/*WE 写使能*/
#define FMC_WE_GPIO_PORT GPIOC
#define FMC_WE_GPIO_CLK RCC_AHB1Periph_GPIOC
#define FMC_WE_GPIO_PIN GPIO_Pin_0
#define FMC_WE_PINSOURCE GPIO_PinSource0
#define FMC_WE_AF GPIO_AF_FMC/*RAS 行选通*/
#define FMC_RAS_GPIO_PORT GPIOF
#define FMC_RAS_GPIO_CLK RCC_AHB1Periph_GPIOF
#define FMC_RAS_GPIO_PIN GPIO_Pin_11
#define FMC_RAS_PINSOURCE GPIO_PinSource11
#define FMC_RAS_AF GPIO_AF_FMC/*CAS 列选通*/
#define FMC_CAS_GPIO_PORT GPIOG
#define FMC_CAS_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_CAS_GPIO_PIN GPIO_Pin_15
#define FMC_CAS_PINSOURCE GPIO_PinSource15
#define FMC_CAS_AF GPIO_AF_FMC/*CLK 同步时钟,存储区域 2*/
#define FMC_CLK_GPIO_PORT GPIOG
#define FMC_CLK_GPIO_CLK RCC_AHB1Periph_GPIOG
#define FMC_CLK_GPIO_PIN GPIO_Pin_8
#define FMC_CLK_PINSOURCE GPIO_PinSource8
#define FMC_CLK_AF GPIO_AF_FMC/*CKE 时钟使能,存储区域 2*/
#define FMC_CKE_GPIO_PORT GPIOH
#define FMC_CKE_GPIO_CLK RCC_AHB1Periph_GPIOH
#define FMC_CKE_GPIO_PIN GPIO_Pin_7
#define FMC_CKE_PINSOURCE GPIO_PinSource7
#define FMC_CKE_AF GPIO_AF_FMC/*DQM1 数据掩码*/
#define FMC_UDQM_GPIO_PORT GPIOE
#define FMC_UDQM_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_UDQM_GPIO_PIN GPIO_Pin_1
#define FMC_UDQM_PINSOURCE GPIO_PinSource1
#define FMC_UDQM_AF GPIO_AF_FMC/*DQM0 数据掩码*/
#define FMC_LDQM_GPIO_PORT GPIOE
#define FMC_LDQM_GPIO_CLK RCC_AHB1Periph_GPIOE
#define FMC_LDQM_GPIO_PIN GPIO_Pin_0
#define FMC_LDQM_PINSOURCE GPIO_PinSource0
#define FMC_LDQM_AF GPIO_AF_FMC
这里需要注意的是,我们在原理图上可以看到,FMC_SDCKE和FMC_SDNE我们都选择的是1,那说明SDRAM在内存中的映射为FMC_Block2,起始地址为0xD000 0000,SDRAM的大小为8M,因此结束地址为0xD080 0000。
二、FMC及SDRAM配置
1、时钟周期配置
关于时钟周期配置,可以看一下SDRAM的数据手册,输入的时钟为HCLK(180MHz)的2分频为90MHz,因此一个时钟周期为1/90MHz≈11.11ns。
a、TMRD:数据手册中参数要求为2个周期;
b、TXSR:最小时间要求为70ns,计算得为约7个周期;
c、TRAS:要求最小时间42ns,最大100000ns,计算得为最小4个周期;
d、TRC:要求最小63ns,计算得最小为6个周期;
e、TWR:CAS Latency选择为2时,周期为2;
f、TRP:要求最小为15ns,计算得为2个周期;
g、TRCD:要求最小为15ns,计算得为2个周期;
/* SDCLK: 90 Mhz (HCLK/2 :180Mhz/2) 1 个时钟周期 Tsdclk =1/90MHz=1/90000000Hz=11.11ns*/
/* TMRD: 2个时钟周期 */
FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2;
/* TXSR: min=70ns (70/11.11=6.03个周期) 即为7个周期*/
FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7;
/* TRAS: min=42ns (42/11.11=3.7个周期) max=100k (ns)因此最小可配置为4个周期 */
FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;
/* TRC: min=63ns (63/11.11=5.67个周期) 即为6个周期 */
FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 6;
/* TWR: 最小为2个周期 */
FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;
/* TRP: 15ns (15/11.11=1.35个周期) 即为2个周期 */
FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2;
/* TRCD: 15ns (15/11.11=1.35个周期) 即为2个周期 */
FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;
2、FMC控制配置
/* FMC SDRAM 控制配置 */
/* 选择存储区域为FMC_Bank2_SDRAM */
FMC_SDRAMInitStructure.FMC_Bank = FMC_BANK_SDRAM;
/* 行地址线宽度: [7:0]共8位 */
FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b;
/* 列地址线宽度: [11:0]共12位 */
FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b;
/* 数据线宽度位16 */
FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;
/* 设置SDRAM 内部bank数量为4 */
FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
/* CAS 潜伏期 */
FMC_SDRAMInitStructure.FMC_CASLatency = SDRAM_CAS_LATENCY;
/* 禁止写保护*/
FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
/* SDCLK 时钟分频因子, SDCLK = HCLK/SDCLOCK_PERIOD*/
FMC_SDRAMInitStructure.FMC_SDClockPeriod = SDCLOCK_PERIOD;
/* 突发读模式设置*/
FMC_SDRAMInitStructure.FMC_ReadBurst = SDRAM_READBURST;
/* 读延迟配置 */
FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_0;
/* SDRAM 时序参数 */
FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct =&FMC_SDRAMTimingInitStructure;
/* 调用初始化函数,向寄存器写入配置 */
FMC_SDRAMInit(&FMC_SDRAMInitStructure);
/* 执行FMC SDRAM的初始化流程*/
SDRAM_InitSequence(); //函数在下面实现
3、初始化SDRAM
static void SDRAM_InitSequence(void)
{FMC_SDRAMCommandTypeDef FMC_SDRAMCommandStructure;uint32_t tmpr = 0;/* 配置命令:开启提供给 SDRAM 的时钟 *///下发使能 CLK 命令FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled;//设置FMC内部存储区域2FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;//设置FMC自动刷新次数,前面未下发自动刷新,此项任意值即可。FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 0;//此时不下发加载模式寄存器,配置为任意值即可FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;/* 检查 SDRAM 标志,等待至 SDRAM 空闲 */while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);/* 发送上述命令*/FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);/*延时 */SDRAM_delay(10);/* 配置命令:对所有的 bank 预充电 *///发送对所有Bank预充电命令FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL;//设置FMC内部存储区域2FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;//设置FMC自动刷新次数,前面未下发自动刷新,此项任意值即可。FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 0;//此时不下发加载模式寄存器,配置为任意值即可FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;/* 检查 SDRAM 标志,等待至 SDRAM 空闲 */while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);/* 发送上述命令*/FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);/*延时 */SDRAM_delay(10);/* 配置命令:自动刷新 *///发送自动刷新命令FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh;//设置FMC内部存储区域2FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;//设置FMC自动刷新次数,这里设置为2次FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 2;//此时不下发加载模式寄存器,配置为任意值即可FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;/* 检查 SDRAM 标志,等待至 SDRAM 空闲 */while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);/* 发送自动刷新命令*/FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);/*延时 */SDRAM_delay(10);/* 设置 sdram 加载模式寄存器配置 */tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_4 | //突发长度设置为4SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | //突发模式为顺序模式SDRAM_MODEREG_CAS_LATENCY_2 | //列选通延迟为2个周期SDRAM_MODEREG_OPERATING_MODE_STANDARD | //工作模式为正常模式SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED; ///* 配置命令:设置 SDRAM 寄存器 */FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode;FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_COMMAND_TARGET_BANK;FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 0;FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr;/* 检查 SDRAM 标志,等待至 SDRAM 空闲 */while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);/* 发送上述命令*/FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);/*延时 */SDRAM_delay(10);/* 设置刷新计数器 *//*刷新速率 = (COUNT + 1) x SDRAM 频率时钟COUNT =( SDRAM 刷新周期/行数) - 20*//* 64ms/4096=15.62us (15.62 us x FSDCLK) - 20 =1386 */FMC_SetRefreshCount(1386);/* 发送上述命令*/while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);
}
到这里,已经可以实现SDRAM的读写了,这里我们写个测试程序,来实验一下上面的劳动成果。
三、测试
前面我们将SDRAM映射到FMC_Block2上了,起始地址为0xD0000000,这里我们将要用到的几个参数都做宏定义
#define SDRAM_SIZE 0x800000 //400000*16bits = 0x800000 ,8M字节
/*SDRAM 的bank选择*/
#define FMC_BANK_SDRAM FMC_Bank2_SDRAM
#define FMC_COMMAND_TARGET_BANK FMC_Command_Target_bank2/* 这里将SDRAM挂载在FMC_Block2这里,起始地址为0xD000 0000 */
#define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)/* FMC SDRAM 数据宽度 */
#define SDRAM_MEMORY_WIDTH FMC_SDMemory_Width_16b /* FMC SDRAM CAS Latency */
#define SDRAM_CAS_LATENCY FMC_CAS_Latency_2 /* FMC SDRAM SDCLK时钟分频因子 */
#define SDCLOCK_PERIOD FMC_SDClock_Period_2 /* FMC SDRAM 突发读取特性 */
#define SDRAM_READBURST FMC_Read_Burst_Enable
映射之后,向SDRAM中写入读取数据就可以用指针的方式进行操作,如下:
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_usart_dma.h"
#include "bsp_sdram.h"
#include <stdio.h>
uint16_t read_data;
int main(void)
{DEBUG_USART1_Config();LED_Config();printf("\r\n这是测试扩展外部SDRAM的例程实验\r\n");/*初始化SDRAM模块*/SDRAM_Init();*(uint16_t*)(SDRAM_BANK_ADDR) = 0xFEFE;read_data = *( uint16_t*) SDRAM_BANK_ADDR;printf("\r\n读取到的数据为:0x%x\r\n",read_data);while(1){}
}
这里需要注意,虽然我们通过指针将数据存放到SDRAM中了,但是实际在读取的时候,定义的变量read_data还是存放在sdram中,双击工程名,打开map文件,查看read_data会发现,系统分配的地址还是在内部SRAM中。

如果想将变量也定义到SDRAM中的话,需要进行强制指定将变量分配到SDRAM中,方法如下:
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_usart_dma.h"
#include "bsp_sdram.h"
#include <stdio.h>
uint16_t read_data;
uint16_t read_data1 __attribute__((at(SDRAM_BANK_ADDR+0x124))); /* 强制指定将read_data1定义到SDRAM中 */
int main(void)
{DEBUG_USART1_Config();LED_Config();printf("\r\n这是测试扩展外部SDRAM的例程实验\r\n");/*初始化SDRAM模块*/SDRAM_Init();*(uint16_t*)(SDRAM_BANK_ADDR) = 0xFEFE;read_data = *( uint16_t*) SDRAM_BANK_ADDR;printf("\r\n读取到的数据为:0x%x\r\n",read_data);*(uint16_t*)(SDRAM_BANK_ADDR+0x124) = 0xf9f9; //这里也可以直接用read_data1来赋值printf("\r\n读取到的数据为:0x%x\r\n",read_data1);while(1){}
}
再看map文件中,read_data1已经被定义到SDRAM中了。

注意,在map中要查询变量的地址时,一定要将变量定义为全局变量,否则在map中查不到,并且此时强制指定分配地址也会无效。
相关文章:
023 - STM32学习笔记 - 扩展外部SDRAM(二) - 扩展外部SDRAM实验
023- STM32学习笔记 - 扩展外部SDRAM(一) - 扩展外部SDRAM实验 本节内容中要配置的引脚很多,如果你用的开发板跟我的不一样,请详细参照STM32规格书中说明对相关GPIO引脚进行配置。 先提前对本届内容的变成步骤进行总结如下&…...
机器学习 | Python实现XGBoost极限梯度提升树模型答疑
机器学习 | MATLAB实现XGBoost极限梯度提升树模型答疑 目录 机器学习 | MATLAB实现XGBoost极限梯度提升树模型答疑问题系列问题回答问题系列 关于XGBoost有几个问题想请教一下。1.XGBoost的API有哪些种调用方法?2.参数如何调? 问题回答 XGBoost的API有2种调用方法,一种是我们…...
关于使用远程工具连接mysql数据库时,提示:Public Key Retrieval is not allowed
我在使用DBeaver工具连接 数据库时,提示:Public Key Retrieval is not allowed, 我在前一天还是可以连接的,但是今天突然无法连接了, 但是最后捣鼓了一下又可以了。 具体方法:首先先把mysql服务停了&#x…...
leetcode做题笔记117. 填充每个节点的下一个右侧节点指针 II
给定一个二叉树: struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。 初始状态下,所有 next 指针都…...
解决博客不能解析PHP直接下载源码问题
背景: 在网站设置反向代理后,网站突然不能正常访问,而是会直接下载访问文件的PHP源码 解决办法: 由于在搞完反向代理之后,PHP版本变成了纯静态,所以网站不能正常解析;只需要把PHP版本恢复正常…...
voc 转coco
import os import random import shutil import sys import json import glob import xml.etree.ElementTree as ET""" 修改下面3个参数 1.val_files_num : 验证集的数量 2.test_files_num :测试集的数量 3.voc_annotations : voc的annotations路径 …...
【C语言每日一题】03. 对齐输出
题目来源:http://noi.openjudge.cn/ch0101/03/ 03 对齐输出 总时间限制: 1000ms 内存限制: 65536kB 问题描述 读入三个整数,按每个整数占8个字符的宽度,右对齐输出它们。 输入 只有一行,包含三个整数,整数之间以一…...
七大排序完整版
目录 一、直接插入排序 (一)单趟直接插入排 1.分析核心代码 2.完整代码 (二)全部直接插入排 1.分析核心代码 2.完整代码 (三)时间复杂度和空间复杂度 二、希尔排序 (一)对…...
C语言的数据类型简介
一、基本类型 (1)六种基本类型 **字符串常量和字符常量的不同 1)‘a’为字符常量,”a”为字符串常量 2)每个字符串的结尾,编译器会自动添加一个结束标志位‘\0’ “a”包含两个字符’a’和’\0’ &#x…...
Fei-Fei Li-Lecture 16:3D Vision 【斯坦福大学李飞飞CV课程第16讲:3D Vision】
目录 P1 2D Detection and Segmentation P2 Video 2D time series P3 Focus on Two Problems P4 Many more topics in 3D Vision P5-10 Multi-View CNN P11 Experiments – Classification & Retrieval P12 3D Shape Representations P13--17 3D Shape Represen…...
【计算机视觉】YOLO 入门:训练 COCO128 数据集
一、COCO128 数据集 我们以最近大热的YOLOv8为例,回顾一下之前的安装过程: %pip install ultralytics import ultralytics ultralytics.checks()这里选择训练的数据集为:COCO128 COCO128是一个小型教程数据集,由COCOtrain2017中…...
【数分面试答疑】XX场景如何分析问题的思考
问题: 如何分析消费贷客户的用款活跃度,简单列出分析报告的思路框架 解答 这个问题是一个典型的数据分析类的面试问题,主要考察面试者对于消费贷客户的用款活跃度分析的理解和方法,以及对于数据分析报告的撰写和呈现的能力。回…...
html中如何用vue语法,并使用UI组件库 ,html中引入vue+ant-design-vue或者vue+element-plus
html中如何用vue语法,并使用UI组件库 前言 先说一下本次应用的场景,本次项目中,需要引入github中别人写好的插件,插件比较大,没有方法直接在自己项目中,把别人的项目打包合并生成html(类似于前…...
【数据结构】二叉数的存储与基本操作的实现
文章目录 🍀二叉树的存储🌳二叉树的基本操作🐱👤二叉树的创建🐱👓二叉树的遍历🎡前中后序遍历📌前序遍历📌中序遍历📌后续遍历 🛫层序遍历&am…...
使用 Netty 实现群聊功能的步骤和注意事项
文章目录 前言声明功能说明实现步骤WebSocket 服务启动Channel 初始化HTTP 请求处理HTTP 页面内容WebSocket 请求处理 效果展示总结 前言 通过之前的文章介绍,我们可以深刻认识到Netty在网络编程领域的卓越表现和强大实力。这篇文章将介绍如何利用 Netty 框架开发一…...
一篇文章搞定《WebView的优化及封装》
一篇文章搞定《WebView的优化及封装》 前言WebView的过程分析确定优化方案一、预加载,复用缓冲池(初始化优化)优化的解析说明具体的实现 二、预置模版(请求、渲染优化)优化的解析说明具体的实现1、离线包2、预获取数据…...
FreeSWITCH 1.10.10 简单图形化界面5 - 使用百度TTS
FreeSWITCH 1.10.10 简单图形化界面5 - 使用百度TTS 0、 界面预览1、注册百度AI开放平台,开通语音识别服务2、获取AppID/API Key/Secret Key3、 安装百度语音合成sdk4、合成代码5、在PBX中使用百度TTS6、音乐文件-TTS7、拨号规则-tts_command 0、 界面预览 http://…...
DP读书:不知道干什么就和我一起读书吧
DP读书:不知道干什么就和我一起读书吧 为啥写博客:好处一:记录自己的学习过程优点二:让自己在各大社群里不那么尴尬推荐三:坚持下去,找到一个能支持自己的伙伴 虽然清楚知识需要靠时间沉淀,但在…...
【Linux】进程通信 — 信号(上篇)
文章目录 📖 前言1. 什么是信号1.1 认识信号:1.2 信号的产生:1.3 信号的异步:1.4 信号的处理: 2. 前后台进程3. 系统接口3.1 signal:3.1 - 1 不能被捕捉的信号 3.2 kill:3.2 - 1 killall 3.3 ra…...
JS弃之可惜食之无味的代码冷知识
JS代码冷知识大全 1. 变量提升与暂死 在JavaScript中,变量提升是一个有趣且容易让人误解的概念。在代码中,变量和函数声明会在其所在作用域的顶部被提升,但是初始化并不会被提升。这可能导致在声明之前就使用变量,结果为undefin…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
