ARM嵌入式学习(三) --- 入门51(串口)
一、UART的基本概念
1.1 什么是UART?
UART(Unervisal Async Recveiver Transmitter)是一种全双工、异步、串行通信协议。它只需要两根信号线:
-
TXD(Transmit Data):发送数据线
-
RXD(Receive Data):接收数据线
-

通信双方通过TXD和RXD交叉连接(即一方的TXD接另一方的RXD),实现数据的双向传输。
1.2 通信方式
单工:通信时,数据接收方和发送方是固定的,通过一根信号线实现数据收发
数据传输的方向是单一的(只能由发送方发给接收方)
半双工:通信时,数据接收方和发送方不是固定的(双方都可以发送或接收数据)
通过一根信号线实现数据收发,数据传输的方向可以是双向的(同一时刻,数据
传输的方向是单一的)例如:I2C:SCL 时钟线 SDA 数据线
全双工:通信时,数据接收方和发送方不是固定的(双方都可以发送或接收数据)
通过两根信号线实现数据收发,数据传输的方向是双向的
例如:UART:串口发送数据时,遵循LSB 低位先行原则,先发送数据低位,再发送高位
SPI:MOSI(Master output Slaver Input) MISO(Master Input Slaver Output) CS 片选线 SCLK 时钟线(四个共同组成SPI通信)
1.3 与CH340的关系
CH340(电平转换芯片)是一款常用的USB转串口芯片,它可以将电脑的USB信号转换为TTL电平的串口信号,从而实现电脑与单片机之间的串口通信。
二、串行通信 vs 并行通信
| 特性 | 串行通信 | 并行通信 |
|---|---|---|
| 传输方式 | 一根信号线逐位传输 | 多根信号线同时传输多位 |
| 传输速度 | 相对较低 | 高 |
| 成本 | 低,硬件简单 | 高,需要多根线 |
| 抗干扰 | 较强(尤其是差分传输) | 较差,容易受电磁干扰 |
| 传输距离 | 远(如RS485可达千米) | 近(通常不超过30米) |
三、同步通信 vs 异步通信
-
同步通信:需要单独的时钟线(如SCLK)来同步数据,收发双方按照时钟节拍传输。优点是传输效率高,缺点是需要额外的时钟线。
-
异步通信:不需要时钟线,但双方必须约定相同的波特率(即每秒传输的位数),以此实现位同步。UART就是典型的异步通信。
四、串口通信参数:
串口通信需要双方约定好以下参数,通常以类似“9600 8 N 1”的格式表示:
-
波特率:每秒传输的位数,单位bps。常见值:2400、4800、9600、115200等。
-
数据位:一帧中实际数据的位数,通常为8位(也可为5/6/7位)。
-
校验位:用于简单的错误检测,可选:
-
N(None):无校验
-
O(Odd):奇校验,奇校验值为'1',若数据位中'1'的个数加上奇校验中的'1',保持'1'的总个数为奇数个,则代表奇校验通过
-
E(Even):偶校验,偶校验值为'0',若数据位中'1’的个数加上偶校验中的'0',保持'1‘的总个数为偶数个,则代表偶校验通过
-
-
停止位:一帧结束的标志,通常为1位(也可为1.5或2位)。
示例:9600 8 N 1 表示波特率9600,数据位8位,无校验,1位停止位。
9600波特率一秒传 9600/1+8+1(起始位+数据位+停止位) = 960个字节。
五、常用电平标准
在实际应用中,根据通信距离和环境干扰,需要选择合适的电平标准。
| 标准 | 电平定义 | 特点 | 应用场景 |
|---|---|---|---|
| TTL | 高电平:5V(或3.3V) 低电平:0V |
直接用于单片机引脚,传输距离短 | 板级通信,如单片机与传感器 |
| RS232 | 逻辑1:-3V ~ -15V 逻辑0:+3V ~ +15V |
负逻辑,抗干扰能力较强 | 早期电脑串口,点对点通信 |
| RS485 | 差分信号:A-B > +200mV 为1 A-B < -200mV 为0 |
差分传输,抗共模干扰强,支持多机 | 工业现场,远距离(上千米) |
注意:单片机通常输出TTL电平,如需与RS232设备通信,需使用MAX232等电平转换芯片;与RS485通信则需使用MAX485等转换芯片。
六、数据校验方式
为了保证数据传输的可靠性,除了硬件校验位外,软件层面也常用以下校验方式:
6.1 奇偶校验(Parity Check)
-
奇校验:保证一帧数据(包括校验位)中1的个数为奇数。若原始数据中1的个数为奇数,则校验位置0;若为偶数,则校验位置1。
-
偶校验:保证1的个数为偶数。
-
局限:只能检测奇数个位错误,无法检测偶数个位错误,也无法纠正错误。
6.2 累加和校验(Checksum)
发送方将数据字节累加,取低8位作为校验和附在数据末尾;接收方同样累加,比对校验和。简单但冲突概率较高。
6.3 CRC校验(Cyclic Redundancy Check)
循环冗余校验,通过多项式除法计算校验码,检错能力强,广泛应用于网络和存储。
七、51单片机串口编程基础
7.1 相关寄存器
-
SBUF:串口数据缓冲寄存器,写SBUF即启动发送,读SBUF即获取接收到的数据。
-
SCON:串口控制寄存器,用于设置工作方式、允许接收等。
-
PCON:电源控制寄存器,其SMOD位可控制波特率加倍。
-
TMOD:定时器模式寄存器,用于设置定时器1作为波特率发生器。
-
TH1/TL1:定时器1的初值寄存器,用于产生波特率。
7.2 波特率计算
以定时器1作为波特率发生器为例,常用模式2(8位自动重装)。波特率计算公式为:
波特率 = (2^SMOD / 32) × (定时器1溢出率)
其中定时器1溢出率 = 晶振频率 / (12 × (256 - TH1))
假设晶振11.0592MHz,设置SMOD=0(不加倍),波特率=9600,则:
9600 = (1/32) × (11059200 / (12 × (256 - TH1))) 解得:TH1 = 256 - 11059200 / (12 × 32 × 9600) ≈ 256 - 3 = 253 (0xFD)
若需2400bps,则TH1=232(SMOD=1)。
7.3 定时器1初始化代码示例
// 串口初始化
void uart_init(void)
{
// 1. 配置串口工作模式:SM0 0 SM1 1 8位UART模式
SCON &= ~(3 << 6);
SCON |= (1 << 6); // SM1
SCON &= ~(1 << 7); // SM0
// 2. 允许串口接收数据
SCON |= (1 << 4);
// 3. PCON 波特率加倍
PCON &= ~(3 << 6);
PCON &= ~(1 << 6);
PCON |= (1 << 7);
// 4. 配置定时器1工作模式:8位自动重装载
TMOD &= ~(0x0F << 4);
TMOD |= (1 << 5);
// 5. 设置定时器1初值:2400bps
TL1 = 232;
TH1 = 232;
// 6. 允许定时器1开始计数
TCON |= (1 << 6);
// 7. 允许CPU响应所有中断
IE |= (1 << 7);
// 8. 允许串口产生中断
IE |= (1 << 4);
}
7.4 发送(轮询)与接收(zhon)
-
发送(轮询):将数据写入SBUF,等待发送完成标志TI置1。
// 发送单个字符
void uart_sendchar(char ch)
{
SBUF = ch;
// 判断是否发送完8位数据
while ((SCON & (1 << 1)) == 0); //当T1为0是还在发送,为1是跳出循环即发送完成
SCON &= ~(1 << 1);//手动把T1置为0
}
// 发送字符串
void uart_sendstr(const char *p)
{
while (*p)
{
uart_sendchar(*p++);
}
}
// 发送指定长度数据
void uart_sendbuff(const char *p, int len)
{
while (len--)
{
uart_sendchar(*p++);
}
}
-
接收(中断):当RI置1时,从SBUF读取数据。
// 串口接收中断服务函数
void uart_handler(void) interrupt 4
{
if ((SCON & (1 << 0)) == 1)//接受完成后R1会置为1
{
if (pos < 32)
{
recv_buffer[pos++] = SBUF;
recv_buffer[pos] = 0;
}
}
SCON &= ~(1 << 0);//手动把R1置为0
}
注意:发送或者接收完成之后都要手动把T1或者R1置为0
八、Modbus协议简介
Modbus是一种广泛应用于工业控制的应用层协议,基于主从架构,通常运行在串口上。
示例:

示例中校验码=前面字节的总和
我们假设功能码为:
01:控制led
02:控制数码管
03:控制蜂鸣器
04:温度采集
数据位用来调整该功能的各种设置
解码Modbus协议:
#define DEV_ADDRESS 0x01
// 解析获得功能码
int parse(void)
{
int ret = 0;
int i = 0;
int sum = 0;
if ((unsigned char)recv_buffer[0] == 0xAA && (unsigned char)recv_buffer[6] == 0xBB)
{
if ((unsigned char)recv_buffer[1] == DEV_ADDRESS)
{
for (i = 0; i < 5; i++)
{
sum += recv_buffer[i];
}
if (sum == recv_buffer[5])
{
ret = recv_buffer[2];
}
}
}
return ret;
}
注意:if(sum == recv_buffer[i])这个条件不能使用unsigned char,因为如果相加的数大于两个字节,校验码只有两位如果是无符号会补0就和sum不一样大了
从机应答处理:
#define DEV_ADDRESS 0x01
void callback(void)//从机给主机发信息
{
int i = 0;
int sum = 0;
xdata char send_buffer[10];
memcpy(send_buffer,recv_buffer,7);//AA 01 01 42 00 EE BB
send_buffer[2] |= (1<<7); //0000 0001 |1000 0000 = 0x81
if ((unsigned char)send_buffer[0] == 0xAA && (unsigned char)send_buffer[6] == 0xBB)
{
if ((unsigned char)send_buffer[1] == DEV_ADDRESS)
{
for (i = 0; i < 5; i++)
{
sum += send_buffer[i];
}
send_buffer[5] = sum;
}
}
uart_sendbuff(send_buffer,7);
}
功能码的最高位是数据流向位功能码的最高位是数据流向位,所以应答把功能码最高位,置为1。
解码后通过switch(功能码recv_buffer[2])来实现不同的功能,通过数据位1的值(recv_buffer[3])来设置功能
九、总结
本文从UART的基础概念出发,详细介绍了串行通信的对比、通信参数、电平标准、校验方式,并结合51单片机给出了串口初始化和收发代码,最后简单介绍了Modbus协议。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)