Sigfox LPWAN技术赋能深海物联网监测:从低功耗设计到海洋环境感知
2026/5/8 16:21:39
串口中断的核心是“事件触发”:
| 特性 | 查询模式 | 中断模式 |
|---|---|---|
| CPU占用 | 100%(持续轮询寄存器) | 极低(仅中断时响应) |
| 实时性 | 差(可能错过数据) | 高(数据到达立即处理) |
| 系统并发性 | 无(CPU被轮询占用) | 好(CPU可处理其他任务) |
| 适用场景 | 简单小数据收发 | 实时性要求高、多任务场景 |
以STM32F103C8T6的USART1为例,先完成串口基础初始化,再开启中断配置。
#include"stm32f1xx_hal.h"/* 全局变量定义 */UART_HandleTypeDef huart1;// 串口1句柄uint8_tuart1_rx_buf[1024]={0};// 接收缓冲区uint16_tuart1_rx_len=0;// 已接收数据长度uint8_tuart1_rx_flag=0;// 接收完成标志(0:未完成,1:完成)uint8_tuart1_tx_buf[1024]={0};// 发送缓冲区uint16_tuart1_tx_len=0;// 待发送数据长度uint16_tuart1_tx_index=0;// 发送数据索引/* 串口1初始化函数(115200 8N1:8位数据、无校验、1位停止位) */voidMX_USART1_UART_Init(void){huart1.Instance=USART1;// 串口1huart1.Init.BaudRate=115200;// 波特率huart1.Init.WordLength=UART_WORDLENGTH_8B;// 8位数据位huart1.Init.StopBits=UART_STOPBITS_1;// 1位停止位huart1.Init.Parity=UART_PARITY_NONE;// 无校验huart1.Init.Mode=UART_MODE_TX_RX;// 同时开启收发huart1.Init.HwFlowCtl=UART_HWCONTROL_NONE;// 无硬件流控huart1.Init.OverSampling=UART_OVERSAMPLING_16;if(HAL_UART_Init(&huart1)!=HAL_OK){Error_Handler();// 初始化失败处理}}/* HAL库底层硬件初始化(引脚、时钟、中断) */voidHAL_UART_MspInit(UART_HandleTypeDef*uartHandle){GPIO_InitTypeDef GPIO_InitStruct={0};if(uartHandle->Instance==USART1){/* 1. 使能时钟 */__HAL_RCC_USART1_CLK_ENABLE();// USART1时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();// GPIOA时钟使能(USART1对应PA9/PA10)/* 2. 配置TX/RX引脚 */// PA9(TX):推挽复用输出GPIO_InitStruct.Pin=GPIO_PIN_9;GPIO_InitStruct.Mode=GPIO_MODE_AF_PP;GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);// PA10(RX):浮空输入GPIO_InitStruct.Pin=GPIO_PIN_10;GPIO_InitStruct.Mode=GPIO_MODE_INPUT;GPIO_InitStruct.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);/* 3. 配置串口中断(核心!) */HAL_NVIC_SetPriority(USART1_IRQn,0,0);// 设置中断优先级(主优先级0,子优先级0)HAL_NVIC_EnableIRQ(USART1_IRQn);// 使能USART1中断}}/* 错误处理函数 */voidError_Handler(void){__disable_irq();while(1){// 可添加LED闪烁等错误提示}}适用于接收已知长度的数据,每收到1个字节触发一次中断,逐字节接收。
/* 启动串口1接收中断(逐字节接收) */voidUART1_Start_RXNE_IT(void){__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);// 清除RXNE标志__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);// 开启RXNE中断(接收非空)}串口中断需在USART1_IRQHandler中处理,先判断中断类型,再读取数据:
/* USART1中断服务函数 */voidUSART1_IRQHandler(void){uint8_trecv_data=0;// 处理RXNE中断(接收非空)if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)!=RESET&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_RXNE)!=RESET){recv_data=huart1.Instance->DR;// 读取接收寄存器数据(自动清除RXNE标志)// 数据存入接收缓冲区if(uart1_rx_len<1023)// 防止缓冲区溢出{uart1_rx_buf[uart1_rx_len++]=recv_data;// 示例:以'\n'作为接收完成标志if(recv_data=='\n'){uart1_rx_flag=1;// 置位接收完成标志}}else{uart1_rx_len=0;// 缓冲区满,重置}}// 其他中断处理(如错误中断)HAL_UART_IRQHandler(&huart1);}intmain(void){/* 系统初始化 */HAL_Init();SystemClock_Config();// 需自行实现系统时钟配置(如72MHz)MX_USART1_UART_Init();/* 启动接收中断 */UART1_Start_RXNE_IT();/* 主循环 */while(1){// 检测到接收完成if(uart1_rx_flag==1){// 回显接收到的数据(查询模式发送,简化示例)HAL_UART_Transmit(&huart1,uart1_rx_buf,uart1_rx_len,1000);// 重置标志和缓冲区uart1_rx_flag=0;uart1_rx_len=0;memset(uart1_rx_buf,0,sizeof(uart1_rx_buf));}// 其他任务(如LED闪烁)HAL_Delay(100);}}IDLE中断在“接收线空闲”时触发,无需指定数据长度,适合接收不定长的字符串/数据帧(如上位机发送的任意长度指令)。
/* 启动串口1 IDLE中断(接收不定长数据) */voidUART1_Start_IDLE_IT(void){__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);// 清除IDLE标志__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);// 开启IDLE中断__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);// 开启RXNE中断(保证逐字节接收)// 启动HAL库接收中断(可选,辅助管理)HAL_UART_Receive_IT(&huart1,&uart1_rx_buf[uart1_rx_len],1);}voidUSART1_IRQHandler(void){uint32_ttemp;uint8_trecv_data=0;// 处理RXNE中断(逐字节接收数据)if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)!=RESET&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_RXNE)!=RESET){recv_data=huart1.Instance->DR;if(uart1_rx_len<1023){uart1_rx_buf[uart1_rx_len++]=recv_data;// 继续开启HAL接收中断HAL_UART_Receive_IT(&huart1,&uart1_rx_buf[uart1_rx_len],1);}}// 处理IDLE中断(接收完成)if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=RESET&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_IDLE)!=RESET){__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除IDLE标志(必须手动清除)// 读SR和DR寄存器,确保标志清除彻底temp=huart1.Instance->SR;temp=huart1.Instance->DR;uart1_rx_flag=1;// 置位接收完成标志}// HAL库默认中断处理(处理错误等)HAL_UART_IRQHandler(&huart1);}仅需将UART1_Start_RXNE_IT()替换为UART1_Start_IDLE_IT(),其余逻辑不变。
发送中断在“发送寄存器为空”时触发,适用于大数据量发送(避免CPU等待),逐字节自动填充数据。
/* 启动串口1发送中断(发送指定长度数据) */voidUART1_Start_TXE_IT(uint8_t*data,uint16_tlen){if(len==0||data==NULL)return;// 拷贝数据到发送缓冲区memcpy(uart1_tx_buf,data,len);uart1_tx_len=len;uart1_tx_index=0;// 清除TXE标志,开启发送中断__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_TXE);__HAL_UART_ENABLE_IT(&huart1,UART_IT_TXE);// 发送第一个字节(触发首次TXE中断)huart1.Instance->DR=uart1_tx_buf[uart1_tx_index++];}在USART1_IRQHandler中补充TXE中断逻辑:
voidUSART1_IRQHandler(void){// 原有RXNE/IDLE中断处理...// 处理TXE中断(发送寄存器空)if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TXE)!=RESET&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_TXE)!=RESET){if(uart1_tx_index<uart1_tx_len){// 发送下一个字节huart1.Instance->DR=uart1_tx_buf[uart1_tx_index++];}else{// 发送完成,关闭TXE中断__HAL_UART_DISABLE_IT(&huart1,UART_IT_TXE);// 可选:置位发送完成标志}}HAL_UART_IRQHandler(&huart1);}intmain(void){// 系统初始化...UART1_Start_IDLE_IT();// 启动接收中断uint8_tsend_data[]="Hello UART Interrupt!\r\n";UART1_Start_TXE_IT(send_data,sizeof(send_data)-1);// 启动发送中断while(1){// 接收数据处理...}}51单片机串口中断配置更简单,以STC89C52为例,对比理解:
#include<reg52.h>#defineFOSC11059200L#defineBAUD9600#defineTIMER1_VAL(256-FOSC/(16*BAUD))unsignedcharuart_rx_buf[32]={0};unsignedcharuart_rx_len=0;/* 串口初始化(9600波特率,中断模式) */voidUART_Init(void){TMOD|=0x20;// 定时器1工作在模式2(8位自动重装)TH1=TIMER1_VAL;TL1=TIMER1_VAL;TR1=1;// 启动定时器1SCON=0x50;// 串口模式1,REN=1(允许接收)EA=1;// 开启总中断ES=1;// 开启串口中断}/* 串口中断服务函数 */voidUART_ISR(void)interrupt4{if(RI)// 接收中断标志{RI=0;// 清除接收标志uart_rx_buf[uart_rx_len++]=SBUF;// 读取数据if(uart_rx_len>=31)uart_rx_len=0;}if(TI)// 发送中断标志{TI=0;// 清除发送标志}}/* 中断模式发送字节 */voidUART_Send_Byte(unsignedchardat){SBUF=dat;// 写入发送寄存器,触发发送while(!TI);// 等待发送完成(51无TXE中断,需轮询,仅接收为纯中断)TI=0;}voidmain(void){UART_Init();while(1){if(uart_rx_len>0){// 回显数据for(inti=0;i<uart_rx_len;i++){UART_Send_Byte(uart_rx_buf[i]);}uart_rx_len=0;}}}| 问题现象 | 核心原因 | 解决方案 |
|---|---|---|
| 中断不触发 | 1. 中断未使能;2. 优先级配置错误;3. 标志未清除 | 1. 检查HAL_NVIC_EnableIRQ();2. 确认优先级未被屏蔽;3. 中断前清除标志 |
| 接收数据乱码 | 1. 波特率不匹配;2. 时钟配置错误;3. 缓冲区溢出 | 1. 核对串口参数(115200 8N1);2. 确认STM32系统时钟(如72MHz);3. 增加缓冲区或限制长度 |
| 数据丢失 | 1. 中断响应过慢;2. 缓冲区未及时处理;3. IDLE标志未清除 | 1. 提高中断优先级;2. 主循环快速处理接收数据;3. 手动清除IDLE标志(读SR+DR) |
| 发送中断死循环 | 发送索引越界 | 严格判断uart1_tx_index < uart1_tx_len,发送完成关闭中断 |
if(uart1_rx_len < 1023))。