电子工程单片机C语言之串口通信协议代码分享.docx
- 文档编号:25998757
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:12
- 大小:78.13KB
电子工程单片机C语言之串口通信协议代码分享.docx
《电子工程单片机C语言之串口通信协议代码分享.docx》由会员分享,可在线阅读,更多相关《电子工程单片机C语言之串口通信协议代码分享.docx(12页珍藏版)》请在冰豆网上搜索。
电子工程单片机C语言之串口通信协议代码分享
单片机C语言之串口通信协议(代码分享)
现实生活中,我们总是要与人打交道,互通有无。
单片机也一样,需要跟各种设备交互。
例如汽车的显示仪表需要知道汽车的转速及电动机的运行参数,那么显示仪表就需要从汽车的底层控制器取得数据。
而这个数据的获得过程就是一个通信过程。
类似的例子还有控制器通常是单片机或者PLC与变频器的通信。
通信的双方需要遵守一套既定的规则也称为协议,这就好比我们人之间的对话,需要在双方都遵守一套语言语法规则才有可能达成对话。
通信协议又分为硬件层协议和软件层协议。
硬件层协议主要规范了物理上的连线,传输电平信号及传输的秩序等硬件性质的内容。
常用的硬件协议有串口,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的原理图。
好了直接上代码吧。
[cpp] view plain copy
#include"reg51.h"
/*
将P1.0虚拟成串口发送脚TX
以9600bit/s的比特率向外发送数据
因为波特率是9600bit/s
所以me发送一位的时间是t=1000000us/9600=104us
*/
sbitTX=P3^1;//P1^0outputTTLsignal,needtotransferredtors232signal,canbeconnectedtoP3^1
#defineu16unsignedint//宏定义
#defineu8unsignedchar
u8sbuf;
bitti=0;
voiddelay(u16x)
{
while(x--);
}
voidTimer0_Init()
{
TMOD|=0x01;
TH0=65440/256;
TH0=65440%256;
TR0=0;
}
voidIsr_Init()
{
EA=1;
ET0=1;
}
voidSend_Byte(u8dat)
{
sbuf=dat;//通过引入全局变量sbuf,可以保存形参dat
TX=0;//A起始位
TR0=1;
while(ti==0);//等待发送完成
ti=0;//清除发送完成标志
}
voidTF0_isr()interrupt1//每104us进入一次中断
{
staticu8i;//记录进入中断的次数
TH0=65440/256;
TL0=65440%256;
i++;
if(i>=1&&i<=8)
{if((sbuf&(1<<(i-1)))==0)//(sbuf&(1<<(i-1)))表示取出i-1位
{
TX=0;
}
else
{
TX=1;
}
}
if(i==9)//停止位
{TX=1;
}
if(i==10)
{
TR0=0;
i=0;
ti=1;//发送完成
}
}
voidmain()
{
TX=1;//使TX处于空闲状态
Timer0_Init();
Isr_Init();
while
(1)
{
Send_Byte(65);//0x41
delay(60000);
}
}
实验引入了定时器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,这样就保证了串口的时序更加准确,虽然牺牲了定时器的准确度。
实验二,片外串口发送一个字节。
好了现在开始我们的实验之旅。
直接看代码吧。
[cpp] view plain copy
#include"reg51.h"
#
defineu16unsignedint
#defineu8unsignedchar
voiddelay(u16x)
{
while(x--);
}
voidUart_Init()//串口初始化
{
SCON=0x50;//8位异步模式
TMOD|=0x20;//定时器1工作方式2
TH1=253;//9600bit/s
TR1=1;
}
voidSend_Byte(u8dat)
{
SBUF=dat;//启动发送,只需要把发送内容给SBUF这个寄存器
while(TI==0);//等待发送完成,因为TI为1时表示在发送停止位
TI=0;
}
voidmain()
{
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”。
下面我们通过一个实验来展示如何发送字符串。
我们实验的目标是打印字符串“HelloWorld!
第一!
”到打印机。
直接上代码。
[cpp] view plain copy
#include"reg51.h"
#defineu16unsignedint
#defineu8unsignedchar
voiddelay(u16x)
{
while(x--);
}
voidUart_Init()//串口初始化
{
SCON=0x50;//8位异步模式
TMOD|=0x20;//定时器1工作方式2
TH1=253;//9600bit/s
TR1=1;
}
voidSend_Byte(u8dat)//串口发送一个字节
{
SBUF=dat;//启动发送,只需要把发送内容给SBUF这个寄存器
while(TI==0);//等待发送完成,因为TI为1时表示在发送停止位
TI=0;
}
voidSend_String(u8*str)//发送一个字符串*str为字符串第一个字符的地址
{
abc:
//标号
if(*str!
=0){
Send_Byte(*str);
str++;
gotoabc;
}
}
voidmain()
{
Uart_Init();
while
(1)
{
Send_String("HelloWorld!
第一!
");
Send_Byte(10);
delay(60000);
delay(60000);
}
}
实验效果
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 电子工程 单片机C语言之串口通信协议代码分享 电子 工程 单片机 语言 串口 通信协议 代码 分享
![提示](https://static.bdocx.com/images/bang_tan.gif)