5.19 UART 使用说明
本章说明 N32G033 标准外设库中 UART 驱动的文件组成、30 个公开函数,以及 485_DMA、DMA_Interrupt、DMA_Polling、HalfDuplex、Interrupt、IrDA_Tx&Rx 的 LP_Transmit_Receive 与 Transmit_Receive、LIN 的 LIN_Master 与 LIN_Slave、MultiProcessor、Polling、Printf、WakeUpFromStop 共 13 个示例。多处理器通信、单线半双工、IrDA、LIN 模式与 UART3 低功耗唤醒请配合用户手册的“UART”章节阅读,芯片已知问题以勘误手册为准。
5.19.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32g033_std_periph_driver/inc/n32g033_uart.h |
定义 UART_InitType 结构体与字长、停止位、校验、工作模式、过采样、中断、DMA、唤醒、LIN、IrDA、RS-485 驱动使能、标志位等宏,并按复位与初始化、地址与唤醒、LIN 与数据传输、预分频与半双工与 IrDA、标志与中断、驱动使能与接收超时六个区段声明 30 个公开函数 |
firmware/n32g033_std_periph_driver/src/n32g033_uart.c |
实现上述 30 个函数:UART 复位与初始化、地址与静默模式唤醒、LIN 与 IrDA 配置、单线半双工、数据收发与断开帧、标志与中断处理、RS-485 驱动器使能与接收超时 |
projects/n32g033_EVAL/examples/UART/ |
UART 示例根目录,含 485_DMA、DMA_Interrupt、DMA_Polling、HalfDuplex、Interrupt、IrDA_Tx&Rx(LP_Transmit_Receive、Transmit_Receive)、LIN(LIN_Master、LIN_Slave)、MultiProcessor、Polling、Printf、WakeUpFromStop 共 13 个示例 |
projects/n32g033_EVAL/examples/UART/Interrupt/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32g033_EVAL/bsp/ |
示例共用的板级支持文件:delay.h 的 SysTick 延时与 log.h 的串口日志接口 |
5.19.2 API 参考
以下 30 个公开函数按 n32g033_uart.h 的声明顺序分为 6 组;每个条目给出功能摘要与 n32g033_uart.c 中该函数正上方的注释原文。
复位与初始化
UART_DeInit、UART_Init、UART_StructInit、UART_Enable、UART_ConfigInt、UART_EnableDMA
地址与唤醒
UART_SetAddr、UART_ConfigWakeUpMode、UART_EnableRcvWakeUp
LIN 与数据传输
UART_ConfigLINBreakDetectLength、UART_EnableLIN、UART_SendData、UART_ReceiveData、UART_SendBreak
预分频、半双工与 IrDA
UART_SetPrescaler、UART_EnableHalfDuplex、UART_ConfigIrDAMode、UART_EnableIrDA
标志与中断
UART_GetFlagStatus、UART_ClrFlag、UART_ClrRTOFlag、UART_GetIntStatus
驱动使能与接收超时
UART_PinSwapSet、UART_CfgDriverAssertTime、UART_CfgDriverdeassertTime、UART_DriverPolaritySet、UART_DriverModeSet、UART_RTOSet、UART_CfgRTOWidth、UART_CfgWakeupMode
void UART_DeInit(UART_Module* UARTx)复位 UARTx 外设寄存器。
name UART_DeInit
fun Deinitializes the UARTx peripheral registers to their default reset values.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
return none
void UART_Init(UART_Module* UARTx, UART_InitType* UART_InitStruct)按 UART_InitStruct 初始化 UARTx 外设。
name UART_Init
fun Initializes the UARTx peripheral according to UART_InitStruct.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_InitStruct (The input parameters must be the following values):
- buad_rate :
- (((buad_rate) > 0) && ((buad_rate) < 0x003D0900))
- WordLength
- UART_WL_8B
- UART_WL_9B
- StopBits
- UART_STPB_1
- UART_STPB_0_5
- UART_STPB_2
- UART_STPB_1_5
- Parity
- UART_PE_NO
- UART_PE_EVEN
- UART_PE_ODD
- Mode
- UART_MODE_RX
- UART_MODE_TX
- OverSampling
- UART_8OVER
- UART_16OVER
return none
void UART_StructInit(UART_InitType* UART_InitStruct)用默认值填充 UART_InitStruct 的各个成员。
name UART_StructInit.
fun Fills each UART_InitStruct member with its default value.
param UART_InitStruct (The input parameters must be the following values):
- BaudRate
- WordLength
- StopBits
- Parity
- Mode
- OverSampling
return none
void UART_Enable(UART_Module* UARTx, FunctionalState Cmd)使能或禁止指定的 UART 外设。
name UART_Enable
fun Enables or disables the specified UART peripheral.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_ConfigInt(UART_Module* UARTx, uint32_t UART_INT, FunctionalState Cmd)使能或禁止指定的 UART 中断。
name UART_ConfigInt
fun Enables or disables the specified UART interrupts.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_INT (The input parameters must be the following values):
- UART_INT_PEF Parity Error interrupt
- UART_INT_TXC Transmission complete interrupt
- UART_INT_TXDE Transmit Data Register empty interrupt
- UART_INT_RXDNE Receive Data register not empty interrupt
- UART_INT_IDLEF Idle line detection interrupt
- UART_INT_RTOE Receiver timeout interrupt
- UART_INT_LINBD LIN Break detection interrupt
- UART_INT_ERRF Error interrupt(Frame error, noise error, overrun error)
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_EnableDMA(UART_Module* UARTx, uint32_t UART_DMAReq, FunctionalState Cmd)使能或禁止 UART 的 DMA 接口。
name UART_EnableDMA
fun Enables or disables the UART's DMA interface.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_DMAReq (The input parameters must be the following values):
- UART_DMAREQ_TX UART DMA transmit request
- UART_DMAREQ_RX UART DMA receive request
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_SetAddr(UART_Module* UARTx, uint8_t UART_Addr)设置 UART 节点的地址。
name UART_SetAddr
fun Sets the address of the UART node.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_Addr (The input parameters must be the following values):
- (((UART_Addr)>=0x00)&&((UART_Addr)<0x10))
return none
void UART_ConfigWakeUpMode(UART_Module* UARTx, uint32_t UART_WakeUpMode)选择 UART 从静默模式唤醒的方式。
name UART_ConfigWakeUpMode
fun Selects the UART WakeUp method.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_WakeUpMode (The input parameters must be the following values):
- UART_WUM_IDLELINE WakeUp by an idle line detection
- UART_WUM_ADDRMASK WakeUp by an address mark
return none
void UART_EnableRcvWakeUp(UART_Module* UARTx, FunctionalState Cmd)决定 UART 是否处于静默模式。
name UART_EnableRcvWakeUp
fun Determines if the UART is in mute mode or not.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_ConfigLINBreakDetectLength(UART_Module* UARTx, uint32_t UART_LINBreakDetectLength)设置 UART 的 LIN 断开帧检测长度。
name UART_ConfigLINBreakDetectLength
fun Sets the UART LIN Break detection length.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_LINBreakDetectLength (The input parameters must be the following values):
- UART_LINBDL_10B 10-bit break detection
- UART_LINBDL_11B 11-bit break detection
return none
void UART_EnableLIN(UART_Module* UARTx, FunctionalState Cmd)使能或禁止 UART 的 LIN 模式。
name UART_EnableLIN
fun Enables or disables the UART's LIN mode.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_SendData(UART_Module* UARTx, uint32_t Data)通过 UARTx 外设发送单个数据。
name UART_SendData
fun Transmits single data through the UARTx peripheral.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Data :
- the data to transmit
return none
uint32_t UART_ReceiveData(UART_Module* UARTx)返回 UARTx 外设最近一次接收到的数据。
name UART_ReceiveData
fun Returns the most recent received data by the UARTx peripheral.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
return none
void UART_SendBreak(UART_Module* UARTx)发送断开帧。
name UART_SendBreak
fun Transmits break characters.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
return none
void UART_SetPrescaler(UART_Module* UARTx, uint8_t UART_Prescaler)设置系统时钟预分频系数。
name UART_SetPrescaler
fun Sets the system clock prescaler.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_Prescaler:
- specifies the prescaler clock
return none
void UART_EnableHalfDuplex(UART_Module* UARTx, FunctionalState Cmd)使能或禁止 UART 的单线半双工通信。
name UART_EnableHalfDuplex
fun Enables or disables the UART's Half Duplex communication.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void UART_ConfigIrDAMode(UART_Module* UARTx, uint32_t UART_IrDAMode)配置 UART 的 IrDA 接口。
name UART_ConfigIrDAMode
fun Configures the UART's IrDA interface.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_IrDAMode (The input parameters must be the following values):
- UART_IRDAMODE_LOWPPWER
- UART_IRDAMODE_NORMAL
return none
void UART_EnableIrDA(UART_Module* UARTx, FunctionalState Cmd)使能或禁止 UART 的 IrDA 接口。
name UART_EnableIrDA
fun Enables or disables the UART's IrDA interface.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
FlagStatus UART_GetFlagStatus(UART_Module* UARTx, uint32_t UART_FLAG)检查指定的 UART 标志位是否置位。
name UART_GetFlagStatus
fun Checks whether the specified UART flag is set or not.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_FLAG (The input parameters must be the following values):
- UART_FLAG_RTO recevier timeout Flag
- UART_FLAG_FEF Framing error Flag
- UART_FLAG_NEF Noise error flag Flag
- UART_FLAG_OREF Overrun error
- UART_FLAG_PEF Parity error
- UART_FLAG_LINBD LIN break detection flag
- UART_FLAG_RXDNE Receive data register not empty flag
- UART_FLAG_TXC Transmission Complete flag
- UART_FLAG_TXDE Transmit data register empty flag
- UART_FLAG_IDLEF Idle Line detection flag
return UART_FLAG
- SET
- RESET
void UART_ClrFlag(UART_Module* UARTx, uint32_t UART_FLAG)清除 UARTx 的挂起标志位。
name UART_ClrFlag
fun Clears the UARTx's pending flags.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_FLAG (The input parameters must be the following values):
- UART_FLAG_LINBD LIN break detection flag
- UART_FLAG_RXDNE Receive data register not empty flag
- UART_FLAG_TXC Transmission Complete flag
return none
note
- PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun
error) and IDLE (Idle line detected) flags are cleared by software
sequence: a read operation to UART_STS register (UART_GetFlagStatus())
followed by a read operation to UART_DAT register (UART_ReceiveData()).
- RXNE flag can be also cleared by a read to the UART_DAT register
(UART_ReceiveData()).
- TC flag can be also cleared by software sequence: a read operation to
UART_STS register (UART_GetFlagStatus()) followed by a write operation
to UART_DAT register (UART_SendData()).
- TXE flag is cleared only by a write to the UART_DAT register
(UART_SendData()).
- RTO flag is cleared only by a write 1 to the UART_CTRL2.RTOCF bit.
(UART_ClrRTOFlag()).
void UART_ClrRTOFlag(UART_Module* UARTx)清除 UARTx 的接收超时挂起标志位。
name UART_ClrRTOFlag
fun Clears the UARTx's RTO pending flag.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
return none
INTStatus UART_GetIntStatus(UART_Module* UARTx, uint32_t UART_INT)检查指定的 UART 中断是否置位。
name UART_GetIntStatus
fun Checks whether the specified UART interrupt has set or not.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param UART_INT (The input parameters must be the following values):
- UART_INT_PEF Parity Error interrupt
- UART_INT_TXC Transmission complete interrupt
- UART_INT_TXDE Transmit Data Register empty interrupt
- UART_INT_RXDNE Receive Data register not empty interrupt
- UART_INT_IDLEF Idle line detection interrupt
- UART_INT_RTOE Receiver timeout interrupt
- UART_INT_LINBD LIN Break detection interrupt
- UART_INT_ERRF Error interrupt(Frame error, noise error, overrun error)
return bitstatus
- SET
- RESET
void UART_PinSwapSet(UART_Module* UARTx,FunctionalState Cmd)使能或禁止交换 TX/RX 引脚。
name UART_PinSwapSet
fun Swap TX/RX pins enable or disable.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd:(The input parameters must be the following values)
- ENABLE
- DISABLE
return none
void UART_CfgDriverAssertTime(UART_Module* UARTx,uint32_t Time)设置驱动器使能信号的生效时间。
name UART_CfgDriverAssertTime
fun Set Driver Enable assertion time.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Time:(The input parameters must be the following values)
- 0x01 ~ 0x1F
return none
void UART_CfgDriverdeassertTime(UART_Module* UARTx,uint32_t Time)设置驱动器使能信号的失效时间。
name UART_CfgDriverdeassertTime
fun Set Driver Enable deassertion time.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Time:(The input parameters must be the following values)
- 0x01 ~ 0x1F
return none
void UART_DriverPolaritySet(UART_Module* UARTx,FunctionalState Cmd)选择驱动器使能信号的极性。
name UART_DriverPolaritySet
fun Driver enable polarity selection.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd:(The input parameters must be the following values)
- ENABLE
- DISABLE
return none
void UART_DriverModeSet(UART_Module* UARTx,FunctionalState Cmd)选择驱动器使能模式。
name UART_DriverModeSet
fun Driver enable mode selection.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd:(The input parameters must be the following values)
- ENABLE
- DISABLE
return none
void UART_RTOSet(UART_Module* UARTx,FunctionalState Cmd)使能或禁止接收超时。
name UART_RTOSet
fun Receiver timeout enable or disable.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Cmd:(The input parameters must be the following values)
- ENABLE
- DISABLE
return none
void UART_CfgRTOWidth(UART_Module* UARTx,uint32_t Width)配置接收超时的时长。
name UART_CfgRTOWidth
fun configure receive timeout width.
param UARTx (The input parameters must be the following values):
- UART1
- UART2
- UART3
param Width:(The input parameters must be the following values)
- 0x00000000 ~ 0x0FFFFFFF
return none
void UART_CfgWakeupMode(UART_Module* UARTx,uint32_t WakeupEn,uint32_t WakeupMode)配置低功耗唤醒模式。
name UART_CfgWakeupMode
fun configure wakeup mode.
param UARTx (The input parameters must be the following values):
- UART3
param WakeupEn:(The input parameters must be the following values)
- UART_WAKEUP_DATACLR
- UART_WAKEUP_DATANCLR
param WakeupMode:(The input parameters must be the following values)
- UART_WAKEUP_ENABLE
- UART_WAKEUP_DISABLE
return none
5.19.3 Demo 示例说明
UART 示例覆盖基础收发、DMA 收发、中断收发、单线半双工、RS-485、IrDA、LIN、多处理器通信与 UART3 低功耗唤醒:基础类示例让 UART1 与 UART2 交叉收发并比对数据,DMA 与中断类示例改用通道搬运或中断服务,IrDA、LIN、多处理器与唤醒类示例则分别演示对应的协议模式。
5.19.3.1 485_DMA 驱动使能与 DMA 收发
工程目录: projects/n32g033_EVAL/examples/UART/485_DMA/
使能 GPIO、UART 与 DMA 时钟→配置收发引脚与 RS-485 驱动使能引脚→建立发送与接收两个 DMA 通道并重映射→打开 UART 的驱动使能与 DMA 请求→等待收发通道传输完成→比对收发数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 经 RS-485 收发器把 TxBuffer1 通过 DMA 发给 UART2,UART2 再用 DMA 把收到的数据搬进 RxBuffer2;两侧传输完成后比对收发数据,结果存入 TransferStatus2。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、DMA_Configuration、Buffercmp;inc/main.h 定义 UARTy/UARTz 实例、引脚与 DMA 通道、请求重映射宏;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3,UART2 Tx PA1、Rx PA0 均为复用推挽,DE 为 PA4 与 PA8 复用推挽; 波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; 驱动使能:生效与失效时间取 0,极性 DISABLE,模式 ENABLE;DMA:UARTy 发送走通道 1,UARTz 接收走通道 2。 |
| 程序与数据路径 | main -> RCC_Configuration 开 DMA、GPIO、UART 时钟 -> GPIO_Configuration 配 Tx/Rx/DE -> DMA_Configuration 建发送与接收通道并重映射 -> UART_Init 后配置 RS-485 驱动使能 -> UART_EnableDMA 打开 TX/RX 请求 -> DMA_EnableChannel 开通道 -> 轮询 DMA 完成标志 -> Buffercmp 比对。 |
| 主要 API | UART_CfgDriverAssertTime、UART_CfgDriverdeassertTime、UART_DriverPolaritySet、UART_DriverModeSet、UART_EnableDMA、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 按 readme 把两板的 UART1/UART2 与 RS-485 收发器连接; 全速运行时在 watch 窗口查看 TransferStatus2,PASSED 为通过,FAILED 为异常。 |
| 使用提示 | DE 引脚由 UART 硬件按发送时序自动控制,无需软件翻转; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例用 RS-485 驱动使能引脚配合 DMA,把 UART1 与 UART2 之间的单向收发各交给一个 DMA 通道。
数据路径: DMA_Configuration 建 UART1 发送与 UART2 接收通道 → UART_EnableDMA 打开 TX/RX 请求 → DMA_EnableChannel 启动通道 → 硬件自动搬运 TxBuffer1/RxBuffer2 → 轮询 DMA 完成标志 → Buffercmp 比对
1. 初始化收发参数并配置 RS-485 驱动使能。 驱动使能的生效/失效时间与极性决定 DE 引脚的翻转时刻,RS-485 收发器据此切换收发方向。
/* Configure UARTy and UARTz */
UART_Init(UARTy, &UART_InitStructure);
UART_Init(UARTz, &UART_InitStructure);
/* Configure UARTy 485 funtion */
UART_CfgDriverAssertTime(UARTy,0);
UART_CfgDriverdeassertTime(UARTy,0);
UART_DriverPolaritySet(UARTy,DISABLE);
UART_DriverModeSet(UARTy,ENABLE);
/* Configure UARTz 485 funtion */
UART_CfgDriverAssertTime(UARTz,0);
UART_CfgDriverdeassertTime(UARTz,0);
UART_DriverPolaritySet(UARTz,DISABLE);
UART_DriverModeSet(UARTz,ENABLE);
更换收发器或改变收发切换时刻时,按器件手册调整生效/失效时间与极性。
2. 除收发引脚外,DE 引脚也要配成复用。 驱动使能信号由 UART 外设内部产生,只有把对应引脚设为复用输出才能送出。
/* Configure UARTy DE as alternate function push-pull and pull-up */
GPIO_InitStructure.Pin = UARTy_DEPin;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.GPIO_Alternate = UARTy_DE_GPIO_AF;
GPIO_InitPeripheral(UARTy_GPIO, &GPIO_InitStructure);
/* Configure UARTz DE as alternate function push-pull and pull-up */
GPIO_InitStructure.Pin = UARTz_DEPin;
GPIO_InitStructure.GPIO_Alternate = UARTz_DE_GPIO_AF;
GPIO_InitPeripheral(UARTz_GPIO, &GPIO_InitStructure);
DE 引脚编号与复用编号随所选的 UART 实例引脚组合变化,改引脚时要与头文件里的三处宏一起改。
3. 发送与接收各建一个 DMA 通道并做请求重映射。 外设地址取 UART 数据寄存器基址,发送方向为内存到外设,接收方向为外设到内存。
/* UARTy_Tx_DMA_Channel (triggered by UARTy Tx event) Config */
DMA_DeInit(UARTy_Tx_DMA_Channel);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = UARTy_DAT_Base;
DMA_InitStructure.MemAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.BufSize = TxBufferSize1;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_Init(UARTy_Tx_DMA_Channel, &DMA_InitStructure);
DMA_RequestRemap(UARTy_Tx_DMA_REMAP,DMA,UARTy_Tx_DMA_Channel,ENABLE);
缓冲区长度与通道编号写在头文件的宏里,换 UART 实例时同步核对通道与重映射宏。
4. 打开 DMA 请求与通道后轮询完成标志。 UART 侧的 DMA 请求使能与 DMA 通道使能是两件事,缺一不可,完成后两路标志都要等。
/* Wait until UARTy TX DMA Channel Transfer Complete */
while (DMA_GetFlagStatus(UARTy_Tx_DMA_FLAG,DMA) == RESET)
{
}
/* Wait until UARTz RX DMA Channel Transfer Complete */
while (DMA_GetFlagStatus(UARTz_Rx_DMA_FLAG,DMA) == RESET)
{
}
轮询会在阻塞等待期间占住 CPU,实际工程可改为 DMA 传输完成中断再处理。
对应源码: src/main.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.2 DMA_Interrupt DMA 发送与中断接收
工程目录: projects/n32g033_EVAL/examples/UART/DMA_Interrupt/
使能时钟与中断→配置收发引脚→初始化 UART 并建立两个发送 DMA 通道→打开发送 DMA 请求与 UART2 接收中断→开通道并使能 UART→UART1 侧轮询接收、UART2 侧中断接收→等待两通道完成并比对
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 用 DMA 把 TxBuffer1 发给 UART2,UART2 用 DMA 把 TxBuffer2 发给 UART1;UART2 侧接收走 RXDNE 中断,UART1 侧接收在主循环轮询,最后分别比对两组数据。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、NVIC_Configuration、GPIO_Configuration、DMA_Configuration、Buffercmp;src/n32g033_it.c 含 UARTz_IRQHandler;inc/main.h 给出实例与 DMA 宏;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3,UART2 Tx PA1、Rx PA0 复用推挽; 波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; DMA:UART1 发送走通道 1,UART2 发送走通道 3; 中断:UART2 的 RXDNE 中断优先级 0。 |
| 程序与数据路径 | main -> RCC_Configuration 开各时钟 -> NVIC_Configuration 开 UART2 中断 -> GPIO_Configuration -> DMA_Configuration 建两个发送通道 -> UART_EnableDMA 打开两路 TX 请求、UART_ConfigInt 开 UART2 RXDNE -> DMA_EnableChannel -> 主循环轮询 UART1 收 TxBuffer2 -> 中断收 TxBuffer1 到 RxBuffer2 -> 轮询两 DMA 完成 -> Buffercmp。 |
| 主要 API | UART_EnableDMA、UART_ConfigInt、UART_GetFlagStatus、UART_ReceiveData、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 一块板上 UART1 与 UART2 交叉连线; 全速运行时在 watch 窗口依次查看 TransferStatus1 与 TransferStatus2,PASSED 为通过。 |
| 使用提示 | 发送用 DMA、接收用中断时,DMA 只负责搬出,接收计数由中断维护; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例把两个方向的发送都交给 DMA,接收一侧用中断、另一侧用轮询,对照着看可以分清两类收发的配合方式。
数据路径: DMA_Configuration 建两个发送通道 → UART_EnableDMA 打开两路 TX 请求 → UART_ConfigInt 开 UART2 接收中断 → DMA_EnableChannel 启动 → 主循环轮询 UARTy 收 TxBuffer2,UARTz_IRQHandler 收 TxBuffer1 → 轮询两通道完成 → Buffercmp 比对
1. 两个方向的发送各建一个 DMA 通道。 两次配置只改外设地址、内存地址与缓冲区长度,其余属性沿用。
/* UARTz_Tx_DMA_Channel (triggered by UARTz Tx event) Config */
DMA_DeInit(UARTz_Tx_DMA_Channel);
DMA_InitStructure.PeriphAddr = UARTz_DAT_Base;
DMA_InitStructure.MemAddr = (uint32_t)TxBuffer2;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.BufSize = TxBufferSize2;
DMA_Init(UARTz_Tx_DMA_Channel, &DMA_InitStructure);
DMA_RequestRemap(UARTz_Tx_DMA_REMAP,DMA,UARTz_Tx_DMA_Channel,ENABLE);
第二个通道沿用了首个通道的结构体设置,移植时不要漏掉外设数据宽度与优先级这类已赋值成员。
2. 打开发送 DMA 请求并单独开接收中断。 DMA 请求把外设与通道关联,中断则只针对 UART2 的接收。
/* Enable UARTy DMA TX request */
UART_EnableDMA(UARTy, UART_DMAREQ_TX,ENABLE);
/* Enable UARTz DMA TX request */
UART_EnableDMA(UARTz, UART_DMAREQ_TX,ENABLE);
/* Enable the UARTz Receive Interrupt */
UART_ConfigInt(UARTz, UART_INT_RXDNE,ENABLE);
/* Enable UARTy TX DMA Channel */
DMA_EnableChannel(UARTy_Tx_DMA_Channel,ENABLE);
/* Enable UARTz TX DMA Channel */
DMA_EnableChannel(UARTz_Tx_DMA_Channel,ENABLE);
请求、通道与中断三处要成对打开,少一处该方向就不会流转。
3. 主循环轮询 UART1 接收,收满即退出。 这里接收 UART2 用 DMA 发来的 TxBuffer2,靠判断 RXDNE 标志逐字节取数。
/* Receive the TxBuffer2 */
while (indexFlag < TxBufferSize2)
{
while (UART_GetFlagStatus(UARTy, UART_FLAG_RXDNE) == RESET)
{
/* Overflow data loss */
if(UART_GetFlagStatus(UARTy, UART_FLAG_OREF) != RESET)
{
UARTy->DAT;
}
}
RxBuffer1[indexFlag++] = UART_ReceiveData(UARTy);
}
轮询里顺带读一次数据寄存器是为了清溢出标志,防止丢数后卡死。
4. 中断服务按帧长收满后自行关闭。 UART2 的接收在中断里完成,收到预定字节数就把接收中断关掉。
void UARTz_IRQHandler(void)
{
if ((UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) != RESET) && (UART_GetIntStatus(UARTz, UART_INT_RXDNE) != RESET))
{
/* Read one byte from the receive data register */
RxBuffer2[RxCounter++] = UART_ReceiveData(UARTz);
if (RxCounter == NbrOfDataToRead)
{
/* Disable the UARTz Receive interrupt */
UART_ConfigInt(UARTz, UART_INT_RXDNE, DISABLE);
}
}
中断里先判标志再判中断使能位,避免误进服务函数;收满关中断可减少后续无谓进入。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.3 DMA_Polling DMA 发送与 DMA 接收
工程目录: projects/n32g033_EVAL/examples/UART/DMA_Polling/
使能 GPIO、UART 与 DMA 时钟→配置收发引脚→建立发送与接收两个 DMA 通道并重映射→打开 UART 的 DMA 请求→开通道并使能 UART→等待收发通道传输完成→比对收发数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 用 DMA 把 TxBuffer1 发给 UART2,UART2 用 DMA 把收到的数据搬进 RxBuffer2;两侧传输完成后比对,结果存入 TransferStatus2。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、DMA_Configuration、Buffercmp;inc/main.h 定义实例与 DMA 通道、重映射宏;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3,UART2 Tx PA1、Rx PA0 复用推挽; 波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; DMA:UART1 发送走通道 1,UART2 接收走通道 2。 |
| 程序与数据路径 | main -> RCC_Configuration 开 DMA、GPIO、UART 时钟 -> GPIO_Configuration -> DMA_Configuration 建发送与接收通道 -> UART_Init -> UART_EnableDMA 打开 TX/RX 请求 -> DMA_EnableChannel -> 轮询 DMA 完成标志 -> Buffercmp 比对。 |
| 主要 API | UART_EnableDMA、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 一块板上 UART1 与 UART2 交叉连线; 全速运行时在 watch 窗口查看 TransferStatus2,PASSED 为通过。 |
| 使用提示 | 与 485_DMA 同源,只是去掉 RS-485 驱动使能,用于验证纯 DMA 收发通路; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例是最小化的 DMA 收发通路,发送与接收各一个通道,主程序只负责等完成标志。
数据路径: DMA_Configuration 建 UART1 发送与 UART2 接收通道 → UART_EnableDMA 打开 TX/RX 请求 → DMA_EnableChannel 启动 → 硬件搬运 TxBuffer1/RxBuffer2 → 轮询 DMA 完成标志 → Buffercmp 比对
1. 初始化收发参数并打开两路 DMA 请求。 收发用的实例与模式在此确定,请求把两个实例分别接到各自的通道。
/* Configure UARTy and UARTz */
UART_Init(UARTy, &UART_InitStructure);
UART_Init(UARTz, &UART_InitStructure);
/* Enable UARTy DMA TX request */
UART_EnableDMA(UARTy, UART_DMAREQ_TX,ENABLE);
/* Enable UARTz DMA Rx request */
UART_EnableDMA(UARTz, UART_DMAREQ_RX,ENABLE);
请求方向要与数据流向一致,发送实例开 TX 请求、接收实例开 RX 请求。
2. 接收通道把外设数据搬到内存缓冲。 接收方向为外设到内存,内存地址指向接收缓冲。
/* UARTz RX DMA1 Channel (triggered by UARTz Rx event) Config */
DMA_DeInit(UARTz_Rx_DMA_Channel);
DMA_InitStructure.PeriphAddr = UARTz_DAT_Base;
DMA_InitStructure.MemAddr = (uint32_t)RxBuffer2;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = TxBufferSize1;
DMA_Init(UARTz_Rx_DMA_Channel, &DMA_InitStructure);
DMA_RequestRemap(UARTz_Rx_DMA_REMAP,DMA,UARTz_Rx_DMA_Channel,ENABLE);
接收缓冲大小不能小于发送字节数,否则会溢出覆盖。
3. 等两路完成标志后再比对。 只有收发两路都置起完成标志,数据才完整搬完,此时比对才有意义。
/* Wait until UARTy TX DMA Channel Transfer Complete */
while (DMA_GetFlagStatus(UARTy_Tx_DMA_FLAG,DMA) == RESET)
{
}
/* Wait until UARTz RX DMA Channel Transfer Complete */
while (DMA_GetFlagStatus(UARTz_Rx_DMA_FLAG,DMA) == RESET)
{
}
阻塞轮询适合一次性传输,连续传输应改用通道中断或循环模式。
对应源码: src/main.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.4 HalfDuplex 单线半双工收发
工程目录: projects/n32g033_EVAL/examples/UART/HalfDuplex/
使能 GPIO 与 UART 时钟→把两个实例各配成一个发送引脚→初始化 UART 并使能单线半双工→UART1 发、UART2 收→UART2 发、UART1 收→比对两组收发数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 与 UART2 各只用一个 Tx 引脚并开启单线半双工模式,两个方向轮流收发:UART1 发 TxBuffer1 给 UART2,UART2 发 TxBuffer2 给 UART1,最后分别比对两组数据。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、Buffercmp;inc/main.h 定义两个实例的发送引脚与复用宏;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:只配 UART1 Tx PA2 与 UART2 Tx PA1,均为复用推挽,不配 Rx; 波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; 模式:收发使能后调用半双工使能,两板对应 Tx 引脚相连。 |
| 程序与数据路径 | main -> RCC_Configuration 开 GPIO、UART 时钟 -> GPIO_Configuration 配两个发送引脚 -> UART_Init 配收发 -> UART_Enable 使能 -> UART_EnableHalfDuplex 开半双工 -> 轮询 TXDE 发送、轮询 RXDNE 接收两个方向 -> Buffercmp 比对。 |
| 主要 API | UART_EnableHalfDuplex、UART_SendData、UART_ReceiveData、UART_GetFlagStatus、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 把两板的 UART1 Tx(PA2) 与 UART2 Tx(PA1) 用一根线相连; 全速运行时在 watch 窗口查看 TransferStatus1 与 TransferStatus2,PASSED 为通过。 |
| 使用提示 | 半双工下收发共用一根线,两侧不能同时发送; 未发送时引脚应处于浮空或开漏高电平,避免总线被拉住; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例把两个 UART 配成单线半双工,收发共用各自的发送引脚,靠软件安排两个方向的先后。
数据路径: GPIO_Configuration 只配发送引脚 → UART_Init 配收发 → UART_Enable 使能 → UART_EnableHalfDuplex 开半双工 → 轮询 TXDE 逐字节发 → 轮询 RXDNE 逐字节收 → Buffercmp 比对
1. 只配置发送引脚。 单线半双工下收发共用一根线,因此只把发送引脚设为复用推挽。
/* Configure UARTy Tx as alternate function push-pull and pull-up */
GPIO_InitStructure.Pin = UARTy_TxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitStructure.GPIO_Alternate = UARTy_Tx_GPIO_AF;
GPIO_InitPeripheral(UARTy_GPIO, &GPIO_InitStructure);
/* Configure UARTz Tx as alternate function push-pull and pull-up */
GPIO_InitStructure.Pin = UARTz_TxPin;
GPIO_InitStructure.GPIO_Alternate = UARTz_Tx_GPIO_AF;
GPIO_InitPeripheral(UARTz_GPIO, &GPIO_InitStructure);
半双工引脚既作发送又作接收,改引脚时要保证两侧复用编号一致。
2. 使能 UART 后再打开半双工模式。 半双工使能建立在 UART 已使能、收发模式已配置的基础上。
/* Enable the UARTy and UARTz */
UART_Enable(UARTy, ENABLE);
UART_Enable(UARTz, ENABLE);
/* Enable UARTy Half Duplex Mode*/
UART_EnableHalfDuplex(UARTy, ENABLE);
/* Enable UARTz Half Duplex Mode*/
UART_EnableHalfDuplex(UARTz, ENABLE);
3. 两个方向分别轮询收发。 同一根线上先由一个方向发、另一个方向收,收完再交换角色。
while (NbrOfDataToRead2--)
{
/* Wait until end of transmit */
while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
/* Write one byte in the UARTy Transmit Data Register */
UART_SendData(UARTy, TxBuffer1[TxCounter1++]);
/* Wait the byte is entirely received by UARTz */
while (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) == RESET)
{
/* Overflow data loss */
if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
{
UARTz->DAT;
}
}
/* Store the received byte in the RxBuffer2 */
RxBuffer2[RxCounter2++] = UART_ReceiveData(UARTz);
}
收发在同一循环内一收一送,移植到实际总线时要自行加入方向切换与总线空闲判断。
对应源码: src/main.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.5 Interrupt 中断收发
工程目录: projects/n32g033_EVAL/examples/UART/Interrupt/
使能时钟与中断→配置收发引脚→初始化 UART 并打开收发中断→设置 NVIC 并使能 UART→收发在中断里完成→主循环等待计数后比对
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 与 UART2 互发数据,双方都打开发送与接收中断,字节的收发全部在中服务里完成,主循环只等待收发计数到量后比对两组数据。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、NVIC_Configuration、GPIO_Configuration、Buffercmp;src/n32g033_it.c 含 UARTy_IRQHandler 与 UARTz_IRQHandler;inc/main.h 给出实例与中断宏;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3,UART2 Tx PA1、Rx PA0 复用推挽; 波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; 中断:两个实例都开 RXDNE 与 TXDE,NVIC 优先级分别为 0 与 1。 |
| 程序与数据路径 | main -> RCC_Configuration -> NVIC_Configuration 开两个中断通道 -> GPIO_Configuration -> UART_Init -> UART_ConfigInt 打开收发中断 -> UART_Enable -> 中断收发 -> 主循环等待 RxCounter 到量 -> Buffercmp 比对。 |
| 主要 API | UART_ConfigInt、UART_GetFlagStatus、UART_GetIntStatus、UART_SendData、UART_ReceiveData、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 一块板上 UART1 与 UART2 交叉连线; 全速运行时在 watch 窗口查看 TransferStatus1 与 TransferStatus2,PASSED 为通过。 |
| 使用提示 | 中断里收发按计数自行关闭对应使能位,避免持续进入; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例把收发都搬进中断,主程序退化为等待计数,适合对照中断与轮询两种收发方式的差异。
数据路径: NVIC_Configuration 开中断通道 → UART_ConfigInt 打开收发中断 → UART_Enable 使能 → UARTy_IRQHandler/UARTz_IRQHandler 逐字节收发 → 主循环等待计数 → Buffercmp 比对
1. 打开收发中断。 发送与接收各有一个使能位,两个实例都要配。
/* Enable UARTy Receive and Transmit interrupts */
UART_ConfigInt(UARTy, UART_INT_RXDNE, ENABLE);
UART_ConfigInt(UARTy, UART_INT_TXDE, ENABLE);
/* Enable UARTz Receive and Transmit interrupts */
UART_ConfigInt(UARTz, UART_INT_RXDNE, ENABLE);
UART_ConfigInt(UARTz, UART_INT_TXDE, ENABLE);
2. 配置两个中断通道与优先级。 中断服务函数由 NVIC 分发,通道号来自头文件的实例宏。
/* Enable the UARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = UARTy_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = NVIC_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the UARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = UARTz_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = NVIC_PRIORITY_1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
3. 中断里按标志收发并收满关中断。 先判状态标志再判中断使能位,收满或发满后关闭对应中断。
void UARTy_IRQHandler(void)
{
if ((UART_GetFlagStatus(UARTy, UART_FLAG_RXDNE) != RESET) && (UART_GetIntStatus(UARTy, UART_INT_RXDNE) != RESET))
{
/* Read one byte from the receive data register */
RxBuffer1[RxCounter1++] = UART_ReceiveData(UARTy);
if (RxCounter1 == NbrOfDataToRead1)
{
/* Disable the UARTy Receive interrupt */
UART_ConfigInt(UARTy, UART_INT_RXDNE, DISABLE);
}
}
if ((UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) != RESET) && (UART_GetIntStatus(UARTy, UART_INT_TXDE) != RESET))
{
/* Write one byte to the transmit data register */
UART_SendData(UARTy, TxBuffer1[TxCounter1++]);
if (TxCounter1 == NbrOfDataToTransfer1)
{
/* Disable the UARTy Transmit interrupt */
UART_ConfigInt(UARTy, UART_INT_TXDE, DISABLE);
}
}
中断里顺带处理并清除错误标志,可避免噪声、溢出或帧错误后中断反复触发。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.6 IrDA_Tx&Rx LP_Transmit_Receive IrDA 低功耗模式收发
工程目录: projects/n32g033_EVAL/examples/UART/IrDA_Tx&Rx/LP_Transmit_Receive/
使能时钟并降低外设时钟→配置收发引脚与载波输出引脚→初始化 UART 并分设收发→设置预分频并打开 IrDA 低功耗模式→轮询发送缓冲→中断接收→比对收发数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 发送 TxBuffer1,经 IrDA 调制后由 UART2 接收并存入 RxBuffer1,两侧工作在 IrDA 低功耗模式,最后比对收发数据得到 TransferStatus。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;src/bsp_Tim_38K.c 与 inc/bsp_Tim_38K.h 输出 38 kHz 载波;src/n32g033_it.c 含接收中断;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2 与 UART2 Rx PA0 复用推挽; 波特率 600、8 数据位、1 停止位、无校验; 时钟:PCLK 分频取 RCC_HCLK_DIV8;预分频取 210,IrDA 模式取低功耗; 载波:TIM3 在 PB4 输出 38 kHz。 |
| 程序与数据路径 | main -> RCC_Configuration 开时钟并降 PCLK -> NVIC_Configuration 开 UART2 中断 -> GPIO_Configuration -> ADVANCE_TIM_Init 输出载波 -> UART_Init 分设收发 -> UART_SetPrescaler 设预分频 -> UART_ConfigIrDAMode 与 UART_EnableIrDA 开 IrDA -> 轮询 TXDE 发送 -> 中断收 RxBuffer1 -> Buffercmp 比对。 |
| 主要 API | UART_SetPrescaler、UART_ConfigIrDAMode、UART_EnableIrDA、UART_ConfigInt、UART_SendData、UART_GetFlagStatus、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 按 readme 把 PA0 接红外接收、PA2 接红外发射、PB4 接 38 kHz 载波; 全速运行时在 watch 窗口查看 TransferStatus,PASSED 为通过。 |
| 使用提示 | 低功耗模式要求分频后时钟落在 1.42 MHz 至 2.12 MHz 之间,故先降低 PCLK 再设预分频; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例先降低外设时钟、再设 IrDA 预分频,使编码用的时钟落到低功耗模式要求的频段。
数据路径: RCC_ConfigPclk 降 PCLK → ADVANCE_TIM_Init 输出 38 kHz 载波 → UART_Init 分设收发 → UART_SetPrescaler → UART_ConfigIrDAMode 选低功耗 → UART_EnableIrDA 使能 → 轮询发送、中断接收 → Buffercmp 比对
1. 先降低 PCLK 再初始化 38 kHz 载波。 低功耗 IrDA 的编码时钟由 PCLK 经预分频得到,因此先分频。
/* System Clocks Configuration */
RCC_Configuration();
RCC_ConfigPclk(RCC_HCLK_DIV8);
/* NVIC configuration */
NVIC_Configuration();
/* Configure the GPIO ports */
GPIO_Configuration();
/* GPIO PB4 Output 38KHz Clock */
ADVANCE_TIM_Init();
主频越低、特定波特率的误差也越小,换主频时同步核对分频系数。
2. 分设收发并配置 IrDA 低功耗模式。 两个实例一个只发、一个只收,预分频值与模式都要一致。
UART_InitStructure.Mode = UART_MODE_TX;
UART_Init(UARTy, &UART_InitStructure);
UART_InitStructure.Mode = UART_MODE_RX;
UART_Init(UARTz, &UART_InitStructure);
/* Enable UARTz Receive and Transmit interrupts */
UART_ConfigInt(UARTz, UART_INT_RXDNE, ENABLE);
/* Set the UARTy and UARTz prescaler */
UART_SetPrescaler(UARTy, 210);
UART_SetPrescaler(UARTz, 210);
/* Configure the UARTy IrDA mode */
UART_ConfigIrDAMode(UARTy, UART_IRDAMODE_LOWPPWER);
UART_ConfigIrDAMode(UARTz, UART_IRDAMODE_LOWPPWER);
/* Enable the UARTy and UARTz IrDA mode */
UART_EnableIrDA(UARTy,ENABLE);
UART_EnableIrDA(UARTz,ENABLE);
预分频值决定脉宽,两侧取值必须一致才能正确解码。
3. 轮询发送缓冲,接收交给中断。 发送侧逐字节等 TXDE,接收侧收满预定字节数即停。
while (TxCounter < TxBufferSize1)
{
/* Send one byte from Board1_UARTy to Board2_UARTz */
UART_SendData(UARTy, TxBuffer1[TxCounter++]);
/* Loop until Board1_UARTy DAT register is empty */
while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
}
IrDA 是半双工接口,收发之间要留出足够间隔,移植时应插入发送与接收的切换等待。
对应源码: src/main.c、src/bsp_Tim_38K.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.7 IrDA_Tx&Rx Transmit_Receive IrDA 正常模式收发
工程目录: projects/n32g033_EVAL/examples/UART/IrDA_Tx&Rx/Transmit_Receive/
使能时钟→配置收发引脚与载波输出引脚→初始化 UART 并分设收发→设置预分频并打开 IrDA 正常模式→轮询发送缓冲→中断接收→比对收发数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 发送 TxBuffer1,经 IrDA 调制后由 UART2 接收并存入 RxBuffer1,两侧工作在 IrDA 正常模式,最后比对收发数据得到 TransferStatus。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;src/bsp_Tim_38K.c 与 inc/bsp_Tim_38K.h 输出 38 kHz 载波;src/n32g033_it.c 含接收中断;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2 与 UART2 Rx PA0 复用推挽; 波特率 1200、8 数据位、1 停止位、无校验、16 倍过采样; 预分频取 1,IrDA 模式取正常; 载波:TIM3 在 PB4 输出 38 kHz。 |
| 程序与数据路径 | main -> RCC_Configuration 开时钟 -> NVIC_Configuration 开 UART2 中断 -> GPIO_Configuration -> ADVANCE_TIM_Init 输出载波 -> UART_Init 分设收发 -> UART_SetPrescaler 取 1 -> UART_ConfigIrDAMode 与 UART_EnableIrDA 开 IrDA -> 轮询 TXDE 发送 -> 中断收 RxBuffer1 -> Buffercmp 比对。 |
| 主要 API | UART_SetPrescaler、UART_ConfigIrDAMode、UART_EnableIrDA、UART_ConfigInt、UART_SendData、UART_GetFlagStatus、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 按 readme 把 PA0 接红外接收、PA2 接红外发射、PB4 接 38 kHz 载波; 全速运行时在 watch 窗口查看 TransferStatus,PASSED 为通过。 |
| 使用提示 | 正常模式预分频只能取 1,且最大波特率为 115200; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例与低功耗模式同源,区别在预分频取 1、模式取正常,编码时钟直接用外设时钟。
数据路径: ADVANCE_TIM_Init 输出 38 kHz 载波 → UART_Init 分设收发 → UART_SetPrescaler 取 1 → UART_ConfigIrDAMode 选正常 → UART_EnableIrDA 使能 → 轮询发送、中断接收 → Buffercmp 比对
1. 输出 38 kHz 载波并初始化收发。 载波由 TIM3 定时翻转输出引脚产生,供红外发射器调制。
/* Configure the GPIO ports */
GPIO_Configuration();
/* GPIO PC2 Output 38KHz Clock */
ADVANCE_TIM_Init();
/* UARTy and UARTz configuration */
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate = 1200;
UART_InitStructure.WordLength = UART_WL_8B;
UART_InitStructure.StopBits = UART_STPB_1;
UART_InitStructure.Parity = UART_PE_NO;
UART_InitStructure.OverSampling = UART_16OVER;
2. 预分频取 1 并配置 IrDA 正常模式。 正常模式下 PSCV 只能设为 1,两侧取值一致。
/* Set the UARTy and UARTz prescaler */
UART_SetPrescaler(UARTy, 0x01);
UART_SetPrescaler(UARTz, 0x01);
/* Configure the UARTy IrDA mode */
UART_ConfigIrDAMode(UARTy, UART_IRDAMODE_NORMAL);
UART_ConfigIrDAMode(UARTz, UART_IRDAMODE_NORMAL);
/* Enable the UARTy and UARTz IrDA mode */
UART_EnableIrDA(UARTy,ENABLE);
UART_EnableIrDA(UARTz,ENABLE);
IrDA 与 LIN、半双工模式互斥,切换模式前要先把相关使能位清零。
3. 轮询发送缓冲,收满后比对两组数据。 发送逐字节等 TXDE,接收由中断填充,收满计数后一次比对。
while (TxCounter < TxBufferSize1)
{
/* Send one byte from Board1_UARTy to Board2_UARTz */
UART_SendData(UARTy, TxBuffer1[TxCounter++]);
/* Loop until Board1_UARTy DAT register is empty */
while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
}
while (RxCounter < TxBufferSize1)
{
}
等待接收计数的空循环应与中断完成的判断配合,避免收满前提前退出。
对应源码: src/main.c、src/bsp_Tim_38K.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.8 LIN_Master LIN 主机发帧
工程目录: projects/n32g033_EVAL/examples/UART/LIN/LIN_Master/
使能时钟与中断→配置 UART2 收发引脚→初始化并使能 LIN 断开检测→主循环发主请求帧与从应答帧→打印从机回传数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART2 作为 LIN 主机,先发带 8 字节数据的请求帧(标识符 0x3C),再发标识符 0x3D 的从应答帧头,等待从机回传 8 字节数据并打印。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Memset、Buffercopy;src/lin_master.c 与 inc/lin_master.h 实现帧组织与收发;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART2 Tx PA1、Rx PA0 复用推挽(GPIO_AF4);波特率 9600(readme 标注 115200,示例按 9600 配置)、8 数据位、1 停止位、无校验、16 倍过采样; LIN:断开检测长度 10 位; 另用 UART1 打印,Tx PA9、Rx PA10。 |
| 程序与数据路径 | main -> log_init 初始化打印串口 -> RCC_Configuration -> NVIC_Configuration -> GPIO_Configuration -> UART_Init -> UART_ConfigLINBreakDetectLength 与 UART_EnableLIN -> UART_Enable -> 主循环 TestLinMaster -> FrameHandle 经 MasterSendBytes 发帧 -> WaitFrameRes 收从机数据。 |
| 主要 API | UART_ConfigLINBreakDetectLength、UART_EnableLIN、UART_SendBreak、UART_SendData、UART_GetFlagStatus、UART_ReceiveData、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 两块板分别烧主机与从机工程,UART2 收发引脚对接; 主机串口打印收到的从机数据,连续 4 次无响应则打印 slave no response!!。 |
| 使用提示 | 断开帧由 UART_SendBreak 发出,帧头同步字节固定为 0x55;标识符 0x3C 为请求帧、0x3D 为应答帧; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例在 LIN 模式下由主机发起断开帧与帧头,再从机回数据,主机只管发帧头与等待应答。
数据路径: UART_Init 配收发 → UART_ConfigLINBreakDetectLength 与 UART_EnableLIN 开 LIN → UART_Enable → TestLinMaster 组帧 → FrameHandle 判标识符 → MasterSendBytes 发断开帧与数据 → WaitFrameRes 收从机应答
1. 初始化 UART 并使能 LIN 断开检测。 LIN 帧以断开字段起始,因此先设检测长度再开 LIN 模式。
/* UARTx configuration */
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate = 9600;
UART_InitStructure.WordLength = UART_WL_8B;
UART_InitStructure.StopBits = UART_STPB_1;
UART_InitStructure.Parity = UART_PE_NO;
UART_InitStructure.OverSampling = UART_16OVER;
UART_InitStructure.Mode = UART_MODE_RX | UART_MODE_TX;
/* Configure UARTx */
UART_Init(UARTx, &UART_InitStructure);
/* Enable the UARTx LIN mode*/
UART_ConfigLINBreakDetectLength(UARTx,UART_LINBDL_10B);
UART_EnableLIN(UARTx,ENABLE);
/* Enable the UARTx */
UART_Enable(UARTx,ENABLE);
LIN 模式只支持 8 位数据帧,且与 IrDA、单线半双工模式互斥。
2. 发帧时先送断开帧再逐字节发数据。 每个字节等到发送完成标志置位再送下一个,帧尾再等一次。
void MasterSendBytes(uint8_t *pBuf, uint8_t Len)
{
UART_SendBreak(UARTx);
while(Len--)
{
while(UART_GetFlagStatus(UARTx, UART_FLAG_TXC) == RESET);
UART_SendData(UARTx, *pBuf++);
}
while(UART_GetFlagStatus(UARTx, UART_FLAG_TXC ) == RESET);
}
UART_FLAG_TXC 表示整帧发送完成,用它而不是 TXDE 可保证最后一个字节真正送出。
3. 按标识符组织请求帧或应答帧。 主机依标识符决定发请求帧还是只发应答帧头。
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);
}
标识符经奇偶校验位扩展后再发送,移植时不要直接用 6 位原始标识符。
对应源码: src/main.c、src/lin_master.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.9 LIN_Slave LIN 从机应答
工程目录: projects/n32g033_EVAL/examples/UART/LIN/LIN_Slave/
使能时钟与中断→配置 UART2 收发引脚→初始化并使能 LIN 与收发中断→主循环等待接收标志→解析帧并组织应答数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART2 作为 LIN 从机,在中断里以状态机接收主机的断开字段、同步字节、标识符与数据,收到完整帧后由主循环组织应答帧并回送。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Memset、Buffercopy、LIN_RespData;src/lin_slave.c 与 inc/lin_slave.h 实现状态机接收与应答;src/n32g033_it.c 把 UART2 中断转给 LIN_IRQHandler;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART2 Tx PA1、Rx PA0 复用推挽(GPIO_AF4);波特率 9600(readme 标注 115200,示例按 9600 配置)、8 数据位、1 停止位、无校验、16 倍过采样; LIN:断开检测长度 10 位; 中断:接收数据中断与 LIN 断开检测中断均使能。 |
| 程序与数据路径 | main -> log_init、RCC_Configuration、NVIC_Configuration、GPIO_Configuration -> UART_Init -> UART_ConfigLINBreakDetectLength 与 UART_EnableLIN -> UART_ConfigInt 开 RXDNE 与 LINBD -> UART_Enable -> 配置从机标识符收发类型 -> 中断 LIN_IRQHandler 驱动状态机收帧 -> 主循环收到标志后 BOOT_ExecutiveCommand 处理并 LIN_SetResp 应答。 |
| 主要 API | UART_ConfigLINBreakDetectLength、UART_EnableLIN、UART_ConfigInt、UART_GetFlagStatus、UART_ClrFlag、UART_ReceiveData、UART_SendData、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 两块板分别烧主机与从机工程,UART2 收发引脚对接; 从机串口打印收到的命令数据,主机侧收到的应答为全 1。 |
| 使用提示 | 从机既接收主机请求帧、也回送应答帧,标识符收发类型由 IDType 表区分;收到断开字段后状态机从头开始,帧同步字节固定为 0x55; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把接收拆成中断内状态机与主循环处理两段,接收与应答解耦,便于按标识符表扩展。
数据路径: UART_Init 配收发 → UART_EnableLIN 与 UART_ConfigLINBreakDetectLength → UART_ConfigInt 开 RXDNE 与 LINBD → UART_Enable → LIN_IRQHandler 清标志并推进 LIN_EX_RxAsync → 主循环置 GotMsgFlag 后 LIN_SetResp 组帧 → LIN_SendBytes 回送
1. 初始化并使能 LIN 与两类中断。 断开检测中断用于对帧,接收数据中断用于取字节。
/* Configure UARTx */
UART_Init(UARTx, &UART_InitStructure);
/* Enable the UARTx LIN mode*/
UART_ConfigLINBreakDetectLength(UARTx,UART_LINBDL_10B);
UART_EnableLIN(UARTx,ENABLE);
/* Enable UARTx Receive and Transmit interrupts */
UART_ConfigInt(UARTx, UART_INT_RXDNE,ENABLE);
UART_ConfigInt(UARTx, UART_INT_LINBD,ENABLE);
/* Enable the UARTx */
UART_Enable(UARTx,ENABLE);
2. 中断里清标志并把字节交给状态机。 收到断开字段后重置状态机,正常字节则按帧结构推进。
void LIN_IRQHandler(void)
{
if(UART_GetFlagStatus(UARTx, UART_FLAG_LINBD) == SET)
{
UART_ClrFlag(UARTx, UART_FLAG_LINBD);//Clear the LIN interval field detection flag bit
//Read the status register and data register and clear the FE flag
UARTx->STS;
UARTx->DAT;
LinRxState = SYNCH;
return;
}
if (UART_GetFlagStatus(UARTx, UART_FLAG_RXDNE) == SET)
{
UART_ClrFlag(UARTx, UART_FLAG_RXDNE); //Clear the receive interrupt flag bit
if(UART_GetFlagStatus(UARTx, UART_FLAG_FEF) == RESET)
{
LIN_EX_RxAsync((uint8_t)UART_ReceiveData(UARTx));
}
}
/* Determine if an error flag still exists and clear the error flag */
if ((UART_GetFlagStatus(UARTx, UART_FLAG_OREF) != RESET) || \
(UART_GetFlagStatus(UARTx, UART_FLAG_NEF) != RESET) || \
(UART_GetFlagStatus(UARTx, UART_FLAG_PEF) != RESET) || \
(UART_GetFlagStatus(UARTx, UART_FLAG_FEF) != RESET))
{
/*Read the sts register first,and the read the DAT register to clear the all error flag*/
(void)UARTx->STS;
(void)UARTx->DAT;
/* Under normal circumstances, all error flags will be cleared when the upper data is read and will not be executed here;
users can add their own processing according to the actual scenario. */
}
}
先读状态寄存器再读数据寄存器才能把溢出、噪声、校验与帧错误标志一起清掉。
3. 组织应答帧并写入校验和。 按标识符算出校验和,长度不足 8 字节时把校验和放在数据之后。
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;
}
经典校验和与增强校验和的覆盖范围不同,由调用处按标识符类型选择。
对应源码: src/main.c、src/lin_slave.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.10 MultiProcessor 多处理器地址唤醒
工程目录: projects/n32g033_EVAL/examples/UART/MultiProcessor/
使能时钟→配置收发引脚与按键 LED→初始化 9 位帧→设置地址与地址唤醒→持续发送并按收到翻灯→按键中断切换静默并唤醒
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 反复发送字节 0x33,UART2 收到后翻转 LED1; 按键触发中断后 UART1 发地址标识 0x102 唤醒处于静默模式的 UART2,演示多处理器地址唤醒。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、Delay;src/n32g033_it.c 含 EXTI0_6_IRQHandler;src/bsp_led.c/src/bsp_key.c 与对应头文件驱动 LED 与按键;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3(GPIO_AF1),UART2 Tx PA1、Rx PA0(GPIO_AF4),均复用推挽上拉;波特率 115200、9 数据位、1 停止位、无校验、16 倍过采样; 地址:UART1 取 0x1,UART2 取 0x2; 唤醒:UART2 采用地址标识唤醒; LED1 在 PA6。 |
| 程序与数据路径 | main -> RCC_Configuration、GPIO_Configuration -> LED_GPIO_Config、EXTI_Key_Config -> UART_Init 配 9 位帧 -> UART_Enable -> UART_SetAddr、UART_ConfigWakeUpMode 设地址与唤醒 -> 主循环 UART_SendData 发 0x33 并按收到翻灯 -> 按键中断 EXTI0_6_IRQHandler 切静默或发地址唤醒。 |
| 主要 API | UART_SetAddr、UART_ConfigWakeUpMode、UART_EnableRcvWakeUp、UART_SendData、UART_ReceiveData、UART_GetFlagStatus、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 一块板上 UART1 与 UART2 交叉连线; 全速运行后反复按 KEY,观察 LED1 是否按收到 0x33 闪烁、按一次后 UART2 进入静默停止响应。 |
| 使用提示 | 多处理器通信必须使用 9 位数据帧,第 9 位用于区分地址与数据; 静默模式由 UART_EnableRcvWakeUp 切换;开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用 9 位帧的第 9 位作地址标识,UART2 只有收到与自身地址匹配的帧才退出静默。
数据路径: UART_Init 配 9 位帧 → UART_Enable → UART_SetAddr 设地址 → UART_ConfigWakeUpMode 选地址唤醒 → 主循环 UART_SendData 发 0x33 并判 RXDNE 翻灯 → EXTI0_6_IRQHandler 用 UART_EnableRcvWakeUp 切静默、发 0x102 唤醒
1. 用 9 位帧并配置地址与唤醒方式。 9 位帧多出的一位用于标记地址,唤醒方式决定如何识别地址。
/* USARTy and USARTz configuration */
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate = 115200;
UART_InitStructure.WordLength = UART_WL_9B;
UART_InitStructure.StopBits = UART_STPB_1;
UART_InitStructure.Parity = UART_PE_NO;
UART_InitStructure.OverSampling = UART_16OVER;
UART_InitStructure.Mode = UART_MODE_RX | UART_MODE_TX;
/* Configure UARTy and USARTz */
UART_Init(UARTy, &UART_InitStructure);
UART_Init(UARTz, &UART_InitStructure);
/* Enable the UARTy and UARTz */
UART_Enable(UARTy, ENABLE);
UART_Enable(UARTz, ENABLE);
/* Set the UARTy and UARTz Address */
UART_SetAddr(UARTy, 0x1);
UART_SetAddr(UARTz, 0x2);
/* Select the UARTz WakeUp Method */
UART_ConfigWakeUpMode(UARTz, UART_WUM_ADDRMASK);
地址与唤醒方式要和通信对端约定一致,两端字长都必须取 9 位。
2. 主循环一直发数据并按接收翻灯。 收到 0x33 即闪灯,顺带读数据寄存器清溢出标志。
while (1)
{
/* Send one byte from UARTy to UARTz */
UART_SendData(UARTy, 0x33);
/* Wait while UARTy TXE = 0 */
while(UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
if (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) != RESET)
{
if (UART_ReceiveData(UARTz) == 0x33)
{
LED1_ON;
Delay(0x1FFFFF);
LED1_OFF;
Delay(0x1FFFFF);
}
/* Overflow data loss */
if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
{
UARTz->DAT;
}
}
}
3. 按键中断里切换静默与发送地址唤醒。 第一次进入静默,下一次发地址标识把对端唤醒。
void EXTI0_6_IRQHandler(void)
{
Delay(0x1FFF);;
if (ControlFlag == 2)
{
ControlFlag = 0;
}
if (ControlFlag == 0)
{
/* Flush DAT register and clear the UARTz RXNE flag */
UART_ReceiveData(UARTz);
/* Enable the UARTz mute mode*/
UART_EnableRcvWakeUp(UARTz, ENABLE);
ControlFlag = 1;
}
else if (ControlFlag == 1)
{
/* Send the address mark (0x102) to wakeup UARTz */
UART_SendData(UARTy, 0x102);
/* Wait while UARTy TXE = 0 */
while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
ControlFlag = 2;
}
/* Clear Key Button EXTI Line Pending Bit */
EXTI_ClrITPendBit(KEY1_INT_EXTI_LINE);
}
发地址标识前先写一次数据寄存器,避免残留数据被当作唤醒地址。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.11 Polling 轮询收发
工程目录: projects/n32g033_EVAL/examples/UART/Polling/
使能时钟→配置收发引脚→初始化并使能 UART→UART1 发、UART2 收→UART2 发、UART1 收→比对两组数据
| 项目 | 源码解读 |
|---|---|
| 功能 | UART1 与 UART2 交叉连接,两方向都靠查询标志位逐字节收发:UART1 发 TxBuffer1 由 UART2 接收,UART2 发 TxBuffer2 由 UART1 接收,最后分别比对。 |
| 源码组成 | src/main.c 含 main、RCC_Configuration、GPIO_Configuration、Buffercmp;inc/main.h 定义两个实例的引脚与复用宏;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART1 Tx PA2、Rx PA3(GPIO_AF1),UART2 Tx PA1、Rx PA0(GPIO_AF4),均复用推挽上拉;波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; 不使用中断与 DMA。 |
| 程序与数据路径 | main -> RCC_Configuration -> GPIO_Configuration -> UART_Init -> UART_Enable -> 轮询 TXDE 与 RXDNE 完成 UART1 到 UART2 的发送 -> 反向发送 UART2 到 UART1 -> Buffercmp 比对两组收发数据。 |
| 主要 API | UART_SendData、UART_ReceiveData、UART_GetFlagStatus、UART_Init、UART_StructInit、UART_Enable |
| 验证方法 | 一块板上 UART1 与 UART2 交叉连线; 全速运行时在 watch 窗口查看 TransferStatus1 与 TransferStatus2,PASSED 为通过。 |
| 使用提示 | 轮询方式会占住 CPU,适合入门验证与低速小数据量场景; 收发缓冲大小必须覆盖整帧字节数; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1; 测试前需断开 NS-LINK 的 MCU_TX/MCU_RX 跳线帽。 |
核心代码解读与移植
示例是最基础的收发写法,两个方向都以查询标志位完成,适合作为接入驱动函数的起点。
数据路径: UART_Init 配收发 → UART_Enable → 轮询 TXDE/RXDNE 完成正向发送 → 反向发送 → Buffercmp 比对两组缓冲
1. 初始化收发参数。 收发用的实例、字长、校验与过采样在此确定。
/* UARTy and UARTz configuration */
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate = 115200;
UART_InitStructure.WordLength = UART_WL_8B;
UART_InitStructure.StopBits = UART_STPB_1;
UART_InitStructure.Parity = UART_PE_NO;
UART_InitStructure.OverSampling = UART_16OVER;
UART_InitStructure.Mode = UART_MODE_RX | UART_MODE_TX;
/* Configure UARTy and UARTz */
UART_Init(UARTy, &UART_InitStructure);
UART_Init(UARTz, &UART_InitStructure);
/* Enable the UARTy and UARTz */
UART_Enable(UARTy, ENABLE);
UART_Enable(UARTz, ENABLE);
2. 正向轮询:UART1 发、UART2 收。 每送一个字节先等发送缓冲空,再等对端接收标志置位。
while (TxCounter1 < TxBufferSize1)
{
/* Send one byte from UARTy to UARTz */
UART_SendData(UARTy, TxBuffer1[TxCounter1++]);
/* Loop until UARTy DAT register is empty */
while (UART_GetFlagStatus(UARTy, UART_FLAG_TXDE) == RESET)
{
}
/* Loop until the UARTz Receive Data Register is not empty */
while (UART_GetFlagStatus(UARTz, UART_FLAG_RXDNE) == RESET)
{
/* Overflow data loss */
if(UART_GetFlagStatus(UARTz, UART_FLAG_OREF) != RESET)
{
UARTz->DAT;
}
}
/* Store the received byte in RxBuffer */
RxBuffer2[RxCounter2++] = UART_ReceiveData(UARTz);
}
接收等待里读一次数据寄存器是为清溢出标志,防止丢数后死等。
3. 反向轮询与结果比对。 反向发送把两个实例的角色对调,收发完成后分别比对两组数据。
/* Check the received data with the send ones */
/* TransferStatus = PASSED, if the data transmitted from UARTy and
received by UARTz are the same */
/* TransferStatus = FAILED, if the data transmitted from UARTy and
received by UARTz are different */
TransferStatus1 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
只有两个方向都收满才比对,避免半帧数据导致误判。
对应源码: src/main.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.3.12 Printf printf 重定向输出
工程目录: projects/n32g033_EVAL/examples/UART/Printf/
初始化日志串口→用 printf 输出字符串→主循环空转
| 项目 | 源码解读 |
|---|---|
| 功能 | 调用板级日志接口把 UART1 配成打印串口,再用 printf 把一行字符串从 UART1 输出到上位机,演示标准库输出重定向到 UART。 |
| 源码组成 | src/main.c 只含 main,调用 log_init 与 printf;串口配置与 fputc 重定向在 projects/n32g033_EVAL/bsp/src/log.c 与 bsp/inc/log.h;inc/main.h 仅包含公共头文件;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 由日志接口配置 UART1,TX PA9、RX PA10(GPIO_AF4)复用推挽上拉;波特率 115200、8 数据位、1 停止位、无校验、16 倍过采样; 日志使能宏 LOG_ENABLE 需打开。 |
| 程序与数据路径 | main -> log_init 配置并使能 UART1 -> printf 输出字符串 -> fputc 逐字符经 UART_SendData 发出 -> 主循环空转。 |
| 主要 API | 主函数不直接调用 UART 驱动函数,日志串口由板级 log_init(内部调用 UART_Init、UART_Enable 与 UART_SendData)配置 |
| 验证方法 | 串口工具接 PA9,波特率设为 115200; 复位后应收到字符串 This is a Printf demo of UART.。 |
| 使用提示 | 重定向依赖 fputc,工程需链接到 bsp 的 log.c;换用其他 UART 打印时改 log.c 顶部的实例与引脚宏即可;开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用板级日志组件把 UART1 接到标准输出,主函数只需初始化与打印。
数据路径: log_init 配置并使能 UART1 → printf 进入标准库 → fputc 接管输出 → UART_SendData 逐字符发出
1. 初始化日志串口并打印。 主函数只做两件事:开串口、输出一行字符串。
int main(void)
{
/* Configure UART1 */
log_init();
/* Use the printf function to output a message on the upper computer */
printf("This is a Printf demo of UART.\n\r");
while (1)
{
}
}
2. 日志接口内部完成 UART1 的引脚与参数配置。 打印通道的引脚、波特率与校验都在这几行定下。
RCC_EnableAPBPeriphClk(LOG_PERIPH | LOG_PERIPH_GPIO, ENABLE);
GPIO_InitStructure.Pin = LOG_TX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF4;
GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitPeripheral(LOG_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = LOG_RX_PIN;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF4;
GPIO_InitPeripheral(LOG_GPIO, &GPIO_InitStructure);
USART_InitStructure.BaudRate = 115200;
USART_InitStructure.WordLength = UART_WL_8B;
USART_InitStructure.StopBits = UART_STPB_1;
USART_InitStructure.Parity = UART_PE_NO;
USART_InitStructure.Mode = UART_MODE_RX | UART_MODE_TX;
USART_InitStructure.OverSampling = UART_16OVER;
// init uart
UART_Init(LOG_UARTx, &USART_InitStructure);
// enable uart
UART_Enable(LOG_UARTx, ENABLE);
实例与引脚由 log.c 顶部的宏给出,换串口时只改宏而不动调用处。
3. 重定向字符输出并等待发送完成。 每个字符都要等整帧发送完成标志,保证输出不丢字符。
int fputc(int ch, FILE* f)
{
if (ch == '\r')
{
is_lr_sent = 1;
}
else if (ch == '\n')
{
if (!is_lr_sent)
{
/* Loop until the end of transmission */
while (UART_GetFlagStatus(LOG_UARTx, UART_FLAG_TXC) == RESET)
{
}
UART_SendData(LOG_UARTx, (uint8_t)'\r');
}
is_lr_sent = 0;
}
else
{
is_lr_sent = 0;
}
/* Loop until the end of transmission */
while (UART_GetFlagStatus(LOG_UARTx, UART_FLAG_TXC) == RESET)
{
}
UART_SendData(LOG_UARTx, (uint8_t)ch);
return ch;
}
这里对换行做了补回车处理,方便上位机串口工具正常显示每一行。
对应源码: src/main.c、projects/n32g033_EVAL/bsp/src/log.c
5.19.3.13 WakeUpFromStop UART3 低功耗唤醒
工程目录: projects/n32g033_EVAL/examples/UART/WakeUpFromStop/
使能 PWR、UART3 与 GPIO 时钟→配置 UART3 引脚与 EXTI 事件线→切换系统时钟与 UART3 时钟到 LSI→初始化 UART3 并持续发送→收到 0x55 后配置唤醒并进入 STOP→唤醒后重新初始化 UART3
| 项目 | 源码解读 |
|---|---|
| 功能 | UART3 每秒发送一个递增字节; 收到 0x55 后配置数据匹配唤醒并进入 STOP 模式,靠接收线上的下降沿与数据匹配唤醒,苏醒后重新初始化 UART3 继续发送。 |
| 源码组成 | src/main.c 含 main、GPIO_Configuration、SetSysClock_LSI;inc/main.h 定义 UART3 实例与引脚;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:UART3 Tx PA2、Rx PA3(GPIO_AF6)复用推挽上拉;波特率 2000、8 数据位、1 停止位、无校验、16 倍过采样; 时钟:系统时钟切到 LSI(约 32 kHz),UART3 也取 LSI; 唤醒:数据匹配清除方式,EXTI10 配置为双边沿事件。 |
| 程序与数据路径 | main -> RCC_EnableAPBPeriphClk 开 PWR/UART3/GPIO -> GPIO_Configuration -> EXTI_InitPeripheral 配 EXTI10 -> SetSysClock_LSI 与 RCC_ConfigUart3Clk -> UART_Init 并使能 UART3 -> 主循环每秒 UART_SendData 递增 -> 收到 0x55 后 UART_CfgWakeupMode 与 PWR_EnterSTOPMode -> 唤醒后 UART_DeInit 与 UART_Init 重配 UART3。 |
| 主要 API | UART_CfgWakeupMode、UART_DeInit、UART_Init、UART_StructInit、UART_Enable、UART_SendData、UART_ReceiveData、UART_GetFlagStatus |
| 验证方法 | 串口工具接 PA2/PA3,波特率 2000; 复位后收到递增数据,下发 0x55 后停止发送,再次下发 0x55 后恢复发送。 |
| 使用提示 | 只有 UART3 支持低功耗唤醒,且波特率低、需以 LSI 为时钟源; 进入 STOP 前中断需已使能,唤醒后 UART 寄存器要重新初始化; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把系统时钟降到 LSI 并让 UART3 用它工作,收到约定字节后进入 STOP,验证 UART3 低功耗唤醒。
数据路径: RCC_EnableAPBPeriphClk 开时钟 → GPIO_Configuration → SetSysClock_LSI 与 RCC_ConfigUart3Clk → UART_Init 与 UART_Enable → 主循环发送 → 收到 0x55 后 UART_CfgWakeupMode 与 PWR_EnterSTOPMode → 唤醒后 UART_DeInit 与 UART_Init 重配
1. 开时钟、配 EXTI 事件线并切换时钟源。 唤醒依赖接收线电平变化,因此先配好 EXTI10 事件线再切时钟。
/* Enable PWR/UART3/GPIO Clock */
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_PWR | RCC_APB_PERIPH_UART3 | RCC_APB_PERIPH_GPIO, ENABLE);
/* Configure the GPIO ports */
GPIO_Configuration();
/*Configure key EXTI line*/
EXTI_InitStructure.EXTI_Line = EXTI_LINE10;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
/* Configure the system clock as LSI */
SetSysClock_LSI();
/* Choose LSI as the clock source for UART3 */
RCC_ConfigUart3Clk(RCC_UART3_CLKSRC_LSI);
2. 主循环发送并在收到约定字节后进入 STOP。 数据匹配唤醒会清掉匹配标志,苏醒后要重配 UART3。
while (1)
{
/* Insert a long delay */
SysTick_Delay_Ms(1000);
if(UART_GetFlagStatus(UART3,UART_FLAG_RXDNE) == SET)
{
if(UART_ReceiveData(UART3) == 0x55)
{
/* configure uart3 wakeup mode */
UART_CfgWakeupMode(UART3,UART_WAKEUP_DATACLR,UART_WAKEUP_ENABLE);
/* Enter STOP mode */
PWR_EnterSTOPMode(PWR_STOPENTRY_WFE);
/* Configure UARTy and UARTz */
UART_DeInit(UARTy);
UART_Init(UARTy, &UART_InitStructure);
/* Enable the UARTy and UARTz */
UART_Enable(UARTy, ENABLE);
}
}
else
{
UART_SendData(UART3,cnt_temp++);
}
写数据寄存器清除的唤醒方式要求收到匹配字节后读取一次数据寄存器,代码已由 UART_ReceiveData 完成。
3. 切换到 LSI 系统时钟。 低功耗唤醒需以 LSI 为时钟源,切换时同时降低 Flash 等待周期。
ErrorStatus SetSysClock_LSI(void)
{
uint32_t timeout_value = 0xFFFFFFFF;
/* Wait till LSI is ready */
if (RCC_GetFlagStatus(RCC_CLKINT_FLAG_LSIRDF) == SET)
{
/* HCLK = SYSCLK */
RCC_ConfigHclk(RCC_SYSCLK_DIV1);
/* PCLK = HCLK */
RCC_ConfigPclk(RCC_HCLK_DIV1);
RCC_ConfigSysclk(RCC_SYSCLK_SRC_LSI);
while (RCC_GetSysclkSrc() != RCC_CFG_SCLKSTS_LSI)
{
if ((timeout_value--) == 0)
{
return ERROR;
}
}
FLASH_SetLatency(FLASH_LATENCY_0);
}
else
{
/* If LSI fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
return ERROR;
}
return SUCCESS;
}
对应源码: src/main.c、firmware/n32g033_std_periph_driver/inc/n32g033_uart.h
5.19.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 通信中修改波特率寄存器 | 用户手册 UART 章写明波特率寄存器 BRCF 在 UART 使能(UEN=1)时不可写,通信过程中也不得修改; 需要修改时应先关闭 UART,改完再使能。 |
用户手册 |
| 通信中修改字长 | 用户手册 UART 章写明字长(WL)在传输过程中不得改变; 需要修改时应先停止收发。 |
用户手册 |
| 使用 0.5 位停止位 | 用户手册 UART 章写明 0.5 位停止位不对停止位采样,无法检测帧错误与断开帧; 需要上述检测时不要使用 0.5 位停止位。 |
用户手册 |
| 单线半双工模式 | 用户手册 UART 章写明使用单线半双工前需确认 LINMEN 与 IRDAMEN 均为 0; 不使用该模式时 TX 引脚应处于浮空或开漏高电平,避免总线被拉住。 |
用户手册 |
| 使用 IrDA 模式 | 用户手册 UART 章写明使用 IrDA 需把停止位配成 0 并关闭 LINMEN、HDMEN; 正常模式预分频只能取 1,最大波特率 115200,发送与接收之间至少间隔 10 ms。 |
用户手册 |
| 使用 LIN 模式 | 用户手册 UART 章写明使用 LIN 需把停止位配成 0 并关闭 HDMEN、IRDAMEN; LIN 只支持 8 位数据帧,OSPM 位须保持为 0。 |
用户手册 |
| UART3 低功耗唤醒 | 用户手册 UART 章写明只有 UART3 支持低功耗唤醒; 进入 STOP 前使能唤醒并配好时钟源,唤醒后需重新初始化 UART 寄存器。 |
用户手册 |
勘误手册 V1.0.0(2025.9.25)针对 A 版芯片,未列出 UART 相关条目。