5.21 USART 使用说明

本章说明 N32G41x 标准外设库中 USART 驱动的文件组成与 38 个公开函数,涵盖波特率与帧格式配置、数据收发、多处理器通信、LIN、SmartCard、IrDA、RS485 驱动使能、收发超时、低功耗唤醒、DMA 与中断标志管理;当前 SDK 提供了 19 个 USART 示例工程。各工作模式的寄存器位域与配置顺序请配合用户手册的“通用同步异步收发器(USART)”章节阅读,芯片已知问题以勘误手册为准。

5.21.1 相关文件

相对路径 作用
firmware/n32g41x_std_periph_driver/inc/n32g41x_usart.h 定义 USART_InitType 与 USART_ClockInitType 结构体及其字长、停止位、校验、模式、流控、过采样等配置宏,并按功能分区声明 38 个公开函数
firmware/n32g41x_std_periph_driver/src/n32g41x_usart.c 实现上述 38 个函数:读改写 USART 寄存器的帧格式、使能、保护时间、超时、中断与标志位,覆盖查询收发、DMA、同步、LIN、SmartCard、IrDA、RS485 与低功耗唤醒
projects/n32g41x_EVAL/examples/USART/ 包含 19 个示例:485_DMA、485_Master、485_Slave、DMA_Interrupt、DMA_Polling_MaxBaudrate、HalfDuplex、HardwareFlowCtrl、Interrupt、LP_Transmit_Receive、Transmit_Receive、LIN_Master、LIN_Slave、MultiProcessor、Polling、Printf、SmartCard、Synchronous、WakeUpFromSleep、WakeUpFromStop
projects/n32g41x_EVAL/examples/USART/Polling/ 演示查询标志位方式双机通信的示例工程,含 MDK-ARM/、EWARM/、inc/main.h、inc/n32g41x_it.h、src/main.c、src/n32g41x_it.c、readme.txt
projects/n32g41x_EVAL/bsp/inc/n32g41x_stb.h 定义评估板 LED1~LED3 与按键 KEY1~KEY3、WKUP 的端口、引脚与有效电平宏,供部分 USART 示例引用

5.21.2 API 参考

搜索 API共 38 个函数

以下 38 个公开函数按 n32g41x_usart.h 的声明顺序分为 13 组;每个条目给出功能摘要与 n32g41x_usart.c 中该函数正上方的注释原文。

复位与初始化

USART_DeInit、USART_Init、USART_StructInit、USART_ClockInit、USART_ClockStructInit、USART_Enable

数据传输

USART_SendData、USART_ReceiveData、USART_SendBreak

多处理器通信

USART_SetAddr、USART_ConfigWakeUpMode、USART_EnableRcvWakeUp

LIN 模式

USART_ConfigLINBreakDetectLength、USART_EnableLIN

SmartCard 模式

USART_SetGuardTime、USART_SetPrescaler、USART_EnableSmartCard、USART_SetSmartCardNACK

半双工模式

USART_EnableHalfDuplex

IrDA 模式

USART_ConfigIrDAMode、USART_EnableIrDA

RS485 驱动使能

USART_CfgDriverAssertTime、USART_CfgDriverdeassertTime、USART_DriverPolaritySet、USART_DriverModeSet

收发超时

USART_RTOSet、USART_CfgRTOWidth

其它配置

USART_FirstByteDelayFixSet、USART_PinSwapSet

低功耗唤醒

USART_EnableWakeUp、USART_CfgWakeUpDataClr

DMA

USART_EnableDMA

中断与标志管理

USART_ConfigInt、USART_GetFlagStatus、USART_ClrFlag、USART_ClrRTOFlag、USART_GetIntStatus、USART_ClrIntPendingBit

void USART_DeInit(const USART_Module *USARTx)将 USARTx 外设寄存器恢复为复位默认值。
源码注释
name    USART_DeInit
fun     Deinitializes the USARTx peripheral registers to their default reset values.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
void USART_Init(USART_Module *USARTx, const USART_InitType *USART_InitStruct)按 USART_InitStruct 初始化 USARTx 外设。
源码注释
name    USART_Init
fun     Initializes the USARTx peripheral according to USART_InitStruct.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_InitStruct (The input parameters must be the following values):
          - BaudRate :
            - (((BaudRate) > 0) && ((BaudRate) < 0x00989680)) (0-10 Mbit/s)
          - WordLength
            - USART_WL_8B
            - USART_WL_9B
          - StopBits
            - USART_STPB_1
            - USART_STPB_0_5
            - USART_STPB_2
            - USART_STPB_1_5
          - Parity
            - USART_PE_NO
            - USART_PE_EVEN
            - USART_PE_ODD
          - Mode
            - USART_MODE_RX
            - USART_MODE_TX
          - HardwareFlowControl
            - USART_HFCTRL_NONE
            - USART_HFCTRL_RTS
            - USART_HFCTRL_CTS
            - USART_HFCTRL_RTS_CTS
          - OverSampling
            - USART_8OVER
            - USART_16OVER
return  none
void USART_StructInit(USART_InitType *USART_InitStruct)用默认值填充 USART_InitStruct 的各个成员。
源码注释
name    USART_StructInit.
fun     Fills each USART_InitStruct member with its default value.
param   USART_InitStruct (The input parameters must be the following values):
          - BaudRate
          - WordLength
          - StopBits
          - Parity
          - Mode
          - HardwareFlowControl
          - OverSampling
return  none
void USART_ClockInit(USART_Module *USARTx, const USART_ClockInitType *USART_ClockInitStruct)按 USART_ClockInitStruct 中指定的参数初始化 USARTx 外设时钟。
源码注释
name    USART_ClockInit
fun     Initializes the USARTx peripheral Clock according to the specified parameters in the USART_ClockInitStruct.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_ClockInitStruct (The input parameters must be the following values):
          - Clock :
            - USART_CLK_DISABLE
            - USART_CLK_ENABLE
          - Polarity :
            - USART_CLKPOL_LOW
            - USART_CLKPOL_HIGH
          - Phase :
            - USART_CLKPHA_1EDGE
            - USART_CLKPHA_2EDGE
          - LastBit :
            - USART_CLKLB_DISABLE
            - USART_CLKLB_ENABLE
return  none
note    Polarity, Phase and LastBit can be configured only when both the transmitter and
        the receiver are disabled (USART_CTRL1.TXEN=0 and USART_CTRL1.RXEN=0).
void USART_ClockStructInit(USART_ClockInitType *USART_ClockInitStruct)用默认值填充 USART_ClockInitStruct 的各个成员。
源码注释
name    USART_ClockStructInit
fun     Fills each USART_ClockInitStruct member with its default value.
param   USART_ClockInitStruct (The output structure to be filled with the following values):
          - Clock : USART_CLK_DISABLE
          - Polarity : USART_CLKPOL_LOW
          - Phase : USART_CLKPHA_1EDGE
          - LastBit : USART_CLKLB_DISABLE
return  none
void USART_Enable(USART_Module *USARTx, FunctionalState Cmd)使能或禁用指定的 USART 外设。
源码注释
name    USART_Enable
fun     Enables or disables the specified USART peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SendData(USART_Module *USARTx, uint32_t Data)通过 USARTx 外设发送单个数据。
源码注释
name    USART_SendData
fun     Transmits single data through the USARTx peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Data :
          - the data to transmit
return  none
uint32_t USART_ReceiveData(const USART_Module *USARTx)返回 USARTx 外设最近接收到的数据。
源码注释
name    USART_ReceiveData
fun     Returns the most recent received data by the USARTx peripheral.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  USARTx->DAT value
void USART_SendBreak(USART_Module *USARTx)发送断开帧字符。
源码注释
name    USART_SendBreak
fun     Transmits break characters.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
void USART_SetAddr(USART_Module *USARTx, uint8_t USART_Addr)设置 USART 节点的地址。
源码注释
name    USART_SetAddr
fun     Sets the address of the USART node.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_Addr (The input parameters must be the following values):
          - (((USART_Addr)>=0x00)&&((USART_Addr)<0x10))
return  none
void USART_ConfigWakeUpMode(USART_Module *USARTx, uint32_t USART_WakeUpMode)选择 USART 的唤醒方式。
源码注释
name    USART_ConfigWakeUpMode
fun     Selects the USART WakeUp method.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_WakeUpMode (The input parameters must be the following values):
          - USART_WUM_IDLELINE WakeUp by an idle line detection
          - USART_WUM_ADDRMASK WakeUp by an address mark
return  none
void USART_EnableRcvWakeUp(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 USART 的静默模式(接收唤醒)。
源码注释
name    USART_EnableRcvWakeUp
fun     Enables or disables the USART mute mode (receiver wake-up).
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigLINBreakDetectLength(USART_Module *USARTx, uint32_t USART_LINBreakDetectLength)设置 USART 的 LIN 断开检测长度。
源码注释
name    USART_ConfigLINBreakDetectLength
fun     Sets the USART LIN Break detection length.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_LINBreakDetectLength (The input parameters must be the following values):
          - USART_LINBDL_10B 10-bit break detection
          - USART_LINBDL_11B 11-bit break detection
return  none
void USART_EnableLIN(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 USART 的 LIN 模式。
源码注释
name    USART_EnableLIN
fun     Enables or disables the USART's LIN mode.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SetGuardTime(USART_Module *USARTx, uint8_t USART_GuardTime)设置 USART 的保护时间。
源码注释
name    USART_SetGuardTime
fun     Sets the specifies USART guard time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_GuardTime:
          - specifies the guard time
return  none
void USART_SetPrescaler(USART_Module *USARTx, uint8_t USART_Prescaler)设置系统时钟预分频系数。
源码注释
name    USART_SetPrescaler
fun     Sets the system clock prescaler.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_Prescaler:
          - specifies the prescaler clock
return  none
void USART_EnableSmartCard(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 USART 的智能卡模式。
源码注释
name    USART_EnableSmartCard
fun     Enables or disables the USART's Smart Card mode.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_SetSmartCardNACK(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 NACK 发送。
源码注释
name    USART_SetSmartCardNACK
fun     Enables or disables NACK transmission.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_EnableHalfDuplex(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 USART 的半双工通信。
源码注释
name    USART_EnableHalfDuplex
fun     Enables or disables the USART's Half Duplex communication.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigIrDAMode(USART_Module *USARTx, uint32_t USART_IrDAMode)配置 USART 的 IrDA 接口。
源码注释
name    USART_ConfigIrDAMode
fun     Configures the USART's IrDA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_IrDAMode (The input parameters must be the following values):
          - USART_IRDAMODE_LOWPPWER
          - USART_IRDAMODE_NORMAL
return  none
void USART_EnableIrDA(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 USART 的 IrDA 接口。
源码注释
name    USART_EnableIrDA
fun     Enables or disables the USART's IrDA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_CfgDriverAssertTime(USART_Module *USARTx, uint32_t Time)设置驱动使能信号的置位时间。
源码注释
name    USART_CfgDriverAssertTime
fun     Set Driver Enable assertion time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void USART_CfgDriverdeassertTime(USART_Module *USARTx, uint32_t Time)设置驱动使能信号的复位时间。
源码注释
name    USART_CfgDriverdeassertTime
fun     Set Driver Enable deassertion time.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Time:(The input parameters must be the following values)
          - 0x01 ~ 0x1F
return  none
void USART_DriverPolaritySet(USART_Module *USARTx, FunctionalState Cmd)选择驱动使能信号的极性。
源码注释
name    USART_DriverPolaritySet
fun     Driver enable polarity selection.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_DriverModeSet(USART_Module *USARTx, FunctionalState Cmd)选择驱动使能模式。
源码注释
name    USART_DriverModeSet
fun     Driver enable mode selection.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_RTOSet(USART_Module *USARTx, FunctionalState Cmd)使能或禁用接收超时。
源码注释
name    USART_RTOSet
fun     Receiver timeout enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_CfgRTOWidth(USART_Module *USARTx, uint32_t Width)配置接收超时的宽度。
源码注释
name    USART_CfgRTOWidth
fun     configure receive timeout width.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Width:(The input parameters must be the following values)
          - 0x00000000 ~ 0x0FFFFFFF
return  none
void USART_FirstByteDelayFixSet(USART_Module *USARTx, FunctionalState Cmd)使能或禁用首字节延时固定。
源码注释
name    USART_FirstByteDelayFixSet
fun     First byte delay fixed enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE  : the first byte delay is fixed     (USART_CTRL1.TEDEN = 0)
          - DISABLE : the first byte delay is not fixed (USART_CTRL1.TEDEN = 1)
return  none
void USART_PinSwapSet(USART_Module *USARTx, FunctionalState Cmd)使能或禁用 TX/RX 引脚交换。
源码注释
name    USART_PinSwapSet
fun     Swap TX/RX pins enable or disable.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param  Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_EnableWakeUp(USART_Module *USARTx, FunctionalState Cmd)配置 USART 在低功耗模式下接收到一个字节时唤醒系统。
源码注释
name    USART_EnableWakeUp
fun     configure when USART in lowpower mode, receive a byte will wakeup system.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART4
param   Cmd:(The input parameters must be the following values)
          - ENABLE
          - DISABLE
return  none
void USART_CfgWakeUpDataClr(USART_Module *USARTx, uint32_t WakeupMode)配置 USART 从低功耗模式唤醒时是否清除唤醒数据。
源码注释
name    USART_CfgWakeUpDataClr
fun     configure when USART wakeup from low power mode, can select whether clear wakeup data.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART4
param   WakeupMode:(The input parameters must be the following values)
          - USART_WAKEUP_CLRDATA
          - USART_WAKEUP_NOTCLRDATA
return  none
void USART_EnableDMA(USART_Module *USARTx, uint32_t USART_DMAReq, FunctionalState Cmd)使能或禁用 USART 的 DMA 接口。
源码注释
name    USART_EnableDMA
fun     Enables or disables the USART's DMA interface.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_DMAReq (The input parameters must be the following values):
          - USART_DMAREQ_TX       USART DMA transmit request
          - USART_DMAREQ_RX       USART DMA receive request
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void USART_ConfigInt(USART_Module *USARTx, uint32_t USART_INT, FunctionalState Cmd)使能或禁用指定的 USART 中断。
源码注释
name    USART_ConfigInt
fun     Enables or disables the specified USART interrupts.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_PEF   Parity Error interrupt
          - USART_INT_TXC   Transmission complete interrupt
          - USART_INT_TXDE  Transmit Data Register empty interrupt
          - USART_INT_RXDNE Receive Data register not empty interrupt
          - USART_INT_IDLEF Idle line detection interrupt
          - USART_INT_RTOE  Receiver timeout interrupt
          - USART_INT_LINBD LIN Break detection interrupt
          - USART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
          - USART_INT_CTSF  CTS change interrupt
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
FlagStatus USART_GetFlagStatus(const USART_Module *USARTx, uint32_t USART_FLAG)检查指定的 USART 标志是否置位。
源码注释
name    USART_GetFlagStatus
fun     Checks whether the specified USART flag is set or not.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_FLAG (The input   parameters must be the following values):
          - USART_FLAG_RTO      receiver timeout Flag
          - USART_FLAG_FEF      Framing error Flag
          - USART_FLAG_NEF      Noise error flag Flag
          - USART_FLAG_OREF     Overrun error
          - USART_FLAG_PEF      Parity error
          - USART_FLAG_LINBD    LIN break detection flag
          - USART_FLAG_CTSF     CTS Change flag
          - USART_FLAG_RXDNE    Receive data register not empty flag
          - USART_FLAG_TXC      Transmission Complete flag
          - USART_FLAG_TXDE     Transmit data register empty flag
          - USART_FLAG_IDLEF    Idle Line detection flag
return  SET or RESET
          - SET   : the specified flag is set
          - RESET : the specified flag is reset
void USART_ClrFlag(USART_Module *USARTx, uint32_t USART_FLAG)清除 USARTx 的待处理标志。
源码注释
name    USART_ClrFlag
fun     Clears the USARTx's pending flags.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_FLAG (The input   parameters must be the following values):
          - USART_FLAG_LINBD    LIN break detection flag
          - USART_FLAG_CTSF     CTS Change flag
          - USART_FLAG_RXDNE    Receive data register not empty flag
          - USART_FLAG_TXC      Transmission Complete flag
return  none
note
          - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun
            error) and IDLE (Idle line detected) flags are cleared by software
            sequence: a read operation to USART_STS register (USART_GetFlagStatus())
            followed by a read operation to USART_DAT register (USART_ReceiveData()).
          - RXNE flag can be also cleared by a read to the USART_DAT register
            (USART_ReceiveData()).
          - TC flag can be also cleared by software sequence: a read operation to
            USART_STS register (USART_GetFlagStatus()) followed by a write operation
            to USART_DAT register (USART_SendData()).
          - TXE flag is cleared only by a write to the USART_DAT register
            (USART_SendData()).
          - RTO flag is cleared only by a write 1 to the USART_CTRL2.RTOCF bit.
            (USART_ClrRTOFlag()).
void USART_ClrRTOFlag(USART_Module *USARTx)清除 USARTx 的接收超时待处理标志。
源码注释
name    USART_ClrRTOFlag
fun     Clears the USARTx's RTO pending flag.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
return  none
INTStatus USART_GetIntStatus(const USART_Module *USARTx, uint32_t USART_INT)检查指定的 USART 中断是否置位。
源码注释
name    USART_GetIntStatus
fun     Checks whether the specified USART interrupt has set or not.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_PEF   Parity Error interrupt
          - USART_INT_TXC   Transmission complete interrupt
          - USART_INT_TXDE  Transmit Data Register empty interrupt
          - USART_INT_RXDNE Receive Data register not empty interrupt
          - USART_INT_IDLEF Idle line detection interrupt
          - USART_INT_RTOE  Receiver timeout interrupt
          - USART_INT_LINBD LIN Break detection interrupt
          - USART_INT_ERRF  Error interrupt(Frame error, noise error, overrun error)
          - USART_INT_CTSF  CTS change interrupt
return  SET or RESET
          - SET   : the interrupt enable bit is set (the interrupt is enabled)
          - RESET : the interrupt enable bit is not set (the interrupt is disabled)
void USART_ClrIntPendingBit(USART_Module *USARTx, uint32_t USART_INT)清除 USARTx 的中断待处理位。
源码注释
name    USART_ClrIntPendingBit
fun     Clears the USARTx's interrupt pending bits.
param   USARTx (The input parameters must be the following values):
          - USART1
          - USART2
          - USART3
          - USART4
param   USART_INT (The input parameters must be the following values):
          - USART_INT_LINBD    LIN break detection interrupt
          - USART_INT_CTSF     CTS Change interrupt
          - USART_INT_RXDNE    Receive data register not empty interrupt
          - USART_INT_TXC      Transmission Complete interrupt
return  none
note
          - Only the four flags that USART_STS defines as rc_w0 can be cleared by a write:
            LINBDF, CTSF, RXDNE and TXC. The other pending bits are read-only and are
            cleared as follows:
          - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun
            error) and IDLE (Idle line detected) pending bits are cleared by
            software sequence: a read operation to USART_STS register (USART_GetFlagStatus())
            followed by a read operation to USART_DAT register (USART_ReceiveData()).
          - RXNE pending bit can be also cleared by a read to the USART_DAT register (USART_ReceiveData()).
          - TC pending bit can be also cleared by software sequence: a read
            operation to USART_STS register (USART_GetFlagStatus()) followed by a write
            operation to USART_DAT register (USART_SendData()).
          - TXE pending bit is cleared only by a write to the USART_DAT register
            (USART_SendData()).
          - RTO flag is cleared only by a write 1 to the USART_CTRL2.RTOCF bit.
            (USART_ClrRTOFlag()).

使用提示:n32g41x_usart.h 第 341、342 行以 USART_Module *USARTx 与 USART_Module* USARTx 两种写法重复声明了本函数,n32g41x_usart.c 中只有一份实现。

5.21.3 Demo 示例说明

本节说明 USART 的 19 个示例,覆盖轮询、中断、DMA、RS485、单线半双工、硬件流控、IrDA 红外、LIN、多处理器、智能卡、同步与低功耗唤醒等收发场景。

5.21.3.1 485_DMA RS485 DMA 收发

工程目录: projects/n32g41x_EVAL/examples/USART/485_DMA/

使能 DMA、GPIO 与两路 USART 时钟→配置 USART1 的 TX(PA9) 与 DE(PA12)、USART2 的 RX(PA3) 与 DE(PA1) 为复用推挽→配置一个发送通道与一个接收通道→使能两路 USART 的 RS485 DE 信号自动控制→使能发送与接收 DMA 请求并启动通道→等待两个 DMA 通道传输完成→比较收发缓冲并写入 TransferStatus2

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)之间通过 DMA 完成一次 RS485 通信:USART1 用 DMA 发送 TxBuffer1,USART2 用 DMA 接收进 RxBuffer2,最后比较两者是否一致。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、DMA_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的引脚、DMA 通道与重映射宏;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9、Rx PA10、DE PA12),USARTz 为 USART2(Tx PA2、Rx PA3、DE PA1);
除 N32G412 的 PA9 复用为 GPIO_AF4、N32G415 为 GPIO_AF0 外,其余引脚均为 GPIO_AF4 复用推挽上拉。
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样,收发均使能;
两路的 USART_DriverModeSet 使能、USART_DriverPolaritySet 设为 DISABLE(DE 低有效)。
DMA 上 USART1_TX 用 DMA_CH1、USART2_RX 用 DMA_CH4。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 依次调用 RCC_Configuration、GPIO_Configuration、DMA_Configuration,用 USART_StructInit 填默认值后 USART_Init 配置两路,随后配置 RS485 DE、开 DMA 请求与通道、USART_Enable 启动两路,用两个 while 轮询等待发送与接收 DMA 传输完成标志,最后 Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1) 写入 TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_CfgDriverAssertTime、USART_CfgDriverdeassertTime、USART_DriverPolaritySet、USART_DriverModeSet、USART_EnableDMA、USART_Enable、DMA_StructInit、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus
验证方法 复位运行后用调试器查看变量 TransferStatus2,为 PASSED 表示 USART2 收到的数据与 USART1 发送的一致,为 FAILED 表示不一致。
使用提示 需先断开开发板 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
外部需将 USART1_TX(PA9) 与 USART2_RX(PA3) 相连。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7。
核心代码解读与移植

示例把 USART1 的发送和 USART2 的接收都交给 DMA,用 RS485 DE 信号自动控制收发方向,最后比较缓冲区。

数据路径: main → USART_Init 配置 USART1/USART2 → USART_DriverModeSet 使能 RS485 DE → DMA_Init/DMA_RequestRemap 建立发送与接收通道 → DMA 把 TxBuffer1 搬到 USART1 数据寄存器、把 USART2 数据寄存器搬到 RxBuffer2 → Buffercmp 把结果写入 TransferStatus2

1. 使能 RS485 DE 自动控制并开 DMA 请求。 两路都先设置 DE 的起始与禁止时间、极性,再打开驱动模式,让收发器方向由硬件按发送状态自动切换;随后把 USART1 的发送请求和 USART2 的接收请求连到 DMA。

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

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

移植到其它板卡时要同步核对 DE 所在的引脚、复用号与有效极性;DE 引脚接错则收发方向无法按发送状态翻转。

2. 用 DMA 通道描述发送搬运。 发送通道以外设数据寄存器为目标、以 TxBuffer1 为源,字节宽度、外设地址不增、存储器地址递增,正常模式并给最高优先级,再通过请求重映射把通道绑到 USART1_TX 事件。

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

接收通道复用同一结构体,只改外设地址、存储器地址与方向;改动缓冲或长度时要同时改 BufSize 与 TxBufferSize1。

3. 轮询传输完成并比较结果。 两路启动后主循环各自等待发送与接收通道的传输完成标志,全部置位后再比较 TxBuffer1 与 RxBuffer2,把判定结果写入 TransferStatus2。

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

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

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

两个等待标志对应各自的通道,改动通道号时要同步改标志宏与重映射宏,否则轮询的是未使用的通道。

对应源码: src/main.c、inc/main.h

5.21.3.2 485_Master RS485 主机中断收发

工程目录: projects/n32g41x_EVAL/examples/USART/485/485_Master/

使能 GPIO 与 USART2 时钟并配置 USART2 中断→配置 USART2 的 TX、RX、DE 引脚为复用推挽→配置 USART2 为 RS485 DE 自动控制→使能发送与接收中断→主循环等待对端回传的数据→收齐后比较收发缓冲并打印结果

项目 源码解读
功能 演示两块开发板之间通过 USART2 的 RS485 模式(DE 信号自动控制)以中断方式通信:Master 用中断发送 TxBuffer2 给 Slave,接收 Slave 回传的数据后比较并打印结果。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
inc/main.h:USART2 的引脚、DE 与中断宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
对端为同目录下的 485_Slave/ 子工程。
关键配置 USARTz 为 USART2,Tx 为 PA2、Rx 为 PA3、DE 为 PA1,均为 GPIO_AF4 复用推挽上拉;
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样,收发使能;
DE 设为 USART_CfgDriverAssertTime(USARTz, 1)、USART_CfgDriverdeassertTime(USARTz, 0)、USART_DriverPolaritySet(USARTz, DISABLE)、USART_DriverModeSet(USARTz, ENABLE)。
NVIC 优先级分组 2,USART2_IRQn 的抢占优先级为 NVIC_PRE_PRIORITY_0、子优先级为 NVIC_SUB_PRIORITY_1。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 调用 log_init、RCC_Configuration、NVIC_Configuration、GPIO_Configuration,配置并 USART_Init USART2 与 RS485 DE,开 USART_INT_TXDE、USART_INT_RXDNE 后 USART_Enable;
中断把接收字节写入 RxBuffer2、把 TxBuffer2 逐字节发出,主循环 while (RxCounter2 < RxBufferSize2) 等到收齐后 Buffercmp 比较并打印 OK 或 Fail。
主要 API USART_StructInit、USART_Init、USART_CfgDriverAssertTime、USART_CfgDriverdeassertTime、USART_DriverPolaritySet、USART_DriverModeSet、USART_ConfigInt、USART_Enable、USART_GetFlagStatus、USART_GetIntStatus、USART_ReceiveData、USART_SendData
验证方法 两块板分别下载 Master 与 Slave 程序,先复位 Slave 再复位 Master,观察串口打印:N32G41x 485 Master test OK 为通过,N32G41x 485 Master test Fail 为异常。
使用提示 需两块评估板与两个 RS485 收发器模块,A-A、B-B 互连,并先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
Slave 需先上电运行,再由 Master 触发。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例让 USART2 在 RS485 模式下用收发中断完成一次主从通信,主循环只等待接收计数。

数据路径: main → USART_Init 配置 USART2 → USART_DriverModeSet 使能 RS485 DE → USART_ConfigInt 开收发中断 → USARTz_IRQHandler 在 TxBuffer2/RxBuffer2 间搬运字节 → 主循环 Buffercmp 比较并打印

1. 开 RS485 DE 与收发中断。 先把 DE 起始时间设为 1 个采样单位、极性低有效并打开驱动模式,再打开发送与接收中断,让收发都由 USARTz_IRQHandler 处理。

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

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

移植到其它板卡时要同步核对 DE 引脚与有效极性;USART_CfgDriverAssertTime 在驱动模式下不能配成 0 以下的无效值。

2. 中断里先接收再发送。 中断先判接收标志,把收到的字节写入 RxBuffer2 并在计数到达后关闭接收中断;再判发送标志,把 TxBuffer2 的下一字节写入数据寄存器,发送计数到达后关闭发送中断。

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

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

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

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

接收与发送的边界分别来自 NbrOfDataToRead1 与 NbrOfDataToTransfer2,改动缓冲区长度时要同步改这两个计数上限。

3. 中断里清差错标志。 收发之外还检查溢出、噪声、校验与帧错误标志,一旦置位就先读状态寄存器、再读数据寄存器把错误标志清零。

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

清错序列依赖先读 STS 再读 DAT 的顺序,替换成其它外设时不能只清其中一个寄存器。

4. 主循环等待收齐并打印结果。 主程序等到 RxCounter2 达到接收长度后比较 TxBuffer2 与 RxBuffer2,按 TransferStatus2 打印通过或异常。

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

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

等待条件用的是接收缓冲区长度 RxBufferSize2,改动 TxBuffer2 内容长度时要同步改这个长度,否则主循环会一直等待。

对应源码: src/main.c、src/n32g41x_it.c

5.21.3.3 485_Slave RS485 从机中断收发

工程目录: projects/n32g41x_EVAL/examples/USART/485/485_Slave/

使能 GPIO 与 USART2 时钟并配置 USART2 中断→配置 USART2 的 TX、RX、DE 引脚为复用推挽→配置 USART2 为 RS485 DE 自动控制并只开接收中断→主循环等待 Master 发来的数据→收齐后比较收发缓冲并打印结果→使能发送中断把数据回传给 Master

项目 源码解读
功能 演示两块开发板之间 USART2 RS485 模式的中断通信:Slave 先等待接收 Master 的数据,收齐后与预期数据比较并打印,随后把同一缓冲区经中断回传给 Master。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
inc/main.h:USART2 的引脚、DE 与中断宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
对端为同目录下的 485_Master/ 子工程。
关键配置 与 Master 相同,USARTz 为 USART2,Tx 为 PA2、Rx 为 PA3、DE 为 PA1,均为 GPIO_AF4 复用推挽上拉;
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样,收发使能;
DE 设为 USART_CfgDriverAssertTime(USARTz, 1)、USART_CfgDriverdeassertTime(USARTz, 0)、USART_DriverPolaritySet(USARTz, DISABLE)、USART_DriverModeSet(USARTz, ENABLE)。
启动时只开 USART_INT_RXDNE,收齐后才开 USART_INT_TXDE。
NVIC 抢占优先级为 NVIC_PRE_PRIORITY_0、子优先级为 NVIC_SUB_PRIORITY_1,优先级分组 2。
程序与数据路径 main 配置时钟、NVIC、GPIO 与 USART2 及 RS485 DE,先开接收中断并 USART_Enable;
while (RxCounter2 < RxBufferSize2) 等到收齐后 Buffercmp 比较并打印 OK 或 Fail,再调用 USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE) 开发送中断,由 USARTz_IRQHandler 把 TxBuffer2 逐字节发出。
主要 API USART_StructInit、USART_Init、USART_CfgDriverAssertTime、USART_CfgDriverdeassertTime、USART_DriverPolaritySet、USART_DriverModeSet、USART_ConfigInt、USART_Enable、USART_GetFlagStatus、USART_GetIntStatus、USART_ReceiveData、USART_SendData
验证方法 两块板分别下载 Slave 与 Master 程序,先复位 Slave 再复位 Master,观察串口打印:N32G41x 485 Slave test OK 为通过,N32G41x 485 Slave test Fail 为异常。
使用提示 需两块评估板与两个 RS485 收发器模块,A-A、B-B 互连,并先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
Slave 必须先上电运行,再由 Master 触发。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例让 USART2 先处于只收状态等待主机,收到并比较后再打开发送中断把数据回传。

数据路径: main → USART_ConfigInt(USART_INT_RXDNE) 只开接收中断 → USARTz_IRQHandler 收字节写入 RxBuffer2 → Buffercmp 比较并打印 → USART_ConfigInt(USART_INT_TXDE) 开发送中断 → 中断把 TxBuffer2 回传

1. 先只开接收中断并启动。 从机初始只用接收中断,启动后打印提示并进入等待,收发方向在比较完成后再切换。

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

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

从机与主机共用同一引脚与 DE 配置,移植时要保持两端波特率、字长与 DE 极性一致。

2. 收齐比较后再开发送中断。 主循环等到接收计数达到长度后比较 TxBuffer2 与 RxBuffer2 并打印结果,随后打开发送中断,把回传交给中断处理。

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

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

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

发送与接收的计数上限都来自缓冲区长度宏,改动回传数据长度时要同步改 TxBufferSize2 与 RxBufferSize2。

3. 中断完成接收与回传搬运。 USARTz_IRQHandler 收到字节写入 RxBuffer2,收到足够数量后关接收中断;发送中断触发时从 TxBuffer2 取字节写入数据寄存器,发完关发送中断。

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

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

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

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

从机回传的就是接收时用的同一缓冲区内容,改动回传内容时要同步核对两个方向的计数上限。

4. 使能 RS485 DE 自动控制。 DE 起始时间、极性、驱动模式与主机一致,收发方向仍由硬件按发送状态自动切换。

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

主机与从机的 DE 配置必须一致,两端极性或起始时间不同会导致总线收发方向错乱。

对应源码: src/main.c、src/n32g41x_it.c

5.21.3.4 DMA_Interrupt DMA 发送与中断接收

工程目录: projects/n32g41x_EVAL/examples/USART/DMA_Interrupt/

使能 DMA、GPIO 与两路 USART 时钟并配置 USART2 中断→配置 USART1 与 USART2 的 TX、RX 引脚为复用推挽→配置两路 USART 的发送 DMA 通道→使能发送 DMA 请求、USART2 接收中断并启动两路→USART1 轮询接收 USART2 发来的数据→等待两个发送通道传输完成并比较两组缓冲

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)的 DMA 发送与两种接收方式:USART1 用 DMA 发送 TxBuffer1、USART2 用接收中断收进 RxBuffer2;
USART2 用 DMA 发送 TxBuffer2、USART1 用轮询收进 RxBuffer1。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、DMA_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的引脚与 DMA 通道宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9、Rx PA10),USARTz 为 USART2(Tx PA2、Rx PA3);
除 N32G412 的 PA9 复用为 GPIO_AF4、N32G415 为 GPIO_AF0 外,其余引脚均为 GPIO_AF4 复用推挽上拉;
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样;
USART1_TX 用 DMA_CH1、USART2_TX 用 DMA_CH3,方向为 DMA_DIR_PERIPH_DST;
USART2_IRQn 抢占优先级为 NVIC_PRE_PRIORITY_0、子优先级为 NVIC_SUB_PRIORITY_0。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 配置时钟、NVIC、GPIO、DMA 与两路 USART,开两路发送 DMA 请求、USART2 接收中断与两个 DMA 通道后 USART_Enable;
随后在 while (indexFlag < TxBufferSize2) 中轮询 USART1 的 USART_FLAG_RXDNE 把 USART2 发来的数据收进 RxBuffer1,再等待两个发送通道传输完成标志,Buffercmp 比较后写入 TransferStatus1、TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_EnableDMA、USART_ConfigInt、USART_Enable、USART_GetFlagStatus、USART_ReceiveData、USART_GetIntStatus、USART_SendData、DMA_StructInit、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus、NVIC_Init
验证方法 复位运行后依次查看变量 TransferStatus1 与 TransferStatus2,两者均为 PASSED 表示两组收发数据一致,任一为 FAILED 表示对应方向不一致。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
外部需把 USART1_TX(PA9) 接 USART2_RX(PA3)、USART2_TX(PA2) 接 USART1_RX(PA10)。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例让两路 USART 都用 DMA 发送,接收则一路轮询、一路中断,以对照 DMA 发送配合不同接收方式。

数据路径: main → USART_EnableDMA(USART_DMAREQ_TX) 为两路开 DMA 发送 → USART_ConfigInt(USART_INT_RXDNE) 开 USART2 接收中断 → DMA_Init 把 TxBuffer1/TxBuffer2 搬到各自数据寄存器 → 主循环轮询 USART_FLAG_RXDNE 收 RxBuffer1,USARTz_IRQHandler 收 RxBuffer2 → Buffercmp 把结果写入两个状态变量

1. 开 DMA 请求、接收中断并启动通道。 两路都只开 DMA 发送请求,接收侧 USART1 用轮询、USART2 用中断,两个发送 DMA 通道在此一并使能。

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

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

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

改成双向都用 DMA 时要同时补开接收请求与接收通道,只开发送请求会导致接收侧永远等不到数据。

2. 轮询接收 USART1 的数据。 USART1 的接收不走 DMA 也不走中断,主循环在接收标志置位后把字节取进 RxBuffer1,遇到溢出标志时用读数据寄存器的方式丢弃。

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

轮询收数与接收中断不能同时使能,移植时若改为中断接收,要删掉这段轮询并改由中断写缓冲。

3. 用 DMA 通道描述发送搬运。 发送通道以外设数据寄存器为目标、以 TxBuffer1 为源,字节宽度、外设地址不增、存储器地址递增,再按请求重映射绑到 USART1_TX 事件。

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

USART2 的发送通道复用同一结构体,只改外设地址、存储器地址与重映射名;改动源缓冲或长度要同步改 BufSize。

4. 等待发送完成并比较结果。 轮询接收结束后等待两个发送通道的传输完成标志,再分别比较 TxBuffer2/RxBuffer1 与 TxBuffer1/RxBuffer2。

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

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

两个状态变量分别对应两个方向,改动发送或接收缓冲长度时要同步改 Buffercmp 的长度参数。

对应源码: src/main.c、src/n32g41x_it.c

5.21.3.5 DMA_Polling_MaxBaudrate DMA 轮询最高波特率

工程目录: projects/n32g41x_EVAL/examples/USART/DMA_Polling_MaxBaudrate/

使能 DMA、GPIO 与两路 USART 时钟→配置 USART1 与 USART4 的 TX、RX 引脚为复用推挽→配置两路 USART 的发送与接收四个 DMA 通道→使能两路收发 DMA 请求与通道→轮询等待四个 DMA 通道传输完成→比较两组缓冲并写入两个状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART4(USARTz)以最高波特率 10000000 bps 通过 DMA 双向传输:双方各自用 DMA 发送并接收,轮询四个通道的传输完成标志后比较收发数据。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、DMA_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的引脚与 DMA 通道宏;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9、Rx PA10),USARTz 为 USART4(Tx PA1、Rx PA0);
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0,USART4 两脚为 GPIO_AF5;
波特率 10000000、8 数据位、1 停止位、无校验、无硬件流控、8 倍过采样(USART_8OVER);
DMA 分配为 USART1_TX 用 DMA_CH1、USART1_RX 用 DMA_CH2、USART4_TX 用 DMA_CH3、USART4_RX 用 DMA_CH4,字节宽度、外设地址不增、存储器地址递增、DMA_MODE_NORMAL。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 配置时钟、GPIO、DMA 与两路 USART,开两路收发 DMA 请求和四个 DMA 通道后 USART_Enable;
随后依次轮询四个 DMA_GetFlagStatus 等待 USART1_TX、USART1_RX、USART4_TX、USART4_RX 传输完成,最后 Buffercmp 比较并把结果写入 TransferStatus1、TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_EnableDMA、USART_Enable、DMA_StructInit、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus
验证方法 编译下载运行后检查 TransferStatus1 与 TransferStatus2,两者均为 PASSED 说明双向 DMA 传输正确,任一为 FAILED 说明对应方向出错。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
连接为 USART1_TX(PA9) 接 USART4_RX(PA0)、USART4_TX(PA1) 接 USART1_RX(PA10)。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用两路的收发四个 DMA 通道把波特率拉到最高,主循环只轮询标志,不做逐字节收发。

数据路径: main → USART_Init(BaudRate = 10000000、USART_8OVER)→ USART_EnableDMA(USART_DMAREQ_RX | USART_DMAREQ_TX) → DMA_Init/DMA_RequestRemap 建立四个通道 → 轮询四个完成标志 → Buffercmp 把结果写入两个状态变量

1. 两路收发 DMA 请求与四个通道使能。 两路都同时开收发请求,四个通道一次性使能,收发全部交给 DMA。

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

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

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

四个通道与四个请求要成对使能,漏开任一个方向的请求会让对应标志永远等不到。

2. 用 DMA 通道描述发送搬运。 一个通道描述发送:外设数据寄存器为目标、TxBuffer1 为源,字节宽度、外设地址不增、存储器地址递增,再按请求重映射绑到 USART1_TX 事件。

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

其余三个通道复用同一结构体,只改方向、外设地址、存储器地址与重映射名;收发共用外设时要核对每个通道绑定的请求线。

3. 轮询四个通道传输完成并比较。 主循环依次等待 USART1_TX、USART1_RX、USART4_TX、USART4_RX 四个标志,全部置位后比较两组缓冲。

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

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

最高波特率下四个标志必须按各自通道核对,改动通道分配时要同步改标志宏,否则轮询的是未使用的通道。

对应源码: src/main.c

5.21.3.6 HalfDuplex 单线半双工

工程目录: projects/n32g41x_EVAL/examples/USART/HalfDuplex/

使能 GPIO 与两路 USART 时钟→配置 USART1、USART2 的 TX 引脚为复用开漏输出→使能两路 USART 的半双工模式→USART1 逐字节发送,USART2 轮询接收进缓冲→USART2 逐字节发送,USART1 轮询接收进缓冲→比较两组缓冲并写入两个状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)以单线半双工模式通信:先由 USART1 发送 TxBuffer1 给 USART2,再由 USART2 发送 TxBuffer2 给 USART1,两次都由软件轮询标志收发。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的 USART 与引脚宏;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(只用 Tx PA9),USARTz 为 USART2(只用 Tx PA2),两脚都是 GPIO_MODE_AF_OD(复用开漏)上拉;
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0,PA2 为 GPIO_AF4;
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样,收发使能;
收发前分别调用 USART_EnableHalfDuplex 使能半双工。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 配置时钟、GPIO 与两路 USART 后 USART_Enable 并分别使能半双工;
第一段 while (NbrOfDataToRead2--) 里 USART1 等 USART_FLAG_TXDE 后 USART_SendData 逐字节发送 TxBuffer1,USART2 等 USART_FLAG_RXDNE 后收进 RxBuffer2;
随后清 USART1 数据寄存器,第二段把 USART2 的 TxBuffer2 发到 USART1 的 RxBuffer1;
最后 Buffercmp 比较写入 TransferStatus1、TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_EnableHalfDuplex、USART_GetFlagStatus、USART_SendData、USART_ReceiveData
验证方法 复位运行后依次查看变量 TransferStatus1 与 TransferStatus2,两者均为 PASSED 表示两个方向都一致,任一为 FAILED 表示对应方向出错。
使用提示 半双工下 TX 与 RX 在片内相连,只用一根线连接两块板的 TX 引脚(USART1_Tx 的 PA9 与 USART2_Tx 的 PA2);
需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用单线半双工把两路各自只留一根 TX 线相连,软件分两段轮流收发。

数据路径: main → USART_EnableHalfDuplex 使能两路半双工 → 第一段 USART_SendData/USART_ReceiveData 把 TxBuffer1 送到 RxBuffer2 → 第二段把 TxBuffer2 送到 RxBuffer1 → Buffercmp 把结果写入两个状态变量

1. TX 引脚配成复用开漏。 半双工只用一根线,TX 引脚必须是开漏并上拉,空闲时才不会被外部拉死。

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

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

移植时若把 TX 配成推挽,两个开漏/推挽并存会在总线上互相驱动,容易损坏引脚。

2. 使能半双工模式。 两路先 USART_Enable,再各自打开半双工,收发在内部共用一个数据通道。

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

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

半双工与 IrDA 等模式互斥,移植时开启其中一个前要确认另一个未使能。

3. 第一段:USART1 发、USART2 收。 每发一字节先等 TXDE,每收一字节先等 RXDNE,逐字节推进并把收到的数据写入 RxBuffer2,收完清一次数据寄存器。

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

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

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

每字节收发成对推进,改动方向顺序时要同步调整两段的收发计数与清寄存器动作。

4. 比较两组收发结果。 两段收发完成后分别比较 TxBuffer1/RxBuffer2 与 TxBuffer2/RxBuffer1。

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

两个状态变量分别对应两个方向,改动缓冲长度时要同步改 Buffercmp 的长度参数。

对应源码: src/main.c

5.21.3.7 HardwareFlowCtrl 硬件流控

工程目录: projects/n32g41x_EVAL/examples/USART/HardwareFlowCtrl/

使能 GPIO 与两路 USART 时钟→配置 USART1、USART2 的 TX、RX、RTS、CTS 引脚为对应复用功能→配置两路 USART 同时使用 CTS 与 RTS 硬件流控→USART1 逐字节发送,USART2 轮询接收进缓冲→USART2 逐字节发送,USART1 轮询接收进缓冲→比较两组缓冲并写入两个状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)在 CTS/RTS 硬件流控下双向传输:USART1 发送 TxBuffer1、USART2 接收,随后 USART2 发送 TxBuffer2、USART1 接收,收发都用轮询。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的 USART、TX/RX/RTS/CTS 引脚与复用宏;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9、Rx PA10、RTS PA12、CTS PA11),USARTz 为 USART2(Tx PA2、Rx PA3、RTS PA1、CTS PA0);
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0,其余 TX/RX/RTS/CTS 均为 GPIO_AF4;
RTS 配为复用推挽,RX 与 CTS 配为 GPIO_MODE_INPUT 上拉;
波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样,USART_InitStructure.HardwareFlowControl = USART_HFCTRL_CTS \| USART_HFCTRL_RTS,收发使能。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 配置时钟、GPIO 与两路 USART(硬件流控同时开 CTS 与 RTS)后 USART_Enable;
第一段 while (NbrOfDataToRead2--) 里 USART1 USART_SendData 逐字节发 TxBuffer1,USART2 轮询 USART_FLAG_RXDNE 收进 RxBuffer2;
第二段反向把 TxBuffer2 送到 RxBuffer1;
最后 Buffercmp 比较写入 TransferStatus1、TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_GetFlagStatus、USART_SendData、USART_ReceiveData
验证方法 编译下载运行后检查 TransferStatus1 与 TransferStatus2,两者均为 PASSED 说明硬件流控传输成功,任一为 FAILED 说明对应方向出错。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
除 TX/RX 外还要连接 USARTy 的 RTS(PA12) 接 USARTz 的 CTS(PA0)、USARTz 的 RTS(PA1) 接 USARTy 的 CTS(PA11)。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例在普通异步收发基础上给两路都打开 CTS 与 RTS,让收发节奏由硬件流控信号协调。

数据路径: main → USART_Init(HardwareFlowControl = USART_HFCTRL_CTS \| USART_HFCTRL_RTS)→ USART_Enable → 第一段 USART_SendData/USART_ReceiveData 把 TxBuffer1 送到 RxBuffer2 → 第二段把 TxBuffer2 送到 RxBuffer1 → Buffercmp 把结果写入两个状态变量

1. 配置 RTS 输出与 RX、CTS 输入。 RTS 作输出用复用推挽,RX 与 CTS 作输入用上拉住上拉,四个引脚各按方向配置。

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

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

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

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

RTS 与 CTS 的方向不能互换,CTS 作为输入必须能读到对端 RTS 的电平。

2. 第一段发送并轮询接收。 USART1 发一字节后等 TXDE,USART2 等 RXDNE 收进 RxBuffer2,流控信号自动协调两端的收发节奏。

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

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

轮询收发要保留溢出分支,改动缓冲或长度时要同步调整收发计数上限。

3. 比较两组收发结果。 两段收发完成后分别比较两个方向,把结果写入 TransferStatus1、TransferStatus2。

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

两个状态变量分别对应两个方向,硬件流控下仍要按方向核对缓冲。

对应源码: src/main.c

5.21.3.8 Interrupt 中断收发

工程目录: projects/n32g41x_EVAL/examples/USART/Interrupt/

使能 GPIO 与两路 USART 时钟并配置两路 NVIC 中断→配置 USART1 与 USART2 的 TX、RX 引脚为复用推挽→配置两路 USART 并同时开收发中断→中断里两路互相收发数据进各自缓冲→主循环等待两个方向收齐→比较两组缓冲并写入两个状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)以中断方式双向通信:两路同时收发,USART1 发 TxBuffer1 由 USART2 收进 RxBuffer2,USART2 发 TxBuffer2 由 USART1 收进 RxBuffer1。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
inc/main.h:USARTy/USARTz 的 USART、引脚与 IRQn 宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTy_IRQHandler、USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9、Rx PA10),USARTz 为 USART2(Tx PA2、Rx PA3);
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0,其余为 GPIO_AF4 复用推挽上拉;
波特率 115200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样,收发使能;
两路都开 USART_INT_RXDNE 与 USART_INT_TXDE;
NVIC 分组 2,USART1_IRQn 与 USART2_IRQn 抢占优先级为 NVIC_PRE_PRIORITY_0,子优先级分别为 NVIC_SUB_PRIORITY_0、NVIC_SUB_PRIORITY_1。
系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz。
程序与数据路径 main 配置时钟、NVIC、GPIO 与两路 USART,同时开两路的收发中断后 USART_Enable;
中断里 USARTy_IRQHandler 用 USART_ReceiveData/USART_SendData 在 RxBuffer1、TxBuffer1 间搬运,USARTz_IRQHandler 对 RxBuffer2、TxBuffer2 同理,计数到达后关闭对应中断;
主循环用两个 while 等到收齐,最后 Buffercmp 比较写入 TransferStatus1、TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_ConfigInt、USART_Enable、USART_GetFlagStatus、USART_GetIntStatus、USART_ReceiveData、USART_SendData
验证方法 复位运行后依次查看变量 TransferStatus1 与 TransferStatus2,两者均为 PASSED 表示两个方向都正确,任一为 FAILED 表示对应方向出错。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
外部连接 USART1_TX(PA9) 接 USART2_RX(PA3)、USART1_RX(PA10) 接 USART2_TX(PA2)。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例把两路的收发全部交给中断,主循环只等两个方向的接收计数到位。

数据路径: main → USART_ConfigInt 开两路收发中断 → USARTy_IRQHandler/USARTz_IRQHandler 用 USART_ReceiveData/USART_SendData 在 TxBuffer 与 RxBuffer 间搬运 → 主循环等两个接收计数收齐 → Buffercmp 把结果写入两个状态变量

1. 同时打开收发中断并启动。 两路各自打开接收与发送中断,让收发都由中断服务函数处理。

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

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

收发计数上限变化时功能不受影响,但接收中断关闭条件依赖计数上限,要同步核对。

2. USART1 中断完成收发搬运。 USARTy_IRQHandler 收到字节写入 RxBuffer1,收够后关接收中断;发送中断触发时从 TxBuffer1 取字节,发完关发送中断。

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

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

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

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

接收与发送的边界分别来自 NbrOfDataToRead1 与 NbrOfDataToTransfer1,改动缓冲区长度时要同步改这两个上限。

3. USART2 中断完成收发搬运。 USARTz_IRQHandler 对 RxBuffer2、TxBuffer2 做同样的收发处理,两个方向互为镜像。

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

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

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

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

两个中断服务函数的边界宏分别对应各自的缓冲区与计数上限,改动一侧要保持另一侧同步。

4. 主循环收齐后比较。 主循环分别等待两个方向的接收计数达到缓冲长度,再比较两组收发结果。

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

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

等待条件是接收缓冲长度,改动发送缓冲内容长度时要同步改这个长度,否则主循环会一直等待。

对应源码: src/main.c、src/n32g41x_it.c

5.21.3.9 LP_Transmit_Receive IrDA 低功耗模式收发

工程目录: projects/n32g41x_EVAL/examples/USART/IrDA_Tx&Rx/LP_Transmit_Receive/

使能 GPIO 与两路 USART 时钟并配置 USART2 中断→配置 USART1 的 TX、USART2 的 RX 引脚为复用推挽→用 GTIM3 在 PB4 上产生 38 kHz 载波→配置两路 IrDA 为低功耗模式并设置预分频值→USART1 逐字节发送,USART2 中断接收进缓冲→收齐后比较收发缓冲并写入状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)以串行 IrDA 低功耗模式通信:USART1 逐字节发送 TxBuffer1,USART2 通过中断接收进 RxBuffer1,并用 GTIM3 在 PB4 上产生 38 kHz 载波。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
src/bsp_Tim_38K.c、inc/bsp_Tim_38K.h:GTIM3 与 PB4 的 38 kHz 载波初始化;
inc/main.h:USARTy/USARTz 的 USART、引脚与 IRQn 宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9),USARTz 为 USART2(Rx PA3);
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0;
波特率 600、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样;
USARTy 为 USART_MODE_TX、USARTz 为 USART_MODE_RX;
USART_SetPrescaler(USARTy, 250)、USART_SetPrescaler(USARTz, 125),USART_ConfigIrDAMode 设为 USART_IRDAMODE_LOWPPWER 并 USART_EnableIrDA;
RCC_ConfigPclk1、RCC_ConfigPclk2 均分频到 8,系统时钟为 HSI 16 MHz;
载波由 GTIM3 在 PB4 输出。
程序与数据路径 main 配置时钟、NVIC、GPIO,调用 ADVANCE_TIM_Init 起 38 kHz 载波,用 USART_Init 把两路配成一发一收,开 USART2 接收中断后 USART_Enable;
主循环用 USART_SendData 逐字节发送并等 USART_FLAG_TXDE 与 USART2 收齐;
USARTz_IRQHandler 用 USART_ReceiveData 写入 RxBuffer1,最后 Buffercmp 比较写入 TransferStatus。
主要 API USART_StructInit、USART_Init、USART_ConfigInt、USART_SetPrescaler、USART_ConfigIrDAMode、USART_EnableIrDA、USART_Enable、USART_GetFlagStatus、USART_SendData、USART_ReceiveData、NVIC_Init
验证方法 复位运行后查看变量 TransferStatus,为 PASSED 表示红外接收数据与发送一致,为 FAILED 表示不一致。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
USART1 的 Tx(PA9) 接红外发射端、USART2 的 Rx(PA3) 接红外接收端,PB4 输出 38 kHz 载波;
低功耗模式预分频后的时钟需落在手册规定的范围内。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用 IrDA 低功耗模式和一路 38 kHz 载波把串行数据以红外方式从 USART1 送到 USART2。

数据路径: main → ADVANCE_TIM_Init 在 PB4 输出 38 kHz 载波 → USART_ConfigIrDAMode(USART_IRDAMODE_LOWPPWER) 与 USART_SetPrescaler 配置 IrDA 低功耗模式 → USART_SendData 发 TxBuffer1 → USARTz_IRQHandler 收进 RxBuffer1 → Buffercmp 把结果写入 TransferStatus

1. 设置预分频并选低功耗模式。 发送端与接收端分别设不同的预分频值,再各自选低功耗 IrDA 模式并打开 IrDA,最后才启动 USART。

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

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

低功耗模式下脉冲宽度由预分频值决定,两端的预分频要按手册规定的时钟范围成对设置。

2. 配置 38 kHz 载波定时器。 GTIM3 以计数器周期和预分频值决定更新频率,时基初始化后清标志、开更新中断并使能定时器。

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

周期与预分频值定义在 BSP 头文件里,改动载波频率时要同步改这两个宏并保持一致的分频关系。

3. 定时器中断翻转载波脚。 更新中断里清标志并翻转 PB4,用软件翻转产生载波方波。

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

载波脚与翻转宏由 BSP 头文件定义,移植到别的板卡时要同步改端口与引脚宏。

4. 接收中断把字节写进缓冲。 USARTz_IRQHandler 收到字节写入 RxBuffer1,收够后关接收中断,并检查差错标志后读状态寄存器与数据寄存器把错误清除。

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

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

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

接收计数上限取的是 NbrOfDataToRead1,它等于缓冲区长度,改动发送内容长度时要同步改这个长度。

5. 发送并在收齐后比较。 主循环按串口空标志逐字节发送 TxBuffer1,发送结束后等待接收计数收齐,再比较收发缓冲。

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

此例的长度宏取 countof 未减 1,收发的字节数包含末尾空字符,移植时要与自己的缓冲定义口径一致。

对应源码: src/main.c、src/bsp_Tim_38K.c、src/n32g41x_it.c

5.21.3.10 Transmit_Receive IrDA 正常模式收发

工程目录: projects/n32g41x_EVAL/examples/USART/IrDA_Tx&Rx/Transmit_Receive/

使能 GPIO 与两路 USART 时钟并配置 USART2 中断→配置 USART1 的 TX、USART2 的 RX 引脚为复用推挽→用 GTIM3 在 PB4 上产生 38 kHz 载波→配置两路 IrDA 为正常模式,预分频值为 1→USART1 逐字节发送,USART2 中断接收进缓冲→收齐后比较收发缓冲并写入状态变量

项目 源码解读
功能 演示 USART1(USARTy)与 USART2(USARTz)以串行 IrDA 正常模式通信:USART1 逐字节发送 TxBuffer1,USART2 通过中断接收进 RxBuffer1,并用 GTIM3 在 PB4 上产生 38 kHz 载波。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
src/bsp_Tim_38K.c、inc/bsp_Tim_38K.h:GTIM3 与 PB4 的 38 kHz 载波初始化;
inc/main.h:USARTy/USARTz 的 USART、引脚与 IRQn 宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTz_IRQHandler 等中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1(Tx PA9),USARTz 为 USART2(Rx PA3);
PA9 在 N32G412 复用为 GPIO_AF4、N32G415 为 GPIO_AF0;
波特率 1200、8 数据位、1 停止位、无校验、无硬件流控、16 倍过采样;
USARTy 为 USART_MODE_TX、USARTz 为 USART_MODE_RX;
USART_SetPrescaler 两路均取 0x01,USART_ConfigIrDAMode 设为 USART_IRDAMODE_NORMAL 并 USART_EnableIrDA;
RCC_ConfigPclk1、RCC_ConfigPclk2 均分频到 4,系统时钟 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz;
载波由 GTIM3 在 PB4 输出。
程序与数据路径 main 配置时钟、NVIC、GPIO,调用 ADVANCE_TIM_Init 起 38 kHz 载波,用 USART_Init 把两路配成一发一收,开 USART2 接收中断后 USART_Enable;
主循环用 USART_SendData 逐字节发送并等 USART_FLAG_TXDE 与 USART2 收齐;
USARTz_IRQHandler 用 USART_ReceiveData 写入 RxBuffer1,最后 Buffercmp 比较写入 TransferStatus。
主要 API USART_StructInit、USART_Init、USART_ConfigInt、USART_SetPrescaler、USART_ConfigIrDAMode、USART_EnableIrDA、USART_Enable、USART_GetFlagStatus、USART_SendData、USART_ReceiveData、NVIC_Init
验证方法 复位运行后查看变量 TransferStatus,为 PASSED 表示红外接收数据与发送一致,为 FAILED 表示不一致。
使用提示 需先断开 NS-LINK 的 MCU_TX、MCU_RX 跳线帽;
USART1 的 Tx(PA9) 接红外发射端、USART2 的 Rx(PA3) 接红外接收端,PB4 输出 38 kHz 载波;
正常模式下预分频值取 1。
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用 IrDA 正常模式和一路 38 kHz 载波把串行数据以红外方式从 USART1 送到 USART2。

数据路径: main → ADVANCE_TIM_Init 在 PB4 输出 38 kHz 载波 → USART_ConfigIrDAMode(USART_IRDAMODE_NORMAL) 与 USART_SetPrescaler(USARTy, 0x01)、USART_SetPrescaler(USARTz, 0x01) 配置 IrDA 正常模式 → USART_SendData 发 TxBuffer1 → USARTz_IRQHandler 收进 RxBuffer1 → Buffercmp 把结果写入 TransferStatus

1. 设置预分频并选正常模式。 两端预分频都取 1,选正常 IrDA 模式并打开 IrDA,正常模式下脉冲宽度固定、预分频只作为分频系数。

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

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

正常模式下预分频值固定为 1,改动时要确认仍符合该模式对分频的要求。

2. 配置发送与接收的引脚。 发送脚用复用推挽并提高输出能力,接收脚用复用推挽上拉,为红外发射端提供足够的驱动电流。

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

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

发送脚驱动能力不足会导致红外发射距离变短,移植时要按实际发射管核对输出能力设置。

3. 配置 38 kHz 载波定时器。 GTIM3 使能时钟后按时基结构初始化,周期与预分频值决定更新频率,随后清标志、开更新中断并使能定时器。

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

周期与预分频值定义在 BSP 头文件里,改动载波频率时要同步改这两个宏并保持一致的分频关系。

4. 接收中断把字节写进缓冲。 USARTz_IRQHandler 收到字节写入 RxBuffer1,收够后关接收中断,并检查差错标志后读状态寄存器与数据寄存器把错误清除。

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

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

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

接收计数上限取的是 NbrOfDataToRead1,它等于缓冲区长度,改动发送内容长度时要同步改这个长度。

5. 发送并在收齐后比较。 主循环按串口空标志逐字节发送 TxBuffer1,发送结束后等待接收计数收齐,再比较收发缓冲。

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

此例的长度宏取 countof 未减 1,收发的字节数包含末尾空字符,移植时要与自己的缓冲定义口径一致。

对应源码: src/main.c、src/bsp_Tim_38K.c、src/n32g41x_it.c

5.21.3.11 LIN_Master LIN 主机

工程目录: projects/n32g41x_EVAL/examples/USART/LIN/LIN_Master/

初始化打印串口与时钟→配置 USART2 为 LIN 主模式→发送主请求帧与从应答帧请求→接收从机回帧并校验校验和→打印 8 字节接收数据

项目 源码解读
功能 演示 USART 的 LIN 模式作为主节点轮询工作:先向从机发送 8 字节 0x0F 的主请求帧,再发出从应答帧请求,随后接收从机回发的 8 字节数据并校验。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Memset、Buffercopy;
src/lin_master.c:帧处理与收发流程(SetFramePID、SetFrameChecksum、WaitFrameRes、TestLinMaster 等);
inc/main.h、inc/lin_master.h:USART2 与引脚宏、M_LIN_EX_MSG 消息结构;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTx 为 USART2,TX 为 PA2、RX 为 PA3,复用号均为 GPIO_AF4,时钟 RCC_APB1_PERIPH_USART2;
波特率 9600,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,流控 USART_HFCTRL_NONE,过采样 USART_16OVER,收发使能;
USART_ConfigLINBreakDetectLength 设为 USART_LINBDL_10B。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 先 log_init 并打印 test LIN master mode,依次 RCC_Configuration、NVIC_Configuration、GPIO_Configuration,用 USART_Init 配置 USART2、USART_EnableLIN 打开 LIN 模式后 USART_Enable;
while(1) 每 500 ms 调用 TestLinMaster:发出断开帧与主请求帧后延时 20 ms,最多 4 次发出应答帧请求,并用 WaitFrameRes 接收从机 8 字节回帧、比较校验和,成功则逐字节打印。
主要 API USART_StructInit、USART_Init、USART_ConfigLINBreakDetectLength、USART_EnableLIN、USART_Enable、USART_SendBreak、USART_SendData、USART_GetFlagStatus、USART_ReceiveData
验证方法 两块评估板分别下载 LIN_Master 与 LIN_Slave,先复位从机再复位主机;
主机串口打印 test LIN master mode,请求帧阶段打印 SetFrameMsg ID:0x3c 与 PID 校验位信息,从机回帧校验通过后按 recv_data[0] 至 recv_data[7] 打印 8 字节数据,从机无响应时打印 slave no response!!。
使用提示 需先断开评估板 NS-LINK 的 MCU_TX 与 MCU_RX 跳线帽;
USART2 的 TX(PA2)与 RX(PA3)要与从机交叉连接;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例把一次 LIN 传输拆成同步、PID、数据与校验四段,由一组帧处理函数逐段构造,再借 USART 的断开帧机制启动。

数据路径: main → USART_Init 与 USART_EnableLIN 配置 USART2 → TestLinMaster → TestMasterReqFrame/TestSlaveResFrame 填 M_LIN_EX_MSG → FrameHandle 调 SetFramePID 与 SetFrameChecksum → MasterSendFrame/MasterSendBytes 先发断开帧再发字节 → WaitFrameRes 接收并比较校验和

1. 用宏选定 USART2 与两只引脚。 inc/main.h 把串口、时钟、TX/RX 引脚与复用号集中定义为一组宏,收发代码只引用宏名。

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

换用其它串口或引脚时只改这组宏;该引脚在 N32G412 与 N32G415 上复用号相同,无需条件编译。

2. 配置 USART2 并打开 LIN 模式。 main 逐项填入波特率、字长、停止位、校验、流控、过采样与收发模式,USART_Init 写入后再选断开检测长度并打开 LIN 模式。

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

改动波特率、字长或引脚时要同步核对 main.h 的宏与从机侧同项配置,两端不一致时从机不会响应。

3. 计算校验和并发送断开帧。 MasterGetCheckSum 按 LIN 经典校验和算法累加各字节,MasterSendBytes 在发送数据前先产生断开帧,再用发送完成标志同步每个字节。

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

断开检测长度(10 位)与校验和类型要与从机一致;MasterSendBytes 每次调用都会先发一个断开帧,长度取决于调用的数据段。

4. 构造请求帧与应答帧、等待回帧。 TestMasterReqFrame 填 8 字节 0x0F 与 PID 0x3C,FrameHandle 先用原始 PID 判断帧类型,再换算 PID、算校验和并发送。

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

FrameHandle 用换算前的原始 PID(0x3C/0x3D)选择帧类型,这两个常量要与从机 IDType 表里的 MSG_RECEIVE_ID 与 MSG_SEND_ID 成对匹配,改 ID 时两处一起改。

对应源码: src/main.c、src/lin_master.c、inc/main.h、inc/lin_master.h

5.21.3.12 LIN_Slave LIN 从机

工程目录: projects/n32g41x_EVAL/examples/USART/LIN/LIN_Slave/

初始化打印串口与时钟→配置 USART2 为 LIN 从模式并开启接收与断开检测中断→状态机依次接收同步字节、PID 与数据→收到完整帧后置位标志→主循环执行命令并回发 8 字节应答

项目 源码解读
功能 演示 USART 的 LIN 模式作为从节点:接收主机发来的请求帧(0x3C)与从应答帧请求(0x3D),按主机请求把数据改成 8 字节 0x01 回发并打印过程。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、LIN_RespData;
src/lin_slave.c:LIN_IRQHandler、LIN_EX_RxAsync 状态机与收发流程函数;
inc/main.h、inc/lin_slave.h:USART2 与引脚宏、LIN_EX_MSG 结构、GET_PID 与 ID 类型宏;
src/n32g41x_it.c、inc/n32g41x_it.h:USARTx_IRQHandler 调用 LIN_IRQHandler;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTx 为 USART2,TX 为 PA2、RX 为 PA3,复用号均为 GPIO_AF4;
波特率 9600,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,流控 USART_HFCTRL_NONE,过采样 USART_16OVER,收发使能;
USART_ConfigLINBreakDetectLength 设为 USART_LINBDL_10B,并使能 USART_INT_RXDNE 与 USART_INT_LINBD 中断;
IDType[0x3C] 置 ID_TYPE_SR、IDType[0x3D] 置 ID_TYPE_SW。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 先 log_init 并打印 test LIN slave mode,执行 RCC_Configuration、NVIC_Configuration、GPIO_Configuration 后配置并打开 USART2 的 LIN 模式、接收与断开检测中断;
随后设置 IDType 表并进入 while(1),GotMsgFlag 置位时调用 BOOT_ExecutiveCommand(pLINMsg->Data, LIN_RespData),把收到数据的每个字节改成 0x01 后由 LIN_SetResp 计算校验和、LIN_SendBytes 回发,最后清零 GotMsgFlag。
主要 API USART_StructInit、USART_Init、USART_ConfigLINBreakDetectLength、USART_EnableLIN、USART_ConfigInt、USART_Enable、USART_GetFlagStatus、USART_ClrFlag、USART_ReceiveData、USART_SendData
验证方法 与 LIN_Master 配合,先复位从机再复位主机;
从机串口打印 test LIN slave mode,收到数据后打印 BOOT_ExecutiveCommand 及 pData[0] 至 pData[7] 的十六进制值,并回发 8 字节 0x01 供主机校验。
使用提示 需先断开评估板 NS-LINK 的 MCU_TX 与 MCU_RX 跳线帽;
USART2 的 TX(PA2)与 RX(PA3)要与主机交叉连接;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

从机把接收过程做成一个四态状态机,由断开检测中断复位状态,再由接收中断逐字节推进,凑齐一帧后交给主循环回发。

数据路径: 主机断开帧 → USART2 断开检测中断 → LIN_IRQHandler 置 LinRxState=SYNCH → LIN_EX_RxAsync 依次收 0x55、PID、8 字节数据与校验 → 置位 GotMsgFlag 并记录 pLINMsg → main 调 BOOT_ExecutiveCommand 把数据改为 0x01 → LIN_RespData/LIN_SetResp 算校验和 → LIN_SendBytes 回发

1. 配置从机并使能接收与断开检测中断。 main 打开 LIN 模式后进行两处配置:使能 USART_INT_RXDNE 与 USART_INT_LINBD 两个中断,并把 0x3C、0x3D 两个 ID 分别登记为“收数据”与“发数据”类型。

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

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

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

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

IDType 用 data & 0x3F 作下标,登记关系要与主机发送的 PID 对应,改帧 ID 时两端一起改。

2. 用断开检测中断复位接收状态。 断开帧到来时,LIN_IRQHandler 先判断 USART_FLAG_LINBD,清标志并读 STS、DAT 完成清零序列,再把状态置为 SYNCH 等待同步字节。

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

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

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

USART_FLAG_LINBD 的清零序列(先读 USART_STS 再读 USART_DAT)与 RXDNE 分支的四类错误标志清理都要保留,换串口时把 USARTx 换成对应宏。

3. 四态状态机逐字节收帧。 LIN_EX_RxAsync 在 SYNCH 校验 0x55,在 ID_LEN 校验 PID 并按 IDType 决定收数据还是立即应答,在 DATA_GET 收满 8 字节后进入 CHECKSUM,凑齐后置位 GotMsgFlag。

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

数据段固定收 8 字节(DataLen >= 8 才进入校验),双缓冲下标 LINRxDataIndex 在 0 与 1 间轮转;改数据长度时这里与 LIN_EX_MSG.Data 数组大小要一起改。

4. 执行命令并用校验和回发应答帧。 主循环收到 GotMsgFlag 后调用 BOOT_ExecutiveCommand,它把 8 字节数据改成 0x01 并通过回调 LIN_RespData 交给 LIN_SetResp;LIN_SetResp 换算 PID、按经典算法算校验和并写入待发消息。

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

LIN_SetResp 的 CheckType 为 0 时只对数据段算校验和,GET_PID 宏负责补两个奇偶校验位;改校验和类型或 ID 时,要与主机的换算保持一致。

对应源码: src/main.c、src/lin_slave.c、inc/main.h、inc/lin_slave.h

5.21.3.13 MultiProcessor 多处理器地址唤醒

工程目录: projects/n32g41x_EVAL/examples/USART/MultiProcessor/

配置两路 USART 与地址标识唤醒→USART1 持续向 USART2 发送一字节→USART2 收到后点亮 LED1→按下 KEY1 让 USART2 进入静默模式→再次按下 KEY1 由 USART1 发地址帧唤醒

项目 源码解读
功能 演示多处理器通信的地址标识唤醒:USART1 作发送端持续发 0x33,USART2 作接收端收到后点亮 LED1;
按 KEY1 让 USART2 进入静默模式(LED1 停止翻转),再按一次由 USART1 发地址匹配帧 0x102 唤醒。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、Delay;
src/bsp_led.c、inc/bsp_led.h:LED1(PA8)定义与 LED_GPIO_Config;
src/bsp_key.c、inc/bsp_key.h:KEY1(PA4)EXTI 配置;
src/n32g41x_it.c、inc/n32g41x_it.h:EXTI4_IRQHandler 与其它中断服务函数;
inc/main.h:USART1/USART2 与引脚宏;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USART1 为 USARTy,TX 为 PA9、RX 为 PA10;
USART2 为 USARTz,TX 为 PA2、RX 为 PA3,复用号均 GPIO_AF4;
波特率 115200,字长 USART_WL_9B,停止位 USART_STPB_1,校验 USART_PE_NO,流控 USART_HFCTRL_NONE,过采样 USART_16OVER;
USART_SetAddr 设 USARTy 地址 0x1、USARTz 地址 0x2,USART_ConfigWakeUpMode 设 USARTz 为 USART_WUM_ADDRMASK。
KEY1 为 PA4 上拉输入、EXTI4 下降沿触发;
LED1 为 PA8 推挽输出。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 依次 RCC_Configuration、GPIO_Configuration、LED_GPIO_Config、EXTI_Key_Config,用同一 USART_InitType 配置并打开 USARTy 与 USARTz,再设地址与唤醒方式;
while(1) 中 USARTy 用 USART_SendData 发 0x33 并等 USART_FLAG_TXDE,当 USARTz USART_FLAG_RXDNE 置位且收到的字节为 0x33 时,通过 LED1_ON/LED1_OFF 配合 Delay 使 LED1 亮灭。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_SetAddr、USART_ConfigWakeUpMode、USART_EnableRcvWakeUp、USART_SendData、USART_GetFlagStatus、USART_ReceiveData
验证方法 运行后 LED1 持续闪烁;
按 KEY1 后 USARTz 进入静默模式,LED1 停止闪烁;
再按一次 KEY1,USARTy 发出地址帧 0x102,USARTz 被唤醒,LED1 恢复闪烁。
使用提示 需先断开评估板 NS-LINK 的 MCU_TX 与 MCU_RX 跳线帽;
用一根导线把 USARTy 的 TX(PA9)接到 USARTz 的 RX(PA3);
PA9 的复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF0,代码已按型号区分;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用两路 USART 演示地址标识唤醒:发送端按固定地址匹配帧唤醒接收端,接收端由按键中断在正常运行与静默之间切换。

数据路径: USARTy(PA9)发送 0x33 → USARTz(PA3)接收 → USART_FLAG_RXDNE 置位且数据为 0x33 → LED1_ON/LED1_OFF 改变 PA8 电平 → 按键 KEY1(PA4)→ EXTI4_IRQHandler 切换 ControlFlag → USART_EnableRcvWakeUp 进静默或 USART_SendData(USARTy, 0x102) 唤醒

1. 用宏选定两路串口与引脚。 inc/main.h 把两个串口、时钟、TX/RX 引脚与复用号集中定义,并对 PA9 的复用号按型号分支。

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

#define USARTy_APBxClkCmd RCC_EnableAPB2PeriphClk

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

USARTy_Tx_GPIO_AF 用 #if defined(N32G412)/#elif defined(N32G415) 区分;换串口或引脚时改这组宏,端口配置代码不用动。

2. 配置两路串口、地址与唤醒方式。 main 用同一结构体初始化 USARTy 与 USARTz,使能后分别设置地址 0x1 与 0x2,并把 USARTz 的唤醒方式设为地址标识。

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

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

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

地址匹配时数据为 9 位(USART_WL_9B),最高位为 1 表示地址帧;USART_WUM_ADDRMASK 决定由地址标识而非空闲帧唤醒。

3. 主循环收发并用 LED 反馈。 USARTy 每轮发一个 0x33 并等发送缓冲区空,USARTz 收到 0x33 时翻转一次 LED1 的亮灭。

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

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

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

Delay 的参数决定 LED 亮灭节奏;USARTz 的接收数据要与发送端约定的内容(0x33)一致,改数据时两端一起改。

4. 按键中断切换静默与唤醒。 每次按下 KEY1 触发一次 EXTI4_IRQHandler:ControlFlag 为 0 时清空接收数据并把 USARTz 置入静默,为 1 时由 USARTy 发地址帧 0x102,为 2 时复位标志。

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

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

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

        ControlFlag = 2;
    }

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

进入静默前先读走 USART_DAT 清 RXDNE,否则 USART_EnableRcvWakeUp 的写操作会被忽略;唤醒地址 0x102 的低 4 位要等于 USARTz 的 USART_SetAddr 设定值。

对应源码: src/main.c、src/n32g41x_it.c、inc/main.h、inc/bsp_led.h、inc/bsp_key.h

5.21.3.14 Polling 查询收发

工程目录: projects/n32g41x_EVAL/examples/USART/Polling/

配置 USART1 与 USART2 为查询收发→USART1 逐字节发送缓冲区 1 到 USART2→USART2 接收存到接收缓冲区 2→USART2 反向发送缓冲区 2 到 USART1→比较收发缓冲区得到两个状态变量

项目 源码解读
功能 演示 USARTy(USART1)与 USARTz(USART2)之间用查询标志位实现的双向通信:先把 USARTy 的缓冲区发到 USARTz,再由 USARTz 反向发回,最后比较收发缓冲区得到两个状态量。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、Buffercmp 与收发缓冲区、计数器;
inc/main.h:USART1/USART2 与引脚、时钟宏、TestStatus;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1,TX 为 PA9、RX 为 PA10,TX 复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF0;
USARTz 为 USART2,TX 为 PA2、RX 为 PA3,复用号均 GPIO_AF4;
波特率 115200,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,过采样 USART_16OVER,收发使能;
四个 USART 引脚均配为复用推挽并上拉。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 先 RCC_Configuration、GPIO_Configuration,用 USART_Init 配置并 USART_Enable 两路串口;
第一个 while 中 USARTy 逐字节 USART_SendData 发 TxBuffer1,USARTz 等 USART_FLAG_RXDNE 后 USART_ReceiveData 存入 RxBuffer2;
第二个 while 把 TxBuffer2 从 USARTz 发到 USARTy 存入 RxBuffer1;
最后用 Buffercmp 比较得到 TransferStatus1 与 TransferStatus2。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_SendData、USART_GetFlagStatus、USART_ReceiveData
验证方法 编译下载、复位运行后,在调试器中依次查看 TransferStatus1 与 TransferStatus2;
两者均为 PASSED 表示收发数据一致,FAILED 表示不一致。
使用提示 需先断开评估板 NS-LINK 的 MCU_TX 与 MCU_RX 跳线帽;
用导线把 USART1_TX(PA9)接 USART2_RX(PA3)、USART1_RX(PA10)接 USART2_TX(PA2)交叉连接;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例不做中断,而是用收发标志位轮询把两路串口串成一次问答,再把两个方向的接收缓冲区与发送缓冲区逐一比较。

数据路径: TxBuffer1 → USART_SendData(USARTy) → PA9 到 PA3 → USART_ReceiveData(USARTz) → RxBuffer2 → TxBuffer2 → USART_SendData(USARTz) → PA2 到 PA10 → USART_ReceiveData(USARTy) → RxBuffer1 → Buffercmp 得到 TransferStatus1、TransferStatus2

1. 配置并打开两路串口。 main 用同一结构体填入参数后分别配置并使能 USARTy 与 USARTz。

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

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

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

两路使用同一组参数,改波特率或字长时两处同时生效;引脚与复用号在 main.h 的宏里定义。

2. 查询方式的第一段收发。 USARTy 每发一字节就等 USART_FLAG_TXDE,再等 USARTz 的 USART_FLAG_RXDNE 后读出,循环直到发完缓冲区。

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

接收等待循环里检查 USART_FLAG_OREF 并读走 USARTz->DAT,用于在溢出时丢弃数据避免卡死;改缓冲区长度时 TxBufferSize1 由数组长度自动得出。

3. 查询方式的第二段收发(反向)。 交换收发角色后,USARTz 逐字节发送 TxBuffer2,USARTy 接收存入 RxBuffer1。

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

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

RxCounter1 在两段循环里分别累加,注意两段比较用的缓冲区是 RxBuffer2 与 RxBuffer1,不要对调。

4. 比较收发缓冲区。 Buffercmp 逐字节比较两个缓冲区,全部相同返回 PASSED,否则返回 FAILED。

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

    return PASSED;
}

比较长度取自发送缓冲区长度宏;增加数据量时要同步 TxBuffer1、TxBuffer2 及对应接收缓冲区的大小。

对应源码: src/main.c、inc/main.h

5.21.3.15 Printf 串口 printf

工程目录: projects/n32g41x_EVAL/examples/USART/Printf/

初始化 USART1 与 TX 引脚→打开 USART1→打印一行字符串→进入空循环

项目 源码解读
功能 演示把标准输出重定向到 USART1:初始化一路串口后调用 printf 打印一行字符串,用于最小可用的串口调试输出。
源码组成 src/main.c:main、GPIO_Configuration、RCC_Configuration 与打印语句;
inc/main.h:按芯片型号条件编译的 USARTx、引脚与复用宏;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTx 为 USART1,时钟 RCC_APB2_PERIPH_USART1;
只配置 TX 引脚 PA9,配为复用推挽并上拉,复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF0;
波特率 115200,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,流控 USART_HFCTRL_NONE,过采样 USART_16OVER,收发使能。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 先 RCC_Configuration、GPIO_Configuration,填好 USART_InitType 后由 USART_Init 配置 USART1、USART_Enable 使能,随后调用 printf("This is a Printf demo of USART.\n\r") 输出,再进入空 while(1)。
主要 API USART_StructInit、USART_Init、USART_Enable
验证方法 编译下载、复位运行后,在 PC 端串口工具以 115200-8-N-1 接收,可看到一行 This is a Printf demo of USART.。
使用提示 该示例未在 readme 中要求断开跳线帽;
串口接收端接 USART1_TX(PA9);
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用型号相关的宏选出串口与 TX 引脚,只配置发送方向,再靠标准库的 printf 输出一行字符串。

数据路径: main → RCC_Configuration 打开 GPIO 与 USART1 时钟 → GPIO_Configuration 把 PA9 配为复用推挽 → USART_Init 写入 USARTx 参数 → USART_Enable → printf 经 USART1 的 TX 发出

1. 按型号选择串口与引脚宏。 inc/main.h 用 #if defined(N32G412)/#elif defined(N32G415) 给出 USARTx、时钟、TX/RX 引脚与复用号,二者仅 TX 复用号不同。

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

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

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

换到其它串口或引脚时改这组宏;两个分支都定义了 USARTx、USARTx_CLK、USARTx_TX_PIN 等,缺一不可。

2. 只配置 TX 引脚为复用推挽。 GPIO_Configuration 把 USARTx_TX_PIN 配为 GPIO_MODE_AF_PP、上拉并写入复用号,没有配置 RX。

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

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

示例只用于发送,未配置 RX 引脚;若需要接收,要按同样方式补上 RX 引脚的配置。

3. 配置 USART1 并打印。 main 填入 115200-8-N-1 与 16 倍过采样后使能串口,再调用 printf 输出。

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

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

printf 的重定向由工程里的标准库支持实现;改波特率时 PC 端串口工具要同步设置,否则收到乱码。

对应源码: src/main.c、inc/main.h

5.21.3.16 SmartCard 智能卡 T=0 通信

工程目录: projects/n32g41x_EVAL/examples/USART/SmartCard/

初始化系统与日志→配置 USART3 智能卡模式与复位引脚→上电并等待卡复位应答→解析 ATR 并协商 PPS→用 DMA 收发 APDU 命令→打印随机数结果并热复位

项目 源码解读
功能 演示 USART3 的 SmartCard 模式按 T=0 协议与 PSAM 卡交互:接收 ATR、协商 PPS、执行获取随机数 APDU 命令,并支持热复位;
PPS 阶段用 DMA 加速数据收发。
源码组成 src/main.c:main、System_Init、Test_PSAM、SC__HotReset_handle;
src/smartcard.c:SC_Init、SC_Handler 状态机与 SC_PPSConfig、SC_SendData 等收发流程函数;
inc/main.h;
inc/smartcard.h:USART3/DMA/复位引脚宏、SC_ADPU_* 与 SC_ATR 等结构;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 SC_USART 为 USART3,TX 为 PB10、CK 为 PB12,复用号均 GPIO_AF0,时钟 RCC_APB1_PERIPH_USART3;
复位脚为 PB9 推挽输出;
智能卡模式下字长 USART_WL_9B、停止位 USART_STPB_1_5、偶校验 USART_PE_EVEN、16 倍过采样、无流控,时钟 USART_CLK_ENABLE、极性 USART_CLKPOL_LOW、相位 USART_CLKPHA_1EDGE、末位时钟 USART_CLKLB_ENABLE,USART_SetPrescaler 与 USART_SetGuardTime 由 SC_InitCfg 传入;
DMA 用 DMA_CH5(TX)与 DMA_CH6(RX)。
调试输出为 USART1(PA9/PA10)115200。
程序与数据路径 main 先 System_Init,再 Test_PSAM 进入 SC_Handler 状态机:SC_POWER_ON→SC_RESET_LOW 由 SC_AnswerReq 拉低再拉高复位脚并收 ATR,SC_ACTIVE 由 SC_decode_Answer2reset 解析,SC_ACTIVE_ON_T0 调 SC_SendData 发 APDU;
随后调用 SC_PPSConfig 协商速率,发获取随机数命令并打印返回的 SC_Responce.Data、SW1、SW2,最后 SC__HotReset_handle 与 SC_DeInit。
主要 API USART_Init、USART_Enable、USART_EnableSmartCard、USART_SetSmartCardNACK、USART_SetGuardTime、USART_SetPrescaler、USART_ClockInit、USART_ConfigInt、USART_EnableDMA、USART_SendData、USART_ReceiveData、DMA_Init、DMA_EnableChannel
验证方法 连接 PSAM 智能卡模块后运行,USART1 以 115200 输出调试信息:打印 Pclk1、Clk_Div 与波特率,ATR 各字节、PTS Confirm success 与协商后波特率,获取随机数成功时打印 get rand success! 和随机数字节,SW1、SW2 为 0x90、0x00。
使用提示 SmartCard 使用 USART3(APB1),调试输出使用 USART1(APB2);
把 PB10(TX)接卡 I/O、PB12(CK)接卡 CLK、PB9 接卡 RST;
时钟分频值 Clk_Div 需根据实际 PCLK1 频率调整以满足卡要求;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例把智能卡交互组织成分层结构:SC_Init 配置底层时钟与帧格式,状态机负责复位与 ATR,PPS 阶段用 DMA 完成速率协商。

数据路径: main → SC_Handler 状态机 → SC_Init 配置 USART3 与 CK/PB9 → SC_AnswerReq 收 ATR → SC_decode_Answer2reset 解析 → SC_PPSConfig 用 DMA_CH5/DMA_CH6 协商速率 → SC_SendData 发 APDU 并收 SC_Responce

1. 用宏选定智能卡串口、引脚与 DMA。 inc/smartcard.h 把 USART3、CK/TX 引脚、复位脚与 TX/RX 的 DMA 通道、标志、重映射集中定义。

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

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

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

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

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

TX 用 DMA_CH5、RX 用 DMA_CH6,与 DMA_REMAP_USART3_TX/DMA_REMAP_USART3_RX 成对;换串口或 DMA 通道时这几组宏要一起改。

2. 配置智能卡时钟与帧格式。 SC_Init 用 USART_SetPrescaler/USART_SetGuardTime 设 CK 分频与保护时间,配好同步时钟参数后按 SC_InitCfg 选停止位与校验,再由 USART_Init 写入。

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

Clk_Div 先右移一位再写入预分频,实际分频为设定值的两倍;波特率由 Pclk1 与 CK 分频值算出,改 Clk_Div 时波特率随之变化。

3. 使能中断、NACK 与智能卡接口。 SC_Init 末尾使能校验与错误中断,按 NackEn 选择是否发送 NACK,再打开智能卡接口并把复位脚置高。

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

USART_EnableSmartCard 是进入智能卡模式的关键一步;SC_Reset(Bit_SET) 把 PB9 置高,接线时要确认该脚连到卡的 RST。

4. 用 DMA 协商 PPS。 SC_PPSConfig 按卡的 ATR 组装 PPSS/PPS0/PPS1/PCK,经 DMA_Configuration 用 TX/RX 通道收发,比较 4 字节一致后重算波特率并禁用 USART 后重配。

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

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

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

PPS 交换依赖 DMA 传输完成标志轮询;改波特率前要 USART_Enable(DISABLE),因为 USART_BRCF 与过采样位只在 USART 关闭时可写;收到协商失败时示例改走热复位分支。

对应源码: src/main.c、src/smartcard.c、inc/main.h、inc/smartcard.h

5.21.3.17 Synchronous 同步模式与 SPI 从机

工程目录: projects/n32g41x_EVAL/examples/USART/Synchronous/

配置 USART1 同步模式与 SPI1 从机→USART1 发送缓冲区到 SPI1 并接收→SPI1 发送缓冲区并由 USART1 发空字节产生时钟→USART1 接收 SPI1 数据→比较两组收发缓冲区

项目 源码解读
功能 演示 USART1 同步模式(输出时钟)与 SPI1 从机之间的双向通信:USART1 先发数据给 SPI1,再从 SPI1 收数据,最后比较两组收发缓冲区。
源码组成 src/main.c:main、RCC_Configuration、GPIO_Configuration、SPI_Configuration、Buffercmp 与收发缓冲区;
inc/main.h:USART1/SPI1 与引脚、复用宏、TestStatus;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1,TX 为 PA9、RX 为 PA10、CK 为 PA8;
SPIy 为 SPI1,SCK 为 PA5、MISO 为 PA6、MOSI 为 PA7,复用号均 GPIO_AF0;
USART1 配为 115200-8-N-1、16 倍过采样,时钟 USART_CLK_ENABLE、极性 USART_CLKPOL_LOW、相位 USART_CLKPHA_2EDGE、末位时钟 USART_CLKLB_ENABLE;
SPI1 为从模式,SPI_MODE_SLAVE、8 位、SPI_CLKPOL_LOW、SPI_CLKPHA_SECOND_EDGE、SPI_NSS_SOFT、LSB 在前。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 用 USART_ClockInit 配同步时钟、USART_Init 配 USART1 后 USART_Enable;
第一段循环把 TxBuffer1 逐字节发出、等 SPI_I2S_RNE_FLAG 后存入 RxBuffer2;
第二段从 SPI1 发 TxBuffer2,USART1 同时发哑字节 DYMMY_BYTE 产生时钟,等 USART_FLAG_RXDNE 后存入 RxBuffer1;
最后 Buffercmp 比较得到 TransferStatus1、TransferStatus2。
主要 API USART_ClockStructInit、USART_ClockInit、USART_StructInit、USART_Init、USART_Enable、USART_SendData、USART_GetFlagStatus、USART_ReceiveData
验证方法 编译下载、复位运行后,在调试器中依次查看 TransferStatus1 与 TransferStatus2;
两者均为 PASSED 表示双向数据一致,FAILED 表示不一致。
使用提示 需先断开评估板 NS-LINK 的 MCU_TX 与 MCU_RX 跳线帽,并拔除串口打印跳帽;
用导线把 USART1_TX(PA9)接 SPI1_MOSI(PA7)、USART1_RX(PA10)接 SPI1_MISO(PA6)、USART1_CK(PA8)接 SPI1_SCK(PA5);
PA8 的复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF5;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例让 USART1 以同步主模式输出时钟、SPI1 以从模式配合,两个方向都用查询标志位收发,最后比较两组缓冲区。

数据路径: TxBuffer1 → USART_SendData(USARTy) → TX 与 CK 到 SPI1 → SPI_I2S_ReceiveData(SPIy) → RxBuffer2 → SPI_I2S_TransmitData(SPIy) → USART_SendData(USARTy, DYMMY_BYTE) 产生时钟 → USART_ReceiveData(USARTy) → RxBuffer1 → Buffercmp 得到两个 TransferStatus

1. 用宏按型号选定 USART1 与 SPI1 引脚。 inc/main.h 把 USART1 与 SPI1 的引脚、复用号集中定义,其中 USART1 的 TX 与 CK 复用号按型号分支。

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

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

SPIy_MOSIPin 与 USARTy_TxPin、USARTy_ClkPin 与 SPIy_SCKPin 要在硬件上相连;改引脚时这组宏与接线一起改。

2. 配置 USART1 的同步时钟与帧格式。 main 先用 USART_ClockInit 设定极性、相位与末位时钟,再用 USART_Init 写入 115200-8-N-1。

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

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

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

USART_CLKPHA_2EDGE 与 SPI1 的 SPI_CLKPHA_SECOND_EDGE 对应,两边相位要一致;同步模式下 CK 只在发送器被激活并有数据写入时输出时钟。

3. 把 SPI1 配为从模式。 SPI_Configuration 用 SPI_Init 填入从模式、8 位、CPOL/CPHA、软件 NSS 与 LSB 在前,随即使能 SPI1。

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

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

    SPI_I2S_DeInit(SPIy);

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

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

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

SPI1 的 CPOL/CPHA 与首末位顺序要与 USART1 同步参数一致;SPI_NSS_SOFT 表示由软件管理从设备选择。

4. 两个方向查询收发并比较。 第一段 USART1 发、SPI1 收;第二段 SPI1 发、USART1 发哑字节产生时钟后收,最后比较两组缓冲区。

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

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

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

第二段里 USART1 发送的 DYMMY_BYTE 只用于产生 CK 时钟、不是有效数据;收发长度由两组缓冲区长度宏自动得出,改数据量时缓冲区与长度一起改。

对应源码: src/main.c、inc/main.h

5.21.3.18 WakeUpFromSleep 从 SLEEP 唤醒

工程目录: projects/n32g41x_EVAL/examples/USART/WakeUpFromSleep/

配置 USART1 为 115200 收发→每秒发送一个递增字节→收到 0x55 时开接收中断并进 SLEEP→接收中断经 NVIC 唤醒 CPU→关接收中断继续发送

项目 源码解读
功能 演示用 USART1 的 RXDNE 中断把 MCU 从 SLEEP 模式唤醒:正常运行时每秒发送一个递增字节,收到 0x55 后进入 SLEEP,再收到任意数据时被中断唤醒并恢复发送。
源码组成 src/main.c:main、GPIO_Configuration、NVIC_Configuration 与引脚配置;
inc/main.h:USART1 与引脚宏、USART1_IRQn、全局变量声明;
src/n32g41x_it.c、inc/n32g41x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 USARTy 为 USART1,TX 为 PA9、RX 为 PA10,TX 复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF0;
波特率 115200,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,过采样 USART_16OVER,收发使能;
SLEEP 用 PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI) 进入;
USART1 的 NVIC 抢占与子优先级均为 0,中断号为 USART1_IRQn。
系统时钟 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。
程序与数据路径 main 打开 USART1 与 GPIO 时钟,执行 GPIO_Configuration、NVIC_Configuration,用 USART_Init/USART_Enable 配置并使能 USART1;
while(1) 中每 SysTick_Delay_Ms(1000) 后判断:若 USART_FLAG_RXDNE 置位且读回 0x55,则使能 USART_INT_RXDNE 中断并用 PWR_EnterSLEEPMode 进 SLEEP,唤醒后关闭该中断;
否则 USART_SendData 发送 cnt_temp 低 8 位并等 USART_FLAG_TXDE。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_ConfigInt、USART_SendData、USART_GetFlagStatus、USART_ReceiveData
验证方法 复位运行后,串口工具以 115200-8-N-1 每秒收到一个递增字节;
下发 0x55 后打印暂停(进入 SLEEP),再下发任意数据后打印恢复。
使用提示 外部连线 USART1_TX(PA9)接 PC 的 RX、USART1_RX(PA10)接 PC 的 TX;
SLEEP 模式下所有时钟保持运行,唤醒后无需重新配置系统时钟;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例在正常运行与 SLEEP 之间用一个接收标志切换:平时每秒发一个字节,收到约定的 0x55 后打开接收中断并进入 SLEEP,靠该中断把 CPU 唤醒。

数据路径: main → GPIO_Configuration/NVIC_Configuration → USART_Init/USART_Enable → 主循环每 1 s USART_SendData 发递增字节 → 收到 0x55 → USART_ConfigInt(USART_INT_RXDNE, ENABLE) → PWR_EnterSLEEPMode → RXDNE 中断经 NVIC 唤醒 → 关闭中断继续发送

1. 用宏选定 USART1 与引脚。 inc/main.h 把 USART1、时钟、TX/RX 引脚与复用号定义为一组宏,并声明唤醒相关的全局变量。

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

USARTy_Tx_GPIO_AF 按型号区分;USARTy_IRQn 与中断向量表里的 USART1 中断号对齐,换串口时要一并改。

2. 配置 USART1 为 115200-8-N-1。 main 填入固定参数后由 USART_Init 配置并 USART_Enable 使能。

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

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

串口参数固定为 115200-8-N-1;改波特率时 PC 端串口工具要同步设置。

3. 主循环收发与进出 SLEEP。 每 1 s 处理一次:收到 0x55 就开接收中断并进 SLEEP,否则发送一个递增字节。

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

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

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

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

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

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

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

进 SLEEP 用 PWR_SLEEP_NOW 与 WFI,任何 NVIC 中断都能唤醒;若改为其它低功耗模式,唤醒后可能需重新配置系统时钟,这里的 SLEEP 不需要。

对应源码: src/main.c、inc/main.h

5.21.3.19 WakeUpFromStop 从 STOP 唤醒

工程目录: projects/n32g41x_EVAL/examples/USART/WakeUpFromStop/

复位后点亮 LED1 五秒作为下载窗口→配置 USART1 低功耗唤醒并切换到 LSI→进 STOP 等待接收→收到一帧经 EXTI 唤醒退出 STOP→重配 GPIO 与 USART1 并点亮 LED1 一秒

项目 源码解读
功能 演示 USART1 低功耗唤醒事件把 MCU 从 STOP 模式唤醒:STOP 下系统时钟关闭、USART1 用 LSI 保持工作,收到一帧数据后产生唤醒事件经 EXTI24 退出 STOP,唤醒后重配并点亮 LED1 一秒。
源码组成 src/main.c:main、LED1_Init、GPIO_Configuration、USART1_PrepareStopWakeup、USART1_RestoreAfterStop;
inc/main.h:USART1 与引脚宏、USARTy_WKUP_EXTI_LINE;
src/n32g41x_it.c、inc/n32g41x_it.h:USART1_IRQHandler 与其它中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程,LED 引脚宏来自 bsp/inc/n32g41x_stb.h。
关键配置 USARTy 为 USART1,TX 为 PA9、RX 为 PA10,TX 复用号在 N32G412 为 GPIO_AF4、N32G415 为 GPIO_AF0;
波特率 1200,字长 USART_WL_8B,停止位 USART_STPB_1,校验 USART_PE_NO,过采样 USART_16OVER;
USART_SetPrescaler 未改,唤醒时用 RCC_ConfigUsart1Clk 切到 RCC_USART1_CLKSEL_LSI 并写 USARTy->BRCF = 0x1AU;
USART_CfgWakeUpDataClr 设为 USART_WAKEUP_NOTCLRDATA,USART_EnableWakeUp 使能唤醒,EXTI 用 EXTI_LINE24 上升沿;
低功耗时钟 RCC_ConfigLowPowerClk(RCC_LP_CLKSEL_LSI)。
程序与数据路径 main 打开 USART1 与 GPIO 时钟,GPIO_Configuration、LED1_Init 后点亮 LED1 保持 5 s 再熄灭;
使能 LSI 并选低功耗时钟、配 NVIC 与 EXTI24,进入 while(1):先熄灭 LED1,USART1_PrepareStopWakeup 把 USART1 切到 APB2、配唤醒与 BRCF 后切到 LSI,PWR_EnterSTOPMode 进入 STOP;
USART1_IRQHandler 置位 UsartWakeupFlag,main 检查后 USART1_RestoreAfterStop 重配并点亮 LED1 一秒。
主要 API USART_StructInit、USART_Init、USART_Enable、USART_ConfigInt、USART_EnableWakeUp、USART_CfgWakeUpDataClr、USART_GetFlagStatus、USART_ReceiveData
验证方法 复位后 LED1 亮 5 s 再熄灭;
把串口工具设为 1200-8-N-1 下发 0x55,MCU 退出 STOP 且 LED1 亮 1 s,随后 LED1 熄灭、MCU 再次进入 STOP 循环。
使用提示 外部连线 USART1_TX(PA9)接 PC 的 RX、USART1_RX(PA10)接 PC 的 TX;
低功耗唤醒只需按 1200 波特率发送;
写 USART_BRCF 前要保证 USART 处于关闭状态;
支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7;
开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例围绕 STOP 的进出组织代码:进入前把 USART1 从 APB2 切到 LSI 并打开唤醒,退出后重新切回 APB2 并恢复,两段配置严格对称。

数据路径: main → LED1 亮 5 s → 使能 LSI 并选低功耗时钟 → 配 NVIC 与 EXTI24 → USART1_PrepareStopWakeup 切 APB2、配唤醒与 BRCF、切 LSI → PWR_EnterSTOPMode → USART1_IRQHandler 置 UsartWakeupFlag → USART1_RestoreAfterStop → LED1 亮 1 s

1. 用宏定义串口、引脚与唤醒 EXTI 线。 inc/main.h 给出 USART1、TX/RX 引脚与复用宏,并把唤醒事件绑定到 EXTI_LINE24。

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

#define USARTy_WKUP_EXTI_LINE   EXTI_LINE24

extern volatile uint8_t UsartWakeupFlag;

USARTy_WKUP_EXTI_LINE 为 EXTI_LINE24,与 USART1_IRQn 共用同一个中断向量;换串口时这条 EXTI 线要随手册对应关系一并改。

2. 进入 STOP 前的低功耗配置。 USART1_PrepareStopWakeup 先关闭 USART,切到 APB2 时钟重配,读走残留接收数据,打开唤醒与 RXDNE 中断后写 BRCF,最后切到 LSI。

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

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

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

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

USART_BRCF 与过采样位只在 USART 关闭(UEN=0)时可写,所以这段在 USART_Enable(DISABLE) 之后进行;USARTy->BRCF = 0x1AU 是按 LSI 频率算出的分频值,改波特率或时钟源时要重算。

3. 唤醒后恢复 APB2 配置。 USART1_RestoreAfterStop 重新打开 GPIO 与 LED1,把 USART1 切回 APB2、重配 USART 并关闭唤醒与 RXDNE 中断。

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

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

恢复顺序与进入前相反:先切回 APB2 再 USART_Init,最后关闭唤醒功能;LED1_Init 依赖 n32g41x_stb.h 的 LED1 宏,换板时要改该头文件。

4. 进 STOP 并处理唤醒中断。 main 配置 LSI、NVIC 与 EXTI24 后进入主循环,唤醒中断在 USART1_IRQHandler 里置标志位、清 EXTI 并点亮 LED1。

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

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

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

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

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

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

中断里先关 RXDNE 再读走数据,避免数据保持时反复触发;只有 UsartWakeupFlag 置位后主循环才继续,保证唤醒来源是一次有效接收。

对应源码: src/main.c、inc/main.h、src/n32g41x_it.c、projects/n32g41x_EVAL/bsp/inc/n32g41x_stb.h

5.21.4 注意与排障

场景 检查与建议 来源
使能后修改波特率或过采样 用户手册第 21 章写明 USART_BRCF 在 USART_CTRL1.UEN=1 时不能写入,USART_CTRL1.OSPM 也只在禁止 USART 时可写;
修改波特率或过采样前先清零 UEN。
用户手册
传输途中修改数据字长或关闭发送 用户手册说明 USART_CTRL1.WL 在发送或接收过程中不能修改,传输中清零 USART_CTRL1.TXEN 会使波特率计数器停止并破坏正在传输的数据。 用户手册
切换同步、单线、IrDA、LIN 与智能卡模式 用户手册要求启用其中任一专用模式时清零其余模式的使能位,例如启用同步模式(USART_CTRL2.CLKEN)须清零 LINMEN、SCMEN、HDMEN、IRDAMEN,各模式需清零的位以手册对应章节为准。 用户手册
清除状态与错误标志 用户手册规定 RXDNE、OREF、PEF、NEF、FEF、IDLEF 需先读 USART_STS 再读 USART_DAT 清零,TXC 需先读 USART_STS 再写 USART_DAT 清零;
按此顺序操作。
用户手册
低功耗唤醒选错串口或时钟 用户手册说明仅 USART1 与 USART4 支持低功耗唤醒,且时钟源须选择为 LSI 或 LSE;
使用其它串口或 APB 时钟时该功能不成立。
用户手册
智能卡模式的帧格式 用户手册指出智能卡模式下数据长度必须为 8 位并配置校验位,发送时停止位只能为 1.5 位,建议收发均用 1.5 停止位以避免在两种停止位配置间切换;
CK 时钟分频范围为 fCK/2 至 fCK/62。
用户手册
多处理器静默位写入被忽略 用户手册写明 USART_CTRL1.RCVWU 仅在 USART_STS.RXDNE=0(接收数据寄存器为空)时可由软件写入,否则写操作被忽略;
进入静默前先读走 USART_DAT。
用户手册

N32G412、N32G415 系列勘误手册 V1.0.0(2026.6.30)的勘误列表只列出一行 A 版芯片条目,勘误链接列为空,未列出 USART 相关条目。

国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计