[GN] sigrokdecode 模块

系列文章目录

sigrokdecode 模块学习指南 — 准备阶段
通讯协议 - Uart
sigrokdecode 模块
UART协议解码器源码解析
Uart协议解码器源码各个方法



前言

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_META
    • data 参数的内容取决于输出类型 ( 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 表示低/高) 的元组。 元组中的条目列表与解码器的 channelsoptional_channels 元组的索引/排序相匹配。

# 示例解码器:UART。可选通道:RX、TX
rx, tx = self.wait(...)
## ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see <http://www.gnu.org/licenses/>. ## import sigrokdecode as srd # Dictionary of ROM commands and their names, next state. command = { 0x33: ['Read ROM' , 'GET ROM' ], 0x0f: ['Conditional read ROM' , 'GET ROM' ], 0xcc: ['Skip ROM' , 'TRANSPORT' ], 0x55: ['Match ROM' , 'GET ROM' ], 0xf0: ['Search ROM' , 'SEARCH ROM'], 0xec: ['Conditional search ROM' , 'SEARCH ROM'], 0x3c: ['Overdrive skip ROM' , 'TRANSPORT' ], 0x69: ['Overdrive match ROM' , 'GET ROM' ], 0xa5: ['Resume' , 'TRANSPORT' ], 0x96: ['DS2408: Disable Test Mode' , 'GET ROM' ], } class Decoder(srd.Decoder): api_version = 3 id = 'onewire_network' name = '1-Wire network layer' longname = '1-Wire serial communication bus (network layer)' desc = 'Bidirectional, half-duplex, asynchronous serial bus.' license = 'gplv2+' inputs = ['onewire_link'] outputs = ['onewire_network'] tags = ['Embedded/industrial'] annotations = ( ('text', 'Human-readable text'), ) def __init__(self): self.reset() def reset(self): self.ss_block = 0 self.es_block = 0 self.state = 'COMMAND' self.bit_cnt = 0 self.search = 'P' self.data_p = 0x0 self.data_n = 0x0 self.data = 0x0 self.rom = 0x0000000000000000 def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) def putx(self, data): # Helper function for most annotations. self.put(self.ss_block, self.es_block, self.out_ann, data) def puty(self, data): # Helper function for most protocol packets. self.put(self.ss_block, self.es_block, self.out_python, data) def decode(self, ss, es, data): code, val = data # State machine. if code == 'RESET/PRESENCE': self.search = 'P' self.bit_cnt = 0 self.put(ss, es, self.out_ann, [0, ['Reset/presence: %s' % ('true' if val else 'false')]]) self.put(ss, es, self.out_python, ['RESET/PRESENCE', val]) self.state = 'COMMAND' return # For now we're only interested in 'RESET/PRESENCE' and 'BIT' packets. if code != 'BIT': return if self.state == 'COMMAND': # Receiving and decoding a ROM command. if self.onewire_collect(8, val, ss, es) == 0: return if self.data in command: self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, command[self.data][0])]]) self.state = command[self.data][1] else: self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, 'unrecognized')]]) self.state = 'COMMAND ERROR' elif self.state == 'GET ROM': # A 64 bit device address is selected. # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte) if self.onewire_collect(64, val, ss, es) == 0: return self.rom = self.data & 0xffffffffffffffff self.putx([0, ['ROM: 0x%016x' % self.rom]]) self.puty(['ROM', self.rom]) self.state = 'TRANSPORT' elif self.state == 'SEARCH ROM': # A 64 bit device address is searched for. # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte) if self.onewire_search(64, val, ss, es) == 0: return self.rom = self.data & 0xffffffffffffffff self.putx([0, ['ROM: 0x%016x' % self.rom]]) self.puty(['ROM', self.rom]) self.state = 'TRANSPORT' elif self.state == 'TRANSPORT': # The transport layer is handled in byte sized units. if self.onewire_collect(8, val, ss, es) == 0: return self.putx([0, ['Data: 0x%02x' % self.data]]) self.puty(['DATA', self.data]) elif self.state == 'COMMAND ERROR': # Since the command is not recognized, print raw data. if self.onewire_collect(8, val, ss, es) == 0: return self.putx([0, ['ROM error data: 0x%02x' % self.data]]) # Data collector. def onewire_collect(self, length, val, ss, es): # Storing the sample this sequence begins with. if self.bit_cnt == 0: self.ss_block = ss self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) self.bit_cnt += 1 # Storing the sample this sequence ends with. # In case the full length of the sequence is received, return 1. if self.bit_cnt == length: self.es_block = es self.data = self.data & ((1 << length) - 1) self.bit_cnt = 0 return 1 else: return 0 # Search collector. def onewire_search(self, length, val, ss, es): # Storing the sample this sequence begins with. if (self.bit_cnt == 0) and (self.search == 'P'): self.ss_block = ss if self.search == 'P': # Master receives an original address bit. self.data_p = self.data_p & ~(1 << self.bit_cnt) | \ (val << self.bit_cnt) self.search = 'N' elif self.search == 'N': # Master receives a complemented address bit. self.data_n = self.data_n & ~(1 << self.bit_cnt) | \ (val << self.bit_cnt) self.search = 'D' elif self.search == 'D': # Master transmits an address bit. self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) self.search = 'P' self.bit_cnt += 1 # Storing the sample this sequence ends with. # In case the full length of the sequence is received, return 1. if self.bit_cnt == length: self.es_block = es self.data_p = self.data_p & ((1 << length) - 1) self.data_n = self.data_n & ((1 << length) - 1) self.data = self.data & ((1 << length) - 1) self.search = 'P' self.bit_cnt = 0 return 1 else: return 0 注释这段代码
最新发布
07-15
<think>我们被要求为libsigrokdecode中的onewire_network解码器代码添加详细注释。该代码用于解码1-Wire网络层协议。下面将逐行注释代码,并解释其功能。 首先,我们回顾一下1-Wire协议的基本知识: - 1-Wire是一种单总线通信协议,由Dallas Semiconductor(现为Maxim Integrated)开发。 - 它使用单一数据线进行通信,支持多个设备连接到同一条总线。 - 通信过程包括复位脉冲、存在脉冲、ROM命令、功能命令和数据交换等阶段。 现在,我们开始注释代码: ```python import sigrokdecode as srd # 定义ROM命令的字典,每个命令对应一个列表,包含命令名称和下一个状态 # 命令格式:命令字节: [命令名称, 下一个状态] command = { 0x33: ['Read ROM', 'GET ROM'], # 读取ROM命令,下一步是获取ROM 0x0f: ['Conditional read ROM', 'GET ROM'], # 条件读取ROM,下一步是获取ROM 0xcc: ['Skip ROM', 'TRANSPORT'], # 跳过ROM命令,下一步进入传输状态 0x55: ['Match ROM', 'GET ROM'], # 匹配ROM命令,下一步是获取ROM 0xf0: ['Search ROM', 'SEARCH ROM'], # 搜索ROM命令,下一步进入搜索ROM状态 0xec: ['Conditional search ROM', 'SEARCH ROM'], # 条件搜索ROM,下一步进入搜索ROM状态 0x3c: ['Overdrive skip ROM', 'TRANSPORT'], # 过驱跳过ROM,下一步进入传输状态 0x69: ['Overdrive match ROM', 'GET ROM'], # 过驱匹配ROM,下一步是获取ROM 0xa5: ['Resume', 'TRANSPORT'], # 恢复命令,下一步进入传输状态 0x96: ['DS2408: Disable Test Mode', 'GET ROM'], # DS2408特定命令:禁用测试模式,下一步是获取ROM } # 定义解码器类,继承自srd.Decoder class Decoder(srd.Decoder): # 元数据 api_version = 3 # 使用sigrokdecode API版本3 id = 'onewire_network' # 解码器的唯一ID name = '1-Wire network layer' # 解码器的名称 longname = '1-Wire serial communication bus (network layer)' # 解码器的长名称 desc = 'Bidirectional, half-duplex, asynchronous serial bus.' # 描述 license = 'gplv2+' # 许可证 inputs = ['onewire_link'] # 输入协议层(来自链路层) outputs = ['onewire_network'] # 输出协议层(网络层) tags = ['Embedded/industrial'] # 标签 # 注解类型:这里只定义了一种文本类型的注解 annotations = ( ('text', 'Human-readable text'), ) def __init__(self): self.reset() # 初始化时调用reset方法 def reset(self): """重置解码器状态""" # 用于记录当前数据块的起始和结束采样点 self.ss_block = 0 # 当前数据块的起始采样点 self.es_block = 0 # 当前数据块的结束采样点 self.state = 'COMMAND' # 初始状态为等待命令 self.bit_cnt = 0 # 位计数器,用于统计当前已接收的位数 # 以下三个变量用于搜索ROM过程 self.search = 'P' # 搜索状态:P(原始位)、N(取反位)、D(主机发送的位) self.data_p = 0x0 # 存储原始位 self.data_n = 0x0 # 存储取反位 self.data = 0x0 # 存储当前接收的数据(在搜索过程中,存储主机发送的位) self.rom = 0x0000000000000000 # 存储ROM值(64位) def start(self): """注册输出类型""" # 注册Python输出和注解输出 self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) def putx(self, data): """辅助函数:输出注解""" # 将注解数据输出到注解输出 self.put(self.ss_block, self.es_block, self.out_ann, data) def puty(self, data): """辅助函数:输出Python数据""" # 将数据输出到Python输出 self.put(self.ss_block, self.es_block, self.out_python, data) def decode(self, ss, es, data): """解码方法,处理来自链路层的数据""" # 数据包格式:第一个元素是包类型,第二个是值 code, val = data # 状态机 # 如果收到复位/存在脉冲 if code == 'RESET/PRESENCE': # 重置搜索状态为P(原始位) self.search = 'P' self.bit_cnt = 0 # 位计数器清零 # 输出复位/存在脉冲的注解 self.put(ss, es, self.out_ann, [0, ['Reset/presence: %s' % ('true' if val else 'false')]]) # 输出Python数据:复位/存在脉冲 self.put(ss, es, self.out_python, ['RESET/PRESENCE', val]) # 状态重置为等待命令 self.state = 'COMMAND' return # 目前只对'RESET/PRESENCE'和'BIT'类型的数据包感兴趣 if code != 'BIT': return # 根据当前状态处理位数据 if self.state == 'COMMAND': # 接收并解码ROM命令(8位) # onewire_collect函数收集8位数据,返回1表示收集完成,0表示未完成 if self.onewire_collect(8, val, ss, es) == 0: return # 如果未完成,则返回等待下一位 # 检查命令是否在预定义的字典中 if self.data in command: # 输出命令注解 self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, command[self.data][0])]]) # 转换到下一个状态(由字典定义) self.state = command[self.data][1] else: # 未识别的命令 self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, 'unrecognized')]]) self.state = 'COMMAND ERROR' # 进入错误状态 elif self.state == 'GET ROM': # 获取64位ROM地址(包括8位家族码,48位序列号,8位CRC) if self.onewire_collect(64, val, ss, es) == 0: return # 保存ROM值 self.rom = self.data & 0xffffffffffffffff # 输出ROM注解 self.putx([0, ['ROM: 0x%016x' % self.rom]]) # 输出Python数据:ROM self.puty(['ROM', self.rom]) # 进入传输状态(后续为功能命令和数据) self.state = 'TRANSPORT' elif self.state == 'SEARCH ROM': # 搜索ROM过程,需要收集64位(但过程与普通收集不同) if self.onewire_search(64, val, ss, es) == 0: return # 保存ROM值 self.rom = self.data & 0xffffffffffffffff # 输出ROM注解 self.putx([0, ['ROM: 0x%016x' % self.rom]]) # 输出Python数据:ROM self.puty(['ROM', self.rom]) # 进入传输状态 self.state = 'TRANSPORT' elif self.state == 'TRANSPORT': # 传输状态:处理功能命令和数据(以字节为单位) if self.onewire_collect(8, val, ss, es) == 0: return # 输出数据注解 self.putx([0, ['Data: 0x%02x' % self.data]]) # 输出Python数据:数据字节 self.puty(['DATA', self.data]) elif self.state == 'COMMAND ERROR': # 命令错误状态:收集8位数据并输出 if self.onewire_collect(8, val, ss, es) == 0: return self.putx([0, ['ROM error data: 0x%02x' % self.data]]) # 数据收集函数:用于收集固定长度的位数据 def onewire_collect(self, length, val, ss, es): """收集固定长度的位数据""" # 如果是新序列的开始(位计数器为0),则记录起始采样点 if self.bit_cnt == 0: self.ss_block = ss # 将当前位值val放入data的对应位置(从低位开始) # 注意:这里按顺序收集,第一个接收的位在最低位(LSB first) self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) self.bit_cnt += 1 # 位计数器加1 # 如果已经收集到指定长度的位 if self.bit_cnt == length: # 记录结束采样点 self.es_block = es # 确保data只保留length位(屏蔽高位) self.data = self.data & ((1 << length) - 1) self.bit_cnt = 0 # 重置位计数器 return 1 # 返回1表示收集完成 else: return 0 # 返回0表示未完成 # 搜索ROM专用函数:用于处理搜索ROM过程中的位数据 def onewire_search(self, length, val, ss, es): """处理搜索ROM过程的三步(读原始位、读取反位、写选择位)""" # 如果是新序列的开始且状态为P(原始位),记录起始采样点 if (self.bit_cnt == 0) and (self.search == 'P'): self.ss_block = ss # 根据当前搜索状态处理 if self.search == 'P': # 主机读取原始位(来自从机) # 将位值放入data_p的对应位置 self.data_p = self.data_p & ~(1 << self.bit_cnt) | (val << self.bit_cnt) # 进入下一个状态:读取取反位 self.search = 'N' elif self.search == 'N': # 主机读取取反位(来自从机) self.data_n = self.data_n & ~(1 << self.bit_cnt) | (val << self.bit_cnt) # 进入下一个状态:主机将发送选择位 self.search = 'D' elif self.search == 'D': # 主机发送选择位(决定搜索路径) # 将主机发送的位值放入data的对应位置 self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) # 回到P状态,准备处理下一位 self.search = 'P' # 位计数器加1(因为完成了一个完整的三步) self.bit_cnt += 1 # 如果已经处理完指定长度(64位) if self.bit_cnt == length: # 记录结束采样点 self.es_block = es # 屏蔽高位,只保留length位 self.data_p = self.data_p & ((1 << length) - 1) self.data_n = self.data_n & ((1 << length) - 1) self.data = self.data & ((1 << length) - 1) # 重置搜索状态为P self.search = 'P' self.bit_cnt = 0 # 重置位计数器 return 1 # 返回1表示完成 else: return 0 # 返回0表示未完成 ``` ### 代码功能总结 该解码器实现了1-Wire网络层协议的状态机,主要处理以下内容: 1. **复位/存在脉冲**:重置总线并检测从机存在。 2. **ROM命令**:解析主机发送的命令(如读取ROM、匹配ROM、搜索ROM等)。 3. **ROM数据**:处理64位ROM地址的传输。 4. **数据传输**:在ROM命令之后,处理功能命令和数据字节。 ### 状态机说明 - **COMMAND**:等待并解析8位ROM命令。 - **GET ROM**:收集64位ROM数据(用于读ROM、匹配ROM等命令)。 - **SEARCH ROM**:处理搜索ROM过程(需要三步:读原始位、读取反位、写选择位)。 - **TRANSPORT**:传输功能命令和数据(以字节为单位)。 - **COMMAND ERROR**:处理未识别命令后的数据。 ### 注解输出 解码器会输出人类可读的注解信息,包括: - 复位/存在脉冲 - ROM命令及其名称 - ROM数据(64位地址) - 传输层的数据字节 ### Python输出 解码器还会输出Python数据,包括: - `['RESET/PRESENCE', val]`:复位/存在脉冲(val为True/False) - `['ROM', rom]`:64位ROM值 - `['DATA', data]`:传输层的数据字节 ### 搜索ROM过程 搜索ROM过程是1-Wire协议中用于识别总线上多个从机设备的一种算法。该解码器通过`onewire_search`函数处理搜索ROM的三步: 1. 主机读取从机的原始地址位(P)。 2. 主机读取从机的取反地址位(N)。 3. 主机根据P和N决定搜索路径,并发送一个选择位(D)。 通过64次这样的三步操作,主机可以识别一个从机的完整ROM地址。 ### 参考 [^1]: 编码器-解码器架构在深度学习中的应用(类比于本解码器的状态机设计) [^2]: Python的OS模块提供了与操作系统交互的功能,而本解码器则是与硬件交互的底层协议解析 [^3]: 神经网络手写数字识别代码展示了模式识别,而本解码器识别1-Wire协议模式 ### 相关问题 1. 1-Wire协议中的搜索ROM算法具体是如何工作的? 2. 如何扩展此解码器以支持特定1-Wire设备(如DS18B20)的功能命令? 3. 在1-Wire网络层之上,传输层(功能命令)是如何工作的? 4. 如何处理1-Wire总线上的冲突检测?</think>以下是对 `onewire_network` 解码器代码的逐行详细注释和功能解析: ```python import sigrokdecode as srd # 导入sigrok解码库 # 定义1-Wire ROM命令字典:命令码 -> [命令名称, 下一状态] command = { 0x33: ['Read ROM', 'GET ROM'], # 读取ROM命令 0x0f: ['Conditional read ROM', 'GET ROM'], # 条件读取ROM 0xcc: ['Skip ROM', 'TRANSPORT'], # 跳过ROM直接访问设备 0x55: ['Match ROM', 'GET ROM'], # 匹配特定ROM设备 0xf0: ['Search ROM', 'SEARCH ROM'], # 搜索总线上的设备 0xec: ['Conditional search ROM', 'SEARCH ROM'], # 条件搜索ROM 0x3c: ['Overdrive skip ROM', 'TRANSPORT'], # 过驱模式跳过ROM 0x69: ['Overdrive match ROM', 'GET ROM'], # 过驱模式匹配ROM 0xa5: ['Resume', 'TRANSPORT'], # 恢复之前中断的操作 0x96: ['DS2408: Disable Test Mode', 'GET ROM'], # DS2408专用命令 } class Decoder(srd.Decoder): # === 解码器元数据 === api_version = 3 # 使用sigrokdecode API版本3 id = 'onewire_network' # 解码器唯一标识符 name = '1-Wire network layer' # 解码器名称 longname = '1-Wire serial communication bus (network layer)' # 完整名称 desc = 'Bidirectional, half-duplex, asynchronous serial bus.' # 描述 license = 'gplv2+' # 许可证类型 inputs = ['onewire_link'] # 输入数据来自链路层 outputs = ['onewire_network'] # 输出到网络层 tags = ['Embedded/industrial'] # 应用领域标签 # 注解类型定义(可视化输出) annotations = ( ('text', 'Human-readable text'), # 人类可读文本注解 ) def __init__(self): self.reset() # 初始化时调用reset方法 def reset(self): """重置解码器状态""" self.ss_block = 0 # 当前数据块起始采样点 self.es_block = 0 # 当前数据块结束采样点 self.state = 'COMMAND' # 初始状态:等待命令 self.bit_cnt = 0 # 当前数据位计数器 self.search = 'P' # 搜索ROM时的状态机(P=原始位,N=取反位,D=决策位) self.data_p = 0x0 # 存储原始位值 self.data_n = 0x0 # 存储取反位值 self.data = 0x0 # 当前接收的数据 self.rom = 0x0000000000000000 # 存储64位ROM地址 def start(self): """注册输出接口""" self.out_python = self.register(srd.OUTPUT_PYTHON) # Python数据输出 self.out_ann = self.register(srd.OUTPUT_ANN) # 可视化注解输出 def putx(self, data): """辅助函数:输出可视化注解""" self.put(self.ss_block, self.es_block, self.out_ann, data) def puty(self, data): """辅助函数:输出Python数据""" self.put(self.ss_block, self.es_block, self.out_python, data) def decode(self, ss, es, data): """核心解码方法 ss: 起始采样点 es: 结束采样点 data: 来自链路层的数据包 (code, value) """ code, val = data # 解包数据 (类型, 值) # === 状态机处理 === if code == 'RESET/PRESENCE': # 处理复位/存在脉冲 self.search = 'P' # 重置搜索状态 self.bit_cnt = 0 # 重置位计数器 # 输出注解 self.put(ss, es, self.out_ann, [0, ['Reset/presence: %s' % ('true' if val else 'false')]]) # 输出Python数据 self.put(ss, es, self.out_python, ['RESET/PRESENCE', val]) self.state = 'COMMAND' # 进入命令等待状态 return # 仅处理BIT类型数据包 if code != 'BIT': return # === 命令处理状态 === if self.state == 'COMMAND': # 收集8位命令数据 if self.onewire_collect(8, val, ss, es) == 0: return # 未收齐8位则等待 if self.data in command: # 检查是否为有效命令 cmd_info = command[self.data] self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, cmd_info[0])]]) self.state = cmd_info[1] # 转换到命令指定的状态 else: # 无效命令处理 self.putx([0, ['ROM command: 0x%02x \'%s\'' % (self.data, 'unrecognized')]]) self.state = 'COMMAND ERROR' # 进入错误状态 # === ROM读取状态 === elif self.state == 'GET ROM': # 收集64位ROM数据 (8字节) if self.onewire_collect(64, val, ss, es) == 0: return self.rom = self.data & 0xffffffffffffffff # 保存64位ROM地址 self.putx([0, ['ROM: 0x%016x' % self.rom]]) # 输出注解 self.puty(['ROM', self.rom]) # 输出Python数据 self.state = 'TRANSPORT' # 进入数据传输状态 # === ROM搜索状态 === elif self.state == 'SEARCH ROM': # 使用特殊方法收集64位ROM数据 if self.onewire_search(64, val, ss, es) == 0: return self.rom = self.data & 0xffffffffffffffff # 保存64位ROM地址 self.putx([0, ['ROM: 0x%016x' % self.rom]]) # 输出注解 self.puty(['ROM', self.rom]) # 输出Python数据 self.state = 'TRANSPORT' # 进入数据传输状态 # === 数据传输状态 === elif self.state == 'TRANSPORT': # 收集8位数据 if self.onewire_collect(8, val, ss, es) == 0: return self.putx([0, ['Data: 0x%02x' % self.data]]) # 输出注解 self.puty(['DATA', self.data]) # 输出Python数据 # === 命令错误状态 === elif self.state == 'COMMAND ERROR': # 收集8位错误数据 if self.onewire_collect(8, val, ss, es) == 0: return self.putx([0, ['ROM error data: 0x%02x' % self.data]]) # 输出错误数据 # === 数据收集方法 === def onewire_collect(self, length, val, ss, es): """收集固定长度的位数据 length: 需要收集的位数 val: 当前位的值 (0/1) ss: 起始采样点 es: 结束采样点 返回1表示收集完成,0表示未完成 """ if self.bit_cnt == 0: # 新序列开始 self.ss_block = ss # 记录起始点 # 将位值存入数据寄存器 (LSB优先) self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) self.bit_cnt += 1 # 位计数器递增 if self.bit_cnt == length: # 达到指定长度 self.es_block = es # 记录结束点 self.data = self.data & ((1 << length) - 1) # 屏蔽高位 self.bit_cnt = 0 # 重置计数器 return 1 # 收集完成 return 0 # 收集未完成 # === ROM搜索专用方法 === def onewire_search(self, length, val, ss, es): """处理ROM搜索的三步过程 1. 主机读取原始位 (P) 2. 主机读取取反位 (N) 3. 主机发送选择位 (D) """ if self.bit_cnt == 0 and self.search == 'P': # 新序列开始 self.ss_block = ss # 记录起始点 if self.search == 'P': # 原始位阶段 self.data_p |= val << self.bit_cnt # 存储原始位 self.search = 'N' # 进入下一阶段 elif self.search == 'N': # 取反位阶段 self.data_n |= val << self.bit_cnt # 存储取反位 self.search = 'D' # 进入下一阶段 elif self.search == 'D': # 决策位阶段 self.data |= val << self.bit_cnt # 存储主机决策位 self.search = 'P' # 重置为原始位阶段 self.bit_cnt += 1 # 完成一位处理 if self.bit_cnt == length: # 完成所有位处理 self.es_block = es # 记录结束点 # 屏蔽高位数据 self.data_p &= (1 << length) - 1 self.data_n &= (1 << length) - 1 self.data &= (1 << length) - 1 self.search = 'P' # 重置搜索状态 self.bit_cnt = 0 # 重置位计数器 return 1 # 处理完成 return 0 # 处理未完成 ``` ### 解码器功能详解 #### 1. 核心状态机 ```mermaid stateDiagram-v2 [*] --> COMMAND COMMAND --> GET_ROM: 读ROM命令 COMMAND --> SEARCH_ROM: 搜索ROM命令 COMMAND --> TRANSPORT: 跳过ROM命令 COMMAND --> COMMAND_ERROR: 无效命令 GET_ROM --> TRANSPORT: 完成64位ROM接收 SEARCH_ROM --> TRANSPORT: 完成64位ROM搜索 TRANSPORT: 数据传输状态 COMMAND_ERROR: 错误处理状态 ``` #### 2. 关键数据结构 - **ROM命令字典**:将1-Wire命令字节映射到操作描述和下一状态 - **解码器状态**: - `COMMAND`:等待/解析命令 - `GET ROM`:接收64位ROM地址 - `SEARCH ROM`:执行ROM搜索算法 - `TRANSPORT`:传输应用层数据 - `COMMAND ERROR`:处理无效命令 #### 3. 核心算法 1. **复位/存在检测** 处理主设备发出的复位脉冲和从设备的响应脉冲,初始化通信序列[^1]。 2. **ROM命令解析** 解析8位命令字节,根据命令类型转换状态: ```python if self.data in command: # 0x33, 0x55等 self.state = command[self.data][1] # 切换到下一状态 ``` 3. **ROM地址处理** 两种收集方式: - **直接收集**:用于`Read ROM`/`Match ROM`(`onewire_collect`) - **三步搜索**:用于`Search ROM`(`onewire_search`) ```python # 三步搜索过程 P: 读取原始位 → N: 读取取反位 → D: 发送选择位 ``` 4. **数据传输** 在`TRANSPORT`状态下以字节为单位处理应用层数据: ```python self.puty(['DATA', self.data]) # 输出数据字节 ``` #### 4. 输出系统 - **可视化注解**:用于PulseView等工具的协议解码显示 - **Python输出**:提供结构化数据供上层处理 ```python ['RESET/PRESENCE', True] # 复位脉冲 ['ROM', 0xAABBCCDDEEFF0011] # 64位ROM地址 ['DATA', 0xBE] # 应用层数据 ``` ### 协议解码示例 典型的1-Wire通信序列: 1. 主设备发送复位脉冲(480μs) 2. 从设备回复存在脉冲(60-240μs) 3. 主设备发送ROM命令(如0x33读取ROM) 4. 从设备返回64位ROM地址 5. 主设备发送功能命令(如0x44启动温度转换) ### 相关问题 1. 1-Wire协议中的ROM搜索算法如何实现设备冲突解决? 2. 如何扩展此解码器支持DS18B20温度传感器的功能命令? 3. 在1-Wire网络层中,过驱模式(Overdrive)如何提高通信速度? 4. 多设备1-Wire网络中如何优化ROM搜索过程? 5. 1-Wire协议的CRC校验机制在解码器中如何实现? [^1]: 编码器-解码器架构在协议解析中的应用参考 [^2]: Python的底层操作接口设计与本解码器的硬件交互模式类似 [^3]: 模式识别技术在协议解码中的应用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GGood_Name

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值