哈喽哈喽大家好,我是阿飞的小蝴蝶,大家可以叫我阿飞或者小飞,我又回来啦!
我们接着上节的内容来给讲一下51单片机串口的使用方法,好的,直接开始我们今天的内容。
串行口的结构:
这里有两个物理上独立的接收、发送寄存器SBUF,它们占用同一内存(99H)。
在程序逻辑上,SBUF只有一个,既代表发送寄存器,又代表接收寄存器,具有同一单元地址,但在物理结构上,则有两个完全独立的SBUF,一个发送寄存器SBUF和一个接收寄存器SBUF。如果CPU写SBUF,数据会被存入发送寄存器准备发送;如果CPU读SBUF,则读入的数据一定来自接收寄存器SBUF。(a=SBUF;SBUF=a;)。
接下来讲一下与串口有关的寄存器:
首先是SCON寄存器:
这里我简单说一声每一位的功能:
SM0:当PCON寄存器中SMOD0位为1时,该为用于帧错误检测。为0时,该位与SM1一起控制串口通信的工作方式。(这里一般用于控制工作方式)
SM0、SM1控制串口通信工作方式:
这里的SYSclk是晶振频率,方式0和方式2是比波特率固定的,具体大家可以自己算一下。方式1和方式3通过配置定时器1来改变波特率,SMOD是PCON寄存器中可以配置的,配置为1时可以将波特率提升1倍,后边会再介绍。
我们一般使用方式1,发送/接收8位数据,波特率可变。
SM2:多机通信控制位,主要用在方式2和方式3,为0时双机通信,为1时多机通信。
REN:串行接收允许位,REN=0时禁止接收,REN=1时允许接收
TB8/TR8:在方式2和方式3时分别用于存放发送/接收的第9位数据。
TI:发送中断请求标志位,数据发送结束时,标志位被自动置1并向CPU请求中断,需通过程序置0。
RI:接收中断请求标志位,数据接收结束时,标志位被自动置1并向CPU请求中断,需通过程序置0。
接下来是PCON寄存器:
SMOD:波特率选择位:SMOD=1时,工作方式1、2、3中波特率加倍。
最后还是我们熟悉的IE寄存器:
这里与串口有关的为:
EA:CPU总中断允许控制位,前边多次介绍过的,不多说了
ES:串行口中断允许位,ES=1,允许串行口中断;ES=0,禁止串行口中断。
我们配置好串口工作模式、打开串口中断后,CPU接收到串口的中断请求时就会相应串口中断执行串口中断服务函数。
波特率计算:
当串口工作在方式1时,波特率由定时器1的溢出率决定,此时定时器1通常选用定时器初值自动重装的工作模式(工作模式2,TMOD=0x20;)此时仅用低8位来计数,溢出以后高8位的存放的数据会放入低8位中重新计时,无需再通过程序重放初值
因此,波特率=(2^SMOD/32)*(单片机时钟频率/(256-X)),X是初值。
在实际应用中,通常是选确定波特率,再根据波特率算出TI的定时初值,因此:X=256-(2^SMOD/32)*(单片机时钟频率/波特率)
我举个栗子:波特率为9600时,T1存放的初值(晶振频率为11.0592MHZ)。
SMOD=0,(2^SMOD/32)=1/32,(单片机时钟频率/波特率)=((11.0592MHZ/12)/9600)=96。X=253,因此T1存放初值为253(TH1=0xfd;TL1=0xfd;)。
再使用串口前,我们要把上边讲到的寄存器进行配置,设定好工作方式。
1、设置T1工作方式(编程TMOD寄存器);
2、计算T1的初值,装载TH1、TL1;
3、启动T1(编程TCON中的TR1位);
4、确定串行口控制(编程SCON寄存器);
5、如需串口在中断方式工作时,要进行中断设置编程IE寄存器
好的,现在用代码来对串口进行初始化:
void uart_init()
{
TMOD = 0x20; //T1工作模式2 8位自动重装
TH1 = 0xfd;
TL1 = 0xfd; //比特率9600
TR1 = 1; //启动T1定时器
SM0 = 0;
SM1 = 1; //串口工作方式1 10位异步
REN = 1; //串口允许接收
EA = 1; //开总中断
ES = 1; //串口中断打开
}
下边用一个例子来带大家了解一下:
串口收发数据,设置波特率为9600,把接收到的数据用P1端口的LED灯以二进制的形式显示(灭为0,亮为1),在把接收到的数据加1并发出。
#include <reg52.h> #define u8 unsigned char #define u16 unsigned int u8 num; //用于存放接收/发送数据 void delay(u16 z) { u16 x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 EA = 1; //开总中断 ES = 1; //串口中断打开 } void main() { UART_init(); //串口初始化 while(1); } void UART() interrupt 4 { if(RI) //检测是否接收完成 { num = SBUF; //采集接收到的数据 P1 = SBUF; num++; //把接收的数据加1并发送 RI = 0; SBUF = num; while(!TI); //等待发送完成 TI = 0; } }
当我们使用串口发送字符串时,可以直接添加头文件“#include <stdio.h>”,这个就是我们学习C语言时常用到了,我们可以直接调用其中的“printf”、“putchar”等函数来通过串口发送数据,这里要注意的是:在发送前要通过程序来把“TI”置1,因为在这些函数开头就会通过“while(!TI);”来判断数据是否发送完成。
/*************************************************
void putchar (unsigned char sbyte)
{
while(!TI); //等待数据发送完成
//如果没有置1,程序会卡在这里
SBUF = sbyte;
}
*************************************************/
好的,现在用程序来实现一下:
#include <reg52.h> #define u8 unsigned char #define u16 unsigned int u8 num; //用于存放接收/发送数据 void Delay(u16 n) { u8 j; while(n--) for(j=113;j>0;j--); } void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 }//这里没有使用中断和接收功能,可以不配置 void main() { UART_init(); //串口初始化 while(1) { TI = 1; printf("欢迎大家使用串口通信!\n"); while(!TI); //等待发送完成 TI = 0; Delay(1000); //每隔1S发送一次 } }
调试串口时,可以使用“STC-ISP(V6.85)”(其它版本应该也可以)
好的,本节内容就先到这里啦,这节没有小练习啦,我会找个时间来更新一篇“备赛番外篇”来带大家使用一些串口(保证是大家都想看到的哦~)