云计算百科
云计算领域专业知识百科平台

江科大笔记—USART串口通信协议与STM32实战配置

1. USART串口通信基础概念

大家好,我是江科大的老学长,今天来聊聊USART串口通信协议和STM32的实战配置。如果你刚开始接触嵌入式开发,串口通信绝对是你第一个要掌握的通信协议,因为它简单实用,几乎所有的微控制器都支持这个功能。

USART的全称是Universal Synchronous/Asynchronous Receiver/Transmitter,中文叫通用同步异步收发器。简单来说,它就是STM32内部的一个硬件模块,能够自动帮我们处理数据的发送和接收。你只需要把数据扔给它的数据寄存器,它就会自动生成对应的数据帧,通过TX引脚发送出去;同样地,当RX引脚收到数据时,它也会自动解析数据帧,把数据存到寄存器里等你来取。

在实际项目中,我经常用串口来做调试输出。比如当程序运行到某个关键点时,通过串口发送一些状态信息到电脑上,这样就能很直观地知道程序运行到哪里了。另外,很多传感器模块,比如温湿度传感器、陀螺仪等,也常常通过串口来传输数据。所以掌握好串口通信,对你后续的嵌入式开发会有很大帮助。

2. USART通信协议详解

2.1 数据帧结构

串口通信的数据是以帧为单位进行传输的。每个数据帧包含起始位、数据位、可选的校验位和停止位。这种结构确保了即使在没有时钟信号的情况下,接收方也能正确解析数据。

起始位总是低电平,它标志着一帧数据的开始。当通信线路处于空闲状态时,引脚保持高电平。起始位的低电平产生一个下降沿,告诉接收设备:"注意,数据要开始传输了!"这个设计很巧妙,我在实际项目中就遇到过因为没有正确配置起始位而导致通信失败的情况。

数据位是实际要传输的有效数据,通常是8位(一个字节),但也可以是5到9位。STM32的USART支持可配置的数据位长度,这在某些特殊应用中很有用。需要注意的是,数据位的传输是低位先行的,也就是说,最低位(LSB)先发送,最高位(MSB)最后发送。

校验位用于错误检测,常见的有奇校验和偶校验。奇校验要求数据位和校验位中"1"的个数为奇数,偶校验则要求为偶数。虽然奇偶校验的检错能力有限,但对于一般的应用来说已经足够了。我在实际项目中通常使用无校验,因为大多数情况下通信环境还是比较可靠的。

停止位标志着一帧数据的结束,必须是高电平。停止位不仅用于帧间隔,还为下一次的起始位做准备——它确保线路恢复到高电平状态,这样下一次起始位才能产生下降沿。

2.2 波特率与同步机制

波特率是串口通信中最重要的参数之一,它决定了数据传输的速度。波特率表示每秒传输的码元数量,单位是波特(Baud)。在二进制调制下,一个码元就是一个比特,所以波特率就等于比特率。

如果发送方和接收方的波特率设置不一致,就会出现数据错误。比如发送方以9600bps的速率发送数据,而接收方以19200bps的速率接收,那么接收到的数据就完全是乱码。我在初学时就犯过这个错误,调试了好久才发现是波特率不匹配的问题。

STM32的USART使用分数波特率发生器来产生精确的波特率。计算公式是:USARTDIV = fck / (16 * Baudrate),其中fck是USART的时钟频率。STM32的USART1挂载在APB2总线上,而USART2、USART3等挂载在APB1总线上,所以它们的时钟源可能不同,在配置时需要注意。

2.3 工作模式与电平标准

USART支持全双工通信,也就是说可以同时进行发送和接收。这是因为有独立的TX和RX线路,发送和接收互不干扰。相比之下,I2C和SPI等协议在某些情况下只能是半双工。

串口通信的电平标准主要有TTL电平和RS-232电平。TTL电平用0V表示逻辑0,3.3V或5V表示逻辑1;而RS-232用电平用-3V到-15V表示逻辑1,+3V到+15V表示逻辑0。现在大多数单片机都使用TTL电平,如果需要和RS-232设备通信,就需要使用MAX232之类的电平转换芯片。

在实际连接时,一定要记住TX接RX,RX接TX!这个看似简单的规则,我却见过不少初学者接反。如果两个设备都有独立供电,VCC可以不接;但如果其中一个设备需要供电,就要连接VCC线。接地线(GND)则是必须连接的,这确保了双方有共同的参考地电位。

3. STM32的USART外设配置

3.1 硬件电路连接

我们先来看看STM32与外部设备的硬件连接方式。最常用的场景是STM32与电脑通信,这通常需要一个USB转串口模块,比如CH340G或者CP2102。这些模块一边是USB接口,可以插在电脑上,另一边是串口引脚(TX、RX、GND),与STM32的串口引脚相连。

在连接时,STM32的TX引脚要接模块的RX引脚,STM32的RX引脚要接模块的TX引脚,GND对接GND。如果模块需要供电,还要连接VCC线。我曾经遇到过因为忘记接GND而导致通信不稳定的情况,所以特别提醒大家:GND一定要接!

对于STM32F103系列,USART1的默认引脚是PA9(TX)和PA10(RX),但也可以通过重映射功能切换到其他引脚。其他USART的引脚位置需要查阅具体芯片的数据手册。在设计PCB时,我建议在TX和RX线上预留串联电阻的位置,这样可以在信号完整性有问题时进行调整。

3.2 GPIO初始化设置

配置USART的第一步是初始化相关的GPIO引脚。由于USART引脚是复用功能,所以需要将GPIO设置为复用推挽输出(TX)和浮空输入或上拉输入(RX)。

以下是GPIO初始化的代码示例:

// 使能GPIO时钟和USART时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
// 配置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);

这里有几个需要注意的地方:一是要记得使能GPIO端口的时钟和USART的时钟;二是TX引脚要设置为复用推挽输出,而不是普通的推挽输出;三是RX引脚建议设置为浮空输入或上拉输入,而不是下拉输入。

3.3 USART参数配置

接下来是配置USART的工作参数,包括波特率、数据位长度、停止位、校验位和硬件流控制等。STM32的标准外设库提供了USART_InitTypeDef结构体来简化配置过程。

USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位数据
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 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_Cmd(USART1, ENABLE); // 使能USART

波特率的选择需要根据实际应用场景来决定。115200bps是最常用的速率,但对于长距离通信或噪声环境,可能需要降低波特率以提高可靠性。我曾经在一个工业项目中,因为电磁干扰太强,不得不将波特率从115200降到9600才能稳定通信。

数据位长度通常选择8位,这与一个字节的长度一致。如果需要奇偶校验,可以选择9位数据长度(8位数据+1位校验位)。停止位通常选择1位,但在某些特殊应用中可能需要选择1.5位或2位。

4. 中断配置与数据处理

4.1 NVIC中断配置

为了高效处理串口数据,我们通常使用中断方式而不是轮询方式。STM32的NVIC(Nested Vectored Interrupt Controller)提供了灵活的中断优先级管理机制。

首先需要配置USART的中断类型。最常用的是接收中断(USART_IT_RXNE),当接收寄存器非空时触发中断。此外,还可以使能发送完成中断(USART_IT_TC)或发送寄存器空中断(USART_IT_TXE)。

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

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

在配置中断优先级时,需要根据系统中其他中断的重要性来合理分配优先级。如果串口通信的实时性要求不高,可以设置较低的优先级,避免影响更重要的任务。

4.2 中断服务函数编写

中断服务函数是处理串口数据的核心。当触发中断时,程序会跳转到中断服务函数执行相应的处理逻辑。

void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
// 读取接收到的数据
uint8_t receivedData = USART_ReceiveData(USART1);

// 处理数据,比如存入缓冲区
// …

// 可选:回显数据
USART_SendData(USART1, receivedData);
}

// 清除中断标志位
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}

在中断服务函数中,首先要检查是哪种中断触发的,然后进行相应的处理。处理完成后,一定要清除中断标志位,否则会连续进入中断。

对于数据接收,我建议使用环形缓冲区(ring buffer)来存储数据。因为中断可能在任何时候发生,而主程序可能正在处理其他任务。使用缓冲区可以解耦数据接收和数据处理,提高系统的可靠性。

4.3 数据发送与接收

数据发送相对简单,只需要将数据写入USART的数据寄存器(DR)即可。但需要注意等待发送完成或发送寄存器为空,否则可能会覆盖之前的数据。

void USART_SendByte(USART_TypeDef* USARTx, uint8_t data)
{
USART_SendData(USARTx, data);
// 等待发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}

void USART_SendString(USART_TypeDef* USARTx, const char* str)
{
while(*str)
{
USART_SendByte(USARTx, *str++);
}
}

对于大量数据的发送,建议使用DMA(Direct Memory Access)方式,这样可以解放CPU,提高系统效率。STM32的USART支持DMA传输,可以通过配置实现自动的数据发送和接收。

在数据接收方面,除了中断方式,还可以使用轮询方式。但轮询方式会占用CPU资源,一般只用于简单的应用或调试目的。

5. 实战应用与调试技巧

5.1 常用调试工具与方法

串口调试最常用的工具是串口调试助手,如SecureCRT、Putty、或者各种厂家提供的调试工具。这些工具可以显示接收到的数据,也可以发送数据到单片机。

在调试时,我习惯首先测试回环(loopback)功能:将STM32的TX和RX引脚短接,然后发送数据,看是否能正确接收。这样可以排除外部电路的问题,确认STM32本身的串口功能正常。

如果通信不正常,可以先用示波器或逻辑分析仪检查波形。正常的串口波形应该有清晰的起始位、数据位和停止位。通过测量位时间,可以验证波特率是否正确。我曾经就用这种方法发现过时钟配置错误导致的波特率偏差问题。

5.2 常见问题与解决方案

在串口通信中,最常见的问题是数据乱码,这通常是波特率不匹配导致的。确保发送和接收方的波特率、数据位、停止位和校验位设置完全一致。

另一个常见问题是数据丢失,特别是在高速传输时。这可能是由于中断优先级设置不合理,导致未能及时处理接收到的数据。解决方法包括优化中断优先级、使用DMA传输、或者增加硬件缓冲区。

电磁干扰也是导致通信错误的重要原因。在工业环境中,可以使用屏蔽线缆、增加滤波电容、或者降低波特率来提高抗干扰能力。我在一个项目中就曾因为电机干扰导致串口通信错误,后来在线上加了磁环才解决问题。

5.3 性能优化建议

对于需要高速传输的应用,可以考虑以下优化措施:使用更高的系统时钟以提高波特率精度;使用DMA减少CPU开销;使用硬件流控制(RTS/CTS)避免数据丢失。

对于低功耗应用,可以合理管理USART的时钟开关,在不需要通信时关闭时钟以节省功耗。STM32还支持在停止模式下通过串口唤醒,这对于电池供电的设备很有用。

代码优化方面,避免在中断服务函数中执行耗时操作;使用内存池管理数据缓冲区;对于频繁发送的数据,可以预先格式化好存储在缓冲区中。

在实际项目中,我还会添加通信协议层,比如简单的帧头帧尾校验,或者更复杂的Modbus协议。这样可以提高通信的可靠性,也便于与其他设备对接。

记得在正式发布程序时,移除或禁用调试输出代码,以减少不必要的开销和提高安全性。可以通过宏定义来控制调试输出的开关,这样在需要时可以快速启用调试功能。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 江科大笔记—USART串口通信协议与STM32实战配置
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!