一. 通信协议
1.1 通信协议种类
UART( 异步) 、 I2C( 同步) 、 SPI( 同步) 、 ADC、 GPIO
1.2 通信方式
1.2.1 单工
1、数据的发送方和接收方固定
2、A只发,B只收
3、数据传输是通过一根信号线

1.2.2 半双工
1、收发双方通过一根信号线完成
2、但是数据的发送方和接收方不固定,双方既可以作为数据的发送方也可以作为接收方
数据传输双向
3、在同一时刻,数据传输单向 典型协议:I2C

1.2.3 全双工
1、通信双方既可以发送数据也可以接收数据
2、数据传输通过两根信号线实现,数据传输方向是双向的
3、串口为全双工

1.3 数据传输方式
1.3.1 串行传输
通过一根信号线传输数据,按照先后次序传输
特点: 传输速率慢
硬件成本低,实现简单
传输距离远,抗干扰性强(RS485 使用电压差传输 理论可以传1200米 双绞线)
1.3.2 并行传输
通过多根信号线同时传输数据
特点: 传输速率快
硬件成本高,实现复杂
传输距离近,抗干扰性差(30米以内)
二. 串口
2.1 串口基本知识
2.1.1 串口传输顺序
LSB: 数据最低位
MSB: 数据最高位
串口传输顺序: 串口是LSB优先原则,先发送数据低位
2.1.2 串口的通信时序

时序:
先发送一个bit低电平,代表起始位
再发送8bit数据
发送一个bit的校验位
发送一个bit的电平,代表停止位,表示串口通信结束
2.1.3 校验方式
校验方式:
1、累加和校验
2、奇偶校验
奇校验: 校验位是1,用数据位中bit1的个数加上校验位的1,如果1的个数是奇数个,代表奇校验通过,接收时也是一样
缺点: 若是有两个比特位一起出错则可能检查不出来,无法检测偶数个bit位出错
偶校验: 校验位是0,用数据位中bit1的个数加上校验位的0,如果1的个数是偶数个,代表偶校验通过,接收时也是一样
3、CRC校验
2.1.4 串口波特率
波特率bps:
每秒传输的bit数量
常见波特率:2400,4800,9600,115200
串口不同工作模式下的波特率:

工作模式1下的波特率计算公式(T1用做波特率发生器,工作在自动重装初值模式下):



推导出定时器初值的计算公式:

2.1.5 串口通信参数
eg:9600 8 N 1
波特率+数据位+无校验+停止位1位
若有校验位则:O(Odd)表示:奇校验 E(Even)表示偶校验
2.1.6 串口电平种类
TTL电平: 高电平1:5v 低电平0:0v 使用CH340芯片转换
RS232电平 (负逻辑):高电平1:-3~-15v
低电平0:3~15v
增加了传输距离
使用MAX232芯片转换
RS485电平 (两根线的差分信号):高电平1:2~6v
低电平0:-6~-2v
工业级,传输距离更远
2.2 串口结构

图中有两个物理上独立的接收、发送缓冲器SBUF,它们占用同一地址99H ,可同时发送、接收数据(全双工)。发送缓冲器只能写人,不能读出;接收缓冲器只能读出,不能写入。定时器T1作为串行通信的波特率发生器,T1溢出率先经过2分频(也可以不分频)再经16分频作为串行发送或接收的移位时钟。
接收时是双缓冲结构 ,由于在前一个字节从接收缓冲器SBUF读走之前,已经开始接收第二个字节(串行输入至移位寄存器),若在第二个字节接收完毕而前一个字节仍未被读走时,
就会丢失前一个字节的内容。串行口的发送和接收都是以SBUF的名称进行读或写的,当向SBUF发出"写"命令时,即是向发送缓冲器SBUF装载并开始由TXD引脚向外串行地发送一帧数据,发送完后便使发送中断标志T1=1
当串行口接收中断标志RI=0 时,置允许接收位REN=1就会启动接收过程,一帧数据进人输入移位寄存器,并装载到接收SBUF中,同时使RI=1。执行读SBUF 的命令,则可以由接收缓冲器 SBUF 取出信息,并存于某个指定的位置。
对于发送缓冲器,因为发送时CPU是主动的,所以不会产生重叠错误。
2.3 与串口相关寄存器

2.3.1. SCON串口控制寄存器

|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SM0/FE | 当PCON寄存器中的SMOD0/PCON.6位为1时, 该位用于帧错误检测。当检测到一个 无效停止位时,通过UART接收器设置该位。它必须由软件清零。 当PCON寄存器中的SMOD0/PCON.6位为0时,该位和SM1一起指定串行通信的工作 方式 |
| SM0 SM1 | 00 方式0 同步移位串行方式:移位寄存器 波特率是SYSclk/12, 01 方式1 8位UART,波特率可变
10 方式2 9位UART
11 方式3 9位UART,波特率可变
|
| SM2 | 多机通信控制位,用于方式2和方式3 |
| REN | 允许/禁止串行接收控制位。由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RxD,开始接收信息。 软件复位REN,即REN=0,则禁止接收。 |
| TB8 | 在方式2或方式3,它为要发送的第9位数据,按需要由软件置位或清0。例如,可用作数据的校验位或多机通信中表示地址帧/数据帧的标志位。 |
| RB8 | 在方式2或方式3,是接收到的第9位数据。在方式1,若SM2=0,则RB8是接收到的停止 位。方式0不用RB8。 |
| TI | 发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。 在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。 |
| RI | 接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,**响应中断后必须用软件复位,**即RI=0。 在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明),必须由软件复即RI=0.位, |
2.3.2 PCON 电源控制寄存器

|-----------|---------------------------------------------------------------------------------------------------------------------|
| SMOD | 波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍; SMOD=0,则各工作方式的波特率不变。复位时SMOD=0。 |
| SMOD0 | **帧错误检测有效控制位。**当SMOD0=1,SCON寄存器中的SM0/FE位用于FE(帧错误检测)功能: 当SMOD0=0,SCON寄存器中的SM0/FE位用于SM0功能,和SM1一起指定串行口的工作方式。复位时SMOD0=0 |
2.4 串口配置流程
串口配置流程:
1、与串口相关
串口工作模式+波特率加倍
8位UART,波特率可变:SCON:bit6置1,bit7清零
允许串口接收数据:SCON中bit4置1
PCON中的bit6清零:为了使SCON中的SM1 SM0指定工作方式
PCON的bit7置1,串口波特率翻倍
2、与定时器相关
定时器1工作模式+定时器1计数,定时器1作为串口的波特率发生器
TMOD中的bit5置1,bit4清零,定时器1工作在八位自动重装模式
TH1:230 232(bps= 2400)(注意不同单片机的晶振频率对应不同的初值)
TL1:230 232
TCON的bit6置1,定时器1开始计数
晶振: 12M算定时器为整数
11.0592M算波特率为整数
3、与中断相关
串口中断+CPU中断
将IE中的bit7,bit4置1,打开串口与CPU中断
示例代码:
cpp
/初始化串口
void Uart_init()
{
/波特率加倍
PCON |= (1 << 7);
/允许SCON中的SM0和SM1一起指定串口的工作模式
PCON &= ~(1 << 6);
/配置串口工作模式:8位UART,波特率可变
SCON &= ~(3 << 6);
SCON |= (1 << 6);
/允许串口接收数据
SCON |= (1 << 4);
/定时器1配置工作模式:8位重装
TMOD &= ~(0x0F << 4);
TMOD |= (1 << 5);
/波特率2400对应的初值
TL1 = 232;
TH1 = 232;
/定时器1开始定时
TCON |= (1 << 6);
/开启CPU总中断与定时器1对应的中断
IE |= (1 << 4) | (1 << 7);
}
2.5 串口发送数据
发送数据时,只要向SUBF中写一个字节,就自动启动了发送
**注意:**在发送了一字节的数据后,发送中断请求标志位TI会被自动置位,但是要由软件清零
示例代码:
cpp
/串口发送单字节
void Uart_sendchar(unsigned char ch)
{
SBUF = ch;
while( 0 == (SCON & (1 << 1)) );/还处在发送中状态
SCON &= ~(1 << 1);/软件清零
}
/串口发送字符串
void Uart_sendstr(const char *p)
{
while(*p)
{
Uart_sendchar(*p++);
}
}
/串口发送数组中指定长度数据
void Uart_sendbuffer(const char *p, int len)
{
while(len--)
{
Uart_sendchar(*p);
p++;
}
}
2.6 串口接收数据
串口在接收数据时,由于不知道数据到来的时机,故要使用中断
要使用串口中断函数

注意:在51单片机中RAM只有256字节
在定义数组char buff32时,最好加关键字:xdata
加关键字:xdata
xdata char buff32
就放在了内部扩展的RAM中
内部扩展RAM:0x0000-0xFFFF可以存64字节
示例代码:
cpp
xdata unsigned char recv_buff[32];
unsigned int pos;
/串口接收数据中断服务函数
void Uart_recv_handler(void) interrupt 4
{
/判断接收中断请求标志位 RI 是否被置1
if( SCON & (1 << 0) == 1 )
{
/防止越界
if(pos < 32)
{
/每次接收一字节
recv_buff[pos] = SBUF;
pos++;
/向末尾增加'\0'
recv_buff[pos] = 0;
}
/必须由软件清零此标志位
SCON &= ~(1 << 0);
}
}
三. 主从应答模式
主从应答
应用层:
Modbus协议
上位机/主机:根据协议的格式 发送控制指令,
Modbus协议
eg:
起始码:
0xAA
地址码:
0x01
功能码:
0x01:控制LED灯
02:控制数码管
03:控制蜂鸣器
04:传感器温度采集
数据位1 高位
0x00
数据位2 低位
0x42
校验码
CRC校验
累加和校验
0xEE
结束码
0xBB
从机:根据主机发的数据,解析数据,完成控制 ,完成应答
回复应答也是这种格式去回复
注意:功能码:0x81
功能码最高位表示数据流向位
0:主->从
1:从->主
校验码也变
超过一个字节时保留低位即可