当前位置: 首页 > news >正文

网站开发 系统需求文档个性化定制网站

网站开发 系统需求文档,个性化定制网站,支付宝 手机网站开发,网站信息服务费怎么做分录文章目录0 前言1 准备工作1.1 下载DSView1.2 DSLogic逻辑分析仪2 理解DSView解码机制2.1 运行DSView的demo2.2 UART 帧解码步骤3 解码脚本解析—— UART 解码器3.1 找到解码脚本3.2 import & 异常类 & 全局函数3.2.1 校验位检查3.2.2 抛出异常3.3 解码器 Decoder 类3.3…

文章目录

  • 0 前言
  • 1 准备工作
    • 1.1 下载DSView
    • 1.2 DSLogic逻辑分析仪
  • 2 理解DSView解码机制
    • 2.1 运行DSView的demo
    • 2.2 UART 帧解码步骤
  • 3 解码脚本解析—— UART 解码器
    • 3.1 找到解码脚本
    • 3.2 import & 异常类 & 全局函数
      • 3.2.1 校验位检查
      • 3.2.2 抛出异常
    • 3.3 解码器 Decoder 类
      • 3.3.1 解码器属性配置
      • 3.3.2 初始化函数和复位函数
    • 3.4 解码结果显示函数
    • 3.5 采样点数和位置的计算
      • 3.5.1 计算UART一个bit对应的采样点数
      • 3.5.2 计算UART一个bit中间点的索引
    • 3.6 状态机
      • 3.6.1 状态1:等待起始位
      • 3.6.2 状态2:获取起始位
      • 3.6.3 状态3:获取数据位
      • 3.6.4 状态4:获取校验位
      • 3.6.5 状态5:获取停止位
    • 3.7 主循环 & 状态机调度(关键)
      • 3.7.1 get_wait_cond()_计算阻塞的点数
      • 3.7.2 inspect_sample()_状态机调度
      • 3.7.3 inspect_edge()_边沿捕获处理函数
      • 3.7.4 handle_break()_错误处理函数

0 前言

题主为了使用逻辑分析仪解码自己的私有协议,琢磨怎么自己实现一个解码器脚本

在网上几乎没找到相关的资料,所以自己找到了 DSLogic 的解码脚本,并研究了一下解码逻辑,然后改了个脚本出来。为了避免后面有需求的朋友花时间再研究一遍,所以写这个帖子。


这个帖子在写了一半的时候,发现已经有大佬做了非常棒的讲解,而且大佬也是用UART做示例的
原文链接: 逻辑分析仪协议解码教程

相比于大佬的帖子,本文会讲的更基础更细一些,在大模型的帮助下,代码注释也会更多一点

DSView 解码器是基于 sigrok 开源项目的 libsigrokdecode
sigrok 官方也提供了大量的资料,链接:sigrok开源项目


本文通过解析 DSView 的解码器脚本源码,帮助你使用 DSLogic 逻辑分析仪解码私有的通讯协议,内容如下:
🌟 DSView 逻辑分析仪的解码器入门
🌟 理解 DSView 的解码机制
🌟 实战解析 UART 解码器脚本
🌟 尝试完成私有协议的硬件解码任务


1 准备工作

1.1 下载DSView

下载链接:DreamSourceLab——Download
在这里插入图片描述

1.2 DSLogic逻辑分析仪

也可以暂时不用,因为 DSView 软件提供了demo,即使没有接 DSLogic 也可以运行。
在这里插入图片描述


2 理解DSView解码机制

2.1 运行DSView的demo

运行 DSView 的demo来理解其解码机制。
按照图片指示操作,在界面上来分析UART的解码过程。

  1. 切换到demo模式,并删除所有解码器
    在这里插入图片描述
  2. 只保留UART通道
    在这里插入图片描述
  3. 添加第一个 UART 解码器
    在这里插入图片描述
  4. 添加第二个 UART 解码器
    在这里插入图片描述
  5. 查看 UART 解码结果
    在这里插入图片描述

2.2 UART 帧解码步骤

通过观察上述解码结果,可以看出一个串口帧解码大概分为以下几个步骤:

1️⃣ 寻找跳变沿:上升沿 / 下降沿
2️⃣ 确定起始位状态:合法 / 非法
3️⃣ 确定数据位状态:0 / 1
4️⃣ 确定结束位状态:0 / 1

上述demo添加了两个 UART 解码器,即 0: UART1: UART
直观的看下来, 1: UART 解码器增加了对 解码位置每个bit值 的显示


3 解码脚本解析—— UART 解码器

上文提到,UART 有两个解码器,我们这里来分析一下 1: UART 解码器,因为其功能更多一些

以下脚本的解析并非按照代码原生的顺序,遇到关键的代码会特别标明

3.1 找到解码脚本

脚本位置在 DSView 安装目录下的 decode 文件夹内,可以看出解码脚本是用Python写的

在这里插入图片描述


3.2 import & 异常类 & 全局函数

总结下来,解码器实现的功能可以概括为以下两点:
🌧️ 找到关键信息位置,如起始位、数据位、校验位、终止位等的位置
🌧️ 解码数字信号,得到对应的信息或数据,并直观地显示在对应的位置

import sigrokdecode as srd            # 流式协议解码库
from common.srdhelper import bitpack  # 用于将解码得到的二进制比特转换为字节
from math import floor, ceil          # 天花板函数和地板函数
'''
OUTPUT_PYTHON 格式:Packet:[<ptype>, <rxtx>, <pdata>]
以下是 <ptype> 类型及其对应的 <pdata> 值说明:'STARTBIT':数据为起始位的整数值(0/1)。
'DATA':始终为包含两个元素的元组:
第1项:UART数据的整数值(有效范围0至511,因数据最多支持9位)。
第2项:各数据位及其ss/es编号的列表。
'PARITYBIT':数据为校验位的整数值(0/1)。
'STOPBIT':数据为停止位的整数值(0/1)。
'INVALID STARTBIT':数据为无效起始位的整数值(0/1)。
'INVALID STOPBIT':数据为无效停止位的整数值(0/1)。
'PARITY ERROR':数据为包含两项的元组,第一项为预期校验值,第二项为实际校验值。
'BREAK':数据固定为0。
'FRAME':数据为包含两项的元组,第一项为UART数据的整数值,第二项为布尔值,表示UART帧的有效性。
'''

3.2.1 校验位检查

支持四种校验方式:
🌧️ 0 校验:检查校验位是否为0
🌧️ 1 校验:检查校验位是否为1
🌧️ 奇校验:检查数据位,1出现的次数是奇数则校验通过
🌧️ 偶校验:检查数据位,1出现的次数是偶数则校验通过

# Given a parity type to check (odd, even, zero, one), the value of the
# parity bit, the value of the data, and the length of the data (5-9 bits,
# usually 8 bits) return True if the parity is correct, False otherwise.
# 'none' is _not_ allowed as value for 'parity_type'.
def parity_ok(parity_type, parity_bit, data, num_data_bits):# Handle easy cases first (parity bit is always 1 or 0).if parity_type == 'zero':return parity_bit == 0elif parity_type == 'one':return parity_bit == 1# Count number of 1 (high) bits in the data (and the parity bit itself!).ones = bin(data).count('1') + parity_bit# Check for odd/even parity.if parity_type == 'odd':return (ones % 2) == 1elif parity_type == 'even':return (ones % 2) == 0

3.2.2 抛出异常

class SamplerateError(Exception):passclass ChannelError(Exception):pass

3.3 解码器 Decoder 类

本章以下所有的内容都属于脚本的核心: Decoder

3.3.1 解码器属性配置

注意:在改写自己的解码器的时候,必须要把id改成其它的,否则进入软件的时候会报错

class Decoder(srd.Decoder):api_version = 3id = '1:uart'name = '1:UART'longname = 'Universal Asynchronous Receiver/Transmitter'desc = 'Asynchronous, serial bus.'license = 'gplv2+'inputs = ['logic']outputs = ['uart']tags = ['Embedded/industrial']channels = ({'id': 'rxtx', 'type': 209, 'name': 'RX/TX', 'desc': 'UART transceive line', 'idn':'dec_1uart_chan_rxtx'},)# 可选设定参数,出现在在选定编码器后弹出来的参数配置界面中# id : 参数句柄,不出现在用户界面上# desc : 描述信息,出现在用户界面上# default : 默认值,出现在用户界面上# valuse  : 可选值,出现在用户界面上(如果不提供,用户可自由配置)options = (# 波特率配置项:默认115200,{'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200, 'idn':'dec_1uart_opt_baudrate'},# 数据位数配置项:默认8位,可选范围4-128位{'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,'values': tuple(range(4,129,1)), 'idn':'dec_1uart_opt_num_data_bits'},# 校验类型配置项:默认无校验,可选奇校验/偶检验/0校验/1校验{'id': 'parity_type', 'desc': 'Parity type', 'default': 'none','values': ('none', 'odd', 'even', 'zero', 'one'), 'idn':'dec_1uart_opt_parity_type'},# 校验检查配置项:默认启用校验检查{'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes','values': ('yes', 'no'), 'idn':'dec_1uart_opt_parity_check'},# 停止位配置项:默认1.0位,支持0.0-2.5位{'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,'values': (0.0, 0.5, 1.0, 1.5, 2.0, 2.5), 'idn':'dec_1uart_opt_num_stop_bits'},# 比特序配置项:默认低位优先,可选lsb-first/msb-first            {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first','values': ('lsb-first', 'msb-first'), 'idn':'dec_1uart_opt_bit_order'},# 数据格式配置项:默认十六进制,支持ascii/dec/hex/oct/bin{'id': 'format', 'desc': 'Data format', 'default': 'hex','values': ('ascii', 'dec', 'hex', 'oct', 'bin') ,'idn':'dec_1uart_opt_format'},# 信号反转配置项:默认不反转,可选yes/no{'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no','values': ('yes', 'no'), 'idn':'dec_1uart_opt_invert'},# 起止位显示配置项:默认不显示,可选yes/no{'id': 'anno_startstop', 'desc': 'Display Start/Stop?', 'default': 'no','values': ('yes', 'no'), 'idn':'dec_1uart_anno_startstop'},)# 协议解码类型定义annotations = (('108', 'data', 'data'),('7', 'start', 'start bits'),('6', 'parity-ok', 'parity OK bits'),('0', 'parity-err', 'parity error bits'),('1', 'stop', 'stop bits'),('1000', 'warnings', 'warnings'),('209', 'data-bits', 'data bits'),('10', 'break', 'break'),)# 显示解码结果的行annotation_rows = (# 'data'类别:标注为RX/TX,包含第0-4行(共5行)('data', 'RX/TX', (0, 1, 2, 3, 4)),# 'data-bits'类别:标注为Bits,仅包含第6行('data-bits', 'Bits', (6,)),# 'warnings'类别:标注为Warnings,仅包含第5行('warnings', 'Warnings', (5,)),# 'break'类别:标注为break,仅包含第7行('break', 'break', (7,)),)# 二进制协议的解码结果binary = (('rxtx', 'RX/TX dump'),)idle_state = 'WAIT FOR START BIT'

3.3.2 初始化函数和复位函数

def __init__(self):self.reset()def reset(self):self.samplerate = Noneself.samplenum = 0self.frame_start = -1self.frame_valid = Noneself.startbit = -1self.cur_data_bit = 0self.datavalue = 0self.paritybit = -1self.stopbit1 = -1self.startsample = -1self.state = 'WAIT FOR START BIT'self.databits = []self.break_start = Nonedef 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['num_data_bits'] + 7) // 8

3.4 解码结果显示函数

在完成协议解码后,需要用一个个注释块来显示解码结果,这里函数的目标是:

1️⃣ 找到注释块的 起始位置终止位置,画出注释块
(所有位置都是用采样点索引来表示的,即找到起始采样点索引和终止采样点索引)
2️⃣ 在注释块上显示解码结果

def putx(self, data):# s是起始采样点索引,halfbit是每一个bit对应的采样点数s, halfbit = self.startsample, self.bit_width / 2.0# 显示起始位和终止位:标注范围从当前位起始点前半个比特到当前采样点后半个比特if self.options['anno_startstop'] == 'yes' :self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)# 不显示起始位和终止位:标注范围从帧起始点到当前采样点加上停止位长度(考虑配置的停止位数)else :self.put(self.frame_start, self.samplenum + ceil(halfbit * (1+self.options['num_stop_bits'])), self.out_ann, data)
def putpx(self, data):s, halfbit = self.startsample, self.bit_width / 2.0self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)def putg(self, data):s, halfbit = self.samplenum, self.bit_width / 2.0self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data)def putp(self, data):s, halfbit = self.samplenum, self.bit_width / 2.0self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)def putgse(self, ss, es, data):self.put(ss, es, self.out_ann, data)def putpse(self, ss, es, data):self.put(ss, es, self.out_python, data)def putbin(self, data):s, halfbit = self.startsample, self.bit_width / 2.0self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)

3.5 采样点数和位置的计算

3.5.1 计算UART一个bit对应的采样点数

DSLogic 可以达到 100MHz 及以上的采样率,halfbit 是每一个bit对应的采样点数

例:串口波特率为 115200 下,UART 一个 bit 对应逻辑分析仪在 100MHz 下采样 868 个点
halfbit = 100000000 / 115200 = 868

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'])

3.5.2 计算UART一个bit中间点的索引

如上所说, UART 一个 bit 对应逻辑分析仪在其采样率下采若干个点。

如果想知道 UART 的某个 bit1 还是 0 ,那么选择这个比特最中间的采样点是最可靠的。
因为显然,这个 bit 的两侧可能是跳变沿,其数据是不可靠的。

def get_sample_point(self, bitnum):# 确定比特采样点的绝对样本编号# 比特位置(bitpos)表示指定UART比特位中间点的样本编号。# 0=起始位,1至x=数据位,x+1=奇偶校验位(若启用)或第一个停止位,以此类推# 比特窗口内的采样点编号为0,1,...,(比特宽度-1)# 因此比特窗口中间采样点的索引计算公式为:(比特宽度 - 1) / 2。bitpos = self.frame_start + (self.bit_width - 1) / 2.0bitpos += bitnum * self.bit_widthreturn bitpos

3.6 状态机

3.6.1 状态1:等待起始位

在状态1中,记录下起始位产生时的采样点索引值,并跳转到状态2。

def wait_for_start_bit(self, signal):# Save the sample number where the start bit begins.self.frame_start = self.samplenumself.frame_valid = Trueself.state = 'GET START BIT'

3.6.2 状态2:获取起始位

def get_start_bit(self, signal):self.startbit = signal# 起始位必须为0。若非如此,将报告错误,并回到状态1,重新等待起始位if self.startbit != 0:self.putp(['INVALID STARTBIT', 0, self.startbit])self.putg([5, ['Frame error', 'Frame err', 'FE']])self.frame_valid = Falsees = self.samplenum + ceil(self.bit_width / 2.0)self.putpse(self.frame_start, es, ['FRAME', 0,(self.datavalue, self.frame_valid)])# 回到状态1self.state = 'WAIT FOR START BIT'	return# 复位本数据帧相关的变量# 将self.startsample标记为-1,用于后续状态3标识首个数据位self.cur_data_bit = 0self.datavalue = 0self.startsample = -1# 显示起始位self.putp(['STARTBIT', 0, self.startbit])if self.options['anno_startstop'] == 'yes':self.putg([1, ['Start bit', 'Start', 'S']])# 进入下一个状态:获取数据位self.state = 'GET DATA BITS'

3.6.3 状态3:获取数据位

def get_data_bits(self, signal):# 获取首个数据位,中间采样点的绝对索引值,用于生成后续数据位采样的索引值if self.startsample == -1:self.startsample = self.samplenum# 原始信号输出到逻辑分析仪界面self.putg([6, ['%d' % signal]])# 获取该数据位起始位置的绝对索引值,和结束位置的绝对索引值,用于解码结果显示的起始位置和结束位置s, halfbit = self.samplenum, int(self.bit_width / 2)self.databits.append([signal, s - halfbit, s + halfbit])# 数据位检查# 只有当本帧所有的数据位都完成采集,才会一起处理,并在界面上显示最终解码结果self.cur_data_bit += 1if self.cur_data_bit < self.options['num_data_bits']:return# 将所有的数据位格式由二进制转换为16进制,并显示解码结果bits = [b[0] for b in self.databits]if self.options['bit_order'] == 'msb-first':bits.reverse()self.datavalue = bitpack(bits)self.putpx(['DATA', 0, (self.datavalue, self.databits)])self.putx([0, ['@%02X' % self.datavalue]])# 二进制数据的转换与输出b = self.datavaluebdata = b.to_bytes(self.bw, byteorder='big')self.putbin([0, bdata])self.putbin([1, bdata])# 清空 self.databits 列表,准备接收下一帧数据self.databits = []# 状态机切换# 若配置了校验位(parity_type != 'none'),则切换到 GET PARITY BIT 状态# 若未配置校验位,直接切换到 GET STOP BITS 状态,准备接收停止位self.state = 'GET PARITY BIT'if self.options['parity_type'] == 'none':self.state = 'GET STOP BITS'

3.6.4 状态4:获取校验位

def get_parity_bit(self, signal):self.paritybit = signalif parity_ok(self.options['parity_type'], self.paritybit,self.datavalue, self.options['num_data_bits']):self.putp(['PARITYBIT', 0, self.paritybit])self.putg([2, ['Parity bit', 'Parity', 'P']])else:# TODO: Return expected/actual parity values.self.putp(['PARITY ERROR', 0, (0, 1)]) # FIXME: Dummy tuple...self.putg([3, ['Parity error', 'Parity err', 'PE']])self.frame_valid = Falseself.state = 'GET STOP BITS'

3.6.5 状态5:获取停止位

# TODO: Currently only supports 1 stop bit.
def get_stop_bits(self, signal):self.stopbit1 = signal# Stop bits must be 1. If not, we report an error.if self.stopbit1 != 1:self.putp(['INVALID STOPBIT', 0, self.stopbit1])self.putg([5, ['Frame error', 'Frame err', 'FE']])self.frame_valid = Falseself.putp(['STOPBIT', 0, self.stopbit1])if self.options['anno_startstop'] == 'yes':self.putg([2, ['Stop bit', 'Stop', 'T']])# Pass the complete UART frame to upper layers.es = self.samplenum + ceil(self.bit_width / 2.0)self.putpse(self.frame_start, es, ['FRAME', 0,(self.datavalue, self.frame_valid)])self.state = 'WAIT FOR START BIT'

3.7 主循环 & 状态机调度(关键)

def decode(self):# 如果没有指定波特率,则报错if not self.samplerate:raise SamplerateError('Cannot decode without samplerate.')# 如果Invert Signal被配置为yes,则标记inv,后面处理的时候对输入信号翻转inv = self.options['invert'] == 'yes'cond_data_idx = None# 确定一个完整帧时间跨度内的样本数量,信号低电平持续至少该时长即为中断条件# 起始位宽度:固定为1bitframe_samples = 1     # 数据位宽度:根据配置确定,4bit-128bitframe_samples += self.options['num_data_bits'] # 校验位宽度:有校验则为1bit,无校验则为0bitframe_samples += 0 if self.options['parity_type'] == 'none' else 1# 停止位宽度:根据配置确定,0bit-2.5bitframe_samples += self.options['num_stop_bits']# 将UART一个数据帧的位长度转变为逻辑分析仪采样点数frame_samples *= self.bit_widthself.break_min_sample_count = ceil(frame_samples)cond_edge_idx = None# 主循环while True:# self.wait的退出阻塞条件conds = []                            # conds为等待条件列表cond_data_idx = len(conds)conds.append(self.get_wait_cond(inv)) # 详见get_wait_cond()注释cond_edge_idx = len(conds)conds.append({0: 'e'})                # 向等待条件列表添加终止标记# 阻塞,直到满足conds要求的阻塞条件被满足# 条件有可能是:等待检测到边沿,也有可能是逻辑分析仪采集到一定数量的点数(rxtx, ) = self.wait(conds)# 已经获取到特定位置的采样点,调用相应的处理函数# 在这里将实现状态机的调度if cond_data_idx is not None and (self.matched & (0b1 << cond_data_idx)):self.inspect_sample(rxtx, inv)# 已经获取到了特定的边沿,调用相应的处理函数# 在这里将实现错误处理if cond_edge_idx is not None and (self.matched & (0b1 << cond_edge_idx)):self.inspect_edge(rxtx, inv)

3.7.1 get_wait_cond()_计算阻塞的点数

def get_wait_cond(self, inv):# 获取当前状态机的状态,该状态用于返回输入给Decoder.wait()的条件state = self.state# 当前状态是等待起始位:返回条件字典# 键0表示起始位,值'r'(上升沿)或'f'(下降沿)# 如果Invert Signal被配置为yes,则捕获下降沿,反之则捕获上升沿# 当捕获到上升沿或下降沿时,Decoder.wait()退出阻塞if state == 'WAIT FOR START BIT':return {0: 'r' if inv else 'f'}# 当前状态是获取起始位:bitnum = 0# 在本函数后面的self.get_sample_point(bitnum)函数中,自带0.5个bit的延时# 这代表:从捕获到上升沿/下降沿后的第0.5个bit,是起始位的判断位置# 当到达这个位置的时候,Decoder.wait()退出阻塞if state == 'GET START BIT':bitnum = 0# 当前状态是获取起始位:bitnum = 1(起始位) + 已经获取到的数据位数量# 这是因为要依次获取每个数据位的采样位置(即每个bit的中心)# 从捕获到上升沿/下降沿后的第1.5bit、2.5bit直到n.5bit都是数据位(n=数据位长度-1)elif state == 'GET DATA BITS':bitnum = 1 + self.cur_data_bit # self.cur_data_bit由0开始递增,直到数据位长度-1# 当前状态是获取校验位:bitnum = 1(起始位) + 数据位长度elif state == 'GET PARITY BIT':bitnum = 1 + self.options['num_data_bits']# 当前状态是获取停止位:bitnum = 1(起始位) + 数据位长度 + 校验位长度elif state == 'GET STOP BITS':bitnum = 1 + self.options['num_data_bits']bitnum += 0 if self.options['parity_type'] == 'none' else 1# 将UART bit长度转换为逻辑分析仪采样点的点数# self.get_sample_point(bitnum)函数内部会自动加0.5个bit的采样点数,即bit的中间采样点位置want_num = ceil(self.get_sample_point(bitnum))# 返回从现在开始,需要等待多少采样点,才可以退出Decoder.wait()的阻塞return {'skip': want_num - self.samplenum}

3.7.2 inspect_sample()_状态机调度

def inspect_sample(self, signal, inv):# 信号翻转处理判断if inv:signal = not signal# 状态机调度state = self.stateif state == 'WAIT FOR START BIT':self.wait_for_start_bit(signal)elif state == 'GET START BIT':self.get_start_bit(signal)elif state == 'GET DATA BITS':self.get_data_bits(signal)elif state == 'GET PARITY BIT':self.get_parity_bit(signal)elif state == 'GET STOP BITS':self.get_stop_bits(signal)

3.7.3 inspect_edge()_边沿捕获处理函数

def inspect_edge(self, signal, inv):# 信号翻转处理判断if inv:signal = not signal# 判断当前是否是起始位的电平状态# self.break_start是UART起始位的第一个采样点if not signal:self.break_start = self.samplenumreturn# Signal went high. Was there an extended period with low signal?if self.break_start is None:return# 错误处理diff = self.samplenum - self.break_startif diff >= self.break_min_sample_count:self.handle_break()self.break_start = None

3.7.4 handle_break()_错误处理函数

错误处理函数用于防止状态机卡在某个状态中,无法退出。
比如接收到了起始位,校验也通过,但是数据位迟迟没有到来。

def handle_break(self):self.putpse(self.frame_start, self.samplenum,['BREAK', 0, 0])self.putgse(self.frame_start, self.samplenum,[7, ['Break condition', 'Break', 'Brk', 'B']])self.state = 'WAIT FOR START BIT'
http://www.dtcms.com/a/428353.html

相关文章:

  • IDEA+SpringBoot实现远程DEBUG到本机
  • 网站建设与维护 目录开发公司前期手续流程
  • 物品奖励系统介绍
  • 广州站西手表公司彩页设计制作
  • sat4j中参数作用
  • 网站建设课程有哪些收获西安注册公司虚拟地址
  • 哪家做网站性价比高朋友圈自己做的网站
  • 建设网站需要掌握什么编程语言川菜餐馆网站建设模板美食餐厅企业建站php源码程序
  • 网上商城公司网站建设方案被网上教开网店的骗了怎么办
  • 网站域名无法访问国外建站工具
  • SurfaceFlinger BufferQueue(三) DequeueBuffer
  • AI智能体(Agent)大模型入门【4】--下载训练好的大模型部署到本地上
  • 网站tkd怎么做学做烘培的网站
  • 肇庆网站制作费用wordpress 开源主题
  • 【开题答辩全过程】以 NBA球星管理系统为例,包含答辩的问题和答案
  • asp.net中文官方网站江门市网站建设公司
  • 网站开发费用报价单硬件开发学什么专业
  • 网站外链怎么购买建设网站基本步骤
  • 临沧网站建设公司佛山网站建设公司价格
  • 惠海 48V 60V 80V 降 3.3V 5V 9V 12V电动车/摩托车整车芯片解决方案(下)
  • 做网站用小动画网络公司 建站 官方网站
  • 储卡器底部塑料壳外部细节建模实战分享|附完整视频教程
  • android源码下载网站宁波seo关键词费用
  • 福州做网站哪家最好不利于优化网站的因素
  • 矩阵运算:深度学习的数学基石与手工实现解析
  • 自己做的网站网站搜索潍坊程序设计网站建设公司
  • 修改网站照片需要怎么做电子商务网站前台业务系统主要是
  • 河南洛阳网站建设做个网站上百度怎么做
  • 公司品牌营销策划龙岗网站优化培训
  • 成都做营销型网站wordpress s3插件