STM32单片机快速入门——串口通讯篇
串口(Serial Port)是一种全双工数据通信接口,广泛应用于嵌入式系统和计算机外设之间的数据交换。它通常用于两台设备之间的通信,并且可以通过简单的线缆连接,极大地方便了设备间的连接和数据传输。串口通信的最大特点是通过单根数据线顺序传输数据,即每次传输一个比特(bit),因此被称为“串行通信”。串口通信是一种高效、简单的点对点通信方式,其设计思想简单,适合低速、长距离的数据传输。通过配置不同的
目录
一.串口简介
串口(Serial Port)是一种全双工数据通信接口,广泛应用于嵌入式系统和计算机外设之间的数据交换。它通常用于两台设备之间的点对点通信,并且可以通过简单的线缆连接,极大地方便了设备间的连接和数据传输。串口通信的最大特点是通过单根数据线顺序传输数据,即每次传输一个比特(bit),因此被称为“串行通信”。
串口通信是一种高效、简单的点对点通信方式,其设计思想简单,适合低速、长距离的数据传输。通过配置不同的通信参数(如波特率、数据位、校验位、停止位等),可以满足多种不同的通信需求。对于嵌入式开发人员来说,掌握串口通信的基本原理和使用方法,对于设备间的交互和调试具有重要意义。尽管如今有更高速的通信方式(如USB、以太网等),串口依然在许多低速、低功耗设备中保持着广泛的应用。
二.串口通讯协议
2.1 串口通信方式
- 同步通信:发送方和接收方共享时钟信号,数据传输是同步进行的,数据帧之间不需要间隔时间。
- 异步通信:数据发送方和接收方之间不共享时钟信号,接收方根据固定的波特率(数据传输速度)来估算数据的时序。每个数据帧之间需要有间隔时间,接收方通过起始位来确定数据的开始。
- USART:支持同步通信,异步通信。
- UART:只支持异步通信。
2.2 串口通信硬件电路
- VCC:供电端,需按照实际要求接入电源。
- GND:接地端。
- TX:数据发送端,接另一模块的RX端。
- RX:数据接收端,接另一模块的TX端。
注意事项:
- 若单片机和其他模块都有单独供电,则可不接VCC。
- 如果只需要单向的数据传输,则可以只接一根数据通信线(RX或TX)。
- TX,RX在两个模块之间交叉连接。
- TX,RX的电平都是相对于GND而言的。
- 当通信双方电平标准不一致时,需要添加电平转换芯片。例:单片机与电脑通信。
以下是STM32中常见的电平标准,在单片机中,最常见的是TTL电平,之后我们都以TTL进行讲解。

2.3 串口参数及时序
2.3.1 串口相关参数
- 波特率:串口通信的速率,常见的波特率有9600 Baud,19200 Baud,115200 Baud等。
- 起始位:标志着一个数据帧的开始,固定为低电平。
- 数据位:低位先行(LSB),数据位的有效载荷,1为高电平,0为低电平,可配置数据位长度(8/9)。通常以8居多。
- 校验位:用于数据验证,分为无校验/奇校验/偶校验。
- 停止位:用于两个数据帧之间的间隔,固定位高电平。长度可配置0.5/1/1.5/2。通常使用1居多。
- 支持同步模式,硬件流控制,DMA,智能卡,lrDA,LIN。
2.3.2 串口通信时序
USART/UART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里,自带波特率发生器,最高达4.5Mbits/s。
2.3.2.1 数据帧

校验位可根据需求进行设置,若不进行设置,可能的奇偶校验位就是有效的数据位之一,用来承载数据,这里推荐的配置为:
- 9位字长:8位数据位 + 1位校验位
- 8位字长:8位数据位,无校验位
2.3.2.2 停止位
可以配置成 0.5/1/1.5/2 位停止位,一般情况下,如无特殊需求,一般配置成1位。

三.串口的基本结构

核心结构
- 发送数据寄存器(TDR) + 发送移位寄存器
通过写操作,将数据写入TDR,写入完成后,硬件就会自动检查发送移位寄存器中是否有数据正在进行移位,有则等待,若没有,则数据就会立刻转入发送移位寄存器,此时会将标志位TXE(发送寄存器空)置1,代表TDR为空,新转入的数据进入准备发送状态。此时发送移位寄存器就会在下面的发送器控制下,将数据输出到TX引脚。这样可以保证连续发送数据的时候,数据帧之间不会有空闲,提高了工作效率。
- 接收数据寄存器(RDR) + 接收移位寄存器
通过RX引脚接收到的数据会进入接收移位寄存器,数据由高位向低位逐渐移位,然后全部保存到接收数据寄存器(RDR),当完成一个字节的移位后,会将标志位RXNE(接收寄存器非空)置1,通过标志位,就可以对数据进行读取处理。
- 硬件数据流控(了解)
什么是硬件数据流控?
如果数据发送的太快来不及接收,就会出现丢弃或数据覆盖现象,当使用流控时,就可以避免这个问题,nRTS为输出引脚,输出低电平0有效,对方的nCTS接收到低电平后,就可以继续发送数据,反之1为无效,就会暂停发送;nCTS为输入引脚,用于接收对方发来的nRTS信号。其中两者名字前的n代表低电平有效。
每一帧帧头的起始位,帧尾的停止位等,硬件电路会自动进行剔除。
波特率发生器

总工作流程示意图(来源:江科大自化协)

四. 代码示例
4.1 串口配置流程

4.2 串口单字节收发(不带校验位)
#include "stm32f10x.h"
// 串口初始化函数
void USART1_Init(void) {
// 使能USART1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 初始化GPIOA的PA9和PA10为USART1的TX和RX
GPIO_InitTypeDef GPIO_InitStruct;
// 配置PA9为USART1的TX (推挽输出)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; // TX
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置PA10为USART1的RX (浮空输入)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; // RX
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置USART1的参数
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600; // 波特率9600
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1个停止位
USART_InitStruct.USART_Parity = USART_Parity_No; // 不使用校验位
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置为收发模式
USART_Init(USART1, &USART_InitStruct);
// 使能USART1,准备开始通信
USART_Cmd(USART1, ENABLE);
}
// 发送一个字节数据
void USART1_SendData(uint8_t data) {
// 等待直到发送数据寄存器为空(TXE标志位清零表示发送缓冲区为空)
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 发送数据
USART_SendData(USART1, data);
}
// 接收一个字节数据
uint8_t USART1_ReceiveData(void) {
// 等待直到接收数据寄存器非空(RXNE标志位置位表示有接收数据)
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
// 返回接收到的数据
return USART_ReceiveData(USART1);
}
int main(void) {
// 初始化USART1
USART1_Init();
uint8_t receivedData;
while (1) {
// 等待接收一个字节的数据
receivedData = USART1_ReceiveData();
// 将接收到的数据发送回去(即回显)
USART1_SendData(receivedData);
}
}
4.3 串口单数据收发(带偶校验位)
#include "stm32f10x.h"
// 串口初始化函数(带校验位)
void USART1_Init(void) {
// 使能USART1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
// 配置PA9为TX(推挽输出)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置PA10为RX(浮空输入)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置USART1(带校验位)
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600; // 波特率9600
USART_InitStruct.USART_WordLength = USART_WordLength_9b; // 9位数据位(包括校验位)
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1个停止位
USART_InitStruct.USART_Parity = USART_Parity_Even; // 偶校验(改为Odd可使用奇校验)
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置为收发模式
USART_Init(USART1, &USART_InitStruct);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
// 发送一个字节(带校验位)
void USART1_SendData(uint8_t data) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待TXE标志位
// 由于使用校验位,需要发送9位数据
USART_SendData(USART1, (uint16_t)data);
}
// 接收一个字节(带校验位)
uint8_t USART1_ReceiveData(void) {
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待RXNE标志位
// 由于数据位是9位,接收时要去掉最高位
return (uint8_t)(USART_ReceiveData(USART1) & 0xFF);
}
int main(void) {
// 初始化USART1(带校验位)
USART1_Init();
uint8_t receivedData;
while (1) {
// 接收数据
receivedData = USART1_ReceiveData();
// 将接收到的数据发送回去(回显)
USART1_SendData(receivedData);
}
}
五. 附录
5.1 ASCII码表

六. 结语
串口多搭配DMA来一起使用,我们下节进行讲解。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)