5.13 FEMC usage

This chapter describes the file layout and the 16 public functions of the FEMC driver in the N32H49x standard peripheral library, together with the five examples: SRAM, PSRAM, Nor_Flash, Nand_Flash, and LCDDisplay. The external memory controller maps SRAM, PSRAM, NOR Flash, NAND Flash, or an LCD interface into the chip memory space; read the bank selection, bus width, timing, and extended-mode configuration rules, as well as the fetch and access constraints, alongside the “Flexible External Memory Controller (FEMC)” chapter of the user manual.

5.13.1 Related files

Relative path Purpose
firmware/n32h49x_std_periph_driver/inc/n32h49x_femc.h Defines bank, data width, timing, extended-mode, and flag values, and declares the 16 public functions
firmware/n32h49x_std_periph_driver/src/n32h49x_femc.c Implements those 16 functions: NOR/SRAM and NAND bank initialization, ECC, interrupts, and flags
projects/n32h49x_EVAL/examples/FEMC/ Root of the FEMC examples, with the 5 examples SRAM, PSRAM, Nor_Flash, Nand_Flash, and LCDDisplay
projects/n32h49x_EVAL/examples/FEMC/SRAM/ Example project layout: MDK-ARM/, EWARM/, inc/, src/, readme.txt
projects/n32h49x_EVAL/bsp/ Shared board files referenced by the example: UART logging and delay functions

5.13.2 API reference

Search API16 functions

The 16 public functions below are grouped by their declaration order in n32h49x_femc.h; each entry gives the function summary and the comment block directly above the function in n32h49x_femc.c.

NOR/SRAM banks

FEMC_DeInitNorSram, FEMC_InitNorSram, FEMC_InitNorSramStruct, FEMC_EnableNorSram

NAND banks

FEMC_DeInitNand, FEMC_InitNand, FEMC_InitNandStruct, FEMC_EnableNand

NAND ECC

FEMC_EnableNandEcc, FEMC_RestartNandEcc, FEMC_GetEcc

NAND interrupts and flags

FEMC_ConfigInt, FEMC_GetFlag, FEMC_ClrFlag, FEMC_GetITFlag, FEMC_ClrITFlag

void FEMC_DeInitNorSram(FEMC_SN_Bank_Block *Block)Deinitializes the FEMC SRAM/NOR Banks registers to their default reset values.
Source comment
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)Deinitializes the FEMC NAND Banks registers to their default reset values.
Source comment
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)Initializes the FEMC NOR/SRAM Bank Blocks according to the specified parameters in the FEMC_NORSRAMInitStruct.
Source comment
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)Initializes the FEMC NAND Banks according to the specified parameters in the FEMC_NANDInitStruct.
Source comment
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)Fills each FEMC_NORSRAMInitStruct member with its default value.
Source comment
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)Fills each FEMC_NANDInitStruct member with its default value.
Source comment
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)Enables or disables the specified NOR/SRAM Memory Bank.
Source comment
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)Enables or disables the specified NAND Memory Bank.
Source comment
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)Enables or disables the FEMC NAND ECC feature.
Source comment
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)Clear ECC result and start a new ECC process.
Source comment
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)Returns the error correction code register value.
Source comment
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)Enables or disables the specified RTC interrupts.
Source comment
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

Use note: The source comment says RTC interrupts, while the implementation works on the bits of Bank->STS selected by FEMC_INT (Bank->STS |= FEMC_INT sets them, Bank->STS &= ~FEMC_INT clears them), matching the FEMC_NAND_INT_* values listed under param.

FlagStatus FEMC_GetFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)Checks whether the specified NAND flag is set or not.
Source comment
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)Clears the FEMC’s pending flags.
Source comment
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)Checks whether the specified NAND interrupt flag is set or not.
Source comment
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

Use note: The name field in the source comment is written as FEMC_GetFlag. and differs from the function name FEMC_GetITFlag; call it by the function name.

void FEMC_ClrITFlag(FEMC_NAND_Bank *Bank, uint32_t FEMC_FLAG)Clears the FEMC’s interrupt pending flags.
Source comment
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 descriptions

This section covers the five FEMC examples: SRAM, PSRAM, Nor_Flash, and Nand_Flash validate read/write of external memory, while LCDDisplay drives an LCD through FEMC.

5.13.3.1 SRAM read/write check

Example directory: projects/n32h49x_EVAL/examples/FEMC/SRAM/

Enable GPIO and FEMC clocks→Configure data, address, and control pins→Initialize the external SRAM on Block1→Write a buffer→Read back, compare word by word, and print the result

Item Source analysis
Function Writes data to an external 16-bit SRAM through FEMC and reads it back for checking;
prints a result containing pass on a match, otherwise one containing fail
Source files 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: headers;
readme.txt: features, environment, and usage notes;
MDK-ARM/, EWARM/: Keil and IAR projects
Key configuration Clock: FEMC is clocked from AHB at a 240MHz system clock;
Device: external 16-bit SRAM IS61WV102416FBLL-10TLI on Block1 with 16-bit data width and a non-multiplexed address/data bus;
Pins: data PD14/PD15/PD0/PD1, PE7PE15,PD8PD10, address PF0PF5,PF12PF15, PG0PG5,PD11PD13, PE3, and control NOE:PD4, NWE:PD5, NE:PG9, NBL0:PE0, NBL1:PE1
Program and data path main calls FEMC_Sram_Init to configure pins and the bank;
fills a transmit buffer with Fill_Buffer_16 and writes it with FEMC_Sram_Write_Buffer;
reads it back with FEMC_Sram_Read_Buffer and compares word by word, counting mismatches
Main APIs FEMC_InitNorSramStruct, FEMC_InitNorSram, FEMC_EnableNorSram
Verification After building, flashing, and powering on, the UART prints a start message;
on a match it prints a pass message containing sram and pass, otherwise a fail message containing fail
Use note The hardware platform is the full-featured board N32H497ZGL7_EVB V1.0;
the toolchains are KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1;
the UART is USART1 (TX PA9, RX PA10, 115200);
the board has no on-board SRAM, so an external device is required
Core-code walkthrough and porting

The example first enables GPIO and FEMC clocks and configures pins and Block1, then writes a buffer and reads it back for comparison.

Data path: FEMC_Sram_Init enables clocks and configures pins -> FEMC_InitNorSramStruct and FEMC_InitNorSram load the bank configuration -> FEMC_EnableNorSram enables the bank -> main writes and reads back for comparison

1. Configure and initialize the external SRAM. FEMC_Sram_Init enables the FEMC clock, sets the timing and bank parameters for a 16-bit asynchronous SRAM, and enables the selected bank.

src/femc_sram.csrc/femc_sram.c · FEMC_Sram_Init
    /* 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 with a 16-bit data width matches the device used; the timing struct is assigned to both read and write, which suffices outside extended mode.

2. Write the buffer. FEMC_Sram_Write_Buffer derives the bank base address from the bank index, then writes halfwords and increments the address.

src/femc_sram.csrc/femc_sram.c · 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;
    }
}

The bank base address follows the bank index and the address advances by 2 to align halfwords; the read function mirrors this.

3. Read back and compare. main fills the transmit buffer, writes it, reads it back, counts mismatches, and prints the result.

src/main.csrc/main.c · 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 writes an IndexTmp + Offset sequence, and wr_status counts mismatches, so 0 means the read-back is correct.

Corresponding source files: src/main.c, src/femc_sram.c

5.13.3.2 PSRAM read/write check

Example directory: projects/n32h49x_EVAL/examples/FEMC/PSRAM/

Enable GPIO and FEMC clocks→Configure data, address, and control pins→Initialize the external PSRAM on Block3→Write a buffer→Read back, compare, and print the result

Item Source analysis
Function Writes data to an external 16-bit PSRAM through FEMC and reads it back for checking;
prints a pass message on a match, otherwise a fail message
Source files 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: headers;
readme.txt: features, environment, and usage notes;
MDK-ARM/, EWARM/: Keil and IAR projects
Key configuration Clock: FEMC is clocked from AHB at a 240MHz system clock;
Device: external 16-bit PSRAM IS66WV51216EBLLLL-70TLI on Block3 with 16-bit data width;
Pins: data and address as in the SRAM example, with control NOE:PD4, NWE:PD5, NE:PG8
Program and data path main calls FEMC_Psram_Init to configure pins and the bank;
fills a transmit buffer with Fill_Buffer_16 and writes it with FEMC_Psram_Write_Buffer;
reads it back with FEMC_Psram_Read_Buffer and compares with Buffer16cmp, then prints the result
Main APIs FEMC_InitNorSramStruct, FEMC_InitNorSram, FEMC_EnableNorSram
Verification After building, flashing, and powering on, the UART prints a start message;
on a match it prints a pass message containing psram and pass, otherwise a fail message containing fail
Use note The hardware platform is the full-featured board N32H497ZGL7_EVB V1.0;
the toolchains are KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1;
the UART is USART1 (TX PA9, RX PA10, 115200);
the board has no on-board PSRAM, so an external device is required
Core-code walkthrough and porting

The example mirrors the SRAM example, differing in the bank index, device type, and timing parameters.

Data path: FEMC_Psram_Init enables clocks and configures pins -> FEMC_InitNorSramStruct and FEMC_InitNorSram load the bank configuration -> FEMC_EnableNorSram enables the bank -> main writes and reads back for comparison

1. Configure and initialize the external PSRAM. FEMC_Psram_Init sets PSRAM timing parameters, selects the PSRAM device type, and initializes on Block3.

src/femc_psram.csrc/femc_psram.c · FEMC_Psram_Init
    /* 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 selects the PSRAM timing model; PSRAM needs a longer address setup time than SRAM, so adjust the timing per the device datasheet when porting.

2. Write the buffer. FEMC_Psram_Write_Buffer mirrors the SRAM writer: it selects the bank base address from the bank index and writes halfwords.

src/femc_psram.csrc/femc_psram.c · FEMC_Psram_Write_Buffer
        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;
    }
}

The bank base address maps one-to-one with the bank, so accesses land in the corresponding external PSRAM region.

3. Read back and compare. main selects Block3, writes, reads back, and compares with Buffer16cmp for a PASSED or FAILED result.

src/main.csrc/main.c · main
    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 compares halfword by halfword and returns a status; to port to another bank, only block_index and the pin macros change.

Corresponding source files: src/main.c, src/femc_psram.c

5.13.3.3 Nor_Flash read/write/erase check

Example directory: projects/n32h49x_EVAL/examples/FEMC/Nor_Flash/

Enable GPIO and FEMC clocks→Initialize the external NOR Flash on Block1→Read and print the device ID→Write a buffer and read it back for comparison→Erase the block and check for all 0xFF

Item Source analysis
Function Reads the ID of an external NOR Flash through FEMC and performs write, read-back check, and erase;
prints a message containing pass when both read/write and erase succeed, otherwise one containing fail
Source files 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: headers;
readme.txt: features, environment, and usage notes;
MDK-ARM/, EWARM/: Keil and IAR projects
Key configuration Clock: FEMC is clocked from AHB at a 240MHz system clock;
Device: external 16-bit NOR Flash S29GL01GS11TFI020 on Block1 with extended mode enabled;
Pins: data and address as above, with control NOE:PD4, NWE:PD5, NE:PB9, NWAIT:PD6
Program and data path main calls FEMC_Nor_Init to configure pins and the bank;
reads and prints the manufacturer and device IDs with FEMC_Nor_ReadID;
writes a buffer, reads it back for comparison, then erases the block and checks that the read-back is all 0xFFFF
Main APIs FEMC_InitNorSramStruct, FEMC_InitNorSram, FEMC_EnableNorSram
Verification After building, flashing, and powering on, the UART prints a start message and the device ID;
when read/write and erase both succeed it prints a message containing norflash and pass, otherwise one containing fail
Use note The hardware platform is the full-featured board N32H497ZGL7_EVB V1.0;
the toolchains are KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1;
the UART is USART1 (TX PA9, RX PA10, 115200);
the board has no on-board NOR Flash, so an external device is required
Core-code walkthrough and porting

Besides the read/write check, the example adds ID reading and block erase, then reads back after erase to check for all 0xFFFF.

Data path: FEMC_Nor_Init enables clocks and configures pins -> FEMC_Nor_ReadID reads the device ID -> FEMC_Nor_Write_Buffer_Halfword writes -> FEMC_Nor_Read_Buffer_Halfword reads back for comparison -> FEMC_Nor_Erase_Block erases and checks

1. Configure and initialize the external NOR Flash. FEMC_Nor_Init sets NOR timing and enables extended mode, then enables the selected bank.

src/femc_nor.csrc/femc_nor.c · FEMC_Nor_Init
    /* 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 selects the NOR timing model; with extended mode enabled, separate read and write timing registers become available.

2. Write the buffer and read it back. main reads the device ID, then fills and writes the buffer and reads it back for a first status.

src/main.csrc/main.c · main
    /*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 fetches the manufacturer and device IDs across two read cycles, and the buffer starts at 0x0001 for easy distinction.

3. Erase the block and check. The example erases the block containing the write address, then reads back to verify the whole range is 0xFFFF for a second status.

src/main.csrc/main.c · main
    /* 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");
    }

After a NOR erase the cells are all ones, so the read-back should be 0xFFFF; the test passes only when both statuses are PASSED.

Corresponding source files: src/main.c, src/femc_nor.c

5.13.3.4 Nand_Flash read/write/erase check

Example directory: projects/n32h49x_EVAL/examples/FEMC/Nand_Flash/

Enable GPIO and FEMC clocks→Initialize the external NAND Flash on Bank3→Reset and read the device ID→Erase the block and write a page→Read the page back and compare byte by byte

Item Source analysis
Function Erases a block, writes a page, and reads the page back for checking on an 8-bit NAND Flash through FEMC;
also prints the device ID and prints a message containing pass on a match, otherwise one containing fail
Source files 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: headers;
readme.txt: features, environment, and usage notes;
MDK-ARM/, EWARM/: Keil and IAR projects
Key configuration Clock: FEMC is clocked from AHB at a 240MHz system clock;
Device: external 8-bit NAND Flash IS34ML01G081-TLI on Bank3 with 8-bit width and ECC disabled in the example;
Pins: data PD14/PD15/PD0/PD1, PE7~PE10, with control NOE:PD4, NWE:PD5, CLE:PD11, ALE:PD12, NCE:PG9, NWAIT:PD6
Program and data path main calls FEMC_Nand_Init to configure pins and the bank;
after reset it reads the device ID, then erases the target block and writes one page;
it clears the buffer, reads the page back, and compares byte by byte for the result
Main APIs FEMC_InitNandStruct, FEMC_InitNand, FEMC_EnableNand
Verification After building, flashing, and powering on, the UART prints a start message, the device ID, and the erase/write/read status;
on a match it prints a message containing nandflash and pass, otherwise one containing fail
Use note The hardware platform is the evaluation board N32H497VEL7_STB V1.0;
the toolchains are KEIL MDK-ARM 5.25 or IAR EWARM 8.50.1;
the UART is USART1 (TX PA9, RX PA10, 115200);
the board has no on-board NAND Flash, so an external device is required
Core-code walkthrough and porting

The example initializes the NAND bank, resets and reads the ID, then erases a block, writes a page, and reads it back for a byte-by-byte comparison.

Data path: FEMC_Nand_Init enables clocks and configures pins -> FEMC_Nand_Reset and FEMC_Nand_ReadID -> FEMC_Nand_Erase_Block -> FEMC_Nand_WritePage_Byte writes -> FEMC_Nand_ReadPage_Byte reads back for comparison

1. Initialize the NAND bank. main sets an 8-bit width and Bank3, then calls the initialization function.

src/main.csrc/main.c · main
    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");

The width and bank index must match the FEMC_BANK_NAND configuration in femc_nand.c, otherwise the access address and the device disagree.

2. Configure the NAND timing and bank. FEMC_Nand_Init sets setup, hold, and high-Z times, picks the bus width, and enables the bank.

src/femc_nand.csrc/femc_nand.c · 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);

The example disables ECC and covers the command and attribute spaces with the timing; when ECC is enabled, empty the FIFO before toggling the ECC enable state.

3. Write and read a page. FEMC_Nand_WritePage_Byte issues the page-write command and addresses, writes a byte at a time, and polls the status for completion.

src/femc_nand.csrc/femc_nand.c · 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);
        }
    }

The address is validated first; the page write then sends the write command, column address, and row address before the data, and the block must be erased beforehand.

Corresponding source files: src/main.c, src/femc_nand.c

5.13.3.5 LCDDisplay driving an LCD through FEMC

Example directory: projects/n32h49x_EVAL/examples/FEMC/LCDDisplay/

Enable GPIO and FEMC clocks→Initialize the LCD interface through FEMC→Reset and read the LCD ID→Configure registers for the panel model→Display two LOGO images

Item Source analysis
Function Maps the LCD as external memory through FEMC and writes display data to it;
reads the LCD ID after reset, initializes the registers for the model, and displays LOGO images
Source files 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, and others;
src/fonts.c, src/logo1.c, src/logo2.c: font and image data;
inc/main.h, inc/lcd.h, inc/fonts.h, inc/n32h49x_it.h: headers;
readme.txt: features, environment, and usage notes;
MDK-ARM/, EWARM/: Keil and IAR projects
Key configuration Clock: FEMC is clocked from AHB at a 240MHz system clock;
Panel: TFT LCD 320240 (ILI9341) or 800480 (ILI9806G), selecting register initialization from the ID read back after reset;
Pins: CS:PG12, RS:PD13, WR:PD5, RD:PD4, RST:PH5, backlight PB5, with data D0~D15 as in the SRAM example
Program and data path main calls LCD_Configuration for clocks, pins, FEMC setup, and reset;
after reading the LCD ID it sets the resolution for the model and configures the registers;
LCD_Test clears the screen and draws two LOGO images
Main APIs FEMC_InitNorSramStruct, FEMC_InitNorSram, FEMC_EnableNorSram
Verification After building and running, the LCD shows two LOGO images in turn;
the UART prints the read-back LCD ID to confirm the panel model
Use note The hardware platform is the full-featured board N32H497ZGL7_EVB V1.0;
the toolchains are KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1;
the panel is 320240 (ILI9341) or 800480 (ILI9806G);
the backlight control pin is PB5, and the display is judged mainly by observing the screen
Core-code walkthrough and porting

The example accesses the LCD as an asynchronous memory device on FEMC: it initializes the interface and panel registers, then refreshes images to the screen.

Data path: main calls LCD_Configuration -> LCD_FEMC_Configuration sets up the FEMC interface -> LCD_Get_ID reads the model -> ILI9341_REG_Config or ILI9806G_REG_Config initializes -> LCD_Test draws the images

1. Initialize the LCD interface and configure by model. LCD_Configuration performs clocks, pins, FEMC setup, and reset, then chooses the resolution and register-init function from the read-back ID.

src/lcd.csrc/lcd.c · LCD_Configuration
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
    {
    
    }
}

Different panel models use different backlight active levels and resolutions; the example sets them per ID, so add a branch for a new panel when porting.

2. Main flow calls configuration and display. main initializes logging, then calls the configuration, orientation, and display functions.

src/main.csrc/main.c · main
int main(void)
{
    log_init();

#if defined(N32H49X)
    /* LCD configuration */
    LCD_Configuration();
    LCD_GramScan(0);
    LCD_Test();
#endif 

    while (1)
    {
    }
}

LCD_GramScan(0) sets the scan orientation and LCD_Test draws the actual picture; the main loop is empty because the picture is drawn once after initialization.

3. Draw the LOGO images. LCD_Test sets the font and colors, clears the screen, and draws the two LOGO images centered horizontally.

src/main.csrc/main.c · LCD_Test
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 writes pixel by pixel from the image struct; the coordinates use lcd_width and lcd_height, so they adapt to both resolutions.

Corresponding source files: src/main.c, src/lcd.c

5.13.4 Notes and troubleshooting

Scenario Check & action Source
XIP fetch restriction When fetching instructions from external memory (XIP), do not use multi-register or double-word access instructions such as LDM, STM, LDRD, and STRD;
they cross the fetch boundary and cause read errors.
User manual
NAND ECC switching Before toggling the NAND ECC enable state, wait for the ECC result FIFO to empty and disable before enabling;
otherwise the ECC result does not match the data.
User manual
Byte select and writes A write has no effect unless the byte select (NBL) is enabled;
the memory cell is written only after byte select and write enable are configured.
User manual
Unaligned access An unaligned access to FEMC memory is split into multiple accesses;
keep the read/write data width aligned with the access address.
User manual
Extended mode and timing The write timing register takes effect only when extended mode (EXTEN) is enabled;
with extended mode off, reads and writes share one timing set.
User manual
Data hold time The data hold time (DATAHLD) should be greater than 0;
otherwise the data is unstable during the hold phase.
User manual
Clock divider duty cycle When the divider CLKDIV is odd, the output clock does not have a 50% duty cycle;
choose an even divider for a symmetric clock.
User manual
Shared-clock bank phase Multiple banks on the same shared clock should use a consistent clock phase and access mode;
otherwise their access timings do not match.
User manual
Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.