1. 树莓派串口通信基础解析
树莓派的串口通信功能是其与外部设备交互的重要方式之一。作为一名嵌入式开发者,我经常使用UART接口连接各种传感器、模块和其他微控制器。树莓派提供了两种UART硬件:PL011和mini UART,它们各有特点。
PL011 UART是ARM架构的原生UART控制器,性能稳定且功能完整。它支持高达3Mbps的波特率,具有独立的64字节收发FIFO,支持硬件流控(RTS/CTS)。在实际项目中,当需要可靠的高速通信时,PL011是首选。
mini UART则是Broadcom设计的简化版UART,它的波特率与GPU核心频率相关。这意味着当GPU频率变化时(如进行图形密集型任务时),mini UART的波特率会随之漂移,可能导致通信错误。mini UART也没有硬件流控和奇偶校验功能,适合对稳定性要求不高的场景。
注意:树莓派3及更新型号默认将PL011分配给蓝牙模块,而将mini UART分配给GPIO的UART引脚。这是很多初学者容易混淆的地方。
2. 硬件准备与引脚配置
2.1 硬件连接方式
树莓派的UART使用GPIO14(TXD)和GPIO15(RXD)引脚。与PC或其他设备连接时,需要注意电平转换:
直接连接其他3.3V设备时,可以直连:
- 树莓派TXD → 对方RXD
- 树莓派RXD → 对方TXD
- 共地连接必不可少
连接5V设备或PC时需要电平转换:
- 推荐使用MAX3232或CH340G等转换芯片
- 也可以使用分压电阻(1kΩ+2kΩ)进行简易转换
2.2 启用UART接口
在最新版Raspbian系统中,UART默认是禁用的(分配给蓝牙)。启用步骤:
sudo raspi-config选择"Interfacing Options" → "Serial":
- 第一个提示选择"No"(禁用控制台登录)
- 第二个提示选择"Yes"(启用硬件串口)
然后修改/boot/config.txt文件:
sudo nano /boot/config.txt添加或修改以下行:
enable_uart=1 dtoverlay=pi3-miniuart-bt保存后重启系统。
3. Python实现UART通信
3.1 安装pyserial库
pip install pyserial3.2 基础通信示例
import serial import time # 配置串口参数 ser = serial.Serial( port='/dev/serial0', # 使用别名确保兼容性 baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) try: while True: # 发送数据 ser.write(b'Hello from Raspberry Pi\n') # 接收数据 if ser.in_waiting > 0: received_data = ser.readline() print(f"Received: {received_data.decode('utf-8').strip()}") time.sleep(1) except KeyboardInterrupt: print("Program terminated") finally: ser.close()3.3 高级功能实现
- 二进制数据传输:
# 发送二进制数据 data = bytes([0xAA, 0xBB, 0xCC]) ser.write(data) # 接收二进制数据 raw_data = ser.read(3) # 读取3个字节- 使用线程实现全双工通信:
from threading import Thread def receive_thread(): while True: if ser.in_waiting: print(ser.readline().decode()) Thread(target=receive_thread, daemon=True).start()4. C语言实现UART通信
4.1 使用wiringPi库
首先安装wiringPi:
sudo apt-get install wiringpi基础通信代码:
#include <stdio.h> #include <string.h> #include <wiringPi.h> #include <wiringSerial.h> int main() { int serial_port; char buffer[256]; if ((serial_port = serialOpen("/dev/serial0", 115200)) < 0) { fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno)); return 1; } if (wiringPiSetup() == -1) { fprintf(stdout, "Unable to start wiringPi: %s\n", strerror(errno)); return 1; } while (1) { // 发送数据 sprintf(buffer, "Counter: %d\n", millis()/1000); serialPuts(serial_port, buffer); // 接收数据 while (serialDataAvail(serial_port)) { printf("%c", serialGetchar(serial_port)); fflush(stdout); } delay(1000); } return 0; }4.2 使用Linux原生API
对于更底层的控制,可以直接使用termios:
#include <fcntl.h> #include <termios.h> #include <unistd.h> int setup_uart(const char *device) { int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) return -1; struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; tcsetattr(fd, TCSANOW, &options); return fd; }5. 常见问题与解决方案
5.1 波特率不匹配问题
症状:接收到的数据乱码 解决方法:
- 确认双方波特率设置一致
- 检查时钟稳定性(特别是使用mini UART时)
- 在config.txt中添加
core_freq=250固定核心频率
5.2 数据丢失问题
症状:部分数据包丢失 解决方法:
- 实现软件流控(XON/XOFF)
- 减小数据包大小
- 增加接收缓冲区:
ser = serial.Serial(..., rtscts=True) # 启用硬件流控
5.3 权限问题
症状:无法打开/dev/serial0 解决方法:
sudo usermod -a -G dialout pi然后注销重新登录。
6. 性能优化技巧
提高通信可靠性:
- 添加数据校验(CRC或校验和)
- 实现重传机制
- 使用消息帧格式(如[STX][DATA][ETX][CRC])
提高吞吐量:
- 增大缓冲区大小
ser = serial.Serial(..., write_timeout=0, xonxoff=True, rtscts=True, dsrdtr=True)- 使用DMA传输(C语言实现)
多串口扩展:
- 使用USB转串口适配器(如CP2102)
- 通过GPIO模拟软串口(需要精确计时)
在实际项目中,我通常会结合Python的快速开发优势和C语言的高性能特点。例如用Python实现上层协议解析,而用C处理底层的高速数据采集。这种混合编程方式既能保证开发效率,又能满足性能需求。