[经验分享] 查询和中断接收发送冲突 的解决办法

[复制链接]
61|13
youtome 发表于 2026-1-18 21:59 | 显示全部楼层 |阅读模式
代码中printf无法正常输出以及接收逻辑不响应的问题,主要源于中断与查询方式的冲突和接收标志位未正确设置,具体分析和修正如下:

一、核心问题分析
1. printf失效:串口中断与sendByte查询逻辑冲突
printf依赖putchar,而putchar调用sendByte发送字符。sendByte使用查询方式(while(!TI)等待发送完成),但你同时开启了串口中断(ES=1):

当SBUF发送完成时,TI会被硬件置 1,此时会触发串口中断,进入uart_ISR。
在中断服务程序中,你执行了TI=0(清除发送标志位),但此时sendByte中的while(!TI)还未退出 —— 中断提前清零TI,导致while循环***等待(TI已被清 0),sendByte被卡死,最终printf无法输出。
2. 接收逻辑不响应:recv_flag未被正确置位
在uart_ISR中,接收数据后仅读取了SBUF到recv_data,但未设置recv_flag=1。

main函数中if(recv_flag==1)的条件***不成立,导致接收后的数据处理逻辑(+1 回发、打印)无法执行。
二、修正后的代码
  1. #include "uart.h"
  2. #include <stdio.h>  // 确保包含stdio.h,支持printf

  3. unsigned char recv_data;
  4. unsigned char recv_flag = 0;

  5. void UartInit(void)                //4800bps@11.0592MHz
  6. {
  7.         PCON &= 0x7F;                // 波特率不倍速
  8.         SCON = 0x50;                // 8位数据,允许接收(方式1)
  9.         TI = 0;             // 初始化清除发送标志位
  10.         RI = 0;             // 初始化清除接收标志位
  11.        
  12.         TMOD &= 0x0F;                // 清除定时器1模式位
  13.         TMOD |= 0x20;                // 定时器1为8位自动重装方式
  14.         TL1 = 0xFA;                // 4800bps初值(正确)
  15.         TH1 = 0xFA;                // 重装值
  16.         ET1 = 0;                // 禁止定时器1中断
  17.         TR1 = 1;                // 启动定时器1  
  18.        
  19.         EA = 1;                // 打开总中断
  20.         ES = 1;                // 打开串口中断
  21. }

  22. // 发送单个字节(关键:发送时关闭串口中断,避免TI被提前清零)
  23. void sendByte(unsigned char dat)
  24. {
  25.         ES = 0;          // 关闭串口中断,防止TI被中断服务程序清零
  26.         SBUF = dat;
  27.         while(!TI);      // 等待发送完成
  28.         TI = 0;          // 手动清除发送标志位
  29.         ES = 1;          // 恢复串口中断
  30. }

  31. void sendString(unsigned char *dat)
  32. {
  33.         while(*dat != '\0')
  34.         {
  35.                 sendByte(*dat++);
  36.         }
  37. }

  38. // 重写putchar,供printf调用
  39. char putchar(char c)
  40. {
  41.         sendByte((unsigned char)c);
  42.         return c;
  43. }

  44. // 串口中断服务程序
  45. void uart_ISR() interrupt 4
  46. {
  47.         if(RI)                        // 接收中断
  48.         {
  49.                 RI = 0;                // 清除接收标志位
  50.                 recv_data = SBUF;        // 读取接收数据
  51.                 recv_flag = 1;                // 置位接收完成标志(关键修正)
  52.         }
  53.         if(TI)                        // 发送中断(仅清除标志,不做其他操作)
  54.         {
  55.                 TI = 0;
  56.         }
  57. }
  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. #include "uart.h"
  4. #include <stdio.h>  // 确保包含stdio.h

  5. void main()
  6. {
  7.         UartInit();
  8.         printf("wait for serial communication test start\r\n");
  9.         printf("please send a frame of data\r\n");
  10.        
  11.         while(1)
  12.         {                       
  13.                 if(recv_flag == 1)
  14.                 {
  15.                         recv_flag = 0;
  16.                         recv_data += 1;        // 接收数据+1
  17.                         sendByte(recv_data);  // 回发+1后的数据
  18.                         printf("\r\n");
  19.                         printf("%bd\r\n", recv_data);  // 打印+1后的数据
  20.                 }
  21.         }
  22. }
三、修正说明
解决printf失效:
在sendByte中添加ES=0和ES=1,发送时临时关闭串口中断,避免中断服务程序提前清零TI,确保while(!TI)能正常等待发送完成。
激活接收逻辑:
在uart_ISR的接收部分添加recv_flag=1,让main函数能检测到接收完成事件,执行后续处理。
其他注意:
确保终端(串口助手)配置为4800bps、8 位数据、1 位停止位、无校验。
包含stdio.h头文件,否则printf可能无法被编译器识别。
四、其他方式

只在中断中处理 接收后加1 发送出的数据, 不单独调用 sendbyte函数
  1. void uart_ISR() interrupt 4
  2. {
  3.         if(RI)
  4.         {
  5.                 recv_data=SBUF;
  6.                 SBUF=recv_data+1;
  7.                 RI=0;
  8.         }
  9.         if(TI)
  10.         {
  11.                 TI=0;
  12.         }

  13. }


sheflynn 发表于 2026-2-3 19:44 | 显示全部楼层
串口通信的可靠性比速度更重要。              
hudi008 发表于 2026-2-3 21:49 | 显示全部楼层
选择简单可靠的方案,添加充分的错误处理和调试信息
hilahope 发表于 2026-2-3 22:10 | 显示全部楼层
使用查询方式              
modesty3jonah 发表于 2026-2-4 16:49 | 显示全部楼层
完全避免查询与中断冲突              
uiint 发表于 2026-2-4 22:59 | 显示全部楼层
开启了串口中断              
lzbf 发表于 2026-2-5 11:28 | 显示全部楼层
将查询发送改为中断驱动,避免主程序阻塞。
earlmax 发表于 2026-2-5 15:20 | 显示全部楼层
临时关闭中断              
1988020566 发表于 2026-2-5 18:14 | 显示全部楼层
逻辑简单直观,无并发冲突风险              
burgessmaggie 发表于 2026-2-5 20:41 | 显示全部楼层
必须声明变量为 volatile 防止编译器优化出错!
fengm 发表于 2026-2-5 21:54 | 显示全部楼层
关键在于合理管理中断标志位和使用缓冲区来处理发送和接收数据,避免查询方式与中断方式之间的冲突。
mollylawrence 发表于 2026-2-5 22:58 | 显示全部楼层
发送采用环形缓冲区,避免数据覆盖,支持批量发送。
ulystronglll 发表于 2026-2-6 10:25 | 显示全部楼层
同一时刻只能有一种控制流主导串口资源
jtracy3 发表于 2026-2-6 17:46 | 显示全部楼层
查询与中断的根本冲突              
您需要登录后才可以回帖 登录 | 注册

本版积分规则

50

主题

4401

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部
0