5.13 FEMC 使用说明
本章说明 N32H49x 标准外设库中 FEMC 驱动的文件组成与 16 个公开函数,以及 SRAM、PSRAM、Nor_Flash、Nand_Flash、LCDDisplay 五个示例。外部存储器控制器把 SRAM、PSRAM、NOR Flash、NAND Flash 或 LCD 接口映射到芯片的存储空间;存储体选择、总线宽度、时序与扩展模式等配置规则,以及取指与访问约束请配合用户手册的“外部存储器控制器(FEMC)”章节阅读。
5.13.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_femc.h |
定义存储体、数据宽度、时序、扩展模式与标志位等取值,声明 16 个公开函数 |
firmware/n32h49x_std_periph_driver/src/n32h49x_femc.c |
实现上述 16 个函数:NOR/SRAM 与 NAND 存储体初始化、ECC、中断与标志 |
projects/n32h49x_EVAL/examples/FEMC/ |
FEMC 示例根目录,含 SRAM、PSRAM、Nor_Flash、Nand_Flash、LCDDisplay 5 个示例 |
projects/n32h49x_EVAL/examples/FEMC/SRAM/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32h49x_EVAL/bsp/ |
该示例引用的公共板级文件:串口日志与延时函数 |
5.13.2 API 参考
以下 16 个公开函数按 n32h49x_femc.h 的声明顺序分为 4 组;每个条目给出功能摘要与 n32h49x_femc.c 中该函数正上方的注释原文。
NOR/SRAM 存储体
FEMC_DeInitNorSram、FEMC_InitNorSram、FEMC_InitNorSramStruct、FEMC_EnableNorSram
NAND 存储体
FEMC_DeInitNand、FEMC_InitNand、FEMC_InitNandStruct、FEMC_EnableNand
NAND ECC
FEMC_EnableNandEcc、FEMC_RestartNandEcc、FEMC_GetEcc
NAND 中断与标志
FEMC_ConfigInt、FEMC_GetFlag、FEMC_ClrFlag、FEMC_GetITFlag、FEMC_ClrITFlag
void FEMC_DeInitNorSram(FEMC_SN_Bank_Block *Block)将 FEMC 的 SRAM/NOR 存储体寄存器复位为默认值。
name FEMC_DeInitNorSram.
fun Deinitializes the FEMC SRAM/NOR Banks registers to their default reset values.
param Block: FEMC_SN_Bank pointer to SRAM/NorFlash bank block which will beinitialized.
- FEMC_SN_BANK_BLOCK1 FEMC SRAM/NOR Bank Block1
- FEMC_SN_BANK_BLOCK2 FEMC SRAM/NOR Bank Block2
- FEMC_SN_BANK_BLOCK3 FEMC SRAM/NOR Bank Block3
- FEMC_SN_BANK_BLOCK4 FEMC SRAM/NOR Bank Block4
return none
void FEMC_DeInitNand(FEMC_NAND_Bank *Bank)将 FEMC 的 NAND 存储体寄存器复位为默认值。
name FEMC_DeInitNand.
fun Deinitializes the FEMC NAND Banks registers to their default reset values.
param Bank: FEMC_NAND_Bank pointer to NAND bank which will beinitialized.
- FEMC_NAND_BANK2 FEMC Bank2 NAND
- FEMC_NAND_BANK3 FEMC Bank3 NAND
return none
void FEMC_InitNorSram(FEMC_NorSramInitType* FEMC_NORSRAMInitStruct)根据 FEMC_NORSRAMInitStruct 中指定的参数初始化 FEMC 的 NOR/SRAM 存储体。
name FEMC_InitNorSram.
fun Initializes the FEMC NOR/SRAM Bank Blocks according to the specified parameters in the FEMC_NORSRAMInitStruct.
param FEMC_NORSRAMInitStruct : pointer to a FEMC_NorSramInitType
structure that contains the configuration information for
the FEMC NOR/SRAM specified Bank Blocks.
return none
void FEMC_InitNand(FEMC_NandInitType* FEMC_NANDInitStruct)根据 FEMC_NANDInitStruct 中指定的参数初始化 FEMC 的 NAND 存储体。
name FEMC_InitNand.
fun Initializes the FEMC NAND Banks according to the specified parameters in the FEMC_NANDInitStruct.
param FEMC_NANDInitStruct pointer to a FEMC_NandInitType
structure that contains the configuration information for the FEMC NAND specified Banks.
return none
void FEMC_InitNorSramStruct(FEMC_NorSramInitType* FEMC_NORSRAMInitStruct)将 FEMC_NORSRAMInitStruct 的每个成员填充为其默认值。
name FEMC_InitNorSramStruct.
fun Fills each FEMC_NORSRAMInitStruct member with its default value.
param FEMC_NORSRAMInitStruct pointer to a FEMC_NorSramInitType structure which will be initialized.
return none
void FEMC_InitNandStruct(FEMC_NandInitType* FEMC_NANDInitStruct)将 FEMC_NANDInitStruct 的每个成员填充为其默认值。
name FEMC_InitNandStruct.
fun Fills each FEMC_NANDInitStruct member with its default value.
param FEMC_NANDInitStruct pointer to a FEMC_NandInitType structure which will be initialized.
return none
void FEMC_EnableNorSram( FEMC_SN_Bank_Block *Block, FunctionalState Cmd)使能或禁止指定的 NOR/SRAM 存储体。
name FEMC_EnableNorSram.
fun Enables or disables the specified NOR/SRAM Memory Bank.
param Block specifies the NOR/SRAM Bank Block to be used
- FEMC_SN_BANK_BLOCK1
- FEMC_SN_BANK_BLOCK2
- FEMC_SN_BANK_BLOCK3
- FEMC_SN_BANK_BLOCK4
param Cmd :
- ENABLE
- DISABLE
return none
void FEMC_EnableNand(FEMC_NAND_Bank *Bank, FunctionalState Cmd)使能或禁止指定的 NAND 存储体。
name FEMC_EnableNand.
fun Enables or disables the specified NAND Memory Bank.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param Cmd :
- ENABLE
- DISABLE
return none
void FEMC_EnableNandEcc(FEMC_NAND_Bank *Bank, FunctionalState Cmd)使能或禁止 FEMC NAND 的 ECC 功能。
name FEMC_EnableNandEcc.
fun Enables or disables the FEMC NAND ECC feature.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param Cmd :
- ENABLE
- DISABLE
return none
void FEMC_RestartNandEcc(FEMC_NAND_Bank *Bank)清除 ECC 结果并启动一次新的 ECC 过程。
name FEMC_RestartNandEcc.
fun Clear ECC result and start a new ECC process.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
return none
uint32_t FEMC_GetEcc(FEMC_NAND_Bank *Bank)返回纠错码寄存器的值。
name FEMC_GetEcc.
fun Returns the error correction code register value.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
return The Error Correction Code (ECC) value.
void FEMC_ConfigInt(FEMC_NAND_Bank *Bank, uint32_t FEMC_INT, FunctionalState Cmd)使能或禁止指定的 RTC 中断。
name FEMC_ConfigInt.
fun Enables or disables the specified RTC interrupts.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param FEMC_INT specifies the NAND flag
- FEMC_NAND_INT_HIGH_LEVEL
- FEMC_NAND_INT_RASING_EDGE
- FEMC_NAND_INT_FALLING_EDGE
param Cmd :
- ENABLE
- DISABLE
return none
使用提示: 源码注释写的是 RTC 中断,实现操作的是 Bank->STS 中由 FEMC_INT 指定的位(Bank->STS |= FEMC_INT 置位、Bank->STS &= ~FEMC_INT 清位),对应 param 中列出的 FEMC_NAND_INT_*。
FlagStatus FEMC_GetFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)检查指定的 NAND 标志是否置位。
name FEMC_GetFlag.
fun Checks whether the specified NAND flag is set or not.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param FEMC_FLAG specifies the NAND flag
- FEMC_NAND_FLAG_FIFO_EMPTY
return FlagStatus
- SET : the flag is set
- RESET : the flag is reset
void FEMC_ClrFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)清除 FEMC 的挂起标志。
name FEMC_ClrFlag.
fun Clears the FEMC's pending flags.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param FEMC_FLAG specifies the NAND flag
- FEMC_NAND_FLAG_FIFO_EMPTY
return none
FlagStatus FEMC_GetITFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)检查指定的 NAND 中断标志是否置位。
name FEMC_GetFlag.
fun Checks whether the specified NAND interrupt flag is set or not.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param FEMC_FLAG specifies the NAND flag
- FEMC_NAND_INT_FLAG_HIGH_LEVEL
- FEMC_NAND_INT_FLAG_RASING_EDGE
- FEMC_NAND_INT_FLAG_FALLING_EDGE
return FlagStatus
- SET : the flag is set
- RESET : the flag is reset
使用提示: 源码注释的 name 字段写作 FEMC_GetFlag.,与函数名 FEMC_GetITFlag 不一致,按函数名调用即可。
void FEMC_ClrITFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)清除 FEMC 的中断挂起标志。
name FEMC_ClrITFlag.
fun Clears the FEMC's interrupt pending flags.
param Bank specifies the NAND Bank to be used
- FEMC_NAND_BANK2
- FEMC_NAND_BANK3
param FEMC_FLAG specifies the NAND flag
- FEMC_NAND_INT_FLAG_HIGH_LEVEL
- FEMC_NAND_INT_FLAG_RASING_EDGE
- FEMC_NAND_INT_FLAG_FALLING_EDGE
return none
5.13.3 Demo 示例说明
本节说明 FEMC 的五个示例:SRAM、PSRAM、Nor_Flash、Nand_Flash 演示对外部存储器的读写校验,LCDDisplay 演示通过 FEMC 驱动 LCD 显示。
5.13.3.1 SRAM 读写校验
工程目录: projects/n32h49x_EVAL/examples/FEMC/SRAM/
使能 GPIO 与 FEMC 时钟→配置数据、地址与控制引脚→用 Block1 初始化外部 SRAM→写入缓冲数据→读回逐字比较并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 FEMC 把数据写入外部 16 位 SRAM 再读回校验; 读写一致时串口打印带 pass 的通过信息,否则打印带 fail 的失败信息 |
| 源码组成 | src/main.c:main、Buffer16cmp、Buffer8cmp、Fill_Buffer_16、Fill_Buffer_8;src/femc_sram.c:FEMC_GPIO_Init、FEMC_Sram_Init、FEMC_Sram_Write_Buffer、FEMC_Sram_Read_Buffer;inc/main.h、inc/femc_sram.h、inc/n32h49x_it.h:头文件;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程 |
| 关键配置 | 时钟:FEMC 时钟源为 AHB,系统时钟 240MHz; 器件:外部 16 位 SRAM IS61WV102416FBLL-10TLI,选用 Block1,数据宽度 16 位,地址数据总线非复用; 引脚:数据线 PD14/PD15/PD0/PD1、PE7PE15、PD8PD10,地址线 PF0PF5、PF12PF15、PG0PG5、PD11PD13、PE3,控制线 NOE:PD4、NWE:PD5、NE:PG9、NBL0:PE0、NBL1:PE1 |
| 程序与数据路径 | main 调用 FEMC_Sram_Init 完成引脚与存储体配置;用 Fill_Buffer_16 填充发送缓冲后调用 FEMC_Sram_Write_Buffer 写入;再调用 FEMC_Sram_Read_Buffer 读回并逐字比较,统计不一致的个数 |
| 主要 API | FEMC_InitNorSramStruct、FEMC_InitNorSram、FEMC_EnableNorSram |
| 验证方法 | 编译烧录上电后串口打印启动信息; 读写一致时打印带 sram 与 pass 的通过信息,否则打印带 fail 的失败信息 |
| 使用提示 | 硬件平台为全功能板 N32H497ZGL7_EVB V1.0; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1; 串口为 USART1(TX PA9,RX PA10,115200); 开发板上没有 SRAM,需要外接器件 |
核心代码解读与移植
示例先完成 GPIO 与 FEMC 时钟、引脚和 Block1 存储体配置,再写入缓冲并读回逐字比较。
数据路径: FEMC_Sram_Init 使能时钟并配置引脚 → FEMC_InitNorSramStruct 与 FEMC_InitNorSram 写入存储体配置 → FEMC_EnableNorSram 使能存储体 → main 写入并读回比较
1. 配置并初始化外部 SRAM。 FEMC_Sram_Init 使能 FEMC 时钟,按 16 位异步 SRAM 设置时序与存储体参数,再使能选定的存储体。
/* Enable the FEMC Clock */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_FEMC, ENABLE);
/*-- FEMC Configuration ----------------------------------------------------*/
p.AddrSetTime = FEMC_NOR_SRAM_ADDR_SETUP_TIME_2HCLK;
p.AddrHoldTime = FEMC_NOR_SRAM_ADDR_HOLD_TIME_2HCLK;
p.DataSetTime = FEMC_NOR_SRAM_DATA_SETUP_TIME(5);
p.BusRecoveryCycle = FEMC_NOR_SRAM_BUSRECOVERY_TIME_1HCLK;
p.ClkDiv = FEMC_NOR_SRAM_CLK_DIV_2;
p.DataHoldTime = FEMC_NOR_SRAM_DATA_HOLD_TIME_2HCLK;
p.AccMode = FEMC_NOR_SRAM_ACC_MODE_A;
if (block_index == 1)
{
Block = FEMC_SN_BANK_BLOCK1;
}
else if (block_index == 2)
{
Block = FEMC_SN_BANK_BLOCK2;
}
else if (block_index == 3)
{
Block = FEMC_SN_BANK_BLOCK3;
}
else if (block_index == 4)
{
Block = FEMC_SN_BANK_BLOCK4;
}
FEMC_InitNorSramStruct(&FEMC_NORSRAMInitStructure);
FEMC_NORSRAMInitStructure.Block = Block;
FEMC_NORSRAMInitStructure.DataAddrMux = FEMC_NOR_SRAM_MUX_DISABLE;
FEMC_NORSRAMInitStructure.MemType = FEMC_MEM_TYPE_SRAM;
FEMC_NORSRAMInitStructure.MemDataWidth = FEMC_NOR_SRAM_DATA_WIDTH_16B;
FEMC_NORSRAMInitStructure.BurstAccMode = FEMC_NOR_SRAM_BURST_MODE_DISABLE;
FEMC_NORSRAMInitStructure.WaitSigPolarity = FEMC_NOR_SRAM_WAIT_SIGNAL_LOW;
FEMC_NORSRAMInitStructure.WrapMode = FEMC_NOR_SRAM_WRAP_DISABLE;
FEMC_NORSRAMInitStructure.WaitSigConfig = FEMC_NOR_SRAM_NWAIT_BEFORE_STATE;
FEMC_NORSRAMInitStructure.WriteEnable = FEMC_NOR_SRAM_WRITE_ENABLE;
FEMC_NORSRAMInitStructure.WaitSigEnable = FEMC_NOR_SRAM_NWAIT_DISABLE;
FEMC_NORSRAMInitStructure.ExtModeEnable = FEMC_NOR_SRAM_EXTENDED_DISABLE;
FEMC_NORSRAMInitStructure.WriteBurstEnable = FEMC_NOR_SRAM_BURST_WRITE_DISABLE;
FEMC_NORSRAMInitStructure.RWTimingStruct = &p;
FEMC_NORSRAMInitStructure.WTimingStruct = &p;
FEMC_InitNorSram(&FEMC_NORSRAMInitStructure);
/* Enable FEMC Bank1_NOR Bank */
FEMC_EnableNorSram(Block, ENABLE);
FEMC_MEM_TYPE_SRAM 与 16 位数据宽度对应所用器件;时序结构体同时赋给读写时序,非扩展模式下一套时序即可。
2. 写入缓冲数据。 FEMC_Sram_Write_Buffer 按存储体号取得块基地址,再逐半字写入并递增地址。
void FEMC_Sram_Write_Buffer(uint16_t* pBuffer, uint8_t block_index, uint32_t write_addr, uint32_t number)
{
uint32_t transfer_addr;
if (block_index == 1)
{
transfer_addr = Bank1_Block1_SRAM_ADDR;
}
else if (block_index == 2)
{
transfer_addr = Bank1_Block2_SRAM_ADDR;
}
else if (block_index == 3)
{
transfer_addr = Bank1_Block3_SRAM_ADDR;
}
else if (block_index == 4)
{
transfer_addr = Bank1_Block4_SRAM_ADDR;
}
for (; number != 0; number--) /* while there is data to write */
{
/* Transfer data to the memory */
*(uint16_t*)(transfer_addr + write_addr) = *pBuffer++;
/* Increment the address*/
write_addr += 2;
}
}
块基地址由存储体号决定,地址每次加 2 以对齐半字宽度;读函数与之对应。
3. 读回并比较。 main 填充发送缓冲并写入,再读回比较,统计不一致的个数后打印结果。
block_index = 1;
/*config femc ,external 16bit sram*/
FEMC_Sram_Init(block_index);
/* Write data to FEMC SRAM memory */
/* Fill the buffer to send */
Fill_Buffer_16(tx_buffer, BUFFER_SIZE, 0x1230);
log_info("femc write external sram test\r\n");
FEMC_Sram_Write_Buffer(tx_buffer, block_index, WRITE_READ_ADDR, BUFFER_SIZE);
/* Read data from FEMC SRAM memory */
log_info("femc read external sram test\r\n");
FEMC_Sram_Read_Buffer(rx_buffer, block_index, WRITE_READ_ADDR, BUFFER_SIZE);
/* Read back SRAM memory and check content correctness */
for (index = 0x00; index < BUFFER_SIZE; index++)
{
if (rx_buffer[index] != tx_buffer[index])
{
wr_status++;
}
}
Fill_Buffer_16 写入 IndexTmp + Offset 序列,wr_status 统计不一致个数,为 0 时表示读回正确。
对应源码: src/main.c、src/femc_sram.c
5.13.3.2 PSRAM 读写校验
工程目录: projects/n32h49x_EVAL/examples/FEMC/PSRAM/
使能 GPIO 与 FEMC 时钟→配置数据、地址与控制引脚→用 Block3 初始化外部 PSRAM→写入缓冲数据→读回比较并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 FEMC 把数据写入外部 16 位 PSRAM 再读回校验; 读写一致时串口打印带 pass 的通过信息,否则打印带 fail 的失败信息 |
| 源码组成 | src/main.c:main、Buffer16cmp、Buffer8cmp、Fill_Buffer_16、Fill_Buffer_8;src/femc_psram.c:FEMC_GPIO_Init、FEMC_Psram_Init、FEMC_Psram_Write_Buffer、FEMC_Psram_Read_Buffer;inc/main.h、inc/femc_psram.h、inc/n32h49x_it.h:头文件;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程 |
| 关键配置 | 时钟:FEMC 时钟源为 AHB,系统时钟 240MHz; 器件:外部 16 位 PSRAM IS66WV51216EBLLLL-70TLI,选用 Block3,数据宽度 16 位; 引脚:数据线与地址线同 SRAM 示例,控制线 NOE:PD4、NWE:PD5、NE:PG8 |
| 程序与数据路径 | main 调用 FEMC_Psram_Init 完成引脚与存储体配置;用 Fill_Buffer_16 填充发送缓冲后调用 FEMC_Psram_Write_Buffer 写入;再调用 FEMC_Psram_Read_Buffer 读回,用 Buffer16cmp 比较并打印结果 |
| 主要 API | FEMC_InitNorSramStruct、FEMC_InitNorSram、FEMC_EnableNorSram |
| 验证方法 | 编译烧录上电后串口打印启动信息; 读写一致时打印带 psram 与 pass 的通过信息,否则打印带 fail 的失败信息 |
| 使用提示 | 硬件平台为全功能板 N32H497ZGL7_EVB V1.0; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1; 串口为 USART1(TX PA9,RX PA10,115200); 开发板上没有 PSRAM,需要外接器件 |
核心代码解读与移植
示例与 SRAM 示例结构一致,差别在存储体号与器件类型、时序参数。
数据路径: FEMC_Psram_Init 使能时钟并配置引脚 → FEMC_InitNorSramStruct 与 FEMC_InitNorSram 写入存储体配置 → FEMC_EnableNorSram 使能存储体 → main 写入并读回比较
1. 配置并初始化外部 PSRAM。 FEMC_Psram_Init 按 PSRAM 时序设置参数,把器件类型设为 PSRAM,并用 Block3 初始化。
/* Enable the FEMC Clock */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_FEMC, ENABLE);
FEMC_InitNorSramStruct(&FEMC_NORSRAMInitStructure);
/*-- FEMC Configuration ----------------------------------------------------*/
ReadWriteTiming.AddrSetTime = FEMC_NOR_SRAM_ADDR_SETUP_TIME_12HCLK;
ReadWriteTiming.AddrHoldTime = FEMC_NOR_SRAM_ADDR_HOLD_TIME_2HCLK;
ReadWriteTiming.DataSetTime = FEMC_NOR_SRAM_DATA_SETUP_TIME(8);
ReadWriteTiming.DataHoldTime = FEMC_NOR_SRAM_DATA_HOLD_TIME_2HCLK;
ReadWriteTiming.BusRecoveryCycle = FEMC_NOR_SRAM_BUSRECOVERY_TIME_2HCLK;
ReadWriteTiming.ClkDiv = FEMC_NOR_SRAM_CLK_DIV_2;
ReadWriteTiming.AccMode = FEMC_NOR_SRAM_ACC_MODE_A;
if (block_index == 1)
{
Block = FEMC_SN_BANK_BLOCK1;
}
else if (block_index == 2)
{
Block = FEMC_SN_BANK_BLOCK2;
}
else if (block_index == 3)
{
Block = FEMC_SN_BANK_BLOCK3;
}
else if (block_index == 4)
{
Block = FEMC_SN_BANK_BLOCK4;
}
FEMC_NORSRAMInitStructure.Block = Block;
FEMC_NORSRAMInitStructure.DataAddrMux = FEMC_NOR_SRAM_MUX_DISABLE;
FEMC_NORSRAMInitStructure.MemType = FEMC_MEM_TYPE_PSRAM;
FEMC_NORSRAMInitStructure.MemDataWidth = FEMC_NOR_SRAM_DATA_WIDTH_16B;
FEMC_NORSRAMInitStructure.BurstAccMode = FEMC_NOR_SRAM_BURST_MODE_DISABLE;
FEMC_NORSRAMInitStructure.AsynchroWait = FEMC_NOR_SRAM_ASYNC_NWAIT_DISABLE;
FEMC_NORSRAMInitStructure.WaitSigPolarity = FEMC_NOR_SRAM_WAIT_SIGNAL_LOW;
FEMC_NORSRAMInitStructure.WrapMode = FEMC_NOR_SRAM_WRAP_DISABLE;
FEMC_NORSRAMInitStructure.WaitSigConfig = FEMC_NOR_SRAM_NWAIT_BEFORE_STATE;
FEMC_NORSRAMInitStructure.WriteEnable = FEMC_NOR_SRAM_WRITE_ENABLE;
FEMC_NORSRAMInitStructure.WaitSigEnable = FEMC_NOR_SRAM_NWAIT_DISABLE;
FEMC_NORSRAMInitStructure.ExtModeEnable = FEMC_NOR_SRAM_EXTENDED_DISABLE;
FEMC_NORSRAMInitStructure.WriteBurstEnable = FEMC_NOR_SRAM_BURST_WRITE_DISABLE;
FEMC_NORSRAMInitStructure.NadvPolarity = FEMC_NOR_SRAM_NADV_SIGNAL_HIGH;
FEMC_NORSRAMInitStructure.RWTimingStruct = &ReadWriteTiming;
FEMC_NORSRAMInitStructure.WTimingStruct = &WriteTiming;
FEMC_InitNorSram(&FEMC_NORSRAMInitStructure);
/* Enable FEMC Bank1 Block */
FEMC_EnableNorSram(Block, ENABLE);
FEMC_MEM_TYPE_PSRAM 选择 PSRAM 时序模型;PSRAM 的地址建立时间比 SRAM 更长,移植时应按器件手册调整时序。
2. 写入缓冲数据。 FEMC_Psram_Write_Buffer 与 SRAM 写函数结构相同,按存储体号选择块基地址后逐半字写入。
transfer_addr = Bank1_Block1_PSRAM_ADDR;
}
else if (block_index == 2)
{
transfer_addr = Bank1_Block2_PSRAM_ADDR;
}
else if (block_index == 3)
{
transfer_addr = Bank1_Block3_PSRAM_ADDR;
}
else if (block_index == 4)
{
transfer_addr = Bank1_Block4_PSRAM_ADDR;
}
for (; number != 0; number--) /*!< while there is data to write */
{
/*!< Transfer data to the memory */
*(uint16_t*)(transfer_addr + write_addr) = *pBuffer++;
/*!< Increment the address*/
write_addr += 2;
}
}
块基地址与存储体一一对应,访问即映射到外部 PSRAM 的相应区域。
3. 读回并比较。 main 选定 Block3,写入后读回并用 Buffer16cmp 比较,直接得到 PASSED 或 FAILED。
block_index = 3; // 1:Block1; 2:Block2; 3:Block3; 4:Block4
/*config femc ,external 16bit sram*/
FEMC_Psram_Init(block_index);
/* Fill the buffer to send */
Fill_Buffer_16(tx_buffer, BUFFER_SIZE, 0x1230);
FEMC_Psram_Write_Buffer(tx_buffer, block_index, WRITE_READ_ADDR, BUFFER_SIZE);
/* Read data from FEMC PSRAM memory */
FEMC_Psram_Read_Buffer(rx_buffer, block_index, WRITE_READ_ADDR, BUFFER_SIZE);
/* Read back PSRAM memory and check content correctness */
wr_status = Buffer16cmp(tx_buffer, rx_buffer, BUFFER_SIZE);
if (wr_status == PASSED)
{
/* OK */
log_info("femc psram read and write test pass\r\n");
}
Buffer16cmp 逐半字比较并返回状态;移植到其它存储体时只需修改 block_index 与对应的引脚宏。
对应源码: src/main.c、src/femc_psram.c
5.13.3.3 Nor_Flash 读写擦校验
工程目录: projects/n32h49x_EVAL/examples/FEMC/Nor_Flash/
使能 GPIO 与 FEMC 时钟→用 Block1 初始化外部 NOR Flash→读取并打印器件 ID→写入缓冲并读回比较→擦除块并校验全为 0xFF
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 FEMC 读取外部 NOR Flash 的 ID,并对其进行写入、读回校验与擦除操作; 读写与擦除都通过时串口打印带 pass 的信息,否则打印带 fail 的信息 |
| 源码组成 | src/main.c:main、Buffer16cmp、Buffer8cmp、Fill_Buffer_16、Fill_Buffer_8;src/femc_nor.c:FEMC_GPIO_Init、FEMC_Nor_Init、FEMC_Nor_ReadID、FEMC_Nor_Write_Buffer_Halfword、FEMC_Nor_Read_Buffer_Halfword、FEMC_Nor_Erase_Block;inc/main.h、inc/femc_nor.h、inc/n32h49x_it.h:头文件;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程 |
| 关键配置 | 时钟:FEMC 时钟源为 AHB,系统时钟 240MHz; 器件:外部 16 位 NOR Flash S29GL01GS11TFI020,选用 Block1,扩展模式使能; 引脚:数据线与地址线同上,控制线 NOE:PD4、NWE:PD5、NE:PB9、NWAIT:PD6 |
| 程序与数据路径 | main 调用 FEMC_Nor_Init 完成引脚与存储体配置;用 FEMC_Nor_ReadID 读取并打印厂商与器件 ID;写入缓冲并读回比较,再擦除块并校验读回值全为 0xFFFF |
| 主要 API | FEMC_InitNorSramStruct、FEMC_InitNorSram、FEMC_EnableNorSram |
| 验证方法 | 编译烧录上电后串口打印启动信息与器件 ID; 读写与擦除都通过时打印带 norflash 与 pass 的信息,否则打印带 fail 的信息 |
| 使用提示 | 硬件平台为全功能板 N32H497ZGL7_EVB V1.0; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1; 串口为 USART1(TX PA9,RX PA10,115200); 开发板上没有 NOR Flash,需要外接器件 |
核心代码解读与移植
示例在读写校验之外增加 ID 读取与块擦除,擦除后再次读回校验全为 0xFFFF。
数据路径: FEMC_Nor_Init 使能时钟并配置引脚 → FEMC_Nor_ReadID 读取器件 ID → FEMC_Nor_Write_Buffer_Halfword 写入 → FEMC_Nor_Read_Buffer_Halfword 读回比较 → FEMC_Nor_Erase_Block 擦除并校验
1. 配置并初始化外部 NOR Flash。 FEMC_Nor_Init 按 NOR 器件设置时序并启用扩展模式,再使能选定的存储体。
/* Enable the FEMC Clock */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_FEMC, ENABLE);
/*-- FEMC Configuration ----------------------------------------------------*/
p.AddrSetTime = FEMC_NOR_SRAM_ADDR_SETUP_TIME_15HCLK;
p.AddrHoldTime = FEMC_NOR_SRAM_ADDR_HOLD_TIME_2HCLK;
p.DataSetTime = FEMC_NOR_SRAM_DATA_SETUP_TIME(12);
p.BusRecoveryCycle = FEMC_NOR_SRAM_BUSRECOVERY_TIME_1HCLK;
p.ClkDiv = FEMC_NOR_SRAM_CLK_DIV_2;
p.DataHoldTime = FEMC_NOR_SRAM_DATA_HOLD_TIME_2HCLK;
p.AccMode = FEMC_NOR_SRAM_ACC_MODE_B;
if (block_index == 1)
{
Block = FEMC_SN_BANK_BLOCK1;
}
else if (block_index == 2)
{
Block = FEMC_SN_BANK_BLOCK2;
}
else if (block_index == 3)
{
Block = FEMC_SN_BANK_BLOCK3;
}
else if (block_index == 4)
{
Block = FEMC_SN_BANK_BLOCK4;
}
FEMC_InitNorSramStruct(&FEMC_NORSRAMInitStructure);
FEMC_NORSRAMInitStructure.Block = Block;
FEMC_NORSRAMInitStructure.DataAddrMux = FEMC_NOR_SRAM_MUX_DISABLE;
FEMC_NORSRAMInitStructure.MemType = FEMC_MEM_TYPE_NOR;
FEMC_NORSRAMInitStructure.MemDataWidth = FEMC_NOR_SRAM_DATA_WIDTH_16B;
FEMC_NORSRAMInitStructure.BurstAccMode = FEMC_NOR_SRAM_BURST_MODE_DISABLE;
FEMC_MEM_TYPE_NOR 选择 NOR 时序模型,扩展模式使能后可用单独的读、写时序寄存器。
2. 写入缓冲并读回比较。 main 读取器件 ID 后填充缓冲写入,读回比较得到第一个状态。
/*config femc ,external 16bit nor flash*/
FEMC_Nor_Init(block_index);
/* read nor flash id */
FEMC_Nor_ReadID(&Nor_id, block_index);
log_info(" FEMC external norflash Manufacturer Code = 0x%x, Device ID1 = 0x%x, Device ID2 = 0x%x, Device ID3 = 0x%x \r\n",
Nor_id.Manufacturer_Code, Nor_id.Device_Code1, Nor_id.Device_Code2, Nor_id.Device_Code3);
/* Write data to NOR memory */
/* Fill the buffer to send */
Fill_Buffer_16(tx_buffer, BUFFER_SIZE, 0x0001);
FEMC_Nor_Write_Buffer_Halfword(block_index, tx_buffer, WRITE_READ_ADDR, BUFFER_SIZE); // write buffer
/* Read data from NOR memory */
FEMC_Nor_Read_Buffer_Halfword(block_index, rx_buffer, WRITE_READ_ADDR, BUFFER_SIZE);
/* Read back NOR memory and check content correctness */
wr_status1 = Buffer16cmp(tx_buffer, rx_buffer, BUFFER_SIZE);
FEMC_Nor_ReadID 通过两个读周期取回厂商与器件 ID,写入用 0x0001 起始序列便于区分。
3. 擦除块并校验。 示例擦除写入地址所在块,再读回校验整段都为 0xFFFF,得到第二个状态。
/* Erase the NOR memory block to write on */
FEMC_Nor_Erase_Block(block_index, WRITE_READ_ADDR);
/* Read data from NOR memory */
FEMC_Nor_Read_Buffer_Halfword(block_index, rx_buffer, WRITE_READ_ADDR, BUFFER_SIZE);
for(i=0;i<BUFFER_SIZE;i++)
{
if(rx_buffer[i] != 0xFFFF)
{
wr_status2 = FAILED;
break;
}
}
if (wr_status1 == PASSED && wr_status2 == PASSED)
{
/* OK */
log_info(" femc norflash read and write test pass \r\n");
}
NOR 擦除后存储单元为全 1,故读回应为 0xFFFF;两个状态都为 PASSED 才判定通过。
对应源码: src/main.c、src/femc_nor.c
5.13.3.4 Nand_Flash 读写擦校验
工程目录: projects/n32h49x_EVAL/examples/FEMC/Nand_Flash/
使能 GPIO 与 FEMC 时钟→用 Bank3 初始化外部 NAND Flash→复位并读取器件 ID→擦除块并写入页→读回页面并逐字节比较
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 FEMC 对 8 位 NAND Flash 擦除块、写页与读页并校验; 同时打印读取到的器件 ID,读回一致时打印带 pass 的信息,否则打印带 fail 的信息 |
| 源码组成 | src/main.c:main、Buffer16cmp、Buffer8cmp;src/femc_nand.c:FEMC_GPIO_Init、FEMC_Nand_Init、FEMC_Nand_Reset、FEMC_Nand_ReadID、FEMC_Nand_Erase_Block、FEMC_Nand_WritePage_Byte、FEMC_Nand_ReadPage_Byte、Flash_Write_Command;inc/main.h、inc/femc_nand.h、inc/n32h49x_it.h:头文件;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程 |
| 关键配置 | 时钟:FEMC 时钟源为 AHB,系统时钟 240MHz; 器件:外部 8 位 NAND Flash IS34ML01G081-TLI,选用 Bank3,位宽 8 位,示例关闭 ECC; 引脚:数据线 PD14/PD15/PD0/PD1、PE7~PE10,控制线 NOE:PD4、NWE:PD5、CLE:PD11、ALE:PD12、NCE:PG9、NWAIT:PD6 |
| 程序与数据路径 | main 调用 FEMC_Nand_Init 完成引脚与存储体配置;复位后读取器件 ID,随后擦除目标块、写入一页; 清零缓冲后读回该页并逐字节比较,得到测试结果 |
| 主要 API | FEMC_InitNandStruct、FEMC_InitNand、FEMC_EnableNand |
| 验证方法 | 编译烧录上电后串口打印启动信息、器件 ID 与擦写读状态; 读回一致时打印带 nandflash 与 pass 的信息,否则打印带 fail 的信息 |
| 使用提示 | 硬件平台为评估板 N32H497VEL7_STB V1.0; 开发环境为 KEIL MDK-ARM 5.25 或 IAR EWARM 8.50.1; 串口为 USART1(TX PA9,RX PA10,115200); 开发板上没有 NAND Flash,需要外接器件 |
核心代码解读与移植
示例先初始化 NAND 存储体并复位、读取 ID,再擦除块、写入页并读回逐字节比较。
数据路径: FEMC_Nand_Init 使能时钟并配置引脚 → FEMC_Nand_Reset 与 FEMC_Nand_ReadID → FEMC_Nand_Erase_Block → FEMC_Nand_WritePage_Byte 写入 → FEMC_Nand_ReadPage_Byte 读回比较
1. 初始化 NAND 存储体。 main 设置位宽为 8 位、存储体为 Bank3 并调用初始化函数。
log_init();
log_info("this is a femc external nand flash demo\r\n");
nand_flash_width = 8; // config memory bus width
bank_id = 3; // config band index,2:bank2;3:bank3,must config FEMC_BANK_NAND in femc_nand.c.
bank_address = BANK3_NAND_ADDR;
FEMC_Nand_Init(bank_id, nand_flash_width);
log_info(" nand flash test start \r\n");
位宽与存储体号必须与 femc_nand.c 中的 FEMC_BANK_NAND 配置一致,否则访问地址与器件不匹配。
2. 配置 NAND 时序与存储体。 FEMC_Nand_Init 设置建立、保持与高阻时间,按位宽选择总线宽度并使能存储体。
/*-- FEMC Configuration ------------------------------------------------------*/
/* Enable the FEMC Clock */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_FEMC, ENABLE);
FEMC_GPIO_Init(bank_index);
p.SetTime = FEMC_NAND_SETUP_TIME(0x4);
p.WaitSetTime = FEMC_NAND_WAIT_TIME(0x6);
p.HoldSetTime = FEMC_NAND_HOLD_TIME(0x5);
p.HiZSetTime = FEMC_NAND_HIZ_TIME(0x5);
if(bank_index == 2)
{
nand_bank = FEMC_NAND_BANK2;
}
else if(bank_index == 3)
{
nand_bank = FEMC_NAND_BANK3;
}
FEMC_InitNandStruct(&FEMC_NANDInitStructure);
FEMC_NANDInitStructure.Bank = nand_bank;
FEMC_NANDInitStructure.WaitFeatureEnable = FEMC_NAND_NWAIT_ENABLE;
FEMC_NANDInitStructure.MemDataWidth = bus_width == 8 ? FEMC_NAND_BUS_WIDTH_8B : FEMC_NAND_BUS_WIDTH_16B;
FEMC_NANDInitStructure.EccEnable = FEMC_NAND_ECC_DISABLE;
FEMC_NANDInitStructure.EccPageSize = FEMC_NAND_ECC_PAGE_2048BYTES;
FEMC_NANDInitStructure.TCLRSetTime = FEMC_NAND_CLE_DELAY_1HCLK;
FEMC_NANDInitStructure.TARSetTime = FEMC_NAND_ALE_DELAY_1HCLK;
FEMC_NANDInitStructure.CommSpaceTimingStruct = &p;
FEMC_NANDInitStructure.AttrSpaceTimingStruct = &p;
FEMC_InitNand(&FEMC_NANDInitStructure);
/* FEMC NAND Bank Cmd Test */
FEMC_EnableNand(nand_bank, ENABLE);
示例关闭 ECC,时序覆盖命令与地址两个空间;开启 ECC 时需注意先清空 FIFO 再切换 ECC 使能状态。
3. 写页与读页。 FEMC_Nand_WritePage_Byte 发送写页命令与地址后逐字节写入,并用状态轮询等待完成。
uint32_t FEMC_Nand_WritePage_Byte(uint32_t Bank_NAND_Addr, uint8_t* pBuffer, NAND_ADDRESS Address, uint32_t NumPageToWrite)
{
uint32_t index = 0x00, numpagewritten = 0x00, addressstatus = NAND_VALID_ADDRESS;
uint32_t status = NAND_READY, size = 0x00;
if(Bank_NAND_Addr == BANK2_NAND_ADDR)
{
if((FEMC_NAND_BANK2->CTRL & FEMC_NAND_ECC_ENABLE) != RESET)
{
FEMC_EnableNandEcc(FEMC_NAND_BANK2, DISABLE);
FEMC_EnableNandEcc(FEMC_NAND_BANK2, ENABLE);
}
}
else if(Bank_NAND_Addr == BANK3_NAND_ADDR)
{
if((FEMC_NAND_BANK3->CTRL & FEMC_NAND_ECC_ENABLE) != RESET)
{
FEMC_EnableNandEcc(FEMC_NAND_BANK3, DISABLE);
FEMC_EnableNandEcc(FEMC_NAND_BANK3, ENABLE);
}
}
地址先经有效性判断,再逐页发送写命令、列地址与行地址并写入数据;写页前必须先擦除对应块。
对应源码: src/main.c、src/femc_nand.c
5.13.3.5 LCDDisplay 通过 FEMC 驱动 LCD
工程目录: projects/n32h49x_EVAL/examples/FEMC/LCDDisplay/
使能 GPIO 与 FEMC 时钟→用 FEMC 初始化 LCD 接口→复位并读取 LCD ID→按屏型号配置寄存器→显示两块 LOGO 图形
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 FEMC 把 LCD 映射为外部存储器并写入显示数据; 复位后读取 LCD ID,按型号初始化寄存器并显示 LOGO 图形 |
| 源码组成 | src/main.c:main、LCD_Test;src/lcd.c:LCD_Configuration、LCD_RCC_Configuration、LCD_GPIO_Configuration、LCD_FEMC_Configuration、LCD_GramScan、LCD_Clear、lcd_draw_image 等;src/fonts.c、src/logo1.c、src/logo2.c:字库与图片数据;inc/main.h、inc/lcd.h、inc/fonts.h、inc/n32h49x_it.h:头文件;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程 |
| 关键配置 | 时钟:FEMC 时钟源为 AHB,系统时钟 240MHz; 屏型号:TFT LCD 320240(ILI9341)或 800480(ILI9806G),复位后按读回的 ID 选择寄存器初始化; 引脚:CS:PG12、RS:PD13、WR:PD5、RD:PD4、RST:PH5、背光 PB5,数据线 D0~D15 同 SRAM 示例 |
| 程序与数据路径 | main 调用 LCD_Configuration 完成时钟、引脚、FEMC 配置与复位;读取 LCD ID 后按型号设置分辨率并配置寄存器; LCD_Test 清屏后把两块 LOGO 图片绘制到屏幕 |
| 主要 API | FEMC_InitNorSramStruct、FEMC_InitNorSram、FEMC_EnableNorSram |
| 验证方法 | 编译下载运行后 LCD 依次显示两块 LOGO 图形; 串口打印读回的 LCD ID,用于确认屏型号 |
| 使用提示 | 硬件平台为全功能板 N32H497ZGL7_EVB V1.0; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1; 屏为 320240(ILI9341)或 800480(ILI9806G); 背光控制引脚为 PB5,显示效果主要靠观察屏幕 |
核心代码解读与移植
示例把 LCD 当作 FEMC 上的异步存储器件访问,先初始化接口与屏寄存器,再向屏幕刷新图片。
数据路径: main 调用 LCD_Configuration → LCD_FEMC_Configuration 建立 FEMC 接口 → LCD_Get_ID 读取型号 → ILI9341_REG_Config 或 ILI9806G_REG_Config 初始化 → LCD_Test 绘制图片
1. 初始化 LCD 接口并按型号配置。 LCD_Configuration 依次完成时钟、引脚、FEMC 配置与复位,再按读回的 ID 选择分辨率与寄存器初始化函数。
void LCD_Configuration(void)
{
LCD_RCC_Configuration();
LCD_GPIO_Configuration();
LCD_FEMC_Configuration();
LCD_Reset();
printf("\n\r LCD ID : 0x%x\n\r", LCD_Get_ID());
if((LCD_Get_ID())==0x9341)
{
lcd_id = 0x9341;
lcd_width = 240;
lcd_height = 320;
LCD_BL_Control(IO_STATE_LOW);
ILI9341_REG_Config();
}
else if((LCD_Get_ID())==0x9806)
{
lcd_id = 0x9806;
lcd_width = 480;
lcd_height = 800;
LCD_BL_Control(IO_STATE_HIGH);
ILI9806G_REG_Config();
}
else
{
}
}
不同屏型号的背光有效电平与分辨率不同,示例按 ID 分别设置,移植新屏时需增加对应分支。
2. 主流程调用配置与显示。 main 先初始化日志,再调用配置、方向设置与显示函数。
int main(void)
{
log_init();
#if defined(N32H49X)
/* LCD configuration */
LCD_Configuration();
LCD_GramScan(0);
LCD_Test();
#endif
while (1)
{
}
}
LCD_GramScan(0) 设置显示扫描方向,LCD_Test 负责具体画面;主循环为空,画面在初始化后一次性绘制。
3. 绘制 LOGO 图形。 LCD_Test 设置字体与颜色后清屏,再把两块 LOGO 图片按居中对齐绘制到屏幕。
void LCD_Test(void)
{
static uint8_t testCNT = 0;
testCNT++;
LCD_SetFont(&Font8x16);
LCD_SetColors(RED,WHITE);
LCD_Clear(0,0,lcd_width,lcd_height);
lcd_draw_image((lcd_width - pic_logo1.width) / 2, lcd_height / 4, &pic_logo1);
lcd_draw_image((lcd_width - pic_logo2.width) / 2, lcd_height / 3 * 2, &pic_logo2);
}
lcd_draw_image 按图片结构体逐像素写入;坐标用 lcd_width、lcd_height 计算,可自适应两种分辨率。
对应源码: src/main.c、src/lcd.c
5.13.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| XIP 取指访问限制 | 在外部存储器上取指(XIP)时不要使用 LDM、STM、LDRD、STRD 等多寄存器或双字访问指令; 这些指令会跨越取指边界导致读取错误。 |
用户手册 |
| NAND ECC 切换 | 切换 NAND ECC 使能前应先等待 ECC 结果 FIFO 清空,并按先禁止再使能的顺序操作; 否则 ECC 结果与数据不对应。 |
用户手册 |
| 字节选择与写操作 | 未使能字节选择(NBL)时写操作不生效; 配置字节选择与写使能后才能写入存储单元。 |
用户手册 |
| 非对齐访问 | 对 FEMC 存储器的非对齐访问会被拆分为多次访问; 需要保证读写数据宽度与访问地址对齐。 |
用户手册 |
| 扩展模式与读写时序 | 扩展模式(EXTEN)使能后,写时序寄存器的配置才有效; 关闭扩展模式时读写共用一套时序。 |
用户手册 |
| 数据保持时间 | 数据保持时间(DATAHLD)应大于 0; 否则数据在保持阶段不稳定。 |
用户手册 |
| 时钟分频占空比 | 当分频系数 CLKDIV 为奇数时输出时钟为非 50% 占空比; 选择偶数分频可获得对称时钟。 |
用户手册 |
| 共用时钟存储体相位 | 同一共用时钟的多个存储体应配置一致的时钟相位与访问模式; 否则不同存储体的访问时序不匹配。 |
用户手册 |