Add DSLogic capture, signal conversion and DSView decoders

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent cc22c803d1
commit 513e79b127
44 changed files with 5982 additions and 36 deletions

View File

@@ -0,0 +1,82 @@
import sigrokdecode as srd
from common.setgui_decoders.pm35_uart import PM35Parser
class Decoder(srd.Decoder):
api_version = 3
id = 'pm35_uart'
name = 'PM35 UART'
longname = 'PM35 / TMS320F28335 terminal'
desc = 'MODBUS 03/06 register traffic and CRC16.'
license = 'gplv2+'
inputs = ['uart']
outputs = ['pm35']
tags = ['Embedded/industrial']
options = (
{'id': 'rx_role', 'desc': 'RX carries', 'default': 'response', 'values': ('request', 'response', 'auto')},
{'id': 'tx_role', 'desc': 'TX carries', 'default': 'request', 'values': ('request', 'response', 'auto')},
{'id': 'baudrate', 'desc': 'UART baudrate (RTU gap detection)', 'default': 115200},
{'id': 'bits_per_char', 'desc': 'Bits per UART character including start/stop/parity', 'default': 10},
)
annotations = (('frame', 'Frame'), ('error', 'Error'))
annotation_rows = (('frames', 'Frames', (0,)), ('errors', 'Errors', (1,)))
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.parsers = {}
self.last = {}
self.samplerate = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Аннотации предназначены для дорожек, структурированные события — для стека.
def start(self):
if self.options['baudrate'] <= 0 or self.options['bits_per_char'] <= 0:
raise ValueError('Baudrate and bits per character must be positive')
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_python = self.register(srd.OUTPUT_PYTHON)
def emit(self, events, direction):
for ss, es, result, error in events:
self.put(ss, es, self.out_ann, [1 if error else 0,
[('RX', 'TX')[direction] + ' ' + (error or result['summary'])]])
if result:
result['direction'] = direction
self.put(ss, es, self.out_python, ['FRAME', result])
def decode(self, ss, es, data):
kind, direction, payload = data
if direction not in (0, 1):
return
parser = self.parsers.setdefault(direction, PM35Parser(self.options[('rx_role', 'tx_role')[direction]]))
if kind in ('INVALID STOPBIT', 'INVALID STARTBIT', 'PARITY ERROR', 'BREAK'):
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, [kind]])
return
if kind != 'FRAME':
return
if not payload[1]:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, ['Invalid UART frame']])
return
# Граница MODBUS определяется паузой 3,5 символа. bits_per_char включает
# все биты символа; эта настройка должна соответствовать базовому UART.
if self.samplerate and direction in self.last:
gap = self.samplerate * 3.5 * self.options['bits_per_char'] / self.options['baudrate']
if ss - self.last[direction] >= gap:
self.emit(parser.flush(), direction)
self.last[direction] = es
if not 0 <= payload[0] <= 255:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, ['Configure UART for 8 data bits']])
return
self.emit(parser.feed(payload[0], ss, es), direction)