diff options
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | decoders/Makefile.am | 3 | ||||
-rw-r--r-- | decoders/onewire/Makefile.am | 26 | ||||
-rw-r--r-- | decoders/onewire/__init__.py | 115 | ||||
-rw-r--r-- | decoders/onewire/onewire.py | 197 |
5 files changed, 341 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index f42f0f4..ae68871 100644 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,7 @@ AC_CONFIG_FILES([Makefile decoders/transitioncounter/Makefile decoders/uart/Makefile decoders/usb/Makefile + decoders/onewire/Makefile ]) AC_OUTPUT diff --git a/decoders/Makefile.am b/decoders/Makefile.am index 0256d16..2fb35a5 100644 --- a/decoders/Makefile.am +++ b/decoders/Makefile.am @@ -37,5 +37,6 @@ SUBDIRS = \ spi \ transitioncounter \ uart \ - usb + usb \ + onewire diff --git a/decoders/onewire/Makefile.am b/decoders/onewire/Makefile.am new file mode 100644 index 0000000..d08a61c --- /dev/null +++ b/decoders/onewire/Makefile.am @@ -0,0 +1,26 @@ +## +## This file is part of the sigrok 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, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +pkgdatadir = $(DECODERS_DIR)/onewire + +dist_pkgdata_DATA = __init__.py onewire.py + +CLEANFILES = *.pyc + diff --git a/decoders/onewire/__init__.py b/decoders/onewire/__init__.py new file mode 100644 index 0000000..1d3c7d1 --- /dev/null +++ b/decoders/onewire/__init__.py @@ -0,0 +1,115 @@ +## +## This file is part of the sigrok 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, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +''' +1-Wire protocol decoder. + +Universal Asynchronous Receiver Transmitter (UART) is a simple serial +communication protocol which allows two devices to talk to each other. + +It uses just two data signals and a ground (GND) signal: + - RX/RXD: Receive signal + - TX/TXD: Transmit signal + +The protocol is asynchronous, i.e., there is no dedicated clock signal. +Rather, both devices have to agree on a baudrate (number of bits to be +transmitted per second) beforehand. Baudrates can be arbitrary in theory, +but usually the choice is limited by the hardware UARTs that are used. +Common values are 9600 or 115200. + +The protocol allows full-duplex transmission, i.e. both devices can send +data at the same time. However, unlike SPI (which is always full-duplex, +i.e., each send operation is automatically also a receive operation), UART +allows one-way communication, too. In such a case only one signal (and GND) +is required. + +The data is sent over the TX line in so-called 'frames', which consist of: + - Exactly one start bit (always 0/low). + - Between 5 and 9 data bits. + - An (optional) parity bit. + - One or more stop bit(s). + +The idle state of the RX/TX line is 1/high. As the start bit is 0/low, the +receiver can continually monitor its RX line for a falling edge, in order +to detect the start bit. + +Once detected, it can (due to the agreed-upon baudrate and thus the known +width/duration of one UART bit) sample the state of the RX line "in the +middle" of each (start/data/parity/stop) bit it wants to analyze. + +It is configurable whether there is a parity bit in a frame, and if yes, +which type of parity is used: + - None: No parity bit is included. + - Odd: The number of 1 bits in the data (and parity bit itself) is odd. + - Even: The number of 1 bits in the data (and parity bit itself) is even. + - Mark/one: The parity bit is always 1/high (also called 'mark state'). + - Space/zero: The parity bit is always 0/low (also called 'space state'). + +It is also configurable how many stop bits are to be used: + - 1 stop bit (most common case) + - 2 stop bits + - 1.5 stop bits (i.e., one stop bit, but 1.5 times the UART bit width) + - 0.5 stop bits (i.e., one stop bit, but 0.5 times the UART bit width) + +The bit order of the 5-9 data bits is LSB-first. + +Possible special cases: + - One or both data lines could be inverted, which also means that the idle + state of the signal line(s) is low instead of high. + - Only the data bits on one or both data lines (and the parity bit) could + be inverted (but the start/stop bits remain non-inverted). + - The bit order could be MSB-first instead of LSB-first. + - The baudrate could change in the middle of the communication. This only + happens in very special cases, and can only work if both devices know + to which baudrate they are to switch, and when. + - Theoretically, the baudrate on RX and the one on TX could also be + different, but that's a very obscure case and probably doesn't happen + very often in practice. + +Error conditions: + - If there is a parity bit, but it doesn't match the expected parity, + this is called a 'parity error'. + - If there are no stop bit(s), that's called a 'frame error'. + +More information: +TODO: URLs + +Protocol output format: + +UART packet: +[<packet-type>, <rxtx>, <packet-data>] + +This is the list of <packet-types>s and their respective <packet-data>: + - 'STARTBIT': The data is the (integer) value of the start bit (0/1). + - 'DATA': The data is the (integer) value of the UART data. Valid values + range from 0 to 512 (as the data can be up to 9 bits in size). + - 'PARITYBIT': The data is the (integer) value of the parity bit (0/1). + - 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1). + - 'INVALID STARTBIT': The data is the (integer) value of the start bit (0/1). + - 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1). + - 'PARITY ERROR': The data is a tuple with two entries. The first one is + the expected parity value, the second is the actual parity value. + - TODO: Frame error? + +The <rxtx> field is 0 for RX packets, 1 for TX packets. +''' + +from .onewire import * + diff --git a/decoders/onewire/onewire.py b/decoders/onewire/onewire.py new file mode 100644 index 0000000..f69c595 --- /dev/null +++ b/decoders/onewire/onewire.py @@ -0,0 +1,197 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2011-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, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +# 1-Wire protocol decoder + +import sigrokdecode as srd + +# Annotation feed formats +ANN_ASCII = 0 +ANN_DEC = 1 +ANN_HEX = 2 +ANN_OCT = 3 +ANN_BITS = 4 + +class Decoder(srd.Decoder): + api_version = 1 + id = 'onewire' + name = '1-Wire' + longname = '' + desc = '1-Wire bus and MicroLan' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['onewire'] + probes = [ + {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire bus'}, + ] + optional_probes = [ + {'id': 'pwr', 'name': 'PWR', 'desc': '1-Wire power'}, + ] + options = { + 'overdrive': ['Overdrive', 0], + } + annotations = [ + ['ASCII', 'Data bytes as ASCII characters'], + ['Decimal', 'Databytes as decimal, integer values'], + ['Hex', 'Data bytes in hex format'], + ['Octal', 'Data bytes as octal numbers'], + ['Bits', 'Data bytes in bit notation (sequence of 0/1 digits)'], + ] + + def putx(self, data): + self.put(self.startsample, self.samplenum - 1, self.out_ann, data) + + def __init__(self, **kwargs): + # Common variables + self.samplenum = 0 + # Link layer variables + self.lnk_state = 'WAIT FOR EVENT' + self.lnk_event = 'NONE' + self.lnk_start = -1 + self.lnk_bit = -1 + self.lnk_cnt = 0 + self.lnk_byte = -1 + # Network layer variables + self.net_state = 'WAIT FOR EVENT' + self.net_event = 'NONE' + self.net_command = -1 + # Transport layer variables + self.trn_state = 'WAIT FOR EVENT' + self.trn_event = 'NONE' + + self.data_sample = -1 + self.cur_data_bit = 0 + self.databyte = 0 + self.startsample = -1 + + def start(self, metadata): + self.samplerate = metadata['samplerate'] + self.out_proto = self.add(srd.OUTPUT_PROTO, 'onewire') + self.out_ann = self.add(srd.OUTPUT_ANN, 'onewire') + + # The width of the 1-Wire time base (30us) in number of samples. + # TODO: optimize this value + self.time_base = float(self.samplerate) / float(0.000030) + + def report(self): + pass + + def get_data_sample(self, owr): + # Skip samples until we're in the middle of the start bit. + if not self.reached_data_sample(): + return + + self.data_sample = owr + + self.cur_data_bit = 0 + self.databyte = 0 + self.startsample = -1 + + self.state = 'GET DATA BITS' + + self.put(self.cycle_start, self.samplenum, self.out_proto, + ['STARTBIT', self.startbit]) + self.put(self.cycle_start, self.samplenum, self.out_ann, + [ANN_ASCII, ['Start bit', 'Start', 'S']]) + + def get_data_bits(self, owr): + # Skip samples until we're in the middle of the desired data bit. + if not self.reached_bit(self.cur_data_bit + 1): + return + + # Save the sample number where the data byte starts. + if self.startsample == -1: + self.startsample = self.samplenum + + # Get the next data bit in LSB-first or MSB-first fashion. + if self.options['bit_order'] == 'lsb-first': + self.databyte >>= 1 + self.databyte |= \ + (owr << (self.options['num_data_bits'] - 1)) + elif self.options['bit_order'] == 'msb-first': + self.databyte <<= 1 + self.databyte |= (owr << 0) + else: + raise Exception('Invalid bit order value: %s', + self.options['bit_order']) + + # Return here, unless we already received all data bits. + # TODO? Off-by-one? + if self.cur_data_bit < self.options['num_data_bits'] - 1: + self.cur_data_bit += 1 + return + + self.state = 'GET PARITY BIT' + + self.put(self.startsample, self.samplenum - 1, self.out_proto, + ['DATA', self.databyte]) + + self.putx([ANN_ASCII, [chr(self.databyte)]]) + self.putx([ANN_DEC, [str(self.databyte)]]) + self.putx([ANN_HEX, [hex(self.databyte), + hex(self.databyte)[2:]]]) + self.putx([ANN_OCT, [oct(self.databyte), + oct(self.databyte)[2:]]]) + self.putx([ANN_BITS, [bin(self.databyte), + bin(self.databyte)[2:]]]) + + def decode(self, ss, es, data): + for (self.samplenum, owr) in data: + + # First sample: Save OWR value. + if self.oldbit == None: + self.oldbit = owr + continue + + # Data link layer + if self.lnk_state == 'WAIT FOR FALLING EDGE': + # The start of a cycle is a falling edge. + if (old_owr == 1 and owr == 0): + # Save the sample number where the start bit begins. + self.lnk_start = self.samplenum + # Go to waiting for sample time + self.lnk_state = 'WAIT FOR SAMPLE' + elif self.lnk_state == 'WAIT FOR SAMPLE': + # Data should be sample one 'time unit' after a falling edge + if (self.samplenum == self.lnk_start + self.time_base): + self.lnk_bit = owr & 0x1 + self.lnk_cnt = self.lnk_cnt + 1 + self.lnk_byte = (self.lnk_byte << 1) & self.lnk_bit + self.lnk_state = 'WAIT FOR RISING EDGE' + elif self.lnk_state == 'WAIT FOR RISING EDGE': + # The end of a cycle is a rising edge. + if (old_owr == 0 and owr == 1): + # Data bit cycle length should be between 2*T and + if (self.samplenum < self.lnk_start + 2*self.time_base): + if (self.lnk_cnt == 8) + self.put(self.startsample, self.samplenum - 1, self.out_proto, ['BYTE', self.lnk_byte]) + self.lnk_cnt = 0 + if (self.samplenum == self.lnk_start + 8*self.time_base): + self.put(self.startsample, self.samplenum - 1, self.out_proto, ['RESET']) + + # Go to waiting for sample time + self.lnk_state = 'WAIT FOR SAMPLE' + + elif self.state_lnk == 'GET DATA BITS' : self.get_data_bits(owr) + else : raise Exception('Invalid state: %d' % self.state) + + # Save current RX/TX values for the next round. + self.oldbit = owr + |