Jetson Nano串口通信保姆级教程:用Python和STM32F4实现数据收发(附完整代码)
2026/4/24 10:27:49 网站建设 项目流程

Jetson Nano与STM32串口通信实战指南:从硬件对接到Python代码优化

在嵌入式开发领域,Jetson Nano作为一款强大的边缘计算设备,经常需要与各种微控制器进行数据交互。而串口通信作为最基础、最可靠的通信方式之一,依然是大多数开发者的首选。本文将带您从零开始,一步步实现Jetson Nano与STM32之间的稳定串口通信,避开那些新手常踩的"坑"。

1. 硬件准备与连接

1.1 所需材料清单

在开始之前,请确保您已准备好以下硬件:

  • Jetson Nano开发板(任何版本均可)
  • STM32开发板(本文以STM32F4为例,但原理适用于其他系列)
  • USB转TTL模块(用于调试,如CH340、CP2102等)
  • 杜邦线若干(建议使用不同颜色区分功能)
  • 万用表(非必须,但强烈推荐用于排查连接问题)

1.2 引脚连接详解

Jetson Nano的40针GPIO接口中包含多个串口,其中最常用的是/dev/ttyTHS1,对应引脚如下:

Jetson Nano引脚STM32引脚信号类型颜色建议
6 (GND)GND地线黑色
8 (UART1_TX)USART2_RX发送绿色
10 (UART1_RX)USART2_TX接收蓝色

注意:连接时务必确保TX接RX,RX接TX,这是串口通信中最常见的错误之一。

1.3 电源考虑

虽然Jetson Nano和STM32可以共用电源,但建议:

  • 对于高功率外设(如摄像头、电机等),使用独立电源供电
  • 确保共地连接(GND相连),这是信号稳定的基础
  • 使用万用表检查各连接点电压,避免短路风险

2. Jetson Nano环境配置

2.1 系统设置与权限

Jetson Nano默认的串口权限可能限制普通用户访问,我们需要进行以下配置:

# 将用户加入dialout组 sudo usermod -a -G dialout $USER # 修改串口设备权限(临时方案) sudo chmod 666 /dev/ttyTHS1 # 永久解决方案:创建udev规则 echo 'KERNEL=="ttyTHS1", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ttyTHS1.rules sudo udevadm control --reload-rules

2.2 Python环境搭建

推荐使用Python 3.6+环境,并安装必要的库:

# 安装pip(如果尚未安装) sudo apt-get install python3-pip # 安装pyserial库 pip3 install pyserial # 可选:安装交互式调试工具 pip3 install ipython

2.3 基础通信测试

创建一个简单的测试脚本serial_test.py

import serial import time def main(): try: ser = serial.Serial( port='/dev/ttyTHS1', baudrate=115200, timeout=1 ) print(f"Serial port {ser.name} opened successfully!") while True: ser.write(b'Hello STM32\n') time.sleep(1) if ser.in_waiting > 0: data = ser.readline().decode('utf-8').strip() print(f"Received: {data}") except Exception as e: print(f"Error: {str(e)}") finally: if 'ser' in locals() and ser.is_open: ser.close() if __name__ == "__main__": main()

运行此脚本前,请确保STM32端已正确配置并运行。如果一切正常,您应该能看到周期性的发送和接收信息。

3. STM32固件开发

3.1 CubeMX基础配置

使用STM32CubeMX进行初始化配置:

  1. 选择正确的STM32型号
  2. 启用USART2:
    • 模式:Asynchronous
    • 波特率:115200
    • 字长:8 Bits
    • 停止位:1
    • 校验:None
  3. 启用USART1(用于调试输出)
  4. 生成代码时启用DMA(可选,提升性能)

3.2 中断接收实现

修改生成的代码,添加中断接收逻辑:

/* Private variables ---------------------------------------------------------*/ uint8_t rxBuffer[256]; uint8_t rxIndex = 0; volatile uint8_t dataReady = 0; /* USER CODE BEGIN 0 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) { if(rxBuffer[rxIndex] != '\n') { // 以换行符作为消息结束标志 rxIndex++; } else { dataReady = 1; rxIndex = 0; } HAL_UART_Receive_IT(&huart2, &rxBuffer[rxIndex], 1); } } /* USER CODE END 0 */

3.3 主循环处理

在main函数中添加数据处理逻辑:

/* Infinite loop */ while (1) { if(dataReady) { // 处理接收到的数据 processReceivedData(rxBuffer, rxIndex); // 通过USART1发送回显(调试用) HAL_UART_Transmit(&huart1, rxBuffer, rxIndex, HAL_MAX_DELAY); // 重置标志位 dataReady = 0; rxIndex = 0; } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }

4. 高级应用与性能优化

4.1 数据协议设计

简单的文本协议虽然易于调试,但在实际项目中,我们通常需要更高效的二进制协议。以下是一个示例帧结构:

字段长度(字节)说明
帧头2固定为0xAA55
数据长度1有效数据长度(最大255)
数据N有效载荷
CRC校验1从帧头到数据的异或校验

Python端解析代码示例:

def parse_frame(data): if len(data) < 4: return None if data[0] != 0xAA or data[1] != 0x55: return None length = data[2] if len(data) < 4 + length: return None payload = data[3:3+length] crc = data[3+length] # 计算校验 calc_crc = 0 for b in data[:3+length]: calc_crc ^= b if calc_crc != crc: return None return payload

4.2 性能优化技巧

Jetson Nano端优化:

  • 使用单独的线程处理串口接收
  • 实现环形缓冲区减少数据丢失风险
  • 考虑使用select模块监控串口活动

STM32端优化:

  • 启用DMA传输减少CPU负载
  • 使用双缓冲技术
  • 合理设置中断优先级

4.3 常见问题排查

当通信出现问题时,可以按照以下步骤排查:

  1. 基础检查

    • 确认连线正确(TX-RX交叉)
    • 检查共地连接
    • 确认波特率等参数一致
  2. 信号质量检查

    • 使用示波器或逻辑分析仪观察信号波形
    • 检查电压电平是否匹配(3.3V对3.3V)
  3. 软件调试

    • 在STM32端添加LED指示灯辅助调试
    • 使用minicomscreen直接测试串口
    • 检查Python脚本的编码设置(特别是中文处理)
# 调试用串口监控命令 # 在终端中直接查看串口输出 screen /dev/ttyTHS1 115200

4.4 实际项目经验分享

在工业环境中使用时,有几个关键点需要注意:

  • 电磁干扰:长距离传输时考虑使用RS485代替TTL
  • 错误恢复:实现自动重连机制
  • 日志记录:保存通信日志便于后期分析
  • 超时处理:设置合理的超时时间,避免线程阻塞

一个健壮的工业级实现可能会包含以下特性:

class RobustSerial: def __init__(self, port, baudrate): self.port = port self.baudrate = baudrate self.connection = None self.reconnect_attempts = 0 self.max_reconnect = 5 def connect(self): try: self.connection = serial.Serial( port=self.port, baudrate=self.baudrate, timeout=1, write_timeout=1 ) self.reconnect_attempts = 0 return True except Exception as e: print(f"Connection failed: {str(e)}") self.reconnect_attempts += 1 if self.reconnect_attempts < self.max_reconnect: time.sleep(1) return self.connect() return False def send_command(self, cmd, retries=3): for attempt in range(retries): try: if not self.connection or not self.connection.is_open: if not self.connect(): continue self.connection.write(cmd) return True except Exception as e: print(f"Send failed (attempt {attempt+1}): {str(e)}") self.connection = None return False

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询