帮忙测试一个串口发送接收程序
stc89c52rc,keilc不管是发送文本模式还是hex模式,明明程序SBUF=SBUF=2;得到的全是+1.
但是如果把代码改成+4或者+1,都是得到正确结果。怪哉!!!
REN = 0; // 暂时禁止接收
和REN = 1; // 重新允许接收
是deepseek给出的修改建议,加不加都一样。。{:4_192:}
#include <STC89C5xRC.H>
void UART_Init(void);
void main(void)
{
UART_Init(); // 调用串口初始化,配置 9600, 8N1
while(1) // 无限循环,不断执行回显功能
{
// 1. 等待接收一个字节
while(!RI); // RI 是接收中断标志位,硬件在接收到一帧完整数据后自动置 1
// 这里用查询方式:只要 RI 为 0,就在这里死等,直到接收完成
RI = 0; // 接收完成后,必须由软件手动清除 RI 标志,
// 否则下一次 while(!RI) 会立刻通过,造成重复回显
REN = 0; // 暂时禁止接收
// 2. 将接收到的字节发送回去(实现回显)
SBUF = SBUF+2; // 当读出 SBUF 时,得到的是接收到的数据;
// 当写入 SBUF 时,会启动一次串行发送。
// 这条语句相当于:
// unsigned char temp = SBUF;// 读接收到的数据
// SBUF = temp; // 写入发送缓冲器,启动发送
REN = 1; // 重新允许接收
while(!TI); // TI 是发送中断标志位,硬件在发送完一帧数据后自动置 1
// 等待发送完成,避免在发送过程中又去写 SBUF 造成数据破坏
TI = 0; // 发送完成后,软件清除 TI 标志,为下一次发送做准备
}
}
void UART_Init(void)
{
SCON = 0x50; // 方式1,允许接收。注意这里是0x50,TI/RI由硬件操作,通常初始0即可
// 或直接 SCON = 0x51; 也完全没问题,手动物清了TI和RI
TMOD &= 0x0F; // 修改的是 高 4 位(定时器 1),而 低 4 位(定时器 0)完全没有改变。
//TMOD的高四位是关于TImer1,低四位是Timer0.
TMOD |= 0x20; // 设定定时器1为方式2 (8位自动重装)
TH1 = 0xFD; // 波特率9600 (晶振11.0592MHz)
TL1 = 0xFD;
ET1 = 0; // 禁止定时器1中断,用作波特率发生器时通常不产生中断
TR1 = 1; // 启动定时器1
// 如果用到中断接收,再开总中断和串口中断
ES = 0; // 允许串口中断
EA = 0; // CPU开总中断
}
你这个现象本质上不是“串口硬件把 +2 变成了 +1”,而是 你在读写同一个 SBUF 时发生了写回/读回混淆,再加上表达式里对 SBUF 的访问顺序不可靠,导致结果看起来像“总是少 1”。8051 的 SBUF 不是普通 RAM,它是两个物理上不同的寄存器:写 SBUF 是发射缓冲,读 SBUF 是接收缓冲;SBUF 不能被当成一个可读可写且值一致的变量来用 。
正确写法
建议改成下面这种方式:
#include <STC89C5xRC.H>
void UART_Init(void)
{
SCON = 0x50; // 8位UART, REN=1
TMOD &= 0x0F;
TMOD |= 0x20; // T1方式2
TH1 = 0xFD; // 11.0592MHz, 9600bps
TL1 = 0xFD;
TR1 = 1;
ES = 0;
EA = 0;
}
void main(void)
{
unsigned char dat;
UART_Init();
while(1)
{
while(!RI);
dat = SBUF; // 先把接收到的数据存到普通变量
RI = 0;
dat += 2; // 再做运算
while(!TI); // 如果你要等发送完成,先确保初始TI状态正确
TI = 0;
SBUF = dat; // 最后发送
}
}
angmall 发表于 2026-5-3 14:29
你这个现象本质上不是“串口硬件把 +2 变成了 +1”,而是 你在读写同一个 SBUF 时发生了写回/读回混淆,再 ...
我现在用的是没有dat+2,而是原样输出,也没有输出。根本没报告接收。
#include <STC89C5xRC.H>
void UART_Init(void);
void main(void)
{
unsigned char dat;
UART_Init(); // 调用串口初始化,配置 9600, 8N1
while(1) // 无限循环,不断执行回显功能
{
// 1. 等待接收一个字节
while(!RI);
dat=SBUF;
RI = 0;//dat+=2;
while(!TI);
TI = 0;
SBUF=dat;
}
}
void UART_Init(void)
{
SCON = 0x50; // 方式1,允许接收。注意这里是0x50,TI/RI由硬件操作,通常初始0即可
// 或直接 SCON = 0x51; 也完全没问题,手动物清了TI和RI
TMOD &= 0x0F; // 修改的是 高 4 位(定时器 1),而 低 4 位(定时器 0)完全没有改变。
//TMOD的高四位是关于TImer1,低四位是Timer0.
TMOD |= 0x20; // 设定定时器1为方式2 (8位自动重装)
TH1 = 0xFD; // 波特率9600 (晶振11.0592MHz)
TL1 = 0xFD;
ET1 = 0; // 禁止定时器1中断,用作波特率发生器时通常不产生中断
TR1 = 1; // 启动定时器1
// 如果用到中断接收,再开总中断和串口中断
ES = 0; // 允许串口中断
EA = 0; // CPU开总中断
}
// 串口中断服务函数(空实现,什么都不做)
void UART_ISR(void) interrupt 4
{
// 空函数体
// 这里可以后续添加接收/发送处理
}
https://www.stcaimcu.com/data/download/Datasheet/STC89C52RC-RD+.pdf
深圳国芯人工智能有限公司-工具软件
#include "STC89C5xRC.H" //包含STC89的头文件
#include "intrins.h" //使用_nop_()函数所必须要包含的头文件,
//否则延时函数中调用的_nop_()函数没有被头文件引用过来,
//会导致编译器找不到这个而函数而报错。
unsigned char int0_flag = 0; //定义1个位变量,INT0事件位变量标志,记录INT0已产生中断
// 供主循环查询INT0是否已产生中断,在主循环中处理INT0的中断事件任务,不堵塞其他中断
unsigned char int1_flag = 0; //定义1个位变量,INT1事件位变量标志,记录INT1已产生中断
// 供主循环查询INT1是否已产生中断,在主循环中处理INT1的中断事件任务,不堵塞其他中断
unsigned char int2_flag = 0; //定义1个位变量,INT2事件位变量标志,记录INT2已产生中断
// 供主循环查询INT2是否已产生中断,在主循环中处理INT2的中断事件任务,不堵塞其他中断
unsigned char int3_flag = 0; //定义1个位变量,INT3事件位变量标志,记录INT3已产生中断
// 供主循环查询INT3是否已产生中断,在主循环中处理INT3的中断事件任务,不堵塞其他中断
unsigned char t0_flag = 0; //定义1个位变量,T0事件位变量标志,记录定时器0已产生中断
// 供主循环查询定时器0是否已产生中断,在主循环中处理定时器0的中断事件任务,不堵塞其他中断
unsigned char t1_flag = 0; //定义1个位变量,T1事件位变量标志,记录定时器1已产生中断
// 供主循环查询定时器1是否已产生中断,在主循环中处理定时器1的中断事件任务,不堵塞其他中断
unsigned char uart_txflag = 0; //定义1个位变量,UART事件位变量标志,记录UART已产生发送中断
// 供主循环查询UART是否已产生发送中断,在主循环中处理UART的中断事件任务,不堵塞其他中断
unsigned char uart_rxflag = 0; //定义1个位变量,UART事件位变量标志,记录UART已产生接收中断
// 供主循环查询UART是否已产生接收中断,在主循环中处理UART的中断事件任务,不堵塞其他中断
void Timer0_Init(void) //定时器0初始化,10毫秒@22.1184MHz
{
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0xB8; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void Timer1_Init(void) //定时器1初始化,20毫秒@22.1184MHz
{
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x00; //设置定时初始值
TH1 = 0x70; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}
void Uart_Init(void) //串口初始化,115200bps@22.1184MHz
{
SCON = 0x50; //8位数据,可变波特率
T2CON &= ~0x03; //设置定时器2工作模式为16位自动重载模式
T2CON |= 0x30; //设置定时器2作为串口的发送和接收时钟
RCAP2L = TL2 = 0xFA; //设置定时初始值
RCAP2H = TH2 = 0xFF; //设置定时初始值
TR2 = 1; //定时器2开始计时
ES = 1; //使能串口中断
}
void main (void)
{
int0_flag = 0; //初始化用户标志位
int1_flag = 0; //初始化用户标志位
int2_flag = 0; //初始化用户标志位
int3_flag = 0; //初始化用户标志位
t0_flag = 0; //初始化用户标志位
t1_flag = 0; //初始化用户标志位
uart_txflag = 0; //初始化用户标志位
uart_rxflag = 0; //初始化用户标志位
// IT0 = 0; //使能 INT0 低电平中断
IT0 = 1; //使能 INT0 下降沿中断
EX0 = 1; //使能 INT0 中断
IE0 = 0; //清INT0中断标志
// IT1 = 0; //使能 INT1 低电平中断
IT1 = 1; //使能 INT1 下降沿中断
EX1 = 1; //使能 INT1 中断
IE1 = 0; //清INT1中断标志
// IT2 = 0; //使能 INT2 低电平中断
IT2 = 1; //使能 INT2 下降沿中断
EX2 = 1; //使能 INT2 中断
IE2 = 0; //清INT2中断标志
// IT3 = 0; //使能 INT3 低电平中断
IT3 = 1; //使能 INT3 下降沿中断
EX3 = 1; //使能 INT3 中断
IE3 = 0; //清INT3中断标志
Timer0_Init(); //调用定时器0初始化函数
Timer1_Init(); //调用定时器1初始化函数
Uart_Init(); //调用UART初始化函数
EA = 1; //总中断允许位打开
while(1) //主循环中查询需要处理的各种事件
{
/*本演示程序中,主循环查询各中断有无需要继续处理的事件的次序,
依次是 INTx/TIMERx/UARTx, 用户可以自己根据实际情况,
调整查询各中断有无需要继续处理的事件的优先次序*/
//查询外部中断0事件
if(int0_flag) //主循环中查询,INT0是否已产生中断,是否有需要处理的INT 0事件
{
int0_flag = 0; //清0,INT0事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询外部中断1事件
if(int1_flag) //主循环中查询,INT1是否已产生中断,是否有需要处理的INT1事件
{
int1_flag = 0; //清0,INT1事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询外部中断2事件
if(int2_flag) //主循环中查询,INT2是否已产生中断,是否有需要处理的INT2事件
{
int2_flag = 0; //清0,INT2事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询外部中断3事件
if(int3_flag) //主循环中查询,INT3是否已产生中断,是否有需要处理的INT3事件
{
int3_flag = 0; //清0,INT3事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询定时器0中断事件
if(t0_flag) //主循环中查询,定时器0是否已产生中断,是否有需要处理的定时器0事件
{
t0_flag = 0; //清0,T0事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询定时器1中断事件
if(t1_flag) //主循环中查询,定时器1是否已产生中断,是否有需要处理的定时器1事件
{
t1_flag = 0; //清0,T1事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
//查询串口中断事件
if(uart_txflag) //主循环中查询,UART是否已产生发送中断,是否有需要处理的UART发送事件
{
uart_txflag = 0; //清0,UART发送事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
if(uart_rxflag) //主循环中查询,UART是否已产生接收中断,是否有需要处理的UART接收事件
{
uart_rxflag = 0; //清0,UART接收事件位变量标志
_nop_(); //用户在此添加需要处理的事件
_nop_();
}
}
}
void int0_isr(void) interrupt 0
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
int0_flag = 1; // int0_flag置1是通知主循环处理部分INT0中断事件不需要特急处理的任务
//置1,记录INT0已产生中断,供主循环查询判断有无需处理的INT0任务
}
void int1_isr(void) interrupt 2
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
int1_flag = 1; // int1_flag置1是通知主循环处理部分INT1中断事件不需要特急处理的任务
}
void int2_isr(void) interrupt 6
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
int2_flag = 1; // int2_flag置1是通知主循环处理部分INT2中断事件不需要特急处理的任务
}
void int3_isr(void) interrupt 7
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
int3_flag = 1; // int3_flag置1是通知主循环处理部分INT3中断事件不需要特急处理的任务
}
void Timer0_Isr(void) interrupt 1 //定时器0中断服务程序
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
t0_flag = 1; // t0_flag置1是通知主循环处理部分T0中断事件不需要特急处理的任务
//置1,记录定时器0已产生中断,供主循环查询判断有无需处理的定时器0任务
}
void Timer1_Isr(void) interrupt 3
{
_nop_(); //特急处理,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
t1_flag = 1; // t1_flag置1是通知主循环处理部分T1中断事件不需要特急处理的任务
//置1,记录定时器1已产生中断,供主循环查询判断有无需处理的定时器1任务
}
void Uart_Isr(void) interrupt 4
{
if (TI) //检测串口发送中断
{
TI = 0; //清除串口发送中断请求位
_nop_(); //特急处理
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
uart_txflag = 1; // uart_txflag置1是通知主循环处理部分串口发送中断事件不需要特急处理的任务
//置1,记录UART已产生发送中断,供主循环查询判断有无需处理的UART发送任务
}
if (RI) //检测串口接收中断
{
RI = 0; //清除串口接收中断请求位
_nop_(); //特急处理
//以上程序代表部分需特急处理的中断事件,可在中断服务程序中直接处理
//但时间不要太长,否则会影响其他中断事件的实时响应速度
uart_rxflag = 1; // uart_rxflag置1是通知主循环处理部分串口接收中断事件不需要特急处理的任务
//置1,记录UART已产生接收中断,供主循环查询判断有无需处理的UART接收任务
}
}
可能程序的读和写操作
对应底层操作各自的寄存器
页:
[1]