
串行接口(Serial Port),简称串口,是一种异步串行通信接口。它通过单一数据线将数据逐位顺序传输,实现设备间的数据交换。
想象一下两个说不同语言的人如何交流?他们需要一个翻译官。在电子世界里,串口(Serial Port) 就扮演着这个角色!
如同两人通过声波交流需要遵循共同的语言规则一样,设备间通信也需要统一的"语言规则"——这就是通信协议。串口就是这种规范化的通信方式,解决了原始IO控制中时序、同步、容错等复杂问题。
解决三大头疼问题:
定义:数据在单条数据线上按时间顺序逐位传输
通俗解释:数据像单车送货,一次只送一件,按顺序送达
特点:
-特点:

定义:使用多条数据线同时传输多个数据位
通俗解释:数据像货车车队,多辆车同时出发送货
特点:

特性 | 串行通信 | 并行通信 |
|---|---|---|
传输线数量 | 少(1-3条) | 多(8条以上) |
传输速度 | 相对较慢 | 相对较快 |
成本 | 低 | 高 |
抗干扰能力 | 强 | 弱 |
适用距离 | 远距离 | 短距离 |
控制复杂度 | 较高 | 较低 |
现阶段绝大部分的通讯口都使用串口。



定义:单位时间内传输的二进制位数,决定通信速度
常见语速:
计算示例:

一个完整的数据帧包含:
[起始位] + [数据位] + [校验位] + [停止位]起始位:1位低电平,标志传输开始
数据位:5~9位实际数据,通常8位(1字节)
校验位:1位,用于错误检测
停止位:1~2位高电平,标志传输结束



LSB(Least Significant Bit)则是低地址存放最低有效字节 MSB(Most Significant Bit)是指低地址存放最高有效字节
例子1:通过串口发送十进制数字 27
二进制:00011011
传输顺序:起始位(0) + 11011000(数据位,LSB优先) + 校验位 + 停止位(1)例子2:通过串口发送字符串 “hello”
ASCII编码:
h → 01101000 → 起始位 + 00010110 + 校验 + 停止位
e → 01100101 → 起始位 + 10100110 + 校验 + 停止位
l → 01101100 → 起始位 + 00110110 + 校验 + 停止位
l → 01101100 → 起始位 + 00110110 + 校验 + 停止位
o → 01101111 → 起始位 + 11110110 + 校验 + 停止位功能:用于数据收发的调试和监控

功能:实现USB接口到TTL电平串口的转换

功能:用于信号波形的捕获和分析


STM32单片机内置多个USART(Universal Synchronous/Asynchronous Receiver/Transmitter)模块,支持全双工异步通信。


USART = 嘴巴 + 耳朵,既能说又能听!
USART模块的初始化 → 配置波特率 + 数据帧格式
//USART1 波特率115200、8位数据位、无校验、1位停止位
#include "stm32f10x.h"
int main(void)
{
// 对 USART1 初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 开启时钟
// 配置相关参数 -> 配置波特率 + 数据帧格式
USART_InitTypeDef USART_InitStruct = {0};
USART_InitStruct.USART_BaudRate = 115200; // 波特率 115200
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Tx; // 全双工
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位 数据位
USART_InitStruct.USART_Parity = USART_Parity_No; // 无校验
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位 停止位
USART_Init(USART1, &USART_InitStruct); // 完成初始化
// 使能 USART1
USART_Cmd(USART1, ENABLE);
}这一步就相当于:


查看那些IO可以作为嘴巴或耳朵

赋予嘴巴、耳朵基本的说、听机能
// 默认PA9、PA10引脚配置
void USART1_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置TX引脚(PA9)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 高速
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置RX引脚(PA10)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// 重映射到PB6、PB7引脚配置
void USART1_GPIO_Remap_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOB和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 使能USART1重映射
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
// 配置TX引脚(PB6)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置RX引脚(PB7)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}空闲状态:
开始传输:
数据传输:
结束传输:
传输能力:

我们可以通过这些标志位的值获取USART的工作状态
我们可以把它看作说话的进度条

FlagStatus
作用:查询USART标志位的值,返回值:RESET-0;SET-1
// 查询USART标志位的值
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
// 返回值:RESET-0;SET-1USART_SendData
作用:把要发送的数据写入到发送数据寄存器里
// 发送数据到数据寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
// 参数为uint16_t:为了支持9位数据位/**
* @brief 使用串口发送多个字节
*
* @param USARTx 指定发送串口
* @param pData 要发送的数据
* @param Size 要发送的字节的数
*/
void USART_Send_Bytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
for(uint32_t i = 0; i < Size; i++)
{
// #1. 等待发送数据寄存器空
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
// #2. 将要发送的数据写入到发送数据寄存器
USART_SendData(USARTx, pData[i]);
}
// #3. 等待数据发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
1.生成格式化字符串

2.通过fputc发送到控制台

#include <stdio.h>
// 重定向printf到串口
int fputc(int ch, FILE *f)
{
// #3. 等待发送寄存器为空
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// #2. 发送字符
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
// 重定向scanf从串口输入(可选)
int fgetc(FILE *f)
{
// #1. 等待接收到数据
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
// #2. 返回接收到的字符
return (int)USART_ReceiveData(USART1);
}重写fputs函数其实就是给fputs函数输出重定向,使其发送目标由控制台变为串口
#include <stdio.h>
#include <time.h>
// 格式化输出时间信息
void Print_TimeInfo(void)
{
// 获取系统运行时间(需要自己实现计时功能)
uint32_t system_time = GetSystemTick();
uint32_t hours = system_time / 3600000;
uint32_t minutes = (system_time % 3600000) / 60000;
uint32_t seconds = (system_time % 60000) / 1000;
uint32_t milliseconds = system_time % 1000;
printf("系统运行时间: %02lu:%02lu:%02lu.%03lu\r\n",
hours, minutes, seconds, milliseconds);
}可以把他当作听力的状态
USART_ReceiveData
作用:从接收数据寄存器读取数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
// 作用:从接收数据寄存器读取数据
// 返回uint16_t:为了支持9位数据位PE:Parity Error - 奇偶校验错 如果接收到的数据有校验错误,则PE = 1;否则 PE = 0
FE:Frame Error - 帧格式错误 接收到了无效的数据帧,则FE = 1;否则FE = 0
NE:Noise Error - 噪声错 接收的数据中检测到了噪声,则NE = 1;否则NE = 0
ORE:Overrun Error - 过载错 由于过载造成了数据丢失,则ORE = 1;否则ORE = 0