UART:通用异步收发器,通常用于单片机和电脑之间以及单片机和单片机之间的通信。
在 UART 通信过程中,是低位先发,高位后发的原则。
波特率:发送二进制数据位的速率,习惯上用 baud 表示,即我们发送一位二进制数据的持续时间=1/baud。
台式机 RS232接口:
单片机和PC进行通信,使用串行通信标准,但电平标准不一致,需要进行转换。RS232串口执行负逻辑电平标准,即-3V~-15V电压表示1,+3V~+15V电压表示0,而单片机执行TTL电平标准,即5V代表高电平,0V代表低电平。在开发板中使用MAX232芯片进行电平转换。笔记本电脑普遍不再集成串口,而用USB接口代替,则需在开发板中集成USB转串口电路,使用到CH340芯片进行电平转换。
UART模块
单片机中集成UART硬件模块,用于自动收发数据。51单片机中的UART串口由串行口控制寄存器SCON、发送和接收电路三部分构成。
在硬件串口模块中,有一个专门的波特率发生器用来控制发送和接收数据的速度。对于STC89C52 单片机来讲,这个波特率发生器只能由定时器 T1 或定时器 T2 产生,而不能由定时器 T0 产生。
方式 1 下的波特率发生器必须使用定时器 T1 的模式 2,也就是自动重装载模式,定时器的重载值计算公式为:TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率。
和波特率有关的还有一个寄存器,是一个电源管理寄存器 PCON,他的最高位可以把波特率提高一倍,也就是如果写 PCON |= 0x80 以后,计算公式就成了:TH1 = TL1 = 256 - 晶振值/12 /16 /波特率。
解释:256 是 8 位定时器的溢出值,也就是 TL1 的溢出值;晶振值是 11059200,12 是说 1 个机器周期等于 12 个时钟周期;一个波特率包含高电平和低电平时段,在这取其中一个时段所以除以2;串口模块接收数据采取的方式是把一位信号采集 16 次,其中第 7、8、9 次取出来,这三次中其中两次如果是高电平,那么就认定这一位数据是 1,如果两次是低电平,那么就认定这一位是 0。这样一旦受到意外干扰读错一次数据,也依然可以保证最终数据的正确性。
串行通信基本步骤:(1)配置串口为模式1;(2)配置定时器T1为模式2,即自动重装模式;(3)根据波特率计算TH1和TL1的初值,如果有需要可以使用PCON进行波特率加倍;(4)打开定时器控制寄存器TR1,运行定时器,同时关闭T1中断。
通信实例代码:
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //数码管显示字符转换表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[7] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff};
unsigned char THR0,TLR0;
unsigned char RxdByte = 0; //串口接收到的字节
void ConfigTimer(unsigned long ms);
void ConfigUART(unsigned int baud);
void main()
{
EA = 1; //使能总中断
ENLED = 0;
ADDR3 = 1;
ConfigTimer(1); //定时1ms
ConfigUART(9600);
TMOD = TMOD & 0xF0;
TMOD = TMOD | 0x01; //设置定时器0模式1 ,不影响高四位
ET0 = 1;
TR0 = 1;
while(1)
{
LedBuff[0] = LedChar[RxdByte & 0x0f];
LedBuff[1] = LedChar[RxdByte>>4];
}
}
void ConfigTimer(unsigned long ms)
{
unsigned long temp;
temp = 65536 - ms*11059200/12/1000;
THR0 = (unsigned char)(temp>>8);
TLR0 = (unsigned char)temp;
}
void ConfigUART(unsigned int baud)
{
SCON = 0x50;
TMOD &= 0x0f;
TMOD |= 0x20;
TH1 = 256 -(11059200/12/2/16)/baud;
TL1 = TH1;
ET1 = 0; //禁止T1中断
ES = 1; //使能串口中断
TR1 = 1;
}
void LedScan()
{
static unsigned char i = 0;
P0=0xff;
switch(i)
{
case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0=LedBuff[i];i++;break;
case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0=LedBuff[i];i++;break;
case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0=LedBuff[i];i++;break;
case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0=LedBuff[i];i++;break;
case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0=LedBuff[i];i++;break;
case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0=LedBuff[i];i=0;break;
default:break;
}
}
void InterruptTimer0() interrupt 1
{
TH0=THR0;
TL0=TLR0;
LedScan();
}
void InterruptUART() interrupt 4
{
if(RI)
{
RI = 0;
RxdByte = SBUF;
SBUF = RxdByte;
}
if(TI)
{
TI = 0;
}
}
十六进制收发和字符收发解释:十六进制收发都是按字节数据的真实值进行的;而字符格式发送和字符格式接收,是按 ASCII 码表中字符形式进行的,但它实际上最终传输的还是一个字节数据。