(1)通信方式可分为并行通信和串行通信。由于并行通信占用资源过多且成本高,故串行通信更为普遍。
(2)通信方式从传输方向上又可以分为单工通信、半双工通信和全双工通信三类。
关于UART模块,STC89C52有两个专门引脚,P3^ 0和P3^ 1,即RXD和TXD。RXD是串行接收引脚,TXD是串行发送引脚。在计算机上一般有个9针的串行接口,如下图:
它的9个引脚分别为:
引脚 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
定义 | 载波检测DCD | 接收数据RXD | 发送数据TXD | 数据终端准备好DTR | 信号地线 | 数据准备好DSR | 请求发送RTS | 清除发送CTS | 振铃提示RI |
常用的引脚是2、3、5。虽然这三个引脚和单片机上的串口名称相同,却不能直接和单片机的串口连接。因为单片机是正逻辑,而RS-232是负(反)逻辑,即1代表﹣3V ~ ﹣15V电压, 0代表﹢3V~﹢15V电压。所以需要借助一个电平转换芯片MAX232,如下图:
MAX232芯片在这里起到的就是“中间人”的作用,将RS-232电平转换成UART电平,也把UART电平转换成RS-232电平,从而实现标准RS-232接口和单片机UART之间的通信连接。
由于我们并不关心通信的具体过程,只需一个通信的结果,这意味着我们可以在单片机内部做一个硬件模块,让它自动接收数据,接收完成后,通知我们即可。51单片机的内部也恰好存在这样一个UART模块,UART串口的结构由串行口控制寄存器SCON、发送和接受电路三部分组成,与串口相关的寄存器有SCON、PCON、AUXR。
串行控制寄存器用以选择串行通信的工作方式和某些控制功能。其格式如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符号 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
常用的配置是:定时器T1用于波特率发生器,一般使用模式2(8位制动重载),串口为模式1,允许接收。
TMOD = (TMOD & 0x0F)| 0x20;//T1工作在模式2
SCON = 0x50; //串口工作在模式1,且允许接收
电源控制寄存器PCON中的SMOD/PCON.7用于设置方式1、方式2、方式3的波特率是否加倍。PCON = 0x80时,波特率加倍。其他位在这里用不到,先不做介绍。
对于IAP15F2K61S2单片机,需要对新增的AUXR寄存器进行配置(注意!!!)各位格式及含义如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符号 | T0x12 | T1x12 | UART_M0x6 | T2R | T2_C/T | RB8 | T2x12 | SIST2 |
T0x12:定时器0速度控制位 .清零,传统速度12分频;置1,不分频
T1x12:定时器1速度控制位 .清零,传统速度12分频;置1,不分频
UART_M0x6:串口模式0的通信速度设置位。 清零,传统速度12分频;置1,不分频。
T2R:定时器2允许控制位。清零,不允许T2运行;置1,允许。
T2_C/T:控制定时器2用作定时器或计数器。清零,用作定时器(对内部系统时钟进行计数);置1,用作计数器,对引脚T2/P3.1的外部脉冲进行计数。
T2x12:定时器2速度控制位 .清零,传统速度12分频;置1,不分频
常用的选择是:串口工作在模式1,T1(或者T2)用作波特率发生器,并且采用传统速度12分频。
sfr AUXR = 0x8E ; //首先定义AUXR寄存器
//串口配置函数中
AUXR = 0x00;//如果使用T2作波特率发生器,则写为AUXR = 0x40
如果使用的是CT107D的开发板(蓝桥),忘了这两条语句,烧录完一定是失败的,泪的教训。
以串口工作在模式1为例: 在UART通信过程中,遵循先低后高的原则(低位先发,高位后发)。例如发送数据0xE4,即0b11100100,先让串行发送引脚TXD拉低电平,持续一段时间后,发送一位0,拉低,持续一段时间,发送0,拉高电平,持续一段时间,发送1…直到8位发送完。1/持续时间即为波特率(baud),也就是发送二进制数据位的速率,串口每秒钟传输的位数。在通信之前,双方约定好波特率,保持一致,即可正常通信。
约定好发送速度后,还需要约定好起始标志和结束标志,不管是提前接收还是延迟接收,都会导致数据传送错误。UART通信中,规定没有通信信号发生时,通信线路保持高电平(发送方和接收方均为高电平)。发送方在发送数据之前,先发一位0表示起始位,然后发送8位数据位(先低后高)(一个字节),之后再发送一位1表示停止位。也就是说,每帧数据由1个起始位 “0”、8个数据位 和1个停止位“1”(共10位)构成,其中起始位和停止位在发送时是自动插入。接收方原本保持高电平,一旦检测到一位低电平,便开始准备接收数据,接收到8位数据位后,检测到停止位,再准备下一个数据的接收。时域示意图如下:
对于发送引脚而言,左边是先发生的,右边是后发生的,数据位的切换时间分即为波特率分之一秒。
在具体使用UART模块时,串口的波特率是使用定时器T0中断体现的。硬件串口模块中,有一个专门的波特率发生器用来产生控制波特率,对于STC89C52系列单片机而言,这个波特率发生器只能是定时器T1或T2(需额外配置寄存器),不能是T0,默认使用T1。方式1下的波特率发生器必须使用T1的模式2,即自动重装载模式,定时器T1重载值计算公式为:
TH1=TL1=256 - 晶振值/12/32/波特率
电源管理寄存器PCON的最高位可以把波特率提高一倍,如果PCON |= 0x80,则定时器T1重载值计算公式为:
TH1=TL1=256 - 晶振值/12/16/波特率
关于“16”的解释:接收数据时把一位信号采集16次,取出第7、8、9次数据,如果这三次中有两次是高电平,则认为此位是1,如果两次是0,认为此位是0。这样即使受到干扰读错了一位数据,也依然保证最终结果的正确性。
串口通信的发送和接收电路有两个名称(地址)相同的SBUF寄存器,一个作为发送缓冲,一个作为接收缓冲。相当于有两个门牌号相同的房间,一个只进不出,一个只出不进。相互之间不干扰,实现UART的全双工通信。
代码功能:计算机以十六进制发送数据 ,单片机接收后以十进制在数码管上显示,并且将数据再发送到计算机
#include"reg52.h"
sfr AUXR = 0x8E;
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u8 code SMG[] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,
0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f}; //共阳数码管显示字符转换表
u8 LedBuff[8] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; //数码管显示缓冲区
u8 RxdByte = 0;
u8 T0RH = 0;
u8 T0RL = 0;
void Timer0Init(u16 ms);
void T1_UART_Init(u16 baud);
void LedScan();
void main()
{
EA = 1; //打开使能总中断
Timer0Init(1); //配置T0定时1ms
T1_UART_Init(9600); //T1配置波特率为9600
while(1)
{
LedBuff[1] = SMG[RxdByte & 0x0F]; //在数码管上显示接收字节的低四位
LedBuff[0] = SMG[RxdByte >> 4]; //在数码管上显示接收字节的高四位
}
}
void Timer0Init(u16 ms) //定时器0初始化函数
{
T0RH = (65536 - 11059200/12*ms/1000)/256; //T0定时器的高8位
T0RL = (65536 - 11059200/12*ms/1000)%256; // T0定时器的低8位
TMOD = (TMOD & 0x0F)|0x01; //定时器T0工作在模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
TR0 = 1; //打开T0定时器
ET0 = 1; //允许T0中断
}
void LedScan()
{
static u8 i = 0;
P2 = (P2&0x1f)|0xe0; //打开段选通道
P0 = 0xff; //关闭所有段选,消隐
P2 = (P2&0x1f)|0xc0; //打开位选通道
P0 = 0x01<<i; //位选
P2 = (P2&0x1f)|0xe0; //打开段选通道
P0 = LedBuff[i]; //段选
if(i<7) //一个扫描循环结束(8位数码管,8ms)
{
i++;
}
else
i = 0; //重新开始下一次循环
}
void InterruptTimer0()interrupt 1
{
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
LedScan(); //数码管扫描显示
}
void T1_UART_Init(u16 baud) //串口配置函数,baud为通信波特率
{
AUXR = 0x00;
SCON = 0x50; //0101 0000配置串口工作在模式1
TMOD = (TMOD & 0x0F) | 0x20; //配置T1工作在模式2
TH1 = 256 - (11059200/12/32)/baud; //计算T1重载值
TL1 = TH1; //初值等于重载值
ET1 = 0; //T1用作波特率发生器,禁止T1中断
ES = 1; //使能串口中断
TR1 = 1; //启动T1
}
void InterruptUART()interrupt 4
{
if(RI) //接收到字节
{
RI = 0; //手动清零接收中断标志位
RxdByte = SBUF; //接收到的数据保存到接收字节变量中
SBUF = RxdByte; //接收到的数据再直接发回,提示输入信息是否正确接收
}
if(TI) //字节发送完毕
{
TI = 0; //手动清零发送中断标志位
}
}
用到了定时器T0(方式1,16位,用于数码管的扫描),T1(方式2,8位自动重载,用作波特率发生器),T0的中断,UART串口的中断。