基于C语言实现GD32的USART中断通信:从初始化到上位机数据交互全流程
USART(通用同步/异步收发器)是MCU与外部设备通信最基础也最常用的外设之一。在GD32系列MCU中,USART支持全双工异步通信,通过TX/RX两根信号线即可实现与PC上位机、蓝牙模块、其他MCU的数据交互。
中断驱动是USART通信的核心机制。相比轮询方式(CPU不断查询状态寄存器),中断方式在数据到达时由硬件自动触发中断服务函数(ISR),CPU仅在真正有数据需要处理时才介入,其余时间可执行其他任务或进入低功耗模式。对于115200bps的波特率,每个字节传输约87μs,若采用轮询方式,CPU将被大量无效查询消耗;而中断方式下,CPU仅在每87μs被唤醒一次处理一个字节,效率提升数十倍。
本方案涉及的关键原理包括:波特率发生器通过分频系统时钟产生精确的采样时钟(GD32的USART波特率寄存器BRR直接写入分频系数);接收数据寄存器(RDR)在接收到完整帧后自动置位RXNE标志并触发中断;发送数据寄存器(TDR)在数据移入移位寄存器后自动置位TC标志;NVIC(嵌套向量中断控制器)负责管理中断优先级和使能。
上位机通信协议采用"帧头 + 长度 + 命令码 + 数据 + 校验"的自定义格式,上位机通过USB转TTL模块(如CH340/CP2102)连接GD32的USART引脚,以115200bps、8N1格式收发数据。
程序框架分析说明
整个USART中断通信程序采用分层架构,自底向上分为四层:
硬件抽象层(HAL):负责GPIO复用配置(TX引脚设为复用推挽输出,RX引脚设为复用浮空输入)、USART寄存器初始化(波特率、数据位、停止位、校验位)、NVIC中断优先级配置。
驱动层(Driver):封装收发接口。接收侧维护一个环形缓冲区(Ring Buffer),ISR中每收到一个字节即写入缓冲区;发送侧提供usart_send_packet()函数,将应用层数据打包为协议帧后逐字节写入TDR,通过TC中断实现非阻塞发送。
协议解析层(Protocol):实现帧同步与解析。采用状态机模型(空闲→接收帧头→接收长度→接收数据→校验→完成),逐字节解析接收缓冲区中的数据,提取完整帧后回调应用层处理函数。
应用层(App):根据命令码执行具体业务逻辑(如读取传感器数据、控制LED、返回设备信息等),并将响应数据通过usart_send_packet()回传上位机。
程序实现
1. USART与GPIO初始化
以GD32F407的USART0(PA9-TX, PA10-RX)为例,配置115200bps、8数据位、1停止位、无校验:
void usart_init(void)
{
/* 使能时钟 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
/* PA9: TX - 复用推挽输出 */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
/* PA10: RX - 复用浮空输入 */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
/* USART参数配置 */
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
/* 使能接收中断 */
usart_interrupt_enable(USART0, USART_INT_RBNE);
/* NVIC配置:USART0中断,抢占优先级1,子优先级0 */
nvic_irq_enable(USART0_IRQn, 1, 0);
usart_enable(USART0);
}
2. 环形缓冲区与接收中断服务函数
#define RX_BUF_SIZE 256
typedef struct {
uint8_t buf[RX_BUF_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
} RingBuffer_t;
RingBuffer_t rx_ring = {{0}, 0, 0};
void ring_buffer_write(RingBuffer_t *rb, uint8_t data)
{
uint16_t next = (rb->head + 1) % RX_BUF_SIZE;
if (next != rb->tail) { /* 缓冲区未满 */
rb->buf[rb->head] = data;
rb->head = next;
}
}
uint8_t ring_buffer_read(RingBuffer_t *rb)
{
if (rb->head == rb->tail) return 0xFF; /* 缓冲区为空 */
uint8_t data = rb->buf[rb->tail];
rb->tail = (rb->tail + 1) % RX_BUF_SIZE;
return data;
}
/* USART0中断服务函数 */
void USART0_IRQHandler(void)
{
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t byte = usart_data_receive(USART0);
ring_buffer_write(&rx_ring, byte);
}
}
3. 协议帧解析状态机
#define FRAME_HEAD 0xAA
#define FRAME_MAX_LEN 64
typedef enum {
STATE_IDLE,
STATE_LEN,
STATE_CMD,
STATE_DATA,
STATE_CHECK
} ParseState_t;
typedef struct {
uint8_t cmd;
uint8_t len;
uint8_t data[FRAME_MAX_LEN];
} ParsedFrame_t;
ParseState_t parse_state = STATE_IDLE;
uint8_t parse_idx = 0;
ParsedFrame_t current_frame;
void protocol_parse(void)
{
while (rx_ring.head != rx_ring.tail) {
uint8_t byte = ring_buffer_read(&rx_ring);
switch (parse_state) {
case STATE_IDLE:
if (byte == FRAME_HEAD) parse_state = STATE_LEN;
break;
case STATE_LEN:
if (byte > FRAME_MAX_LEN) {
parse_state = STATE_IDLE; /* 长度非法,丢弃 */
} else {
current_frame.len = byte;
parse_idx = 0;
parse_state = STATE_CMD;
}
break;
case STATE_CMD:
current_frame.cmd = byte;
parse_state = (current_frame.len > 0) ? STATE_DATA : STATE_CHECK;
break;
case STATE_DATA:
current_frame.data[parse_idx++] = byte;
if (parse_idx >= current_frame.len) parse_state = STATE_CHECK;
break;
case STATE_CHECK:
/* 校验:帧头 + 长度 + 命令 + 数据 的累加和 */
uint8_t checksum = FRAME_HEAD + current_frame.len
+ current_frame.cmd;
for (int i = 0; i < current_frame.len; i++)
checksum += current_frame.data[i];
if (checksum == byte) {
app_handle_frame(¤t_frame); /* 帧完整,回调应用层 */
}
parse_state = STATE_IDLE;
break;
}
}
}
4. 发送接口与应用层处理
void usart_send_byte(uint8_t data)
{
usart_data_transmit(USART0, data);
while (RESET == usart_flag_get(USART0, USART_FLAG_TBE));
}
void usart_send_packet(uint8_t cmd, uint8_t *data, uint8_t len)
{
uint8_t checksum = FRAME_HEAD + len + cmd;
usart_send_byte(FRAME_HEAD);
usart_send_byte(len);
usart_send_byte(cmd);
for (int i = 0; i < len; i++) {
usart_send_byte(data[i]);
checksum += data[i];
}
usart_send_byte(checksum);
}
/* 应用层帧处理 */
void app_handle_frame(ParsedFrame_t *frame)
{
switch (frame->cmd) {
case 0x01: /* 读取设备ID */
{
uint8_t resp[] = {0x01, 0x02, 0x03, 0x04};
usart_send_packet(0x81, resp, 4);
break;
}
case 0x02: /* 控制LED */
if (frame->data[0]) gpio_bit_set(GPIOC, GPIO_PIN_13);
else gpio_bit_reset(GPIOC, GPIO_PIN_13);
usart_send_packet(0x82, NULL, 0); /* ACK */
break;
}
}
int main(void)
{
system_init();
usart_init();
while (1) {
protocol_parse(); /* 主循环中持续解析接收数据 */
}
}
应用
上位机调试工具:PC端通过串口助手或自研上位机软件发送命令帧,实时读取GD32的ADC采样值、GPIO状态、寄存器配置等,实现参数在线调整和固件调试。
传感器数据上报:GD32采集温湿度、压力、加速度等传感器数据,按协议帧格式打包后通过USART上报至上位机或网关设备,上位机实时绘制波形曲线。
固件OTA升级:上位机通过USART发送固件分片数据帧,GD32接收后写入Flash指定区域,校验通过后跳转执行新固件,实现串口IAP升级。
多设备级联通信:多个GD32节点通过USART串联(RS485总线),上位机以广播帧下发命令,各节点根据设备地址过滤响应,实现一对多控制。
蓝牙/WiFi透传:GD32的USART连接蓝牙模块(如HC-05),手机APP通过蓝牙发送数据,GD32接收解析后执行对应操作,实现无线远程控制。
通过中断驱动 + 环形缓冲区 + 协议状态机的三层架构,本方案在保证通信可靠性的同时,将CPU占用率降至最低,为GD32与上位机的高效数据交互提供了一套完整且可复用的实现框架。





