5.19 UART Guide

This chapter describes the file layout and the 30 public functions of the UART driver in the N32G033 standard peripheral library, together with 13 examples: 485_DMA, DMA_Interrupt, DMA_Polling, HalfDuplex, Interrupt, the LP_Transmit_Receive and Transmit_Receive of IrDA_Tx&Rx, the LIN_Master and LIN_Slave of LIN, MultiProcessor, Polling, Printf, and WakeUpFromStop. Read multi-processor communication, single-wire half-duplex, IrDA, LIN mode, and UART3 low-power wakeup together with the UART chapter of the user manual; for known chip issues, refer to the errata sheet.

5.19.1 Related files

Relative path Purpose
firmware/n32g033_std_periph_driver/inc/n32g033_uart.h Defines the UART_InitType structure and the macros for word length, stop bits, parity, working mode, oversampling, interrupts, DMA, wakeup, LIN, IrDA, RS-485 driver enable, and flags, and declares 30 public functions in six sections: reset and initialization, address and wakeup, LIN and data transfer, prescaler and half-duplex and IrDA, flags and interrupts, and driver enable and receiver timeout
firmware/n32g033_std_periph_driver/src/n32g033_uart.c Implements the 30 functions: UART reset and initialization, address and mute-mode wakeup, LIN and IrDA configuration, single-wire half-duplex, data transfer and break frame, flag and interrupt handling, and RS-485 driver enable and receiver timeout
projects/n32g033_EVAL/examples/UART/ Root directory of the UART examples, containing 485_DMA, DMA_Interrupt, DMA_Polling, HalfDuplex, Interrupt, IrDA_Tx&Rx (LP_Transmit_Receive, Transmit_Receive), LIN (LIN_Master, LIN_Slave), MultiProcessor, Polling, Printf, and WakeUpFromStop, 13 examples in total
projects/n32g033_EVAL/examples/UART/Interrupt/ Layout of one example project directory: MDK-ARM/, EWARM/, inc/, src/, readme.txt
projects/n32g033_EVAL/bsp/ Shared board support files for the examples: the SysTick delay in delay.h and the serial log interface in log.h

5.19.2 API reference

Search API30 functions

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

Reset and initialization

UART_DeInit, UART_Init, UART_StructInit, UART_Enable, UART_ConfigInt, UART_EnableDMA

Address and wakeup

UART_SetAddr, UART_ConfigWakeUpMode, UART_EnableRcvWakeUp

LIN and data transfer

UART_ConfigLINBreakDetectLength, UART_EnableLIN, UART_SendData, UART_ReceiveData, UART_SendBreak

Prescaler, half-duplex, and IrDA

UART_SetPrescaler, UART_EnableHalfDuplex, UART_ConfigIrDAMode, UART_EnableIrDA

Flags and interrupts

UART_GetFlagStatus, UART_ClrFlag, UART_ClrRTOFlag, UART_GetIntStatus

Driver enable and receiver timeout

UART_PinSwapSet, UART_CfgDriverAssertTime, UART_CfgDriverdeassertTime, UART_DriverPolaritySet, UART_DriverModeSet, UART_RTOSet, UART_CfgRTOWidth, UART_CfgWakeupMode

void UART_DeInit(UART_Module* UARTx)Deinitializes the UARTx peripheral registers to their default reset values.
Source comment
name    UART_DeInit
fun     Deinitializes the UARTx peripheral registers to their default reset values.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
return  none
void UART_Init(UART_Module* UARTx, UART_InitType* UART_InitStruct)Initializes the UARTx peripheral according to UART_InitStruct.
Source comment
name    UART_Init
fun     Initializes the UARTx peripheral according to UART_InitStruct.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_InitStruct (The input parameters must be the following values):
          - buad_rate :
            - (((buad_rate) > 0) && ((buad_rate) < 0x003D0900))
          - WordLength
            - UART_WL_8B
            - UART_WL_9B
          - StopBits
            - UART_STPB_1
            - UART_STPB_0_5
            - UART_STPB_2
            - UART_STPB_1_5
          - Parity
            - UART_PE_NO
            - UART_PE_EVEN
            - UART_PE_ODD
          - Mode
            - UART_MODE_RX
            - UART_MODE_TX
          - OverSampling
            - UART_8OVER
            - UART_16OVER
return  none
void UART_StructInit(UART_InitType* UART_InitStruct)Fills each UART_InitStruct member with its default value.
Source comment
name    UART_StructInit.
fun     Fills each UART_InitStruct member with its default value.
param   UART_InitStruct (The input parameters must be the following values):
          - BaudRate
          - WordLength
          - StopBits
          - Parity
          - Mode
          - OverSampling
return  none
void UART_Enable(UART_Module* UARTx, FunctionalState Cmd)Enables or disables the specified UART peripheral.
Source comment
name    UART_Enable
fun     Enables or disables the specified UART peripheral.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_ConfigInt(UART_Module* UARTx, uint32_t UART_INT, FunctionalState Cmd)Enables or disables the specified UART interrupts.
Source comment
name    UART_ConfigInt
fun     Enables or disables the specified UART interrupts.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_INT (The input parameters must be the following values):
          - UART_INT_PEF   Parity Error interrupt
          - UART_INT_TXC   Transmission complete interrupt
          - UART_INT_TXDE  Transmit Data Register empty interrupt
          - UART_INT_RXDNE Receive Data register not empty interrupt
          - UART_INT_IDLEF Idle line detection interrupt
          - UART_INT_RTOE  Receiver timeout interrupt
          - UART_INT_LINBD LIN Break detection interrupt
          - UART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_EnableDMA(UART_Module* UARTx, uint32_t UART_DMAReq, FunctionalState Cmd)Enables or disables the UART’s DMA interface.
Source comment
name    UART_EnableDMA
fun     Enables or disables the UART's DMA interface.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_DMAReq (The input parameters must be the following values):
          - UART_DMAREQ_TX UART DMA transmit request
          - UART_DMAREQ_RX UART DMA receive request
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_SetAddr(UART_Module* UARTx, uint8_t UART_Addr)Sets the address of the UART node.
Source comment
name    UART_SetAddr
fun     Sets the address of the UART node.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_Addr (The input parameters must be the following values):
          - (((UART_Addr)>=0x00)&&((UART_Addr)<0x10))
return  none
void UART_ConfigWakeUpMode(UART_Module* UARTx, uint32_t UART_WakeUpMode)Selects the UART WakeUp method.
Source comment
name    UART_ConfigWakeUpMode
fun     Selects the UART WakeUp method.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_WakeUpMode (The input parameters must be the following values):
          - UART_WUM_IDLELINE WakeUp by an idle line detection
          - UART_WUM_ADDRMASK WakeUp by an address mark
return  none
void UART_EnableRcvWakeUp(UART_Module* UARTx, FunctionalState Cmd)Determines if the UART is in mute mode or not.
Source comment
name    UART_EnableRcvWakeUp
fun     Determines if the UART is in mute mode or not.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_ConfigLINBreakDetectLength(UART_Module* UARTx, uint32_t UART_LINBreakDetectLength)Sets the UART LIN Break detection length.
Source comment
name    UART_ConfigLINBreakDetectLength
fun     Sets the UART LIN Break detection length.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_LINBreakDetectLength (The input parameters must be the following values):
          - UART_LINBDL_10B 10-bit break detection
          - UART_LINBDL_11B 11-bit break detection
return  none
void UART_EnableLIN(UART_Module* UARTx, FunctionalState Cmd)Enables or disables the UART’s LIN mode.
Source comment
name    UART_EnableLIN
fun     Enables or disables the UART's LIN mode.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_SendData(UART_Module* UARTx, uint32_t Data)Transmits single data through the UARTx peripheral.
Source comment
name    UART_SendData
fun     Transmits single data through the UARTx peripheral.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Data :
          - the data to transmit
return  none
uint32_t UART_ReceiveData(UART_Module* UARTx)Returns the most recent received data by the UARTx peripheral.
Source comment
name    UART_ReceiveData
fun     Returns the most recent received data by the UARTx peripheral.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
return  none
void UART_SendBreak(UART_Module* UARTx)Transmits break characters.
Source comment
name    UART_SendBreak
fun     Transmits break characters.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
return  none
void UART_SetPrescaler(UART_Module* UARTx, uint8_t UART_Prescaler)Sets the system clock prescaler.
Source comment
name    UART_SetPrescaler
fun     Sets the system clock prescaler.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_Prescaler:
          - specifies the prescaler clock
return  none
void UART_EnableHalfDuplex(UART_Module* UARTx, FunctionalState Cmd)Enables or disables the UART’s Half Duplex communication.
Source comment
name    UART_EnableHalfDuplex
fun     Enables or disables the UART's Half Duplex communication.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void UART_ConfigIrDAMode(UART_Module* UARTx, uint32_t UART_IrDAMode)Configures the UART’s IrDA interface.
Source comment
name    UART_ConfigIrDAMode
fun     Configures the UART's IrDA interface.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_IrDAMode (The input parameters must be the following values):
          - UART_IRDAMODE_LOWPPWER
          - UART_IRDAMODE_NORMAL
return  none
void UART_EnableIrDA(UART_Module* UARTx, FunctionalState Cmd)Enables or disables the UART’s IrDA interface.
Source comment
name    UART_EnableIrDA
fun     Enables or disables the UART's IrDA interface.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
FlagStatus UART_GetFlagStatus(UART_Module* UARTx, uint32_t UART_FLAG)Checks whether the specified UART flag is set or not.
Source comment
name    UART_GetFlagStatus
fun     Checks whether the specified UART flag is set or not.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_FLAG (The input   parameters must be the following values):
          - UART_FLAG_RTO      recevier timeout Flag
          - UART_FLAG_FEF      Framing error Flag
          - UART_FLAG_NEF      Noise error flag Flag
          - UART_FLAG_OREF     Overrun error
          - UART_FLAG_PEF      Parity error
          - UART_FLAG_LINBD    LIN break detection flag
          - UART_FLAG_RXDNE    Receive data register not empty flag
          - UART_FLAG_TXC      Transmission Complete flag
          - UART_FLAG_TXDE     Transmit data register empty flag
          - UART_FLAG_IDLEF    Idle Line detection flag
return  UART_FLAG
          - SET
          - RESET
void UART_ClrFlag(UART_Module* UARTx, uint32_t UART_FLAG)Clears the UARTx’s pending flags.
Source comment
name    UART_ClrFlag
fun     Clears the UARTx's pending flags.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_FLAG (The input   parameters must be the following values):
          - UART_FLAG_LINBD    LIN break detection flag
          - UART_FLAG_RXDNE    Receive data register not empty flag
          - UART_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 UART_STS register (UART_GetFlagStatus())
            followed by a read operation to UART_DAT register (UART_ReceiveData()).
          - RXNE flag can be also cleared by a read to the UART_DAT register
            (UART_ReceiveData()).
          - TC flag can be also cleared by software sequence: a read operation to
            UART_STS register (UART_GetFlagStatus()) followed by a write operation
            to UART_DAT register (UART_SendData()).
          - TXE flag is cleared only by a write to the UART_DAT register
            (UART_SendData()).
          - RTO flag is cleared only by a write 1 to the UART_CTRL2.RTOCF bit.
            (UART_ClrRTOFlag()).
void UART_ClrRTOFlag(UART_Module* UARTx)Clears the UARTx’s RTO pending flag.
Source comment
name    UART_ClrRTOFlag
fun     Clears the UARTx's RTO pending flag.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
return  none
INTStatus UART_GetIntStatus(UART_Module* UARTx, uint32_t UART_INT)Checks whether the specified UART interrupt has set or not.
Source comment
name    UART_GetIntStatus
fun     Checks whether the specified UART interrupt has set or not.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param   UART_INT (The input parameters must be the following values):
          - UART_INT_PEF   Parity Error interrupt
          - UART_INT_TXC   Transmission complete interrupt
          - UART_INT_TXDE  Transmit Data Register empty interrupt
          - UART_INT_RXDNE Receive Data register not empty interrupt
          - UART_INT_IDLEF Idle line detection interrupt
          - UART_INT_RTOE  Receiver timeout interrupt
          - UART_INT_LINBD LIN Break detection interrupt
          - UART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
return  bitstatus
          - SET
          - RESET
void UART_PinSwapSet(UART_Module* UARTx,FunctionalState Cmd)Swap TX/RX pins enable or disable.
Source comment
name    UART_PinSwapSet
fun     Swap TX/RX pins enable or disable.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void UART_CfgDriverAssertTime(UART_Module* UARTx,uint32_t Time)Set Driver Enable assertion time.
Source comment
name    UART_CfgDriverAssertTime
fun     Set Driver Enable assertion time.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void UART_CfgDriverdeassertTime(UART_Module* UARTx,uint32_t Time)Set Driver Enable deassertion time.
Source comment
name    UART_CfgDriverdeassertTime
fun     Set Driver Enable deassertion time.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void UART_DriverPolaritySet(UART_Module* UARTx,FunctionalState Cmd)Driver enable polarity selection.
Source comment
name    UART_DriverPolaritySet
fun     Driver enable polarity selection.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void UART_DriverModeSet(UART_Module* UARTx,FunctionalState Cmd)Driver enable mode selection.
Source comment
name    UART_DriverModeSet
fun     Driver enable mode selection.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void UART_RTOSet(UART_Module* UARTx,FunctionalState Cmd)Receiver timeout enable or disable.
Source comment
name    UART_RTOSet
fun     Receiver timeout enable or disable.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void UART_CfgRTOWidth(UART_Module* UARTx,uint32_t Width)configure receive timeout width.
Source comment
name    UART_CfgRTOWidth
fun     configure receive timeout width.
param   UARTx (The input parameters must be the following values):
          - UART1
          - UART2
          - UART3
param  Width:(The input parameters must be the following values)
          - 0x00000000 ~ 0x0FFFFFFF
return  none
void UART_CfgWakeupMode(UART_Module* UARTx,uint32_t WakeupEn,uint32_t WakeupMode)configure wakeup mode.
Source comment
name    UART_CfgWakeupMode
fun     configure wakeup mode.
param   UARTx (The input parameters must be the following values):
          - UART3
param  WakeupEn:(The input parameters must be the following values)
          - UART_WAKEUP_DATACLR
          - UART_WAKEUP_DATANCLR
param  WakeupMode:(The input parameters must be the following values)
          - UART_WAKEUP_ENABLE
          - UART_WAKEUP_DISABLE
return  none

5.19.3 Demo descriptions

The UART examples cover basic transfer, DMA transfer, interrupt-driven transfer, single-wire half-duplex, RS-485, IrDA, LIN, multi-processor communication, and UART3 low-power wakeup: the basic examples make UART1 and UART2 exchange data and compare it, the DMA and interrupt examples move that data over channels or interrupt service routines, and the IrDA, LIN, multi-processor, and wakeup examples demonstrate the corresponding protocol modes.

5.19.3.1 485_DMA RS-485 driver-enable and DMA transfer

Example directory: projects/n32g033_EVAL/examples/UART/485_DMA/

Enable the GPIO, UART, and DMA clocks→Configure the data pins and the RS-485 driver-enable pins→Build and remap the transmit and receive DMA channels→Enable the RS-485 driver enable and the UART DMA requests→Wait for both channels to finish→Compare the sent and received data

Item Source analysis
Function UART1 sends TxBuffer1 to UART2 through an RS-485 transceiver over DMA, and UART2 moves the received data into RxBuffer2 over DMA;
after both transfers finish the sent and received data are compared and the result is stored in TransferStatus2.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, and Buffercmp;
inc/main.h defines the UARTy/UARTz instances and the pin, DMA channel, and request-remap macros;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3, UART2 Tx PA1, Rx PA0, all alternate-function push-pull, and DE on PA4 and PA8 alternate-function push-pull;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
driver enable: assert and deassert times set to 0, polarity DISABLE, mode ENABLE;
DMA: UARTy transmit on channel 1, UARTz receive on channel 2.
Program and data path main -> RCC_Configuration enables the DMA, GPIO, and UART clocks -> GPIO_Configuration sets up Tx/Rx/DE -> DMA_Configuration builds and remaps the transmit and receive channels -> after UART_Init the RS-485 driver enable is configured -> UART_EnableDMA opens the TX/RX requests -> DMA_EnableChannel starts the channels -> the DMA completion flags are polled -> Buffercmp compares.
Main APIs UART_CfgDriverAssertTime, UART_CfgDriverdeassertTime, UART_DriverPolaritySet, UART_DriverModeSet, UART_EnableDMA, UART_Init, UART_StructInit, UART_Enable
Verification Connect the UART1/UART2 and the RS-485 transceivers of the two boards as described in readme;
at full speed watch TransferStatus2 in the debugger;
PASSED means success and FAILED means failure.
Use note The DE pin is driven automatically by the UART hardware according to the transmit timing, so no software toggling is needed;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example uses the RS-485 driver-enable pin together with DMA, giving the single-direction transfer between UART1 and UART2 one DMA channel each.

Data path: DMA_Configuration builds the UART1 transmit and UART2 receive channels -> UART_EnableDMA opens the TX/RX requests -> DMA_EnableChannel starts the channels -> the hardware moves TxBuffer1/RxBuffer2 -> the DMA completion flags are polled -> Buffercmp compares

1. Initialize the transfer parameters and configure the RS-485 driver enable. The assert/deassert times and the polarity determine when the DE pin flips, so the RS-485 transceiver switches direction accordingly.

src/main.csrc/main.c · main
    /* Configure UARTy and UARTz */
    UART_Init(UARTy, &UART_InitStructure);
    UART_Init(UARTz, &UART_InitStructure);
    
    /* Configure UARTy 485 funtion */
    UART_CfgDriverAssertTime(UARTy,0);
    UART_CfgDriverdeassertTime(UARTy,0);
    UART_DriverPolaritySet(UARTy,DISABLE);
    UART_DriverModeSet(UARTy,ENABLE);
    /* Configure UARTz 485 funtion */
    UART_CfgDriverAssertTime(UARTz,0);
    UART_CfgDriverdeassertTime(UARTz,0);
    UART_DriverPolaritySet(UARTz,DISABLE);
    UART_DriverModeSet(UARTz,ENABLE);

When the transceiver or the switching instant changes, adjust the assert/deassert times and the polarity according to its datasheet.

2. Besides the data pins, the DE pins must be configured as alternate function. The driver-enable signal is generated inside the UART peripheral, so it reaches the pin only when that pin is set to alternate-function output.

src/main.csrc/main.c · GPIO_Configuration
    /* Configure UARTy DE as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = UARTy_DEPin;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = UARTy_DE_GPIO_AF;
    GPIO_InitPeripheral(UARTy_GPIO, &GPIO_InitStructure); 
    
    /* Configure UARTz DE as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = UARTz_DEPin;
    GPIO_InitStructure.GPIO_Alternate = UARTz_DE_GPIO_AF;
    GPIO_InitPeripheral(UARTz_GPIO, &GPIO_InitStructure); 

The DE pin number and alternate-function index change with the pin combination of the selected UART instance; when changing pins, update all three macros in the header together.

3. Build one DMA channel each for transmit and receive and remap the requests. The peripheral address is the UART data-register base, with a memory-to-peripheral direction for transmit and a peripheral-to-memory direction for receive.

src/main.csrc/main.c · DMA_Configuration
    /* UARTy_Tx_DMA_Channel (triggered by UARTy Tx event) Config */
    DMA_DeInit(UARTy_Tx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = UARTy_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(UARTy_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(UARTy_Tx_DMA_REMAP,DMA,UARTy_Tx_DMA_Channel,ENABLE);

The buffer length and channel number live in the header macros; check the channel and remap macros together when switching UART instances.

4. Open the DMA requests and channels, then poll the completion flags. Enabling the UART-side DMA request and enabling the DMA channel are two separate steps, and both flags must be waited for.

src/main.csrc/main.c · main
    /* Wait until UARTy TX DMA Channel  Transfer Complete */
    while (DMA_GetFlagStatus(UARTy_Tx_DMA_FLAG,DMA) == RESET)
    {
    }
    
    /* Wait until UARTz RX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(UARTz_Rx_DMA_FLAG,DMA) == RESET)
    {
    }

Polling occupies the CPU while it blocks, so a real project can switch to a DMA transfer-complete interrupt instead.

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

5.19.3.2 DMA_Interrupt DMA transmit and interrupt receive

Example directory: projects/n32g033_EVAL/examples/UART/DMA_Interrupt/

Enable the clocks and interrupts→Configure the data pins→Initialize UART and build two transmit DMA channels→Open the transmit DMA requests and the UART2 receive interrupt→Start the channels and enable UART→Poll receive on the UART1 side and receive by interrupt on the UART2 side→Wait for both channels to finish and compare

Item Source analysis
Function UART1 sends TxBuffer1 to UART2 over DMA, and UART2 sends TxBuffer2 to UART1 over DMA;
the UART2 side receives by RXDNE interrupt while the UART1 side receives by polling in the main loop, and the two sets of data are compared at the end.
Source files src/main.c contains main, RCC_Configuration, NVIC_Configuration, GPIO_Configuration, DMA_Configuration, and Buffercmp;
src/n32g033_it.c contains UARTz_IRQHandler;
inc/main.h provides the instance and DMA macros;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3, UART2 Tx PA1, Rx PA0 alternate-function push-pull;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
DMA: UART1 transmit on channel 1, UART2 transmit on channel 3;
interrupts: the UART2 RXDNE interrupt has priority 0.
Program and data path main -> RCC_Configuration enables the DMA/GPIO/UART clocks -> NVIC_Configuration enables the UART2 interrupt -> GPIO_Configuration -> DMA_Configuration builds two transmit channels -> UART_Init -> UART_EnableDMA opens both TX requests and UART_ConfigInt opens the UART2 RXDNE -> DMA_EnableChannel -> the main loop polls UART1 RXDNE to receive TxBuffer2 -> the interrupt receives TxBuffer1 into RxBuffer2 -> both DMA completions are polled -> Buffercmp.
Main APIs UART_EnableDMA, UART_ConfigInt, UART_GetFlagStatus, UART_ReceiveData, UART_Init, UART_StructInit, UART_Enable
Verification Cross-connect UART1 and UART2 on one board;
at full speed check TransferStatus1 and then TransferStatus2 in the debugger;
PASSED means success.
Use note When transmit uses DMA and receive uses interrupts, DMA only moves the data out while the interrupt maintains the receive count;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example hands both transmit directions to DMA and uses an interrupt on one receive side and polling on the other, so the two receive styles can be compared.

Data path: DMA_Configuration builds two transmit channels -> UART_EnableDMA opens both TX requests -> UART_ConfigInt opens the UART2 receive interrupt -> DMA_EnableChannel starts them -> the main loop polls UARTy to receive TxBuffer2 while UARTz_IRQHandler receives TxBuffer1 -> both channel completions are polled -> Buffercmp compares

1. Build one DMA channel for each transmit direction. The two configurations differ only in the peripheral address, the memory address, and the buffer length, reusing the remaining attributes.

src/main.csrc/main.c · DMA_Configuration
    /* UARTz_Tx_DMA_Channel (triggered by UARTz Tx event) Config */
    DMA_DeInit(UARTz_Tx_DMA_Channel);
    DMA_InitStructure.PeriphAddr = UARTz_DAT_Base;
    DMA_InitStructure.MemAddr    = (uint32_t)TxBuffer2;
    DMA_InitStructure.Direction  = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize    = TxBufferSize2;
    DMA_Init(UARTz_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(UARTz_Tx_DMA_REMAP,DMA,UARTz_Tx_DMA_Channel,ENABLE);

The second channel reuses the structure settings of the first, so do not miss the already-assigned members such as the peripheral data width and the priority when porting.

2. Open the transmit DMA requests and enable the receive interrupt separately. The DMA request links the peripheral to the channel, while the interrupt concerns only the UART2 receive.

src/main.csrc/main.c · main
    /* Enable UARTy DMA TX request */
    UART_EnableDMA(UARTy, UART_DMAREQ_TX,ENABLE);
    /* Enable UARTz DMA TX request */
    UART_EnableDMA(UARTz, UART_DMAREQ_TX,ENABLE);

    /* Enable the UARTz Receive Interrupt */
    UART_ConfigInt(UARTz, UART_INT_RXDNE,ENABLE);

    /* Enable UARTy TX DMA Channel */
    DMA_EnableChannel(UARTy_Tx_DMA_Channel,ENABLE);
    /* Enable UARTz TX DMA Channel */
    DMA_EnableChannel(UARTz_Tx_DMA_Channel,ENABLE);

The request, the channel, and the interrupt are opened in pairs; missing either one stops that direction from flowing.

3. Poll UART1 receive in the main loop until it is full. Here TxBuffer2, sent by UART2 over DMA, is received byte by byte on the RXDNE flag.

src/main.csrc/main.c · main
    /* Receive the TxBuffer2 */
    while (indexFlag < TxBufferSize2)
    {
        while (UART_GetFlagStatus(UARTy, UART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(UART_GetFlagStatus(UARTy, UART_FLAG_OREF) != RESET)
            {
                UARTy->DAT;
            }
        }
        RxBuffer1[indexFlag++] = UART_ReceiveData(UARTy);
    }

Reading the data register inside the polling loop clears the overrun flag, so no data loss can deadlock the loop.

4. The interrupt service receives a whole frame and then disables itself. The UART2 receive is completed inside the interrupt, which turns the receive interrupt off once the expected number of bytes has arrived.

src/n32g033_it.csrc/n32g033_it.c · UARTz_IRQHandler
void UARTz_IRQHandler(void)
{
    if ((UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) != RESET) && (UART_GetIntStatus(UARTz, UART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer2[RxCounter++] = UART_ReceiveData(UARTz);

        if (RxCounter == NbrOfDataToRead)
        {
            /* Disable the UARTz Receive interrupt */
            UART_ConfigInt(UARTz, UART_INT_RXDNE, DISABLE);
        }
    }

Checking the flag before the interrupt-enable bit avoids entering the service routine needlessly; disabling the interrupt when full reduces later entries.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.3 DMA_Polling DMA transmit and DMA receive

Example directory: projects/n32g033_EVAL/examples/UART/DMA_Polling/

Enable the GPIO, UART, and DMA clocks→Configure the data pins→Build and remap the transmit and receive DMA channels→Open the UART DMA requests→Start the channels and enable UART→Wait for both channels to finish→Compare the sent and received data

Item Source analysis
Function UART1 sends TxBuffer1 to UART2 over DMA, and UART2 moves the received data into RxBuffer2 over DMA;
after both transfers finish the data is compared and the result is stored in TransferStatus2.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, and Buffercmp;
inc/main.h defines the instances and the DMA channel and remap macros;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3, UART2 Tx PA1, Rx PA0 alternate-function push-pull;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
DMA: UART1 transmit on channel 1, UART2 receive on channel 2.
Program and data path main -> RCC_Configuration enables the DMA, GPIO, and UART clocks -> GPIO_Configuration -> DMA_Configuration builds the transmit and receive channels -> UART_Init -> UART_EnableDMA opens the TX/RX requests -> DMA_EnableChannel -> the DMA completion flags are polled -> Buffercmp compares.
Main APIs UART_EnableDMA, UART_Init, UART_StructInit, UART_Enable
Verification Cross-connect UART1 and UART2 on one board;
at full speed watch TransferStatus2 in the debugger;
PASSED means success.
Use note This shares its origin with 485_DMA, only without the RS-485 driver enable, and checks a plain DMA transfer path;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example is the minimal DMA transfer path, with one channel each for transmit and receive, while the main program only waits for the completion flags.

Data path: DMA_Configuration builds the UART1 transmit and UART2 receive channels -> UART_EnableDMA opens the TX/RX requests -> DMA_EnableChannel starts them -> the hardware moves TxBuffer1/RxBuffer2 -> the DMA completion flags are polled -> Buffercmp compares

1. Initialize the transfer parameters and open both DMA requests. The instances and modes used for the transfer are fixed here, and the requests attach the two instances to their respective channels.

src/main.csrc/main.c · main
    /* Configure UARTy and UARTz */
    UART_Init(UARTy, &UART_InitStructure);
    UART_Init(UARTz, &UART_InitStructure);

    /* Enable UARTy DMA TX request */
    UART_EnableDMA(UARTy, UART_DMAREQ_TX,ENABLE);
    /* Enable UARTz DMA Rx request */
    UART_EnableDMA(UARTz, UART_DMAREQ_RX,ENABLE);

The request direction must match the data flow: the transmitting instance opens the TX request and the receiving instance opens the RX request.

2. The receive channel moves peripheral data into a memory buffer. The receive direction is peripheral to memory, with the memory address pointing at the receive buffer.

src/main.csrc/main.c · DMA_Configuration
    /* UARTz RX DMA1 Channel (triggered by UARTz Rx event) Config */
    DMA_DeInit(UARTz_Rx_DMA_Channel);
    DMA_InitStructure.PeriphAddr = UARTz_DAT_Base;
    DMA_InitStructure.MemAddr    = (uint32_t)RxBuffer2;
    DMA_InitStructure.Direction  = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize    = TxBufferSize1;
    DMA_Init(UARTz_Rx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(UARTz_Rx_DMA_REMAP,DMA,UARTz_Rx_DMA_Channel,ENABLE);

The receive buffer must be no smaller than the number of transmitted bytes, or it will overflow and overwrite.

3. Wait for both completion flags before comparing. The data is only fully moved once both the transmit and receive flags are set, which is when the comparison is meaningful.

src/main.csrc/main.c · main
    /* Wait until UARTy TX DMA Channel  Transfer Complete */
    while (DMA_GetFlagStatus(UARTy_Tx_DMA_FLAG,DMA) == RESET)
    {
    }
    
    /* Wait until UARTz RX DMA Channel Transfer Complete */
    while (DMA_GetFlagStatus(UARTz_Rx_DMA_FLAG,DMA) == RESET)
    {
    }

Blocking polling suits a one-shot transfer; for continuous transfer, use a channel interrupt or the circular mode instead.

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

5.19.3.4 HalfDuplex Single-wire half-duplex transfer

Example directory: projects/n32g033_EVAL/examples/UART/HalfDuplex/

Enable the GPIO and UART clocks→Configure one transmit pin for each of the two instances→Initialize UART and enable single-wire half-duplex→UART1 sends, UART2 receives→UART2 sends, UART1 receives→Compare the two sets of transferred data

Item Source analysis
Function UART1 and UART2 each use a single Tx pin and enter single-wire half-duplex mode, alternating directions: UART1 sends TxBuffer1 to UART2, then UART2 sends TxBuffer2 to UART1, and the two sets of data are compared at the end.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, and Buffercmp;
inc/main.h defines the transmit pins and alternate-function macros of the two instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: only UART1 Tx PA2 and UART2 Tx PA1 are configured, both alternate-function push-pull, with no Rx;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
mode: half-duplex is enabled after the receive and transmit modes are set, and the matching Tx pins of the two boards are wired together.
Program and data path main -> RCC_Configuration enables the GPIO and UART clocks -> GPIO_Configuration configures the two transmit pins -> UART_Init sets up receive and transmit -> UART_Enable enables them -> UART_EnableHalfDuplex turns on half-duplex -> both directions poll TXDE to send and RXDNE to receive -> Buffercmp compares.
Main APIs UART_EnableHalfDuplex, UART_SendData, UART_ReceiveData, UART_GetFlagStatus, UART_Init, UART_StructInit, UART_Enable
Verification Connect the UART1 Tx (PA2) of one board to the UART2 Tx (PA1) of the other with a single wire;
at full speed check TransferStatus1 and TransferStatus2 in the debugger;
PASSED means success.
Use note In half-duplex the receive and transmit share one wire, so the two sides must not transmit at the same time;
when not transmitting, the pin should float or be open-drain high so the bus is not held;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example configures two UARTs for single-wire half-duplex, sharing their transmit pins for receive and transmit, with the software ordering the two directions.

Data path: GPIO_Configuration configures only the transmit pins -> UART_Init sets up receive and transmit -> UART_Enable enables them -> UART_EnableHalfDuplex turns on half-duplex -> TXDE is polled to send byte by byte -> RXDNE is polled to receive byte by byte -> Buffercmp compares

1. Configure only the transmit pin. In single-wire half-duplex the receive and transmit share one wire, so only the transmit pin is set to alternate-function push-pull.

src/main.csrc/main.c · GPIO_Configuration
    /* Configure UARTy Tx as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = UARTy_TxPin;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitStructure.GPIO_Alternate = UARTy_Tx_GPIO_AF;
    GPIO_InitPeripheral(UARTy_GPIO, &GPIO_InitStructure);

    /* Configure UARTz Tx as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = UARTz_TxPin;
    GPIO_InitStructure.GPIO_Alternate = UARTz_Tx_GPIO_AF;
    GPIO_InitPeripheral(UARTz_GPIO, &GPIO_InitStructure);

The half-duplex pin both drives and receives, so make sure the alternate-function index matches on both sides when changing pins.

2. Enable UART before turning on half-duplex mode. Enabling half-duplex builds on a UART that is already enabled with the receive and transmit modes configured.

src/main.csrc/main.c · main
    /* Enable the UARTy and UARTz */
    UART_Enable(UARTy, ENABLE);
    UART_Enable(UARTz, ENABLE);

    /* Enable UARTy Half Duplex Mode*/
    UART_EnableHalfDuplex(UARTy, ENABLE);
    /* Enable UARTz Half Duplex Mode*/
    UART_EnableHalfDuplex(UARTz, ENABLE);

3. Poll the receive and transmit of the two directions separately. On the same wire one side sends while the other receives, and the roles are exchanged after the transfer.

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

        /* Wait the byte is entirely received by UARTz */
        while (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
            {
                UARTz->DAT;
            }
        }
        /* Store the received byte in the RxBuffer2 */
        RxBuffer2[RxCounter2++] = UART_ReceiveData(UARTz);
    }

Receive and transmit alternate within the same loop; on a real bus, add direction switching and bus-idle detection yourself.

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

5.19.3.5 Interrupt Interrupt-driven transfer

Example directory: projects/n32g033_EVAL/examples/UART/Interrupt/

Enable the clocks and interrupts→Configure the data pins→Initialize UART and open the receive and transmit interrupts→Set up the NVIC and enable UART→The transfer completes inside the interrupts→The main loop waits for the counters before comparing

Item Source analysis
Function UART1 and UART2 exchange data, both with the transmit and receive interrupts enabled;
every byte is sent and received inside the service routines, and the main loop only waits for the counters to fill before comparing the two sets of data.
Source files src/main.c contains main, RCC_Configuration, NVIC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32g033_it.c contains UARTy_IRQHandler and UARTz_IRQHandler;
inc/main.h provides the instance and interrupt macros;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3, UART2 Tx PA1, Rx PA0 alternate-function push-pull;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
interrupts: both instances enable RXDNE and TXDE, with NVIC priorities 0 and 1.
Program and data path main -> RCC_Configuration -> NVIC_Configuration enables the two interrupt channels -> GPIO_Configuration -> UART_Init -> UART_ConfigInt opens the receive and transmit interrupts -> UART_Enable -> the interrupts transfer the data -> the main loop waits for RxCounter to fill -> Buffercmp compares.
Main APIs UART_ConfigInt, UART_GetFlagStatus, UART_GetIntStatus, UART_SendData, UART_ReceiveData, UART_Init, UART_StructInit, UART_Enable
Verification Cross-connect UART1 and UART2 on one board;
at full speed check TransferStatus1 and TransferStatus2 in the debugger;
PASSED means success.
Use note The interrupt disables its own enable bit once the count is reached, so it is not entered continuously;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example moves the transfer entirely into the interrupts, leaving the main program to wait for the counters, which is useful for comparing the interrupt and polling styles.

Data path: NVIC_Configuration enables the interrupt channels -> UART_ConfigInt opens the receive and transmit interrupts -> UART_Enable enables them -> UARTy_IRQHandler/UARTz_IRQHandler transfer byte by byte -> the main loop waits for the counters -> Buffercmp compares

1. Open the receive and transmit interrupts. Receive and transmit each have their own enable bit, and both instances need configuring.

src/main.csrc/main.c · main
    /* Enable UARTy Receive and Transmit interrupts */
    UART_ConfigInt(UARTy, UART_INT_RXDNE, ENABLE);
    UART_ConfigInt(UARTy, UART_INT_TXDE, ENABLE);

    /* Enable UARTz Receive and Transmit interrupts */
    UART_ConfigInt(UARTz, UART_INT_RXDNE, ENABLE);
    UART_ConfigInt(UARTz, UART_INT_TXDE, ENABLE);

2. Configure the two interrupt channels and their priorities. The service routines are dispatched by the NVIC, with the channel numbers coming from the instance macros.

src/main.csrc/main.c · NVIC_Configuration
    /* Enable the UARTy Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel         = UARTy_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = NVIC_PRIORITY_0;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Enable the UARTz Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel         = UARTz_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = NVIC_PRIORITY_1;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

3. Send and receive by flag inside the interrupt and disable on completion. Check the status flag before the interrupt-enable bit, and turn off the matching interrupt once the count is reached.

src/n32g033_it.csrc/n32g033_it.c · UARTy_IRQHandler
void UARTy_IRQHandler(void)
{
    if ((UART_GetFlagStatus(UARTy, UART_FLAG_RXDNE) != RESET) && (UART_GetIntStatus(UARTy, UART_INT_RXDNE) != RESET))
    {
        /* Read one byte from the receive data register */
        RxBuffer1[RxCounter1++] = UART_ReceiveData(UARTy);

        if (RxCounter1 == NbrOfDataToRead1)
        {
            /* Disable the UARTy Receive interrupt */
            UART_ConfigInt(UARTy, UART_INT_RXDNE, DISABLE);
        }
    }

    if ((UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) != RESET) && (UART_GetIntStatus(UARTy, UART_INT_TXDE) != RESET))
    {
        /* Write one byte to the transmit data register */
        UART_SendData(UARTy, TxBuffer1[TxCounter1++]);

        if (TxCounter1 == NbrOfDataToTransfer1)
        {
            /* Disable the UARTy Transmit interrupt */
            UART_ConfigInt(UARTy, UART_INT_TXDE, DISABLE);
        }
    }

Handling and clearing the error flags inside the interrupt prevents repeated triggering after noise, overrun, or framing errors.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.6 IrDA_Tx&Rx LP_Transmit_Receive IrDA low-power transfer

Example directory: projects/n32g033_EVAL/examples/UART/IrDA_Tx&Rx/LP_Transmit_Receive/

Enable the clocks and lower the peripheral clock→Configure the data pins and the carrier output pin→Initialize UART and set up receive and transmit separately→Set the prescaler and turn on IrDA low-power mode→Poll the transmit buffer→Receive by interrupt→Compare the transferred data

Item Source analysis
Function UART1 sends TxBuffer1, which is modulated by IrDA and received by UART2 into RxBuffer1;
both sides work in IrDA low-power mode, and the transferred data is compared to produce TransferStatus.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
src/bsp_Tim_38K.c and inc/bsp_Tim_38K.h output the 38 kHz carrier;
src/n32g033_it.c holds the receive interrupt;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2 and UART2 Rx PA0 alternate-function push-pull;
baud rate 600, 8 data bits, 1 stop bit, no parity;
clock: PCLK divided by RCC_HCLK_DIV8;
prescaler 210, IrDA mode set to low-power;
carrier: TIM3 outputs 38 kHz on PB4.
Program and data path main -> RCC_Configuration enables the clocks and lowers PCLK -> NVIC_Configuration enables the UART2 interrupt -> GPIO_Configuration -> ADVANCE_TIM_Init outputs the carrier -> UART_Init sets up receive and transmit -> UART_SetPrescaler sets the prescaler -> UART_ConfigIrDAMode and UART_EnableIrDA turn on IrDA -> TXDE is polled to send -> the interrupt receives into RxBuffer1 -> Buffercmp compares.
Main APIs UART_SetPrescaler, UART_ConfigIrDAMode, UART_EnableIrDA, UART_ConfigInt, UART_SendData, UART_GetFlagStatus, UART_Init, UART_StructInit, UART_Enable
Verification Connect PA0 to the infrared receiver, PA2 to the infrared emitter, and PB4 to the 38 kHz carrier as described in readme;
at full speed watch TransferStatus in the debugger;
PASSED means success.
Use note Low-power mode requires the divided clock to fall between 1.42 MHz and 2.12 MHz, so PCLK is lowered before the prescaler is set;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example lowers the peripheral clock first and then sets the IrDA prescaler, so the encoding clock lands in the band required by low-power mode.

Data path: RCC_ConfigPclk lowers PCLK -> ADVANCE_TIM_Init outputs the 38 kHz carrier -> UART_Init sets up receive and transmit -> UART_SetPrescaler -> UART_ConfigIrDAMode selects low-power -> UART_EnableIrDA enables it -> polling sends and the interrupt receives -> Buffercmp compares

1. Lower PCLK before initializing the 38 kHz carrier. The encoding clock of low-power IrDA comes from PCLK through the prescaler, so PCLK is divided first.

src/main.csrc/main.c · main
    /* System Clocks Configuration */
    RCC_Configuration();

    RCC_ConfigPclk(RCC_HCLK_DIV8);
    /* NVIC configuration */
    NVIC_Configuration();

    /* Configure the GPIO ports */
    GPIO_Configuration();
    
    /* GPIO PB4 Output 38KHz Clock */
    ADVANCE_TIM_Init();

A lower system clock also gives a smaller error at a given baud rate; check the division factor together when the clock changes.

2. Set up receive and transmit separately and configure IrDA low-power mode. One instance only transmits and the other only receives, and both the prescaler and the mode must match.

src/main.csrc/main.c · main
    UART_InitStructure.Mode       = UART_MODE_TX;
    UART_Init(UARTy, &UART_InitStructure);
    UART_InitStructure.Mode       = UART_MODE_RX;
    UART_Init(UARTz, &UART_InitStructure);

    /* Enable UARTz Receive and Transmit interrupts */
    UART_ConfigInt(UARTz, UART_INT_RXDNE, ENABLE);
    
    /* Set the UARTy and UARTz prescaler */
    UART_SetPrescaler(UARTy, 210);
    UART_SetPrescaler(UARTz, 210);
    /* Configure the UARTy IrDA mode */
    UART_ConfigIrDAMode(UARTy, UART_IRDAMODE_LOWPPWER);
    UART_ConfigIrDAMode(UARTz, UART_IRDAMODE_LOWPPWER);
    
    /* Enable the UARTy and UARTz IrDA mode */
    UART_EnableIrDA(UARTy,ENABLE);
    UART_EnableIrDA(UARTz,ENABLE);

The prescaler value sets the pulse width, and both sides must take the same value to decode correctly.

3. Poll the transmit buffer while the interrupt handles receive. The transmitting side waits for TXDE on every byte, and the receiving side stops once the expected number of bytes has arrived.

src/main.csrc/main.c · main
    while (TxCounter < TxBufferSize1)
    {
        /* Send one byte from Board1_UARTy to Board2_UARTz */
        UART_SendData(UARTy, TxBuffer1[TxCounter++]);
        /* Loop until Board1_UARTy DAT register is empty */
        while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
        {
        }
    }

IrDA is a half-duplex interface, so leave enough spacing between transmit and receive; insert the direction-switch wait when porting.

Corresponding source files: src/main.c, src/bsp_Tim_38K.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.7 IrDA_Tx&Rx Transmit_Receive IrDA normal-mode transfer

Example directory: projects/n32g033_EVAL/examples/UART/IrDA_Tx&Rx/Transmit_Receive/

Enable the clocks→Configure the data pins and the carrier output pin→Initialize UART and set up receive and transmit separately→Set the prescaler and turn on IrDA normal mode→Poll the transmit buffer→Receive by interrupt→Compare the transferred data

Item Source analysis
Function UART1 sends TxBuffer1, which is modulated by IrDA and received by UART2 into RxBuffer1;
both sides work in IrDA normal mode, and the transferred data is compared to produce TransferStatus.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
src/bsp_Tim_38K.c and inc/bsp_Tim_38K.h output the 38 kHz carrier;
src/n32g033_it.c holds the receive interrupt;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2 and UART2 Rx PA0 alternate-function push-pull;
baud rate 1200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
prescaler 1, IrDA mode set to normal;
carrier: TIM3 outputs 38 kHz on PB4.
Program and data path main -> RCC_Configuration enables the clocks -> NVIC_Configuration enables the UART2 interrupt -> GPIO_Configuration -> ADVANCE_TIM_Init outputs the carrier -> UART_Init sets up receive and transmit -> UART_SetPrescaler takes 1 -> UART_ConfigIrDAMode and UART_EnableIrDA turn on IrDA -> TXDE is polled to send -> the interrupt receives into RxBuffer1 -> Buffercmp compares.
Main APIs UART_SetPrescaler, UART_ConfigIrDAMode, UART_EnableIrDA, UART_ConfigInt, UART_SendData, UART_GetFlagStatus, UART_Init, UART_StructInit, UART_Enable
Verification Connect PA0 to the infrared receiver, PA2 to the infrared emitter, and PB4 to the 38 kHz carrier as described in readme;
at full speed watch TransferStatus in the debugger;
PASSED means success.
Use note In normal mode the prescaler can only take 1 and the maximum baud rate is 115200;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example shares its origin with the low-power version, differing in the prescaler taken as 1 and the mode set to normal, so the encoding clock comes straight from the peripheral clock.

Data path: ADVANCE_TIM_Init outputs the 38 kHz carrier -> UART_Init sets up receive and transmit -> UART_SetPrescaler takes 1 -> UART_ConfigIrDAMode selects normal -> UART_EnableIrDA enables it -> polling sends and the interrupt receives -> Buffercmp compares

1. Output the 38 kHz carrier and initialize the transfer. The carrier is produced by TIM3 toggling the output pin, which drives the infrared emitter.

src/main.csrc/main.c · main
    /* Configure the GPIO ports */
    GPIO_Configuration();
    
    /* GPIO PC2 Output 38KHz Clock */
    ADVANCE_TIM_Init();

    /* UARTy and UARTz configuration */
    UART_StructInit(&UART_InitStructure);
    UART_InitStructure.BaudRate   = 1200;
    UART_InitStructure.WordLength = UART_WL_8B;
    UART_InitStructure.StopBits   = UART_STPB_1;
    UART_InitStructure.Parity     = UART_PE_NO;
    UART_InitStructure.OverSampling        = UART_16OVER;

2. Take the prescaler as 1 and configure IrDA normal mode. In normal mode PSCV can only be 1, and both sides must take the same value.

src/main.csrc/main.c · main
    /* Set the UARTy and UARTz prescaler */
    UART_SetPrescaler(UARTy, 0x01);
    UART_SetPrescaler(UARTz, 0x01);
    /* Configure the UARTy IrDA mode */
    UART_ConfigIrDAMode(UARTy, UART_IRDAMODE_NORMAL);
    UART_ConfigIrDAMode(UARTz, UART_IRDAMODE_NORMAL);
    
    /* Enable the UARTy and UARTz IrDA mode */
    UART_EnableIrDA(UARTy,ENABLE);
    UART_EnableIrDA(UARTz,ENABLE);

IrDA is mutually exclusive with LIN and half-duplex, so clear the related enable bits before switching modes.

3. Poll the transmit buffer and compare the two sets once full. The transmitting side waits for TXDE on every byte, the interrupt fills the receive side, and the data is compared once the count is full.

src/main.csrc/main.c · main
    while (TxCounter < TxBufferSize1)
    {
        /* Send one byte from Board1_UARTy to Board2_UARTz */
        UART_SendData(UARTy, TxBuffer1[TxCounter++]);
        /* Loop until Board1_UARTy DAT register is empty */
        while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
        {
        }
    }
    while (RxCounter < TxBufferSize1)
    {
    }

The idle loop waiting for the receive count should work with the interrupt completion so it does not exit before the frame is full.

Corresponding source files: src/main.c, src/bsp_Tim_38K.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.8 LIN_Master LIN master frame transmission

Example directory: projects/n32g033_EVAL/examples/UART/LIN/LIN_Master/

Enable the clocks and interrupts→Configure the UART2 data pins→Initialize and enable LIN break detection→The main loop sends the master request and slave response frames→Print the data returned by the slave

Item Source analysis
Function UART2 acts as a LIN master: it first sends a request frame with 8 data bytes (identifier 0x3C), then sends the response-frame header with identifier 0x3D, waits for the slave to return 8 data bytes, and prints them.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, Memset, and Buffercopy;
src/lin_master.c and inc/lin_master.h implement frame building and transfer;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART2 Tx PA1, Rx PA0 alternate-function push-pull (GPIO_AF4);
baud rate 9600 (readme states 115200, the example uses 9600), 8 data bits, 1 stop bit, no parity, 16x oversampling;
LIN: break detection length 10 bits;
UART1 is used for printing, Tx PA9, Rx PA10.
Program and data path main -> log_init sets up the print UART -> RCC_Configuration -> NVIC_Configuration -> GPIO_Configuration -> UART_Init -> UART_ConfigLINBreakDetectLength and UART_EnableLIN -> UART_Enable -> the main loop runs TestLinMaster -> FrameHandle sends the frame via MasterSendBytes -> WaitFrameRes receives the slave data.
Main APIs UART_ConfigLINBreakDetectLength, UART_EnableLIN, UART_SendBreak, UART_SendData, UART_GetFlagStatus, UART_ReceiveData, UART_Init, UART_StructInit, UART_Enable
Verification Flash the master and slave projects on two boards and cross-connect their UART2 data pins;
the master prints the slave data it receives, and prints slave no response!! after 4 consecutive timeouts.
Use note The break field is sent by UART_SendBreak, and the frame header sync byte is fixed at 0x55;
identifier 0x3C is the request frame and 0x3D the response frame;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example has the master initiate the break field and frame header in LIN mode, after which the slave returns data and the master only sends headers and waits for responses.

Data path: UART_Init sets up receive and transmit -> UART_ConfigLINBreakDetectLength and UART_EnableLIN turn on LIN -> UART_Enable -> TestLinMaster builds the frame -> FrameHandle checks the identifier -> MasterSendBytes sends the break field and data -> WaitFrameRes receives the slave response

1. Initialize UART and enable LIN break detection. A LIN frame starts with a break field, so the detection length is set before LIN mode is turned on.

src/main.csrc/main.c · main
    /* UARTx configuration */
    UART_StructInit(&UART_InitStructure);
    UART_InitStructure.BaudRate            = 9600;
    UART_InitStructure.WordLength          = UART_WL_8B;
    UART_InitStructure.StopBits            = UART_STPB_1;
    UART_InitStructure.Parity              = UART_PE_NO;
    UART_InitStructure.OverSampling        = UART_16OVER;
    UART_InitStructure.Mode                = UART_MODE_RX | UART_MODE_TX;
    /* Configure UARTx */
    UART_Init(UARTx, &UART_InitStructure);
    /* Enable the UARTx LIN mode*/
    UART_ConfigLINBreakDetectLength(UARTx,UART_LINBDL_10B);
    UART_EnableLIN(UARTx,ENABLE);
    /* Enable the UARTx */
    UART_Enable(UARTx,ENABLE);

LIN mode supports only 8-bit data frames and is mutually exclusive with IrDA and single-wire half-duplex.

2. Send the break field and then the data byte by byte. Each byte waits for the transmit-complete flag before the next is sent, with one more wait at the end of the frame.

src/lin_master.csrc/lin_master.c · MasterSendBytes
void MasterSendBytes(uint8_t *pBuf, uint8_t Len)
{
    UART_SendBreak(UARTx);
    while(Len--)
    {
        while(UART_GetFlagStatus(UARTx, UART_FLAG_TXC) == RESET);
        UART_SendData(UARTx, *pBuf++);
    }
    while(UART_GetFlagStatus(UARTx, UART_FLAG_TXC ) == RESET);
}

UART_FLAG_TXC marks a whole frame sent, so using it instead of TXDE guarantees the last byte has really gone out.

3. Build a request or response frame by the identifier. The master decides by the identifier whether to send a request frame or just a response-frame header.

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);
}

The identifier is extended with its parity bits before sending, so do not use the raw 6-bit identifier when porting.

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

5.19.3.9 LIN_Slave LIN slave response

Example directory: projects/n32g033_EVAL/examples/UART/LIN/LIN_Slave/

Enable the clocks and interrupts→Configure the UART2 data pins→Initialize and enable LIN and the receive interrupts→The main loop waits for the receive flag→Parse the frame and build the response data

Item Source analysis
Function UART2 acts as a LIN slave: inside the interrupt a state machine receives the master’s break field, sync byte, identifier, and data;
once a complete frame has arrived the main loop builds the response frame and sends it back.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, Memset, Buffercopy, and LIN_RespData;
src/lin_slave.c and inc/lin_slave.h implement the state-machine receive and the response;
src/n32g033_it.c forwards the UART2 interrupt to LIN_IRQHandler;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART2 Tx PA1, Rx PA0 alternate-function push-pull (GPIO_AF4);
baud rate 9600 (readme states 115200, the example uses 9600), 8 data bits, 1 stop bit, no parity, 16x oversampling;
LIN: break detection length 10 bits;
interrupts: both the receive-data interrupt and the LIN break-detection interrupt are enabled.
Program and data path main -> log_init -> RCC_Configuration -> NVIC_Configuration -> GPIO_Configuration -> UART_Init -> UART_ConfigLINBreakDetectLength and UART_EnableLIN -> UART_ConfigInt opens RXDNE and LINBD -> UART_Enable -> the slave identifier send/receive types are configured -> the interrupt LIN_IRQHandler drives LIN_EX_RxAsync to receive a frame -> the main loop, once the flag is set, calls BOOT_ExecutiveCommand and replies via LIN_SetResp.
Main APIs UART_ConfigLINBreakDetectLength, UART_EnableLIN, UART_ConfigInt, UART_GetFlagStatus, UART_ClrFlag, UART_ReceiveData, UART_SendData, UART_Init, UART_StructInit, UART_Enable
Verification Flash the master and slave projects on two boards and cross-connect their UART2 data pins;
the slave prints the command data it receives, and the response the master receives is all ones.
Use note The slave both receives the master request frame and returns the response frame, with the identifier send/receive type distinguished by the IDType table;
the state machine restarts after a break field, and the frame sync byte is fixed at 0x55;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example splits receive into an in-interrupt state machine and main-loop handling, decoupling receive from response for easy extension by the identifier table.

Data path: UART_Init sets up receive and transmit -> UART_EnableLIN and UART_ConfigLINBreakDetectLength -> UART_ConfigInt opens RXDNE and LINBD -> UART_Enable -> LIN_IRQHandler clears the flags and advances LIN_EX_RxAsync -> the main loop sets GotMsgFlag and builds the frame in LIN_SetResp -> LIN_SendBytes sends it back

1. Initialize and enable LIN with the two interrupts. The break-detection interrupt aligns the frame while the receive-data interrupt takes the bytes.

src/main.csrc/main.c · main
    /* Configure UARTx */
    UART_Init(UARTx, &UART_InitStructure);
    /* Enable the UARTx LIN mode*/
    UART_ConfigLINBreakDetectLength(UARTx,UART_LINBDL_10B);
    UART_EnableLIN(UARTx,ENABLE);
    
    /* Enable UARTx Receive and Transmit interrupts */
    UART_ConfigInt(UARTx, UART_INT_RXDNE,ENABLE);
    UART_ConfigInt(UARTx, UART_INT_LINBD,ENABLE);
    
    /* Enable the UARTx */
    UART_Enable(UARTx,ENABLE);

2. Clear the flags and hand the byte to the state machine in the interrupt. The state machine resets after a break field and advances through the frame structure on normal bytes.

src/lin_slave.csrc/lin_slave.c · LIN_IRQHandler
void LIN_IRQHandler(void)
{
    if(UART_GetFlagStatus(UARTx, UART_FLAG_LINBD) == SET)
    {
        UART_ClrFlag(UARTx, UART_FLAG_LINBD);//Clear the LIN interval field detection flag bit
        //Read the status register and data register and clear the FE flag
        UARTx->STS;
        UARTx->DAT;
        LinRxState = SYNCH;
        return;
    }
    
    if (UART_GetFlagStatus(UARTx, UART_FLAG_RXDNE)  == SET)
    {
        UART_ClrFlag(UARTx, UART_FLAG_RXDNE); //Clear the receive interrupt flag bit
        if(UART_GetFlagStatus(UARTx, UART_FLAG_FEF) == RESET)
        {
            LIN_EX_RxAsync((uint8_t)UART_ReceiveData(UARTx));
        }
    }
    
    /* Determine if an error flag still exists and clear the error flag */
    if ((UART_GetFlagStatus(UARTx, UART_FLAG_OREF) != RESET)  || \
        (UART_GetFlagStatus(UARTx, UART_FLAG_NEF) != RESET)  || \
        (UART_GetFlagStatus(UARTx, UART_FLAG_PEF) != RESET)  || \
        (UART_GetFlagStatus(UARTx, UART_FLAG_FEF) != RESET))
    {
        /*Read the sts register first,and the read the DAT register to clear the all error flag*/
        (void)UARTx->STS;
        (void)UARTx->DAT;
        /* Under normal circumstances, all error flags will be cleared when the upper data is read and will not be executed here; 
           users can add their own processing according to the actual scenario. */
    }
}

Reading the status register before the data register clears the overrun, noise, parity, and framing error flags together.

3. Build the response frame and write the checksum. The checksum is computed by the identifier, and is placed after the data when the length is under 8 bytes.

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;
}

The classic and enhanced checksums cover different ranges, chosen by the caller according to the identifier type.

Corresponding source files: src/main.c, src/lin_slave.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.10 MultiProcessor Multi-processor address wakeup

Example directory: projects/n32g033_EVAL/examples/UART/MultiProcessor/

Enable the clocks→Configure the data pins, key, and LED→Initialize a 9-bit frame→Set the address and address wakeup→Keep sending and toggle the LED on receive→The key interrupt switches mute mode and wakes up

Item Source analysis
Function UART1 repeatedly sends the byte 0x33 and UART2 toggles LED1 on receipt;
after the key interrupt fires, UART1 sends the address mark 0x102 to wake up the muted UART2, demonstrating multi-processor address wakeup.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, and Delay;
src/n32g033_it.c contains EXTI0_6_IRQHandler;
src/bsp_led.c/src/bsp_key.c and their headers drive the LED and key;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3 (GPIO_AF1), UART2 Tx PA1, Rx PA0 (GPIO_AF4), all alternate-function push-pull with pull-up;
baud rate 115200, 9 data bits, 1 stop bit, no parity, 16x oversampling;
address: UART1 takes 0x1 and UART2 takes 0x2;
wakeup: UART2 uses address-mark wakeup;
LED1 is on PA6.
Program and data path main -> RCC_Configuration -> GPIO_Configuration -> LED_GPIO_Config and EXTI_Key_Config -> UART_Init sets up the 9-bit frame -> UART_Enable -> UART_SetAddr sets the addresses -> UART_ConfigWakeUpMode selects address wakeup -> the main loop UART_SendData sends 0x33 and checks RXDNE to toggle the LED -> the key interrupt EXTI0_6_IRQHandler switches mute mode or sends the wakeup address.
Main APIs UART_SetAddr, UART_ConfigWakeUpMode, UART_EnableRcvWakeUp, UART_SendData, UART_ReceiveData, UART_GetFlagStatus, UART_Init, UART_StructInit, UART_Enable
Verification Cross-connect UART1 and UART2 on one board;
after running at full speed press KEY repeatedly and watch whether LED1 blinks on each received 0x33 and whether UART2 stops responding after the first press.
Use note Multi-processor communication must use a 9-bit data frame, where the ninth bit distinguishes address from data;
mute mode is switched by UART_EnableRcvWakeUp;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses the ninth bit of a 9-bit frame as the address mark, so UART2 leaves mute mode only when it receives a frame matching its own address.

Data path: UART_Init sets up the 9-bit frame -> UART_Enable -> UART_SetAddr sets the addresses -> UART_ConfigWakeUpMode selects address wakeup -> the main loop UART_SendData sends 0x33 and checks RXDNE to toggle the LED -> EXTI0_6_IRQHandler uses UART_EnableRcvWakeUp to switch mute mode and sends 0x102 to wake up

1. Use a 9-bit frame and configure the address and wakeup method. The extra bit of a 9-bit frame marks the address, and the wakeup method decides how it is recognized.

src/main.csrc/main.c · main
    /* USARTy and USARTz configuration */
    UART_StructInit(&UART_InitStructure);
    UART_InitStructure.BaudRate   = 115200;
    UART_InitStructure.WordLength = UART_WL_9B;
    UART_InitStructure.StopBits   = UART_STPB_1;
    UART_InitStructure.Parity     = UART_PE_NO;
    UART_InitStructure.OverSampling        = UART_16OVER;
    UART_InitStructure.Mode       = UART_MODE_RX | UART_MODE_TX;

    /* Configure UARTy and USARTz */
    UART_Init(UARTy, &UART_InitStructure);
    UART_Init(UARTz, &UART_InitStructure);

    /* Enable the UARTy and UARTz */
    UART_Enable(UARTy, ENABLE);
    UART_Enable(UARTz, ENABLE);

    /* Set the UARTy and UARTz Address */
    UART_SetAddr(UARTy, 0x1);
    UART_SetAddr(UARTz, 0x2);

    /* Select the UARTz WakeUp Method */
    UART_ConfigWakeUpMode(UARTz, UART_WUM_ADDRMASK);

The address and wakeup method must match the communication peer, and both ends must take a 9-bit word length.

2. The main loop keeps sending and toggles the LED on receipt. It blinks on each received 0x33 and reads the data register to clear the overrun flag.

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

        /* Wait while UARTy TXE = 0 */
        while(UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
        {
        }

        if (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) != RESET)
        {
            if (UART_ReceiveData(UARTz) == 0x33)
            {
                LED1_ON;
                Delay(0x1FFFFF);
                LED1_OFF;
                Delay(0x1FFFFF);
            }
            /* Overflow data loss */
            if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
            {
                UARTz->DAT;
            }
        }
    }

3. Switch mute mode and send the wakeup address in the key interrupt. The first entry mutes the receiver, and the next one sends the address mark that wakes the peer.

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

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

        ControlFlag = 1;
    }
    else if (ControlFlag == 1)
    {
        /* Send the address mark (0x102) to wakeup UARTz */
        UART_SendData(UARTy, 0x102);
        /* Wait while UARTy TXE = 0 */
        while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
        {
        }

        ControlFlag = 2;
    }

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

A write to the data register precedes the address mark so that no leftover data is mistaken for the wakeup address.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_uart.h

5.19.3.11 Polling Polled transfer

Example directory: projects/n32g033_EVAL/examples/UART/Polling/

Enable the clocks→Configure the data pins→Initialize and enable UART→UART1 sends, UART2 receives→UART2 sends, UART1 receives→Compare the two sets of data

Item Source analysis
Function UART1 and UART2 are cross-connected and transfer in both directions byte by byte on the flag bits: UART1 sends TxBuffer1 received by UART2, and UART2 sends TxBuffer2 received by UART1, and the two sets are compared at the end.
Source files src/main.c contains main, RCC_Configuration, GPIO_Configuration, and Buffercmp;
inc/main.h defines the pins and alternate-function macros of the two instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART1 Tx PA2, Rx PA3 (GPIO_AF1), UART2 Tx PA1, Rx PA0 (GPIO_AF4), all alternate-function push-pull with pull-up;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
no interrupts or DMA are used.
Program and data path main -> RCC_Configuration -> GPIO_Configuration -> UART_Init -> UART_Enable -> TXDE and RXDNE are polled to complete the UART1-to-UART2 transfer -> the reverse UART2-to-UART1 transfer -> Buffercmp compares the two sets of transferred data.
Main APIs UART_SendData, UART_ReceiveData, UART_GetFlagStatus, UART_Init, UART_StructInit, UART_Enable
Verification Cross-connect UART1 and UART2 on one board;
at full speed check TransferStatus1 and TransferStatus2 in the debugger;
PASSED means success.
Use note Polling occupies the CPU and suits introductory checks and low-speed, small-data cases;
the receive and transmit buffers must cover the whole frame;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1;
before testing, remove the MCU_TX/MCU_RX jumpers on NS-LINK.
Core-code walkthrough and porting

The example is the most basic transfer style, completing both directions by querying the flags, and is a good starting point for using the driver functions.

Data path: UART_Init sets up receive and transmit -> UART_Enable -> TXDE/RXDNE are polled to complete the forward transfer -> the reverse transfer -> Buffercmp compares the two buffers

1. Initialize the transfer parameters. The instances, word length, parity, and oversampling used are fixed here.

src/main.csrc/main.c · main
    /* UARTy and UARTz configuration */
    UART_StructInit(&UART_InitStructure);
    UART_InitStructure.BaudRate   = 115200;
    UART_InitStructure.WordLength = UART_WL_8B;
    UART_InitStructure.StopBits   = UART_STPB_1;
    UART_InitStructure.Parity     = UART_PE_NO;
    UART_InitStructure.OverSampling        = UART_16OVER;
    UART_InitStructure.Mode       = UART_MODE_RX | UART_MODE_TX;

    /* Configure UARTy and UARTz */
    UART_Init(UARTy, &UART_InitStructure);
    UART_Init(UARTz, &UART_InitStructure);

    /* Enable the UARTy and UARTz */
    UART_Enable(UARTy, ENABLE);
    UART_Enable(UARTz, ENABLE);

2. Forward polling: UART1 sends, UART2 receives. Each byte waits for the transmit buffer to empty and then for the peer’s receive flag to set.

src/main.csrc/main.c · main
    while (TxCounter1 < TxBufferSize1)
    {
        /* Send one byte from UARTy to UARTz */
        UART_SendData(UARTy, TxBuffer1[TxCounter1++]);
        /* Loop until UARTy DAT register is empty */
        while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
        {
        }

        /* Loop until the UARTz Receive Data Register is not empty */
        while (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) == RESET)
        {
            /* Overflow data loss */
            if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
            {
                UARTz->DAT;
            }
        }
        /* Store the received byte in RxBuffer */
        RxBuffer2[RxCounter2++] = UART_ReceiveData(UARTz);
    }    

Reading the data register inside the receive wait clears the overrun flag, so no data loss can cause a permanent wait.

3. Reverse polling and comparison of the results. The reverse transfer swaps the roles of the two instances, and each set of data is compared once both directions have finished.

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

The comparison only makes sense after both directions have received their full frames, avoiding a false failure on half a frame.

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

5.19.3.12 Printf printf redirection output

Example directory: projects/n32g033_EVAL/examples/UART/Printf/

Initialize the log UART→Output a string with printf→The main loop idles

Item Source analysis
Function The board log interface configures UART1 as a print UART, and printf outputs a line of text from UART1 to the host, demonstrating redirection of the standard library output to UART.
Source files src/main.c contains only main, which calls log_init and printf;
the UART setup and the fputc redirection are in projects/n32g033_EVAL/bsp/src/log.c and bsp/inc/log.h;
inc/main.h only includes the common headers;
the project files are in MDK-ARM/ and EWARM/.
Key configuration The log interface configures UART1, Tx PA9, Rx PA10 (GPIO_AF4) alternate-function push-pull with pull-up;
baud rate 115200, 8 data bits, 1 stop bit, no parity, 16x oversampling;
the LOG_ENABLE macro must be on.
Program and data path main -> log_init configures and enables UART1 -> printf outputs a string -> fputc sends each character through UART_SendData -> the main loop idles.
Main APIs The main function calls no UART driver function directly;
the log UART is configured by the board-level log_init (which internally calls UART_Init, UART_Enable, and UART_SendData)
Verification Connect the host to PA9 with the baud rate set to 115200;
after reset the string This is a Printf demo of UART. should be received.
Use note The redirection relies on fputc, so the project must link the bsp log.c;
to print through another UART, change the instance and pin macros at the top of log.c;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses the board log component to attach UART1 to the standard output, so the main function only has to initialize and print.

Data path: log_init configures and enables UART1 -> printf enters the standard library -> fputc takes over the output -> UART_SendData sends each character

1. Initialize the log UART and print. The main function does only two things: open the UART and output one line of text.

src/main.csrc/main.c · main
int main(void)
{
    /* Configure UART1 */
    log_init();
    /* Use the printf function to output a message on the upper computer */
    printf("This is a Printf demo of UART.\n\r");

    while (1)
    {
    }
}

2. The log interface configures the UART1 pins and parameters. The pins, baud rate, and parity of the print channel are fixed in these lines.

projects/n32g033_EVAL/bsp/src/log.cprojects/n32g033_EVAL/bsp/src/log.c · log_init
    RCC_EnableAPBPeriphClk(LOG_PERIPH | LOG_PERIPH_GPIO, ENABLE);

    GPIO_InitStructure.Pin            = LOG_TX_PIN;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF4;
    GPIO_InitStructure.GPIO_Pull      = GPIO_PULL_UP;
    GPIO_InitPeripheral(LOG_GPIO, &GPIO_InitStructure);

    GPIO_InitStructure.Pin             = LOG_RX_PIN;
    GPIO_InitStructure.GPIO_Alternate  = GPIO_AF4;
    GPIO_InitPeripheral(LOG_GPIO, &GPIO_InitStructure);

    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = UART_WL_8B;
    USART_InitStructure.StopBits            = UART_STPB_1;
    USART_InitStructure.Parity              = UART_PE_NO;
    USART_InitStructure.Mode                = UART_MODE_RX | UART_MODE_TX;
    USART_InitStructure.OverSampling        = UART_16OVER;

    // init uart
    UART_Init(LOG_UARTx, &USART_InitStructure);

    // enable uart
    UART_Enable(LOG_UARTx, ENABLE);

The instance and pins come from the macros at the top of log.c, so switching UARTs means changing only the macros, not the call sites.

3. Redirect the character output and wait for transmission to finish. Each character waits for the whole-frame transmit-complete flag so no character is dropped.

projects/n32g033_EVAL/bsp/src/log.cprojects/n32g033_EVAL/bsp/src/log.c · fputc
int fputc(int ch, FILE* f)
{
    if (ch == '\r')
    {
        is_lr_sent = 1;
    }
    else if (ch == '\n')
    {
        if (!is_lr_sent)
        {
            /* Loop until the end of transmission */
            while (UART_GetFlagStatus(LOG_UARTx, UART_FLAG_TXC) == RESET)
            {
            }
            UART_SendData(LOG_UARTx, (uint8_t)'\r');
        }
        is_lr_sent = 0;
    }
    else
    {
        is_lr_sent = 0;
    }
    /* Loop until the end of transmission */
    while (UART_GetFlagStatus(LOG_UARTx, UART_FLAG_TXC) == RESET)
    {
    }
    UART_SendData(LOG_UARTx, (uint8_t)ch);
    return ch;
}

The newline is paired with a carriage return here so a host serial terminal displays each line correctly.

Corresponding source files: src/main.c, projects/n32g033_EVAL/bsp/src/log.c

5.19.3.13 WakeUpFromStop UART3 low-power wakeup

Example directory: projects/n32g033_EVAL/examples/UART/WakeUpFromStop/

Enable the PWR, UART3, and GPIO clocks→Configure the UART3 pins and the EXTI event line→Switch the system clock and the UART3 clock to LSI→Initialize UART3 and keep sending→On 0x55, configure wakeup and enter STOP→Reinitialize UART3 after wakeup

Item Source analysis
Function UART3 sends an incrementing byte every second;
on receiving 0x55 it configures data-match wakeup and enters STOP mode, waking on the falling edge of the receive line together with the data match, and reinitializes UART3 after it wakes to keep sending.
Source files src/main.c contains main, GPIO_Configuration, and SetSysClock_LSI;
inc/main.h defines the UART3 instance and pins;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: UART3 Tx PA2, Rx PA3 (GPIO_AF6) alternate-function push-pull with pull-up;
baud rate 2000, 8 data bits, 1 stop bit, no parity, 16x oversampling;
clock: the system clock is switched to LSI (about 32 kHz) and UART3 takes LSI too;
wakeup: data-match-clear method, with EXTI10 configured as a both-edge event.
Program and data path main -> RCC_EnableAPBPeriphClk enables PWR/UART3/GPIO -> GPIO_Configuration -> EXTI_InitPeripheral configures EXTI10 -> SetSysClock_LSI and RCC_ConfigUart3Clk -> UART_Init and enables UART3 -> the main loop UART_SendData increments every second -> on 0x55 UART_CfgWakeupMode and PWR_EnterSTOPMode -> after wakeup UART_DeInit and UART_Init reconfigure UART3.
Main APIs UART_CfgWakeupMode, UART_DeInit, UART_Init, UART_StructInit, UART_Enable, UART_SendData, UART_ReceiveData, UART_GetFlagStatus
Verification Connect the host to PA2/PA3 with the baud rate set to 2000;
after reset the incrementing data is received;
send 0x55 to stop it, and send 0x55 again to resume.
Use note Only UART3 supports low-power wakeup, and the baud rate is low and LSI must be the clock source;
the interrupt must already be enabled before STOP, and the UART registers are reinitialized after wakeup;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example lowers the system clock to LSI so UART3 runs on it, enters STOP on the agreed byte, and checks UART3 low-power wakeup.

Data path: RCC_EnableAPBPeriphClk enables the clocks -> GPIO_Configuration -> SetSysClock_LSI and RCC_ConfigUart3Clk -> UART_Init and UART_Enable -> the main loop sends -> on 0x55 UART_CfgWakeupMode and PWR_EnterSTOPMode -> after wakeup UART_DeInit and UART_Init reconfigure

1. Enable the clocks, configure the EXTI event line, and switch the clock source. Wakeup depends on the receive-line level change, so EXTI10 is configured before the clock is switched.

src/main.csrc/main.c · main
    /* Enable PWR/UART3/GPIO Clock */
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_PWR | RCC_APB_PERIPH_UART3 | RCC_APB_PERIPH_GPIO, ENABLE);

    /* Configure the GPIO ports */
    GPIO_Configuration();
    
    /*Configure key EXTI line*/
    EXTI_InitStructure.EXTI_Line = EXTI_LINE10;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);
    
    /* Configure the system clock as LSI */
    SetSysClock_LSI();
    /* Choose LSI as the clock source for UART3 */
    RCC_ConfigUart3Clk(RCC_UART3_CLKSRC_LSI);

2. The main loop sends and enters STOP on the agreed byte. Data-match wakeup clears the match flag, so UART3 is reconfigured after it wakes.

src/main.csrc/main.c · main
    while (1)
    {
        /* Insert a long delay */
        SysTick_Delay_Ms(1000);
        
        if(UART_GetFlagStatus(UART3,UART_FLAG_RXDNE) == SET)
        {
            if(UART_ReceiveData(UART3) == 0x55)
            {
                /* configure uart3 wakeup mode */
                UART_CfgWakeupMode(UART3,UART_WAKEUP_DATACLR,UART_WAKEUP_ENABLE);
                /* Enter STOP mode */
                PWR_EnterSTOPMode(PWR_STOPENTRY_WFE);
                /* Configure UARTy and UARTz */
                UART_DeInit(UARTy);
                UART_Init(UARTy, &UART_InitStructure);
                /* Enable the UARTy and UARTz */
                UART_Enable(UARTy, ENABLE);
            }
        }
        else
        {
            UART_SendData(UART3,cnt_temp++);
        }

The data-register-clear wakeup method requires reading the data register once after the matching byte arrives, which UART_ReceiveData already does.

3. Switch to the LSI system clock. Low-power wakeup needs LSI as the clock source, and the Flash latency is lowered at the same time.

src/main.csrc/main.c · SetSysClock_LSI
ErrorStatus SetSysClock_LSI(void)
{
    uint32_t timeout_value = 0xFFFFFFFF; 
    
    /* Wait till LSI is ready */
    if (RCC_GetFlagStatus(RCC_CLKINT_FLAG_LSIRDF) == SET)
    {
        /* HCLK = SYSCLK */
        RCC_ConfigHclk(RCC_SYSCLK_DIV1);

        /* PCLK = HCLK */
        RCC_ConfigPclk(RCC_HCLK_DIV1);
    
        RCC_ConfigSysclk(RCC_SYSCLK_SRC_LSI);
        while (RCC_GetSysclkSrc() != RCC_CFG_SCLKSTS_LSI)
        {
            if ((timeout_value--) == 0)
            {
                return ERROR;
            }
        }
        
        FLASH_SetLatency(FLASH_LATENCY_0);
    }
    else
    {
        /* If LSI fails to start-up, the application will have wrong clock
           configuration. User can add here some code to deal with this error */
        return ERROR;
    }
    return SUCCESS;

}

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

5.19.4 Notes and troubleshooting

Scenario Check & action Source
Changing the baud-rate register during communication The UART chapter of the user manual states that the baud-rate register BRCF is not writable while UART is enabled (UEN=1) and must not be changed during communication;
disable UART first, make the change, then re-enable it.
User manual
Changing the word length during communication The UART chapter of the user manual states that the word length (WL) must not be changed during transfer;
stop the transfer first if it needs changing.
User manual
Using a 0.5 stop bit The UART chapter of the user manual states that a 0.5 stop bit does not sample the stop bit, so frame error and break detection are impossible;
do not use a 0.5 stop bit when those detections are needed.
User manual
Single-wire half-duplex mode The UART chapter of the user manual states that LINMEN and IRDAMEN must both be 0 before single-wire half-duplex is used;
when the mode is unused, the TX pin should float or be open-drain high so the bus is not held.
User manual
Using IrDA mode The UART chapter of the user manual states that using IrDA requires setting the stop bits to 0 and clearing LINMEN and HDMEN;
in normal mode the prescaler can only be 1, the maximum baud rate is 115200, and transmit and receive must be at least 10 ms apart.
User manual
Using LIN mode The UART chapter of the user manual states that using LIN requires setting the stop bits to 0 and clearing HDMEN and IRDAMEN;
LIN supports only 8-bit data frames, and the OSPM bit must stay 0.
User manual
UART3 low-power wakeup The UART chapter of the user manual states that only UART3 supports low-power wakeup;
enable wakeup and set the clock source before entering STOP, and reinitialize the UART registers after wakeup.
User manual

Errata sheet V1.0.0 (2025.9.25) targets revision A devices and lists no UART-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.