#引言
在使用STM32H743的FMC(Flexible Memory Controller)时,我们可能会遇到一些问题,如单次写操作时片选信号和写使能信号出现多次脉冲。本文将介绍如何通过配置MPU(内存保护单元)和调整数据位宽来解决这些问题。
#问题描述
##问题1:片选信号多次脉冲
当没有配置MPU时,单次写操作中片选信号(NE)会出现两次脉冲。这可能导致不期望的行为和数据传输错误。
##问题2:写使能信号多次脉冲
在解决了问题1的基础上,当设置为16位数据位宽时,单次写操作会在写使能信号(FMC_NWE)上出现两次有效脉冲。通过将数据位宽设置为32位,而硬件上只对低16位IO做复用可以解决这个问题。
#解决方案
##问题1:片选信号多次脉冲
通过配置MPU,将FMC管理的存储区设置为Device或Strongly Ordered类型,禁用缓存和缓冲,可以解决片选信号多次脉冲的问题。
###配置MPU
#include "stm32h7xx_hal.h"
void MPU_Config(void) {
MPU_Region_InitTypeDef MPU_InitStruct;
// 禁用MPU
HAL_MPU_Disable();
// 配置FMC区域
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000; // 这是FMC NE1的基地址,确保与实际配置匹配
MPU_InitStruct.Size = MPU_REGION_SIZE_256MB; // 根据实际的FMC区域大小设置
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // 不缓冲
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // 不缓存
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; // 可共享
MPU_InitStruct.Number = MPU_REGION_NUMBER0; // 使用的MPU区域编号
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; // 设置为Strongly Ordered
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 启用MPU
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
##问题2:写使能信号多次脉冲
在解决了片选信号问题后,通过将数据位宽设置为32位,而硬件上只对低16位IO做复用,可以解决写使能信号多次脉冲的问题。
###配置FMC和GPIO
void MX_FMC_Init(void) {
FMC_NORSRAM_TimingTypeDef Timing;
FMC_NORSRAM_InitTypeDef Init;
// 配置FMC时序参数
Timing.AddressSetupTime = 1;
Timing.AddressHoldTime = 1;
Timing.DataSetupTime = 2;
Timing.BusTurnAroundDuration = 1;
Timing.CLKDivision = 2;
Timing.DataLatency = 2;
Timing.AccessMode = FMC_ACCESS_MODE_A;
// 配置FMC初始化参数
Init.NSBank = FMC_NORSRAM_BANK1;
Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
Init.MemoryType = FMC_MEMORY_TYPE_SRAM;
Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_32; // 设置为32位数据宽度
Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
Init.WrapMode = FMC_WRAP_MODE_DISABLE;
Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
// 初始化FMC
HAL_FMC_NORSRAM_Init(&Init, &Timing);
}
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIO时钟
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
// 配置低16位的数据线为FMC功能
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | 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_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.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 |
GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 |
GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}
###主程序代码
int main(void) {
// 初始化HAL库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 配置MPU
MPU_Config();
// 初始化FMC
MX_FMC_Init();
// 初始化GPIO
MX_GPIO_Init();
while (1) {
// 执行一次写操作
FMC_Write(0x60000000, 0x1234);
HAL_Delay(1000); // 延迟1秒,便于观察
}
}
// FMC写操作函数
void FMC_Write(uint32_t address, uint16_t data) {
*(volatile uint32_t *)(address) = data; // 使用32位地址宽度,但只用低16位
}