从数据帧到校验位:深入解析STM32串口通信的物理层与协议层

在嵌入式系统开发中,串口通信是最基础也是最常用的通信方式之一。无论是调试信息输出、设备间数据交换,还是与上位机通信,串口都扮演着不可或缺的角色。STM32系列微控制器内置了强大的USART(通用同步异步收发器)模块,支持全双工异步通信,为开发者提供了灵活且高效的通信解决方案。本文将深入探讨STM32串口通信的物理层特性、协议层机制以及实际应用中的关键配置细节,帮助开发者从理论到实践全面掌握串口通信技术。

1. 物理层:电平标准与硬件连接

串口通信的物理层定义了电气特性和机械特性,确保数据能够在设备间可靠传输。STM32的USART模块通常通过GPIO引脚与外部设备连接,最常用的引脚是TX(发送)和RX(接收)。

电平标准是物理层的核心概念。常见的串口电平标准有TTL和RS-232两种:

  • TTL电平:逻辑1对应3.3V或5V,逻辑0对应0V,直接与微控制器引脚兼容
  • RS-232电平:逻辑1对应-3V~-15V,逻辑0对应+3V~+15V,传输距离更远,抗干扰能力更强

在实际应用中,STM32的USART引脚输出的是TTL电平。如果需要与采用RS-232标准的设备通信,需要添加电平转换芯片如MAX3232。

硬件连接配置示例:

// 使能GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, 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);

注意:STM32F1系列与F4系列在GPIO配置上存在差异。F4系列所有复用功能的引脚都应设置为GPIO_Mode_AF模式,而F1系列需要根据具体功能选择输入或输出模式。

2. 协议层:数据帧结构与传输机制

串口通信的协议层定义了数据包的格式和传输规则。一个完整的串口数据帧由起始位、数据位、校验位和停止位组成。

2.1 数据帧组成

起始位:标志一个数据帧的开始,固定为低电平,持续1个位时间。

数据位:实际传输的数据,可以是5-9位,STM32支持8位或9位数据长度。数据传输采用低位先行(LSB First)的方式。

校验位:用于错误检测,可选奇校验、偶校验或无校验。奇校验确保数据位和校验位中"1"的个数为奇数,偶校验则确保为偶数。

停止位:标志数据帧的结束,固定为高电平,可以是0.5、1、1.5或2个位时间。

下表总结了数据帧各组成部分的特性:

组成部分 电平状态 持续时间 可选参数
起始位 低电平 1位时间 固定
数据位 高/低电平 5-9位时间 8位或9位
校验位 高/低电平 1位时间 奇校验、偶校验、无校验
停止位 高电平 0.5-2位时间 0.5、1、1.5或2位

2.2 波特率与定时

波特率是指每秒传输的符号数,直接决定通信速度。STM32的USART使用小数波特率发生器,能够精确产生各种常用波特率。

波特率计算公式为:

波特率 = fPCLK / (16 * USARTDIV)

其中USARTDIV是一个无符号定点数,整数部分存放在USART_BRR寄存器的高12位,小数部分存放在低4位。

常用波特率配置示例(fPCLK=72MHz时):

目标波特率 USARTDIV计算值 BRR寄存器值
9600 468.75 0x1D4C
115200 39.0625 0x271
230400 19.53125 0x138

3. STM32 USART功能架构与寄存器配置

STM32的USART模块提供了丰富的功能和灵活的配置选项,通过一系列寄存器实现通信控制。

3.1 核心寄存器介绍

USART_SR(状态寄存器):包含传输状态标志,如TXE(发送数据寄存器空)、TC(发送完成)、RXNE(接收数据寄存器非空)等。

USART_DR(数据寄存器):用于读取接收到的数据或写入要发送的数据。实际包含两个寄存器:TDR(发送数据寄存器)和RDR(接收数据寄存器)。

USART_BRR(波特率寄存器):设置USARTDIV值,生成所需的波特率。

USART_CR1(控制寄存器1):配置字长、校验控制、中断使能等。

USART_CR2(控制寄存器2):配置停止位长度等。

3.2 USART初始化配置

完整的USART初始化过程包括以下步骤:

void USART_InitConfig(void)
{
    USART_InitTypeDef USART_InitStructure;
    
    // 波特率设置
    USART_InitStructure.USART_BaudRate = 115200;
    
    // 字长设置:8位或9位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    
    // 停止位设置
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    
    // 校验位设置
    USART_InitStructure.USART_Parity = USART_Parity_No;
    
    // 硬件流控制设置
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    
    // 工作模式设置
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    
    // 应用配置
    USART_Init(USART1, &USART_InitStructure);
    
    // 使能USART
    USART_Cmd(USART1, ENABLE);
}

提示:如果使用硬件流控制(RTS/CTS),需要额外配置相应的GPIO引脚为复用功能,并使能USART_CR3寄存器中的相应控制位。

4. 数据收发机制与实现方式

STM32串口数据收发支持三种方式:查询方式、中断方式和DMA方式,各有其适用场景。

4.1 查询方式

查询方式是最基础的通信方式,通过循环检测状态标志位来实现数据收发。

发送数据函数

void USART_SendByte(uint8_t data)
{
    // 等待发送数据寄存器为空
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    
    // 写入要发送的数据
    USART_SendData(USART1, data);
    
    // 等待发送完成
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}

接收数据函数

uint8_t USART_ReceiveByte(void)
{
    // 等待接收到数据
    while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    
    // 读取接收到的数据
    return USART_ReceiveData(USART1);
}

查询方式实现简单,但效率较低,在等待期间CPU无法执行其他任务。

4.2 中断方式

中断方式允许CPU在等待数据时执行其他任务,当数据传输完成时通过中断通知CPU。

中断配置

// 使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

// 配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

中断服务函数

void USART1_IRQHandler(void)
{
    uint8_t receivedData;
    
    // 检查是否是接收中断
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        // 读取接收到的数据
        receivedData = USART_ReceiveData(USART1);
        
        // 处理数据...
        
        // 清除中断标志
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

4.3 DMA方式

DMA(直接存储器访问)方式可以在不需要CPU干预的情况下实现数据搬运,大大提高了传输效率,特别适合大数据量传输。

DMA发送配置

// 配置DMA通道
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)txBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = bufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);

// 使能USART的DMA发送
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

// 启动DMA传输
DMA_Cmd(DMA1_Channel4, ENABLE);

5. 高级应用与性能优化

在实际项目中,除了基本的收发功能外,还需要考虑一些高级应用场景和性能优化措施。

5.1 不定长数据接收

处理不定长数据是串口通信中的常见需求,STM32提供了多种解决方案:

空闲中断检测:利用串口空闲中断(IDLE)检测一帧数据的结束。当总线空闲时间超过一个数据帧传输时间时,会产生空闲中断。

// 使能空闲中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

// 在中断服务函数中处理空闲中断
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        // 处理接收完成的数据帧
        // ...
        
        // 清除空闲中断标志(通过读USART_SR和USART_DR)
        uint32_t temp = USART1->SR;
        temp = USART1->DR;
        (void)temp;
    }
}

超时检测:使用定时器设置超时时间,如果在规定时间内没有收到新数据,则认为一帧数据接收完成。

5.2 错误检测与处理

串口通信中可能出现的错误包括噪声错误、帧错误、溢出错误等。STM32的USART模块提供了多种错误检测机制:

  • 奇偶校验错误:当接收到的数据与校验位不匹配时产生
  • 帧错误:当检测到停止位为低电平时产生
  • 噪声错误:当在接收过程中检测到噪声时产生
  • 溢出错误:当接收数据寄存器尚未读取又收到新数据时产生

错误处理示例

void USART1_IRQHandler(void)
{
    // 检查溢出错误
    if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET)
    {
        // 处理溢出错误
        // 清除错误标志:先读USART_SR,再读USART_DR
        uint32_t temp = USART1->SR;
        temp = USART1->DR;
        (void)temp;
    }
    
    // 检查其他错误...
}

5.3 性能优化技巧

双缓冲机制:使用两个缓冲区交替进行数据传输和处理,提高吞吐量。

环形缓冲区:实现生产者-消费者模型,解耦数据接收和处理过程。

波特率自适应:在某些应用中,可以实现波特率自动检测和适配,提高系统兼容性。

DMA与中断结合:对于大数据量传输,使用DMA进行数据搬运,结合中断处理传输完成事件。

在实际项目中,我发现最实用的优化是结合DMA和空闲中断实现高效的不定长数据接收。这种方式既能减轻CPU负担,又能灵活处理各种长度的数据帧,特别适合与各种传感器和模块通信的场景。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐