协议解码器 (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) // 8
      
    • reset(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

注释行用于将多种注释类型组合在一起, 该列表的元素是三个元素元组:

  1. 注释行 ID (与其他 ID 的命名规则相同)

  2. 注释行可读名称/描述字符串

  3. 一个元组, 包含注释元组中注释类的索引

binary

该协议解码器可以输出的二进制输出类型列表, 格式与注释列表相同

tags

协议适用范围标签