版本: 1.0.2
一、 前言
在DSView安裝目錄下,有一個decoders文件夾,里面有許多目錄是以各種協議名稱命名的。每個目錄下有至少2個擴展名為.py的文件,這些都是python代碼文件。在linux系統下,decoders目錄位于”/usr/local/share/libsigrokdecode4DSL”下。
DSView通過底層python解釋器執行python代碼,對邏輯分析儀的數據進行解析,按各種算法得出需要的結果。每個協議目錄下必須存在兩個文件:
- _init_.py,用于發現模塊,這個文件名左右兩邊各有兩個下劃線,這個細節要注意;
- pd.py,用于編寫主要的邏輯代碼;
這兩個文件如何編寫,請仔細閱讀下面的內容。在1.2.0以上的新版本DSView中,decoders下的有一個名為example目錄為示例代碼。
二、python入門
python語言是一個解釋執行的語言。在官方網站下載并安裝好python,就可以進行開發了。
新建一個文本文件,里邊輸入一行文字:
print('Hello,world!')
保存為 test.py,然后在命令行里輸入
python test.py
將會輸出“Hello,world!”
這里的python入門只是為了幫助一些讀者能夠順利閱讀部分協議代碼,它所講的python知識還不夠全面和深入,需要讀者自行通過其它方式獲得python資料,以便提升自己的python編程能力。
- 變量定義
age = 1
name = “Tom” - 數值
1, 1.11, 1000等都屬數值型數據 - 字符串
以單引號或雙引號括起來的一串字符,表示字符串,如
’abc’
“name” - 列表
[]
[1,2,3]
[1,”abc”,”name”,[7,8,9]]
列表里的元素用逗號隔開,上面的第一個列表是空列表,第二個列表全是數字,第三個列表有多種類型的元素,有數值、字符串、列表。
a = [1,2,3]
變量a是一個列表,通過a[n]方式讀取列表里的元素。n的取值從0開始到不超過且不等于列表長度的整數
a[2] = 666
將第3個元素設置成666,列表里的內容可修改
i = a[1]
取列表a的第二個元素賦給變量i - 字典
d = {‘age’:20, ‘name’:’Tome’, ‘data’:[7,8,9]}
字典里的每一項用一對鍵和值表示。如’age’:20
d[‘name’] = ‘Same’
將字典d的name值設置成’Same’
s = d[‘name’]
取字典d的name值賦給變量s
字典里的鍵名可以是數字、字符串、元組等類型,如:
{1:'張三'}
{'name':''張三"}
{(1,2,3): "張三"} - 元組
()
(1,2,3)
(2,’abc’)
元組跟列表一樣,不同的是用()括起來,元組只能讀,不能修改。
注意:當元組只有一個項時,要多打一個逗號,如:
(1,) - 函數
def call(): #普通函數
def call(self): #類成員函數,第一個參數是必須的
def call(a,b,c): #帶三個參數的函數
三、新建協議
- 新建協議目錄
找到存放所有協議的decoders目錄。widnows下,它在DSView的安裝目錄里;
在linux下,它在
/usr/local/share/libsigrokdecode4DSL
打開decoders目錄,新建一個子目錄,并給目錄取名字,要求是能體現協議名稱的名字。這里,我們的示列協議名為”lala”。 - __init.py文件
在bala目錄下新建文件“__init.py”,加入一行如下代碼并保存:
from .pd import Decoder
- pd.py文件
在bala目錄下,建新pd.py文件,用來編寫主要的代碼。
四、框架代碼模板
??以下是解碼協議代碼框架,寫在pd.py文件里。所有協議的代碼核心部分是一樣的。
下面從c模塊繼承一個類:
import sigrokdecode as srd class Decoder(srd.Decoder): api_version = 3 ##協議標識,必須唯一 id = 'bala' ##協議名稱, 不一定要求跟標識一致 name = 'bala' ##協議長名稱 longname = 'bala protocal' ##簡介內容 desc = 'This is an example' ##開源協議 license = 'gplv2+' #第一句是導出c底層的模塊 #第二句是定義一個類,繼承自c底層的類
inputs = ['logic'] #接收的輸入的數據源,默認是是邏輯分析儀數據 #在多層協議模式下,可使用上層協議的輸出名
outputs = ['bala'] #輸出的數據源名,在多層協議模式下, #其它協議可以使用這個輸出名指定數據輸入來源
tags = ['Embedded/industrial'] #協議的適用范圍標簽
channels = ( {'id': 'c1', type:0, 'name': 'c1', 'desc': 'chan1-input'}, ) #必須要綁定的通道 #id:通道標識, 任意命名 #'type':類型,有: -1:COMMON,0:SCLK,1:SDATA,2:ADATA #name:標簽名 #desc:該通道的說明
optional_channels = ( {'id': 'c2', type:0, 'name': 'c2', 'desc': 'chan2-input'}, ) #可選通道,其它跟上面的一樣
options = ( {'id': 'debug_bits','desc': 'Print each bit', 'default': 'no', 'values': ('yes', 'no')}, {'id': 'wordsize', 'desc': 'Data wordsize', 'default': 0}, ) #提供給用戶通過界面設置的參數 #根據業務需要來定義 #通過'self.options[id]'取值,id就是各個項的id值, #比如下面的'wordsize' #上面第一項是下拉框,第二項是輸入框
annotations = ( ('1', 'data1', 'test1'), ('2', 'data2', 'test2'), ('222', 'data3', 'test3'), ) #解析結果項定義 #annotations里每一項可以有2到3個屬性, #當有3個屬性時,第一個表示類型 #類型對應0-16個顏色,當類型范圍 #在200-299時,將繪制邊沿箭頭
annotation_rows = ( ('lab1', 'row1', (0,)), ('lab2', 'row2', (1,2)), ) #解析結果行定義 #(0,)表示可輸出第1個定義的annotations類型 #(1,2)表示可輸出第2個和第3個定義的annotations類型
def __init__(self): self.reset() #這里調用reset函數,完成一些變量的初始化 #構造函數,自動被調用, #這里調用reset函數完成一個變量的初始化
def reset(self): self.count = 0 #初始化函數,這里定義類的私有變量
def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) #這里注冊消息類型 #開始執行解碼任務時,由c底層代碼自動調用一次 #這里,完成一些解碼結果項annotation類型的注冊 #有: OUTPUT_ANN、OUTPUT_PYTHON、 #OUTPUT_BINARY、OUTPUT_META #register函數是c底層類提供的
def put_ann(self,a,b,ann,data): self.put(a, b, self.out_ann, [ann, data]) #輸出內容 #a,b為采樣位置的起點和終點 #ann為annotations定義的項序號 #data是一個列表,列表里有1到3個字符串,它們將顯示到屏幕 #annotation輸出到哪一行由annotation_rows決定 #self.out_ann就是上面注冊的消息類型了 #self.put是c底層類提供的函數
def decode(self): while True: (a,b)=self.wait({0:'r'}) #一直調用,直到所有數據處理完 #解碼任務開始時由c底層代碼調用 #這里不斷循環等待所有采樣數據被處理完成 #wait函數參數詳解: #wait函數可帶參數,也可以不帶參數, #不帶參數時將返回每個采樣數據 #參數'{0:'r'}', 0表示匹配channels第1 #項綁定的通道,r表示查找向上邊沿 #wait函數可傳多個條件, #與條件:{0:'f',1:'r'}, #或條件:[{0:'f'},{1:'r'}] #h:高電平,l:低電平,r:向上邊沿, #f:向下邊沿, #e:向上沿或向下沿, #n:要么0,要么1 #wait函數前的變量(a,b),數量由定義 #的channels里的通道數決定,包括可選通道 #例如:共定義了4個通道, #則變成(a,b,c,d) = self.wait()
#c底層提供了兩個屬性: self.samplenum #當前wait()調用匹配結束的采樣點位置 self.matched #是一個uint64類型數值, #表示0到63個通道的匹配信息, #通過位運算來獲取具體信息。
到這里,解碼協議代碼框架模板結束。
五、應用示例
??在上面代碼框架的基礎上,我們接下來實現一個簡單的例子。具體是,通過解碼某一通道的數據,從一個向上邊沿開始到向下邊沿結束,輸出采樣點差值信息。奇數次輸出放在第二行,偶數次輸出放在第一行。具體編碼和說明如下:
def decode(self): times = 0 rising_sample = 0 flag_arr = [{0:'r'}, {0:'f'}] flag_dex = 0 while True: edge = flag_arr[flag_dex] (a,b) = self.wait(edge) if flag_dex == 0: flag_dex = 1 rising_sample = self.samplenum else: flag_dex = 0 times += 1 falling_sample = self.samplenum v = falling_sample - rising_sample s = '%02X' % v ann = times % 2 self.put_ann(rising_sample, falling_sample, ann, [s]) #times用于輸出次數計數,偶數次輸出到第一行,奇數次輸出到第二行 #ann = times % 2 對次數變量求余,其值在0和1中變換,調用put_ann時指定了annotation的序號 #再根據序號由annotation_rows決定輸出在哪一行 #邊沿條件就兩種:向上和向下,調用wait函數時,在這兩個邊沿條件中切換 #首次是取向上邊沿,然后再換成向下邊沿 #每次調用wait后,通過self.samplenum取采樣位置 #求出兩次的樣品位置差后,轉換成16進制的字符串,通過類函數put_ann輸出
六、解碼模塊工作原理
??通過c代碼和python代碼的互操作,將采樣數據交給python分析。經過一系列的處理,最終生成解碼結果,用于顯示以及供給上層協議作為分析的數據來源。解碼模塊的核心主要由以下部分組成:
- c底層包裝類Decoder
在c代碼里,給python提供一個經過包裝的基類,python可調用基類的一些方法,實現調用c代碼的目的。python端通過以下語句導出c代碼包裝的Decoder類:
import sigrokdecode as srd
python可訪問的Decoder基類的方法有:
- register方法
用于注冊python輸出到c底層的消息類型,有:
(1) OUTPUT_ANN,數據輸出到屏幕
(2) OUTPUT_PYTHON,數據輸出到上層協議
(3) OUTPUT_BINARY
(4) OUTPUT_META
python調用方式:
self.out_ann=self.register(srd.OUTPUT_ANN) self.out_py=self.register(srd.OUTPUT_PYTHON)
put方法
輸出數據到屏幕或上層協議,python調用方式:
self.put(a,b,self.out_ann,[0,['abc']])
其中,a、b為采樣點區間值,self.out_ann為注冊的消息類型,[0,[‘abc’]], 0為消息類型序號,參考之前的內容;[‘abc’]為消息內容了
wait方法
獲得上一次分析位置后的采樣數據,可通過參數指定邊沿查找條件。
調用方式: self.wait()
可指定參數,如:{0,’r’}表示第1個綁定的通道滿足向上邊沿的數據;{1,’f’}表示第2個綁定通道足向下邊沿的數據。其它條件標志還有:h、l、e、n。
可通過多個條件組成并和或的條件。并條件如:{0:’f’,1:’r’},或條件如:[{0:’f’},{1:’r’}]
- has_channel方法
用來叛斷某個通道是否綁定,python調用方式:
self.has_channel(0)
0是通道序號。
- 屬性
c底層類給python提供了兩個屬性:
a. self.samplenum,wait()調用后的采樣數據位置;
b. self.matched,wait()調用后道通匹配信息;
- python層包裝類Decoder
繼承至c底層包裝的類,由c底層實例化并調用python類的方法,代碼如下:
import sigrokdecode as srd class Decoder(srd.Decoder):
子類Decoder的方法有:
- reset方法
這里做一些變量值的重置,以及類的私有變量的定義。變量的定義如:
self.name = 'abc'
- start方法
在解碼任務開始執行前,c底層代碼會調用一次start函數,這里主要是做一些初始化工作,比如注冊消息類型,如:
self.out_ann = self.register(srd.OUTPUT_ANN) self.out_py = self.register(srd.OUTPUT_PYTHON)
- decode方法
由c底層調用,在解碼任務開始時,c底層啟動一個線程,然后在線程里調用decode方法。
在這個函數里,一直循環調用wait函數,不斷從c底層讀取符合邊沿條件的數據,如:
while True: (a,b) = self.wait()
(a,b)元組里的變量數跟聲明的chnnaels里聲明的通道數一致,包括可選的通道上。
解碼任務執行流程:
- (1) 解碼任務開始啟動;
- (2) 上層將采樣數據分批推送到底層;
- (3) 底層檢測并啟動一個線程,該線程調用python層的decode函數,不斷處理上層推送的數據;
- (4) python層經過一系列的計算處理,生成解碼結果,通過put函數輸出到c底層;
- (5) 當所有數據推送完成并經過python層處理,解碼任務結束;
七、框架升級更新記錄
??在DSView版本1.2.0以上,更新以下功能:
- end方法
python層的方法,當所有數據處理完成后會被c底層觸發 - self.last_samplenum屬性
c底層提供的屬性,其值為所有數據推送完成后最后的數據樣位置。當存在end方法時,該屬性將被設置 - 數據多種顯示格式
顯示的annotation數據部分,可以在2進制、16進制、8進制、10進制、ascii格式間轉換。
self.put(s, e, self.out_ann, [1,['%02X' % value])
put函數將數據輸出c底層,并在屏幕上顯示。其中,value為要顯示的數據。在輸出到時需要轉換為16進制的字符串。當需要讓數據支持在多種格式間轉換時,代碼修改如下:
self.put(s, e, self.out_ann, [1,['@' + '%02X' % value])
它是通過在數據部分前加@符號,告訴c底層這一部分內空是數據部分,如:
'@66FB'
如果存在前綴文字,需要將格式部分和數據部分開,如:
['Data:{$}','D:{$}', '@66FB']
{$}是占位符,系統將數據部分格式化后替換掉占位符,就會變成:Data:66FB
-
單片機
+關注
關注
6037文章
44558瀏覽量
635362 -
邏輯分析儀
+關注
關注
3文章
214瀏覽量
23172 -
python
+關注
關注
56文章
4797瀏覽量
84693
發布評論請先 登錄
相關推薦
評論