串口
一.串口原理
1.通信基础——并行和串行
串口是一种通信协议,也叫总线协议。总线可以理解为导线。那么导线之间怎么传递信息?通过传递高低电平来传递信息。
并行通信是将数据通过多条总线一次性传递信息,串行通信是通过一条总线分多次传递信息。
并行通信通讯时间短,但是会导致总线多,结构复杂,布线难度大,而且不同总线之间可能会存在干扰,所以实际工程中大多数还是使用串行通信。
2.通信基础——单工和双工
单工通信:数据只能从发送器到接收器,单向通信。
双工通信:数据可以双向传递。半双工:两个器件通信时,前一个器件给后一个器件发数据时后一个器件不能同时给前一个器件发数据。
全双工就是可以同时接发数据。
通过上面的基础知识可知,串口是串行的全双工的通信协议。
3.通信基础——波特率
在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。波特率表示单位时间内传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,波特率即指一个单位时间内传输符号的个数。波特率可以被理解为一个设备在单位时间内发送(或接收)了多少码元的数据,它是对符号传输速率的一种度量,表示单位时间内传输符号的个数(传符号率)。
比特率指单位时间内通过信道传输的信息量(也称为位传输速率),即单位时间内传送的二进制位数,用来表示有效数据的传输速率。
串口是1bit进行传输的,所以其码元就代表一个二进制数。
所以在串口通信中波特率也可以说是单位时间内传递的二进制位数,数值上与比特率相同,但意义不同。
4.UART数据帧格式
- 起始位 :发送1位逻辑0(低电平),开始传输数据。
- 数据位 :可以是5~8位的数据,先发低位,再发高位,一般常见的就是8位(1个字节),其他的如7位的ASCII码。
- 校验位 :奇偶校验,将数据位加上校验位,1的位数为偶数(偶校验),1的位数为奇数(奇校验)。
- 停止位 :停止位是数据传输结束的标志,可以是1/1.5/2位的逻辑1(高电平)。
- 空闲位 :空闲时数据线为高电平状态,代表无数据传输。
发送0X33数据帧格式
如果再发其他数据,再依次循环这个过程即可。
UART是异步传输,以1个字符为传输单位,传输2个字符之间的时间间隔,比如传输0X33后再传输0X35,这两者时间间隔是未知的。
但是同一字符内相邻位间的时间间隔是确定的,比如0X33低两位的1和1之间的时间间隔是确定的,这涉及到UART传输速率的概念—— 波特率。
波特率的单位是bps,全称是bit per second,意为每秒钟传输的bit数量。
两个串口之间是如何发送和接受数据呢?
首先,UART1以9600波特率发送0X33,先在数据线上放1个104us脉宽的低电平(起始位),然后是连续2个104us脉宽的高电平(2bit逻辑1),依次类推。
其次,UART2以9600波特率接收0X33,通过数这些数据的脉宽,来确认数据。
为了确保数据传输的正确性,减少误差,一般UART1和UART2之间的波特率差别小于10%,一次最多只能传输1个字节(8bit),也有效减小了累计误差。
总结:串口是串行(一条总线传输数据)全双工(发送端和接收端可以同时接发数据)异步(发送端和接收端有各自时钟)通信协议。串行传输信息成本低,但数据的传送控制比并行通信复杂。异步通信不要求收发双方时钟一致,容易实现,但是要加上起始位,校验位和停止位 ,传输效率不高,而且一次最多只能传输1个字节(8bit),减小由于时钟不一致带来的累计误差。
二.串口模块化编程
1.串口模块化编程原理
(1)串口选择工作模式
我们主要使用串行口1的工作模式1,要将SM0 = 0,SM1 = 1,进入工作模式1。
我们应用定时器2作为串行口的波特率发生器。
(2)串口模式一发送过程
(3)串口模式一接收过程
2.串口模块化编程代码
#include "Usart.h"
void UsartInit(void) //4800bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
T2L = 0x8F; //设定定时初值
T2H = 0xFD; //设定定时初值
AUXR |= 0x10; //启动定时器2
ES = 1; //允许串口中断
}
这些代码可以通过isp软件的波特率计算器直接生成。
//串口发送过程
void Usart_Send_String( unsigned char *Usart_String ) //char8位变量,*Usart_String是指变量第一位的地址
{
while(*Usart_String != '\0') // '\0'是最后一位,就是不到最后一位一直循环。
{
SBUF = *Usart_String;// 把变量的数据写入SBUF
while(TI == 0);//TI为0,意味着写入过程还在继续,不断将变量内容写入SBUF。当TI为1时,写入过程结束,跳出循环。
TI = 0;
Usart_String++;
}
}
我们编程只需要将变量的数据写入SBUF,之后的移位,输出由硬件自动完成。
//串口接收过程
void Uart() interrupt 4
{
if (RI)
{
RI = 0; //清除RI位
Uart_Buf[Uart_Buf_Index] = SBUF; //P0显示串口数据
Uart_Buf_Index++;//第n个数据过来之后,变成了n
}
}
在串口通信中,硬件已经将数据载入SBUF中,我们要将SBUF中的数据存入变量中,并且将RI置0。现在写入的数据已经在变量中,只有将它通过数码管显示就行了。
void Uart_Proc(void)
{
if(Usart_Slow_Down) return;//控制刷新频率
Usart_Slow_Down = 1;
if(Uart_Buf_Index != 0)//表示接收到了数据
{
Uart_Buf_Index = 0;
sprintf(seg_string,"%s",Uart_Buf);
Usart_Send_String(seg_string);
}
}
我们可以在串口助手中输入数据,让数码管显示。