代码中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 回发、打印)无法执行。
二、修正后的代码
- #include "uart.h"
- #include <stdio.h> // 确保包含stdio.h,支持printf
- unsigned char recv_data;
- unsigned char recv_flag = 0;
- void UartInit(void) //4800bps@11.0592MHz
- {
- PCON &= 0x7F; // 波特率不倍速
- SCON = 0x50; // 8位数据,允许接收(方式1)
- TI = 0; // 初始化清除发送标志位
- RI = 0; // 初始化清除接收标志位
-
- TMOD &= 0x0F; // 清除定时器1模式位
- TMOD |= 0x20; // 定时器1为8位自动重装方式
- TL1 = 0xFA; // 4800bps初值(正确)
- TH1 = 0xFA; // 重装值
- ET1 = 0; // 禁止定时器1中断
- TR1 = 1; // 启动定时器1
-
- EA = 1; // 打开总中断
- ES = 1; // 打开串口中断
- }
- // 发送单个字节(关键:发送时关闭串口中断,避免TI被提前清零)
- void sendByte(unsigned char dat)
- {
- ES = 0; // 关闭串口中断,防止TI被中断服务程序清零
- SBUF = dat;
- while(!TI); // 等待发送完成
- TI = 0; // 手动清除发送标志位
- ES = 1; // 恢复串口中断
- }
- void sendString(unsigned char *dat)
- {
- while(*dat != '\0')
- {
- sendByte(*dat++);
- }
- }
- // 重写putchar,供printf调用
- char putchar(char c)
- {
- sendByte((unsigned char)c);
- return c;
- }
- // 串口中断服务程序
- void uart_ISR() interrupt 4
- {
- if(RI) // 接收中断
- {
- RI = 0; // 清除接收标志位
- recv_data = SBUF; // 读取接收数据
- recv_flag = 1; // 置位接收完成标志(关键修正)
- }
- if(TI) // 发送中断(仅清除标志,不做其他操作)
- {
- TI = 0;
- }
- }
- #include <REGX52.H>
- #include "Delay.h"
- #include "uart.h"
- #include <stdio.h> // 确保包含stdio.h
- void main()
- {
- UartInit();
- printf("wait for serial communication test start\r\n");
- printf("please send a frame of data\r\n");
-
- while(1)
- {
- if(recv_flag == 1)
- {
- recv_flag = 0;
- recv_data += 1; // 接收数据+1
- sendByte(recv_data); // 回发+1后的数据
- printf("\r\n");
- printf("%bd\r\n", recv_data); // 打印+1后的数据
- }
- }
- }
三、修正说明
解决printf失效:
在sendByte中添加ES=0和ES=1,发送时临时关闭串口中断,避免中断服务程序提前清零TI,确保while(!TI)能正常等待发送完成。
激活接收逻辑:
在uart_ISR的接收部分添加recv_flag=1,让main函数能检测到接收完成事件,执行后续处理。
其他注意:
确保终端(串口助手)配置为4800bps、8 位数据、1 位停止位、无校验。
包含stdio.h头文件,否则printf可能无法被编译器识别。
四、其他方式
只在中断中处理 接收后加1 发送出的数据, 不单独调用 sendbyte函数
- void uart_ISR() interrupt 4
- {
- if(RI)
- {
- recv_data=SBUF;
- SBUF=recv_data+1;
- RI=0;
- }
- if(TI)
- {
- TI=0;
- }
- }
|