串口通信,单片机转串口

通信协议又分为硬件层协议和软件层协议。硬件层协议主要规范了物理上的连线,传输电平信号及传输的秩序等硬件性质的内容。常用的硬件协议有串口,IIC, SPI, RS485, CAN和 USB。软件层协议则更侧重上层应用的规范,比如modbus协议。


好了,那这里我们就着重介绍51单片机的串口通信协议,以下简称串口。串口的6个特征如下。


        (1)、物理上的连线至少3根,分别是Tx数据发送线,Rx数据接收线,GND共用地线。
(2)、0与1的约定。RS232电平,约定﹣5V至﹣25V之间的电压信号为1,﹢5V至﹢25V之间的电压信号为0 。TTL电平,约定5V的电压信号为1,0V电压信号为0 。CMOS电平,约定3.3V的电压信号为1,0V电压信号为0 。其中,CMOS电平一般用于ARM芯片中。
(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的原理图。


好了直接上代码吧。


 
 
  1. #include "reg51.h"
  2. /*
  3. 将P1.0虚拟成串口发送脚TX
  4. 以9600bit/s的比特率向外发送数据
  5. 因为波特率是 9600bit/s
  6. 所以me发送一位的时间是 t=1000000us/9600=104us
  7. */
  8. sbit TX=P3^ 1; //P1^0 output TTL signal, need to transferred to rs232 signal, can be connected to P3^1
  9. #define u16 unsigned int //宏定义
  10. #define u8 unsigned char
  11. u8 sbuf;
  12. bit ti= 0;
  13. void delay(u16 x)
  14. {
  15. while(x--);
  16. }
  17. void Timer0_Init()
  18. {
  19. TMOD |= 0x01;
  20. TH0= 65440/ 256;
  21. TH0= 65440% 256;
  22. TR0= 0;
  23. }
  24. void Isr_Init()
  25. {
  26. EA= 1;
  27. ET0= 1;
  28. }
  29. void Send_Byte(u8 dat)
  30. {
  31. sbuf=dat; //通过引入全局变量sbuf,可以保存形参dat
  32. TX= 0; //A 起始位
  33. TR0= 1;
  34. while(ti== 0); //等待发送完成
  35. ti= 0; //清除发送完成标志
  36. }
  37. void TF0_isr() interrupt 1 //每104us进入一次中断
  38. {
  39. static u8 i; //记录进入中断的次数
  40. TH0= 65440/ 256;
  41. TL0= 65440% 256;
  42. i++;
  43. if(i>= 1 && i<= 8)
  44. {
  45. if((sbuf&( 1<<(i -1)))== 0) // (sbuf&(1<<(i-1)))表示取出i-1位
  46. {
  47. TX= 0;
  48. }
  49. else
  50. {
  51. TX= 1;
  52. }
  53. }
  54. if(i== 9) //停止位
  55. {
  56. TX= 1;
  57. }
  58. if(i== 10)
  59. {
  60. TR0= 0;
  61. i= 0;
  62. ti= 1; //发送完成
  63. }
  64. }
  65. void main()
  66. {
  67. TX= 1; //使TX处于空闲状态
  68. Timer0_Init();
  69. Isr_Init();
  70. while( 1)
  71. {
  72. Send_Byte( 65); //0x41
  73. delay( 60000);
  74. }
  75. }
实验引入了定时器0来控制发送线上的各个位的保持时间。首先main函数进入,TX置1则使发送线处于空闲,这时候发送方和接受方都处于空闲。接下来初始化定时器0,TR0置0表示还不要启动定时器0。接着中断系统初始化,此时中断系统已经开启。进入while循环,先进Send_Byte()函数,将65传给形参dat,dat再将65赋值给sbuf,到这里准备工作就做好了。接着TX置0,这个是起始位,要保持这个起始位104us。于是就启动定时器TR0置1,计时器开始计数。当第一次溢出的时候,也就是过了104us,进入中断,同时接收方也侦测到了这个突然被拉低的信号,于是迅速启动自己的定时器。进入中断子函数后,先是重装定时器初值,然后i加1,也就是当i=1时,就应该发送数据的最低位了,总共有8位数据,所以使用条件语句 if(i>=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,这样就保证了串口的时序更加准确,虽然牺牲了定时器的准确度。

实验二,片外串口发送一个字节。

好了现在开始我们的实验之旅。直接看代码吧。


 
 
  1. #include "reg51.h"
  2. #define u16 unsigned int
  3. #define u8 unsigned char
  4. void delay(u16 x)
  5. {
  6. while(x--);
  7. }
  8. void Uart_Init() //串口初始化
  9. {
  10. SCON= 0x50; //8位异步模式
  11. TMOD|= 0x20; //定时器1工作方式2
  12. TH1= 253; //9600bit/s
  13. TR1= 1;
  14. }
  15. void Send_Byte(u8 dat)
  16. {
  17. SBUF=dat; //启动发送,只需要把发送内容给SBUF这个寄存器
  18. while(TI== 0); //等待发送完成,因为TI为1时表示在发送停止位
  19. TI= 0;
  20. }
  21. void main()
  22. {
  23. Uart_Init();
  24. while( 1)
  25. {
  26. Send_Byte( 'm');
  27. delay( 60000);
  28. }
  29. }

实验二较之实验一,代码减少了很多,而且不用考虑繁琐的位发送时序。只需要明白各个寄存器SCON,TMOD,TCON,SBUF的用法。TI是SCON中的第一位,为发送中断请求标志位。在本方式中,在停止位开始发送时由内部硬件置位,响应中断后TI必须又软件清零。

实验三、片上串口发送一个字符串

上面介绍了如何发送一个字节,那如何发送一个字符串甚至文本呢?这里我们首先介绍下字符串的概念。

字符串:从存储器的某个地址开始,连续存放多个字符的ASCII码,并且在最后一个字符的后面存放一个0,这段连续的内存空间就叫字符串,最后的0叫字符串的结束符。注意这里的0和加单引号的0不是一个概念,加单引号的0是指0的ASCII码。

数组与字符串的关系:字符串是数组的一种特殊情况,数组在特定条件下可当做字符串用。C语言用双引号描述一个字符串,如“abcd”。

下面我们通过一个实验来展示如何发送字符串。我们实验的目标是打印字符串“Hello World ! 第一!”到打印机。直接上代码。


 
 
  1. #include "reg51.h"
  2. #define u16 unsigned int
  3. #define u8 unsigned char
  4. void delay(u16 x)
  5. {
  6. while(x--);
  7. }
  8. void Uart_Init() //串口初始化
  9. {
  10. SCON= 0x50; //8位异步模式
  11. TMOD|= 0x20; //定时器1工作方式2
  12. TH1= 253; //9600bit/s
  13. TR1= 1;
  14. }
  15. void Send_Byte(u8 dat) //串口发送一个字节
  16. {
  17. SBUF=dat; //启动发送,只需要把发送内容给SBUF这个寄存器
  18. while(TI== 0); //等待发送完成,因为TI为1时表示在发送停止位
  19. TI= 0;
  20. }
  21. void Send_String(u8 *str) //发送一个字符串 *str为字符串第一个字符的地址
  22. {
  23. abc: //标号
  24. if(*str != 0)
  25. {
  26. Send_Byte(*str);
  27. str++;
  28. goto abc;
  29. }
  30. }
  31. void main()
  32. {
  33. Uart_Init();
  34. while( 1)
  35. {
  36. Send_String( "Hello World! 第一!");
  37. Send_Byte( 10);
  38. delay( 60000);
  39. delay( 60000);
  40. }
  41. }

实验效果


这里就不再讲述代码了,如有问题,请及时联系。谢谢观看本文!转载此文请联系本人!

转载自:https://blog.csdn.net/a514371309/article/details/73481423

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
串口是计算机上一种非常通用设备通信的协议(不要与通用串行总线Universal Serial Bus或者USB混淆)。大多数计算机包含两个基于RS232的串口串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS- 232口。同时,串口通信协议也可以用于获取远程采集设备的数据。 串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。 典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配: a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为 14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是 GPIB设备的通信。 b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语 “包”指任何通信的情况。 c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。 d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值