[GN] sigrokdecode 模块
系列文章目录
sigrokdecode 模块学习指南 — 准备阶段
通讯协议 - Uart
sigrokdecode 模块
文章目录
- 系列文章目录
- 前言
- sigrokdecode.Decoder 类
- 四个方法
- wait(conds) 函数
- conds 条件
- 跳过条件
- 5个常量
- OUTPUT_ANN:
- OUTPUT_PYTHON
- OUTPUT_BINARY
- OUTPUT_META:
- samplenum / matched / pinvalues
- samplenum
- matched
- pinvalues
前言
libsigrokdecode 为我们提供了一个 sigrokdecode 模块。它包含以下三种数据 :
- sigrokdecode.Decoder 类
- 5个常量
- 解码成功, 返回三个特殊变量: samplenum 数值 、 matched 元组 、pinvalues 元组
sigrokdecode.Decoder 类
是很重要的一个类, 所有的协议解码器都是基于这个类而创建。
四个方法
它还为我们提供了以下四种方法 (函数) :
-
put(startsample, endsample, output_id, data)
此函数用于将解码后的数据提供给底层库, 例如注释, 或者叠加协议时底层协议(例如: spi )传输给上层协议(例如: spi flash)的数据
startsample
指定解码数据/注释的起始位置endsample
指定解码数据/注释的结束位置output_id
是由 register() 函数返回的输出标识符: OUTPUT_ANN/OUTPUT_PYTHON/OUTPUT_BINARY/OUTPUT_METAdata
参数的内容取决于输出类型 ( output_id ), 关于具体的数据类型, 请参考下面 5个常量 小节内容
-
register(output_type)
此函数用于注册解码器输出数据的类型, 该函数会返回一个标识符, 然后可以将其用作 put() 函数的 output_id 参数。
output_type
:OUTPUT_ANN/OUTPUT_PYTHON/OUTPUT_BINARY/OUTPUT_META
-
wait(conds)
此函数由解码器调用, 参数
conds
传入查询条件, 底层 C 库会在采样数据中找到指定的条件, 才会将控制权返回给解码器。这是一个阻塞调用, 只有找到指定条件时, 才会返回。- 等待直到
conds
中的一个或多个条件匹配, 才会返回 - 将
self.samplenum
设置为匹配样本的当前位置 (绝对位置) - 根据匹配的条件设置
self.matched
- 返回一个元组, 其中包含匹配样本的当前引脚值
- 等待直到
-
has_channel(idx)
此函数会根据输入的通道编号
idx
判断当前的可选通道是否存在, 存在返回True
, 否则返回False
。- idx 可选通道的ID
register(output_type)
返回 output_id 用作 put() 函数。【output_type有四个类型——OUTPUT_ANN;OUTPUT_PYTHON;OUTPUT_BINARY;OUTPUT_META】
put(startsample, endsample, output_id, data)
data取决于输出类型output_id
wait(conds) 函数
conds
中可以提供多个条件, 支持逻辑或和逻辑与判断。 条件可以是 列表[]
表示 或逻辑, 如果是 字典{}
表示 与逻辑。
conds 条件
看过前文我们知道 wait(conds) 函数
需要一个 conds 条件
参数, 那我们如何写这个条件呢?
单个条件始终是 Python 中的 字典dict
, 其中可以包含零个或多个键/值对。键 (和值) 可以是不同的类型。
libsigrokdecode 为我们提供了6种 引脚状态条件 定义, 其他值都会导致出错。
'l'
: 低引脚值 (逻辑 0)'h'
: 高引脚值 (逻辑 1)'r'
: 上升沿'f'
: 下降沿'e'
: 任一边缘 (上升或下降)'s'
: 稳定状态, 与’e’相反。也就是说, 没有边沿, 当前和前一个引脚值都为低 (或都为高)
# 等待 pin 2/3/4 为低且 pin 16 有任何边沿
pins = self.wait({2: 'l', 3: 'l', 4: 'l', 16: 'e'})
# 等待 引脚 9 的上升沿 或
# 引脚 27 上的高状态 (逻辑 1) 或
# 样本超过 1000 个样本数
pins = self.wait([{9: 'r'}, {27: 'h'}, {'skip': 1000}])
跳过条件
在解码器中是一种很常见的条件, 它不会关心引脚电平。 当解码器想要跳过一定数量的样本时, 可以使用这个条件。
跳过条件 可以通过条件 字典
中的特殊键 'skip'
来完成
# 跳过接下来的 100 个样本
pins = self.wait({'skip': 100})
# 跳过接下来的 20ms 样本
pins = self.wait({'skip': 20 * (1000 / self.samplerate)})
# 跳过半个位宽的样本 (例如 UART)
self.halfbitwidth = int((self.samplerate / self.options['baudrate']) / 2.0)
pins = self.wait({'skip': self.halfbitwidth})
5个常量
前面在讲到 register(output_type)
函数时提到过这个输入参数 output_type
, 下面我们就来介绍一下这个参数值。
libsigrokdecode
是一个功能很完善的协议解码库, 为了解决解码器处理不同数据的问题, 定义了以下四种数据类型 :
- OUTPUT_ANN 用于注册注释数据输出的常量, 用于输出解码后的注释字符串数据。
- OUTPUT_PYTHON 用于注册 Python 数据输出的常量。 这个会在协议叠加的时候用到, Python 输出数据作为输入数据传递给堆叠到当前解码器上的上层解码器。
- OUTPUT_BINARY 用于注册二进制数据输出的常量。 未指定输出数据的格式, 由解码器选择一种 (或多种) 合适的格式。例如: UART 解码器输出它解码的原始字节, I²S 解码器输出 WAV 格式的音频, 但对于解码显示协议的解码器, 输出也可以是图像 (JPG、PNG 等) 文件, 或许多其他格式之一。
- OUTPUT_META 用于注册自定义数据输出的常量。 可以参考 SPI 解码器源码。
OUTPUT_ANN:
数据参数是一个包含两项的 Python 列表。第一项是注释索引 (由解码器 annotations
中的项顺序确定) , 第二项是注释字符串列表。字符串应该是相同注释文本的更长和更短版本 (按长度排序, 最长的在前) , 前端可以使用它们根据缩放级别显示不同的注释文本。
# 输出的数据跨越 10 到 20 个样本
# 类型为 OUTPUT_ANN, 注释索引为 4, 注释字符串列表为 “Start”、“St”、“S”
self.put(10, 20, self.out_ann, [4, ['Start', 'St', 'S']])
# 输出的数据跨越 10 到 20 个样本
# 类型为 OUTPUT_ANN, 注释索引为 4, 注释字符串列表只是“CRC” (该列表仅包含一项)
self.put(10, 20, self.out_ann, [4, ['CRC']])
OUTPUT_PYTHON
数据参数是将传递给叠加解码器的 Python 对象数据, 格式和内容完全取决于解码器。
# 发出的数据跨越 10 到 20 个样本
# 类型为 OUTPUT_PYTHON, 数据内容本身完全取决于相应的解码器, 记录在其pd.py文件中
self.put(10, 20, self.out_python, ['PACKET', ['Foo', 19.7, [1, 2, 3], ('bar', 'baz')]])
OUTPUT_BINARY
数据参数是一个包含两项的 Python 列表。第一项是二进制格式的索引 (由解码器 binary 中项目的顺序确定) , 第二项是 Python 字节对象
# 发出的数据跨越 10 到 20 个样本
# 类型为 OUTPUT_BINARY, 二进制格式的索引为 4, 发出的字节为 0xfe、0x55、0xaa
self.put(10, 20, self.out_binary, [4, b'\xfe\x55\xaa'])
OUTPUT_META:
数据参数是特定类型的 Python 对象, 在各自的 register() 函数中定义
# 发出的数据跨越 10 到 20 个样本
# 属于 OUTPUT_META 类型, 在这种情况下, 数据本身是一个浮点数。
self.put(10, 20, self.out_meta, 15.7)
# 发出的数据跨越 10 到 20 个样本
# 属于 OUTPUT_META 类型, 在这种情况下, 数据本身是一个整数。
self.put(10, 20, self.out_meta, 42)
samplenum / matched / pinvalues
wait(conds)
函数时候提到过三个数据 : samplenum
, matched
, pinvalues
, 这个三个数据只有在该函数 conds
条件中的一个或多个条件匹配成功时才会正确返回。
samplenum
在解码器中为只读属性, 只能由 libsigrokdecode
底层设置。 当条件匹配成功时, self.wait
函数会返回当前样本解析的绝对位置(样本从0开始), 解码器可以通过访问该变量知道样本当前解析位置。
这里以 uart
解码器举例作为说明, 源码如下 :
# 根据 samplenum 计算当前输出注释在样本中的起始位置和结束位置
s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
matched
当 self.wait(conds)
函数有多个条件匹配成功返回时, 解码器只知道 至少有一个条件匹配成功, 然而在大多数情况下, 它还需要知道哪些条件匹配(或不匹配)。 这就是该变量的作用, self.matched
可以为解码器提供条件是否匹配成功的信息, 它是一个 boolean
(True / False) 类型的元组, 对于每一个条件, 都可以从相应的值中确定条件是否匹配成功。
# 等待 引脚 9 的上升沿 或
# 引脚 27 上的高状态 (逻辑 1) 或
# 样本数超过 1000 个
pins = self.wait([{9: 'r'}, {27: 'h'}, {'skip': 1000}])
if self.matched == (True, True, False):
# 前两个条件在同一时间/样本匹配。
# 引脚 9 包含上升沿, 引脚 27 为高电平。
elif self.matched == (True, False, False):
# 9脚上升沿, 27脚不高。
pinvalues
self.wait
始终会返回一个值, 一个包含此特定解码器所有通道的引脚状态 (0/1 表示低/高) 的元组。 元组中的条目列表与解码器的 channels
和 optional_channels
元组的索引/排序相匹配。
# 示例解码器:UART。可选通道:RX、TX
rx, tx = self.wait(...)