单片机可以用其带有的串口与另一单片机、计算机、其它硬件模块等通信
(图源:B站江协科技视频截图)
TXD(Transmit Exchange Data)
RXD(receive external data)
交叉连接是因为TXD->RXD,延长线不需要交叉
如果两个设备各自都有供电,可以不接VCC
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL(Transistor-Transistor Logic晶体管晶体管逻辑电路,就是单片机使用的电平)电平:+5V表示1,0V表示0(单片机用的)
RS232电平:-3-15V表示1,+3+15V表示0(一般用在电脑等高电压的传输)
RS485电平:两线压差+2+6V表示1,-2-6V表示0(差分信号)
RS485的两线压差是指两根线之前的电压差,而不是对地(传输距离远,更稳定,不使用差分信号的在10m之后就容易产生错误)
名称 | 引脚定义 | 通信方式 | 特点 | 常见用途 |
---|---|---|---|---|
UART | TXD、RXD | 全双工、异步 | 点对点通信 | 51单片机所用通信方式 |
I²C | SCL、SDA | 半双工、同步 | 可挂载多个设备 | 单片机开发板上的24C02用于存储数据,单片机通过I²C的通信接口实现读取和写入 |
SPI | SCLK、MOSI、MISO、CS | 全双工、同步 | 可挂载多个设备 | DS1302等用到 |
1-Wire | DQ | 半双工、异步 | 可挂载多个设备 | 温度传感器等用到 |
CAN、USB等 | CAN常用于汽车等,连接多个传感器,使用差分信号 |
方式 | 特点 |
---|---|
全双工 | 通信双方可以在同一时刻互相传输数据(有两数据线相互连接) |
半双工 | 通信双方可以互相传输数据,但必须分时复用一根数据线(只有一根线) |
单工 | 通信只能由一方发送到另一方,不能反向传输 |
同步 | 通信双方通信速率相同 |
异步 | 通信双方各自约定通信速率 |
通信速率:相当于在读取高低电平时采样的速率,速率正确才能接收到正确数据
同步:通信双方靠一根时钟线(比如上面提到的SCL、SCLK)来约定通信速率(不会导致收发错误)
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
(eg:I²C总线、SPI总线、1-Wire总线(满足总线的特点))
STC89C52RC有1个UART
有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变
(图源:普中科技开发手册)
串口通信的接口和P30P31复用,操作P3的寄存器时则为IO,操作串口寄存器即为串口,不用单独区分
(STC89C52RC开发板上的USB转TTL下载模块)
波特率:串口通信的速率(发送和接收各数据位的间隔时间)
波特率(Baud Rate)是指在串行通信中,单位时间内传输的符号(symbol)数量。它用于衡量串行数据传输速率或通信信号的变化速度。波特率通常以每秒传输的波特数来表示,单位为波特(baud)或者波特/秒(baud/s)。
在串行通信中,每个符号可以代表一个或多个比特(bits)。因此,波特率并不直接等同于比特率。比特率是指传输的比特数量,而波特率是指传输的符号数量。如果每个符号表示一个比特,则波特率和比特率相等;如果每个符号表示多个比特,则波特率高于比特率。
比特率与波特率_百度百科 (baidu.com)
检验位:用于数据验证(9位数据格式的最后一位,可用于奇偶校验等)
停止位:用于数据帧间隔
(图源:B站江协科技视频截图)
(图源:B站江协科技视频截图)
数据通过总线传输,波特率由定时器T1控制,即在使用串口时需要配置定时器(经过2分频加16分频或者直接16分频)
发送时,先把8位的数据写入缓存,然后再发出去
接收时,先将数据接收到移位寄存器,然后移到缓存中
TI和RI表示发送中断和接收中断
(图源:普中科技开发手册)
TI和RI用的是同一个中断通道,通过一个或门
(图源:普中科技开发手册)
注:PCON中只有SMOD和SMOD0是控制串口的,其余都是控制电源的
(图源:普中科技开发手册)
串口用的计时器1是“8位自动重装”模式:把两个计数器分开,一个中断后另一个自动重装(即两个计数器的一个用于计数,另一个用于保存一个固定数据,每次重置即为将保存的数值复制到计数所用计数器),只能计0~255,不用手动复位,可以减少运行时间
11.0592MHz的系统频率是没有误差的,可以选择较高的波特率,12MHZ的系统频率会有误差,波特率越高误差越大
12MHZ的波特率需要加倍,不加倍的话分频后时钟会变慢(时钟分频后可能无法得到准确的波特率)
波特率计算
在以下条件下: @9600bps TL1 = 0xFD;//253 TH1 = 0xFD;
256-253=3(每次溢出需要的脉冲次数)
3*1.085(每个时间周期对应的时间(us)) = 3.255
T1溢出率(每秒溢出的次数)= 1s / 3.255us = 307,219.6620 = 0.3072196620MHz
0.3072196620MHz / 2 / 16 = 0.0096006144375MHz = 9600.61Hz
通过近似计算得到的结果并非整数,以下为通过计算器计算得出的结果
对波特率倍速降低误差率的分析
(不一定对)
当晶振为12MHz时,选择4800bps,在波特率倍速的情况下,其实际波特率为2400bps,计数器初始化为243,计数13为1次溢出
即每13ns溢出一次,溢出率为76,923.0769
4,807.6923076923076923076923076923
此时误差为0.16%
当不使用波特率倍速,计数器初始化为249,计数7次,溢出率为142,857.1428
4,464.285714285714
此时误差为6.99%
由此可知,当系统频率为12MHz时,1秒内的溢出次数(即溢出率)不为整数导致产生误差,而波特率倍速就是将溢出所需时间加长,从而降低每秒溢出的次数,即降低溢出率,通过这种方式可以使溢出率更加接近整数,从而降低误差。
#include
#include "Delay.h"
void UART_inti()//[email protected]
//初始化寄存器
{
SCON = 0x40;
//0100 0000
PCON = 0;
//此处不需要配置中断
//配置定时器1
TMOD &= 0x0F; //TMOD高四位置0
TMOD |= 0x20; //串口需要用“8位自动重装”模式
TL1 = 0xFA; //设置定时初始值
TH1 = 0xFA; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
void UART_SendByte(unsigned int Byte)
//发送数据
{
SBUF = Byte;
//将数据写入SBUF
while(TI == 0);
//当发送未完成,保持在函数内
TI = 0;
//重置TI
}
unsigned char sec = 0;
void main()
{
UART_inti();
while(1)
{
UART_SendByte(sec);
sec++;
Delay(1000);
}
}
选择相应的波特率
HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
文本模式/字符模式:以原始数据编码(ASCII美国信息交换标准代码)后的形式显示
当发送数据的函数直接放在while并且没有任何延时时,打开串口接收的数据可能有错误,复位单片机即可(或者加一个延时,但是这样获得的第一个数据还是错误的)
#include
void Uart1_Init(void) //[email protected]
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器时钟12T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
void UART_SendByte(unsigned int Byte)
//发送数据
{
SBUF = Byte;
//将数据写入SBUF
//这里SBUF不用特地判断,写入时为发送端SBUF,读取时为接收端SBUF
while(TI == 0);
//当发送未完成,保持在函数内
TI = 0;
//重置TI
}
//在初始化函数中添加
EA = 1;//启动所有中断
ES = 1;//启动串口中断
#include
#include "Delay.h"
#include "Uart.h"
unsigned char sec = 0;
void main()
{
Uart_Init();
while(1)
{
}
}
void Uart_Routine() interrupt 4
//出发接收中断后的操作
{
if(RI == 1)
//防止发送中断导致操作进行
{
P2 = SBUF;
//读取数据
Uart_SendByte(SBUF);
//发送接收到的数据
RI = 0;
//清零
}
}