电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
[树莓派Pico] TOF400激光测距mpy库的编写和使用
分 享
扫描二维码分享
[树莓派Pico] TOF400激光测距mpy库的编写和使用
树莓派Pico
TOF400激光
TOF激光测距
wybliw
关注
发布时间: 2022-05-17
丨
阅读: 2132
[TOC] --- * 开发环境:Thonny * 开发板:树莓派Pico * 模块:TOF400 --- ## TOF模块简介 TOF400F测距传感器是基于ST的VL53L1X设计制造的可提供了精确和可重复的远距离测量功能的一款激光测距模块。得益于其内部集成的领先的SPAD阵列(单光子雪崩二极管)、物理红外滤光片和光学器件,使用了ST最新一代的直接ToF技术,可实现更高测距距离、更准确的测量结果及更高的环境光抗干扰性。 TOF400F同时支持串口模式,串口模拟 Modbus 模式, 以及 IIC 模式,很好适应各种不同的应用场景。 有配套上位机,方便调试。TOF400F测距范围最高可达4m,并可根据需求选择高精度或者远距测试模式,使其具备更高的灵活性。 测量范围: | 项目 | 属性 | 数据周期 | 量程 | | :--: | :----: | :------: | :---: | | 0 | 高精度 | 30ms | 1.3米 | | 1 | 长距离 | 200ms | 4米 | 模块实物图: ![](https://cf02.ickimg.com/bbsimages/202205/936a36006e6a22c095dfad2e5a452b9d.png) 引脚描述: ![](https://cf02.ickimg.com/bbsimages/202205/b461e2699901b1ea2bac41ede0d028b2.png) 工作模式: TOF400默认工作模式是串口模式,遵循Modbus_RTU协议,配套上位机可方便调试和设置。此外还有IIC模式,需要指令切换,通过串口发送切换指令,让模块让出IIC总线,之后就可以直接使用IIC访问传感器芯片了。 ## 串口数据格式详解 TOF400默认串口波特率是 115200,数据位8,无校验位,1停止位,无流控。 ### 读指令 读指令的功能码是:0x03. 主机发送读取指令: | 从机地址 | 功能码 | 寄存器高地址 | 寄存器低地址 | 数据高位 | 数据低位 | CRC低位 | CRC高位 | | -------- | ------ | ------------ | ------------ | -------- | -------- | ------- | ------- | | DR | RW | RegH | RegL | DH | DL | CL | CH | | 0x01 | 0x03 | RegH | RegL | DH | DL | CL | CH | 传感器返回: | 从机地址 | 功能码 | 数据字节数 | 数据1高位 | 数据1低位 | ... | CRC低位 | CRC高位 | | -------- | ------ | ---------- | --------- | --------- | ---- | ------- | ------- | | DR | RW | D | D1H | D1L | ... | CL | CH | | 0x01 | 0x03 | D | D1H | D1L | ... | CL | CH | 例如: 主机发送: 01 03 00 10 00 01 85 CF 读取从机1的测量距离 传感器返回:01 03 02 00 65 78 6F 传感器测量距离值为0x0065 (101mm) ### 写指令 写指令的功能码是:0x06. 主机发送读取指令: | 从机地址 | 功能码 | 寄存器高地址 | 寄存器低地址 | 数据高位 | 数据低位 | CRC低位 | CRC高位 | | -------- | ------ | ------------ | ------------ | -------- | -------- | ------- | ------- | | DR | RW | RegH | RegL | DH | DL | CL | CH | | 0x01 | 0x06 | RegH | RegL | DH | DL | CL | CH | 传感器返回: | 从机地址 | 功能码 | 寄存器高地址 | 寄存器低地址 | 数据高位 | 数据低位 | CRC低位 | CRC高位 | | -------- | ------ | ------------ | ------------ | -------- | -------- | ------- | ------- | | DR | RW | RegH | RegL | DH | DL | CL | CH | | 0x01 | 0x06 | RegH | RegL | DH | DL | CL | CH | 例如: 主机发送: 01 06 00 04 00 01 09 CB 设置从机1的测量距离模式为高精度 传感器返回:01 06 00 04 00 01 09 CB 传感器设置成功后响应 **注意:** CRC校验规则为:CRC-16/MODBUS X16+X15+X2+1 ### 寄存器列表 查询TOF400模块手册,可以看到模块寄存器列表,如下(只截取一部分): ![](https://cf02.ickimg.com/bbsimages/202205/794da4b979ac788dd46a17598e586abd.png) 应用举例: ![](https://cf02.ickimg.com/bbsimages/202205/84ad1f2a3d14b05014a0edd48a3a1afd.png) 通过上述例子,我开始编写一个TOF库。 ## 编写TOF库 使用micropython(后面简称mpy)的伙伴,可以参考mpy官方的一些例子,来了解库的结构。 这里用mpy的sdcard库来说明,sdcard库链接:https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdcard.py 基本框架是这样: * 注释,介绍该库的一些信息,使用方法等; * 导入依赖库; * 全局参数申明; * 创建类; * 初始化; * 创建类的方法 ### 创建TOF类 开始编写TOF库,搭建基本框架: ```python """ TOF400 driver. Example usage on rp pico: from machine import I2C, Pin, UART import tof400, time uart=UART(0, baudrate=115200, tx=Pin(16), rx=Pin(17)) tof=tof400.TOF(uart, 1, timeout=5) dist=tof.auto_measure(True) if dist is not None: print("dist: ", dist, "mm") send: deviceaddr cmd regH regL dataH dataL crcL crcH recv: deviceaddr cmd dataBN data1H data1L ... crcL crcH eg: send: 01 03 00 10 00 01 85 CF read tof_1 dist recv: 01 03 02 00 65 78 6F """ import time class TOF: # uart , device id, uart read timeout def __init__(self, uart, id, timeout = 50): self._uart = uart # = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1)) self.id = id self.rxdata = bytes() self.timeout = timeout # default read timeout = 50ms ``` 注释部分,方便使用者了解如何使用库。由于后面要使用到time库,这里导入依赖库time,然后创建TOF类,__init__ 方法是初始化函数,创建对象时系统会自动调用它。 * _uart:uart对象,发送和接受数据; * id:传感器id号; * rxdata:接受数据缓存; * timeout:读取超时设置; 下面按照手册指令列表,实现对应方法。 ### crc校验 模块使用的校验算法是:CRC-16/MODBUS X16+X15+X2+1 查询资料,crc代码实现: ``` python def _crc16(self, data): crc = 0xffff for pos in data: crc = crc ^ pos for i in range(8): if crc & 0x01 != 0: crc = crc >> 1 crc = crc ^ 0xA001 else: crc = crc >> 1 data.append(crc) # crc低位 data.append(crc>>8) # crc高位 ``` * data:发送的数据包; crc高低位按照指令手册来设置。 ### 串口读取 首先等待串口数据到来,超时时间默认100ms,然后循环读取数据,超时时间默认50ms。 代码实现: ```python def read_data(self): self.rxdata = bytes() # clear buffer # 等待串口数据, timeout = 100ms t1 = time.ticks_ms() while (self._uart.any() < 1) and (time.ticks_diff(time.ticks_ms(), t1) < 100): pass # 读取串口数据, timeout = 50ms t1 = time.ticks_ms() while True: while self._uart.any() > 0: self.rxdata += self._uart.read(1) if (time.ticks_diff(time.ticks_ms(), t1) >= self.timeout): break ``` ### 测试传感器 代码实现: ```python def test(self): """ test tof. """ data = bytearray([self.id, 0x06, 0x00, 0x01, 0x00, 0x00]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: print("ok.") pass ``` 对应手册指令test,用于测试传感器是否在线。 ### 设置波特率 代码实现: ```python def set_baud(self, baud = 115200): """ set tof baud. """ if baud == 38400: b=0x01 elif baud == 9600: b=0x02 elif baud == 115200: b=0x03 else: raise ValueError( "Unsupported baudrate. \n" + "only 9600, 38400 and 115200 supported.\n") data = bytearray([self.id, 0x06, 0x00, 0x03, 0x00, b]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass ``` TOF400支持波特率是:9600,38400和115200。 ### 读取测量值 代码实现: ```python def auto_measure(self, auto=True): """ auto measure distance (mm). mode:> True: continue measure. > False: not continue measure. """ data = bytearray([self.id, 0x03, 0x00, 0x10, 0x00, 0x01]) # 判断是否自动读取 if not auto : self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata[0:2] == data[0:2]: #print("ok.") if len(self.rxdata) >= 5: return self.rxdata[3] << 8 | self.rxdata[4] return None ``` * auto:设置自动读取,前提是已开启自动读取模式。 当读取到正确的距离,返回距离值,单位mm,读取失败,返回None。 本文只列举几个示例,全部代码请看最后附件链接。 ## 使用自己编写的TOF库 库编写好后,就可以连接硬件测试了。 ### TOF400接线图 ![](https://cf02.ickimg.com/bbsimages/202205/1fee004e35a59406f7a224b084a52579.png) 本实验使用pico的uart0总线,GP16和GP17引脚。 测试代码: ```python from machine import Pin, UART import tof400 import time uart = UART(0, baudrate=115200, tx=Pin(16), rx=Pin(17)) tof = tof400.TOF(uart, 1, timeout = 5) tof.test() # tof.set_baud(115200) # tof.set_measure_range(4) # tof.reboot() # 设置自动测量时间间隔 tof.set_auto_measure(1000) while True: dist = tof.auto_measure(True) if dist is not None: print("dist: ", dist, "mm") ``` 实际运行效果: ![](https://cf02.ickimg.com/bbsimages/202205/de43daa994bc3a9362cad43f1abd07a1.png) ## 附录 ### tof400库完整代码 ```python """ TOF400 driver. Example usage on rp pico: from machine import I2C, Pin, UART import tof400, time uart = UART(0, baudrate=115200, tx=Pin(16), rx=Pin(17)) tof = tof400.TOF(uart, 1, timeout = 5) dist = tof.auto_measure(True) if dist is not None: print("dist: ", dist, "mm") send: deviceaddr cmd regH regL dataH dataL crcL crcH recv: deviceaddr cmd dataBN data1H data1L ... crcL crcH eg: send: 01 03 00 10 00 01 85 CF read tof_1 dist recv: 01 03 02 00 65 78 6F """ import time class TOF: # uart , device id, uart read timeout def __init__(self, uart, id, timeout = 50): self._uart = uart # = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1)) self.id = id self.rxdata = bytes() self.timeout = timeout # default uart read timeout = 50ms # crc16 def _crc16(self, data): crc = 0xffff for pos in data: crc = crc ^ pos for i in range(8): if crc & 0x01 != 0: crc = crc >> 1 crc = crc ^ 0xA001 else: crc = crc >> 1 data.append(crc) data.append(crc>>8) # read data def read_data(self): self.rxdata = bytes() # clear buffer # waiting uart data, timeout = 100ms t1 = time.ticks_ms() while (self._uart.any() < 1) and (time.ticks_diff(time.ticks_ms(), t1) < 100): pass # read uart data, timeout = 50ms t1 = time.ticks_ms() while True: while self._uart.any() > 0: self.rxdata += self._uart.read(1) if (time.ticks_diff(time.ticks_ms(), t1) >= self.timeout): break def reset_factory(self): """ tof reset factory. """ data = bytearray([self.id, 0x06, 0x00, 0x01, 0xAA, 0x55]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def reboot(self): """ reboot tof. """ data = bytearray([self.id, 0x06, 0x00, 0x01, 0x10, 0x00]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def test(self): """ test tof. """ data = bytearray([self.id, 0x06, 0x00, 0x01, 0x00, 0x00]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: print("ok.") pass def set_device_id(self, id): """ set tof id. id: > 0 : broadcast id > 1~255: device id """ data = bytearray([self.id, 0x06, 0x00, 0x02, (id>>8), id]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def set_baud(self, baud = 115200): """ set tof baud. """ if baud == 38400: b=0x01 elif baud == 9600: b=0x02 elif baud == 115200: b=0x03 else: raise ValueError( "Unsupported baudrate. \n" + "only 9600, 38400 and 115200 supported.\n") data = bytearray([self.id, 0x06, 0x00, 0x03, 0x00, b]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def set_measure_range(self, maxm=1.3): """ set tof measure max range. maxm: > 1.3 : max distance 1.3m. > 4 : max distance 4m. """ if maxm == 1.3: m=0 elif maxm == 4: m=1 else: raise ValueError( "Unsupported measure range. \n" + "max measure: 1.3m and 4m.\n") data = bytearray([self.id, 0x06, 0x00, 0x04, 0x00, m]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def set_auto_measure(self, ms=100): """ tof auto measure. ms: > 0: not continue measure. > n: eg: 100, measure every 100ms. """ data = bytearray([self.id, 0x06, 0x00, 0x05, (ms>>8), ms]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def set_iic_enable(self, en=False): """ iic enbale. en: > 0: disable iic. > 1: enable iic(MCU release io). """ if en : val = 1 else: val = 0 data = bytearray([self.id, 0x06, 0x00, 0x09, 0x00, val]) self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata == data: #print("ok.") pass def auto_measure(self, auto=True): """ auto measure distance (mm). mode: > True: continue measure. > False: not continue measure. """ data = bytearray([self.id, 0x03, 0x00, 0x10, 0x00, 0x01]) if not auto : self._crc16(data) self._uart.write(data) self.read_data() if self.rxdata[0:2] == data[0:2]: #print("ok.") if len(self.rxdata) >= 5: return self.rxdata[3] << 8 | self.rxdata[4] return None ``` ---
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
wybliw
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交