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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
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.
/* 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.
/* 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.
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.
/* 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.
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.
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.
/* 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.
/* 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.
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.
/* 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.
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.
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.
/* 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.
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.
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.
/* 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.
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.
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.
/* 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.
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.
/* 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.
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.
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.
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.
/* 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.
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.
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.