找回密码
 立即注册
查看: 141|回复: 4

帮忙测试一个串口发送接收程序

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:505
  • 最近打卡:2026-07-22 00:17:01

35

主题

204

回帖

754

积分

高级会员

积分
754
发表于 2026-5-3 14:14:42 | 显示全部楼层 |阅读模式
stc89c52rc,keilc

不管是发送文本模式还是hex模式,明明程序SBUF=SBUF=2;得到的全是+1.

但是如果把代码改成+4或者+1,都是得到正确结果。怪哉!!!
REN = 0; // 暂时禁止接收
REN = 1;          // 重新允许接收

是deepseek给出的修改建议,加不加都一样。。

  1. #include <STC89C5xRC.H>
  2. void UART_Init(void);
  3. void main(void)
  4. {
  5.     UART_Init();         // 调用串口初始化,配置 9600, 8N1
  6.     while(1)             // 无限循环,不断执行回显功能
  7.     {
  8.         // 1. 等待接收一个字节
  9.         while(!RI);      // RI 是接收中断标志位,硬件在接收到一帧完整数据后自动置 1
  10.                          // 这里用查询方式:只要 RI 为 0,就在这里死等,直到接收完成
  11.         RI = 0;          // 接收完成后,必须由软件手动清除 RI 标志,
  12.                          // 否则下一次 while(!RI) 会立刻通过,造成重复回显
  13.                                 REN = 0;          // 暂时禁止接收
  14.         // 2. 将接收到的字节发送回去(实现回显)
  15.         SBUF = SBUF+2;     // 当读出 SBUF 时,得到的是接收到的数据;
  16.                          // 当写入 SBUF 时,会启动一次串行发送。
  17.                          // 这条语句相当于:
  18.                          //   unsigned char temp = SBUF;  // 读接收到的数据
  19.                          //   SBUF = temp;                // 写入发送缓冲器,启动发送
  20.                                 REN = 1;          // 重新允许接收
  21.         while(!TI);      // TI 是发送中断标志位,硬件在发送完一帧数据后自动置 1
  22.                          // 等待发送完成,避免在发送过程中又去写 SBUF 造成数据破坏
  23.         TI = 0;          // 发送完成后,软件清除 TI 标志,为下一次发送做准备
  24.     }
  25. }
  26. void UART_Init(void)
  27. {
  28.     SCON = 0x50;      // 方式1,允许接收。注意这里是0x50,TI/RI由硬件操作,通常初始0即可
  29.     // 或直接 SCON = 0x51; 也完全没问题,手动物清了TI和RI
  30.     TMOD &= 0x0F;     // 修改的是 高 4 位(定时器 1),而 低 4 位(定时器 0)完全没有改变。
  31.         //TMOD的高四位是关于TImer1,低四位是Timer0.
  32.     TMOD |= 0x20;     // 设定定时器1为方式2 (8位自动重装)
  33.     TH1 = 0xFD;       // 波特率9600 (晶振11.0592MHz)
  34.     TL1 = 0xFD;
  35.     ET1 = 0;          // 禁止定时器1中断,用作波特率发生器时通常不产生中断
  36.     TR1 = 1;          // 启动定时器1
  37.     // 如果用到中断接收,再开总中断和串口中断
  38.     ES = 0;           // 允许串口中断
  39.     EA = 0;           // CPU开总中断
  40. }
复制代码



回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:956
  • 最近打卡:2026-07-21 08:27:41

14

主题

1400

回帖

7694

积分

论坛元老

积分
7694
发表于 2026-5-3 14:29:16 | 显示全部楼层
你这个现象本质上不是“串口硬件把 +2 变成了 +1”,而是 你在读写同一个 SBUF 时发生了写回/读回混淆,再加上表达式里对 SBUF 的访问顺序不可靠,导致结果看起来像“总是少 1”。8051 的 SBUF 不是普通 RAM,它是两个物理上不同的寄存器:写 SBUF 是发射缓冲,读 SBUF 是接收缓冲;SBUF 不能被当成一个可读可写且值一致的变量来用 。

正确写法
建议改成下面这种方式:

  1. #include <STC89C5xRC.H>
  2. void UART_Init(void)
  3. {
  4.     SCON = 0x50;      // 8位UART, REN=1
  5.     TMOD &= 0x0F;
  6.     TMOD |= 0x20;     // T1方式2
  7.     TH1 = 0xFD;       // 11.0592MHz, 9600bps
  8.     TL1 = 0xFD;
  9.     TR1 = 1;
  10.     ES = 0;
  11.     EA = 0;
  12. }
  13. void main(void)
  14. {
  15.     unsigned char dat;
  16.     UART_Init();
  17.     while(1)
  18.     {
  19.         while(!RI);
  20.         dat = SBUF;     // 先把接收到的数据存到普通变量
  21.         RI = 0;
  22.         dat += 2;       // 再做运算
  23.         while(!TI);     // 如果你要等发送完成,先确保初始TI状态正确
  24.         TI = 0;
  25.         SBUF = dat;     // 最后发送
  26.     }
  27. }
复制代码

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:505
  • 最近打卡:2026-07-22 00:17:01

35

主题

204

回帖

754

积分

高级会员

积分
754
发表于 2026-5-3 15:03:38 | 显示全部楼层
angm*** 发表于 2026-5-3 14:29
你这个现象本质上不是“串口硬件把 +2 变成了 +1”,而是 你在读写同一个 SBUF 时发生了写回/读回混淆,再 ...


我现在用的是没有dat+2,而是原样输出,也没有输出。根本没报告接收。

  1. #include <STC89C5xRC.H>
  2. void UART_Init(void);
  3. void main(void)
  4. {
  5.                 unsigned char dat;
  6.     UART_Init();         // 调用串口初始化,配置 9600, 8N1
  7.     while(1)             // 无限循环,不断执行回显功能
  8.     {
  9.         // 1. 等待接收一个字节
  10.         while(!RI);
  11.                                 dat=SBUF;
  12.         RI = 0;//dat+=2;
  13.         while(!TI);
  14.         TI = 0;
  15.                         SBUF=dat;
  16.                 }
  17. }
  18. void UART_Init(void)
  19. {
  20.     SCON = 0x50;      // 方式1,允许接收。注意这里是0x50,TI/RI由硬件操作,通常初始0即可
  21.     // 或直接 SCON = 0x51; 也完全没问题,手动物清了TI和RI
  22.     TMOD &= 0x0F;     // 修改的是 高 4 位(定时器 1),而 低 4 位(定时器 0)完全没有改变。
  23.         //TMOD的高四位是关于TImer1,低四位是Timer0.
  24.     TMOD |= 0x20;     // 设定定时器1为方式2 (8位自动重装)
  25.     TH1 = 0xFD;       // 波特率9600 (晶振11.0592MHz)
  26.     TL1 = 0xFD;
  27.     ET1 = 0;          // 禁止定时器1中断,用作波特率发生器时通常不产生中断
  28.     TR1 = 1;          // 启动定时器1
  29.     // 如果用到中断接收,再开总中断和串口中断
  30.     ES = 0;           // 允许串口中断
  31.     EA = 0;           // CPU开总中断
  32. }
  33. // 串口中断服务函数(空实现,什么都不做)
  34. void UART_ISR(void) interrupt 4
  35. {
  36.     // 空函数体
  37.     // 这里可以后续添加接收/发送处理
  38. }
复制代码

截图202605031503231482.jpg

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-5-3 18:10:01 | 显示全部楼层

https://www.stcaimcu.com/data/do ... /STC89C52RC-RD+.pdf

深圳国芯人工智能有限公司-工具软件

截图202605031809309414.jpg

截图202605031808424629.jpg



#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 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:316
  • 最近打卡:2026-07-22 06:56:11
已绑定手机

70

主题

875

回帖

3227

积分

论坛元老

DIY玩家

积分
3227
发表于 2026-5-4 00:58:24 | 显示全部楼层
可能程序的读和写操作
对应底层操作各自的寄存器
永怀这学徒心
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 10:36 , Processed in 0.077776 second(s), 62 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表