Compare commits

...
10 Commits
Author SHA1 Message Date
chenyue 03f71f35b2 增加说明文档 2026-06-18 11:01:14 +08:00
chenyue d7040f1029 合并xy2-100 2026-06-18 10:57:36 +08:00
chenyue 1c81c2e075 增加counter,和parallel解码器 2026-06-18 10:45:25 +08:00
chenyue 51dd512ff8 分离数据到不同的行 2026-06-17 21:56:35 +08:00
chenyue 051bbaeb5f 增加数据类型转换 2026-06-17 21:48:32 +08:00
chenyue 62b37bb2d9 能显示数值 2026-06-17 21:42:37 +08:00
chenyue 45d1e8d0ef 首次能正常解析 2026-06-17 21:16:46 +08:00
chenyue 7012873c90 初版可以用,但是最后一个位解析有问题。 2026-06-17 09:38:19 +08:00
chenyue 7ff53f177d 修正bug 2026-04-23 22:52:08 +08:00
chenyue 1f140e9b0b 增加一路数据解析 2026-04-23 22:08:20 +08:00
14 changed files with 890 additions and 7 deletions
+25
View File
@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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/>.
##
'''
I²C (Inter-Integrated Circuit) is a bidirectional, multi-master
bus using two signals (SCL = serial clock line, SDA = serial data line).
'''
from .pd import Decoder
Binary file not shown.
Binary file not shown.
+222
View File
@@ -0,0 +1,222 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2010-2016 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.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/>.
##
# BiSS-C decoder (default): MSB-first, sample on SCL rising edge.
# Frame: 25 data bits + 6 CRC (CRC-6, polynomial x^6 + x^1 + x^0 (0x43) commonly used in BiSS)
import sigrokdecode as srd
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <pdata>]
<ptype>:
- 'START' (START condition)
- 'START REPEAT' (Repeated START condition)
- 'ADDRESS READ' (Slave address, read)
- 'ADDRESS WRITE' (Slave address, write)
- 'DATA READ' (Data, read)
- 'DATA WRITE' (Data, write)
- 'STOP' (STOP condition)
- 'ACK' (ACK bit)
- 'NACK' (NACK bit)
- 'BITS' (<pdata>: list of data/address bits and their ss/es numbers)
<pdata> is the data or address byte associated with the 'ADDRESS*' and 'DATA*'
command. Slave addresses do not include bit 0 (the READ/WRITE indication bit).
For example, a slave address field could be 0x51 (instead of 0xa2).
For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <pdata> is None.
'''
# CMD: [annotation-type-index, long annotation, short annotation]
proto = {
'BIT': [0, 'Bit', 'B'],
'START': [1, 'Start', 'S'],
'ASC': [2, 'arc start cds', 'ARC'],
'P': [3, 'P', 'P'],
'S': [4, 'S', 'S'],
'ERR': [5, 'err', 'err'],
'CRC': [6, 'CRC', 'CRC'],
'STOP': [7, 'Stop', 'P'],
}
class Decoder(srd.Decoder):
api_version = 3
id = '1:biss'
name = '1:BiSS'
longname = 'BiSS-C Encoder/Decoder'
desc = 'BiSS-C position sensor serial protocol (default decoding)'
license = 'gplv2+'
inputs = ['logic']
outputs = ['biss']
tags = ['Embedded/industrial']
channels = (
{'id': 'scl', 'type': 8, 'name': 'SCL', 'desc': 'Clock', 'idn':'dec_1biss_chan_scl'},
{'id': 'sda', 'type': 108, 'name': 'SDA', 'desc': 'Serial data line', 'idn':'dec_1i2c_chan_sda'},
)
annotations = (
('208', 'bit', 'Data/address bit'),
('207', 'start', 'Start condition'),
('6', 'asc', 'arc start cds'),
('5', 'data1', 'p'),
('0', 'data2', 's'),
('112', 'error', 'Address read'),
('111', 'crc', 'Address write'),
('201', 'stop', 'Stop condition'),
('202', 'value', 'value'),
('203', 'value1', 'value2'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
('p', 'p', ( 8,)),
('s', 's', ( 9,)),
('flag', 'Flag', ( 1, 2,3,4,5, 6, 7)),
)
binary = (
('address-read', 'Address read'),
('address-write', 'Address write'),
('data-read', 'Data read'),
('data-write', 'Data write'),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.ss = self.es = self.ss_byte = -1
self.bitcount = 0
self.databyte = 0
self.state = 'FIND START'
self.pdu_start = None
self.pdu_bits = 0
self.bits = []
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'))
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def putp(self, data):
self.put(self.ss, self.es, self.out_python, data)
def putb(self, data):
self.put(self.ss, self.es, self.out_binary, data)
def handle_start(self):
self.ss, self.es = self.samplenum, self.samplenum
self.pdu_start = self.samplenum
cmd = 'START'
self.putp([cmd, None])
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND DATA'
self.bitcount = self.databyte = 0
self.bits = []
def handle_stop(self):
cmd = 'STOP'
self.ss, self.es = self.samplenum, self.samplenum
self.putp([cmd, None])
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND START'
self.bits = []
# Gather 8 bits of data plus the ACK/NACK bit.
def handle_data(self, scl, sda,cmd,size,next_state,num):
# 记录数据值
self.databyte <<= 1
self.databyte |= sda
# 记录数据的起始位置
if self.bitcount == 0:
self.ss_byte = self.samplenum
# Store individual bits and their start/end samplenumbers.
# In the list, index 0 represents the LSB (I2C transmits MSB-first).
self.bits.insert(0, [sda, self.samplenum, self.samplenum])
self.bitcount += 1
if self.bitcount == 2:
self.bitwidth = self.bits[0][2] - self.bits[1][2]
if self.bitcount > 1:
self.bits[1][2] = self.samplenum
if self.bitcount == size:
self.bits[0][2] += self.bitwidth
# Return if we haven't collected all 8 + 1 bits, yet.
if self.bitcount < size:
return
d = self.databyte
self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
# self.putp(['BITS', self.bits])
# self.putp([cmd, d])
# self.putb([bin_class, d])
for bit in self.bits:
self.put(bit[1], bit[2], self.out_ann, [0, ['%d' % bit[0]]])
self.put(self.ss, self.es, self.out_ann, [num, ['{$}', d]])
self.putx([proto[cmd][0], proto[cmd][1:]])
# Done with this packet.
self.bitcount = self.databyte = 0
self.bits = []
self.state = next_state
def decode(self):
while True:
# State machine.
if self.state == 'FIND START':
self.wait({0: 'h', 1: 'f'})
self.handle_start()
elif self.state == 'FIND DATA':
(scl, sda) = self.wait([{0: 'f'}])
self.handle_data(scl, sda,'ASC',3,'FIND DATA1',1)
elif self.state == 'FIND DATA1':
(scl, sda) = self.wait([{0: 'f'}])
self.handle_data(scl, sda,'P',13,'FIND DATA2',8)
elif self.state == 'FIND DATA2':
(scl, sda) = self.wait([{0: 'f'}])
self.handle_data(scl, sda,'S',13,'FIND ERR',9)
elif self.state == 'FIND ERR':
(scl, sda) = self.wait([{0: 'f'}])
self.handle_data(scl, sda,'ERR',2,'FIND CRC',1)
elif self.state == 'FIND CRC':
(scl, sda) = self.wait([{0: 'f'}])
self.handle_data(scl, sda,'CRC',6,'FIND STOP',1)
elif self.state == 'FIND STOP':
(scl, sda) = self.wait([{0: 'h', 1: 'r'}])
self.handle_stop()
+28
View File
@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
##
## 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/>.
##
'''
This decoder is a simple edge counter.
It can count rising and/or falling edges, provides an optional reset
signal. It can also divide the count to e.g. count the number of
fixed-length words (where a word corresponds to e.g. 9 clock edges).
'''
from .pd import Decoder
Binary file not shown.
Binary file not shown.
+145
View File
@@ -0,0 +1,145 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
##
## 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
PIN_DATA, PIN_RESET = range(2)
ROW_EDGE, ROW_WORD, ROW_RESET = range(3)
class Decoder(srd.Decoder):
api_version = 3
id = 'counter'
name = 'Counter'
longname = 'Edge counter'
desc = 'Count the number of edges in a signal.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Util']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line', 'idn':'dec_counter_chan_data'},
)
optional_channels = (
{'id': 'reset', 'name': 'Reset', 'desc': 'Reset line', 'idn':'dec_counter_opt_chan_reset'},
)
annotations = (
('edge_count', 'Edge count'),
('word_count', 'Word count'),
('word_reset', 'Word reset'),
)
annotation_rows = (
('edge_counts', 'Edges', (ROW_EDGE,)),
('word_counts', 'Words', (ROW_WORD,)),
('word_resets', 'Word resets', (ROW_RESET,)),
)
options = (
{'id': 'data_edge', 'desc': 'Edges to count (data)', 'default': 'any',
'values': ('any', 'rising', 'falling'), 'idn':'dec_counter_opt_data_edge'},
{'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0, 'idn':'dec_counter_opt_divider'},
{'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)',
'default': 'falling', 'values': ('rising', 'falling'), 'idn':'dec_counter_opt_reset_edge'},
{'id': 'edge_off', 'desc': 'Edge counter value after start/reset', 'default': 0, 'idn':'dec_counter_opt_edge_off'},
{'id': 'word_off', 'desc': 'Word counter value after start/reset', 'default': 0, 'idn':'dec_counter_opt_word_off'},
{'id': 'dead_cycles', 'desc': 'Ignore this many edges after reset', 'default': 0, 'idn':'dec_counter_opt_dead_cycles'},
{'id': 'start_with_reset', 'desc': 'Assume decode starts with reset',
'default': 'no', 'values': ('no', 'yes'), 'idn':'dec_counter_opt_start_with_reset'},
)
def __init__(self):
self.reset()
def reset(self):
pass
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putc(self, cls, ss, annlist):
self.put(ss, self.samplenum, self.out_ann, [cls, annlist])
def decode(self):
opt_edge_map = {'rising': 'r', 'falling': 'f', 'any': 'e'}
data_edge = self.options['data_edge']
divider = self.options['divider']
if divider < 0:
divider = 0
reset_edge = self.options['reset_edge']
condition = [{PIN_DATA: opt_edge_map[data_edge]}]
have_reset = self.has_channel(PIN_RESET)
if have_reset:
cond_reset = len(condition)
condition.append({PIN_RESET: opt_edge_map[reset_edge]})
edge_count = int(self.options['edge_off'])
edge_start = None
word_count = int(self.options['word_off'])
word_start = None
if self.options['start_with_reset'] == 'yes':
dead_count = int(self.options['dead_cycles'])
else:
dead_count = 0
while True:
self.wait(condition)
now = self.samplenum
if have_reset and (self.matched & (0b1 <<cond_reset)):
edge_count = int(self.options['edge_off'])
edge_start = now
word_count = int(self.options['word_off'])
word_start = now
self.putc(ROW_RESET, now, ['Word reset', 'Reset', 'Rst', 'R'])
dead_count = int(self.options['dead_cycles'])
continue
if dead_count:
dead_count -= 1
edge_start = now
word_start = now
continue
# Implementation note: In the absence of a RESET condition
# before the first data edge, any arbitrary choice of where
# to start the annotation is valid. One may choose to emit a
# narrow annotation (where ss=es), or assume that the cycle
# which corresponds to the counter value started at sample
# number 0. We decided to go with the latter here, to avoid
# narrow annotations (see bug #1210). None of this matters in
# the presence of a RESET condition in the input stream.
if edge_start is None:
edge_start = 0
if word_start is None:
word_start = 0
edge_count += 1
self.putc(ROW_EDGE, edge_start, ["{:d}".format(edge_count)])
edge_start = now
word_edge_count = edge_count - int(self.options['edge_off'])
if divider and (word_edge_count % divider) == 0:
word_count += 1
self.putc(ROW_WORD, word_start, ["{:d}".format(word_count)])
word_start = now
+34
View File
@@ -0,0 +1,34 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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/>.
##
'''
This protocol decoder can decode synchronous parallel buses with various
number of data bits/channels and one (optional) clock line.
If no clock line is supplied, the decoder works slightly differently in
that it interprets every transition on any of the supplied data channels
like there had been a clock transition.
It is required to use the lowest data channels, and use consecutive ones.
For example, for a 4-bit sync parallel bus, channels D0/D1/D2/D3 (and CLK)
should be used. Using combinations like D7/D12/D3/D15 is not supported.
For an 8-bit bus you should use D0-D7, for a 16-bit bus use D0-D15 and so on.
'''
from .pd import Decoder
Binary file not shown.
Binary file not shown.
+260
View File
@@ -0,0 +1,260 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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
from common.srdhelper import bitpack
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <pdata>]
<ptype>, <pdata>
- 'ITEM', [<item>, <itembitsize>]
- 'WORD', [<word>, <wordbitsize>, <worditemcount>]
<item>:
- A single item (a number). It can be of arbitrary size. The max. number
of bits in this item is specified in <itembitsize>.
<itembitsize>:
- The size of an item (in bits). For a 4-bit parallel bus this is 4,
for a 16-bit parallel bus this is 16, and so on.
<word>:
- A single word (a number). It can be of arbitrary size. The max. number
of bits in this word is specified in <wordbitsize>. The (exact) number
of items in this word is specified in <worditemcount>.
<wordbitsize>:
- The size of a word (in bits). For a 2-item word with 8-bit items
<wordbitsize> is 16, for a 3-item word with 4-bit items <wordbitsize>
is 12, and so on.
<worditemcount>:
- The size of a word (in number of items). For a 4-item word (no matter
how many bits each item consists of) <worditemcount> is 4, for a 7-item
word <worditemcount> is 7, and so on.
'''
def channel_list(num_channels):
l = [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}]
for i in range(num_channels):
d = {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data line %d' % i}
l.append(d)
return tuple(l)
class ChannelError(Exception):
pass
NUM_CHANNELS = 32
class Decoder(srd.Decoder):
api_version = 3
id = 'parallel'
name = 'Parallel'
longname = 'Parallel sync bus'
desc = 'Generic parallel synchronous bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['parallel']
tags = ['Util']
optional_channels = channel_list(NUM_CHANNELS)
options = (
{'id': 'clock_edge', 'desc': 'Clock edge to sample on',
'default': 'rising', 'values': ('rising', 'falling'), 'idn':'dec_parallel_opt_clock_edge'},
{'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
'default': 0, 'idn':'dec_parallel_opt_wordsize'},
{'id': 'endianness', 'desc': 'Data endianness',
'default': 'little', 'values': ('little', 'big'), 'idn':'dec_parallel_opt_endianness'},
)
annotations = (
('items', 'Items'),
('words', 'Words'),
)
annotation_rows = (
('items', 'Items', (0,)),
('words', 'Words', (1,)),
)
def __init__(self):
self.reset()
def reset(self):
self.items = []
self.saved_item = None
self.saved_word = None
self.ss_word = self.es_word = None
self.first = True
self.have_clock = True
self.prv_dex = 0
self.num_item_bits = None
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
def putpw(self, data):
self.put(self.ss_word, self.es_word, self.out_python, data)
def putw(self, data):
self.put(self.ss_word, self.es_word, self.out_ann, data)
def put_ann(self, s, e, data):
self.put(s, e, self.out_ann, data)
def put_py(self, s, e, data):
self.put(s, e, self.out_python, data)
def handle_bits(self, item):
# If a word was previously accumulated, then emit its annotation
# now after its end samplenumber became available.
cur_dex = self.samplenum
# Defer annotations for individual items until the next sample
# is taken, and the previous sample's end samplenumber has
# become available.
if self.first:
# Save the start sample and item for later (no output yet).
if not self.have_clock:
self.put_py(self.prv_dex, cur_dex, ['ITEM', self.saved_item])
self.put_ann(self.prv_dex, cur_dex, [0, [self.fmt_item.format(self.saved_item)]])
self.first = False
self.saved_item = item
else:
# Output the saved item (from the last CLK edge to the current).
self.put_py(self.prv_dex, cur_dex, ['ITEM', self.saved_item])
self.put_ann(self.prv_dex, cur_dex, [0, [self.fmt_item.format(self.saved_item)]])
self.saved_item = item
self.prv_dex = cur_dex
self.handel_word(item, cur_dex)
#word
def handel_word(self, item, cur_dex):
if self.saved_word is not None:
if self.options['wordsize'] > 0:
self.es_word = cur_dex
self.putw([1, [self.fmt_word.format(self.saved_word)]])
self.putpw(['WORD', self.saved_word])
self.saved_word = None
if item is None:
return
# Get as many items as the configured wordsize specifies.
if not self.items:
self.ss_word = cur_dex
self.items.append(item)
ws = self.options['wordsize']
if len(self.items) < ws:
return
# Collect words and prepare annotation details, but defer emission
# until the end samplenumber becomes available.
endian = self.options['endianness']
if endian == 'big':
self.items.reverse()
word = sum([self.items[i] << (i * self.num_item_bits) for i in range(ws)])
self.saved_word = word
self.items = []
def end(self):
cur_dex = self.last_samplenum
#the last annotation
if self.saved_item != None:
self.put_py(self.prv_dex, cur_dex, ['ITEM', self.saved_item])
self.put_ann(self.prv_dex, cur_dex, [0, [self.fmt_item.format(self.saved_item)]])
self.handel_word(None, cur_dex)
def decode(self):
# Determine which (optional) channels have input data. Insist in
# a non-empty input data set. Cope with sparse connection maps.
# Store enough state to later "compress" sampled input data.
max_possible = len(self.optional_channels)
idx_channels = [
idx if self.has_channel(idx) else None
for idx in range(max_possible)
]
has_channels = [idx for idx in idx_channels if idx is not None]
if not has_channels:
raise ChannelError('At least one channel has to be supplied.')
max_connected = max(has_channels)
self.have_clock = self.has_channel(0)
self.prv_dex = self.samplenum
have_clock = self.have_clock
# Determine .wait() conditions, depending on the presence of a
# clock signal. Either inspect samples on the configured edge of
# the clock, or inspect samples upon ANY edge of ANY of the pins
# which provide input data.
if have_clock:
edge = self.options['clock_edge'][0]
conds = {0: edge} #'f' or 'r'
else:
conds = [{idx: 'e'} for idx in has_channels]
# Pre-determine which input data to strip off, the width of
# individual items and multiplexed words, as well as format
# strings here. This simplifies call sites which run in tight
# loops later.
idx_strip = max_connected + 1
num_item_bits = idx_strip - 1
num_word_items = self.options['wordsize']
num_word_bits = num_item_bits * num_word_items
num_digits = (num_item_bits + 3) // 4
self.fmt_item = "@{{:0{}X}}".format(num_digits)
num_digits = (num_word_bits + 3) // 4
self.fmt_word = "@{{:0{}X}}".format(num_digits)
self.num_item_bits = num_item_bits
# Keep processing the input stream. Assume "always zero" for
# not-connected input lines. Pass data bits (all inputs except
# clock) to the handle_bits() method.
is_first = True
the_conds = conds
while True:
if not have_clock and is_first:
#get the value at sample 0
conds = None
else:
conds = the_conds
(clk, d0, d1, d2, d3, d4, d5, d6, d7,d8, d9,d10 ,d11 ,d12 ,d13 ,d14 ,d15 ,d16 ,d17 ,d18 ,d19 ,d20 ,d21 ,d22 ,d23 ,d24 ,d25 ,d26 ,d27 ,d28 ,d29 ,d30 ,d31 ) = self.wait(conds)
pins = (clk, d0, d1, d2, d3, d4, d5, d6, d7,d8, d9, d10, d11, d12,d13 ,d14 ,d15 ,d16 ,d17 ,d18 ,d19 ,d20 ,d21 ,d22 ,d23 ,d24 ,d25 ,d26 ,d27 ,d28 ,d29 ,d30 ,d31 )
bits = [0 if idx is None else pins[idx] for idx in idx_channels]
item = bitpack(bits[1:idx_strip])
if not have_clock and is_first:
is_first = False
self.saved_item = item
continue
self.handle_bits(item)
+40
View File
@@ -0,0 +1,40 @@
## xy2-100解码器
2025-9-24 chenyue
### 问题
DSView默认的xy2-100解码器,解析的位置数据是错误的(比如0x7fff被错误解析为-32769,0x8000被解析为0)。
### 解决方案
采用修正后的解码器xy2-100-bsl,修正后的振镜位置区间为[0,65535]
### 使用方法
将附件中的解码器xy2-100-bsl解压后放到DSview安装目录(C:\Program Files\DSView\decoders),重新启动DSView既可以看到新的解码器xy2-100-bsl
## 如何合并git子项目
#!/bin/bash
### 配置变量
主仓库路径="../"
要合并的仓库URL="xy2-100-bsl"
子目录名="xy2-100-cy"
分支名="master" # 或 master
### 克隆要合并的仓库
git clone "$要合并的仓库URL" temp-repo
cd temp-repo
### 使用 filter-repo 移动文件
git filter-repo --to-subdirectory-filter "$子目录名"
### 返回主仓库
cd "$主仓库路径"
### 添加远程并合并
git remote add temp-remote ./temp-repo
git fetch temp-remote
git merge temp-remote/$分支名 --allow-unrelated-histories -m "合并 $子目录名 到子目录"
### 清理
git remote remove temp-remote
+136 -7
View File
@@ -20,13 +20,13 @@
import sigrokdecode as srd
ann_bit, ann_stat_bit, ann_type, ann_command, ann_parameter, ann_parity, ann_pos, ann_status, ann_warning = range(9)
ann_bit,ann_bit2, ann_stat_bit, ann_type, ann_command, ann_parameter, ann_parity, ann_type2, ann_command2, ann_parameter2, ann_parity2, ann_pos,ann_pos2, ann_status, ann_warning, ann_warning2 = range(16)
frame_type_none, frame_type_command, frame_type_16bit_pos, frame_type_18bit_pos = range(4)
class Decoder(srd.Decoder):
api_version = 3
id = 'xy2-100-bsl'
name = 'XY2-100-bsl'
id = 'xy2-100-cy'
name = 'XY2-100-cy'
longname = 'XY2-100(E) and XY-200(E) galvanometer protocol'
desc = 'Serial protocol for galvanometer positioning in laser systems'
license = 'gplv2+'
@@ -40,38 +40,53 @@ class Decoder(srd.Decoder):
{'id': 'sync', 'name': 'SYNC', 'desc': 'Sync'},
{'id': 'data', 'name': 'DATA', 'desc': 'X, Y or Z axis data'},
)
optional_channels = (
optional_channels = (
{'id': 'data2', 'name': 'DATA2', 'desc': 'X, Y or Z axis data'},
{'id': 'status', 'name': 'STAT', 'desc': 'X, Y or Z axis status'},
)
annotations = (
('bit', 'Data Bit'),
('bit', 'Data Bit'),
('stat_bit', 'Status Bit'),
('type', 'Frame Type'),
('command', 'Command'),
('parameter', 'Parameter'),
('parity', 'Parity'),
('type', 'Frame Type'),
('command', 'Command'),
('parameter', 'Parameter'),
('parity', 'Parity'),
('position', 'Position'),
('position', 'Position'),
('status', 'Status'),
('warning', 'Human-readable warnings'),
('warning', 'Human-readable warnings'),
)
annotation_rows = (
('bits', 'Data Bits', (ann_bit,)),
('bits2', 'Data Bits2', (ann_bit2,)),
('stat_bits', 'Status Bits', (ann_stat_bit,)),
('data', 'Data', (ann_type, ann_command, ann_parameter, ann_parity)),
('data2', 'Data2', (ann_type2, ann_command2, ann_parameter2, ann_parity2)),
('positions', 'Positions', (ann_pos,)),
('positions2', 'Positions2', (ann_pos2,)),
('statuses', 'Statuses', (ann_status,)),
('warnings', 'Warnings', (ann_warning,)),
('warnings', 'Warnings', (ann_warning2,)),
)
def __init__(self):
self.samplerate = None
self.reset()
self.reset2()
def reset(self):
self.bits = []
self.stat_bits = []
self.stat_skip_bit = True
def reset2(self):
self.bits2 = []
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
@@ -192,6 +207,116 @@ class Decoder(srd.Decoder):
self.reset()
def process_bit2(self, sync, bit_ss, bit_es, bit2_value):
self.put_ann(bit_ss, bit_es, ann_bit2, ['%d' % bit2_value])
self.bits2.append((bit_ss, bit_es, bit2_value))
if sync == 0:
if len(self.bits2) < 20:
self.put_ann(self.bits2[0][0], bit_es, ann_warning2, ['Not enough data bits 0x%X' % len(self.bits2)])
self.reset2()
return
# Bit structure:
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# T --------------- 18-bit pos ----------------- PARITY or
# -TYPE-- ------------ 16-bit pos -------------- PARITY or
# -TYPE-- -8-bit command -8-bit parameter value- PARITY
# Calculate parity, excluding the parity bit itself
parity = 0
for ss, es, value in self.bits2[:-1]:
parity ^= value
par_ss, par_es, par_value = self.bits2[19]
parity_even = 0
parity_odd = 0
if (par_value == parity):
parity_even = 1
else:
parity_odd = 1
type_1_value = self.bits2[0][2]
type_3_value = (self.bits2[0][2] << 2) | (self.bits2[1][2] << 1) | self.bits2[2][2]
# Determine frame type
type = frame_type_none
parity_status = ['X', 'Unknown']
type_ss = self.bits2[0][0]
type_es = self.bits2[2][1]
### 18-bit position
if (type_1_value == 1) and (parity_odd == 1):
type = frame_type_18bit_pos
type_es = self.bits2[0][1]
self.put_ann(self.bits2[0][0], bit_es, ann_warning2, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
### 16-bit position
elif (type_3_value == 1):
type = frame_type_16bit_pos
if (parity_even == 1):
parity_status = ['OK']
else:
parity_status = ['NOK']
self.put_ann(self.bits2[0][0], bit_es, ann_warning2, ['Parity error', 'PE'])
### Command
elif (type_3_value == 7) and (parity_even == 1):
type = frame_type_command
self.put_ann(self.bits2[0][0], bit_es, ann_warning2, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
### Other
else:
self.put_ann(self.bits2[0][0], bit_es, ann_warning2, ['Error', 'Unknown command or parity error'])
self.reset2()
return
# Output command and parity annotations
if (type == frame_type_16bit_pos):
self.put_ann(type_ss, type_es, ann_type2, ['16 bit Position Frame', '16 bit Pos', 'Pos', 'P'])
if (type == frame_type_18bit_pos):
self.put_ann(type_ss, type_es, ann_type2, ['18 bit Position Frame', '18 bit Pos', 'Pos', 'P'])
if (type == frame_type_command):
self.put_ann(type_ss, type_es, ann_type2, ['Command Frame', 'Command', 'C'])
self.put_ann(par_ss, par_es, ann_parity2, parity_status)
# Output value
if (type == frame_type_16bit_pos) or (type == frame_type_18bit_pos):
pos = 0
if (type == frame_type_16bit_pos):
count = 15
for ss, es, value in self.bits2[3:19]:
pos |= value << count
count -= 1
# pos = pos if pos < 32768 else pos - 65536
else:
count = 17
for ss, es, value in self.bits2[3:19]:
pos |= value << count
count -= 1
pos = pos if pos < 131072 else pos - 262144
self.put_ann(type_es, par_ss, ann_pos2, ['%d' % pos])
if (type == frame_type_command):
count = 7
cmd = 0
cmd_es = 0
for ss, es, value in self.bits2[3:11]:
cmd |= value << count
count -= 1
cmd_es = es
self.put_ann(type_es, cmd_es, ann_command2, ['Command 0x%X' % cmd, 'Cmd 0x%X' % cmd, '0x%X' % cmd])
count = 7
param = 0
for ss, es, value in self.bits2[11:19]:
param |= value << count
count -= 1
self.put_ann(cmd_es, par_ss, ann_parameter2, ['Parameter 0x%X / %d' % (param, param), '0x%X / %d' % (param, param),'0x%X' % param])
self.reset2()
def process_stat_bit(self, sync, bit_ss, bit_es, bit_value):
if self.stat_skip_bit:
self.stat_skip_bit = False
@@ -215,15 +340,16 @@ class Decoder(srd.Decoder):
bit_ss = None
bit_es = None
bit_value = 0
bit2_value = 0
stat_ss = None
stat_es = None
stat_value = 0
sync_value = 0
has_stat = self.has_channel(3)
has_data2 = self.has_channel(3)
has_stat = self.has_channel(4)
while True:
# Wait for any edge on clk
clk, sync, data, stat = self.wait({0: 'e'})
clk, sync, data, data2, stat = self.wait({0: 'e'})
if clk == 1:
stat_value = stat
@@ -231,9 +357,12 @@ class Decoder(srd.Decoder):
bit_es = self.samplenum
if bit_ss:
self.process_bit(sync_value, bit_ss, bit_es, bit_value)
if has_data2:
self.process_bit2(sync_value, bit_ss, bit_es, bit2_value)
bit_ss = self.samplenum
else:
bit_value = data
bit2_value = data2
sync_value = sync
stat_es = self.samplenum