这次尝试在BBB板子上驱动串口通讯。
首先先看看板子的操作系统环境。
root@BeagleBone:~/code# lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 12 (bookworm) Release: 12 Codename: bookworm
第一步:修改/boot/uEnv.txt文件,让系统在启动时加载串口驱动。
#Docs: http://elinux.org/Beagleboard:U-boot_partitioning_layout_2.0 uname_r=5.10.168-ti-r72 #uuid= #dtb= ###U-Boot Overlays### ###Documentation: http://elinux.org/Beagleboard:BeagleBoneBlack_Debian#U-Boot_Overlays ###Master Enable enable_uboot_overlays=1 ### ###Overide capes with eeprom #uboot_overlay_addr0=<file0>.dtbo #uboot_overlay_addr1=<file1>.dtbo #uboot_overlay_addr2=<file2>.dtbo #uboot_overlay_addr3=<file3>.dtbo ### ###Additional custom capes uboot_overlay_addr4=/lib/firmware/ADAFRUIT-SPI0-00A0.dtbo #uboot_overlay_addr4=<file4>.dtbo #uboot_overlay_addr5=<file5>.dtbo #uboot_overlay_addr6=<file6>.dtbo #uboot_overlay_addr7=<file7>.dtbo ### ###Custom Cape #dtb_overlay=<file8>.dtbo ### ###Disable auto loading of virtual capes (emmc/video/wireless/adc) #disable_uboot_overlay_emmc=1 #disable_uboot_overlay_video=1 #disable_uboot_overlay_audio=1 #disable_uboot_overlay_wireless=1 #disable_uboot_overlay_adc=1 ### ###Cape Universal Enable #enable_uboot_cape_universal=1 ### ###Debug: disable uboot autoload of Cape #disable_uboot_overlay_addr0=1 #disable_uboot_overlay_addr1=1 #disable_uboot_overlay_addr2=1 #disable_uboot_overlay_addr3=1 ### ###U-Boot fdt tweaks... (60000 = 384KB) #uboot_fdt_buffer=0x60000 ###U-Boot Overlays### console=ttyS0,115200n8 cmdline=coherent_pool=1M net.ifnames=0 lpj=1990656 rng_core.default_quality=100 quiet #In the event of edid real failures, uncomment this next line: #cmdline=coherent_pool=1M net.ifnames=0 lpj=1990656 rng_core.default_quality=100 quiet video=HDMI-A-1:1024x768@60e #Use an overlayfs on top of a read-only root filesystem: #cmdline=coherent_pool=1M net.ifnames=0 lpj=1990656 rng_core.default_quality=100 quiet overlayroot=tmpfs ##enable Generic eMMC Flasher: #cmdline=init=/usr/sbin/init-beagle-flasher enable_uboot_overlays=1 uboot_overlay_addr0=/lib/firmware/ADAFRUIT-UART1-00A0.dtbo enable_uboot_cape_universal=1
留意最后三行,这部分就是让操作系统在启动时加载串口驱动程序,这样系统启动后就启动了串口1(对应管脚p9_24,p9_26)
手头有一个TOF激光测距传感器:SEN0647。TOF激光测距传感器是一款基于dTOF(直接飞行时间)技术的新一代激光测距传感器。在硬件设计与算法层面进行了全面升级,测距盲区小于5cm,最大量程可达25m(具体视环境反射率而定),测距精度在±3cm以内(典型值),尤其在动态环境下表现稳定,抗环境光干扰能力高达100K LUX,适用于室内外强光环境。支持UART、I2C、I/O等多种通信接口,满足不同开发架构需求。

将TOF激光测距传感器与BBB板子连接。激光测距传感器是5V供电,从板子的P9_2、P9_8取电。串口线与p9_24,p9_26连接。
首先写一个串口检查代码,用来尝试打开串口。
import serial
# 尝试打开 UART1
try:
ser = serial.Serial('/dev/ttyS1', 115200, timeout=1)
print(f"成功打开 {ser.name}")
ser.write(b'Hello UART1\n')
ser.close()
except Exception as e:
print(f"错误: {e}")如果运行后提示:ModuleNotFoundError: No module named 'serial',则需要安装串口的python包。
sudo apt install python3-serial
确定串口打开成功后,编辑python代码。
#!/usr/bin/env python3
import serial
import time
import struct
class TOFSense:
"""
TOFSense 激光测距传感器驱动
协议: 16字节帧, 921600bps, 8N1
"""
def __init__(self, port='/dev/ttyS1', baudrate=921600, timeout=1):
self.port = port
self.baudrate = baudrate
self.timeout = timeout
self.serial = None
def open(self):
"""打开串口"""
try:
self.serial = serial.Serial(
port=self.port,
baudrate=self.baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=0.1
)
time.sleep(0.2)
self.serial.flushInput()
print(f"串口 {self.port} @ {self.baudrate}bps")
return True
except Exception as e:
print(f"错误: {e}")
return False
def close(self):
"""关闭串口"""
if self.serial and self.serial.is_open:
self.serial.close()
def read_frame(self):
"""读取一帧16字节数据"""
if not self.serial:
return None
# 清空旧数据,避免积压
if self.serial.in_waiting > 100:
self.serial.flushInput()
start_time = time.time()
while time.time() - start_time < self.timeout:
# 找帧头 0x57
byte = self.serial.read(1)
if not byte:
continue
if byte[0] == 0x57:
# 检查第二个字节 0x00
byte2 = self.serial.read(1)
if not byte2 or byte2[0] != 0x00:
continue
# 读取剩余14字节
remaining = self.serial.read(14)
if len(remaining) != 14:
continue
# 组装完整帧
frame = bytes([0x57, 0x00]) + remaining
# 校验和检查
calc_sum = sum(frame[:-1]) & 0xFF
if calc_sum != frame[15]:
continue
return frame
return None
def parse_frame(self, frame):
"""
解析16字节帧
Returns:
dict: 包含id, system_time, distance(m), status, signal_strength, precision
"""
if not frame or len(frame) != 16:
return None
result = {}
# ID (字节3)
result['id'] = frame[3]
# System Time (字节4-7, 小端32位)
result['system_time'] = struct.unpack('<I', frame[4:8])[0]
# Distance (字节8-10, 特殊处理)
# 公式: ((buf[10]<<24 | buf[9]<<16 | buf[8]<<8) / 256) / 1000.0
dist_raw = (frame[10] << 24) | (frame[9] << 16) | (frame[8] << 8)
# 有符号处理
if dist_raw & 0x80000000:
dist_raw -= 0x100000000
result['distance'] = (dist_raw / 256.0) / 1000.0 # 转换为米
# 距离状态 (字节11)
result['status'] = frame[11]
# 信号强度 (字节12-13, 小端16位)
result['signal_strength'] = struct.unpack('<H', frame[12:14])[0]
# 重复测距精度 (字节14)
result['precision'] = frame[14]
return result
def read(self):
"""读取并解析一帧数据"""
frame = self.read_frame()
if frame:
return self.parse_frame(frame)
return None
def status_text(status_code):
"""距离状态说明"""
status_map = {
0: "正常",
1: "超量程",
2: "信号弱",
3: "环境光强",
4: "相位差",
5: "硬件错误",
}
return status_map.get(status_code, f"未知({status_code})")
if __name__ == "__main__":
tof = TOFSense('/dev/ttyS1', 921600)
if not tof.open():
exit(1)
try:
while True:
data = tof.read()
if data:
# print(f"{data['id']:>3} | {data['system_time']:>10} | "
# f"{data['distance']:>8.3f} | {status_text(data['status']):>8} | "
# f"{data['signal_strength']:>6} | {data['precision']:>8}")
print(str(round(data['distance']*10,2))+"cm")
else:
print("读取超时")
time.sleep(0.1)
except KeyboardInterrupt:
print("\n停止")
finally:
tof.close()
成功读取到数据。

我要赚赏金
