5.21 USART Guide

This chapter describes the file composition and the 38 public functions of the USART driver in the N32G41x standard peripheral library, covering baud-rate and frame-format configuration, data transmission and reception, multiprocessor communication, LIN, SmartCard, IrDA, RS485 driver enable, receiver timeout, low-power wakeup, DMA and interrupt and flag management. This SDK provides 19 USART example projects. Read the register bit fields and the configuration order of each operating mode together with the “Universal synchronous asynchronous receiver transmitter (USART)” chapter of the user manual, and take the errata sheet as the authority on known chip issues.

5.21.1 Related files

Relative path Purpose
firmware/n32g41x_std_periph_driver/inc/n32g41x_usart.h Defines the USART_InitType and USART_ClockInitType structures with their word-length, stop-bit, parity, mode, flow-control and oversampling macros, and declares 38 public functions in functional sections
firmware/n32g41x_std_periph_driver/src/n32g41x_usart.c Implements those 38 functions: read-modify-write of the USART register fields for frame format, enable, guard time, timeout, interrupt and flags, covering polling transfer, DMA, synchronous, LIN, SmartCard, IrDA, RS485 and low-power wakeup
projects/n32g41x_EVAL/examples/USART/ Contains 19 examples: 485_DMA, 485_Master, 485_Slave, DMA_Interrupt, DMA_Polling_MaxBaudrate, HalfDuplex, HardwareFlowCtrl, Interrupt, LP_Transmit_Receive, Transmit_Receive, LIN_Master, LIN_Slave, MultiProcessor, Polling, Printf, SmartCard, Synchronous, WakeUpFromSleep, WakeUpFromStop
projects/n32g41x_EVAL/examples/USART/Polling/ Example project that demonstrates two-board communication by polling the status flags;
it contains MDK-ARM/, EWARM/, inc/main.h, inc/n32g41x_it.h, src/main.c, src/n32g41x_it.c and readme.txt
projects/n32g41x_EVAL/bsp/inc/n32g41x_stb.h Defines the port, pin and active-level macros of the evaluation board LEDs LED1 to LED3 and buttons KEY1 to KEY3 and WKUP, referenced by some USART examples

5.21.2 API reference

Search API38 functions

The following 38 public functions are listed in the declaration order of n32g41x_usart.h in 13 groups; each entry gives a summary and the original comment above the function in n32g41x_usart.c.

Reset and initialization

USART_DeInit, USART_Init, USART_StructInit, USART_ClockInit, USART_ClockStructInit, USART_Enable

Data transfer

USART_SendData, USART_ReceiveData, USART_SendBreak

Multiprocessor communication

USART_SetAddr, USART_ConfigWakeUpMode, USART_EnableRcvWakeUp

LIN mode

USART_ConfigLINBreakDetectLength, USART_EnableLIN

SmartCard mode

USART_SetGuardTime, USART_SetPrescaler, USART_EnableSmartCard, USART_SetSmartCardNACK

Half-duplex mode

USART_EnableHalfDuplex

IrDA mode

USART_ConfigIrDAMode, USART_EnableIrDA

RS485 driver enable

USART_CfgDriverAssertTime, USART_CfgDriverdeassertTime, USART_DriverPolaritySet, USART_DriverModeSet

Receiver timeout

USART_RTOSet, USART_CfgRTOWidth

Miscellaneous configuration

USART_FirstByteDelayFixSet, USART_PinSwapSet

Low-power wakeup

USART_EnableWakeUp, USART_CfgWakeUpDataClr

DMA

USART_EnableDMA

Interrupt and flag management

USART_ConfigInt, USART_GetFlagStatus, USART_ClrFlag, USART_ClrRTOFlag, USART_GetIntStatus, USART_ClrIntPendingBit

void USART_DeInit(const USART_Module *USARTx)Deinitializes the USARTx peripheral registers to their default reset values.
Source comment
name    USART_DeInit
fun     Deinitializes the USARTx peripheral registers to their default reset values.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
void USART_Init(USART_Module *USARTx, const USART_InitType *USART_InitStruct)Initializes the USARTx peripheral according to USART_InitStruct.
Source comment
name    USART_Init
fun     Initializes the USARTx peripheral according to USART_InitStruct.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_InitStruct (The input parameters must be the following values):
          - BaudRate :
            - (((BaudRate) > 0) && ((BaudRate) < 0x00989680)) (0-10 Mbit/s)
          - WordLength
            - USART_WL_8B
            - USART_WL_9B
          - StopBits
            - USART_STPB_1
            - USART_STPB_0_5
            - USART_STPB_2
            - USART_STPB_1_5
          - Parity
            - USART_PE_NO
            - USART_PE_EVEN
            - USART_PE_ODD
          - Mode
            - USART_MODE_RX
            - USART_MODE_TX
          - HardwareFlowControl
            - USART_HFCTRL_NONE
            - USART_HFCTRL_RTS
            - USART_HFCTRL_CTS
            - USART_HFCTRL_RTS_CTS
          - OverSampling
            - USART_8OVER
            - USART_16OVER
return  none
void USART_StructInit(USART_InitType *USART_InitStruct)Fills each USART_InitStruct member with its default value.
Source comment
name    USART_StructInit.
fun     Fills each USART_InitStruct member with its default value.
param   USART_InitStruct (The input parameters must be the following values):
          - BaudRate
          - WordLength
          - StopBits
          - Parity
          - Mode
          - HardwareFlowControl
          - OverSampling
return  none
void USART_ClockInit(USART_Module *USARTx, const USART_ClockInitType *USART_ClockInitStruct)Initializes the USARTx peripheral Clock according to the specified parameters in the USART_ClockInitStruct.
Source comment
name    USART_ClockInit
fun     Initializes the USARTx peripheral Clock according to the specified parameters in the USART_ClockInitStruct.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_ClockInitStruct (The input parameters must be the following values):
          - Clock :
            - USART_CLK_DISABLE
            - USART_CLK_ENABLE
          - Polarity :
            - USART_CLKPOL_LOW
            - USART_CLKPOL_HIGH
          - Phase :
            - USART_CLKPHA_1EDGE
            - USART_CLKPHA_2EDGE
          - LastBit :
            - USART_CLKLB_DISABLE
            - USART_CLKLB_ENABLE
return  none
note    Polarity, Phase and LastBit can be configured only when both the transmitter and
        the receiver are disabled (USART_CTRL1.TXEN=0 and USART_CTRL1.RXEN=0).
void USART_ClockStructInit(USART_ClockInitType *USART_ClockInitStruct)Fills each USART_ClockInitStruct member with its default value.
Source comment
name    USART_ClockStructInit
fun     Fills each USART_ClockInitStruct member with its default value.
param   USART_ClockInitStruct (The output structure to be filled with the following values):
          - Clock : USART_CLK_DISABLE
          - Polarity : USART_CLKPOL_LOW
          - Phase : USART_CLKPHA_1EDGE
          - LastBit : USART_CLKLB_DISABLE
return  none
void USART_Enable(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the specified USART peripheral.
Source comment
name    USART_Enable
fun     Enables or disables the specified USART peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SendData(USART_Module *USARTx, uint32_t Data)Transmits single data through the USARTx peripheral.
Source comment
name    USART_SendData
fun     Transmits single data through the USARTx peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Data :
          - the data to transmit
return  none
uint32_t USART_ReceiveData(const USART_Module *USARTx)Returns the most recent received data by the USARTx peripheral.
Source comment
name    USART_ReceiveData
fun     Returns the most recent received data by the USARTx peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  USARTx->DAT value
void USART_SendBreak(USART_Module *USARTx)Transmits break characters.
Source comment
name    USART_SendBreak
fun     Transmits break characters.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
void USART_SetAddr(USART_Module *USARTx, uint8_t USART_Addr)Sets the address of the USART node.
Source comment
name    USART_SetAddr
fun     Sets the address of the USART node.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_Addr (The input parameters must be the following values):
          - (((USART_Addr)>=0x00)&&((USART_Addr)<0x10))
return  none
void USART_ConfigWakeUpMode(USART_Module *USARTx, uint32_t USART_WakeUpMode)Selects the USART WakeUp method.
Source comment
name    USART_ConfigWakeUpMode
fun     Selects the USART WakeUp method.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_WakeUpMode (The input parameters must be the following values):
          - USART_WUM_IDLELINE WakeUp by an idle line detection
          - USART_WUM_ADDRMASK WakeUp by an address mark
return  none
void USART_EnableRcvWakeUp(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the USART mute mode (receiver wake-up).
Source comment
name    USART_EnableRcvWakeUp
fun     Enables or disables the USART mute mode (receiver wake-up).
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigLINBreakDetectLength(USART_Module *USARTx, uint32_t USART_LINBreakDetectLength)Sets the USART LIN Break detection length.
Source comment
name    USART_ConfigLINBreakDetectLength
fun     Sets the USART LIN Break detection length.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_LINBreakDetectLength (The input parameters must be the following values):
          - USART_LINBDL_10B 10-bit break detection
          - USART_LINBDL_11B 11-bit break detection
return  none
void USART_EnableLIN(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the USART’s LIN mode.
Source comment
name    USART_EnableLIN
fun     Enables or disables the USART's LIN mode.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SetGuardTime(USART_Module *USARTx, uint8_t USART_GuardTime)Sets the specifies USART guard time.
Source comment
name    USART_SetGuardTime
fun     Sets the specifies USART guard time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_GuardTime:
          - specifies the guard time
return  none
void USART_SetPrescaler(USART_Module *USARTx, uint8_t USART_Prescaler)Sets the system clock prescaler.
Source comment
name    USART_SetPrescaler
fun     Sets the system clock prescaler.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_Prescaler:
          - specifies the prescaler clock
return  none
void USART_EnableSmartCard(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the USART’s Smart Card mode.
Source comment
name    USART_EnableSmartCard
fun     Enables or disables the USART's Smart Card mode.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SetSmartCardNACK(USART_Module *USARTx, FunctionalState Cmd)Enables or disables NACK transmission.
Source comment
name    USART_SetSmartCardNACK
fun     Enables or disables NACK transmission.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_EnableHalfDuplex(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the USART’s Half Duplex communication.
Source comment
name    USART_EnableHalfDuplex
fun     Enables or disables the USART's Half Duplex communication.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigIrDAMode(USART_Module *USARTx, uint32_t USART_IrDAMode)Configures the USART’s IrDA interface.
Source comment
name    USART_ConfigIrDAMode
fun     Configures the USART's IrDA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_IrDAMode (The input parameters must be the following values):
          - USART_IRDAMODE_LOWPPWER
          - USART_IRDAMODE_NORMAL
return  none
void USART_EnableIrDA(USART_Module *USARTx, FunctionalState Cmd)Enables or disables the USART’s IrDA interface.
Source comment
name    USART_EnableIrDA
fun     Enables or disables the USART's IrDA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_CfgDriverAssertTime(USART_Module *USARTx, uint32_t Time)Set Driver Enable assertion time.
Source comment
name    USART_CfgDriverAssertTime
fun     Set Driver Enable assertion time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void USART_CfgDriverdeassertTime(USART_Module *USARTx, uint32_t Time)Set Driver Enable deassertion time.
Source comment
name    USART_CfgDriverdeassertTime
fun     Set Driver Enable deassertion time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void USART_DriverPolaritySet(USART_Module *USARTx, FunctionalState Cmd)Driver enable polarity selection.
Source comment
name    USART_DriverPolaritySet
fun     Driver enable polarity selection.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_DriverModeSet(USART_Module *USARTx, FunctionalState Cmd)Driver enable mode selection.
Source comment
name    USART_DriverModeSet
fun     Driver enable mode selection.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_RTOSet(USART_Module *USARTx, FunctionalState Cmd)Receiver timeout enable or disable.
Source comment
name    USART_RTOSet
fun     Receiver timeout enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_CfgRTOWidth(USART_Module *USARTx, uint32_t Width)configure receive timeout width.
Source comment
name    USART_CfgRTOWidth
fun     configure receive timeout width.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Width:(The input parameters must be the following values)
          - 0x00000000 ~ 0x0FFFFFFF
return  none
void USART_FirstByteDelayFixSet(USART_Module *USARTx, FunctionalState Cmd)First byte delay fixed enable or disable.
Source comment
name    USART_FirstByteDelayFixSet
fun     First byte delay fixed enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE  : the first byte delay is fixed     (USART_CTRL1.TEDEN = 0)
          - DISABLE : the first byte delay is not fixed (USART_CTRL1.TEDEN = 1)
return  none
void USART_PinSwapSet(USART_Module *USARTx, FunctionalState Cmd)Swap TX/RX pins enable or disable.
Source comment
name    USART_PinSwapSet
fun     Swap TX/RX pins enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_EnableWakeUp(USART_Module *USARTx, FunctionalState Cmd)configure when USART in lowpower mode, receive a byte will wakeup system.
Source comment
name    USART_EnableWakeUp
fun     configure when USART in lowpower mode, receive a byte will wakeup system.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART4
param   Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_CfgWakeUpDataClr(USART_Module *USARTx, uint32_t WakeupMode)configure when USART wakeup from low power mode, can select whether clear wakeup data.
Source comment
name    USART_CfgWakeUpDataClr
fun     configure when USART wakeup from low power mode, can select whether clear wakeup data.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART4
param   WakeupMode:(The input parameters must be the following values)
          - USART_WAKEUP_CLRDATA
          - USART_WAKEUP_NOTCLRDATA
return  none
void USART_EnableDMA(USART_Module *USARTx, uint32_t USART_DMAReq, FunctionalState Cmd)Enables or disables the USART’s DMA interface.
Source comment
name    USART_EnableDMA
fun     Enables or disables the USART's DMA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_DMAReq (The input parameters must be the following values):
          - USART_DMAREQ_TX       USART DMA transmit request
          - USART_DMAREQ_RX       USART DMA receive request
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigInt(USART_Module *USARTx, uint32_t USART_INT, FunctionalState Cmd)Enables or disables the specified USART interrupts.
Source comment
name    USART_ConfigInt
fun     Enables or disables the specified USART interrupts.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_PEF   Parity Error interrupt
          - USART_INT_TXC   Transmission complete interrupt
          - USART_INT_TXDE  Transmit Data Register empty interrupt
          - USART_INT_RXDNE Receive Data register not empty interrupt
          - USART_INT_IDLEF Idle line detection interrupt
          - USART_INT_RTOE  Receiver timeout interrupt
          - USART_INT_LINBD LIN Break detection interrupt
          - USART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
          - USART_INT_CTSF  CTS change interrupt
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
FlagStatus USART_GetFlagStatus(const USART_Module *USARTx, uint32_t USART_FLAG)Checks whether the specified USART flag is set or not.
Source comment
name    USART_GetFlagStatus
fun     Checks whether the specified USART flag is set or not.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_FLAG (The input   parameters must be the following values):
          - USART_FLAG_RTO      receiver timeout Flag
          - USART_FLAG_FEF      Framing error Flag
          - USART_FLAG_NEF      Noise error flag Flag
          - USART_FLAG_OREF     Overrun error
          - USART_FLAG_PEF      Parity error
          - USART_FLAG_LINBD    LIN break detection flag
          - USART_FLAG_CTSF     CTS Change flag
          - USART_FLAG_RXDNE    Receive data register not empty flag
          - USART_FLAG_TXC      Transmission Complete flag
          - USART_FLAG_TXDE     Transmit data register empty flag
          - USART_FLAG_IDLEF    Idle Line detection flag
return  SET or RESET
          - SET   : the specified flag is set
          - RESET : the specified flag is reset
void USART_ClrFlag(USART_Module *USARTx, uint32_t USART_FLAG)Clears the USARTx’s pending flags.
Source comment
name    USART_ClrFlag
fun     Clears the USARTx's pending flags.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_FLAG (The input   parameters must be the following values):
          - USART_FLAG_LINBD    LIN break detection flag
          - USART_FLAG_CTSF     CTS Change flag
          - USART_FLAG_RXDNE    Receive data register not empty flag
          - USART_FLAG_TXC      Transmission Complete flag
return  none
note
          - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun
            error) and IDLE (Idle line detected) flags are cleared by software
            sequence: a read operation to USART_STS register (USART_GetFlagStatus())
            followed by a read operation to USART_DAT register (USART_ReceiveData()).
          - RXNE flag can be also cleared by a read to the USART_DAT register
            (USART_ReceiveData()).
          - TC flag can be also cleared by software sequence: a read operation to
            USART_STS register (USART_GetFlagStatus()) followed by a write operation
            to USART_DAT register (USART_SendData()).
          - TXE flag is cleared only by a write to the USART_DAT register
            (USART_SendData()).
          - RTO flag is cleared only by a write 1 to the USART_CTRL2.RTOCF bit.
            (USART_ClrRTOFlag()).
void USART_ClrRTOFlag(USART_Module *USARTx)Clears the USARTx’s RTO pending flag.
Source comment
name    USART_ClrRTOFlag
fun     Clears the USARTx's RTO pending flag.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
INTStatus USART_GetIntStatus(const USART_Module *USARTx, uint32_t USART_INT)Checks whether the specified USART interrupt has set or not.
Source comment
name    USART_GetIntStatus
fun     Checks whether the specified USART interrupt has set or not.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_PEF   Parity Error interrupt
          - USART_INT_TXC   Transmission complete interrupt
          - USART_INT_TXDE  Transmit Data Register empty interrupt
          - USART_INT_RXDNE Receive Data register not empty interrupt
          - USART_INT_IDLEF Idle line detection interrupt
          - USART_INT_RTOE  Receiver timeout interrupt
          - USART_INT_LINBD LIN Break detection interrupt
          - USART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
          - USART_INT_CTSF  CTS change interrupt
return  SET or RESET
          - SET   : the interrupt enable bit is set (the interrupt is enabled)
          - RESET : the interrupt enable bit is not set (the interrupt is disabled)
void USART_ClrIntPendingBit(USART_Module *USARTx, uint32_t USART_INT)Clears the USARTx’s interrupt pending bits.
Source comment
name    USART_ClrIntPendingBit
fun     Clears the USARTx's interrupt pending bits.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_LINBD    LIN break detection interrupt
          - USART_INT_CTSF     CTS Change interrupt
          - USART_INT_RXDNE    Receive data register not empty interrupt
          - USART_INT_TXC      Transmission Complete interrupt
return  none
note
          - Only the four flags that USART_STS defines as rc_w0 can be cleared by a write:
            LINBDF, CTSF, RXDNE and TXC. The other pending bits are read-only and are
            cleared as follows:
          - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun
            error) and IDLE (Idle line detected) pending bits are cleared by
            software sequence: a read operation to USART_STS register (USART_GetFlagStatus())
            followed by a read operation to USART_DAT register (USART_ReceiveData()).
          - RXNE pending bit can be also cleared by a read to the USART_DAT register (USART_ReceiveData()).
          - TC pending bit can be also cleared by software sequence: a read
            operation to USART_STS register (USART_GetFlagStatus()) followed by a write
            operation to USART_DAT register (USART_SendData()).
          - TXE pending bit is cleared only by a write to the USART_DAT register
            (USART_SendData()).
          - RTO flag is cleared only by a write 1 to the USART_CTRL2.RTOCF bit.
            (USART_ClrRTOFlag()).

Use note: n32g41x_usart.h declares this function twice, at lines 341 and 342, as USART_Module *USARTx and USART_Module* USARTx; n32g41x_usart.c provides a single implementation.

5.21.3 Demo descriptions

This section describes the 19 USART examples, covering polling, interrupt, DMA, RS485, single-wire half-duplex, hardware flow control, IrDA infrared, LIN, multiprocessor, smartcard, synchronous, and low-power wakeup transfers.

5.21.3.1 485_DMA RS485 DMA transfer

Example directory: projects/n32g41x_EVAL/examples/USART/485_DMA/

Enable the DMA, GPIO, and both USART clocks→Configure the TX and DE pins of USART1 and the RX and DE pins of USART2 as alternate-function push-pull→Configure one transmit channel and one receive channel→Enable automatic RS485 DE signal control on both USARTs→Enable the transmit and receive DMA requests and start the channels→Wait for both DMA channels to transfer complete→Compare the buffers and store the result in TransferStatus2

Item Source analysis
Function The example performs one RS485 transfer between USART1 (USARTy) and USART2 (USARTz) through DMA: USART1 sends TxBuffer1 over DMA and USART2 receives it into RxBuffer2 over DMA, and the two buffers are compared at the end.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, and Buffercmp;
inc/main.h: the pin, DMA channel, and remap macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9, Rx PA10, DE PA12) and USARTz is USART2 (Tx PA2, Rx PA3, DE PA1);
except that PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, the other pins all use GPIO_AF4 as alternate-function push-pull with pull-up.
The baud rate is 115200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling, with transmit and receive enabled;
on both ports USART_DriverModeSet is enabled and USART_DriverPolaritySet is set to DISABLE (active-low DE).
On DMA, USART1_TX uses DMA_CH1 and USART2_RX uses DMA_CH4. The system clock is HSI+PLL: 80 MHz on N32G412 and 96 MHz on N32G415.
Program and data path main calls RCC_Configuration, GPIO_Configuration, and DMA_Configuration in turn, fills defaults with USART_StructInit and configures both ports with USART_Init, then configures the RS485 DE, enables the DMA requests and channels, and starts both ports with USART_Enable;
two while loops poll the transmit and receive DMA transfer-complete flags, and Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1) stores the result in TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_CfgDriverAssertTime, USART_CfgDriverdeassertTime, USART_DriverPolaritySet, USART_DriverModeSet, USART_EnableDMA, USART_Enable, DMA_StructInit, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification After reset, check the variable TransferStatus2 in the debugger: PASSED means the data received by USART2 matches what USART1 sent, and FAILED means they differ.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
externally wire USART1_TX(PA9) to USART2_RX(PA3).
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1, and the supported devices are N32G412x8L7, N32G412xBL7, N32G415x8L7, and N32G415xBL7.
Core-code walkthrough and porting

The example hands both the transmit of USART1 and the receive of USART2 to the DMA, uses the RS485 DE signal to switch the direction automatically, and compares the buffers at the end.

Data path: main → USART_Init configures USART1/USART2 → USART_DriverModeSet enables RS485 DE → DMA_Init/DMA_RequestRemap sets up the transmit and receive channels → the DMA moves TxBuffer1 to the USART1 data register and the USART2 data register to RxBuffer2 → Buffercmp stores the result in TransferStatus2

1. Enable automatic RS485 DE control and open the DMA requests. Both ports first set the DE assert and deassert times and the polarity, then enable the driver mode so the transceiver direction is switched automatically by hardware according to the transmit state; the USART1 transmit request and the USART2 receive request are then connected to the DMA.

src/main.csrc/main.c · main
    /* Configure USARTy 485 function */
    USART_CfgDriverAssertTime(USARTy, 0);
    USART_CfgDriverdeassertTime(USARTy, 0);
    USART_DriverPolaritySet(USARTy, DISABLE);
    USART_DriverModeSet(USARTy, ENABLE);
    /* Configure USARTz 485 function */
    USART_CfgDriverAssertTime(USARTz, 0);
    USART_CfgDriverdeassertTime(USARTz, 0);
    USART_DriverPolaritySet(USARTz, DISABLE);
    USART_DriverModeSet(USARTz, ENABLE);

    /* Enable USARTy DMA TX request */
    USART_EnableDMA(USARTy, USART_DMAREQ_TX, ENABLE);
    /* Enable USARTz DMA Rx request */
    USART_EnableDMA(USARTz, USART_DMAREQ_RX, ENABLE);

When porting to another board, check the DE pin, its alternate-function number, and the active polarity together; if the DE pin is wired wrong the direction cannot switch with the transmit state.

2. Describe the transmit transfer with a DMA channel. The transmit channel targets the peripheral data register from TxBuffer1, with byte width, no peripheral increment, memory increment, normal mode, and the highest priority, and the channel is bound to the USART1_TX event through the request remap.

src/main.csrc/main.c · DMA_Configuration
    /* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
    DMA_DeInit(USARTy_Tx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = USARTy_DAT_Base;
    DMA_InitStructure.MemAddr        = (uint32_t)TxBuffer1;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = TxBufferSize1;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(USARTy_Tx_DMA_REMAP, DMA, USARTy_Tx_DMA_Channel, ENABLE);

The receive channel reuses the same structure and only changes the peripheral address, memory address, and direction; when the buffer or its length changes, update BufSize and TxBufferSize1 together.

3. Poll for completion and compare the result. After both ports start, the main loop waits for the transmit and receive channel transfer-complete flags; once both are set it compares TxBuffer1 with RxBuffer2 and stores the verdict in TransferStatus2.

src/main.csrc/main.c · main
    /* Wait until USARTy TX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTy_Tx_DMA_FLAG, DMA) == RESET)
    {
    }

    /* Wait until USARTz RX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTz_Rx_DMA_FLAG, DMA) == RESET)
    {
    }

    TransferStatus2 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
    /* TransferStatus2 = PASSED, if the data transmitted from USARTy and
       received by USARTz are the same */
    /* TransferStatus2 = FAILED, if the data transmitted from USARTy and
       received by USARTz are different */

The two wait flags belong to their own channels; when the channel number changes, update the flag and remap macros together, otherwise an unused channel is polled.

Corresponding source files: src/main.c, inc/main.h

5.21.3.2 485_Master RS485 master interrupt transfer

Example directory: projects/n32g41x_EVAL/examples/USART/485/485_Master/

Enable the GPIO and USART2 clocks and configure the USART2 interrupt→Configure the TX, RX, and DE pins of USART2 as alternate-function push-pull→Configure USART2 for automatic RS485 DE control→Enable the transmit and receive interrupts→In the main loop wait for the data echoed by the peer→Compare the buffers after all bytes arrive and print the result

Item Source analysis
Function The example runs an interrupt-driven RS485 transfer on USART2 between two boards (DE signal under automatic control): the master sends TxBuffer2 to the slave over the interrupt, then receives the data echoed by the slave, compares it, and prints the result.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
inc/main.h: the pin, DE, and interrupt macros of USART2;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTz_IRQHandler and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
The peer is the 485_Slave/ subproject in the same directory.
Key configuration USARTz is USART2, with Tx on PA2, Rx on PA3, and DE on PA1, all as GPIO_AF4 alternate-function push-pull with pull-up;
the baud rate is 115200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling, with transmit and receive enabled;
the DE is set with USART_CfgDriverAssertTime(USARTz, 1), USART_CfgDriverdeassertTime(USARTz, 0), USART_DriverPolaritySet(USARTz, DISABLE), and USART_DriverModeSet(USARTz, ENABLE). In the NVIC with priority group 2, USART2_IRQn has preemption priority NVIC_PRE_PRIORITY_0 and subpriority NVIC_SUB_PRIORITY_1. System clock HSI+PLL: 80 MHz (N32G412), 96 MHz (N32G415).
Program and data path main calls log_init, RCC_Configuration, NVIC_Configuration, and GPIO_Configuration, configures and calls USART_Init for USART2 and the RS485 DE, opens USART_INT_TXDE and USART_INT_RXDNE, and starts with USART_Enable;
the interrupt stores the received bytes in RxBuffer2 and sends TxBuffer2 byte by byte, and the main loop waits with while (RxCounter2 < RxBufferSize2), then compares with Buffercmp and prints OK or Fail.
Main APIs USART_StructInit, USART_Init, USART_CfgDriverAssertTime, USART_CfgDriverdeassertTime, USART_DriverPolaritySet, USART_DriverModeSet, USART_ConfigInt, USART_Enable, USART_GetFlagStatus, USART_GetIntStatus, USART_ReceiveData, USART_SendData
Verification Download the master and slave programs to the two boards, reset the slave first and the master next, and watch the UART output: N32G41x 485 Master test OK means pass and N32G41x 485 Master test Fail means a fault.
Use note Two evaluation boards and two RS485 transceiver modules are needed, wired A-A and B-B, and the MCU_TX and MCU_RX jumpers of the NS-LINK must be disconnected first;
the slave must be powered up and running before the master triggers it.
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example lets USART2 complete a master-slave transfer over the transmit and receive interrupts in RS485 mode, and the main loop only waits for the receive count.

Data path: main → USART_Init configures USART2 → USART_DriverModeSet enables RS485 DE → USART_ConfigInt opens the transmit and receive interrupts → USARTz_IRQHandler moves bytes between TxBuffer2/RxBuffer2 → the main loop compares with Buffercmp and prints

1. Open the RS485 DE and the transmit/receive interrupts. The DE assert time is set to 1 sampling unit, the polarity is active-low, and the driver mode is enabled; then the transmit and receive interrupts are opened so that both directions are handled by USARTz_IRQHandler.

src/main.csrc/main.c · main
    /* RS485 Driver Enable configuration */
    USART_CfgDriverAssertTime(USARTz, 1);
    USART_CfgDriverdeassertTime(USARTz, 0);
    USART_DriverPolaritySet(USARTz, DISABLE);
    USART_DriverModeSet(USARTz, ENABLE);

    /* Enable USARTz Receive and Transmit interrupts */
    USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE);
    USART_ConfigInt(USARTz, USART_INT_RXDNE, ENABLE);

When porting to another board, check the DE pin and the active polarity together; in driver mode USART_CfgDriverAssertTime must not be set below a valid value.

2. Receive first, then transmit, inside the interrupt. The handler checks the receive flag first, stores the received byte in RxBuffer2, and disables the receive interrupt when the count is reached; then it checks the transmit flag, writes the next byte of TxBuffer2 to the data register, and disables the transmit interrupt when the transmit count is reached.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
void USARTz_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer2[RxCounter2++] = USART_ReceiveData(USARTz);

        if (RxCounter2 == NbrOfDataToRead1)
        {
            /* Disable the USARTz Receive interrupt */
            USART_ConfigInt(USARTz, USART_INT_RXDNE, DISABLE);
        }
    }

    if ((USART_GetFlagStatus(USARTz, USART_FLAG_TXDE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_TXDE) != RESET))
    {
        /* Write one byte to the transmit data register */
        USART_SendData(USARTz, TxBuffer2[TxCounter2++]);

        if (TxCounter2 == NbrOfDataToTransfer2)
        {
            /* Disable the USARTz Transmit interrupt */
            USART_ConfigInt(USARTz, USART_INT_TXDE, DISABLE);
        }
    }

The receive and transmit limits come from NbrOfDataToRead1 and NbrOfDataToTransfer2; when the buffer length changes, update both count limits.

3. Clear the error flags in the interrupt. Besides the data handling, the handler also checks the overrun, noise, parity, and framing error flags, and once one is set it reads the status register first and then the data register to clear the error flags.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
    /* Determine if an error flag still exists and clear the error flag */
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET) ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_NEF) != RESET) ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_PEF) != RESET) ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_FEF) != RESET))
    {
        /*Read the sts register first,and the read the DAT register to clear the all error flag*/
        (void)USARTz->STS;
        (void)USARTz->DAT;

The clear sequence relies on reading STS before DAT; for another peripheral, do not clear only one of the two registers.

4. Wait for all bytes and print the result. The main program waits until RxCounter2 reaches the receive length, compares TxBuffer2 with RxBuffer2, and prints the pass or fail line according to TransferStatus2.

src/main.csrc/main.c · main
    /* Wait until end of transmission from Slave */
    while (RxCounter2 < RxBufferSize2)
    {
    }

    TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer2, TxBufferSize2);
    if (TransferStatus2 == PASSED)
    {
        log_info(" N32G41x 485 Master test OK \r\n");
    }
    else
    {
        log_info(" N32G41x 485 Master test Fail \r\n");
    }

The wait condition uses the receive buffer length RxBufferSize2; when the content length of TxBuffer2 changes, update this length or the main loop waits forever.

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

5.21.3.3 485_Slave RS485 slave interrupt transfer

Example directory: projects/n32g41x_EVAL/examples/USART/485/485_Slave/

Enable the GPIO and USART2 clocks and configure the USART2 interrupt→Configure the TX, RX, and DE pins of USART2 as alternate-function push-pull→Configure USART2 for automatic RS485 DE control and open only the receive interrupt→In the main loop wait for the data sent by the master→Compare the buffers after all bytes arrive and print the result→Enable the transmit interrupt and echo the data back to the master

Item Source analysis
Function The example runs the interrupt-driven USART2 RS485 transfer between two boards: the slave first waits for the data sent by the master, compares it with the expected data and prints the result once all bytes arrive, and then echoes the same buffer back to the master over the interrupt.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
inc/main.h: the pin, DE, and interrupt macros of USART2;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTz_IRQHandler and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
The peer is the 485_Master/ subproject in the same directory.
Key configuration Same as the master: USARTz (USART2) uses PA2/PA3/PA1 for Tx/Rx/DE, all GPIO_AF4 alternate-function push-pull with pull-up;
baud rate 115200, 8 data bits, no parity, 1 stop bit, no flow control, and 16-times oversampling, with TX/RX enabled;
the DE is set with USART_CfgDriverAssertTime(USARTz, 1), USART_CfgDriverdeassertTime(USARTz, 0), USART_DriverPolaritySet(USARTz, DISABLE), and USART_DriverModeSet(USARTz, ENABLE). At startup only USART_INT_RXDNE is open, and USART_INT_TXDE is opened only after all bytes arrive.
NVIC group 2: preemption NVIC_PRE_PRIORITY_0, subpriority NVIC_SUB_PRIORITY_1.
Program and data path main configures the clocks, NVIC, GPIO, USART2, and the RS485 DE, opens the receive interrupt first and calls USART_Enable;
while (RxCounter2 < RxBufferSize2) waits for all bytes, then Buffercmp compares and prints OK or Fail, and USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE) opens the transmit interrupt, with USARTz_IRQHandler sending TxBuffer2 byte by byte.
Main APIs USART_StructInit, USART_Init, USART_CfgDriverAssertTime, USART_CfgDriverdeassertTime, USART_DriverPolaritySet, USART_DriverModeSet, USART_ConfigInt, USART_Enable, USART_GetFlagStatus, USART_GetIntStatus, USART_ReceiveData, USART_SendData
Verification Download the slave and master programs to the two boards, reset the slave first and the master next, and watch the UART output: N32G41x 485 Slave test OK means pass and N32G41x 485 Slave test Fail means a fault.
Use note Two evaluation boards and two RS485 transceiver modules are needed, wired A-A and B-B, and the MCU_TX and MCU_RX jumpers of the NS-LINK must be disconnected first;
the slave must be powered up and running first, then triggered by the master.
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example keeps USART2 in receive-only state waiting for the master, and after the data is received and compared it opens the transmit interrupt to echo the data back.

Data path: main → USART_ConfigInt(USART_INT_RXDNE) opens only the receive interrupt → USARTz_IRQHandler stores the received bytes in RxBuffer2 → Buffercmp compares and prints → USART_ConfigInt(USART_INT_TXDE) opens the transmit interrupt → the interrupt echoes TxBuffer2 back

1. Open only the receive interrupt and start. The slave initially uses the receive interrupt alone, prints a prompt after starting, and waits; the direction is switched only after the comparison is done.

src/main.csrc/main.c · main
    /* Enable USARTz Receive interrupt (Slave waits for data first) */
    USART_ConfigInt(USARTz, USART_INT_RXDNE, ENABLE);

    /* Enable the USARTz */
    USART_Enable(USARTz, ENABLE);
    log_info(" N32G41x 485 Slave test START, Press Master 485 to reset the pins \r\n");

The slave shares the same pin and DE configuration as the master; when porting, keep the baud rate, word length, and DE polarity identical on both ends.

2. Open the transmit interrupt after the comparison. The main loop waits until the receive count reaches the length, compares TxBuffer2 with RxBuffer2 and prints the result, and then opens the transmit interrupt so that the echo is handled by the interrupt.

src/main.csrc/main.c · main
    /* Wait until all data received from Master */
    while (RxCounter2 < RxBufferSize2)
    {
    }

    TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer2, TxBufferSize2);
    if (TransferStatus2 == PASSED)
    {
        log_info(" N32G41x 485 Slave test OK \r\n");
    }
    else
    {
        log_info(" N32G41x 485 Slave test Fail \r\n");
    }

    /* Now echo data back: enable Transmit interrupt */
    USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE);

The transmit and receive count limits both come from the buffer length macros; when the echo length changes, update TxBufferSize2 and RxBufferSize2 together.

3. Move the bytes inside the interrupt. USARTz_IRQHandler stores received bytes in RxBuffer2 and disables the receive interrupt once enough bytes arrive; when the transmit interrupt fires it takes a byte from TxBuffer2 into the data register and disables the transmit interrupt after the last byte.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
void USARTz_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer2[RxCounter2++] = USART_ReceiveData(USARTz);

        if (RxCounter2 == NbrOfDataToRead1)
        {
            /* Disable the USARTz Receive interrupt */
            USART_ConfigInt(USARTz, USART_INT_RXDNE, DISABLE);
        }
    }

    if ((USART_GetFlagStatus(USARTz, USART_FLAG_TXDE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_TXDE) != RESET))
    {
        /* Write one byte to the transmit data register */
        USART_SendData(USARTz, TxBuffer2[TxCounter2++]);

        if (TxCounter2 == NbrOfDataToTransfer2)
        {
            /* Disable the USARTz Transmit interrupt */
            USART_ConfigInt(USARTz, USART_INT_TXDE, DISABLE);
        }
    }

The slave echoes the same buffer it received into; when the echo content changes, check the count limits of both directions together.

4. Enable automatic RS485 DE control. The DE assert time, polarity, and driver mode are the same as on the master, and the direction is still switched automatically by hardware according to the transmit state.

src/main.csrc/main.c · main
    /* RS485 Driver Enable configuration */
    USART_CfgDriverAssertTime(USARTz, 1);
    USART_CfgDriverdeassertTime(USARTz, 0);
    USART_DriverPolaritySet(USARTz, DISABLE);
    USART_DriverModeSet(USARTz, ENABLE);

The DE configuration of the master and the slave must match; a different polarity or assert time on the two ends makes the bus direction switch incorrectly.

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

5.21.3.4 DMA_Interrupt DMA transmit with interrupt receive

Example directory: projects/n32g41x_EVAL/examples/USART/DMA_Interrupt/

Enable the DMA, GPIO, and both USART clocks and configure the USART2 interrupt→Configure the TX and RX pins of USART1 and USART2 as alternate-function push-pull→Configure the transmit DMA channels of both USARTs→Enable the transmit DMA requests and the USART2 receive interrupt and start both ports→Receive on USART1 by polling the data sent by USART2→Wait for both transmit channels to complete and compare the two buffers

Item Source analysis
Function The example shows DMA transmit with two kinds of receive on USART1 (USARTy) and USART2 (USARTz): USART1 sends TxBuffer1 over DMA while USART2 receives it into RxBuffer2 over the receive interrupt, and USART2 sends TxBuffer2 over DMA while USART1 receives it into RxBuffer1 by polling.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, DMA_Configuration, and Buffercmp;
inc/main.h: the pin and DMA channel macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTz_IRQHandler and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9, Rx PA10) and USARTz is USART2 (Tx PA2, Rx PA3);
except that PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, the other pins all use GPIO_AF4 as alternate-function push-pull with pull-up;
the baud rate is 115200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling;
USART1_TX uses DMA_CH1 and USART2_TX uses DMA_CH3, with direction DMA_DIR_PERIPH_DST;
USART2_IRQn has preemption priority NVIC_PRE_PRIORITY_0 and subpriority NVIC_SUB_PRIORITY_0. The system clock is HSI+PLL: 80 MHz on N32G412 and 96 MHz on N32G415.
Program and data path main configures the clocks, NVIC, GPIO, DMA, and both USARTs, opens the transmit DMA requests of both ports, the USART2 receive interrupt, and the two DMA channels, and starts with USART_Enable;
then inside while (indexFlag < TxBufferSize2) it polls USART_FLAG_RXDNE on USART1 to collect the data sent by USART2 into RxBuffer1, then waits for the two transmit channel transfer-complete flags, and Buffercmp stores the results in TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_EnableDMA, USART_ConfigInt, USART_Enable, USART_GetFlagStatus, USART_ReceiveData, USART_GetIntStatus, USART_SendData, DMA_StructInit, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus, NVIC_Init
Verification After reset, check the variables TransferStatus1 and TransferStatus2 in turn: both PASSED means both directions match, and either FAILED means that direction differs.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
externally wire USART1_TX(PA9) to USART2_RX(PA3) and USART2_TX(PA2) to USART1_RX(PA10).
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses DMA transmit on both USARTs and pairs them with polling on one receive and an interrupt on the other, to contrast different receive styles with the same DMA transmit.

Data path: main → USART_EnableDMA(USART_DMAREQ_TX) opens DMA transmit on both ports → USART_ConfigInt(USART_INT_RXDNE) opens the USART2 receive interrupt → DMA_Init moves TxBuffer1/TxBuffer2 to their data registers → the main loop polls USART_FLAG_RXDNE into RxBuffer1 and USARTz_IRQHandler fills RxBuffer2 → Buffercmp stores the results in the two status variables

1. Open the DMA requests and the receive interrupt and start the channels. Both ports open only the DMA transmit request; on the receive side USART1 uses polling and USART2 uses the interrupt, and the two transmit DMA channels are enabled here as well.

src/main.csrc/main.c · main
    /* Enable USARTy DMA Rx and TX request */
    USART_EnableDMA(USARTy, USART_DMAREQ_TX, ENABLE);
    /* Enable USARTz DMA Rx and TX request */
    USART_EnableDMA(USARTz, USART_DMAREQ_TX, ENABLE);

    /* Enable the USARTz Receive Interrupt */
    USART_ConfigInt(USARTz, USART_INT_RXDNE, ENABLE);

    /* Enable USARTy TX DMA Channel */
    DMA_EnableChannel(USARTy_Tx_DMA_Channel, ENABLE);
    /* Enable USARTz TX DMA Channel */
    DMA_EnableChannel(USARTz_Tx_DMA_Channel, ENABLE);

To use DMA for both directions, add the receive request and the receive channel together; opening only the transmit request leaves the receive side waiting forever.

2. Poll the USART1 receive data. The USART1 receive uses neither DMA nor the interrupt; the main loop takes the byte into RxBuffer1 once the receive flag is set, and discards data by reading the data register when the overrun flag is set.

src/main.csrc/main.c · main
    /* Receive the TxBuffer2 */
    while (indexFlag < TxBufferSize2)
    {
        while (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTy, USART_FLAG_OREF) != RESET)
            {
                USARTy->DAT;
            }
        }
        RxBuffer1[indexFlag++] = USART_ReceiveData(USARTy);
    }

Polling receive and the receive interrupt cannot be enabled at the same time; when porting to an interrupt-driven receive, remove this polling loop and let the handler fill the buffer.

3. Describe the transmit transfer with a DMA channel. The transmit channel targets the peripheral data register from TxBuffer1, with byte width, no peripheral increment, memory increment, and is bound to the USART1_TX event through the request remap.

src/main.csrc/main.c · DMA_Configuration
    /* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
    DMA_DeInit(USARTy_Tx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = USARTy_DAT_Base;
    DMA_InitStructure.MemAddr        = (uint32_t)TxBuffer1;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = TxBufferSize1;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(USARTy_Tx_DMA_REMAP, DMA, USARTy_Tx_DMA_Channel, ENABLE);

The USART2 transmit channel reuses the same structure and only changes the peripheral address, memory address, and remap name; when the source buffer or its length changes, update BufSize together.

4. Wait for the transmit to complete and compare the results. After the polling receive finishes, wait for the two transmit channel transfer-complete flags, then compare TxBuffer2/RxBuffer1 and TxBuffer1/RxBuffer2 separately.

src/main.csrc/main.c · main
    /* Wait until USARTy TX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTy_Tx_DMA_FLAG, DMA) == RESET)
    {
    }
    /* Wait until USARTz TX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTz_Tx_DMA_FLAG, DMA) == RESET)
    {
    }

    /* Check the received data with the send ones */
    TransferStatus1 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);
    /* TransferStatus1 = PASSED, if the data transmitted from USARTz and
       received by USARTy are the same */
    /* TransferStatus1 = FAILED, if the data transmitted from USARTz and
       received by USARTy are different */
    TransferStatus2 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);

The two status variables correspond to the two directions; when a transmit or receive buffer length changes, update the length arguments of Buffercmp together.

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

5.21.3.5 DMA_Polling_MaxBaudrate DMA polling at maximum baud rate

Example directory: projects/n32g41x_EVAL/examples/USART/DMA_Polling_MaxBaudrate/

Enable the DMA, GPIO, and both USART clocks→Configure the TX and RX pins of USART1 and USART4 as alternate-function push-pull→Configure the four transmit and receive DMA channels of the two USARTs→Enable the transmit and receive DMA requests and channels of both ports→Poll the four DMA channels for transfer completion→Compare the two buffers and store the results in the two status variables

Item Source analysis
Function The example transfers in both directions between USART1 (USARTy) and USART4 (USARTz) over DMA at the maximum baud rate of 10000000 bps: each side sends and receives through DMA, and the four channel transfer-complete flags are polled before the data is compared.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, and Buffercmp;
inc/main.h: the pin and DMA channel macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9, Rx PA10) and USARTz is USART4 (Tx PA1, Rx PA0);
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, and the two USART4 pins use GPIO_AF5;
the baud rate is 10000000, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 8-times oversampling (USART_8OVER);
the DMA is allocated as USART1_TX with DMA_CH1, USART1_RX with DMA_CH2, USART4_TX with DMA_CH3, and USART4_RX with DMA_CH4, with byte width, no peripheral increment, memory increment, and DMA_MODE_NORMAL. The system clock is HSI+PLL: 80 MHz on N32G412 and 96 MHz on N32G415.
Program and data path main configures the clocks, GPIO, DMA, and both USARTs, opens the transmit and receive DMA requests and the four DMA channels of both ports, and starts with USART_Enable;
four DMA_GetFlagStatus polls then wait for USART1_TX, USART1_RX, USART4_TX, and USART4_RX to transfer complete, and finally Buffercmp stores the results in TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_EnableDMA, USART_Enable, DMA_StructInit, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification After building and running, check TransferStatus1 and TransferStatus2: both PASSED means the two-way DMA transfer is correct, and either FAILED means that direction is wrong.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
wire USART1_TX(PA9) to USART4_RX(PA0) and USART4_TX(PA1) to USART1_RX(PA10).
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example pushes the baud rate to the maximum with four transmit and receive DMA channels on the two ports, and the main loop only polls the flags instead of moving single bytes.

Data path: main → USART_Init (BaudRate = 10000000, USART_8OVER) → USART_EnableDMA(USART_DMAREQ_RX | USART_DMAREQ_TX) → DMA_Init/DMA_RequestRemap sets up the four channels → poll the four completion flags → Buffercmp stores the results in the two status variables

1. Enable the transmit and receive DMA requests and the four channels. Both ports open the transmit and receive requests at the same time and the four channels are enabled in one pass, leaving all transmit and receive to the DMA.

src/main.csrc/main.c · main
    /* Enable USARTy DMA Rx and TX request */
    USART_EnableDMA(USARTy, USART_DMAREQ_RX | USART_DMAREQ_TX, ENABLE);
    /* Enable USARTz DMA Rx and TX request */
    USART_EnableDMA(USARTz, USART_DMAREQ_RX | USART_DMAREQ_TX, ENABLE);

    /* Enable USARTy TX DMA Channel */
    DMA_EnableChannel(USARTy_Tx_DMA_Channel, ENABLE);
    /* Enable USARTy RX DMA Channel */
    DMA_EnableChannel(USARTy_Rx_DMA_Channel, ENABLE);

    /* Enable USARTz TX DMA Channel */
    DMA_EnableChannel(USARTz_Tx_DMA_Channel, ENABLE);
    /* Enable USARTz RX DMA Channel */
    DMA_EnableChannel(USARTz_Rx_DMA_Channel, ENABLE);

The four channels and the four requests must be enabled in pairs; missing one direction of request makes its flag wait forever.

2. Describe the transmit transfer with a DMA channel. One channel describes a transmit: the peripheral data register as the target and TxBuffer1 as the source, with byte width, no peripheral increment, memory increment, and bound to the USART1_TX event through the request remap.

src/main.csrc/main.c · DMA_Configuration
    /* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
    DMA_DeInit(USARTy_Tx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = USARTy_DAT_Base;
    DMA_InitStructure.MemAddr        = (uint32_t)TxBuffer1;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = TxBufferSize1;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(USARTy_Tx_DMA_REMAP, DMA, USARTy_Tx_DMA_Channel, ENABLE);

The other three channels reuse the same structure and only change the direction, peripheral address, memory address, and remap name; when one peripheral is shared, check the request line each channel is bound to.

3. Poll the four channels and compare. The main loop waits for the USART1_TX, USART1_RX, USART4_TX, and USART4_RX flags in turn, and once all are set it compares the two buffers.

src/main.csrc/main.c · main
    /* Wait until USARTy TX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTy_Tx_DMA_FLAG, DMA) == RESET)
    {
    }
    /* Wait until USARTy RX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTy_Rx_DMA_FLAG, DMA) == RESET)
    {
    }

    /* Wait until USARTz TX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTz_Tx_DMA_FLAG, DMA) == RESET)
    {
    }
    /* Wait until USARTz RX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(USARTz_Rx_DMA_FLAG, DMA) == RESET)
    {
    }

At the maximum baud rate the four flags must be checked against their own channels; when the channel allocation changes, update the flag macros together, otherwise an unused channel is polled.

Corresponding source files: src/main.c

5.21.3.6 HalfDuplex Single-wire half-duplex

Example directory: projects/n32g41x_EVAL/examples/USART/HalfDuplex/

Enable the GPIO and both USART clocks→Configure the TX pins of USART1 and USART2 as open-drain alternate-function outputs→Enable half-duplex mode on both USARTs→USART1 sends byte by byte while USART2 receives by polling into a buffer→USART2 sends byte by byte while USART1 receives by polling into a buffer→Compare the two buffers and store the results in the two status variables

Item Source analysis
Function The example transfers between USART1 (USARTy) and USART2 (USARTz) in single-wire half-duplex mode: USART1 first sends TxBuffer1 to USART2, then USART2 sends TxBuffer2 to USART1, and both directions use software polling.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, and Buffercmp;
inc/main.h: the USART and pin macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (only Tx PA9) and USARTz is USART2 (only Tx PA2), and both pins are GPIO_MODE_AF_OD (open-drain) with pull-up;
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, and PA2 uses GPIO_AF4;
the baud rate is 115200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling, with transmit and receive enabled;
USART_EnableHalfDuplex is called on each port before transferring.
The system clock is HSI+PLL: 80 MHz on N32G412 and 96 MHz on N32G415.
Program and data path main configures the clocks, GPIO, and both USARTs, starts with USART_Enable, and enables half-duplex on each port;
in the first while (NbrOfDataToRead2--) loop USART1 waits for USART_FLAG_TXDE and sends TxBuffer1 byte by byte with USART_SendData while USART2 waits for USART_FLAG_RXDNE and receives into RxBuffer2;
the USART1 data register is then cleared and the second loop sends USART2’s TxBuffer2 to USART1’s RxBuffer1;
finally Buffercmp stores the results in TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_EnableHalfDuplex, USART_GetFlagStatus, USART_SendData, USART_ReceiveData
Verification After reset, check the variables TransferStatus1 and TransferStatus2 in turn: both PASSED means both directions match, and either FAILED means that direction is wrong.
Use note In half-duplex the TX and RX are connected internally, so only one wire joins the TX pins of the two boards (PA9 of USART1_Tx and PA2 of USART2_Tx);
disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK first.
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses single-wire half-duplex so that each port keeps only its TX pin connected, and the software splits the transfer into two phases in turn.

Data path: main → USART_EnableHalfDuplex enables half-duplex on both ports → the first phase moves TxBuffer1 to RxBuffer2 with USART_SendData/USART_ReceiveData → the second phase moves TxBuffer2 to RxBuffer1 → Buffercmp stores the results in the two status variables

1. Configure the TX pin as open-drain alternate function. Half-duplex uses only one wire, and the TX pin must be open-drain with pull-up so that it is not driven against the external line while idle.

src/main.csrc/main.c · GPIO_Configuration
    /* Configure USARTy Tx as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = USARTy_TxPin;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_OD;
    GPIO_InitStructure.GPIO_Alternate = USARTy_Tx_GPIO_AF;
    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);

    /* Configure USARTz Tx as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = USARTz_TxPin;
    GPIO_InitStructure.GPIO_Alternate = USARTz_Tx_GPIO_AF;
    GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);

As two open-drain lines share the bus, configuring TX as push-pull makes the two ends drive each other on the bus and can damage the pins.

2. Enable half-duplex mode. Each port is started with USART_Enable and then half-duplex is opened, so transmit and receive share one data path internally.

src/main.csrc/main.c · main
    /* Enable the USARTy and USARTz */
    USART_Enable(USARTy, ENABLE);
    USART_Enable(USARTz, ENABLE);

    /* Enable USARTy Half Duplex Mode */
    USART_EnableHalfDuplex(USARTy, ENABLE);
    /* Enable USARTz Half Duplex Mode */
    USART_EnableHalfDuplex(USARTz, ENABLE);

Half-duplex is mutually exclusive with modes such as IrDA; when porting, confirm the other mode is disabled before enabling one of them.

3. First phase: USART1 sends and USART2 receives. Each byte waits for TXDE before sending and for RXDNE before receiving, the received data is stored in RxBuffer2, and the data register is cleared once at the end.

src/main.csrc/main.c · main
    while (NbrOfDataToRead2--)
    {
        /* Wait until end of transmit */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }
        /* Write one byte in the USARTy Transmit Data Register */
        USART_SendData(USARTy, TxBuffer1[TxCounter1++]);

        /* Wait the byte is entirely received by USARTz */
        while (USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if (USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET)
            {
                USARTz->DAT;
            }
        }
        /* Store the received byte in the RxBuffer2 */
        RxBuffer2[RxCounter2++] = USART_ReceiveData(USARTz);
    }

    /* Clear the USARTy Data Register */
    USART_ReceiveData(USARTy);

The two directions advance byte by byte as a pair; when the direction order changes, adjust the counters and the data-register clearing action of both phases together.

4. Compare the two transfer results. After both phases finish, compare TxBuffer1/RxBuffer2 and TxBuffer2/RxBuffer1 separately.

src/main.csrc/main.c · main
    /* Check the received data with the send ones */
    TransferStatus1 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
    /* TransferStatus = PASSED, if the data transmitted from USARTy and
       received by USARTz are the same */
    /* TransferStatus = FAILED, if the data transmitted from USARTy and
       received by USARTz are different */
    TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);

The two status variables correspond to the two directions; when a buffer length changes, update the length arguments of Buffercmp together.

Corresponding source files: src/main.c

5.21.3.7 HardwareFlowCtrl Hardware flow control

Example directory: projects/n32g41x_EVAL/examples/USART/HardwareFlowCtrl/

Enable the GPIO and both USART clocks→Configure the TX, RX, RTS, and CTS pins of USART1 and USART2 with their alternate functions→Configure both USARTs to use CTS and RTS hardware flow control→USART1 sends byte by byte while USART2 receives by polling into a buffer→USART2 sends byte by byte while USART1 receives by polling into a buffer→Compare the two buffers and store the results in the two status variables

Item Source analysis
Function The example transfers in both directions between USART1 (USARTy) and USART2 (USARTz) under CTS/RTS hardware flow control: USART1 sends TxBuffer1 and USART2 receives it, then USART2 sends TxBuffer2 and USART1 receives it, both by polling.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, and Buffercmp;
inc/main.h: the USART, TX/RX/RTS/CTS pin, and alternate-function macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9, Rx PA10, RTS PA12, CTS PA11) and USARTz is USART2 (Tx PA2, Rx PA3, RTS PA1, CTS PA0);
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, and the other TX/RX/RTS/CTS pins all use GPIO_AF4;
RTS is configured as alternate-function push-pull, and RX and CTS are configured as GPIO_MODE_INPUT with pull-up;
the baud rate is 115200, 8 data bits, 1 stop bit, no parity, and 16-times oversampling, with USART_InitStructure.HardwareFlowControl = USART_HFCTRL_CTS \| USART_HFCTRL_RTS, and transmit and receive enabled.
The system clock is HSI+PLL: 80 MHz on N32G412 and 96 MHz on N32G415.
Program and data path main configures the clocks, GPIO, and both USARTs (with both CTS and RTS in hardware flow control) and starts with USART_Enable;
in the first while (NbrOfDataToRead2--) loop USART1 sends TxBuffer1 byte by byte with USART_SendData while USART2 polls USART_FLAG_RXDNE and receives into RxBuffer2;
the second loop sends TxBuffer2 the other way into RxBuffer1;
finally Buffercmp stores the results in TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_GetFlagStatus, USART_SendData, USART_ReceiveData
Verification After building and running, check TransferStatus1 and TransferStatus2: both PASSED means the hardware flow control transfer succeeded, and either FAILED means that direction is wrong.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
besides TX/RX, also wire USARTy’s RTS(PA12) to USARTz’s CTS(PA0) and USARTz’s RTS(PA1) to USARTy’s CTS(PA11).
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example adds CTS and RTS to both ports on top of ordinary asynchronous transfer, so the transfer pace is coordinated by the hardware flow control signals.

Data path: main → USART_Init (HardwareFlowControl = USART_HFCTRL_CTS \| USART_HFCTRL_RTS) → USART_Enable → the first phase moves TxBuffer1 to RxBuffer2 with USART_SendData/USART_ReceiveData → the second phase moves TxBuffer2 to RxBuffer1 → Buffercmp stores the results in the two status variables

1. Configure RTS as an output and RX and CTS as inputs. RTS is an output using alternate-function push-pull, and RX and CTS are inputs with pull-up, so the four pins are configured by direction.

src/main.csrc/main.c · GPIO_Configuration
    /* Configure USARTz RTS as alternate function push-pull */
    GPIO_InitStructure.Pin            = USARTz_RTSPin;
    GPIO_InitStructure.GPIO_Alternate = USARTz_RTS_GPIO_AF;
    GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);

    /* Configure USARTy Rx as alternate function input and pull-up */
    GPIO_InitStructure.Pin            = USARTy_RxPin;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitStructure.GPIO_Alternate = USARTy_Rx_GPIO_AF;
    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);

    /* Configure USARTz Rx as alternate function input and pull-up */
    GPIO_InitStructure.Pin            = USARTz_RxPin;
    GPIO_InitStructure.GPIO_Alternate = USARTz_Rx_GPIO_AF;
    GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);

    /* Configure USARTy CTS as alternate function input and pull-up */
    GPIO_InitStructure.Pin            = USARTy_CTSPin;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_INPUT;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitStructure.GPIO_Alternate = USARTy_CTS_GPIO_AF;
    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);

RTS and CTS are not interchangeable; as an input, CTS must be able to read the level of the peer’s RTS.

2. First phase: send and receive by polling. USART1 sends a byte and waits for TXDE, USART2 waits for RXDNE into RxBuffer2, and the flow control signals coordinate the pace of both ends automatically.

src/main.csrc/main.c · main
    while (NbrOfDataToRead2--)
    {
        /* Send one byte from USARTy to USARTz */
        USART_SendData(USARTy, TxBuffer1[TxCounter1++]);
        /* Loop until USARTy DAT register is empty */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }

        /* Loop until the USARTz Receive Data Register is not empty */
        while (USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET)
            {
                USARTz->DAT;
            }
        }
        /* Store the received byte in RxBuffer */
        RxBuffer2[RxCounter2++] = USART_ReceiveData(USARTz);
    }

The polling transfer keeps the overrun branch; when the buffer or its length changes, adjust the transmit and receive count limits together.

3. Compare the two transfer results. After both phases finish, compare the two directions and store the results in TransferStatus1 and TransferStatus2.

src/main.csrc/main.c · main
    /* Check the received data with the send ones */
    TransferStatus1 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
    /* TransferStatus1 = PASSED, if the data transmitted from USARTy and
       received by USARTz are the same */
    /* TransferStatus1 = FAILED, if the data transmitted from USARTy and
       received by USARTz are different */
    TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);
    /* TransferStatus2 = PASSED, if the data transmitted from USARTz and
       received by USARTy are the same */
    /* TransferStatus2 = FAILED, if the data transmitted from USARTz and
       received by USARTy are different */

The two status variables correspond to the two directions; even under hardware flow control, check the buffers direction by direction.

Corresponding source files: src/main.c

5.21.3.8 Interrupt Interrupt transfer

Example directory: projects/n32g41x_EVAL/examples/USART/Interrupt/

Enable the GPIO and both USART clocks and configure both NVIC interrupts→Configure the TX and RX pins of USART1 and USART2 as alternate-function push-pull→Configure both USARTs and open the transmit and receive interrupts together→Both ports move data into their own buffers inside the interrupts→The main loop waits for both directions to complete→Compare the two buffers and store the results in the two status variables

Item Source analysis
Function The example transfers in both directions between USART1 (USARTy) and USART2 (USARTz) through interrupts: both ports send and receive at the same time, USART1 sends TxBuffer1 into USART2’s RxBuffer2, and USART2 sends TxBuffer2 into USART1’s RxBuffer1.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
inc/main.h: the USART, pin, and IRQn macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTy_IRQHandler, USARTz_IRQHandler, and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9, Rx PA10) and USARTz is USART2 (Tx PA2, Rx PA3);
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415, and the other pins use GPIO_AF4 as alternate-function push-pull with pull-up;
the baud rate is 115200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling, with transmit and receive enabled;
both ports open USART_INT_RXDNE and USART_INT_TXDE;
in the NVIC with group 2, USART1_IRQn and USART2_IRQn have preemption priority NVIC_PRE_PRIORITY_0 and subpriorities NVIC_SUB_PRIORITY_0 and NVIC_SUB_PRIORITY_1 respectively.
System clock HSI+PLL: 80 MHz (N32G412), 96 MHz (N32G415).
Program and data path main configures the clocks, NVIC, GPIO, and both USARTs, opens both ports’ transmit and receive interrupts and USART_Enable;
in the interrupts USARTy_IRQHandler moves data between RxBuffer1 and TxBuffer1 with USART_ReceiveData/USART_SendData and USARTz_IRQHandler does the same for RxBuffer2 and TxBuffer2, disabling the matching interrupt once the count is reached;
the main loop waits with two while loops, and Buffercmp stores the results in TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_ConfigInt, USART_Enable, USART_GetFlagStatus, USART_GetIntStatus, USART_ReceiveData, USART_SendData
Verification After reset, check the variables TransferStatus1 and TransferStatus2 in turn: both PASSED means both directions are correct, and either FAILED means that direction is wrong.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
externally wire USART1_TX(PA9) to USART2_RX(PA3) and USART1_RX(PA10) to USART2_TX(PA2).
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example hands all transmit and receive of the two ports to the interrupts, and the main loop only waits for both receive counts to reach their limits.

Data path: main → USART_ConfigInt opens the transmit and receive interrupts of both ports → USARTy_IRQHandler/USARTz_IRQHandler move data between the TxBuffer and RxBuffer with USART_ReceiveData/USART_SendData → the main loop waits for both receive counts → Buffercmp stores the results in the two status variables

1. Open the transmit and receive interrupts together and start. Each port opens its receive and transmit interrupts, so that both directions are handled by the interrupt handlers.

src/main.csrc/main.c · main
    /* Enable USARTy and USARTz Receive and Transmit interrupts */
    USART_ConfigInt(USARTy, USART_INT_RXDNE, ENABLE);
    USART_ConfigInt(USARTy, USART_INT_TXDE, ENABLE);
    USART_ConfigInt(USARTz, USART_INT_RXDNE, ENABLE);
    USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE);

    /* Enable the USARTy and USARTz */
    USART_Enable(USARTy, ENABLE);
    USART_Enable(USARTz, ENABLE);

A change of the count limits does not affect the function, but the receive-interrupt disable condition depends on the count limit, so check it together.

2. The USART1 interrupt moves the data. USARTy_IRQHandler stores received bytes in RxBuffer1 and disables the receive interrupt once enough bytes arrive; when the transmit interrupt fires it takes a byte from TxBuffer1 and disables the transmit interrupt after the last byte.

src/n32g41x_it.csrc/n32g41x_it.c · USARTy_IRQHandler
void USARTy_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTy, USART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer1[RxCounter1++] = USART_ReceiveData(USARTy);

        if (RxCounter1 == NbrOfDataToRead1)
        {
            /* Disable the USARTy Receive interrupt */
            USART_ConfigInt(USARTy, USART_INT_RXDNE, DISABLE);
        }
    }

    if ((USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) != RESET) &&
        (USART_GetIntStatus(USARTy, USART_INT_TXDE) != RESET))
    {
        /* Write one byte to the transmit data register */
        USART_SendData(USARTy, TxBuffer1[TxCounter1++]);

        if (TxCounter1 == NbrOfDataToTransfer1)
        {
            /* Disable the USARTy Transmit interrupt */
            USART_ConfigInt(USARTy, USART_INT_TXDE, DISABLE);
        }
    }

The receive and transmit limits come from NbrOfDataToRead1 and NbrOfDataToTransfer1; when the buffer length changes, update both limits.

3. The USART2 interrupt moves the data. USARTz_IRQHandler performs the same send and receive handling for RxBuffer2 and TxBuffer2, and the two directions mirror each other.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
void USARTz_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer2[RxCounter2++] = USART_ReceiveData(USARTz);

        if (RxCounter2 == NbrOfDataToRead2)
        {
            /* Disable the USARTz Receive interrupt */
            USART_ConfigInt(USARTz, USART_INT_RXDNE, DISABLE);
        }
    }

    if ((USART_GetFlagStatus(USARTz, USART_FLAG_TXDE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_TXDE) != RESET))
    {
        /* Write one byte to the transmit data register */
        USART_SendData(USARTz, TxBuffer2[TxCounter2++]);

        if (TxCounter2 == NbrOfDataToTransfer2)
        {
            /* Disable the USARTz Transmit interrupt */
            USART_ConfigInt(USARTz, USART_INT_TXDE, DISABLE);
        }
    }

The limits of the two handlers correspond to their own buffers and counts; keep one side in step when the other changes.

4. Compare after the main loop completes. The main loop waits for the receive counts of both directions to reach the buffer length, and then compares the two transfer results.

src/main.csrc/main.c · main
    /* Wait until all data are received */
    while (RxCounter2 < RxBufferSize2)
    {
    }
    while (RxCounter1 < RxBufferSize1)
    {
    }

    /* Check the received data with the send ones */
    TransferStatus1 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
    /* TransferStatus1 = PASSED, if the data transmitted from UARTy and
       received by UARTz are the same */
    /* TransferStatus1 = FAILED, if the data transmitted from UARTy and
       received by UARTz are different */
    TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);

The wait conditions are the receive buffer lengths; when the content length of the transmit buffer changes, update these lengths or the main loop waits forever.

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

5.21.3.9 LP_Transmit_Receive IrDA low-power mode transfer

Example directory: projects/n32g41x_EVAL/examples/USART/IrDA_Tx&Rx/LP_Transmit_Receive/

Enable the GPIO and both USART clocks and configure the USART2 interrupt→Configure the TX pin of USART1 and the RX pin of USART2 as alternate-function push-pull→Generate a 38 kHz carrier on PB4 with GTIM3→Configure both USARTs for IrDA low-power mode and set the prescaler→USART1 sends byte by byte while USART2 receives over the interrupt into a buffer→Compare the buffers after all bytes arrive and store the result in the status variable

Item Source analysis
Function The example transfers between USART1 (USARTy) and USART2 (USARTz) in serial IrDA low-power mode: USART1 sends TxBuffer1 byte by byte and USART2 receives it into RxBuffer1 over the interrupt, while GTIM3 generates a 38 kHz carrier on PB4.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
src/bsp_Tim_38K.c and inc/bsp_Tim_38K.h: the GTIM3 and PB4 38 kHz carrier initialization;
inc/main.h: the USART, pin, and IRQn macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTz_IRQHandler and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9) and USARTz is USART2 (Rx PA3);
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415;
the baud rate is 600, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling;
USARTy is USART_MODE_TX and USARTz is USART_MODE_RX;
USART_SetPrescaler(USARTy, 250) and USART_SetPrescaler(USARTz, 125) set the prescalers, USART_ConfigIrDAMode selects USART_IRDAMODE_LOWPPWER, and USART_EnableIrDA opens IrDA;
RCC_ConfigPclk1 and RCC_ConfigPclk2 both divide by 8, and the system clock is HSI 16 MHz;
the carrier is output by GTIM3 on PB4.
Program and data path main configures the clocks, NVIC and GPIO, calls ADVANCE_TIM_Init to start the 38 kHz carrier, uses USART_Init to set one port as transmit and the other as receive, opens the USART2 receive interrupt and starts with USART_Enable;
the main loop sends byte by byte with USART_SendData and waits for USART_FLAG_TXDE and for USART2 to finish;
USARTz_IRQHandler stores the bytes in RxBuffer1 with USART_ReceiveData, and Buffercmp stores the result in TransferStatus.
Main APIs USART_StructInit, USART_Init, USART_ConfigInt, USART_SetPrescaler, USART_ConfigIrDAMode, USART_EnableIrDA, USART_Enable, USART_GetFlagStatus, USART_SendData, USART_ReceiveData, NVIC_Init
Verification After reset, check the variable TransferStatus: PASSED means the infrared received data matches what was sent, and FAILED means they differ.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
connect USART1’s Tx(PA9) to the infrared emitter and USART2’s Rx(PA3) to the infrared receiver, with PB4 outputting the 38 kHz carrier;
in low-power mode the prescaled clock must fall within the range specified by the manual.
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example sends serial data from USART1 to USART2 over infrared using IrDA low-power mode and one 38 kHz carrier.

Data path: main → ADVANCE_TIM_Init outputs the 38 kHz carrier on PB4 → USART_ConfigIrDAMode(USART_IRDAMODE_LOWPPWER) and USART_SetPrescaler configure IrDA low-power mode → USART_SendData sends TxBuffer1 → USARTz_IRQHandler receives into RxBuffer1 → Buffercmp stores the result in TransferStatus

1. Set the prescaler and select low-power mode. The transmit and receive sides set different prescaler values, then each selects the low-power IrDA mode and opens IrDA before the USARTs are started.

src/main.csrc/main.c · main
    /* Set the USARTy and USARTz prescaler */
    USART_SetPrescaler(USARTy, 250);
    USART_SetPrescaler(USARTz, 125);
    /* Configure the USARTy IrDA mode */
    USART_ConfigIrDAMode(USARTy, USART_IRDAMODE_LOWPPWER);
    USART_ConfigIrDAMode(USARTz, USART_IRDAMODE_LOWPPWER);

    /* Enable the USARTy and USARTz IrDA mode */
    USART_EnableIrDA(USARTy, ENABLE);
    USART_EnableIrDA(USARTz, ENABLE);

In low-power mode the pulse width is determined by the prescaler value; the two ends must be set as a pair within the clock range specified by the manual.

2. Configure the 38 kHz carrier timer. GTIM3 derives the update rate from the counter period and prescaler; after the time base is initialized the flag is cleared, the update interrupt is opened, and the timer is enabled.

src/bsp_Tim_38K.csrc/bsp_Tim_38K.c · ADVANCE_TIM_Mode_Config
    TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.Period      = ADVANCE_TIM_Period;
    TIM_TimeBaseStructure.Prescaler   = ADVANCE_TIM_Prescaler;
    TIM_TimeBaseStructure.ClkDiv      = TIM_CLK_DIV1;
    TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_UP;
    TIM_InitTimeBase(ADVANCE_TIM, &TIM_TimeBaseStructure);

The period and prescaler values are defined in the BSP header; when the carrier frequency changes, update both macros and keep the division relationship consistent.

3. Toggle the carrier pin in the timer interrupt. The update interrupt clears the flag and toggles PB4, producing the carrier square wave in software.

src/bsp_Tim_38K.csrc/bsp_Tim_38K.c · ADVANCE_TIM_IRQHandler
void ADVANCE_TIM_IRQHandler(void)
{
    if (TIM_GetIntStatus(ADVANCE_TIM, TIM_INT_UPDATE) != RESET)
    {
        TIM_ClearFlag(ADVANCE_TIM, TIM_FLAG_UPDATE);
        digitalToggle(PORTx, PORTx_PIN);
    }
}

The carrier pin and the toggle macro are defined in the BSP header; when porting to another board, update the port and pin macros together.

4. The receive interrupt stores the bytes in the buffer. USARTz_IRQHandler stores received bytes in RxBuffer1 and disables the receive interrupt once enough bytes arrive, and after checking the error flags it reads the status register and the data register to clear the errors.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
void USARTz_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_RXDNE) != RESET))
    {
        RxBuffer1[RxCounter++] = USART_ReceiveData(USARTz);

        {
            if (RxCounter == NbrOfDataToRead1)
            {
                /* Disable the USARTz Receive interrupt */
                USART_ConfigInt(USARTz, USART_INT_RXDNE, DISABLE);
            }
        }
    }

    /* Determine if an error flag still exists and clear the error flag */
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET) ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_NEF) != RESET)  ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_PEF) != RESET)  ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_FEF) != RESET))
    {
        (void)USARTz->STS;
        (void)USARTz->DAT;
    }
}

The receive count limit uses NbrOfDataToRead1, which equals the buffer length; when the content length changes, update this length together.

5. Send and compare after all bytes arrive. The main loop sends TxBuffer1 byte by byte according to the transmit-empty flag, waits for the receive count to complete after transmission, and then compares the buffers.

src/main.csrc/main.c · main
    while (TxCounter < TxBufferSize1)
    {
        /* Send one byte from Board1_USARTy to Board2_USARTz */
        USART_SendData(USARTy, TxBuffer1[TxCounter++]);
        /* Loop until Board1_USARTy DAT register is empty */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }
    }
    while (RxCounter < TxBufferSize1)
    {
    }
    /* Check the received data with the send ones */
    TransferStatus = Buffercmp(TxBuffer1, RxBuffer1, TxBufferSize1);

This example defines its length macro as countof without subtracting 1, so the byte count includes the trailing null; when porting, keep the buffer definition consistent.

Corresponding source files: src/main.c, src/bsp_Tim_38K.c, src/n32g41x_it.c

5.21.3.10 Transmit_Receive IrDA normal mode transfer

Example directory: projects/n32g41x_EVAL/examples/USART/IrDA_Tx&Rx/Transmit_Receive/

Enable the GPIO and both USART clocks and configure the USART2 interrupt→Configure the TX pin of USART1 and the RX pin of USART2 as alternate-function push-pull→Generate a 38 kHz carrier on PB4 with GTIM3→Configure both USARTs for IrDA normal mode with prescaler 1→USART1 sends byte by byte while USART2 receives over the interrupt into a buffer→Compare the buffers after all bytes arrive and store the result in the status variable

Item Source analysis
Function The example transfers between USART1 (USARTy) and USART2 (USARTz) in serial IrDA normal mode: USART1 sends TxBuffer1 byte by byte and USART2 receives it into RxBuffer1 over the interrupt, while GTIM3 generates a 38 kHz carrier on PB4.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
src/bsp_Tim_38K.c and inc/bsp_Tim_38K.h: the GTIM3 and PB4 38 kHz carrier initialization;
inc/main.h: the USART, pin, and IRQn macros of USARTy/USARTz;
src/n32g41x_it.c and inc/n32g41x_it.h: USARTz_IRQHandler and other interrupt handlers;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1 (Tx PA9) and USARTz is USART2 (Rx PA3);
PA9 uses GPIO_AF4 on N32G412 and GPIO_AF0 on N32G415;
the baud rate is 1200, 8 data bits, 1 stop bit, no parity, no hardware flow control, and 16-times oversampling;
USARTy is USART_MODE_TX and USARTz is USART_MODE_RX;
USART_SetPrescaler sets both to 0x01, USART_ConfigIrDAMode selects USART_IRDAMODE_NORMAL, and USART_EnableIrDA opens IrDA;
RCC_ConfigPclk1 and RCC_ConfigPclk2 both divide by 4, and the system clock is HSI+PLL, 80 MHz on N32G412 and 96 MHz on N32G415;
the carrier is output by GTIM3 on PB4.
Program and data path main configures the clocks, NVIC and GPIO, calls ADVANCE_TIM_Init to start the 38 kHz carrier, uses USART_Init to set one port as transmit and the other as receive, opens the USART2 receive interrupt and starts with USART_Enable;
the main loop sends byte by byte with USART_SendData and waits for USART_FLAG_TXDE and for USART2 to finish;
USARTz_IRQHandler stores the bytes in RxBuffer1 with USART_ReceiveData, and Buffercmp stores the result in TransferStatus.
Main APIs USART_StructInit, USART_Init, USART_ConfigInt, USART_SetPrescaler, USART_ConfigIrDAMode, USART_EnableIrDA, USART_Enable, USART_GetFlagStatus, USART_SendData, USART_ReceiveData, NVIC_Init
Verification After reset, check the variable TransferStatus: PASSED means the infrared received data matches what was sent, and FAILED means they differ.
Use note Disconnect the MCU_TX and MCU_RX jumpers of the NS-LINK on the board first;
connect USART1’s Tx(PA9) to the infrared emitter and USART2’s Rx(PA3) to the infrared receiver, with PB4 outputting the 38 kHz carrier;
in normal mode the prescaler value is 1.
The toolchains are KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example sends serial data from USART1 to USART2 over infrared using IrDA normal mode and one 38 kHz carrier.

Data path: main → ADVANCE_TIM_Init outputs the 38 kHz carrier on PB4 → USART_ConfigIrDAMode(USART_IRDAMODE_NORMAL) and USART_SetPrescaler(USARTy, 0x01) and USART_SetPrescaler(USARTz, 0x01) configure IrDA normal mode → USART_SendData sends TxBuffer1 → USARTz_IRQHandler receives into RxBuffer1 → Buffercmp stores the result in TransferStatus

1. Set the prescaler and select normal mode. Both prescalers are 1, then normal IrDA mode is selected and IrDA is opened; in normal mode the pulse width is fixed and the prescaler only acts as a division factor.

src/main.csrc/main.c · main
    /* Set the USARTy and USARTz prescaler */
    USART_SetPrescaler(USARTy, 0x01);
    USART_SetPrescaler(USARTz, 0x01);
    /* Configure the USARTy IrDA mode */
    USART_ConfigIrDAMode(USARTy, USART_IRDAMODE_NORMAL);
    USART_ConfigIrDAMode(USARTz, USART_IRDAMODE_NORMAL);

    /* Enable the USARTy and USARTz IrDA mode */
    USART_EnableIrDA(USARTy, ENABLE);
    USART_EnableIrDA(USARTz, ENABLE);

In normal mode the prescaler value is fixed at 1; when changing it, confirm that it still meets the division requirement of this mode.

2. Configure the transmit and receive pins. The transmit pin uses alternate-function push-pull with higher drive strength and the receive pin uses alternate-function push-pull with pull-up, so the infrared emitter is driven with enough current.

src/main.csrc/main.c · GPIO_Configuration
    /* Configure USARTy Tx as alternate function push-pull */
    GPIO_InitStructure.Pin            = USARTy_TxPin;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = USARTy_Tx_GPIO_AF;
    GPIO_InitStructure.GPIO_Current   = GPIO_DS_HIGH;
    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);

    /* Configure USARTz Rx as alternate function push-pull */
    GPIO_InitStructure.Pin            = USARTz_RxPin;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitStructure.GPIO_Alternate = USARTz_Rx_GPIO_AF;
    GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);

Insufficient drive strength on the transmit pin shortens the infrared range; when porting, check the drive setting against the actual emitter.

3. Configure the 38 kHz carrier timer. GTIM3 enables its clock and then initializes the time base; the period and prescaler determine the update rate, after which the flag is cleared, the update interrupt is opened, and the timer is enabled.

src/bsp_Tim_38K.csrc/bsp_Tim_38K.c · ADVANCE_TIM_Mode_Config
    ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK, ENABLE);
    TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.Period      = ADVANCE_TIM_Period;
    TIM_TimeBaseStructure.Prescaler   = ADVANCE_TIM_Prescaler;
    TIM_TimeBaseStructure.ClkDiv      = TIM_CLK_DIV1;
    TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_UP;
    TIM_InitTimeBase(ADVANCE_TIM, &TIM_TimeBaseStructure);

The period and prescaler values are defined in the BSP header; when the carrier frequency changes, update both macros and keep the division relationship consistent.

4. The receive interrupt stores the bytes in the buffer. USARTz_IRQHandler stores received bytes in RxBuffer1 and disables the receive interrupt once enough bytes arrive, and after checking the error flags it reads the status register and the data register to clear the errors.

src/n32g41x_it.csrc/n32g41x_it.c · USARTz_IRQHandler
void USARTz_IRQHandler(void)
{
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET) &&
        (USART_GetIntStatus(USARTz, USART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer1[RxCounter++] = USART_ReceiveData(USARTz);

        if (RxCounter == NbrOfDataToRead1)
        {
            /* Disable the USARTz Receive interrupt */
            USART_ConfigInt(USARTz, USART_INT_RXDNE, DISABLE);
        }
    }

    /* Determine if an error flag still exists and clear the error flag */
    if ((USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET) ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_NEF) != RESET)  ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_PEF) != RESET)  ||
        (USART_GetFlagStatus(USARTz, USART_FLAG_FEF) != RESET))
    {
        (void)USARTz->STS;
        (void)USARTz->DAT;
    }
}

The receive count limit uses NbrOfDataToRead1, which equals the buffer length; when the content length changes, update this length together.

5. Send and compare after all bytes arrive. The main loop sends TxBuffer1 byte by byte according to the transmit-empty flag, waits for the receive count to complete after transmission, and then compares the buffers.

src/main.csrc/main.c · main
    while (TxCounter < TxBufferSize1)
    {
        /* Send one byte from Board1_USARTy to Board2_USARTz */
        USART_SendData(USARTy, TxBuffer1[TxCounter++]);
        /* Loop until Board1_USARTy DAT register is empty */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }
    }
    while (RxCounter < TxBufferSize1)
    {
    }
    /* Check the received data with the send ones */
    TransferStatus = Buffercmp(TxBuffer1, RxBuffer1, TxBufferSize1);

This example defines its length macro as countof without subtracting 1, so the byte count includes the trailing null; when porting, keep the buffer definition consistent.

Corresponding source files: src/main.c, src/bsp_Tim_38K.c, src/n32g41x_it.c

5.21.3.11 LIN_Master LIN master

Example directory: projects/n32g41x_EVAL/examples/USART/LIN/LIN_Master/

Initialize the serial log and clocks→configure USART2 as a LIN master→send the master request frame and the slave reply frame request→receive the slave reply frame and check its checksum→print the 8 received bytes

Item Source analysis
Function Shows USART LIN mode working as the master node: it first sends an 8-byte 0x0F master request frame to the slave, then sends a slave reply frame request, and finally receives and checks the 8 bytes the slave returns.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, Memset, Buffercopy;
src/lin_master.c: frame handling and transfer flow (SetFramePID, SetFrameChecksum, WaitFrameRes, TestLinMaster, etc.);
inc/main.h, inc/lin_master.h: USART2 and pin macros, the M_LIN_EX_MSG message structure;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTx is USART2, TX is PA2, RX is PA3, both with alternate function GPIO_AF4 and clock RCC_APB1_PERIPH_USART2;
baud rate 9600, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, flow control USART_HFCTRL_NONE, oversampling USART_16OVER, receiver and transmitter enabled;
USART_ConfigLINBreakDetectLength is set to USART_LINBDL_10B. The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main first calls log_init and prints test LIN master mode, then runs RCC_Configuration, NVIC_Configuration and GPIO_Configuration, configures USART2 with USART_Init, enables LIN mode with USART_EnableLIN, and finally calls USART_Enable;
in while(1) it calls TestLinMaster every 500 ms: it sends the break character and the master request frame, then after 20 ms sends the reply frame request up to 4 times, and WaitFrameRes receives the slave’s 8 bytes, compares the checksum and prints each byte.
Main APIs USART_StructInit, USART_Init, USART_ConfigLINBreakDetectLength, USART_EnableLIN, USART_Enable, USART_SendBreak, USART_SendData, USART_GetFlagStatus, USART_ReceiveData
Verification Download LIN_Master and LIN_Slave to two evaluation boards and reset the slave first, then the master;
the master prints test LIN master mode, then SetFrameMsg ID:0x3c and the PID parity information, and after the slave reply passes the checksum it prints the 8 bytes as recv_data[0] to recv_data[7];
if the slave does not respond it prints slave no response!!.
Use note Disconnect the MCU_TX and MCU_RX jumper caps of the NS-LINK on the evaluation board first;
USART2 TX (PA2) and RX (PA3) are cross-connected to the slave;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example splits one LIN transfer into sync, PID, data and checksum segments built by a group of frame helpers, and starts the transfer through the USART break-character mechanism.

Data path: main -> USART_Init and USART_EnableLIN configure USART2 -> TestLinMaster -> TestMasterReqFrame/TestSlaveResFrame fill M_LIN_EX_MSG -> FrameHandle calls SetFramePID and SetFrameChecksum -> MasterSendFrame/MasterSendBytes send the break character and then the bytes -> WaitFrameRes receives and compares the checksum

1. Select USART2 and two pins with macros. inc/main.h groups the port, clock, TX/RX pins and alternate function numbers into one set of macros that the transfer code references by name.

inc/main.hinc/main.h
#define USARTx              USART2
#define USARTx_GPIO         GPIOA
#define USARTx_CLK          RCC_APB1_PERIPH_USART2
#define USARTx_RxPin        GPIO_PIN_3
#define USARTx_TxPin        GPIO_PIN_2
#define USARTx_Rx_GPIO_AF   GPIO_AF4
#define USARTx_Tx_GPIO_AF   GPIO_AF4
#define USARTx_APBxClkCmd   RCC_EnableAPB1PeriphClk
#define USARTx_IRQn         USART2_IRQn
#define USARTx_IRQHandler   USART2_IRQHandler

When moving to another port or pin, only this group of macros changes; this pin has the same alternate function number on the N32G412 and the N32G415, so no conditional compilation is needed.

2. Configure USART2 and enable LIN mode. main sets the baud rate, word length, stop bits, parity, flow control, oversampling and mode field by field, and after USART_Init selects the break detection length and enables LIN mode.

src/main.csrc/main.c · main
    /* USARTx configuration */
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 9600;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.OverSampling        = USART_16OVER;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;
    /* Configure USARTx */
    USART_Init(USARTx, &USART_InitStructure);
    /* Enable the USARTx LIN mode */
    USART_ConfigLINBreakDetectLength(USARTx, USART_LINBDL_10B);
    USART_EnableLIN(USARTx, ENABLE);
    /* Enable the USARTx */
    USART_Enable(USARTx, ENABLE);

When the baud rate, word length or pins change, the macros in main.h and the matching settings on the slave side must be updated together; if the two ends differ the slave does not respond.

3. Compute the checksum and send the break character. MasterGetCheckSum accumulates each byte with the LIN classic checksum algorithm, and MasterSendBytes sends a break character before the data and synchronizes each byte on the transmission-complete flag.

src/lin_master.csrc/lin_master.c · MasterGetCheckSum
uint8_t MasterGetCheckSum(uint8_t *pData, uint8_t len)
{
    uint16_t check_sum_temp = 0;
    uint8_t i;
    for(i = 0; i < len; i++)
    {
        check_sum_temp += pData[i];
        if(check_sum_temp > 0xFF)
        {
            check_sum_temp -= 0xFF;
        }
    }
    return (~check_sum_temp) & 0xFF;
}
src/lin_master.csrc/lin_master.c · MasterSendBytes
void MasterSendBytes(uint8_t *pBuf, uint8_t Len)
{
    USART_SendBreak(USARTx);
    while(Len--)
    {
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET);
        USART_SendData(USARTx, *pBuf++);
    }
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET);
}

The break detection length (10 bits) and the checksum type must match the slave; each call to MasterSendBytes sends one break character first, whose length depends on the data segment passed in.

4. Build the request and reply frames and wait for the reply. TestMasterReqFrame fills 8 bytes of 0x0F and PID 0x3C, and FrameHandle uses the raw PID to classify the frame, then converts the PID, computes the checksum and transmits.

src/lin_master.csrc/lin_master.c · TestMasterReqFrame
void TestMasterReqFrame(void)
{
    int i = 0;
    M_LIN_EX_MSG CurLINTxMsg;
    CurLINTxMsg.Check = CLASSIC;
    CurLINTxMsg.DataLen = 8;
    CurLINTxMsg.Sync = 0x55;
    CurLINTxMsg.PID = 0x3C;
    for(i = 0; i < CurLINTxMsg.DataLen; i++)
    {
        CurLINTxMsg.Data[i] = 0x0F;
    }
    SetFrameMsg(&M_TxMsg, CurLINTxMsg);
    FrameHandle();
}
src/lin_master.csrc/lin_master.c · FrameHandle
void FrameHandle(void)
{
    uint8_t tmp_PID  = M_TxMsg.PID;
    SetFramePID(&M_TxMsg);
    switch (tmp_PID)
    {
    case 0x3C://Master request frame
        SetFrameChecksum(&M_TxMsg);
        break;
    case 0x3D://Slave reply frame
        M_TxMsg.DataLen = 0;
        break;
    default:
        break;
    }
    MasterSendFrame(M_TxMsg);
}

FrameHandle uses the raw PID (0x3C/0x3D) before conversion to select the frame type; these two constants must match MSG_RECEIVE_ID and MSG_SEND_ID in the slave IDType table, so changing the ID means changing both places.

Corresponding source files: src/main.c, src/lin_master.c, inc/main.h, inc/lin_master.h

5.21.3.12 LIN_Slave LIN slave

Example directory: projects/n32g41x_EVAL/examples/USART/LIN/LIN_Slave/

Initialize the serial log and clocks→configure USART2 as a LIN slave and enable the receive and break-detect interrupts→step the state machine through the sync byte, the PID and the data→set the flag once a full frame is received→run the command in the main loop and send back an 8-byte reply

Item Source analysis
Function Shows USART LIN mode working as the slave node: it receives the master request frame (0x3C) and the slave reply frame request (0x3D), changes the data to 8 bytes of 0x01 and sends it back on request, printing the process.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, LIN_RespData;
src/lin_slave.c: LIN_IRQHandler, the LIN_EX_RxAsync state machine and transfer flow;
inc/main.h, inc/lin_slave.h: USART2 and pin macros, the LIN_EX_MSG structure, the GET_PID and ID type macros;
src/n32g41x_it.c, inc/n32g41x_it.h: USARTx_IRQHandler calls LIN_IRQHandler;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTx is USART2, TX is PA2, RX is PA3, both with alternate function GPIO_AF4;
baud rate 9600, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, flow control USART_HFCTRL_NONE, oversampling USART_16OVER, receiver and transmitter enabled;
USART_ConfigLINBreakDetectLength is set to USART_LINBDL_10B and the USART_INT_RXDNE and USART_INT_LINBD interrupts are enabled;
IDType[0x3C] is set to ID_TYPE_SR and IDType[0x3D] to ID_TYPE_SW. The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main first calls log_init and prints test LIN slave mode, runs RCC_Configuration, NVIC_Configuration and GPIO_Configuration, configures and enables USART2 LIN mode with the receive and break-detect interrupts, sets the IDType table and enters while(1): when GotMsgFlag is set it calls BOOT_ExecutiveCommand(pLINMsg->Data, LIN_RespData), which changes every received byte to 0x01 and passes it to LIN_SetResp to compute the checksum and LIN_SendBytes to send it back, then clears GotMsgFlag.
Main APIs USART_StructInit, USART_Init, USART_ConfigLINBreakDetectLength, USART_EnableLIN, USART_ConfigInt, USART_Enable, USART_GetFlagStatus, USART_ClrFlag, USART_ReceiveData, USART_SendData
Verification Used with LIN_Master, reset the slave first and then the master;
the slave prints test LIN slave mode, and after receiving data it prints BOOT_ExecutiveCommand and the hex values of pData[0] to pData[7], then replies with 8 bytes of 0x01 for the master to check.
Use note Disconnect the MCU_TX and MCU_RX jumper caps of the NS-LINK on the evaluation board first;
USART2 TX (PA2) and RX (PA3) are cross-connected to the master;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The slave turns reception into a four-state machine that the break-detect interrupt resets and the receive interrupt advances byte by byte, handing a complete frame to the main loop for the reply.

Data path: the master break character -> USART2 break-detect interrupt -> LIN_IRQHandler sets LinRxState=SYNCH -> LIN_EX_RxAsync receives 0x55, the PID, the 8 data bytes and the checksum -> GotMsgFlag is set and pLINMsg recorded -> main calls BOOT_ExecutiveCommand to change the data to 0x01 -> LIN_RespData/LIN_SetResp compute the checksum -> LIN_SendBytes sends the reply

1. Configure the slave and enable the receive and break-detect interrupts. After enabling LIN mode, main makes two settings: it enables the USART_INT_RXDNE and USART_INT_LINBD interrupts, and registers IDs 0x3C and 0x3D as “receive data” and “send data” respectively.

src/main.csrc/main.c · main
    /* Enable the USARTx LIN mode */
    USART_ConfigLINBreakDetectLength(USARTx, USART_LINBDL_10B);
    USART_EnableLIN(USARTx, ENABLE);

    /* Enable USARTx Receive and LIN break detect interrupts */
    USART_ConfigInt(USARTx, USART_INT_RXDNE, ENABLE);
    USART_ConfigInt(USARTx, USART_INT_LINBD, ENABLE);

    /* Enable the USARTx */
    USART_Enable(USARTx, ENABLE);

    /* Configure slave ID mode */
    IDType[MSG_RECEIVE_ID & 0x3F] = ID_TYPE_SR;
    IDType[MSG_SEND_ID & 0x3F] = ID_TYPE_SW;

IDType is indexed by data & 0x3F, so the registration must correspond to the PID the master sends; changing a frame ID means changing both ends.

2. Reset the receive state with the break-detect interrupt. When a break frame arrives, LIN_IRQHandler checks USART_FLAG_LINBD, clears the flag and completes the clear sequence by reading STS and DAT, then sets the state to SYNCH to wait for the sync byte.

src/lin_slave.csrc/lin_slave.c · LIN_IRQHandler
void LIN_IRQHandler(void)
{
    if(USART_GetFlagStatus(USARTx, USART_FLAG_LINBD) == SET)
    {
        USART_ClrFlag(USARTx, USART_FLAG_LINBD);
        USARTx->STS;
        USARTx->DAT;
        LinRxState = SYNCH;
        return;
    }

    if (USART_GetFlagStatus(USARTx, USART_FLAG_RXDNE) == SET)
    {
        USART_ClrFlag(USARTx, USART_FLAG_RXDNE);
        if(USART_GetFlagStatus(USARTx, USART_FLAG_FEF) == RESET)
        {
            LIN_EX_RxAsync((uint8_t)USART_ReceiveData(USARTx));
        }
    }

    /* Determine if an error flag still exists and clear the error flag */
    if ((USART_GetFlagStatus(USARTx, USART_FLAG_OREF) != RESET) ||
        (USART_GetFlagStatus(USARTx, USART_FLAG_NEF) != RESET)  ||
        (USART_GetFlagStatus(USARTx, USART_FLAG_PEF) != RESET)  ||
        (USART_GetFlagStatus(USARTx, USART_FLAG_FEF) != RESET))
    {
        (void)USARTx->STS;
        (void)USARTx->DAT;
    }
}

The USART_FLAG_LINBD clear sequence (read USART_STS, then read USART_DAT) and the clearing of the four error flags in the RXDNE branch must be kept; when another port is used, replace USARTx with its macro.

3. Receive a frame byte by byte with a four-state machine. LIN_EX_RxAsync checks 0x55 in SYNCH, checks the PID in ID_LEN and uses IDType to decide whether to receive data or reply at once, collects 8 bytes in DATA_GET before moving to CHECKSUM, and sets GotMsgFlag once the frame is complete.

src/lin_slave.csrc/lin_slave.c · LIN_EX_RxAsync
void LIN_EX_RxAsync(uint8_t data)
{
    switch(LinRxState)
    {
    case IDLE:
        break;
    case SYNCH:
        if(data == 0x55)
        {
            LINRxDataBuffer[LINRxDataIndex].Sync = 0x55;
            LinRxState = ID_LEN;
        }
        else
        {
            LinRxState = IDLE;
        }
        break;
    case ID_LEN:
        if(GET_PID(data) == data)
        {
            LINRxDataBuffer[LINRxDataIndex].PID = data;
            LINRxDataBuffer[LINRxDataIndex].DataLen = 0;
            if(IDType[data & 0x3F] == ID_TYPE_SR)
            {
                LinRxState = DATA_GET;
            }
            else
            {
                if(((LINTxMsg.PID & 0x3F) == (data & 0x3F)) && (LINTxMsg.DataLen > 0))
                {
                    LIN_SendBytes(LINTxMsg.Data, LINTxMsg.DataLen + 1);
                    LINTxMsg.DataLen = 0;
                }
                LinRxState = IDLE;
            }
        }
        else
        {
            LinRxState = IDLE;
        }
        break;
    case DATA_GET:
        LINRxDataBuffer[LINRxDataIndex].Data[LINRxDataBuffer[LINRxDataIndex].DataLen] = data;
        LINRxDataBuffer[LINRxDataIndex].Check = data;
        LINRxDataBuffer[LINRxDataIndex].DataLen++;
        if(LINRxDataBuffer[LINRxDataIndex].DataLen >= 8)
        {
            LinRxState = CHECKSUM;
        }
        else
        {
            LinRxState = DATA_GET;
        }
        break;
    case CHECKSUM:
        LINRxDataBuffer[LINRxDataIndex].Check = data;
        pLINMsg = &LINRxDataBuffer[LINRxDataIndex];
        GotMsgFlag = 1;
        LINRxDataIndex = (LINRxDataIndex + 1) % 2;
        LinRxState = IDLE;
        break;
    default:
        break;
    }
}

The data segment always collects 8 bytes (it enters the checksum state only when DataLen >= 8) and the double-buffer index LINRxDataIndex alternates between 0 and 1; changing the data length means changing this and the LIN_EX_MSG.Data array size together.

4. Run the command and reply with a checksum frame. When the main loop sees GotMsgFlag it calls BOOT_ExecutiveCommand, which changes the 8 bytes to 0x01 and hands them to LIN_SetResp through the LIN_RespData callback; LIN_SetResp converts the PID, computes the checksum with the classic algorithm and stores it in the message to send.

src/lin_slave.csrc/lin_slave.c · BOOT_ExecutiveCommand
void BOOT_ExecutiveCommand(uint8_t *pData, FUN_RESP pFunResp)
{
    int i = 0;
    log_info("BOOT_ExecutiveCommand\r\n");
    for( i = 0; i < 8; i++)
    {
        log_info("pData[%d] = %02x \r\n", i, pData[i]);
        pData[i] = 0x1;
    }
    pFunResp(pData, 8);
}
src/lin_slave.csrc/lin_slave.c · LIN_SetResp
void LIN_SetResp(uint8_t ID, uint8_t *pData, uint8_t Len, uint8_t CheckType)
{
    uint8_t i = 0;
    uint8_t CheckSum = 0;
    if(Len > 8)
    {
        Len = 8;
    }
    LINTxMsg.PID = GET_PID(ID);
    for(i = 0; i < Len; i++)
    {
        LINTxMsg.Data[i] = pData[i];
    }
    if(CheckType)
    {
        CheckSum = LIN_GetCheckSum(&LINTxMsg.PID, Len + 1);
    }
    else
    {
        CheckSum = LIN_GetCheckSum(LINTxMsg.Data, Len);
    }
    if(Len < 8)
    {
        LINTxMsg.Data[Len] = CheckSum;
    }
    else
    {
        LINTxMsg.Check = CheckSum;
    }
    LINTxMsg.DataLen = Len;
}

When CheckType is 0, LIN_SetResp computes the checksum over the data segment only, and the GET_PID macro supplies the two parity bits; changing the checksum type or the ID must stay consistent with the master.

Corresponding source files: src/main.c, src/lin_slave.c, inc/main.h, inc/lin_slave.h

5.21.3.13 MultiProcessor Multiprocessor address wakeup

Example directory: projects/n32g41x_EVAL/examples/USART/MultiProcessor/

Configure the two USARTs and address-mark wakeup→USART1 keeps sending one byte to USART2→USART2 lights LED1 on each reception→press KEY1 to put USART2 into mute mode→press KEY1 again so USART1 sends an address frame that wakes it

Item Source analysis
Function Shows multiprocessor address-mark wakeup: USART1 acts as transmitter and keeps sending 0x33, USART2 as receiver lights LED1 on each reception;
pressing KEY1 puts USART2 into mute mode (LED1 stops toggling) and pressing it again makes USART1 send the matching address frame 0x102 to wake it.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, Delay;
src/bsp_led.c, inc/bsp_led.h: LED1 (PA8) definitions and LED_GPIO_Config;
src/bsp_key.c, inc/bsp_key.h: KEY1 (PA4) EXTI configuration;
src/n32g41x_it.c, inc/n32g41x_it.h: EXTI4_IRQHandler and the other interrupt handlers;
inc/main.h: USART1/USART2 and pin macros;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USART1 is USARTy, TX is PA9, RX is PA10;
USART2 is USARTz, TX is PA2, RX is PA3, both with GPIO_AF4;
baud rate 115200, word length USART_WL_9B, stop bits USART_STPB_1, parity USART_PE_NO, flow control USART_HFCTRL_NONE, oversampling USART_16OVER;
USART_SetAddr sets the USARTy address to 0x1 and the USARTz address to 0x2, and USART_ConfigWakeUpMode sets USARTz to USART_WUM_ADDRMASK. KEY1 is PA4 input with pull-up and EXTI4 falling edge;
LED1 is PA8 push-pull output.
The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main runs RCC_Configuration, GPIO_Configuration, LED_GPIO_Config and EXTI_Key_Config, configures and enables USARTy and USARTz with the same USART_InitType, then sets the addresses and the wakeup mode;
in while(1) USARTy sends 0x33 with USART_SendData and waits for USART_FLAG_TXDE, and when USARTz USART_FLAG_RXDNE is set and the received byte is 0x33, LED1_ON/LED1_OFF with Delay turn LED1 on and off.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_SetAddr, USART_ConfigWakeUpMode, USART_EnableRcvWakeUp, USART_SendData, USART_GetFlagStatus, USART_ReceiveData
Verification After reset LED1 keeps toggling;
pressing KEY1 puts USARTz into mute mode and LED1 stops toggling;
pressing KEY1 again makes USARTy send the address frame 0x102, USARTz wakes up and LED1 toggles again.
Use note Disconnect the MCU_TX and MCU_RX jumper caps of the NS-LINK on the evaluation board first;
connect USARTy TX (PA9) to USARTz RX (PA3) with one wire;
the PA9 alternate function is GPIO_AF4 on the N32G412 and GPIO_AF0 on the N32G415 and the code already distinguishes the models;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses two USARTs to show address-mark wakeup: the transmitter wakes the receiver with a fixed matching address frame, and the receiver switches between normal operation and mute through a key interrupt.

Data path: USARTy (PA9) sends 0x33 -> USARTz (PA3) receives -> USART_FLAG_RXDNE set and data is 0x33 -> LED1_ON/LED1_OFF change the PA8 level -> KEY1 (PA4) -> EXTI4_IRQHandler toggles ControlFlag -> USART_EnableRcvWakeUp enters mute or USART_SendData(USARTy, 0x102) wakes it

1. Select the two USARTs and the pins with macros. inc/main.h groups both ports, clocks, TX/RX pins and alternate function numbers, and branches PA9’s alternate function by model.

inc/main.hinc/main.h
#define USARTy            USART1
#define USARTy_CLK        RCC_APB2_PERIPH_USART1
#define USARTy_GPIO       GPIOA
#define USARTy_TxPin      GPIO_PIN_9
#define USARTy_RxPin      GPIO_PIN_10
#if defined(N32G412)
#define USARTy_Tx_GPIO_AF GPIO_AF4 
#elif defined(N32G415)
#define USARTy_Tx_GPIO_AF GPIO_AF0
#endif
#define USARTy_Rx_GPIO_AF GPIO_AF4

#define USARTy_APBxClkCmd RCC_EnableAPB2PeriphClk

#define USARTz            USART2
#define USARTz_CLK        RCC_APB1_PERIPH_USART2
#define USARTz_GPIO       GPIOA
#define USARTz_TxPin      GPIO_PIN_2
#define USARTz_RxPin      GPIO_PIN_3
#define USARTz_Tx_GPIO_AF GPIO_AF4
#define USARTz_Rx_GPIO_AF GPIO_AF4
#define USARTz_APBxClkCmd RCC_EnableAPB1PeriphClk
#endif

USARTy_Tx_GPIO_AF is selected by #if defined(N32G412)/#elif defined(N32G415); changing the port or pin means changing this group of macros, not the port configuration code.

2. Configure the two USARTs, the addresses and the wakeup mode. main initializes USARTy and USARTz with the same structure, enables them, sets the addresses to 0x1 and 0x2, and sets USARTz to wake up by address mark.

src/main.csrc/main.c · main
    /* USARTy and USARTz configuration */
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = USART_WL_9B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.OverSampling        = USART_16OVER; 
    USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
    
    /* Configure USARTy and USARTz */
    USART_Init(USARTy, &USART_InitStructure);
    USART_Init(USARTz, &USART_InitStructure);

    /* Enable USARTy and USARTz */
    USART_Enable(USARTy, ENABLE);
    USART_Enable(USARTz, ENABLE);
    
    /* Set USARTy address to 0x1, USARTz address to 0x2 */
    USART_SetAddr(USARTy, 0x1);
    USART_SetAddr(USARTz, 0x2);

    /* Configure USARTz to wake up by address mark */
    USART_ConfigWakeUpMode(USARTz, USART_WUM_ADDRMASK);

The data is 9 bits (USART_WL_9B) when an address matches, with the MSB set marking an address frame; USART_WUM_ADDRMASK selects address-mark rather than idle-line wakeup.

3. Transfer in the main loop and report through the LED. USARTy sends one 0x33 per round and waits for the transmit buffer to empty, and USARTz toggles LED1 each time it receives 0x33.

src/main.csrc/main.c · main
    while (1)
    {
        /* Send one byte from UARTy to UARTz */
        USART_SendData(USARTy, 0x33);

        /* Wait while UARTy TXE = 0 */
        while(USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }

        if (USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) != RESET)
        {
            if (USART_ReceiveData(USARTz) == 0x33)
            {
                LED1_ON;
                Delay(0x1FFFFF);
                LED1_OFF;
                Delay(0x1FFFFF);
            }
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET)
            {
                USARTz->DAT;
            }
        }
    }

The Delay argument sets the LED on/off rhythm; the byte USARTz receives must match the value the transmitter sends (0x33), so changing it means changing both ends.

4. Switch mute and wakeup in the key interrupt. Each press of KEY1 triggers EXTI4_IRQHandler: when ControlFlag is 0 it flushes the received data and puts USARTz into mute, when it is 1 USARTy sends the address frame 0x102, and when it is 2 the flag is reset.

src/n32g41x_it.csrc/n32g41x_it.c · EXTI4_IRQHandler
void EXTI4_IRQHandler(void)
{
    Delay(0x1FFF);;
    if (ControlFlag == 2)
    {
        ControlFlag = 0;
    }

    if (ControlFlag == 0)
    {
        /* Flush DAT register and clear the UARTz RXNE flag */
        USART_ReceiveData(USARTz);
        /* Enable the UARTz mute mode*/
        USART_EnableRcvWakeUp(USARTz, ENABLE);

        ControlFlag = 1;
    }
    else if (ControlFlag == 1)
    {
        /* Send the address mark (0x102) to wakeup UARTz */
        USART_SendData(USARTy, 0x102);
        /* Wait while UARTy TXE = 0 */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        {
        }

        ControlFlag = 2;
    }

    /* Clear Key Button EXTI Line Pending Bit */
    EXTI_ClrITPendBit(KEY1_INT_EXTI_LINE);
}

Before entering mute the received data is read out to clear RXDNE, otherwise the USART_EnableRcvWakeUp write is ignored; the low 4 bits of the wakeup address 0x102 must equal the USART_SetAddr value of USARTz.

Corresponding source files: src/main.c, src/n32g41x_it.c, inc/main.h, inc/bsp_led.h, inc/bsp_key.h

5.21.3.14 Polling Polling transfer

Example directory: projects/n32g41x_EVAL/examples/USART/Polling/

Configure USART1 and USART2 for polling transfer→USART1 sends buffer 1 to USART2 byte by byte→USART2 stores the data in receive buffer 2→USART2 sends buffer 2 back to USART1→compare the send and receive buffers into two status variables

Item Source analysis
Function Shows bidirectional communication between USARTy (USART1) and USARTz (USART2) using polled flags: it sends USARTy’s buffer to USARTz, sends it back from USARTz, and compares the send and receive buffers into two status values.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, Buffercmp and the send/receive buffers and counters;
inc/main.h: USART1/USART2 and pin and clock macros, TestStatus;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1, TX is PA9, RX is PA10, with the TX alternate function GPIO_AF4 on the N32G412 and GPIO_AF0 on the N32G415;
USARTz is USART2, TX is PA2, RX is PA3, both with GPIO_AF4;
baud rate 115200, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, oversampling USART_16OVER, receiver and transmitter enabled;
all four USART pins are alternate push-pull with pull-up.
The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main first runs RCC_Configuration and GPIO_Configuration, configures both ports with USART_Init and enables them with USART_Enable;
in the first while USARTy sends TxBuffer1 byte by byte with USART_SendData while USARTz waits for USART_FLAG_RXDNE and stores USART_ReceiveData in RxBuffer2;
the second while sends TxBuffer2 from USARTz to USARTy into RxBuffer1;
finally Buffercmp produces TransferStatus1 and TransferStatus2.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_SendData, USART_GetFlagStatus, USART_ReceiveData
Verification After compiling, downloading and resetting, check TransferStatus1 and TransferStatus2 in order in the debugger;
both PASSED means the data matches, FAILED means it does not.
Use note Disconnect the MCU_TX and MCU_RX jumper caps of the NS-LINK on the evaluation board first;
wire USART1_TX (PA9) to USART2_RX (PA3) and USART1_RX (PA10) to USART2_TX (PA2) in a cross connection;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses no interrupts and instead chains the two ports into one question-and-answer exchange with polled flags, then compares the two receive buffers with the send buffers byte by byte.

Data path: TxBuffer1 -> USART_SendData(USARTy) -> PA9 to PA3 -> USART_ReceiveData(USARTz) -> RxBuffer2 -> TxBuffer2 -> USART_SendData(USARTz) -> PA2 to PA10 -> USART_ReceiveData(USARTy) -> RxBuffer1 -> Buffercmp produces TransferStatus1 and TransferStatus2

1. Configure and enable both ports. main fills one structure and configures and enables USARTy and USARTz with it.

src/main.csrc/main.c · main
    /* Configure the GPIO ports */
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate     = 115200;
    USART_InitStructure.WordLength   = USART_WL_8B;
    USART_InitStructure.StopBits     = USART_STPB_1;
    USART_InitStructure.Parity       = USART_PE_NO;
    USART_InitStructure.OverSampling = USART_16OVER;
    USART_InitStructure.Mode         = USART_MODE_RX | USART_MODE_TX;

    /* USARTy and USARTz configuration */
    USART_Init(USARTy, &USART_InitStructure);
    USART_Init(USARTz, &USART_InitStructure);

    /* Configure USARTy and USARTz */
    USART_Enable(USARTy, ENABLE);
    USART_Enable(USARTz, ENABLE);

Both ports use the same group of settings, so changing the baud rate or word length affects both; the pins and alternate function numbers are defined by the macros in main.h.

2. The first transfer of the polled exchange. USARTy waits for USART_FLAG_TXDE after each byte and then waits for USARTz USART_FLAG_RXDNE before reading, looping until the buffer is sent.

src/main.csrc/main.c · main
    while (TxCounter1 < TxBufferSize1)
    {
        /* Send one byte from USARTy to USARTz */
        USART_SendData(USARTy, TxBuffer1[TxCounter1++]); 
        /* Loop until USARTy DAT register is empty */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
        { 
        }
        /* Loop until the USARTz Receive Data Register is not empty */
        while (USART_GetFlagStatus(USARTz, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTz, USART_FLAG_OREF) != RESET)
            {
                USARTz->DAT;
            }
        }
        /* Store the received byte in RxBuffer */
        RxBuffer2[RxCounter2++] = (uint8_t)USART_ReceiveData(USARTz);
    }

The receive wait loop checks USART_FLAG_OREF and reads USARTz->DAT to discard data on overrun and avoid a lock-up; when the buffer length changes, TxBufferSize1 is derived from the array length automatically.

3. The second transfer of the polled exchange (reverse). After swapping the roles, USARTz sends TxBuffer2 byte by byte and USARTy stores the reception in RxBuffer1.

src/main.csrc/main.c · main
    while (TxCounter2 < TxBufferSize2)
    {
        /* Send one byte from USARTz to USARTy */
        USART_SendData(USARTz, TxBuffer2[TxCounter2++]);
        /* Loop until USARTz DAT register is empty */
        while (USART_GetFlagStatus(USARTz, USART_FLAG_TXDE) == RESET)
        {
        }

        /* Loop until the USARTy Receive Data Register is not empty */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTy, USART_FLAG_OREF) != RESET)
            {
                USARTy->DAT;
            }
        }
        /* Store the received byte in RxBuffer */
        RxBuffer1[RxCounter1++] = (uint8_t)USART_ReceiveData(USARTy);
    }

RxCounter1 increments across both loops; note that the buffers compared are RxBuffer2 and RxBuffer1, so do not swap them.

4. Compare the send and receive buffers. Buffercmp compares the two buffers byte by byte and returns PASSED when they are identical and FAILED otherwise.

src/main.csrc/main.c · Buffercmp
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
    while (BufferLength--)
    {
        if (*pBuffer1 != *pBuffer2)
        {
            return FAILED;
        }
        pBuffer1++;
        pBuffer2++;
    }

    return PASSED;
}

The comparison length comes from the send buffer length macro; increasing the data means resizing TxBuffer1, TxBuffer2 and their receive buffers together.

Corresponding source files: src/main.c, inc/main.h

5.21.3.15 Printf Serial printf

Example directory: projects/n32g41x_EVAL/examples/USART/Printf/

Initialize USART1 and the TX pin→enable USART1→print one string→enter an empty loop

Item Source analysis
Function Shows redirecting the standard output to USART1: it initializes one port and calls printf to print a string, giving a minimal usable serial debug output.
Source files src/main.c: main, GPIO_Configuration, RCC_Configuration and the print statement;
inc/main.h: the USARTx, pin and alternate function macros selected by the chip model;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTx is USART1 with clock RCC_APB2_PERIPH_USART1;
only the TX pin PA9 is configured, as alternate push-pull with pull-up, with alternate function GPIO_AF4 on the N32G412 and GPIO_AF0 on the N32G415;
baud rate 115200, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, flow control USART_HFCTRL_NONE, oversampling USART_16OVER, receiver and transmitter enabled.
The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main first runs RCC_Configuration and GPIO_Configuration, fills USART_InitType, configures USART1 with USART_Init and enables it with USART_Enable, then calls printf("This is a Printf demo of USART.\n\r") and enters an empty while(1).
Main APIs USART_StructInit, USART_Init, USART_Enable
Verification After compiling, downloading and resetting, receive on the PC serial tool at 115200-8-N-1 and see the line This is a Printf demo of USART..
Use note The readme does not require disconnecting the jumper caps for this example;
connect the serial receiver to USART1_TX (PA9);
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses model-dependent macros to select the port and TX pin, configures only the transmit direction, and prints one string through the standard library printf.

Data path: main -> RCC_Configuration enables the GPIO and USART1 clocks -> GPIO_Configuration sets PA9 to alternate push-pull -> USART_Init writes the USARTx settings -> USART_Enable -> printf sends through the USART1 TX pin

1. Select the port and pin macros by model. inc/main.h uses #if defined(N32G412)/#elif defined(N32G415) to give the USARTx, clock, TX/RX pins and alternate function numbers, which differ only in the TX alternate function.

inc/main.hinc/main.h
#if defined(N32G412)
#define USARTx             USART1
#define USARTx_CLK         RCC_APB2_PERIPH_USART1
#define USARTx_CLK_ENABLE() RCC_EnableAPB2PeriphClk(USARTx_CLK, ENABLE)

#define USARTx_GPIO        GPIOA
#define USARTx_GPIO_CLK    RCC_AHB_PERIPH_GPIO
#define USARTx_TX_PIN      GPIO_PIN_9
#define USARTx_RX_PIN      GPIO_PIN_10
#define USARTx_TX_AF       GPIO_AF4
#define USARTx_RX_AF       GPIO_AF4
#elif defined(N32G415)
#define USARTx             USART1
#define USARTx_CLK         RCC_APB2_PERIPH_USART1
#define USARTx_CLK_ENABLE() RCC_EnableAPB2PeriphClk(USARTx_CLK, ENABLE)

#define USARTx_GPIO        GPIOA
#define USARTx_GPIO_CLK    RCC_AHB_PERIPH_GPIO
#define USARTx_TX_PIN      GPIO_PIN_9
#define USARTx_RX_PIN      GPIO_PIN_10
#define USARTx_TX_AF       GPIO_AF0
#define USARTx_RX_AF       GPIO_AF4
#else
#error "Unsupported target. Please use N32G412 or N32G415."
#endif

Moving to another port or pin means changing this group of macros; both branches define USARTx, USARTx_CLK, USARTx_TX_PIN and so on, and none of them may be dropped.

2. Configure only the TX pin as alternate push-pull. GPIO_Configuration sets USARTx_TX_PIN to GPIO_MODE_AF_PP, with pull-up, and writes the alternate function; RX is not configured.

src/main.csrc/main.c · GPIO_Configuration
void GPIO_Configuration(void)
{  
    GPIO_InitType gpio_init;
    GPIO_InitStruct(&gpio_init);

    gpio_init.Pin            = USARTx_TX_PIN;
    gpio_init.GPIO_Mode      = GPIO_MODE_AF_PP;
    gpio_init.GPIO_Alternate = USARTx_TX_AF;
    gpio_init.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitPeripheral(USARTx_GPIO, &gpio_init);
}

The example only transmits and does not configure the RX pin; to receive as well, the RX pin must be configured the same way.

3. Configure USART1 and print. main sets 115200-8-N-1 and 16x oversampling, enables the port, and calls printf.

src/main.csrc/main.c · main
    USART_StructInit(&usart_init);
    usart_init.BaudRate     = 115200;
    usart_init.WordLength   = USART_WL_8B;
    usart_init.StopBits     = USART_STPB_1;
    usart_init.Parity       = USART_PE_NO;
    usart_init.Mode         = USART_MODE_RX | USART_MODE_TX;
    usart_init.HardwareFlowControl = USART_HFCTRL_NONE;
    usart_init.OverSampling = USART_16OVER;

    USART_Init(USARTx, &usart_init);
    USART_Enable(USARTx, ENABLE);
    
    printf("This is a Printf demo of USART.\n\r");

The printf redirection is implemented by the standard library support in the project; when the baud rate changes the PC serial tool must match it, otherwise the output is garbled.

Corresponding source files: src/main.c, inc/main.h

5.21.3.16 SmartCard Smart card T=0 communication

Example directory: projects/n32g41x_EVAL/examples/USART/SmartCard/

Initialize the system and the log→configure USART3 smart card mode and the reset pin→power up and wait for the card answer to reset→parse the ATR and negotiate PPS→exchange the APDU command with DMA→print the random-number result and hot reset

Item Source analysis
Function Shows USART3 SmartCard mode talking to a PSAM card over the T=0 protocol: it receives the ATR, negotiates PPS, runs the get-challenge APDU command and supports hot reset;
the PPS stage uses DMA to speed up the transfer.
Source files src/main.c: main, System_Init, Test_PSAM, SC__HotReset_handle;
src/smartcard.c: SC_Init, the SC_Handler state machine and the SC_PPSConfig, SC_SendData transfer flow;
inc/main.h;
inc/smartcard.h: the USART3, DMA and reset pin macros and the SC_ADPU_* and SC_ATR structures;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration SC_USART is USART3, TX is PB10 and CK is PB12, both with alternate function GPIO_AF0 and clock RCC_APB1_PERIPH_USART3;
the reset pin is PB9 as push-pull output;
in smart card mode the word length is USART_WL_9B, stop bits USART_STPB_1_5, even parity USART_PE_EVEN, 16x oversampling and no flow control, with clock USART_CLK_ENABLE, polarity USART_CLKPOL_LOW, phase USART_CLKPHA_1EDGE and last-bit clock USART_CLKLB_ENABLE, while USART_SetPrescaler and USART_SetGuardTime take values from SC_InitCfg;
the DMA uses DMA_CH5 (TX) and DMA_CH6 (RX).
The debug output is USART1 (PA9/PA10) at 115200.
Program and data path main first runs System_Init, then Test_PSAM enters the SC_Handler state machine: SC_POWER_ON to SC_RESET_LOW pulls the reset pin low and then high in SC_AnswerReq and receives the ATR, SC_ACTIVE parses it with SC_decode_Answer2reset, and SC_ACTIVE_ON_T0 calls SC_SendData to send the APDU;
it then calls SC_PPSConfig to negotiate the rate, sends the get-challenge command, prints the returned SC_Responce.Data, SW1 and SW2, and finishes with SC__HotReset_handle and SC_DeInit.
Main APIs USART_Init, USART_Enable, USART_EnableSmartCard, USART_SetSmartCardNACK, USART_SetGuardTime, USART_SetPrescaler, USART_ClockInit, USART_ConfigInt, USART_EnableDMA, USART_SendData, USART_ReceiveData, DMA_Init, DMA_EnableChannel
Verification With the PSAM smart card module connected, USART1 outputs debug information at 115200: it prints Pclk1, Clk_Div and the baud rate, the ATR bytes, PTS Confirm success and the negotiated baud rate, and on success prints get rand success! with the random-number bytes, with SW1 and SW2 at 0x90 and 0x00.
Use note SmartCard uses USART3 (APB1) and the debug output uses USART1 (APB2);
connect PB10 (TX) to the card I/O, PB12 (CK) to the card CLK and PB9 to the card RST;
the clock divider Clk_Div must be adjusted to the actual PCLK1 frequency to meet the card requirements;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example layers the smart card interaction: SC_Init configures the low-level clock and frame format, the state machine handles reset and the ATR, and the PPS stage negotiates the rate with DMA.

Data path: main -> SC_Handler state machine -> SC_Init configures USART3 and CK/PB9 -> SC_AnswerReq receives the ATR -> SC_decode_Answer2reset parses it -> SC_PPSConfig negotiates the rate with DMA_CH5/DMA_CH6 -> SC_SendData sends the APDU and receives SC_Responce

1. Select the smart card port, pins and DMA with macros. inc/smartcard.h groups USART3, the CK/TX pins, the reset pin and the TX/RX DMA channels, flags and remaps.

inc/smartcard.hinc/smartcard.h
/* USART3 on PB10(TX)/PB12(CK), both AF0 for G412 and G415 */
#define SC_USART                 USART3
#define SC_USART_CLK             RCC_APB1_PERIPH_USART3

#define SC_USART_GPIO            GPIOB
#define SC_USART_GPIO_CLK        RCC_AHB_PERIPH_GPIO
#define SC_USART_PIN_TX          GPIO_PIN_10
#define SC_USART_Tx_GPIO_AF      GPIO_AF0

#define SC_USART_CK_GPIO         GPIOB
#define SC_USART_GPIO_CK_CLK     RCC_AHB_PERIPH_GPIO
#define SC_USART_PIN_CK          GPIO_PIN_12
#define SC_USART_Clk_GPIO_AF     GPIO_AF0

#define SC_USART_APBxClkCmd      RCC_EnableAPB1PeriphClk
#define SC_USART_IRQn            USART3_IRQn
#define SC_USART_IRQHandler      USART3_IRQHandler

#define SC_USART_DMAx           DMA
#define SC_USART_DMAx_CLK       RCC_AHB_PERIPH_DMA
#define SC_USART_DR_Base        (USART3_BASE + 0x10)
#define SC_USART_Tx_DMA_Channel DMA_CH5
#define SC_USART_Tx_DMA_FLAG    DMA_FLAG_TC5
#define SC_USART_Tx_DMA_REMAP   DMA_REMAP_USART3_TX
#define SC_USART_Rx_DMA_Channel DMA_CH6
#define SC_USART_Rx_DMA_FLAG    DMA_FLAG_TC6
#define SC_USART_Rx_DMA_REMAP   DMA_REMAP_USART3_RX

TX uses DMA_CH5 and RX uses DMA_CH6, paired with DMA_REMAP_USART3_TX/DMA_REMAP_USART3_RX; when the port or the DMA channels change these groups must change together.

2. Configure the smart card clock and frame format. SC_Init uses USART_SetPrescaler/USART_SetGuardTime for the CK divider and guard time, configures the synchronous clock parameters, then selects the stop bits and parity from SC_InitCfg before USART_Init writes them.

src/smartcard.csrc/smartcard.c · SC_Init
    /* SC_USART Clock set to (PCLK1 / Clk_Div) */
    USART_SetPrescaler(SC_USART, SC_InitCfg->Clk_Div >> 1);
    /* USART Guard Time set to 16 Bit */
    USART_SetGuardTime(SC_USART, SC_InitCfg->GT);
    USART_ClockStructInit(&USART_ClockInitStructure);
    USART_ClockInitStructure.Clock      = USART_CLK_ENABLE;
    USART_ClockInitStructure.Polarity   = USART_CLKPOL_LOW;
    USART_ClockInitStructure.Phase      = USART_CLKPHA_1EDGE;
    USART_ClockInitStructure.LastBit    = USART_CLKLB_ENABLE;
    USART_ClockInit(SC_USART, &USART_ClockInitStructure);
    RCC_GetClocksFreqValue(&RCC_ClocksStatus);
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate = RCC_ClocksStatus.Pclk1Freq / (372 * ((SC_USART->GTP & (uint16_t)0x00FF) * 2));
    log_info(" Pclk1 =%d,Clk_Div = %d,bps =%d\r\n", RCC_ClocksStatus.Pclk1Freq, (372 * ((SC_USART->GTP & (uint16_t)0x00FF) *2)),USART_InitStructure.BaudRate);
    USART_InitStructure.WordLength = USART_WL_9B;
    if(SC_InitCfg->StopBits == 1)
    {
        USART_InitStructure.StopBits = USART_STPB_0_5;
    }
    else if(SC_InitCfg->StopBits == 3)
    {
        USART_InitStructure.StopBits = USART_STPB_1_5;
    }
    else
    {
        log_info("\r\n StopBits value is error !!! \r\n");
    }
    if(SC_InitCfg->Parity == 1)
    {
        USART_InitStructure.Parity = USART_PE_EVEN;
    }
    else if(SC_InitCfg->Parity == 2)
    {
        USART_InitStructure.Parity = USART_PE_ODD;
    }
    else
    {
        log_info("\r\n Parity value is error !!! \r\n");
    }
    USART_InitStructure.Mode                  = USART_MODE_RX | USART_MODE_TX;
    USART_InitStructure.HardwareFlowControl   = USART_HFCTRL_NONE;
    USART_InitStructure.OverSampling          = USART_16OVER;
    USART_Init(SC_USART, &USART_InitStructure);

Clk_Div is shifted right by one before being written to the prescaler, so the actual division is twice the set value; the baud rate is derived from Pclk1 and the CK divider, so changing Clk_Div changes the baud rate.

3. Enable the interrupts, NACK and smart card interface. At the end of SC_Init the parity and error interrupts are enabled, NACK transmission is selected by NackEn, the smart card interface is turned on and the reset pin is set high.

src/smartcard.csrc/smartcard.c · SC_Init
    /* Enable SC_USART */
    USART_Enable(SC_USART, ENABLE);
    /* Enable the NACK Transmission */
    if(SC_InitCfg->NackEn == 0)
    {
        USART_SetSmartCardNACK(SC_USART, DISABLE);
    }
    else if(SC_InitCfg->NackEn == 1)
    {
        USART_SetSmartCardNACK(SC_USART, ENABLE);
    }
    else
    {
        log_info("\r\n NACK Enable value is error !!! \r\n");
    }
    /* Enable the Smartcard Interface */
    USART_EnableSmartCard(SC_USART, ENABLE);
    /* Set RSTIN HIGH */
    SC_Reset(Bit_SET);
}

USART_EnableSmartCard is the key step into smart card mode; SC_Reset(Bit_SET) sets PB9 high, so the wiring must connect this pin to the card RST.

4. Negotiate PPS with DMA. SC_PPSConfig assembles PPSS/PPS0/PPS1/PCK from the card ATR, exchanges them over the TX/RX DMA channels through DMA_Configuration, and once the four bytes compare equal it recomputes the baud rate and reconfigures the port after disabling USART.

src/smartcard.csrc/smartcard.c · SC_PPSConfig
//            /* Send PPSS */
            TxBuffer1[0] = 0xFF;
//            /* Send PPS0 */
            TxBuffer1[1] = 0x10;
//            /* PPS1~3  */
            TxBuffer1[2] = SC_A2R.T[0];
//            /* PCK */
            TxBuffer1[3] = (uint8_t)0xFF ^ (uint8_t)0x10 ^ (uint8_t)SC_A2R.T[0];
            DMA_Configuration(TxBuffer1, 4, RxBuffer1, 4);            
            /* Enable USARTy DMA Rx and TX request */
            USART_EnableDMA(SC_USART, USART_DMAREQ_RX | USART_DMAREQ_TX, ENABLE);

            /* Enable USARTy TX DMA Channel */
            DMA_EnableChannel(SC_USART_Tx_DMA_Channel, ENABLE);
            /* Enable USARTy RX DMA Channel */
            DMA_EnableChannel(SC_USART_Rx_DMA_Channel, ENABLE);

            /* Wait until USARTy TX DMA Channel Transfer Complete */
            while (DMA_GetFlagStatus(SC_USART_Tx_DMA_FLAG, SC_USART_DMAx) == RESET)
            {
            }
            DMA_ClearFlag(SC_USART_Tx_DMA_FLAG, SC_USART_DMAx);
            /* Wait until USARTy RX DMA Channel Transfer Complete */
            while (DMA_GetFlagStatus(SC_USART_Rx_DMA_FLAG, SC_USART_DMAx) == RESET)
            {
            }            
            DMA_ClearFlag(SC_USART_Rx_DMA_FLAG, SC_USART_DMAx);
            log_info(" locData =%02x %02x %02x %02x\r\n", RxBuffer1[0],RxBuffer1[1],RxBuffer1[2],RxBuffer1[3]);

The PPS exchange polls the DMA transfer-complete flags; USART_Enable(DISABLE) is required before changing the baud rate because USART_BRCF and the oversampling bit are writable only while USART is disabled; on a failed negotiation the example takes the hot-reset branch.

Corresponding source files: src/main.c, src/smartcard.c, inc/main.h, inc/smartcard.h

5.21.3.17 Synchronous Synchronous mode with SPI slave

Example directory: projects/n32g41x_EVAL/examples/USART/Synchronous/

Configure USART1 synchronous mode and SPI1 slave→USART1 sends a buffer to SPI1 and receives→SPI1 sends a buffer while USART1 sends a dummy byte to generate the clock→USART1 receives the SPI1 data→compare the two send/receive buffer pairs

Item Source analysis
Function Shows bidirectional communication between USART1 in synchronous mode (clock output) and SPI1 as a slave: USART1 first sends data to SPI1, then receives data from SPI1, and finally compares the two send/receive buffer pairs.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, SPI_Configuration, Buffercmp and the send/receive buffers;
inc/main.h: USART1/SPI1 and pin and alternate function macros, TestStatus;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1, TX is PA9, RX is PA10 and CK is PA8;
SPIy is SPI1, SCK is PA5, MISO is PA6 and MOSI is PA7, all with GPIO_AF0;
USART1 is set to 115200-8-N-1 with 16x oversampling, clock USART_CLK_ENABLE, polarity USART_CLKPOL_LOW, phase USART_CLKPHA_2EDGE and last-bit clock USART_CLKLB_ENABLE;
SPI1 is the slave, SPI_MODE_SLAVE, 8 bits, SPI_CLKPOL_LOW, SPI_CLKPHA_SECOND_EDGE, SPI_NSS_SOFT and LSB first.
The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main configures the synchronous clock with USART_ClockInit, configures USART1 with USART_Init and enables it with USART_Enable;
the first loop sends TxBuffer1 byte by byte and stores it in RxBuffer2 after SPI_I2S_RNE_FLAG;
the second loop sends TxBuffer2 from SPI1 while USART1 sends a dummy byte DYMMY_BYTE to generate the clock, and stores it in RxBuffer1 after USART_FLAG_RXDNE;
finally Buffercmp produces TransferStatus1 and TransferStatus2.
Main APIs USART_ClockStructInit, USART_ClockInit, USART_StructInit, USART_Init, USART_Enable, USART_SendData, USART_GetFlagStatus, USART_ReceiveData
Verification After compiling, downloading and resetting, check TransferStatus1 and TransferStatus2 in order in the debugger;
both PASSED means the data matches in both directions, FAILED means it does not.
Use note Disconnect the MCU_TX and MCU_RX jumper caps of the NS-LINK on the evaluation board first and remove the serial print jumper;
wire USART1_TX (PA9) to SPI1_MOSI (PA7), USART1_RX (PA10) to SPI1_MISO (PA6) and USART1_CK (PA8) to SPI1_SCK (PA5);
the PA8 alternate function is GPIO_AF4 on the N32G412 and GPIO_AF5 on the N32G415;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example has USART1 output the clock as a synchronous master with SPI1 as the slave, transfers in both directions with polled flags, and compares the two buffer pairs at the end.

Data path: TxBuffer1 -> USART_SendData(USARTy) -> TX and CK to SPI1 -> SPI_I2S_ReceiveData(SPIy) -> RxBuffer2 -> SPI_I2S_TransmitData(SPIy) -> USART_SendData(USARTy, DYMMY_BYTE) generates the clock -> USART_ReceiveData(USARTy) -> RxBuffer1 -> Buffercmp produces the two TransferStatus values

1. Select the USART1 and SPI1 pins by model with macros. inc/main.h groups the USART1 and SPI1 pins and alternate function numbers, branching the USART1 TX and CK alternate functions by model.

inc/main.hinc/main.h
#ifdef _USART1_SPI1_
#define USARTy            USART1
#define USARTy_CLK        RCC_APB2_PERIPH_USART1
#define USARTy_GPIO       GPIOA
#define USARTy_RxPin      GPIO_PIN_10
#define USARTy_TxPin      GPIO_PIN_9
#define USARTy_ClkPin     GPIO_PIN_8
#if defined(N32G412)
#define USARTy_Tx_GPIO_AF GPIO_AF4
#define USARTy_Clk_GPIO_AF GPIO_AF4
#elif defined(N32G415)
#define USARTy_Tx_GPIO_AF GPIO_AF0
#define USARTy_Clk_GPIO_AF GPIO_AF5
#endif
#define USARTy_Rx_GPIO_AF  GPIO_AF4
#define USARTy_APBxClkCmd  RCC_EnableAPB2PeriphClk

#define SPIy              SPI1
#define SPIy_CLK          RCC_APB2_PERIPH_SPI1
#define SPIy_GPIO         GPIOA
#define SPIy_SCKPin       GPIO_PIN_5
#define SPIy_MISOPin      GPIO_PIN_6
#define SPIy_MOSIPin      GPIO_PIN_7
#define SPIy_SCK_GPIO_AF  GPIO_AF0
#define SPIy_MISO_GPIO_AF GPIO_AF0
#define SPIy_MOSI_GPIO_AF GPIO_AF0
#define SPIy_APBxClkCmd   RCC_EnableAPB2PeriphClk
#endif

SPIy_MOSIPin must be wired to USARTy_TxPin and USARTy_ClkPin to SPIy_SCKPin in hardware; changing the pins means changing this group of macros and the wiring together.

2. Configure the USART1 synchronous clock and frame format. main first sets the polarity, phase and last-bit clock with USART_ClockInit, then writes 115200-8-N-1 with USART_Init.

src/main.csrc/main.c · main
    USART_ClockStructInit(&USART_ClockInitStructure);
    USART_ClockInitStructure.Clock    = USART_CLK_ENABLE;
    USART_ClockInitStructure.Polarity = USART_CLKPOL_LOW;
    USART_ClockInitStructure.Phase    = USART_CLKPHA_2EDGE;
    USART_ClockInitStructure.LastBit  = USART_CLKLB_ENABLE;
    USART_ClockInit(USARTy, &USART_ClockInitStructure);
src/main.csrc/main.c · main
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.OverSampling        = USART_16OVER;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure USARTy */
    USART_Init(USARTy, &USART_InitStructure);

    /* Enable the USARTy */
    USART_Enable(USARTy, ENABLE);

USART_CLKPHA_2EDGE corresponds to SPI1 SPI_CLKPHA_SECOND_EDGE, so the phases must match; in synchronous mode CK outputs the clock only while the transmitter is active and data is written.

3. Configure SPI1 as the slave. SPI_Configuration fills SPI_Init with slave mode, 8 bits, CPOL/CPHA, software NSS and LSB first, and immediately enables SPI1.

src/main.csrc/main.c · SPI_Configuration
void SPI_Configuration(void)
{
    SPI_InitType SPI_InitStructure;

    /* Initialize SPI_InitStructure */
    SPI_InitStruct(&SPI_InitStructure);

    SPI_I2S_DeInit(SPIy);

    /* SPIy Config */
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_SLAVE;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    SPI_InitStructure.FirstBit      = SPI_FB_LSB;

    /* Configure SPIy */
    SPI_Init(SPIy, &SPI_InitStructure);

    /* SPIy enable */
    SPI_Enable(SPIy, ENABLE);
}

The SPI1 CPOL/CPHA and bit order must match the USART1 synchronous parameters; SPI_NSS_SOFT means slave selection is managed by software.

4. Transfer in both directions and compare. The first loop has USART1 send and SPI1 receive; the second has SPI1 send while USART1 sends a dummy byte to generate the clock and then receives, and finally the two buffer pairs are compared.

src/main.csrc/main.c · main
    while(NbrOfDataToRead2--)
    {
        /* Write one byte in the USARTy Transmit Data Register */
        USART_SendData(USARTy, TxBuffer1[TxCounter1++]);
        /* Wait until end of transmit */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXC) == RESET)
        {
        }

        /* Wait the byte is entirely received by SPIy */
        while (SPI_I2S_GetStatus(SPIy, SPI_I2S_RNE_FLAG) == RESET)
        {
        }
        /* Store the received byte in the RxBuffer2 */
        RxBuffer2[RxCounter2++] = SPI_I2S_ReceiveData(SPIy);
    }
src/main.csrc/main.c · main
    while (NbrOfDataToRead1--)
    {
        /* Wait until end of transmit */
        while (SPI_I2S_GetStatus(SPIy, SPI_I2S_TE_FLAG) == RESET)
        {
        }
        /* Write one byte in the SPIy Transmit Data Register */
        SPI_I2S_TransmitData(SPIy, TxBuffer2[TxCounter2++]);

        /* Send a Dummy byte to generate clock to slave */
        USART_SendData(USARTy, DYMMY_BYTE);
        /* Wait until end of transmit */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_TXC) == RESET)
        {
        }
        /* Wait the byte is entirely received by USARTy */
        while (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(USART_GetFlagStatus(USARTy, USART_FLAG_OREF) != RESET)
            {
                USARTy->DAT;
            }
        }
        /* Store the received byte in the RxBuffer1 */
        RxBuffer1[RxCounter1++] = USART_ReceiveData(USARTy);
    }

In the second loop the DYMMY_BYTE USART1 sends only generates the CK clock and is not valid data; the transfer length is derived from the two buffer length macros, so changing the data size means changing the buffers and the lengths together.

Corresponding source files: src/main.c, inc/main.h

5.21.3.18 WakeUpFromSleep Wakeup from SLEEP

Example directory: projects/n32g41x_EVAL/examples/USART/WakeUpFromSleep/

Configure USART1 for 115200 transfer→send one incrementing byte every second→on receiving 0x55 enable the receive interrupt and enter SLEEP→the receive interrupt wakes the CPU through the NVIC→disable the receive interrupt and resume sending

Item Source analysis
Function Shows waking the MCU from SLEEP mode with the USART1 RXDNE interrupt: it normally sends one incrementing byte every second, and after receiving 0x55 it enters SLEEP and is woken by an interrupt on the next data received, then resumes sending.
Source files src/main.c: main, GPIO_Configuration, NVIC_Configuration and the pin configuration;
inc/main.h: USART1 and pin macros, USART1_IRQn, the global variable declarations;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration USARTy is USART1, TX is PA9, RX is PA10, with the TX alternate function GPIO_AF4 on the N32G412 and GPIO_AF0 on the N32G415;
baud rate 115200, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, oversampling USART_16OVER, receiver and transmitter enabled;
SLEEP is entered with PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI);
the USART1 NVIC preemption and sub-priority are both 0 and the IRQ number is USART1_IRQn. The system clock is HSI+PLL, 80 MHz on the N32G412 and 96 MHz on the N32G415.
Program and data path main enables the USART1 and GPIO clocks, runs GPIO_Configuration and NVIC_Configuration, configures and enables USART1 with USART_Init/USART_Enable;
in while(1), after each SysTick_Delay_Ms(1000), it checks: if USART_FLAG_RXDNE is set and the value read is 0x55 it enables the USART_INT_RXDNE interrupt and enters SLEEP with PWR_EnterSLEEPMode, and after wakeup disables that interrupt;
otherwise it sends the low 8 bits of cnt_temp with USART_SendData and waits for USART_FLAG_TXDE.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_ConfigInt, USART_SendData, USART_GetFlagStatus, USART_ReceiveData
Verification After reset the serial tool receives one incrementing byte per second at 115200-8-N-1;
sending 0x55 pauses the printing (SLEEP is entered) and sending any data resumes it.
Use note Wire USART1_TX (PA9) to the PC RX and USART1_RX (PA10) to the PC TX;
in SLEEP mode all clocks keep running, so no system clock reconfiguration is needed after wakeup;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example switches between normal operation and SLEEP with a receive flag: it sends one byte per second, and on the agreed 0x55 it opens the receive interrupt and enters SLEEP, which the interrupt then wakes from.

Data path: main -> GPIO_Configuration/NVIC_Configuration -> USART_Init/USART_Enable -> the main loop sends an incrementing byte with USART_SendData every 1 s -> receives 0x55 -> USART_ConfigInt(USART_INT_RXDNE, ENABLE) -> PWR_EnterSLEEPMode -> the RXDNE interrupt wakes the CPU through the NVIC -> the interrupt is disabled and sending resumes

1. Select USART1 and the pins with macros. inc/main.h defines USART1, its clock, TX/RX pins and alternate function as one group, and declares the wakeup-related global variables.

inc/main.hinc/main.h
#define USARTy              USART1
#define USARTy_CLK          RCC_APB2_PERIPH_USART1
#define USARTy_GPIO         GPIOA
#define USARTy_TxPin        GPIO_PIN_9
#define USARTy_RxPin        GPIO_PIN_10
#if defined(N32G412)
#define USARTy_Tx_GPIO_AF   GPIO_AF4
#elif defined(N32G415)
#define USARTy_Tx_GPIO_AF   GPIO_AF0
#endif
#define USARTy_Rx_GPIO_AF   GPIO_AF4
#define USARTy_APBxClkCmd   RCC_EnableAPB2PeriphClk
#define USARTy_IRQn         USART1_IRQn

USARTy_Tx_GPIO_AF differs by model; USARTy_IRQn must align with the USART1 interrupt number in the vector table, so another port means changing it too.

2. Configure USART1 as 115200-8-N-1. main fills the fixed settings and configures and enables the port with USART_Init/USART_Enable.

src/main.csrc/main.c · main
    /* USART configuration: 115200-8-N-1 */
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.OverSampling        = USART_16OVER;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Initialize and enable USART1 */
    USART_Init(USARTy, &USART_InitStructure);
    USART_Enable(USARTy, ENABLE);

The port settings are fixed to 115200-8-N-1; when the baud rate changes the PC serial tool must match it.

3. Transfer in the main loop and enter and leave SLEEP. It acts once per second: on receiving 0x55 it opens the receive interrupt and enters SLEEP, otherwise it sends one incrementing byte.

src/main.csrc/main.c · main
    while (1)
    {
        /* Insert a delay */
        SysTick_Delay_Ms(1000);

        if (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) == SET)
        {
            if (USART_ReceiveData(USARTy) == 0x55)
            {
                /* Enable USART1 RXDNE interrupt for wakeup */
                USART_ConfigInt(USARTy, USART_INT_RXDNE, ENABLE);

                /* Enter SLEEP mode with WFI (SLEEP-NOW mode)
                   CPU stops, all peripherals keep running.
                   Any NVIC interrupt (here USART1 RXDNE) will wake up the CPU. */
                PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI);

                /* --- Woken up from SLEEP mode by USART1 RXDNE interrupt --- */

                /* Disable USART1 RXDNE interrupt (no longer needed for wakeup) */
                USART_ConfigInt(USARTy, USART_INT_RXDNE, DISABLE);

                WakeUpFlag = 0;
            }
        }
        else
        {
            USART_SendData(USARTy, (uint8_t)(cnt_temp++ & 0xFF));
            while (USART_GetFlagStatus(USARTy, USART_FLAG_TXDE) == RESET)
            {
            }
        }

        /* Clear overrun error if occurred */
        if (USART_GetFlagStatus(USARTy, USART_FLAG_OREF) != RESET)
        {
            (void)USARTy->DAT;
        }
    }

It enters SLEEP with PWR_SLEEP_NOW and WFI, so any NVIC interrupt can wake it; other low-power modes may need the system clock to be reconfigured after wakeup, but SLEEP does not.

Corresponding source files: src/main.c, inc/main.h

5.21.3.19 WakeUpFromStop Wakeup from STOP

Example directory: projects/n32g41x_EVAL/examples/USART/WakeUpFromStop/

Light LED1 for five seconds after reset as a download window→configure USART1 low-power wakeup and switch to LSI→enter STOP and wait for reception→one frame of data wakes the MCU out of STOP through EXTI→reconfigure the GPIO and USART1 and light LED1 for one second

Item Source analysis
Function Shows the USART1 low-power wakeup event waking the MCU from STOP mode: in STOP the system clock is off and USART1 keeps running on LSI, one received frame generates a wakeup event through EXTI24 that exits STOP, and after wakeup the example reconfigures and lights LED1 for one second.
Source files src/main.c: main, LED1_Init, GPIO_Configuration, USART1_PrepareStopWakeup, USART1_RestoreAfterStop;
inc/main.h: USART1 and pin macros, USARTy_WKUP_EXTI_LINE;
src/n32g41x_it.c, inc/n32g41x_it.h: USART1_IRQHandler and the other interrupt handlers;
readme.txt: function, environment and usage;
MDK-ARM/, EWARM/: Keil and IAR projects, with the LED pin macros from bsp/inc/n32g41x_stb.h.
Key configuration USARTy is USART1, TX is PA9, RX is PA10, with the TX alternate function GPIO_AF4 on the N32G412 and GPIO_AF0 on the N32G415;
baud rate 1200, word length USART_WL_8B, stop bits USART_STPB_1, parity USART_PE_NO, oversampling USART_16OVER;
the prescaler is left unchanged, and on wakeup RCC_ConfigUsart1Clk switches to RCC_USART1_CLKSEL_LSI and USARTy->BRCF = 0x1AU is written;
USART_CfgWakeUpDataClr is set to USART_WAKEUP_NOTCLRDATA, USART_EnableWakeUp enables wakeup and EXTI uses EXTI_LINE24 on the rising edge;
the low-power clock is RCC_ConfigLowPowerClk(RCC_LP_CLKSEL_LSI).
Program and data path main enables the USART1 and GPIO clocks, after GPIO_Configuration and LED1_Init lights LED1 for 5 s and turns it off;
it enables LSI, selects the low-power clock, sets up the NVIC and EXTI24, and enters while(1): it turns LED1 off, calls USART1_PrepareStopWakeup to move USART1 to APB2, configure wakeup and BRCF and switch to LSI, and enters STOP with PWR_EnterSTOPMode;
USART1_IRQHandler sets UsartWakeupFlag, and main then calls USART1_RestoreAfterStop to reconfigure and lights LED1 for 1 s.
Main APIs USART_StructInit, USART_Init, USART_Enable, USART_ConfigInt, USART_EnableWakeUp, USART_CfgWakeUpDataClr, USART_GetFlagStatus, USART_ReceiveData
Verification After reset LED1 is on for 5 s and then off;
set the serial tool to 1200-8-N-1 and send 0x55, and the MCU exits STOP with LED1 on for 1 s, after which LED1 turns off and the MCU enters STOP again in a loop.
Use note Wire USART1_TX (PA9) to the PC RX and USART1_RX (PA10) to the PC TX;
the low-power wakeup only needs 1200 baud on the sending side;
make sure USART is disabled before writing USART_BRCF;
supports N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example organizes the code around entering and leaving STOP: before entering it moves USART1 from APB2 to LSI and enables wakeup, and after leaving it switches back to APB2 and restores, with the two configurations strictly symmetric.

Data path: main -> LED1 on for 5 s -> enable LSI and select the low-power clock -> set up the NVIC and EXTI24 -> USART1_PrepareStopWakeup switches to APB2, configures wakeup and BRCF, switches to LSI -> PWR_EnterSTOPMode -> USART1_IRQHandler sets UsartWakeupFlag -> USART1_RestoreAfterStop -> LED1 on for 1 s

1. Define the port, pins and wakeup EXTI line with macros. inc/main.h gives USART1, the TX/RX pins and alternate function macros, and binds the wakeup event to EXTI_LINE24.

inc/main.hinc/main.h
#define USARTy              USART1
#define USARTy_CLK          RCC_APB2_PERIPH_USART1
#define USARTy_GPIO         GPIOA
#define USARTy_TxPin        GPIO_PIN_9
#define USARTy_RxPin        GPIO_PIN_10
#if defined(N32G412)
#define USARTy_Tx_GPIO_AF   GPIO_AF4
#elif defined(N32G415)
#define USARTy_Tx_GPIO_AF   GPIO_AF0
#endif
#define USARTy_Rx_GPIO_AF   GPIO_AF4
#define USARTy_APBxClkCmd   RCC_EnableAPB2PeriphClk

#define USARTy_WKUP_EXTI_LINE   EXTI_LINE24

extern volatile uint8_t UsartWakeupFlag;

USARTy_WKUP_EXTI_LINE is EXTI_LINE24 and shares the same interrupt vector as USART1_IRQn; when another port is used this EXTI line must follow the mapping given in the manual.

2. Low-power configuration before entering STOP. USART1_PrepareStopWakeup first disables USART, switches it to the APB2 clock and reconfigures, reads out any residue, enables wakeup and the RXDNE interrupt, writes BRCF and finally switches to LSI.

src/main.csrc/main.c · USART1_PrepareStopWakeup
static void USART1_PrepareStopWakeup(void)
{
    /* Configure USART on APB2, then make LSI the last clock-source change. */
    /* Reconfigure only while USART1 is clocked from APB2. */

    /* requires UEN=0: CTRL1.OSPM and BRCF are only writable while the
   USART is disabled, so a running port must be quiesced first. The baud rate
   change also aborts the frame being transmitted/received. */
    USART_Enable(USARTy, DISABLE);
    RCC_ConfigUsart1Clk(RCC_USART1_CLKSEL_APB2);
    USARTy_APBxClkCmd(USARTy_CLK, ENABLE);
    USART_Init(USARTy, &USART_InitStructure);

    if (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) != RESET)
    {
        (void)USART_ReceiveData(USARTy);
    }

    /* The ISR sets this flag after the WFI wakeup interrupt occurs. */
    UsartWakeupFlag = 0U;
    USART_CfgWakeUpDataClr(USARTy, USART_WAKEUP_NOTCLRDATA);
    USART_EnableWakeUp(USARTy, ENABLE);
    /* RXDNE is the interrupt source that wakes WFI through EXTI24. */
    USART_ConfigInt(USARTy, USART_INT_RXDNE, ENABLE);
    USARTy->BRCF = 0x1AU;
    EXTI_ClrITPendBit(USARTy_WKUP_EXTI_LINE);
    USART_Enable(USARTy, ENABLE);
    RCC_ConfigUsart1Clk(RCC_USART1_CLKSEL_LSI);
}

USART_BRCF and the oversampling bit are writable only while USART is disabled (UEN=0), so this runs after USART_Enable(DISABLE); USARTy->BRCF = 0x1AU is the divider computed for the LSI frequency and must be recomputed when the baud rate or the clock source changes.

3. Restore the APB2 configuration after wakeup. USART1_RestoreAfterStop re-enables the GPIO and LED1, switches USART1 back to APB2, reconfigures USART and disables the wakeup and RXDNE interrupts.

src/main.csrc/main.c · USART1_RestoreAfterStop
static void USART1_RestoreAfterStop(void)
{
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);
    LED1_Init();

    /* Reconfigure only while USART1 is clocked from APB2. */
    USART_Enable(USARTy, DISABLE);
    RCC_ConfigUsart1Clk(RCC_USART1_CLKSEL_APB2);
    USARTy_APBxClkCmd(USARTy_CLK, ENABLE);
    USART_Init(USARTy, &USART_InitStructure);
    USART_ConfigInt(USARTy, USART_INT_RXDNE, DISABLE);
    USART_EnableWakeUp(USARTy, DISABLE);
    USART_Enable(USARTy, ENABLE);
}

The restore order is the reverse of the setup: switch back to APB2, then USART_Init, and finally disable the wakeup function; LED1_Init relies on the LED1 macros in n32g41x_stb.h, which must change with the board.

4. Enter STOP and handle the wakeup interrupt. main configures LSI, the NVIC and EXTI24 and enters the main loop, and the wakeup interrupt in USART1_IRQHandler sets the flag, clears EXTI and lights LED1.

src/main.csrc/main.c · main
    RCC_EnableLsi(ENABLE);
    while (RCC_GetFlagStatus(RCC_CTRL_FLAG_LSIRDF) == RESET)
    {
    }
    RCC_ConfigLowPowerClk(RCC_LP_CLKSEL_LSI);

    /* USART1_IRQn also carries the USART1 low-power EXTI24 interrupt. */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* WFI requires an interrupt source: EXTI24 routes to USART1_IRQn. */
    EXTI_InitStructure.EXTI_Line    = USARTy_WKUP_EXTI_LINE;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);
src/main.csrc/main.c · main
    while (1)
    {
        GPIO_ResetBits(LED1_PORT, LED1_PIN);
        USART1_PrepareStopWakeup();
        PWR_EnterSTOPMode(PWR_STOPENTRY_WFI);

        /* Only process the STOP exit after USART1_IRQHandler confirms it. */
        if (UsartWakeupFlag != 0U)
        {
            USART1_RestoreAfterStop();
            GPIO_SetBits(LED1_PORT, LED1_PIN);
            SysTick_Delay_Ms(1000);
        }
    }
src/n32g41x_it.csrc/n32g41x_it.c · USART1_IRQHandler
void USART1_IRQHandler(void)
{
    /* Stop a level-held RXDNE from immediately retriggering this IRQ. */
    USART_ConfigInt(USARTy, USART_INT_RXDNE, DISABLE);

    if (USART_GetFlagStatus(USARTy, USART_FLAG_RXDNE) != RESET)
    {
        (void)USART_ReceiveData(USARTy);
    }

    /* Clear EXTI24 so a previous wake request cannot retrigger the IRQ. */
    EXTI_ClrITPendBit(USARTy_WKUP_EXTI_LINE);
    /* Notify main that this was a valid USART wakeup. */
    UsartWakeupFlag = 1U;
    GPIO_SetBits(LED1_PORT, LED1_PIN);
}

The interrupt disables RXDNE first and then reads the data out to avoid retriggering while the data is held; the main loop continues only after UsartWakeupFlag is set, ensuring the wakeup came from one valid reception.

Corresponding source files: src/main.c, inc/main.h, src/n32g41x_it.c, projects/n32g41x_EVAL/bsp/inc/n32g41x_stb.h

5.21.4 Notes and troubleshooting

Scenario Check & action Source
Changing the baud rate or oversampling after enabling Chapter 21 of the user manual states that USART_BRCF cannot be written while USART_CTRL1.UEN=1 and that USART_CTRL1.OSPM is writable only while USART is disabled;
clear UEN before changing the baud rate or oversampling.
User manual
Changing the word length or disabling the transmitter mid-transfer The user manual states that USART_CTRL1.WL must not be changed while transmitting or receiving, and that clearing USART_CTRL1.TXEN during a transfer stops the baud rate counter and corrupts the data being transmitted. User manual
Switching between synchronous, single-wire, IrDA, LIN and smart card modes The user manual requires the other mode enable bits to be cleared when any dedicated mode is enabled, for example enabling synchronous mode (USART_CTRL2.CLKEN) requires clearing LINMEN, SCMEN, HDMEN and IRDAMEN;
the bits to clear for each mode are given in the corresponding manual section.
User manual
Clearing status and error flags The user manual specifies that RXDNE, OREF, PEF, NEF, FEF and IDLEF are cleared by reading USART_STS and then reading USART_DAT, and TXC by reading USART_STS and then writing USART_DAT;
follow this order.
User manual
Wrong port or clock for low-power wakeup The user manual states that only USART1 and USART4 support low-power wakeup and that the clock source must be LSI or LSE;
with another port or an APB clock the function does not hold.
User manual
Frame format in smart card mode The user manual states that in smart card mode the data length must be 8 bits with parity enabled, that the stop bits can only be 1.5 when transmitting and that both directions should use 1.5 stop bits to avoid switching between the two stop bit settings;
the CK clock division ranges from fCK/2 to fCK/62.
User manual
Mute-mode write ignored in multiprocessor mode The user manual states that USART_CTRL1.RCVWU can be written only while USART_STS.RXDNE=0 (the receive data register is empty) and that the write is otherwise ignored;
read USART_DAT first before entering mute.
User manual

The N32G412 and N32G415 series errata sheet V1.0.0 (2026.6.30) lists a single row for revision A chips with an empty errata link column, and gives no USART-related entries.

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.