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)