关于51单片机串口通信的问题

2019-07-15 09:27发布

    最近在学校用单片机做一个小项目,在涉及到串口通信这部分是遇到了一些问题,希望论坛的前辈们可以给我一些指点。    单片机串口通信分为接收与发送两种情况,其数据单位都是1个字节。也就是说,每接收/发送一个字节,单片机的RI/ti位会置1从而选择是否进入中断。以最基本的使用“串口助手”实现PC与单片机的串口通信为例:
1.jpeg
如图是PC从串口助手发送一个字符到单片机,再由单片机返回该字符的例程结果。该字符'1'的数据流图应该是从PC端进入到单片机的SBUF寄存器中去,当单片机接收到该字符的结束位时,单片机将RI置1,表示接收完毕。同理,在发送到该字符的结束位时,单片机将TI置1,表示发送完毕。
但当发送多个字符的情况时,我对串口通信的数据传输过程有了疑惑:
22.jpeg
如图,当从PC发送多个字符时(问题1:这里是否可以理解为字符串的串口通信),仍然可以完美的实现PC与单片机之间的数据互通。而程序仍然与之前一样没有改动(总程序见下)。这时我产生了疑惑,就单片机而言,无论接收还是发送,都是以一个字符为单位进行操作,当面对多个字符的传输时,它是先将所有字符依次接收到SBUF中去,然后将接收到的多个字符再依次发送出去?还是每接收一个字符,就立马发送一个字符?(问题2:是实时传输单个字符还是缓存所有数据后再进行处理),比如在处理“1a!”时,当单片机SBUF接收到'1',立刻就将'1'从SBUF中发送出去,然后再对'a'进行接收......
    我认为是后者的情况,因此我想利用LCD1602进行验证。我的思路是:既然单片机是每接收一个字符,就发送一个字符,那我可以定义一个数组,在单片机的接收中断中(由RI==1触发的中断)存储每次SBUF接收的数据,为了验证方便,我假设每次传输的都是3个字符。中断程序如下:
#define DATA_LENGTH 3
uchar Receivedata[];
uchar ReData,Flag1,Flag2;
uint i,j;

void ser_int (void) interrupt 4
{
if(RI == 1)        
{
         RI = 0;                    
        ReData = SBUF;  
        Receivedata=ReData;
        i++;
        if(i==DATA_LENGTH)
        {
        i=0;
        Flag1=1;       //代表3个字符接收完毕
        }
        Flag2=1;       //代表当前字符接收完毕
}
}

如果说单片机是每接收到一个字符就发送它,按照这个中断,最后Receivedata[]应该是一个存储了“1a!”长度为3的字符类型的数组。
接下来我们只要将数组中的字符元素依次显示在LCD1602上就可以证明猜想,以下为主程序:
void main (void)
{
init_time();                //初始化定时器中断
init_1602();               //初始化LCD1602
write_com(0x80);     //选定开始显示的位置是第一行第一列
           while(1)
        {
        if(Flag2==1)
        {
        SBUF = ReData;                     
        while(TI==0);
        TI=0;               
        Flag2=0;
        if(Flag1==1)
        {
        for(j=0;j<3;j++)
       {
       write_dat(Receivedata[j]);
        }
       Flag1=0;
        }
        }
        }                                                                              
}


但最后程序烧录后结果并不理想,当从PC送入三个字符时,LCD并不能正常显示这三个字符,而且串口助手接收也出现了问题。
4.jpeg
3.jpeg
验证行动就此夭折,出现这种情况,首先说明数组Receivedata中并没有正确的存储SBUF中的字符。另外在增加LCD显示模块的过程中,影响了串口通信的准确度。

在此,我希望仔细看过该问答的前辈们指出我的错误,或者回答上面红字标出的问题。另外,新的问题也从验证的过程中产生(问题3,如何将多个字符或者字符串完整的存储下来或进一步显示在1602上?

在我编程的过程中一定忽视了一些致命的问题,恳请赐教!

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
8条回答
人中狼
1楼-- · 2019-07-15 18:38
这里有两个概念,一个是发送数据的帧结构,一个是发送数据的数据包结构,串行通讯是按帧发送和接收的,也就是每次发送和接收都是一个字节。数据包的结构是有一定约束的,比如数据包的包头字符,代表数据包的开始,数据包的结束字符,代笔数据包的接收结束。
对于数据包的结构,可以参考modbus-rtu的报文格式来理解。

评分

参与人数 1积分 +10 收起 理由 ElecFans王岑 + 10

查看全部评分

blust
2楼-- · 2019-07-15 21:03
串口发送接收多字节时,你一开始的理解是对的,按照你的程序逻辑,确实是接收一个字节就会处理并发送一个字节。想要接收一串字符串之后再做处理的话,需要加软件处理,一种方式是加分包延时,即超过一定延时(一般是几毫秒到几十毫秒)没有接收到数据,就对之前接收的所有数据进行一次处理,当然在这之前需要开辟一块接收缓冲区,每个字节收到时要按顺序放到缓冲区内;另一种方式是增加数据结尾标志,这个标志可以自己定义,比如回车,比如空格,或者其他特定的字符,单片机在接收到这个字符时,就认为已经传完一个数据包,开始对其进行处理。
至于你的验证,中断里面的数组忘记加下标了。。。

评分

参与人数 1积分 +10 收起 理由 ElecFans王岑 + 10

查看全部评分

ElecFans王岑
3楼-- · 2019-07-15 21:35
 精彩回答 2  元偷偷看……
f310808020307
4楼-- · 2019-07-15 22:10
问题1:这里是否可以理解为字符串的串口通信
    我觉得不可以,你之前也说了是一个字节的数据发送接收处理
问题2:是实时传输单个字符还是缓存所有数据后再进行处理
    我认为应该是实时传输单个字符,原因通问题1.要确定的话需要判断你的SBUF是多少位的。你还可以通过判断进入发送中断的次数来判断“1a!”是一次发出还是多次发出。
问题3,如何将多个字符或者字符串完整的存储下来或进一步显示在1602上?
    确定上面两个之后,数据存储就能实现了。
代码里面的Receivedata=ReData;有问题的.........

评分

参与人数 1积分 +3 收起 理由 wangling123 + 3

查看全部评分

wangling123
5楼-- · 2019-07-15 23:29
f310808020307 发表于 2017-10-27 11:26
问题1:这里是否可以理解为字符串的串口通信
    我觉得不可以,你之前也说了是一个字节的数据发送接收处理
问题2:是实时传输单个字符还是缓存所有数据后再进行处理

应该是  Receivedata = ReData   改过之后仍然是这种情况
您关于问题2的回答给了我一些启发!谢谢!
wangling123
6楼-- · 2019-07-16 02:59
blust 发表于 2017-10-27 08:44
串口发送接收多字节时,你一开始的理解是对的,按照你的程序逻辑,确实是接收一个字节就会处理并发送一个字节。想要接收一串字符串之后再做处理的话,需要加软件处理,一种方式是加分包延时,即超过一定延时(一般是几毫秒到几十毫秒)没有接收到数据,就对之前接收的所有数据进行一次处理,当然在这之前需要开辟一块接收缓 ...

    您回复中的“处理”我是否可以认为成这里我想将接受到的几个字符显示在LCD上?
    我对您提到的第二种实现方式很感兴趣。您的意思是否可以通过下面的做法实现:可以再接受串口终端(RI==1)中加一个判断语句,if(SBUF接收到的数据为' '),则关闭串口中断(ES==0;),然后在主程序中对之前在串口中接收到的缓冲区数据(Receivedata[])显示在LCD上,显示完毕后再将ES打开。
    至于我在文中的验证,实际上我程序中已经加上下标了,但仍然是文中图片的症状。
    感谢您的回复!

一周热门 更多>