掌握串口通信协议的收发过程

(3)、发送秩序。低位先发。(4)、波特率。收发双方共同约定的一个数据位(0或1)在数据传输线上维持的时间。也可理解为每秒可以传输的位数。常用的波特率有300bit/s, 600bit/s, 2400bit/s, 4800bit/s, 9600bit/s。(5)、通信的起始信号。发送方在没有发送数据时,应该将tx置1 。 当需发送时,先将tx置0,并且保持1位的时间。接受方不断地侦测rx,如果发现rx常时间变高后,突然被拉低(置为0),则视为发送方将要发送数据,迅速启动自己的定时器,从而保证了收发双方定时器同步定时。
(6)、停止信号。发送方发送完最后一个有效位时,必须再将tx保持1位的时间,即为停止位。
好了,理论暂时到这里,现在我们要做一个实验,将一个字节从51单片机发送到电脑串口调试助手上。这个实验的目的是为了掌握串口通信协议的收发过程。
虚拟串口
实验一、虚拟串口实验
一般单片机都有专门的串口引脚,51里面分别是p3.0和p3.1,这些引脚拥有串口的硬件电路,因此使用它们并不需要设置信号的发送停止。为了掌握协议,我们使用其他的引脚来模拟串口,所以也叫虚拟串口。这里我们选用p1.0,然而注意到我们51单片机要发送数据给电脑,必须经过一个串口转usb设备(即ttl电平转换为rs232电平),而限于我们的开发板只有p3.0与p3.1连接到了串口转usb设备,所以我们可以将p1.0短接到p3.1 。 下图是这个串口转usb的原理图。
好了直接上代码吧。
[cpp]view plaincopy
#include reg51.h  
/*
  将p1.0虚拟成串口发送脚tx
  以9600bit/s的比特率向外发送数据
  因为波特率是    9600bit/s
  所以me发送一位的时间是 t=1000000us/9600=104us
*/            
sbit tx=p3^1; //p1^0 output ttl signal, need to transferred to rs232 signal, can be connected to p3^1  
#define u16 unsigned int //宏定义  
#define u8 unsigned char  
u8 sbuf;  
bit ti=0;  
  void delay(u16 x)  
{  
    while(x--);  
}  
void timer0_init()  
{  
    tmod |= 0x01;  
    th0=65440/256;  
    th0=65440%256;  
    tr0=0;  
}  
  void isr_init()  
{  
    ea=1;  
    et0=1;  
}  
  void send_byte(u8 dat)  
{  
    sbuf=dat;//通过引入全局变量sbuf,可以保存形参dat  
    tx=0; //a 起始位  
    tr0=1;  
    while(ti==0);    //等待发送完成  
    ti=0; //清除发送完成标志  
}  
  void tf0_isr() interrupt 1     //每104us进入一次中断  
{  
    static u8 i; //记录进入中断的次数  
      th0=65440/256;  
    tl0=65440%256;  
    i++;  
    if(i>=1 && i<=8)  
    {  
        if((sbuf&(1<<(i-1)))==0)  // (sbuf&(1<=1 && i<=8)来判断是否发送完数据位。然后再通过if(i==9) 来发送停止位,最后当i=10时,也就是发送完了,这时候要关闭定时器(那么程序也就),同时i置0,ti置1(才能跳出while(ti==0)循环),最后将ti置0,保证下次要发送字节时让程序停留在while(ti==0)。
片上串口
以上说的是虚拟串口,上文中谈到与串口相关的引脚p3.0与p3.1,事实上51单片机自带片上串口,那这个串口又该怎么使用呢?
片上串口支持同步模式与异步模式。简单来说同步模式就是指有时钟线,而异步模式无时钟线。这里的时钟线是指在同步通信时,用一根线专门传输时钟信号,这个信号用来与要发送的每一位保持同步,这样就避免了例如异步通信中因为采用定时器而引入的时间误差。
片上串口还支持8位模式和9位模式。如下图所示
其中d0-d7是一个字节的8个位。9位模式只是多了一个位tb8,这个tb8的作用是奇偶校验或多机通信。奇偶校验原理这不加分析。多机通信时比如主机只发送数据给网络中的一台地址为0x02的设备,这时候先让tb8为1,前面的d0-d7则为地址即0x02,之后再让tb8为0,前面的d0-d7则为数据了。
上面设置了片上串口的模式,另外还要设置串口的波特率。
片上串口的波特率等于定时器1工作在方式2时溢出率的32分频。如果要定时器1工作在方式2,那么tmod=0x20。另外要保证为32分频,我们还必须设置计数器初值。设晶振为11.0592mhz,则定时器的计数脉冲为f=f/12,则定时器每计一个脉冲的时间为t=12/f。又令计数器的起点为x,则溢出一次要计的脉冲数为(256-x)。所以在计数起点为x时,溢出一次的时间为t=12/f*(256-x)。则对应的溢出率为1/t=f/(12*(256-x))。对应的波特率就为b=f/(384*(256-x))。
x=256-f/(384*b)
其中f为晶振频率,b为希望的波特率,x为定时器的计数起点th1的值。
例如当晶振为11.0592m,希望波特率为9600bit/s,则th1=253。题外话,我们同样可以演算出在其他常用波特率情况下,th1始终为一个整数。这里也就解释了为什么51里面选用了11.0592m的晶振而不是12m,这样就保证了串口的时序更加准确,虽然牺牲了定时器的准确度。
实验二,片外串口发送一个字节。
好了现在开始我们的实验之旅。直接看代码吧。
[cpp]view plaincopy
#include reg51.h  
#define u16 unsigned int  
#define u8 unsigned char  
  void delay(u16 x)  
{  
    while(x--);  
}  
  void uart_init() //串口初始化  
{  
    scon=0x50; //8位异步模式  
    tmod|=0x20; //定时器1工作方式2  
    th1=253;//9600bit/s  
    tr1=1;  
}  
  void send_byte(u8 dat)  
{  
    sbuf=dat; //启动发送,只需要把发送内容给sbuf这个寄存器  
    while(ti==0); //等待发送完成,因为ti为1时表示在发送停止位  
    ti=0;  
}  
  void main()  
{  
    uart_init();  
    while(1)  
    {  
       send_byte('m');  
       delay(60000);  
    }  
}  
实验二较之实验一,代码减少了很多,而且不用考虑繁琐的位发送时序。只需要明白各个寄存器scon,tmod,tcon,sbuf的用法。ti是scon中的第一位,为发送中断请求标志位。在本方式中,在停止位开始发送时由内部硬件置位,响应中断后ti必须又软件清零。
实验三、片上串口发送一个字符串
上面介绍了如何发送一个字节,那如何发送一个字符串甚至文本呢?这里我们首先介绍下字符串的概念。
字符串:从存储器的某个地址开始,连续存放多个字符的ascii码,并且在最后一个字符的后面存放一个0,这段连续的内存空间就叫字符串,最后的0叫字符串的结束符。注意这里的0和加单引号的0不是一个概念,加单引号的0是指0的ascii码。
数组与字符串的关系:字符串是数组的一种特殊情况,数组在特定条件下可当做字符串用。c语言用双引号描述一个字符串,如“abcd”。
下面我们通过一个实验来展示如何发送字符串。我们实验的目标是打印字符串“hello world ! 第一!”到打印机。直接上代码。
[cpp]view plaincopy
#include reg51.h  
#define u16 unsigned int  
#define u8 unsigned char  
  void delay(u16 x)  
{  
    while(x--);  
}  
  void uart_init() //串口初始化  
{  
    scon=0x50; //8位异步模式  
    tmod|=0x20; //定时器1工作方式2  
    th1=253;//9600bit/s  
    tr1=1;  
}  
  void send_byte(u8 dat)    //串口发送一个字节  
{  
    sbuf=dat; //启动发送,只需要把发送内容给sbuf这个寄存器  
    while(ti==0); //等待发送完成,因为ti为1时表示在发送停止位  
    ti=0;  
}  
   void send_string(u8 *str)   //发送一个字符串  *str为字符串第一个字符的地址  
 {  
    abc:      //标号  
    if(*str != 0)  
    {  
        send_byte(*str);  
        str++;  
        goto abc;     
    }  
 }  
  void main()  
{  
    uart_init();  
    while(1)  
    {  
       send_string(hello world! 第一!);  
       send_byte(10);  
       delay(60000);  
       delay(60000);  
    }  
}

水电池制作方法_如何修复水电瓶
称重传感器怎么测量_称重传感器不准的原因
为什么选择CONTROLLERTESTER
单键/一键触摸触控IC:VKD223B/VKD223EB/223NB,VKD232C两按键/双通道触控感应芯片,取代市面223B/223EB/223NB/232C 原厂开发资料
EeIE智博会▏探索智造·工厂数智化转型与技术应用智造展示&amp;高峰论坛 ————行走的智博会·走进苏州
掌握串口通信协议的收发过程
快速了解!中国制造2025将给工业控制领域带来什么?
液晶面板格局发生巨变 TCL华星业务冲上全球第一
巴基斯坦电信局正式启动了5G通信测试
一加6夜拍怎么样 成像相比前代有着显著的提升
三星1nm时代光刻机体积将增加
骁龙835发布顺序有那么重要吗?三星说其实他们都拿到不货
小米行车记录仪宣布采用海思的芯片
谷歌Home Max:会让无线路由器崩溃停止工作
三防漆在电动汽车和汽车电子行业的应用
“猎鹰9”火箭爆炸事故后,SpaceX重新成功发射通信卫星
全志和瑞芯微比较_哪家强
GPS定位追踪器
国内首获|高芯科技红外模组获车规级AEC-Q104认证
电压稳压器lm317应用电路图大全(十二款lm317典型应用电路)