USART—串口通讯
本文详细介绍了STM32的USART串口通信模块,主要内容包括:1. USART特性:支持全双工异步通信、多种波特率(最高4.5Mbps)、8/9位数据长度、硬件流控、LIN/IrDA/智能卡协议等功能。2. 工作模式:分为异步和同步两种,包含TX/RX引脚配置、硬件流控(RTS/CTS)和同步时钟线(SCLK)的使用方法。3. 数据缓冲机制:采用队列结构进行数据收发,详细比较了查询、中断和DMA
1.串口的简介和特性(部分摘自STM32F10xxx参考手册USART通用同步异步收发器部分)
1.1.USART介绍
通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的 外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽范围的波特率选择。 它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据 组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。 使用多缓冲器配置的DMA方式,可以实现高速数据通信。
1.2.USART主要特性
● 全双工的,异步通信
● NRZ标准格式
● 分数波特率发生器系统
─ 发送和接收共用的可编程波特率,最高达4.5Mbits/s
● 可编程数据字长度(8位或9位)
● 可配置的停止位-支持1或2个停止位
● LIN主发送同步断开符的能力以及LIN从检测断开符的能力
─ 当USART硬件配置成LIN时,生成13位断开符;检测10/11位断开符
● 发送方为同步传输提供时钟
● IRDA SIR 编码器解码器 ─ 在正常模式下支持3/16位的持续时间
● 智能卡模拟功能
─ 智能卡接口支持ISO7816-3标准里定义的异步智能卡协议
─ 智能卡用到的0.5和1.5个停止位
● 单线半双工通信
● 可配置的使用DMA的多缓冲器通信
─ 在SRAM里利用集中式DMA缓冲接收/发送字节
● 单独的发送器和接收器使能位
● 检测标志 ─ 接收缓冲器满
─ 发送缓冲器空
─ 传输结束标志
● 校验控制
─ 发送校验位
─ 对接收数据进行校验
● 四个错误检测标志通用同步异步收发器(USART)
─ 溢出错误
─ 噪音错误
─ 帧错误
─ 校验错误
● 10个带标志的中断源
─ CTS改变
─ LIN断开符检测
─ 发送数据寄存器空
─ 发送完成
─ 接收数据寄存器满
─ 检测到总线为空闲
─ 溢出错误
─ 帧错误
─ 噪音错误
─ 校验错误
● 多处理器通信 -- 如果地址不匹配,则进入静默模式
● 从静默模式中唤醒(通过空闲总线检测或地址标志检测)
● 两种唤醒接收器的方式:地址位(MSB,第9位),总线空闲
1.3.USART的工作模式
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是一种通用串行通信接口,支持同步和异步两种传输模式。其核心功能是通过 TX(发送) 和 RX(接收) 引脚实现数据的串行传输,并可扩展硬件流控和同步时钟线以增强通信可靠性。

1.3.1.TX 与 RX 引脚
- TX(Transmit):数据发送引脚,将并行数据转换为串行数据输出。
- RX(Receive):数据接收引脚,将接收到的串行数据转换为并行数据。
- 异步模式:无需时钟线,依赖预定义的波特率(Baud Rate)实现同步。数据帧通常包括起始位、数据位、校验位和停止位。
1.3.2.硬件流控
硬件流控通过 RTS(Request to Send) 和 CTS(Clear to Send) 引脚协调收发双方的数据流,避免缓冲区溢出:
- RTS:由发送方控制,表示准备好发送数据。
- CTS:由接收方控制,表示准备好接收数据。
- 工作流程:发送方检测 CTS 信号为低电平时才发送数据,接收方通过 RTS 信号暂停或恢复数据流。
1.3.3.同步模式与时钟线
在同步模式下,USART 通过 SCLK(Serial Clock) 引脚提供时钟信号,实现严格时序同步:
- 主从设备:主设备(如 MCU)提供 SCLK,从设备(如传感器)同步时钟收发数据。
- 数据对齐:时钟上升沿或下降沿触发数据采样,确保稳定性。
- 优势:相比异步模式,同步模式支持更高传输速率,但需额外时钟线连接。
1.3.4典型应用场景
- 异步模式:UART 通信(如 MCU 与 PC 串口通信)。
- 同步模式:SPI/I2C 替代方案或高速设备通信。
- 硬件流控:高波特率或长距离通信(如工业 RS-485)。

USART 特性:字长设置

发送器:配置停止位

接收器:起始位侦测

1.4.串口收发顺序就好比“数据结构”里面的队列
【我一开始在学的时候恍惚的认为是栈了(下面是对三者的详细阐述)】(个人理解兼AI回答)
核心区别:栈 (Stack) vs. 队列 (Queue) vs. 串口缓冲区
| 特性 | 数据结构 - 栈 | 数据结构 - 队列 | STM32 串口收发 |
|---|---|---|---|
| 数据存取原则 | 后进先出 | 先进先出 | 先进先出 |
| 操作比喻 | 压栈, 弹栈。像叠盘子,你只能从顶部取。 | 入队, 出队。像单行道排队,先来的人先接受服务。 | 发送:数据按写入顺序依次发出。 接收:数据按到达顺序依次存入缓冲区。 |
| 典型应用 | 函数调用、中断处理、表达式求值 | 消息传递、打印任务调度、网络数据包处理 | USART:异步串行通信 |
结论:串口的数据流更接近于一个“队列”(Queue),而不是“栈”(Stack)。
1.4.1.深入理解STM32串口的“缓冲区”
STM32的串口(USART/UART)硬件本身包含两个关键寄存器,它们本质上是非常小的硬件队列:
-
发送数据寄存器:当你把要发送的数据写入这个寄存器,串口硬件会自动将数据一位一位地通过TX引脚发送出去。
-
接收数据寄存器:当串口从RX引脚一位一位地接收到一个完整的数据(如8位)后,会将数据存入这个寄存器,等待CPU或DMA来读取。
问题来了: 这两个寄存器通常只能存放一个字节(8位)的数据。如果数据来得太快,CPU来不及处理怎么办?
这就是STM32更强大的地方,它通过以下方式扩展了这个“队列”:
1.4.2.使用查询或中断方式
-
CPU主动查询:程序不断检查接收寄存器是否有新数据。这效率低下,且容易丢失数据(如果在新数据到来时,旧数据还没被读走)。
-
中断:这是更常用的方式。
-
发送中断:当发送数据寄存器空了(数据已发出),会产生中断,告诉CPU:“我可以发送下一个数据了”。
-
接收中断:当接收数据寄存器收到新数据,会产生中断,告诉CPU:“快来取数据,不然下一个数据来了可能会覆盖它”。
-
此时,程序员需要在中断服务函数中手动管理一个软件缓冲区(数组)。这个软件缓冲区就是一个在内存中实现的队列。接收中断函数将数据从硬件寄存器“出队”,然后放入软件缓冲区的“队尾”。
-
1.4.3.使用DMA——最像“自动队列”的方式
DMA是解决这个问题的终极武器,它使得串口通信非常接近你想象中的“自动压栈/弹栈”。
-
发送:你只需要把要发送的一串数据(比如一个数组)的首地址和长度告诉DMA和串口。DMA会自动地、无需CPU干预地将数据从内存这个“大队列”中搬运到串口的发送数据寄存器中。CPU此时可以去做其他事情。
-
接收:你提前开辟好一段内存作为接收缓冲区(一个数组),并将其首地址和最大长度告诉DMA和串口。此后,每收到一个字节,DMA都会自动地将其从接收数据寄存器搬运到你指定的内存缓冲区中,并自动更新地址。整个过程CPU完全不用操心。
在这种情况下:
-
你的内存数组就是一个大的“队列”缓冲区。
-
DMA的职责就是自动执行“入队”(接收)和“出队”(发送)操作。
-
CPU只需要在DMA完成传输(或传输一半)时产生中断,然后去处理缓冲区里已经排好队的数据即可。
2.串口的对应操作

2.1.使用串口前必须开启时钟,有了这个时钟初始化才能进行接下来操作
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//对USART1进行时钟初始化,USART1默认GPIO口为PA9和PA10,如果要启用其他IO口进行
//USART1操作,就必须对USART1进行重映像
2.2.在启用时钟后,需开启这部分的“总开关”
void USART_Cmd(USARTTypeDef *USARTx,FunctionalState NewState);
//作用:控制USART模块的使能和禁止(总开关)
//*USARTx串口名称
//FunctionalState NewState:ENABLE-使能;DISABLE-禁止
2.3.然后对串口进行初始化
波特率、数据位长度、停止位长度、校验方式、数据收发方向
void USART_Init(USARTTypeDef *USARTx,//串口名称
USART_InitTypeDef *USART_InistStruct);//初始化的参数
作用:初始化串口
struct USART_InitTypeDef{
uint32_t USART_BaudRate;
// 波特率
uint16_t USART_WordLength;
//数据位长度
- USART_WordLength_8b
- USART_WordLength_9b
uint16_t USART_StopBits;
// 停止位长度
-USART_StopBits_0_5
-USART_StopBits_1
-USART_StopBits_1_5
-USART_StopBits_2
uint16_t USART_Parity;
// 校验方式
- USART_Parity_No
- USART_Parity_Even
- USART_Parity_0dd
uint16_t USART_Mode;
// 数据收发方向
- USART_Mode_Tx
- USART_Mode_Rx
- USART_Mode_Tx | USART_Mode_Rx
}
###初始化USART1 设置波特率115200,数据位8位,停止位1位,无校验###
RCC_APB2PerihClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启时钟
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate =115200;//波特率115200
USART_InitStruct. USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // X
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);
2.4.对GPIO口进行初始化
GPIO_InitTypeDef GPIO_InitStruct;
// Tx PA9 复用输出推挽 GPIoA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct. GPIO_Pin= GPIO_Pin_9; //PA9
GPIO_InitStruct. GPIO_Mode = GPIO_Mode_AF_PP; // 复用输出推挽模式
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;// 最大速度
GPIO_Init(GPIOA, &GPIO_InitStruct);
// Rx PA10 输入浮空 输入上拉
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//输入上拉模式
//由于这里是输入模式,所以不用对Speed进行设置(因为这是从外部输入,而不是从单片机自生引出)
GPIO_Init(GPIOA, &GPIO_InitStruct);
2.5.如果要对串口(如USART1)进行重映射
| 配置项 | USART1_REMAP 值 |
USART1_TX 引脚 | USART1_RX 引脚 | 说明 |
|---|---|---|---|---|
| 默认复用功能 | 0 (或 不重映射) | PA9 | PA10 | 芯片复位后的默认状态,无需开启AFIO时钟的重映射控制。 |
| 重映像功能 | 1 (或 部分重映射) | PB6 | PB7 | 需要开启AFIO时钟,并配置AFIO_MAPR寄存器的USART1_REMAP位为1。 |
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO模块的时钟
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); // USART1_REMAP=1
GPIO_InitTypeDef GPIO_InitStruct;
// Tx PB6 输出推挽
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct. GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;// 复用输出推挽模式
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_10MHz;// 最大速度
GPIO_Init(GPIOB, &GPIO_InitStruct);
// Rx PB7 输入浮空 输入上拉
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct. GPIO_Pin = GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//模式
GPIO_Init(GPIOB, &GPIO_InitStruct);
其中“使能AFIO模块的时钟”是使用AFIO任何功能(如引脚重映射、外部中断配置)之前必须进行的一个基础且必要的步骤。
| 使能AFIO时钟 | 给AFIO模块供电,让它开始工作。 |
| 配置AFIO功能 | 设置重映射、外部中断线等具体功能。 |
2.6.状态标志位
2.6.1TXE(Transmit Data Register Empty)
- 定义:TXE标志位表示发送数据寄存器(TDR)是否为空。当TDR中的数据被传输到移位寄存器(准备发送)时,TXE会被硬件置位。
- 触发条件:TDR中的数据被转移到移位寄存器后自动置位。
- 用途:通常用于检查是否可以写入新数据到TDR。若TXE=1,表示可以发送新数据;若TXE=0,表示当前数据未完全转移,需等待。
- 清除方式:向TDR写入数据后,硬件自动清除TXE标志。
2.6.2.TC(Transmission Complete)
- 定义:TC标志位表示整个发送过程(包括移位寄存器的数据发送)是否完成。当移位寄存器中的数据全部发送完毕且TDR为空时,TC被置位。
- 触发条件:移位寄存器发送完最后一位数据且TDR为空(无新数据待发送)。
- 用途:用于判断一帧数据是否完全发送完毕。例如,在需要严格时序控制的场景(如RS-485切换方向)时,需等待TC=1。
- 清除方式:读取状态寄存器(SR)后写入数据寄存器(DR)或直接通过软件清除。
FlagStatus USART_GetFLagStatus (USARTTypeDef *USARTx,//串口名称 uint16_t USART_FLAG);//要查询的标志位 作用:查询USART标志位的值。返回值:RESET-0; SET-1 标志位: USART_FLAG_TXE // TXE USART_FALG_RXNE // RXNE USART FLAG_TC // TC USART_FLAG_PE // PE if(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == SET){//判断发送数据寄存器是否为空 } if(USART_GetFlagStatus(USART1,USART_FLAG_TC) == SET){//判断数据发送是否完成 }2.7.发数据(1.4里面有对串口收发数据的理解)
void USART_SendData( USARTTypeDef *USARTx,//串口名称 uint16_t Data);//要发送的数据 作用:把要发送的数据写入到发送数据寄存器里 // 发送字节0x01 USART_SendData(USART1, 0x01);//@作用:使用串口一次性发送多个字节 //@参数:pData - 要发送的数据 Size- 字节的数量 void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size) { for(uint32_t i = 0; i < Size; i++){ //#1. 等待发送数据寄存器空 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //#2. 将要发送的数据写入到发送数据寄存器 USART_SendData(USARTx, pData[i]); } //#3. 等待数据发送完成 while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); }2.7.1.fputc函数(要引人C语言的stdio.h头文件)实现当我们printf一段字符生成化字符串然后通过串口发出去
const char *strName = "Tan"; printf("Hi, %s! Nice to meet you! \r\n", strName); //生成格式化字符串 Hi, Tom! Nice to meet you! \r\n //通过fputc发送到控制台 _weak int fputc(int ch, File f){ } //因为单片机用不了控制台,是通过串口形式发送的,重新写fputc int fputc(int ch, File f){ }简单执行操作:
int fputc(int ch, FILE *f){ // #1.等待发送数据寄存器为空 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //#2. 写入发数据寄存器当中 USART_SendData(USART1, (uint8_t)ch); return ch; }###fputc函数的基本功能###
fputc是C标准库中的函数,用于将一个字符写入指定的文件流。其原型为:int fputc(int char, FILE *stream); char:要写入的字符(以int类型传递,实际写入低8位)。stream:目标文件流指针(如stdout、文件流或自定义流)。- 返回值:
- 成功时返回写入的字符(
unsigned char类型)。 - 失败时返回
EOF(通常为-1)。#include <stdio.h> int main() { // 向屏幕写入字符 fputc('A', stdout); // 输出字符 'A' // 向文件写入字符 FILE *file = fopen("example.txt", "w"); if (file != NULL) { fputc('B', file); // 写入字符 'B' 到文件 fclose(file); } return 0; }相关函数
putc:功能与fputc相同,但可能通过宏实现(通常性能更高)。fgetc:从文件流中读取单个字符。fputs:写入字符串到文件流。
- 成功时返回写入的字符(
2.8.收数据
读取数据接口
uint16 t USART_ReteiveData(USARTTypeDef *USARTx);//串口名称
作用:从接收数据寄存器读取数据
同样要等待接收数据寄存器非空
//#1. 等待接收数据寄存器非空_
while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET;
//#2. 接收数据
uint8_t btyeRcvd = USART_ReceiveData(USARTx);
//#3. 处理数据
###对单片机发送数据实现STM32板载LED的亮灭###
板载LED接的是PC13引脚,为开漏输出GPIO_Mode_Out_OD
while(1){
//#1. 等待接收数据寄存器RDR非空
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
//#2. 读取数据
uint8_t byteRcvd = USART_ReceiveData(USART1);
//#3. 对数据进行处理
if(byteRcvd == '0')
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//亮灯
else if(byteRcvd == '1')
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); //灭灯
}
3.帧错误、噪声错误、溢出错误(FE、NE、ORE)
这三个错误都发生在数据接收的过程中,与数据的帧格式 和采样 有关。当这些错误发生时,通常意味着物理线路有干扰、波特率不匹配、或者对方设备发送有问题。
3.1. FE - 帧错误
-
全称: Framing Error
-
触发条件: 当接收器没有在预期的时刻检测到停止位(Stop Bit)时,该标志位会被置起。
-
详细解释:
-
串口协议规定,每个数据帧的结尾必须有一个或多个停止位(通常为1位),其电平为高电平(逻辑1)。
-
接收器在采样完数据位后,会在帧的中间位置采样停止位。如果此时采样到的不是高电平,而是低电平,它就认为这个“帧”的结构不完整或被破坏了,于是产生帧错误。
-
-
常见原因:
-
波特率不匹配:这是最常见的原因。例如,发送方以115200的波特率发送,而接收方设置为9600。接收方会错误地解读比特流,导致它找不到正确的停止位位置。
-
线路干扰:噪声或干扰可能在停止位期间将高电平拉低。
-
发送方驱动能力不足或线路断开/接触不良。
-

3.2. NE - 噪声错误
-
全称: Noise Error
-
触发条件: 在接收数据位的过程中,检测到了电平跳变(噪声)。
-
详细解释:
-
为了抗干扰,STM32的串口接收器对每个数据位会进行多次采样(通常是3次采样,取多数值)。
-
如果在采样期间,检测到了电平的意外跳变(例如,三次采样结果不一致,有的是1,有的是0),硬件就认为这个比特受到了噪声干扰,并置起NE标志。同时,接收到的数据仍然会被存入接收数据寄存器。
-
-
与FE的区别:
-
FE 发生在停止位。
-
NE 发生在数据位(也可能包括校验位)。FE意味着整个帧无效,而NE意味着这个帧的某个数据位可能不可靠,但硬件仍然尽力接收了它。
-
-
常见原因:
-
强烈的电磁干扰。
-
线路接触不良,时通时断。
-
接地问题。
-

3.3. ORE - 溢出错误
-
全称: Overrun Error
-
触发条件: 新的数据已经接收完成,但旧的数据还未从接收数据寄存器中被读取。
-
详细解释:
-
这是最需要重视的错误,因为它直接导致数据丢失。
-
串口的接收数据寄存器只能存放一个字节的数据。当收到一个字节后,该数据会从移位寄存器转移到接收数据寄存器中,并置起标志位(如RXNE)。
-
如果CPU或DMA没有及时读取这个数据,而下一个字节已经接收完毕,那么新数据将无处安放,此时就会发生溢出错误。新数据会被丢弃,ORE标志被置起。
-
-
常见原因:
-
中断响应不及时:主程序忙于处理高优先级任务,导致串口接收中断迟迟得不到响应。
-
没有使用DMA或DMA配置不当:在高速、大数据量通信时,使用查询或普通中断方式很容易导致溢出。使用DMA是避免溢出的最佳方法,因为DMA可以在无CPU干预的情况下将数据直接搬运到指定的大缓冲区中。
-
程序逻辑错误:例如,在中断服务函数中忘了读取数据寄存器。
-

3.4. 奇偶校验错误
3.4.1. 奇偶校验的目的
奇偶校验是一种非常简单的错误检测机制,用于在数据传输过程中检查单个比特是否发生了错误。它在每个数据帧中添加一个额外的位(校验位)。
-
偶校验: 确保数据位 + 校验位中 ‘1’ 的总个数为偶数。
-
奇校验: 确保数据位 + 校验位中 ‘1’ 的总个数为奇数。
举例说明(偶校验):
假设要发送的数据字节是 1100 1010(其中 ‘1’ 的个数是4个,已经是偶数)。
-
发送方(STM32或其他设备)会计算‘1’的个数。因为是偶校验,且‘1’的个数已是偶数,所以校验位设置为 ‘0’。
-
发送方发送完整的帧:数据位
1100 1010+ 校验位0。 -
接收方收到后,计算数据位中‘1’的个数(4个),并查看校验位(0)。
-
接收方验证:4(数据位‘1’的个数) + 0(校验位值) = 4,是偶数。校验通过,不产生PE错误。
3.4.2. 奇偶校验错误(PE)的发生
现在,假设在传输过程中,有一个比特因干扰发生了翻转,比如最高位从‘1’变成了‘0’。接收方收到的数据位变成了 0100 1010。
-
接收方计算收到的数据位中‘1’的个数:
0100 1010中有3个‘1’(是奇数)。 -
接收方看到的校验位仍然是 ‘0’。
-
接收方验证:3(数据位‘1’的个数) + 0(校验位值) = 3,是奇数。
-
但是,我们配置的是偶校验,期望结果是偶数。实际结果与期望不符。
-
此时,硬件就会判定这次传输不可靠,并置起 PE (Parity Error) 标志位。
3.4.3.PE错误的常见原因
-
电气噪声和干扰:这是最主要的原因,导致比特位翻转。
-
波特率不匹配:虽然波特率不匹配更容易引起FE(帧错误),但在某些情况下也可能导致PE。
-
双方配置不一致:一方设置为奇校验,另一方设置为偶校验或无校验。
###总结###
| 错误标志 | 全称 | 触发原因 | 后果 | 关键区别 |
|---|---|---|---|---|
| FE | 帧错误 | 停止位采样不为1 | 当前帧结构错误,数据不可信 | 发生在帧的结尾 |
| NE | 噪声错误 | 数据位采样时检测到电平跳变 | 数据位可能出错,但数据仍被接收 | 发生在数据位/校验位 |
| ORE | 溢出错误 | 新数据到来,旧数据未被读取 | 数据丢失,是最严重的错误 | 与处理速度有关,与数据内容无关 |
| PE | 奇偶校验错误 | 使能校验后,接收到的数据其校验位与计算值不匹配 | 只能检测奇数个比特错误(如1位、3位错误),无法检测偶数个比特错误。开销大(每帧多传一位),在现代高速通信中较少使用,常用于一些简单的工业控制器或老式设备。 | 一种单比特错误检测机制 |
更多推荐




所有评论(0)