协议解码器 (Python脚本) 格式
前面提到的解码器, 其实它就是一个
python 脚本,libsigrokdecode库为了方便大家维护和自定义协议解码器, 降低编写解码器的入门门槛, 使用目前非常流行的python脚本来解析样本数据, 而本文的最终目的就是为了编写python 脚本。协议解码器(python脚本) 的源码可以通过以下方式查看:
打开
ATK-Logic软件的安装目录, 可以在decoders文件夹下查看各解码库python脚本源码。下载正点原子提供的协议解码库源码, 可以在
decoders文件夹下查看各解码库python脚本源码。下载官方
libsigrokdecode库的源码, 可以在decoders文件夹下查看各解码库python脚本源码。为了让我们编写的解码器正常运行起来, 解码器必须有一定的格式要求。
libsigrokdecode库对解码器做了以下一些要求 :
必须导入 sigrokdecode 模块。大家可以随便找一个
pd.py文件查看, 每个文件的第一件事都是先导入sigrokdecode模块, 示例如下 :# 导入 sigrokdecode 模块, 并重名为 srd import sigrokdecode as srd必须依赖 srd.Decoder 创建一个 Decoder 类, 示例如下 :
# 依赖 srd.Decoder 创建一个 Decoder 类 class Decoder(srd.Decoder): ...Decoder 类必须提供三个函数:
start(self)reset(self)decode(self) / decode(self, startsample, endsample, data)
start(self) 此函数在解码开始之前调用。 这是注册输出类型、检查用户提供的解码器选项有效性等的地方。 从
uart解码器中选取示例如下 :def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_ann = self.register(srd.OUTPUT_ANN) self.bw = (self.options['data_bits'] + 7) // 8reset(self) 此函数在解码开始之前调用。 这是将协议解码器内部变量重置为初始状态的地方, 例如状态机和计数器。从
uart解码器中选取示例如下 :def reset(self): self.samplerate = None self.frame_start = [-1, -1] self.frame_valid = [None, None] self.cur_frame_bit = [None, None] self.startbit = [-1, -1] self.cur_data_bit = [0, 0] self.datavalue = [0, 0] self.paritybit = [-1, -1] self.stopbits = [[], []] self.startsample = [-1, -1] self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT'] self.databits = [[], []] self.break_start = [None, None] self.packet_cache = [[], []] self.ss_packet, self.es_packet = [None, None], [None, None] self.idle_start = [None, None]decode 函数在不同模式下, 输入参数是不一样的
- decode(self)
在非叠加解码器中 (例如: uart/i2c), 此函数由底层库调用以开始解码。 它不接受任何参数, 而是进入一个无限循环并通过调用更通用的wait()方法获取样本。这将特定协议解码器从繁琐而常见的任务中解放出来, 例如边缘检测, 或在相对于当前位置的特定时间点对信号进行采样。 从
i2c解码器中选取示例如下 :def decode(self): while True: # State machine. if self.state == 'FIND START': # Wait for a START condition (S): SCL = high, SDA = falling. self.handle_start(self.wait({0: 'h', 1: 'f'})) elif self.state == 'FIND ADDRESS': # Wait for a data bit: SCL = rising. self.handle_address_or_data(self.wait({0: 'r'})) elif self.state == 'FIND DATA': # Wait for any of the following conditions (or combinations): # a) Data sampling of receiver: SCL = rising, and/or # b) START condition (S): SCL = high, SDA = falling, and/or # c) STOP condition (P): SCL = high, SDA = rising pins = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}]) # Check which of the condition(s) matched and handle them. if self.matched[0]: self.handle_address_or_data(pins) elif self.matched[1]: self.handle_start(pins) elif self.matched[2]: self.handle_stop(pins) elif self.state == 'FIND ACK': # Wait for a data/ack bit: SCL = rising. self.get_ack(self.wait({0: 'r'}))
- decode(self, startsample, endsample, data)
在堆栈解码器中 (例如: spi_flash/eeprom23xx), 当存在叠加协议, 且数据输出类型为
OUTPUT_PYTHON时, 底层库会调用解码器中的这个函数。 协议叠加时, 底层协议就是通过这个函数向上层协议传输解码数据。参数介绍如下 :
startsample此数据块中第一个样本的绝对样本编号。
endsample此数据块中最后一个样本的绝对样本编号。
data包含要解码的数据的列表。根据解码器是解码原始样本还是堆叠到另一个解码器上。这个参数由此解码器堆叠在其上的解码器的 OUTPUT_PYTHON 输出数据类型决定。它的格式完全取决于底层解码器的实现。这里以 i2c/eeprom23xx 作为示例 :
# i2c 解码器 (pd.py 文件) # 定义解码器数据输入和输出方式 (输出 i2c 数据) inputs = ['logic'] outputs = ['i2c'] # i2c 解码器注册 OUTPUT_PYTHON 输出类型 def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_bitrate = self.register(srd.OUTPUT_META, meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit')) # i2c OUTPUT_PYTHON 数据输出函数 def putp(self, data): self.put(self.ss, self.es, self.out_python, data) # 选取部分解码器源码示例, 输出 OUTPUT_PYTHON 数据 self.putp(['BITS', self.bits]) cmd = 'NACK' if (sda == 1) else 'ACK' self.putp([cmd, None])# eeprom23xx 解码器 (pd.py 文件) # 定义解码器数据输入方式 (从 i2c 输入数据) inputs = ['i2c'] # eeprom23xx 解码器 decode 函数 def decode(self, ss, es, data): self.cmd, self.databyte = data # Collect the 'BITS' packet, then return. The next packet is # guaranteed to belong to these bits we just stored. if self.cmd == 'BITS': self.bits = self.databyte return # Store the start/end samples of this I²C packet. self.ss, self.es = ss, es # State machine. s = 'handle_%s' % self.state.lower().replace(' ', '_') handle_state = getattr(self, s) handle_state()
- 可选函数:
metadata(self, key, value)用于传递有关数据流的解码器元数据。目前
key的唯一值是SRD_CONF_SAMPLERATE 常量,value是数据流的采样率, 以 Hz 为单位。 这个函数可以被调用多次, 所以确保你的协议解码器正确处理处理这个问题!不要在其中放置依赖于仅调用一次的代码。这里以 uart 解码器作为示例 :def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value # The width of one UART bit in number of samples. self.bit_width = float(self.samplerate) / float(self.options['baudrate'])属性值说明:
api_version |
此模块使用的 libsigrokdecode API 版本, 这里必须为 3 |
id |
此协议解码器的简短唯一标识符, 全小写, 并且只包含 a-z、0-9 和下划线, 必须与解码器的 Python 模块名称 (解码器目录中的子目录名称) 保持一致 |
name |
解码器的名称, 在列出可用的解码器时使用, 示例: JTAG / SD 卡 (SPI 模式) / UART |
longname |
解码器的长名称 |
desc |
解码器的单行描述, 应该以英文句号结束 |
license |
模块所依据的开源协议许可证, 必须是 gplv2+ 或 必须是 gplv3+, libsigrokdecode 中不允许使用其他模块许可证 |
inputs |
此解码器需要的输入类型列表。如果解码器从逻辑分析器驱动程序获取输入, 则应将其设置为logic, 如果它从另一个解码器获取输入, 则应将其设置为该解码器的输出键值, 它应符合与 id 键相同的规则 (小写、无空格等) |
outputs |
此解码器产生的输出类型列表, 如果这个解码器可以将解码后的数据反馈到 数据流中, 它的输出将用这个键的值来标识, 它应该符合与 id 名字相同的规则 |
channels |
包含必须提供给此解码器的通道 (引脚) 信息, 没有他们解码器将无法工作 |
optional_channels |
可选用的通道, (引脚), 该值与上面的 channels 具有相同的格式 (字典元组), 如果相应的协议解码器没有可选通道, 则该元组允许为空 |
options |
描述此解码器选项的元组, 每个元组条目都是一个 Python 字典, 其中包含键 id / desc / default / values |
annotations |
此协议解码器可以输出的注释类列表, 该列表的元素是由标识符字符串和 人可读的描述字符串组成的元组 |
annotation_rows |
注释行用于将多种注释类型组合在一起, 该列表的元素是三个元素元组:
|
binary |
该协议解码器可以输出的二进制输出类型列表, 格式与注释列表相同 |
tags |
协议适用范围标签 |