DSview的SPI解码实例解析
编译流程
问:如果我们改好一个文件怎么编译?
法一:
- 编译安装咱们改好的文件
cd libsigrokdecode4DSL
sudo make install
- 编译dsview
cd ..
cd DSView
cmake .
- 运行,建议在命令行输入
DSview
运行,这样可以看到print等相关打印内容,方便查找错误。
法二:
在/usr/local/share/libsigrokdecode4DSL/decoders/
目录下,增删改文件后,直接打开DSview即可。
程序解析
_init_.py
此文件的代码只有一句,from .pd import Decoder
但是解码器首先调用的__init__.py
,所以通过他引入pd.py
.
pd.py
首先需要import解码器相关的方法。
import sigrokdecode as srd
from collections import namedtuple
关于类函数Decoder重要成员变量及函数介绍
1.父类需定义的变量
以下是父类需要一开始定义的变量,其中很多包含在前端UI界面显示所需的选项及注释。(以下代码中包含各项重要内容的解释说明)
class Decoder(srd.Decoder):
api_version = 2 # api版本,pulseview为3,DSview默认为2(1.00以上版本支持3)
id = '0:spi'
name = '0:SPI'
longname = 'Serial Peripheral Interface'# 长名字,在有足够空间的情况下前端会显示
desc = 'Full-duplex, synchronous, serial bus.'
license = 'gplv2+'
inputs = ['logic'] # 输入方式,相关的采样编号和各个channel电平情况会输入到此logic中
outputs = ['spi']
channels = (
{'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
)
# channels和optional_channels显示通道注释及选项,可选择可用通道
#logic输出信号会根据channel和optional_channels顺序进行输出,即clk,miso,mosi,cs
optional_channels = (
{'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
{'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
{'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
)
#options可添加一些用户输入的一些参数,方便程序根据客户需要进行变化,可直接使用,例如self.options['wordsize']
options = (
{'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
'values': ('active-low', 'active-high')},
{'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
'values': (0, 1)},
{'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
'values': (0, 1)},
{'id': 'bitorder', 'desc': 'Bit order',
'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
{'id': 'wordsize', 'desc': 'Word size', 'default': 8},
)
#annotations:单个注释名
annotations = (
('106', 'miso-data', 'MISO data'),#0
('108', 'mosi-data', 'MOSI data'),#1
('107', 'miso-bits', 'MISO bits'),#2
('109', 'mosi-bits', 'MOSI bits'),#3
('1000', 'warnings', 'Human-readable warnings'),#4
('250', 'tips', 'human tips'),#5
#(颜色编号,id, name)
)
#annotations_rows:注释行,成员包括其name和上述注释名编号
annotation_rows = (
#(id, name, (x, ) x:是annotations的编号,顺序如上面备注,且一个注释行可以有多个注释
('miso-data', 'MISO data', (0,)),
('miso-bits', 'MISO bits', (2,)),
('mosi-data', 'MOSI data', (1,)),
('mosi-bits', 'MOSI bits', (3,)),
('other', 'Other', (4, 5)),
)
2.解码器主要使用的函数
协议解码器类函数class Decoder(srd.Decoder):
主要有以下几个重要成员构成:
__init__(self)
:初始化各类参数,例如CLK,CS等等。
start(self)
:在解码开始之前调用此函数。这是register()输出类型的位置,检查用户提供的PD选项的有效性,等等。
decode(self)
:(DSview1.00以上版本),由于有了wait()
等相关的方法,所以不在需要循环去读取各个采样点的值,其内容为客户自己编写的解码逻辑。
decode(self, ss, es, logic)
:(DSview0.99及一下版本)这是一个由libsigrokdecode后端调用的函数,讲logic的数据进行处理。ss:start sample, es:end sample,logic:输入的量,包含采样编号及电平信息。
3.logic信息结构
逻辑分析仪通过logic会输出采样编号和各个引脚高低电平的信息,采样顺序根据channel
和optional_channels
的顺序排序,例如:
设置如下的Decoder的metadata
channels = (
{'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
)
optional_channels = (
{'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
{'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
{'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
)
则logic接收的元组信息为:
samplenum | 第一组数据 | 第二组数据 | … | ||||||
---|---|---|---|---|---|---|---|---|---|
bit | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | … |
channel | clk | miso | mosi | cs | clk | miso | mosi | cs | … |
所以我们接收的数据只有samplenum和bit两部分的数据,而bit为何值,对应的我们channel
和optional_channels
定义的顺序,可任由我们随意添加修改。
clk和cs建议分别放前面和后面。
4.输出
上述已经解决了显示,输入,但我们解析完输入内容后如何输出到界面中呢?像这样:
如果你想单个注释行列表(annotation_rows)里只有一个项目的话,及annotation_rows中('miso-data', 'MISO data', (0,)),
annotation_rows = (
#(id, name, (x, ) x:是annotations的编号,顺序如上面备注
('miso-data', 'MISO data', (0,)),
('miso-bits', 'MISO bits', (2,)),
('mosi-data', 'MOSI data', (3,4)),
)
系统提供了put()的方法,为了方便,我们只需用到out_annself.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
put(采样起点,采样终点,self.out_ann, [编号,[内容]])
对于一个注释行列表有多个项目,如('mosi-data', 'MOSI data', (3,4)),
self.put(self.ss_block, es, self.out_ann, [1, ['%02X' % self.mosidata, 'data', 'd']])
put(采样起点,采样终点,self.out_ann, [编号,[' name ', ' long name', 'short name']])
此处的编号对应annotation的编号,颜色可在annotation中修改。
至此,根据上述4点内容,可以作为“工具”足够让我们使用了,接下来解析的“方法”可以自行去写,如果还不知道如何写,可继续阅读下面解码功能实现过程
×××××××××××××××××××××××施工中!!!!!!×××××××××××××××××××××××××
5.关于logic采集
在新版本中,添加的wait()
等的方法,更好地采集我们所需条件下的样本。
1.self.wait()
wait(wait_cond)
的意思是程序一直阻塞等待,直到满足wait_cond字典中任意一个元素,则返回对应的引脚信息。
引脚状态条件
wait_cond字典中的键为channel为PD通道索引所对应的键(即第三节所对应的顺序),在这样的情况下,值可以是:l
: 引脚为低电平h
: 引脚为高电平r
: 上升沿f
: 下降沿e
: 跳变沿(上升和下降沿)s
: 与e
状态相反,没有边沿,并且当前和先前的引脚值都为低(或都为高)。
样本跳过条件
self.wait({'skip':100})
:每数到100个样本后返回self.wait({'skip':20 *(1000 / self.samplerate)})
: 每20ms返回一个样本
2.self.matched()
当解码器通过self.wait()
请求前端等待多个条件时,当该调用返回时,PD仅知道至少一个条件已匹配。但是,在大多数情况下,它还需要知道哪些条件匹配(或不匹配)。
这是self.matched提供的信息。它是布尔值(True或False)的元组,始终包含与上次self.wait()调用中存在的条件一样多的条目。对于每个条件,各自的布尔值表示此特定条件是否匹配。
pins = self.wait([{0: 'r'}, {2: 'h'}, {'skip': 1000}])
if self.matched == (True, True, False):
print ("满足channel0和channel2且不满足计数到1000个样本值的条件")
if self.matched & 0b1 << 1
print ("只满足channel2为高电平的条件")
3.self.samplenum
self.samplenum是一个特殊属性,对于协议解码器来说是只读的,并且只能由libsigrokdecode后端设置。
self.samplenum的值始终是最后一次self.wait()返回后的当前绝对采样数(从0开始)。
4.输出设置
我们从上了解到,输出我们需要采样起点、采样终点和输出数据,这样我们就能在起点和终点划分一个小区块显示数据了。
那么如何设置呢?
输出数据: 首先是数据,我们可以通过移位的操作,以8位为例,如果数据从高位存储,我们就将读到的数据偏移7位,存放在自定义的self.data
里面,以此类推,第二个数据偏移6位存放,第三个数据…直到存放完成后,输出self.data
。
采样起点终点:官方建议我们设置一个self.bits[]
的一个二维列表,存放内容如下:
数据 | … | ||||||||
---|---|---|---|---|---|---|---|---|---|
序号 | 0 | 1 | 2 | 3 | 4 | 5 | … | 7 | |
IO | 1 | 0 | 0 | 1 | 0 | 1 | … | 1 | |
samplenum | 108 | 107 | 106 | 105 | 104 | 103 | … | 101 | |
es | 109 | 108 | 107 | 106 | 105 | 104 | … | 102 |
以一个8位数据为例,每次我们读到一个字节的数据,便bits.insert(0, [miso, self.samplenum, es])
,也就是从序号0开始插入,旧值就会往后移动,最后要输出8位数据时,设置采样起点就为bits[-1][1]
,即为表格中加粗的101的采样编号,设置采样终点就为bits[0][2]
,即为表格中加粗的109的采样编号。
总结
最后总结一下编码逻辑:
1.提前定义好所需的相关参数
2.__init__(self)
中定义相关的成员变量,可当c中全局变量来用。
3.start(self)
设置初始化相关,此时,你已经在用户选择的条件之后,可以获取options
的相关内容。
4.decode(self)
根据用户信息设置相关属性,使用wait的方法,设置解码器采样条件。
5.利用matched进一步筛选相关信息。
6.达到筛选条件后,获取对应的data和采样起点终点,用put方式在对应位置输出对应的data。
目前由于0.99版本以下有局限性,比如channel较多的情况下无法同时锁定2个以上的条件,导致解码器会出现偏差,数据不准确等情况。
目前仅仅作介绍
DSview0.99 以下版本
在def decode(self, ss, es, logic):
,logic包含了所有的采样编号和电平信息,需要我们循环去读取,在pulseview封装了wait()
的方法,可以等到某个变量达到某个要求时再进行读取。
但在DSview中,没有wait()
的方法,需要自己手动设计,大致原理是保存上一个采样号的电平信息和本次采样号的电平信息作对比,如果有变化,说明发生上升沿/下降沿的变化,代码如下:
def decode(self, ss, es, logic):
# Either MISO or MOSI can be omitted (but not both). CS# is optional.
for (self.samplenum, pins) in logic:
(clk, miso, mosi, cs) = pins #讲pins中每个电平值分别放入各个变量中,顺序上一章节已做介绍
if not self.pin_checked:
self.have_miso = (miso in (0, 1))
self.have_mosi = (mosi in (0, 1))
self.have_cs = (cs in (0, 1))
# Either MISO or MOSI (but not both) can be omitted.
if not (self.have_miso or self.have_mosi):
raise ChannelError('Either MISO or MOSI (or both) pins required.')
#该模式设定已经在options定义,即客户可选定cpol和cpha,具体参见https://blog.csdn.net/ce123_zhouwei/article/details/6923293
if (self.mode == 0 or self.mode == 3):
self.exp_oldclk = 0
self.exp_clk = 1
else:
self.exp_oldclk = 1
self.exp_clk = 0
self.logic_mask = 0b1001 if self.have_cs else 0b0001
self.exp_logic = 0b0000 if self.active_low else 0b1000
self.asserted_oldcs = 1 if self.active_low else 0
self.asserted_cs = 0 if self.active_low else 1
self.deasserted_oldcs = 0 if self.active_low else 1
self.deasserted_cs = 1 if self.active_low else 0
self.pin_checked = True
logic.logic_mask = self.logic_mask
logic.cur_pos = self.samplenum
logic.edge_index = -1
#logic.itercnt += 1
# Tell stacked decoders that we don't have a CS# signal.
#if not self.no_cs_notification and not self.have_cs:
# self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
# self.no_cs_notification = True
if (self.oldcs, cs) == (self.asserted_oldcs, self.asserted_cs):
#self.ss_transfer = self.samplenum
#self.misobytes = []
#self.mosibytes = []
self.reset_decoder_state()
elif (self.oldcs, cs) == (self.deasserted_oldcs, self.deasserted_cs):
#self.put(self.ss_transfer, self.samplenum, self.out_python,
# ['TRANSFER', self.mosibytes, self.misobytes])
logic.exp_logic = self.exp_logic
cs = self.asserted_oldcs
logic.logic_mask = 0b1000
logic.edge_index = 3
elif not self.have_cs or cs == self.asserted_cs:
if (self.oldclk, clk) == (self.exp_oldclk, self.exp_clk):
#Sample on rising/falling clock edge
self.handle_bit(miso, mosi, clk, cs)
self.oldclk, self.oldcs = clk, cs
来源:CSDN
作者:神气哄哄de陈同学
链接:https://blog.csdn.net/weixin_42942530/article/details/103959854