协议解码器 (Python脚本) 格式 =================================== 前面提到的解码器, 其实它就是一个 ``python 脚本`` , ``libsigrokdecode`` 库为了方便大家维护和自定义协议解码器, 降低编写解码器的入门门槛, 使用目前非常流行的 ``python`` 脚本来解析样本数据, 而本文的最终目的就是为了编写 ``python 脚本``。 **协议解码器(python脚本)** 的源码可以通过以下方式查看: - 打开 ``ATK-Logic`` 软件的安装目录, 可以在 ``decoders`` 文件夹下查看各解码库 ``python脚本`` 源码。 - 下载正点原子提供的协议解码库源码, 可以在 ``decoders`` 文件夹下查看各解码库 ``python脚本`` 源码。 - 下载官方 ``libsigrokdecode`` 库的源码, 可以在 ``decoders`` 文件夹下查看各解码库 ``python脚本`` 源码。 为了让我们编写的解码器正常运行起来, 解码器必须有一定的格式要求。 ``libsigrokdecode`` 库对解码器做了以下一些要求 : .. include:: style.rst - :blue:`必须导入 sigrokdecode 模块`。大家可以随便找一个 ``pd.py`` 文件查看, 每个文件的第一件事都是先导入 ``sigrokdecode`` 模块, 示例如下 : .. code-block:: python # 导入 sigrokdecode 模块, 并重名为 srd import sigrokdecode as srd - :blue:`必须依赖 srd.Decoder 创建一个 Decoder 类`, 示例如下 : .. code-block:: python # 依赖 srd.Decoder 创建一个 Decoder 类 class Decoder(srd.Decoder): ... - :blue:`Decoder 类必须提供三个函数:` ``start(self)`` ``reset(self)`` ``decode(self) / decode(self, startsample, endsample, data)`` - **start(self)** 此函数在解码开始之前调用。 这是注册输出类型、检查用户提供的解码器选项有效性等的地方。 从 ``uart`` 解码器中选取示例如下 : .. code-block:: python 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) // 8 - **reset(self)** 此函数在解码开始之前调用。 这是将协议解码器内部变量重置为初始状态的地方, 例如状态机和计数器。从 ``uart`` 解码器中选取示例如下 : .. code-block:: python 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`` 解码器中选取示例如下 : .. code-block:: python 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 作为示例 : .. code-block:: python # 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]) .. code-block:: python # 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() - :blue:`可选函数:` ``metadata(self, key, value)`` 用于传递有关数据流的解码器元数据。目前 ``key`` 的唯一值是 ``SRD_CONF_SAMPLERATE 常量``, ``value`` 是数据流的采样率, 以 Hz 为单位。 这个函数可以被调用多次, 所以确保你的协议解码器正确处理处理这个问题!不要在其中放置依赖于仅调用一次的代码。这里以 uart 解码器作为示例 : .. code-block:: python 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']) - :blue:`属性值说明:` +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **api_version** | 此模块使用的 libsigrokdecode API 版本, 这里必须为 3 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **id** | 此协议解码器的简短唯一标识符, 全小写, 并且只包含 a-z、0-9 和下划线, | | | | | | 必须与解码器的 Python 模块名称 (解码器目录中的子目录名称) 保持一致 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **name** | 解码器的名称, 在列出可用的解码器时使用, 示例: JTAG / SD 卡 (SPI 模式) / UART | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **longname** | 解码器的长名称 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **desc** | 解码器的单行描述, 应该以英文句号结束 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **license** | 模块所依据的开源协议许可证, 必须是 :blue:`gplv2+` 或 必须是 :blue:`gplv3+`, | | | | | | libsigrokdecode 中不允许使用其他模块许可证 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **inputs** | 此解码器需要的输入类型列表。如果解码器从逻辑分析器驱动程序获取输入, | | | | | | 则应将其设置为logic, 如果它从另一个解码器获取输入, | | | | | | 则应将其设置为该解码器的输出键值, 它应符合与 id 键相同的规则 (小写、无空格等) | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **outputs** | 此解码器产生的输出类型列表, 如果这个解码器可以将解码后的数据反馈到 | | | | | | 数据流中, 它的输出将用这个键的值来标识, 它应该符合与 id 名字相同的规则 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **channels** | 包含必须提供给此解码器的通道 (引脚) 信息, 没有他们解码器将无法工作 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **optional_channels** | 可选用的通道, (引脚), 该值与上面的 channels 具有相同的格式 (字典元组), | | | | | | 如果相应的协议解码器没有可选通道, 则该元组允许为空 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **options** | 描述此解码器选项的元组, 每个元组条目都是一个 Python 字典, | | | | | | 其中包含键 id / desc / default / values | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **annotations** | 此协议解码器可以输出的注释类列表, 该列表的元素是由标识符字符串和 | | | | | | 人可读的描述字符串组成的元组 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **annotation_rows** | 注释行用于将多种注释类型组合在一起, 该列表的元素是三个元素元组: | | | | | | 1. 注释行 ID (与其他 ID 的命名规则相同) | | | | | | 2. 注释行可读名称/描述字符串 | | | | | | 3. 一个元组, 包含注释元组中注释类的索引 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **binary** | 该协议解码器可以输出的二进制输出类型列表, 格式与注释列表相同 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | **tags** | 协议适用范围标签 | +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+