Stamp UWB
Stamp UWB 是一款 QM33120 超宽带收发器。StampUWB 类会根据当前 Stamp 主机选择引脚映射,并提供 PHY、帧、状态和时间戳操作。PHY 固定使用通道 9,并且仅支持一个活动的 StampUWB 实例。
支持以下产品:
支持的主机:
StampS3Mini
StampC6
StampC5
DS-TWR 测距原理
简单示例使用由三条消息组成的双边双向测距交互:
Tag 发送 Poll 帧并记录 Poll 发送时间戳
T1。Anchor 接收该帧并记录 Poll 接收时间戳T2。Anchor 发送 Response 帧并记录 Response 发送时间戳
T3。Tag 接收该帧并记录 Response 接收时间戳T4。Tag 延时发送 Final 帧,记录其发送时间戳
T5,并在帧中携带T1、T4和T5。Anchor 接收 Final 帧并记录时间戳T6。
示例使用以下简化帧格式。多字节数值采用小端序,序列号用于关联三种帧:
Poll 帧:
[0x01, sequence]Response 帧:
[0x02, sequence]Final 帧:
[0x03, sequence, T1, T4, T5],其中每个时间戳使用设备时间戳的低 32 位
Anchor 在处理 32 位时间戳回绕的同时计算飞行时间:
round_a = T4 - T1
round_b = T6 - T3
delay_a = T5 - T4
delay_b = T3 - T2
tof = (round_a * round_b - delay_a * delay_b) \
/ (round_a + round_b + delay_a + delay_b)
distance = abs(tof * DWT_TIME_UNITS * 299702547)
DWT_TIME_UNITS 为 1 / (499200000 * 128) 秒。Anchor 在本地打印计算得到的距离。该简化协议不会将距离回传给 Tag。
UiFlow2 应用示例
简单 DS-TWR Anchor 示例
在 UiFlow2 中打开 stampc5_uwb_simple_anchor.m5f2 项目。
Anchor 接收 Poll 帧和 Final 帧,在本地计算距离,并打印非负的测距结果。
UiFlow2 代码块:
简单 DS-TWR Tag 示例
在 UiFlow2 中打开 stampc5_uwb_simple_tag.m5f2 项目。
Tag 发送 Poll 帧、等待 Response 帧,然后延时发送 Final 帧。该简化协议不会将计算得到的距离返回给 Tag。
UiFlow2 代码块:
MicroPython 应用示例
简单 DS-TWR Anchor 示例
Anchor 接收 Poll 帧和 Final 帧,在本地计算距离,并打印非负的测距结果。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import uwb 9from stamp import StampUWB 10import time 11import struct 12import math 13 14 15stamp_uwb = None 16status = None 17sequence = None 18poll = None 19poll_rx = None 20response_frame = None 21final_frame = None 22response_tx = None 23final_rx = None 24round_a = None 25round_b = None 26delay_a = None 27response_rx = None 28poll_tx = None 29delay_b = None 30denominator = None 31final_tx = None 32tof_dtu = None 33distance = None 34display_distance = None 35 36 37def setup(): 38 global \ 39 stamp_uwb, \ 40 status, \ 41 sequence, \ 42 poll, \ 43 poll_rx, \ 44 response_frame, \ 45 final_frame, \ 46 response_tx, \ 47 final_rx, \ 48 round_a, \ 49 round_b, \ 50 delay_a, \ 51 response_rx, \ 52 poll_tx, \ 53 delay_b, \ 54 denominator, \ 55 final_tx, \ 56 tof_dtu, \ 57 distance, \ 58 display_distance 59 60 M5.begin() 61 stamp_uwb = StampUWB() 62 stamp_uwb.configure( 63 preamble_length=128, 64 pac=8, 65 tx_code=9, 66 rx_code=9, 67 sfd_type=uwb.SFD_DW_8, 68 data_rate=uwb.BR_6M8, 69 phr_mode=uwb.PHR_STD, 70 phr_rate=uwb.PHR_RATE_STD, 71 sfd_timeout=129, 72 ) 73 stamp_uwb.configure_tx_rf(pg_delay=0x34, tx_power=0xFEFEFEFE, pg_count=0) 74 stamp_uwb.set_antenna_delay(tx=16385, rx=16385) 75 stamp_uwb.set_lna_pa(lna=True, pa=True) 76 stamp_uwb.set_rx_timeout(30000) 77 sequence = 0 78 79 80def loop(): 81 global \ 82 stamp_uwb, \ 83 status, \ 84 sequence, \ 85 poll, \ 86 poll_rx, \ 87 response_frame, \ 88 final_frame, \ 89 response_tx, \ 90 final_rx, \ 91 round_a, \ 92 round_b, \ 93 delay_a, \ 94 response_rx, \ 95 poll_tx, \ 96 delay_b, \ 97 denominator, \ 98 final_tx, \ 99 tof_dtu, \ 100 distance, \ 101 display_distance 102 M5.update() 103 try: 104 stamp_uwb.force_trx_off() 105 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 106 stamp_uwb.rx_enable(uwb.RX_IMMEDIATE) 107 status = stamp_uwb.wait_status(uwb.STATUS_RX_ALL, 50) 108 if status & uwb.STATUS_RX_GOOD: 109 poll = stamp_uwb.read_rx_frame() 110 if len(poll) == 2 and poll[0] == 1: 111 sequence = poll[1] 112 poll_rx = stamp_uwb.rx_timestamp() & 0xFFFFFFFF 113 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 114 stamp_uwb.set_rx_after_tx_delay(0) 115 response_frame = bytearray(2) 116 response_frame[0] = 2 117 response_frame[1] = sequence 118 stamp_uwb.write_tx_frame(response_frame, True) 119 stamp_uwb.start_tx(uwb.TX_IMMEDIATE | uwb.RESPONSE_EXPECTED) 120 status = stamp_uwb.wait_status(uwb.STATUS_RX_ALL, 50) 121 if status & uwb.STATUS_RX_GOOD: 122 final_frame = stamp_uwb.read_rx_frame() 123 if ( 124 len(final_frame) == 14 125 and final_frame[0] == 3 126 and final_frame[1] == sequence 127 ): 128 response_tx = stamp_uwb.tx_timestamp() & 0xFFFFFFFF 129 final_rx = stamp_uwb.rx_timestamp() & 0xFFFFFFFF 130 poll_tx, response_rx, final_tx = struct.unpack_from("<III", final_frame, 2) 131 round_a = response_rx - poll_tx & 0xFFFFFFFF 132 round_b = final_rx - response_tx & 0xFFFFFFFF 133 delay_a = final_tx - response_rx & 0xFFFFFFFF 134 delay_b = response_tx - poll_rx & 0xFFFFFFFF 135 denominator = (round_a + round_b) + (delay_a + delay_b) 136 if denominator != 0: 137 tof_dtu = (round_a * round_b - delay_a * delay_b) / denominator 138 distance = math.fabs((tof_dtu * (1 / (499200000 * 128))) * 299702547) 139 display_distance = round(distance * 100) / 100 140 if distance >= 0 and distance <= 100: 141 print( 142 ( 143 str( 144 ( 145 str( 146 ( 147 str((str("sequence=") + str(sequence))) 148 + str(" distance=") 149 ) 150 ) 151 + str(display_distance) 152 ) 153 ) 154 + str(" m") 155 ) 156 ) 157 else: 158 print((str("invalid distance: ") + str(distance))) 159 else: 160 print("invalid final") 161 else: 162 print("final timeout") 163 except: 164 print("ranging retry") 165 stamp_uwb.force_trx_off() 166 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 167 168 time.sleep_ms(10) 169 170 171if __name__ == "__main__": 172 try: 173 setup() 174 while True: 175 loop() 176 except (Exception, KeyboardInterrupt) as e: 177 try: 178 from utility import print_error_msg 179 180 print_error_msg(e) 181 except ImportError: 182 print("please update to latest firmware")
简单 DS-TWR Tag 示例
Tag 发送 Poll 帧、等待 Response 帧,然后延时发送 Final 帧。该简化协议不会将计算得到的距离返回给 Tag。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import uwb 9from stamp import StampUWB 10import time 11import struct 12 13 14stamp_uwb = None 15poll_frame = None 16sequence = None 17status = None 18response = None 19expected_response = None 20poll_tx = None 21response_rx = None 22delayed_time = None 23delayed_time_even = None 24final_tx = None 25final_frame = None 26 27 28def setup(): 29 global \ 30 stamp_uwb, \ 31 poll_frame, \ 32 sequence, \ 33 status, \ 34 response, \ 35 expected_response, \ 36 poll_tx, \ 37 response_rx, \ 38 delayed_time, \ 39 delayed_time_even, \ 40 final_tx, \ 41 final_frame 42 43 M5.begin() 44 stamp_uwb = StampUWB() 45 stamp_uwb.configure( 46 preamble_length=128, 47 pac=8, 48 tx_code=9, 49 rx_code=9, 50 sfd_type=uwb.SFD_DW_8, 51 data_rate=uwb.BR_6M8, 52 phr_mode=uwb.PHR_STD, 53 phr_rate=uwb.PHR_RATE_STD, 54 sfd_timeout=129, 55 ) 56 stamp_uwb.configure_tx_rf(pg_delay=0x34, tx_power=0xFEFEFEFE, pg_count=0) 57 stamp_uwb.set_antenna_delay(tx=16385, rx=16385) 58 stamp_uwb.set_lna_pa(lna=True, pa=True) 59 stamp_uwb.set_rx_after_tx_delay(0) 60 stamp_uwb.set_rx_timeout(30000) 61 sequence = 0 62 63 64def loop(): 65 global \ 66 stamp_uwb, \ 67 poll_frame, \ 68 sequence, \ 69 status, \ 70 response, \ 71 expected_response, \ 72 poll_tx, \ 73 response_rx, \ 74 delayed_time, \ 75 delayed_time_even, \ 76 final_tx, \ 77 final_frame 78 M5.update() 79 try: 80 stamp_uwb.force_trx_off() 81 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 82 poll_frame = bytearray(2) 83 poll_frame[0] = 1 84 poll_frame[1] = sequence 85 stamp_uwb.write_tx_frame(poll_frame, True) 86 stamp_uwb.start_tx(uwb.TX_IMMEDIATE | uwb.RESPONSE_EXPECTED) 87 status = stamp_uwb.wait_status(uwb.STATUS_RX_ALL, 50) 88 if not (status & uwb.STATUS_RX_GOOD): 89 print("response timeout") 90 time.sleep_ms(200) 91 else: 92 response = stamp_uwb.read_rx_frame() 93 expected_response = bytearray(2) 94 expected_response[0] = 2 95 expected_response[1] = sequence 96 if response != expected_response: 97 print("invalid response") 98 time.sleep_ms(200) 99 else: 100 poll_tx = stamp_uwb.tx_timestamp() 101 response_rx = stamp_uwb.rx_timestamp() 102 delayed_time = response_rx + 10000 * 63898 >> 8 103 delayed_time_even = delayed_time & 0xFFFFFFFE 104 final_tx = (delayed_time_even << 8) + 16385 105 final_frame = struct.pack( 106 "<BBIII", 107 3, 108 sequence, 109 poll_tx & 0xFFFFFFFF, 110 response_rx & 0xFFFFFFFF, 111 final_tx & 0xFFFFFFFF, 112 ) 113 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 114 stamp_uwb.set_delayed_trx_time(delayed_time) 115 stamp_uwb.write_tx_frame(final_frame, True) 116 stamp_uwb.start_tx(uwb.TX_DELAYED) 117 status = stamp_uwb.wait_status(uwb.STATUS_TX_DONE, 30) 118 stamp_uwb.clear_status(uwb.STATUS_TX_DONE) 119 print((str("final sent, sequence=") + str(sequence))) 120 sequence = sequence + 1 & 0xFF 121 print(stamp_uwb.tx_timestamp()) 122 except: 123 print("ranging retry") 124 stamp_uwb.force_trx_off() 125 stamp_uwb.clear_status(uwb.STATUS_TX_DONE | uwb.STATUS_RX_ALL) 126 127 time.sleep_ms(200) 128 129 130if __name__ == "__main__": 131 try: 132 setup() 133 while True: 134 loop() 135 except (Exception, KeyboardInterrupt) as e: 136 try: 137 from utility import print_error_msg 138 139 print_error_msg(e) 140 except ImportError: 141 print("please update to latest firmware")
API 参考
class StampUWB
构造函数
常量
选项块提供以下方法使用的 PHY、TX/RX 模式和状态掩码常量。
UiFlow2 代码块:
- uwb.SFD_DW_8
StampUWB.configure()使用的 Decawave 8 符号 SFD。
- uwb.BR_6M8
StampUWB.configure()使用的 6.8 Mbit/s PHY 数据速率。
- uwb.PHR_STD
StampUWB.configure()使用的标准 PHY 帧头模式。
- uwb.PHR_RATE_STD
StampUWB.configure()使用的标准 PHY 帧头速率。
- uwb.TX_IMMEDIATE
立即开始发送。
- uwb.TX_DELAYED
在
StampUWB.set_delayed_trx_time()设置的时间开始发送。
- uwb.RESPONSE_EXPECTED
发送完成后自动启用接收器。此标志需与 TX 启动模式组合使用。
- uwb.RX_IMMEDIATE
立即启用接收器。
- uwb.RX_DELAYED
在已配置的延时 RX 时间启用接收器。
- uwb.IDLE_ON_DELAY_ERROR
如果延时 RX 操作已超时,则返回空闲状态。
- uwb.STATUS_TX_DONE
发送完成状态位。
- uwb.STATUS_RX_GOOD
正确接收帧状态位。
- uwb.STATUS_RX_TIMEOUT
接收超时组合状态掩码。
- uwb.STATUS_RX_ERROR
接收错误组合状态掩码。
- uwb.STATUS_RX_ALL
包含正确接收帧、接收超时和接收错误状态位的组合掩码。
方法
- StampUWB.configure(preamble_length=128, pac=8, tx_code=9, rx_code=9, sfd_type=uwb.SFD_DW_8, data_rate=uwb.BR_6M8, phr_mode=uwb.PHR_STD, phr_rate=uwb.PHR_RATE_STD, sfd_timeout=129)
配置通道 9 的 UWB PHY。
- 参数:
preamble_length (int) – 前导码长度,单位为符号。可选值为
32、64、72、128、256、512、1024、1536、2048和4096。默认值为128。pac (int) – 前导码捕获块大小。可选值为
4、8、16和32。默认值为8。tx_code (int) – TX 前导码编码,范围为
9到12。默认值为9。rx_code (int) – RX 前导码编码,范围为
9到12。默认值为9。sfd_type (int) – SFD 类型。使用
uwb.SFD_DW_8。data_rate (int) – PHY 数据速率。使用
uwb.BR_6M8。phr_mode (int) – PHR 模式。使用
uwb.PHR_STD。phr_rate (int) – PHR 速率。使用
uwb.PHR_RATE_STD。sfd_timeout (int) – SFD 超时时间,单位为符号,范围为
0到65535。默认值为129。
UiFlow2 代码块:

MicroPython 代码块:
import uwb stamp_uwb_0.configure( preamble_length=128, pac=8, tx_code=9, rx_code=9, sfd_type=uwb.SFD_DW_8, data_rate=uwb.BR_6M8, phr_mode=uwb.PHR_STD, phr_rate=uwb.PHR_RATE_STD, sfd_timeout=129, )
- StampUWB.configure_tx_rf(pg_delay=0x34, tx_power=0xFEFEFEFE, pg_count=0)
配置通道 9 的发射器 RF 参数。
- 参数:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.configure_tx_rf(0x34, 0xFEFEFEFE, 0)
- StampUWB.set_antenna_delay(tx=16385, rx=16385)
设置 TX 和 RX 时间戳的天线延迟补偿值。此参数用于补偿 UWB 时间戳参考点与实际天线 收发点之间的固定延迟,以校准测距结果。它不会让数据包延迟发送,也不会改变射频功率。 除非设备已经完成精密测距标定,否则请保持默认值。
- 参数:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.set_antenna_delay(tx=16385, rx=16385)
- StampUWB.set_lna_pa(lna=True, pa=True)
启用或禁用低噪声放大器和功率放大器控制。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.set_lna_pa(lna=True, pa=True)
- StampUWB.set_rx_after_tx_delay(delay_uus=0)
设置从 TX 完成到自动启用 RX 的延时。
- 参数:
delay_uus (int) – 延时,单位为 UWB 微秒,范围为
0到4294967295。默认值为0。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.set_rx_after_tx_delay(0)
- StampUWB.set_rx_timeout(timeout_uus=30000)
设置 RX 帧超时时间。值为
0时禁用超时。- 参数:
timeout_uus (int) – 超时时间,单位为 UWB 微秒,范围为
0到4294967295。默认值为30000。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.set_rx_timeout(30000)
- StampUWB.set_preamble_timeout(timeout=0)
设置前导码检测超时时间。值为
0时禁用超时。- 参数:
timeout (int) – 超时时间,单位为 PAC,范围为
0到65535。默认值为0。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.set_preamble_timeout(0)
- StampUWB.write_tx_frame(data, ranging=True)
将有效载荷写入 TX 缓冲区。数据中不要包含 2 字节 FCS。
- 参数:
data – 类 bytes 有效载荷,长度范围为
0到125字节。ranging (bool) – 设置 TX 帧控制中的测距标志位。默认值为
True。
- 抛出:
ValueError – 当有效载荷超过 125 字节时。
OSError – 当有效载荷无法写入 TX 缓冲区时。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.write_tx_frame(b"hello", ranging=True)
- StampUWB.set_delayed_trx_time(device_time)
设置延时 TX/RX 的设备时间。
- 参数:
device_time (int) – 40 位设备时间戳右移 8 位后的低 32 位,范围为
0到4294967295。
UiFlow2 代码块:

MicroPython 代码块:
delayed_time = (stamp_uwb_0.rx_timestamp() + 4500 * 63898) >> 8 stamp_uwb_0.set_delayed_trx_time(delayed_time)
- StampUWB.start_tx(mode)
启动立即发送或延时发送。
- 参数:
mode (int) – TX 模式由
uwb.TX_IMMEDIATE或uwb.TX_DELAYED与可选的uwb.RESPONSE_EXPECTED组合而成。- 抛出:
OSError – 当延时发送时间已经过去时。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.start_tx(uwb.TX_IMMEDIATE | uwb.RESPONSE_EXPECTED)
- StampUWB.rx_enable(mode=uwb.RX_IMMEDIATE)
启用接收器。
- 参数:
mode (int) – 使用
uwb.RX_IMMEDIATE或uwb.RX_DELAYED。延时 RX 可与uwb.IDLE_ON_DELAY_ERROR组合使用。默认值为uwb.RX_IMMEDIATE。- 抛出:
OSError – 当无法启用接收器时。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.rx_enable(uwb.RX_IMMEDIATE)
- StampUWB.wait_status(mask, timeout_ms=-1)
等待任一指定的系统状态位置位。
- 参数:
- 返回:
原始 32 位系统状态值。
- 返回类型:
- 抛出:
OSError – 如果在软件超时前没有指定状态位置位,则抛出
ETIMEDOUT。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.wait_status(uwb.STATUS_RX_ALL, 50)
- StampUWB.read_status()
读取系统状态寄存器的低 32 位。
- 返回:
原始 32 位系统状态值。
- 返回类型:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.read_status()
- StampUWB.clear_status(mask)
清除指定的系统状态位。
- 参数:
mask (int) – 状态掩码,范围为
0x00000000到0xFFFFFFFF。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.clear_status(uwb.STATUS_TX_DONE)
- StampUWB.force_trx_off()
强制发射器和接收器进入空闲状态。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.force_trx_off()
- StampUWB.frame_length()
获取最近接收帧的长度,包含 2 字节 FCS。
- 返回:
接收帧长度,单位为字节,范围为
2到127。- 返回类型:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.frame_length()
- StampUWB.read_rx_frame()
读取最近接收的有效载荷,不包含 2 字节 FCS。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.read_rx_frame()
- StampUWB.tx_timestamp()
读取最近一次 TX 时间戳。
- 返回:
完整的 40 位 TX 时间戳,单位为设备时间单位。
- 返回类型:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.tx_timestamp()
- StampUWB.rx_timestamp()
读取最近一次 RX 时间戳。
- 返回:
完整的 40 位 RX 时间戳,单位为设备时间单位。
- 返回类型:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.rx_timestamp()
- StampUWB.system_timestamp()
读取当前 UWB 系统时间戳。
- 返回:
32 位系统时间戳,为原始设备时钟计数。每个计数约为
15.65 ps;它不是毫秒或实际日期时间戳。将时间戳差值乘以1 / (499200000 * 128)可转换为秒。计数器约每67.2 ms回绕一次。- 返回类型:
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.system_timestamp()
- StampUWB.reset()
复位、探测并重新初始化 UWB 设备。复位后需要重新配置 PHY 和 RF 参数。
- 抛出:
OSError – 当复位后无法探测或初始化设备时。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.reset() stamp_uwb_0.configure( preamble_length=128, pac=8, tx_code=9, rx_code=9, sfd_type=uwb.SFD_DW_8, data_rate=uwb.BR_6M8, phr_mode=uwb.PHR_STD, phr_rate=uwb.PHR_RATE_STD, sfd_timeout=129, ) stamp_uwb_0.configure_tx_rf( pg_delay=0x34, tx_power=0xFEFEFEFE, pg_count=0 )
- StampUWB.wakeup()
通过 WAKEUP 引脚脉冲唤醒 UWB 设备。
UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.wakeup()
- StampUWB.deinit()
停止 TX/RX 并释放 SPI 和 GPIO 资源。该方法可重复调用。反初始化后,调用其他方法会抛出
OSError(ENODEV)。UiFlow2 代码块:

MicroPython 代码块:
stamp_uwb_0.deinit()




