首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >51串口,查询和中断接收发送冲突 的解决办法

51串口,查询和中断接收发送冲突 的解决办法

原创
作者头像
用户3479619
发布2025-08-23 15:58:11
发布2025-08-23 15:58:11
1070
举报

代码中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中,接收数据后仅读取了SBUFrecv_data,但未设置recv_flag=1

  • main函数中if(recv_flag==1)的条件永远不成立,导致接收后的数据处理逻辑(+1 回发、打印)无法执行。

二、修正后的代码

代码语言:txt
复制
#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;
	}
}

主函数

代码语言:txt
复制
#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后的数据
		}
	}
}

三、修正说明

  1. 解决printf失效: 在sendByte中添加ES=0ES=1,发送时临时关闭串口中断,避免中断服务程序提前清零TI,确保while(!TI)能正常等待发送完成。
  2. 激活接收逻辑: 在uart_ISR的接收部分添加recv_flag=1,让main函数能检测到接收完成事件,执行后续处理。
  3. 其他注意
    • 确保终端(串口助手)配置为4800bps、8 位数据、1 位停止位、无校验
    • 包含stdio.h头文件,否则printf可能无法被编译器识别。

四、其他方式

只在中断中处理 接收后加1 发送出的数据, 不单独调用 sendbyte函数

代码语言:txt
复制
void uart_ISR() interrupt 4
{
	if(RI)
	{
		recv_data=SBUF;
		SBUF=recv_data+1;
		RI=0;
	}
	if(TI)
	{
		TI=0;
	}

}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、核心问题分析
    • 1. printf失效:串口中断与sendByte查询逻辑冲突
    • 2. 接收逻辑不响应:recv_flag未被正确置位
  • 二、修正后的代码
  • 三、修正说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档