51单片机学习笔记(清翔版)(19)——串口通信
时间:2023-08-13 15:07:00
学好了中断再学串口通信
今天的课程是关于数据传输的,这在工业控制和商业控制中非常重要。数据传输包括串口和并行通信。
由于工作方式相同,这里提到的计算机可以理解为单片机。
单片机通信是指单片机与外部设备之间的温度传感器(温度传感器,使单片机知道温度传感器收集的温度)。
控制数码管是并行通信P0直接赋位选择或段选,P一次传输8条数据,如08条线P0=0x01,一次性传输8位数据。
询问和响应是指询问的工作方式。询问是否准备好接收数据,并通过响应线传输准备。然后发送8位数据。
从这节课开始,我们学习的数据传输大多使用串行通信,前8个*8点阵也是串行通信。
缺点:数据传输的控制比平行复杂,这意味着数据传输不止一条数据线,但也需要控制线,控制数据传输的节奏是多长,是一个接一个的数据传输,所以告诉每个传输需要多长时间,比如前8*8点阵有时钟线。
右侧是单片机的串行通信。一个发送引脚,一个接收引脚,另一个接收引脚另一个接收引脚,通过这两行数据交换。以下是相互共享的地方。
下面是异步通信的图片。发送一个字节数据,前面有起始位置,后面有停止位置。读取起始位置,知道数据何时来,读取停止位置,知道帧数据已经完成,由硬件设备判断。每帧数据之间的间隔是任意的。
同步通信数据源源不断地发送,有一条控制线,即时钟线,给时钟,发送数据,只要时钟不停止,发送数据就不能停止。
这些理论就是一个了解,单片机串口通信内部硬件有这个功能,我们只需要配置内部的寄存器就可以了,如果要发送数据,只要给寄存器赋值就可以了,如果接收,我们读这个数据就能读到,我们不用关心帧与帧之间和位与位之间的间隔,如果不自带这个功能,我们用软件模拟,我们就需要清除的知道这些理论知识。例如I2C是软件模拟。
LSB是低位,MSB是高位。
检查前面的数据是否在发送过程中受到干扰或其他原因造成错误。
8*8点阵,使用SPI通信,就是外同步的方式,发送机是单片机,接收机是点阵内部的595,发送机有一个时钟引脚,控制接收机,直接传到595的时钟引脚上,每来一个时钟就发一个数据,收到时钟就接一个数据,源源不断的时钟就源源不断的发,间隔由时钟控制的,可以看出同步通信比异步通信效率高很多,因为时钟是源源不断的,数据是源源不断的过去,异步通信是一帧一帧的,中间夹杂着起始位置、停止位置、验证位置,这三个是无用的东西,也不能说无用,在异步通信中有用,但在同步通信中可以节省,所以效率高,同步通信是连续的,所以更快。
自同步以来,双方同意时钟是一样的。一个时钟发送一个数据,另一边接收一个数据。外同步是最常用的。开发板上一些,比如I2C设备数模转换芯片,E2PROM芯片,都是用的同步通信的外同步。
我们的串口是异步通信,后面是I2C和SPI是同步。
我们单片机串口是全双工的。
只需了解一下验证,就不多说了。因为我们的单片机有自己的串口,所以我们不需要奇偶验证,只需要配置寄存器。
这是指异步通信的传输速率。每帧数据的间隔是任意的,但每帧都是固定的,这是由传输速率的比特率决定的。
现在我们实际上没有使用这两个接口。
25针相对较少,早期的工业设备也可以看到,如90年出厂,分为公头和母头,或阳头和阴头。阳头有针脚,母头有孔。
还能看到9针式。
每个引脚的功能和如何排列都是统一的,否则就无法通信,所以需要这个标准。
我们现在不使用这两种板上用的微型usb内部只有4只脚,只有4只脚TXD、RXD,电源和地。
工业上有9英尺,家用电器很少
非平衡屏蔽双绞线是一种抗干扰线,将两条绞在一起。网多地用于网络电缆。当信号传输时,会有噪声和磁场干扰。使用它和屏蔽层,以尽可能地屏蔽干扰。
电容允许值是上述电气特性。
分布式电容(寄生电容)和电感将在线形成。线越长,分布式电容越大,电容式电感将形成LC当信号传输时,低通滤波电路会受到电路的影响而衰减。这个电容器不是人工添加的,而是自动生成的布线方式。该电容器存在于三极管和场效应管中,直接影响开关速度。电容器尺寸与两条线之间的距离成反比,与导体面积成正比。
看到实际的电容器,容量越大,体积就越大。例如,电解电容器片瓷片电容器(内部有许多金属片,它们是分开的,金属片是绝缘的,然后形成电容器。金属片面积越大,电容器越大,间隔越短,电容器越大)。
99H,H是HEX也就是说,16进制,这是地址,不需要关心reg52.h定义,直接写SBUF就可以。
我们通过程序发送要发送的值SBUF,然后通过门电路、发送控制器和波特率生成器控制每个发送速度(设置比特率),然后通过TXD(P3.1接口)发送出去。
通过RXD接收数据,通过移位寄存器发送到接收SBUF中间。如果我们想读取数据,直接读取SBUF当然也要设置比特率,要和发送一样。
这些都是内部具有串口功能的硬件。很简单。
使用比特率发生器T1做的,计算T一是计算波特率。
SCON是串口工作方式的选择。
串口也可以有查询和中断。查询方法由标志位控制。根据两个标志位,判断是否发送和接收完成。完成后将被放置1。我们直接中断。
必须使用中断IE寄存器。
SCON可位寻址。
SM0和SM1.设置工作模式。SM多机通信。REN是接收控制。TB8和RB8是第九位。TI和RI是发送和接收中断标志位,发送和接收将被放置1。
这两个是最重要的。
波特率时系统时钟的12分频为0。SMOD是波特率倍增。
方法1使我们使用最多,方法2、3是多机通信。
REN位0时,发送的数据不会存储SBUF是的。所以接收需要软件置1。
一定要记住软件把TI和RI清零,否则会一直中断。
什么时候用?SMOD外部时钟是11.0592,如果波特率较高,受时钟影响,波特率最大值有限,那么我们可以使用它SMOD加倍。
fosc是外部晶振,11.0592.
T1的初始值可以自己计算,也可以用软件计算。或者直接记住T1=0xFD。
若使用晶振12MHz,误差很大,导致传输过程中数据可能出错,比如我发1,可能接收3。
尽管机器周期1.085,再做软件延时,感觉没有12M好吧,但是软件延迟不重要,有一点误差也没关系,但是传输数据很重要。
然后开始编程
就这样来吧。
计算机(上位机)发送值,单片机收到的值以十进制的形式显示。
1 #include 2 3 #define uchar unsigned char 4 #define uint unsigned int 5 6 sbit we = P2^7; //定义数码管位选择锁定器接口 7 sbit du = P2^6; //定义数码管位选择锁定器接口 8 9 uchar num; 10 //数码管0~9段选表 11 uchar code leddata[]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; 12 //数码管1~3位选表 13 uchar code DPY[]={0xfe, 0xfd, 0xfb}; 14 void display(uchar i) 15 { 16 static uchar wei; 17 P0=0xff; 18 we=1; 19 P0=DPY[wei]; 20 we=0; 21 switch(wei) 22 { 23 case 0:du=1;P0=leddata[i/100];du=0;break; 24 case 1:du=1;P0=leddata[i0/10];du=0;break; 25 case 2:du=1;P0=leddata[i];du=0;break; 26 } 27 wei ; 28 if(wei==3) 29 wei=0; 30 } 31 ///中断服务特殊功能寄存器配置 32 void init() 33 { 34 35 TMOD |= 0x01; //定时器16为计数工作模式 36 TH0 =0xED; 37 TL0 =0xFF; //5ms 38 ET0 = 1; ///开定时器0中断 39 TR0 = 1.//启动定时器0 40 EA = 1; ///开总中断 41 } 42 void UART_init() 43 { 44 EA=1;///开总中断 45 ES=1;//开串口中断 46 SM0=0;SM1=1.//串口工作方式1 47 REN=1;//串口接受允许
48 TR1=1; //打开定时器1
49 TMOD|=0x20;//定时器1,工作模式2,八位自动重装
50 TH1=0xFD;
51 TL1=0xFD;//253,波特率(比特率)9600
52 }
53 void main()
54 {
55 init();//初始化定时器0
56 UART_init();//串口初始化
57 while(1)
58 {
59
60 }
61 }
62 void UART() interrupt 4
63 {
64 if(RI)
65 {
66 num=SBUF;
67 RI=0;
68 }
69
70 }
71 //定时器0中断服务程序 做数码管动态扫描,不用软件延时
72 void timer0() interrupt 1
73 {
74 TH0 =0xED;
75 TL0 =0xFF; //5ms 模式1非自动重装需要手动重装
76 display(num);
77 }
发16进制的数字。刚打开串口时,会接收到别的数,没关系,下载也是通过串口的。
下面把接收到的数据+1再发送出去。
1 #include
2
3 #define uchar unsigned char
4 #define uint unsigned int
5
6 sbit we = P2^7; //位定义数码管位选锁存器接口
7 sbit du = P2^6; //位定义数码管位选锁存器接口
8
9 uchar num;
10 //数码管0~9段选表
11 uchar code leddata[]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
12 //数码管1~3位选表
13 uchar code DPY[]={0xfe, 0xfd, 0xfb};
14 void display(uchar i)
15 {
16 static uchar wei;
17 P0=0xff;
18 we=1;
19 P0=DPY[wei];
20 we=0;
21 switch(wei)
22 {
23 case 0:du=1;P0=leddata[i/100];du=0;break;
24 case 1:du=1;P0=leddata[i%100/10];du=0;break;
25 case 2:du=1;P0=leddata[i%10];du=0;break;
26 }
27 wei++;
28 if(wei==3)
29 wei=0;
30 }
31 //中断服务特殊功能寄存器配置
32 void init()
33 {
34
35 TMOD |= 0x01; //定时器16为计数工作模式
36 TH0 =0xED;
37 TL0 =0xFF; //5ms
38 ET0 = 1; //开定时器0中断
39 TR0 = 1;//启动定时器0
40 EA = 1; //开总中断
41 }
42 void UART_init()
43 {
44 EA=1;//开总中断
45 ES=1;//开串口中断
46 SM0=0;SM1=1;//串口工作方式1
47 REN=1;//串口接受允许
48 TR1=1; //打开定时器1
49 TMOD|=0x20;//定时器1,工作模式2,八位自动重装
50 TH1=0xFD;
51 TL1=0xFD;//253,波特率(比特率)9600
52 }
53 void main()
54 {
55 init();//初始化定时器0
56 UART_init();//串口初始化
57 while(1)
58 {
59
60 }
61 }
62 void UART() interrupt 4
63 {
64 uchar temp;
65 if(RI)
66 {
67 num=SBUF;//读SBUF,读出串口接收到的数据
68 RI=0;//软件清零接受标志位
69 temp=num;
70 SBUF=++temp;//写SBUF,把要发送的数据送给发送缓冲器,然后单片机硬件会自动把数据发到TXD引脚
71 }
72 if(TI)//判断是否发送完成
73 TI=0;//清零发送完成标志位
74 }
75 //定时器0中断服务程序 做数码管动态扫描,不用软件延时
76 void timer0() interrupt 1
77 {
78 TH0 =0xED;
79 TL0 =0xFF; //5ms 模式1非自动重装需要手动重装
80 display(num);
81 }
上方式计算机接收端。
如果我们选择文本格式发送,发送1后,数码管会显示49,因为发文本格式是以asc码格式发送的。
看到图中1是49。发送1,文本格式的1变为数字是asc的49,那么数码管接收到的就是49,而发给单片机,又以文本格式显示,那么又变回了文本格式,49+1是50,也就是2。文本发送9,那么单片机显示57,计算机接收到文本格式是冒号。
接下来如何通过串口发送汉字?
发送汉字可以使用标准库函数,可以使用printf()和puts(),需要#include
这个串口初始化没有用到中断,我们用的查询方式。
puts()上面为什么TI=1,因为我们给SBUF送值后,串口会自动发送,发送完,硬件置1,我们为什么用软件置1?
puts()是调用了putchar()函数,这个函数发送前先进行while(!TI)判断,如果TI没置1,那么会一直在这里等待,那么要发送的字符就送不到SBUF里,所以就一直不会发送,所以要是用puts()和printf()都应该先把TI置1。
我们也可以在帮助中看到。
然后选择uVision help
putchar()函数就是把字符c发送到串口。
1 #include
2 #include
3
4 #define uchar unsigned char
5 #define uint unsigned int
6
7 void UART_init()
8 {
9 SM0=0;SM1=1;//串口工作方式1
10 TR1=1; //打开定时器1
11 TMOD|=0x20;//定时器1,工作模式2,八位自动重装
12 TH1=0xFD;
13 TL1=0xFD;//253,波特率(比特率)9600
14 }
15 void Delay1ms() //@12.000MHz
16 {
17 unsigned char i, j;
18
19 i = 12;
20 j = 169;
21 do
22 {
23 while (--j);
24 } while (--i);
25 }
26 void delay(uchar num)
27 {
28 while(num--)
29 {
30 Delay1ms();
31 }
32 }
33 void main()
34 {
35 UART_init();//串口初始化
36 while(1)
37 {
38 TI=1;
39 puts("大家好!欢迎学习单片机");
40 while(!TI);
41 TI=0;
42 delay(1000);
43 }
44 }
接收的时候是以文本接收
1s发一次,且换行,而printf不换行。