串口通讯
基本概念
数据帧:就是在线路上传递的一组数据,这组数据可大可小。以电子,无线为介质传输。(对应OSI网络7层模型中的数据链路层)
比特率:在1秒钟所传递的bit数量。(bit/s)
波特率:一秒内的载波数量。(Baud)
比特率和波特率不同的是,比特率重点在传输了多少bit,波特率是指传输了多少承载信息的波。在高低电平信息传输中,两者相同。
晶振频率:指在一秒内晶振的震动频率。(fosc)
时钟频率:时钟频率 = 晶振频率
状态频率:时钟频率X2
机械频率:在1T单片机中,机械频率 = 时钟频率。在12T单片机中,机械频率 = 12乘以时钟频率
串口与并口
并口传输
优点:传输速度快
缺点:传输范围短
串口传输
优点:传输距离长
缺点:传输速度慢
同步通讯与异步通讯
同步通讯:指通讯双方严格按照相同的时钟频率通讯。但是传输的帧很长。
异步通讯
每帧很短,而且有控制bit起到开始结束效验的作用。传输效率低。且每帧之间都用空闲,空闲时电平为1;
效验位
硬件与兼容
232协议和TTL协议
232协议为大多数主机使用的,高电平为12v,低电平为-12v。
TTL协议多为单片机所用,高电平为5v,低电平为0v。
为了保障两种协议兼容,使用兼容芯片如CH340芯片。使得232转TTL,TTL转232。
由于许多主机没有串口,CH340会将串口转USB。如果使用340芯片,则要在PC上下载对应的驱动。
TXD为发送引脚,RXD为接收引脚。
串口
目前只用管着三个帧就好了,公母区别稍微联想一下就很好记。处了这辆还有个串口,就是USB接口。现在基本都是这种方式,传统的串口很少见了。
目前的技术都是用USB虚拟出串口来使用。这个模块有个MAX3232,负责232协议和TTL兼容的,GND用来使得两边电平相同。要不电平会乱跳。
寄存器
串口有关寄存器有:IE,TCON,SCON,PCON,AUXR,SUBF,移位寄存器
IE寄存器和TCON寄存器是定时器里的。
SCON寄存器
RI :接收中断标志位,数据接收结束后,该位由硬件自动置1。由软件置0
TI:发送中断标志位,数据发送结束后,该位由硬件自动置1,由软件置0
RB8:
当SM = 2时,存放接收数据的第9位,在模式2 3 中,用于存放收到的第九位数据,处理数据特征,特征与TB8对应。
当SM2 = 0,且模式为2 3 时,此位用于接收奇偶效验位
TB8:
当SM2 = 0,且模式为2 3时,此位用于发送奇偶效验位
当SM2 = 1时,存放发送数据的第9位,在模式 2 3 中,如果此位置1,则发送的是地址,如果此位置0,则发送的是数据。主要用于多处理机通讯,在此不深究。
REN:串行接收允许位,1允许,0禁止。
SM2:多机通讯使能位,用于处理多机通讯。与接收到的第九位数据有关,当SM2 = 1时,只有第九位数据为1时,才将数据交由SBUF并将第九位数据交由RB8,如果SM2 = 0时,无论第九位数据为什么,都将数据交由SBUF。此位用于多机通讯,不再深究。
SUBF寄存器
一个寄存器两个空间,一个收一个发。地址相同。SBUF = A,为发送,A = SBUF为读取
PCON有关串口就一位,最高位SMOD。当此位置1时,波特率翻倍。
移位寄存器:从SBUF获得并行数据,然后在移位脉冲下一位一位的输出或接收。
IE寄存器和TCON寄存器在这里不再赘述,为定时器内容。需要注意的是当一个定时器作为串口波特率发生器时,需要静止该定时器产生中断
AUXR一般无需设置,他决定了使用那个时钟频率作为波特率发生器以及详细参数。
四种工作方式
工作方式基础
1:SM0和SM1的组合决定了串口的四种工作方式。
方式0:串口为同步移位寄存器的输入输出方式(波特率固定为fosc/12)fosc = 晶振频率,且必须满足SM2 = 0
输出:
首先向SBUF向移位寄存器写入要输出的数据。RXD引脚负责发送数据。TXD负责发送移位脉冲。每发送一次脉冲,数据都从移位寄存器中移出来一位,就像C语言中的移位操作符。移位会导致丢弃和填充。在这里丢弃对应发送,也就是从移位寄存器移出的数据,填充就是填充0。发送随着移位脉冲的节拍有序发送。当发送完数据后,TI计时器会发送中断。也就是前面有过的串行中断。
输入:
首先REN 由1变为0,表示允许接收。然后RXD随着TXD移位脉冲输入到移位寄存器中。RI在输入过程中为0,输入结束后为1,表示输入结束。发送接收中断。并将数据交给SBUF寄存器
方式1:10位异步通信(波特率可变)
从起始位开始到停止位结束,中间1字节的数据。
REN置1时,不断采样RXD的脉冲,当检测到RXD中有脉冲变化,则REN置0开始接收数据。使其移位到移位寄存器中去。REN为1时对RXD的检测称为位采样脉冲。采样频率为波特率的16倍。
将数据八位不断移入移位寄存器,数据从移位寄存器右边移入,当起始位位于移位 。
方式2和3 方式2的波特率为fosc/64或fosc/32,方式3可变
输出
先将起始位输出到TXD引脚,随后移位寄存器开始输出。第一次右移位将D0输出,并在移位寄存器第9位补停止位1,随后依次输出,并补0。当停止位移动到输出位时,检测到这一行为会控制最后一次移位并使得T1置1,引发中断。
输出
接收数据时,移位寄存器从右向左存入数据。当起始位移动到最左边时,检测这一行为并控制最后一次移位。此时诺RI = 0且 SM2 = 0或第九位为1,则RI置1触发中断。并将移位寄存器的数据前八位装入SBUF,第九位装入RB8中。
代码1:实现数码管显示主机用串口发来的数据
#include<reg52.h>
sbit la = P0^0;
sbit lb = P0^1;
#define dataport P1//定义数码管数据端口
unsigned int lbarr[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段码
unsigned int laarr[] = {0x7f};//位码
void delay(unsigned int time)//延迟函数
{
while(time--){}
}
void dispaly(unsigned char a)//显示函数
{
int i = 0;
while(i<8)
{
dataport = laarr[0];
la = 1;
la = 0;
dataport = lbarr[a];
lb = 1;
lb = 0;
delay(1000);
i++;
}
}
void init_serial()
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA = 1;
ES = 1;
}//串口初始化,在工具中得到正确的值复制并打开IE寄存器各项开关,当定时器作为波特率发生器时必须关闭对应的计时器中断
int main()
{
while(1)
{
init_serial();
}
}
void interrupt_serial() interrupt 4
{
unsigned char serial_data;//SBUF缓冲变量
if(RI)//如果中断是由接收引起的
{
serial_data = SBUF;//读取SBUF数据
P1 = serial_data;//显示在P1针脚上方便观察
dispaly(serial_data);//带入显示函数
RI = 0;//置0
}
if(TI)//如果是发送引起的
{
TI = 0;//置0
}
}
代码2
实现单片机返还从主机的通讯
#include<reg52.h>
sbit la = P0^0;
sbit lb = P0^1;
#define dataport P1//定义数码管数据端口
unsigned int lbarr[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段码
unsigned int laarr[] = {0x7f};//位码
void delay(unsigned int time)//延迟函数
{
while(time--){}
}
void dispaly(unsigned char a)//显示函数
{
int i = 0;
while(i<8)
{
dataport = laarr[0];
la = 1;
la = 0;
dataport = lbarr[a];
lb = 1;
lb = 0;
delay(1000);
i++;
}
}
void init_serial()
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA = 1;
ES = 1;
}//串口初始化,在工具中得到正确的值复制并打开IE寄存器各项开关,当定时器作为波特率发生器时必须关闭对应的计时器中断
int main()
{
while(1)
{
init_serial();
}
}
void interrupt_serial() interrupt 4
{
unsigned char serial_data;
if(RI)
{
serial_data = SBUF;
P1 = serial_data;
dispaly(serial_data);
RI = 0;
}
if(TI)
{
TI = 0;
}
}