树莓派4B与STM32串口通信保姆级教程:从GPIO引脚连接到minicom调试全流程
树莓派4B与STM32串口通信全流程实战指南
引言
嵌入式开发中,串口通信是最基础也最关键的技能之一。作为初学者,你可能已经听说过树莓派和STM32这两个名字——前者是当下最受欢迎的单板计算机,后者则是嵌入式领域广泛使用的微控制器。将它们通过串口连接起来,可以构建出无数有趣的项目:从简单的传感器数据采集,到复杂的机器人控制系统。
但现实往往比理论更骨感。第一次尝试连接树莓派4B和STM32进行串口通信时,我遇到了各种问题:引脚接错、配置混乱、终端无法输入字符...每个坑都让我花费数小时排查。正是这些经历让我意识到,初学者需要的不仅是一份步骤清单,更需要理解每个操作背后的原理,以及遇到问题时如何快速定位和解决。
本文将带你完整走通从硬件连接到软件调试的全流程,重点解决三个核心问题:
- 如何正确连接树莓派4B和STM32的串口引脚?
- 需要修改哪些系统配置才能启用硬件串口?
- 使用minicom进行双向通信时有哪些实用技巧和常见问题解决方法?
1. 硬件连接:引脚定义与物理接线
1.1 认识树莓派4B的串口引脚
树莓派4B提供了两个串口:
- 硬件串口(PL011 UART):稳定可靠,默认分配给蓝牙模块
- 迷你串口(mini UART):功能有限,默认分配给GPIO引脚
我们需要使用的是GPIO14(TXD)和GPIO15(RXD)这两个引脚,它们对应的是迷你串口。以下是关键引脚定义:
| 树莓派引脚 | 功能 | 物理位置 |
|---|---|---|
| GPIO14 | TXD | 第8针脚 |
| GPIO15 | RXD | 第10针脚 |
| GND | 地线 | 第6/9/14等针脚 |
注意:树莓派4B的引脚排列与早期版本有所不同,务必使用4B专用引脚图核对。
1.2 STM32串口引脚配置
以常见的STM32F103C8T6(蓝板)为例,我们需要使用USART1的引脚:
| STM32引脚 | 功能 | 对应信号 |
|---|---|---|
| PA9 | TX | 发送数据 |
| PA10 | RX | 接收数据 |
| GND | 地线 | - |
1.3 实际接线方案
正确的连接方式应该是交叉连接:
- 树莓派TXD(GPIO14) → STM32RX(PA10)
- 树莓派RXD(GPIO15) → STM32TX(PA9)
- 两边的GND相连
常见错误包括:
- 直连(TXD接TXD,RXD接RXD)
- 忘记连接地线(导致信号不稳定)
- 使用错误的GPIO引脚(如误用GPIO0/1)
# 检查当前GPIO引脚状态(接线前确认) gpio readall2. 系统配置:启用硬件串口
2.1 修改config.txt文件
树莓派默认配置不适合直接使用硬件串口,需要修改启动配置:
sudo nano /boot/config.txt在文件末尾添加以下内容:
# 启用硬件串口,禁用蓝牙 dtoverlay=disable-bt # 提高UART时钟稳定性 core_freq_min=500 # 交换串口映射 enable_uart=1保存后重启:
sudo reboot2.2 清理cmdline.txt
这个文件控制内核启动参数,需要移除所有串口相关配置:
sudo nano /boot/cmdline.txt删除所有内容,替换为(保持你的root参数):
console=tty1 root=PARTUUID=6a3d6946-02 rootfstype=ext4 fsck.repair=yes rootwait quiet splash2.3 验证串口状态
重启后检查设备文件:
ls -l /dev/serial*正确配置应该显示:
- /dev/serial0 → ttyAMA0
- /dev/serial1 → (不存在或被禁用)
测试串口回环(短接TXD和RXD):
echo "test" > /dev/ttyAMA0 cat < /dev/ttyAMA03. minicom安装与配置
3.1 安装minicom
sudo apt update sudo apt install minicom3.2 基本配置
首次运行需要进行配置:
sudo minicom -s关键设置项:
- Serial port setup → 设备:/dev/ttyAMA0
- 波特率:115200(与STM32保持一致)
- 硬件流控:No
- 软件流控:No
保存为默认配置(选择"Save setup as dfl")
3.3 常用操作指令
启动minicom:
sudo minicom -D /dev/ttyAMA0在minicom界面中:
- Ctrl+A → Z:显示帮助菜单
- Ctrl+A → O:重新加载配置
- Ctrl+A → X:退出并重置
- Ctrl+A → E:开启本地回显(便于调试)
提示:如果遇到无法输入的情况,检查STM32是否已正确初始化串口,或尝试调整流控设置。
4. STM32端配置与联合调试
4.1 STM32CubeMX配置
使用STM32CubeMX进行USART配置时注意:
- 模式:Asynchronous
- 波特率:115200
- 字长:8bit
- 停止位:1
- 校验位:None
- 硬件流控:Disable
生成代码后,添加测试代码:
// 在main.c中添加 HAL_UART_Transmit(&huart1, (uint8_t*)"STM32 Ready\n", 12, 1000); char rxData; while (1) { if(HAL_UART_Receive(&huart1, &rxData, 1, 100) == HAL_OK) { HAL_UART_Transmit(&huart1, &rxData, 1, 100); // 回显接收到的字符 } }4.2 常见问题排查
问题1:minicom无任何输出
- 检查接线是否正确(TXD-RX交叉)
- 确认STM32已正确供电
- 使用万用表测量TXD/RXD电压(应有3.3V电平)
问题2:收到乱码
- 确认双方波特率一致
- 检查地线连接是否可靠
- 尝试降低波特率(如9600)测试
问题3:能接收但不能发送
- 检查minicom的本地回显是否开启(Ctrl+A → E)
- 确认STM32的中断优先级设置
- 测试短接树莓派TXD/RXD排除硬件问题
4.3 进阶调试技巧
- 使用screen作为替代工具:
sudo apt install screen screen /dev/ttyAMA0 115200- 查看串口调试信息:
dmesg | grep tty- 实时监控串口数据:
sudo cat /proc/tty/driver/serial5. 项目实战:构建双向通信系统
5.1 定义简单通信协议
一个可靠的通信系统需要约定基本协议格式,例如:
| 字段 | 起始符 | 长度 | 命令 | 数据 | 校验 | 结束符 |
|---|---|---|---|---|---|---|
| 示例 | 0xAA | 1字节 | 1字节 | N字节 | 1字节 | 0x55 |
STM32端实现示例:
#define STX 0xAA #define ETX 0x55 uint8_t buffer[32]; uint8_t checksum = 0; void ProcessCommand(uint8_t cmd, uint8_t* data, uint8_t len) { // 命令处理逻辑 } void UART_Handler() { if(HAL_UART_Receive(&huart1, buffer, 1, 10) == HAL_OK) { if(buffer[0] == STX) { HAL_UART_Receive(&huart1, buffer+1, 3, 100); // 接收长度+命令+数据 uint8_t len = buffer[1]; HAL_UART_Receive(&huart1, buffer+4, len+2, 100); // 数据+校验+结束符 if(buffer[4+len+1] == ETX) { // 校验和验证 checksum = 0; for(int i=0; i<len+4; i++) checksum ^= buffer[i]; if(checksum == buffer[4+len]) { ProcessCommand(buffer[2], buffer+3, len); } } } } }5.2 树莓派Python控制脚本
使用Python的serial库可以更方便地实现高级功能:
import serial import time ser = serial.Serial( port='/dev/ttyAMA0', baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) def send_command(cmd, data=None): packet = bytearray() packet.append(0xAA) # STX if data: packet.append(len(data)) packet.append(cmd) packet.extend(data) checksum = 0 for b in packet[1:]: checksum ^= b packet.append(checksum) else: packet.append(0) packet.append(cmd) packet.append(cmd) # 简单校验 packet.append(0x55) # ETX ser.write(packet) while True: if ser.in_waiting > 0: response = ser.read(ser.in_waiting) print("Received:", response.hex()) # 示例:每隔5秒发送心跳包 send_command(0x01) time.sleep(5)5.3 性能优化建议
硬件层面:
- 在信号线上添加100Ω电阻减少反射
- 对长距离传输使用RS-232电平转换芯片
- 为STM32添加硬件去耦电容(0.1μF靠近电源引脚)
软件层面:
- 使用DMA传输减少CPU占用
- 实现双缓冲机制提高吞吐量
- 添加超时和重传机制
// STM32 DMA配置示例 UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_tx; DMA_HandleTypeDef hdma_usart1_rx; void MX_DMA_Init(void) { __HAL_RCC_DMA1_CLK_ENABLE(); hdma_usart1_tx.Instance = DMA1_Channel4; hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_tx.Init.Mode = DMA_NORMAL; hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW; HAL_DMA_Init(&hdma_usart1_tx); __HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx); // 类似配置RX DMA... }