AC_CONFIG_FILES([Makefile
libsigrokdecode.pc
decoders/Makefile
+ decoders/dcf77/Makefile
+ decoders/ddc/Makefile
+ decoders/ebr30a_i2c_demux/Makefile
+ decoders/i2c/Makefile
+ decoders/mx25lxx05d/Makefile
+ decoders/nunchuk/Makefile
+ decoders/pan1321/Makefile
+ decoders/spi/Makefile
+ decoders/transitioncounter/Makefile
+ decoders/uart/Makefile
+ decoders/usb/Makefile
])
AC_OUTPUT
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <glib.h>
#include "config.h"
#include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
#include "sigrokdecode-internal.h"
-#include <dirent.h>
/* The list of protocol decoders. */
GSList *pd_list = NULL;
/**
- * Load a protocol decoder module into the embedded python interpreter.
+ * Load a protocol decoder module into the embedded Python interpreter.
*
* @param name The module name to be loaded.
* @param dec Pointer to the struct srd_decoder filled with the loaded module.
py_basedec = NULL;
ret = SRD_ERR;
- srd_dbg("loading module %s", name);
+
+ srd_dbg("decoder: %s: loading module '%s'", __func__, name);
if (!(d = g_try_malloc0(sizeof(struct srd_decoder)))) {
+ srd_err("decoder: %s: d malloc failed", __func__);
ret = SRD_ERR_MALLOC;
goto err_out;
}
/* Import the Python module. */
if (!(d->py_mod = PyImport_ImportModule(name))) {
- /* TODO: report exception message/traceback to err/dbg */
- srd_dbg("import failed");
+ /* TODO: Report exception message/traceback to err/dbg. */
+ srd_warn("decoder: %s: import of '%s' failed", __func__, name);
+ PyErr_Print();
PyErr_Clear();
ret = SRD_ERR_PYTHON;
goto err_out;
/* Get the 'Decoder' class as Python object. */
if (!(d->py_dec = PyObject_GetAttrString(d->py_mod, "Decoder"))) {
/* This generated an AttributeError exception. */
+ PyErr_Print();
PyErr_Clear();
srd_err("Decoder class not found in protocol decoder module %s", name);
ret = SRD_ERR_PYTHON;
return SRD_OK;
}
+/**
+ * Check if the directory in the specified search path is a valid PD dir.
+ *
+ * A valid sigrok protocol decoder consists of a directory, which contains
+ * at least two .py files (the special __init__.py and at least one additional
+ * .py file which contains the actual PD code).
+ *
+ * TODO: We should also check that this is not a random other Python module,
+ * but really a sigrok PD module by some means.
+ *
+ * @param search_path A string containing the (absolute) path to the directory
+ * where 'entry' resides in.
+ * @param entry A string containing the (relative) directory name of the
+ * sigrok PD module. E.g. "i2c" for the I2C protocol decoder.
+ * @return SRD_OK, if the directory is a valid sigrok PD, a negative error
+ * code, such as SRD_ERR, otherwise.
+ */
+int srd_is_valid_pd_dir(const gchar *search_path, const gchar *entry)
+{
+ GDir *dir;
+ int py_files = 0, has_init_py = 0;
+ gchar *path1, *path2, *file;
+ GError *error;
+
+ /* TODO: Error handling. */
+ path1 = g_build_filename(search_path, entry, NULL);
+
+ /* Check that it is a directory (and exists). */
+ if (!g_file_test(path1, G_FILE_TEST_IS_DIR)) {
+ srd_dbg("decoder: %s: '%s' not a directory or doesn't exist",
+ __func__, entry);
+ return SRD_ERR;
+ }
+
+ if (!(dir = g_dir_open(path1, 0, &error))) { /* TODO: flags? */
+ srd_dbg("decoder: %s: '%s' failed to open directory",
+ __func__, entry);
+ return SRD_ERR;
+ }
+
+ /* Check the contents of the directory. */
+ while ((file = g_dir_read_name(dir)) != NULL) {
+ /* TODO: Error handling. */
+ path2 = g_build_filename(path1, file, NULL);
+
+ /* Ignore non-files. */
+ if (!g_file_test(path2, G_FILE_TEST_IS_REGULAR)) {
+ srd_spew("decoder: %s: '%s' not a file, ignoring",
+ __func__, file);
+ continue;
+ }
+
+ /* Count number of .py files. */
+ if (g_str_has_suffix(path2, ".py")) {
+ srd_spew("decoder: %s: found .py file: '%s'",
+ __func__, file);
+ py_files++;
+ }
+
+ /* Check if it's an __init__.py file. */
+ if (g_str_has_suffix(path2, "__init__.py")) {
+ srd_spew("decoder: %s: found __init__.py file: '%s'",
+ __func__, path2);
+ has_init_py = 1;
+ }
+ }
+ g_dir_close(dir);
+
+ /* Check if the directory contains >= 2 *.py files. */
+ if (py_files < 2) {
+ srd_dbg("decoder: %s: '%s' is not a valid PD dir, it doesn't "
+ "contain >= 2 .py files", __func__, entry);
+ return SRD_ERR;
+ }
+
+ /* Check if the directory contains an __init__.py file. */
+ if (!has_init_py) {
+ srd_dbg("decoder: %s: '%s' is not a valid PD dir, it doesn't "
+ "contain an __init__.py file", __func__, entry);
+ return SRD_ERR;
+ }
+
+ /* TODO: Check if it's a PD, not a random other Python module. */
+
+ return SRD_OK;
+}
int srd_load_all_decoders(void)
{
- DIR *dir;
- struct dirent *dp;
+ GDir *dir;
+ gchar *direntry;
int ret;
char *decodername;
struct srd_decoder *dec;
+ GError *error;
- if (!(dir = opendir(DECODERS_DIR))) {
+ if (!(dir = g_dir_open(DECODERS_DIR, 0, &error))) { /* TODO: flags? */
Py_Finalize(); /* Returns void. */
return SRD_ERR_DECODERS_DIR;
}
- while ((dp = readdir(dir)) != NULL) {
- /* Ignore filenames which don't end with ".py". */
- if (!g_str_has_suffix(dp->d_name, ".py"))
+ while ((direntry = g_dir_read_name(dir)) != NULL) {
+ /* Ignore directory entries which are not valid PDs. */
+ if (srd_is_valid_pd_dir(DECODERS_DIR, direntry) != SRD_OK) {
+ srd_dbg("decoder: %s: '%s' not a valid PD dir, "
+ "ignoring it", __func__, direntry);
continue;
+ }
- /* Decoder name == filename (without .py suffix). */
- decodername = g_strndup(dp->d_name, strlen(dp->d_name) - 3);
+ /* The decoder name is the PD directory name (e.g. "i2c"). */
+ decodername = g_strdup(direntry);
/* TODO: Error handling. Use g_try_malloc(). */
if (!(dec = malloc(sizeof(struct srd_decoder)))) {
pd_list = g_slist_append(pd_list, dec);
}
}
- closedir(dir);
+ g_dir_close(dir);
return SRD_OK;
}
-
/**
* TODO
*/
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
-pkgdatadir = $(DECODERS_DIR)
-
# Please keep this list in alphabetical order.
-dist_pkgdata_DATA = \
- dcf77.py \
- ddc.py \
- i2c.py \
- mx25lxx05d.py \
- nunchuk.py \
- pan1321.py \
- spi.py \
- usb.py \
- transitioncounter.py \
- ebr30a_i2c_demux.py \
- uart.py
-
-CLEANFILES = *.pyc
+SUBDIRS = \
+ dcf77 \
+ ddc \
+ ebr30a_i2c_demux \
+ i2c \
+ mx25lxx05d \
+ nunchuk \
+ pan1321 \
+ spi \
+ transitioncounter \
+ uart \
+ usb
+++ /dev/null
-##
-## 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
-##
-
-#
-# DCF77 protocol decoder
-#
-# More information:
-# http://en.wikipedia.org/wiki/DCF77
-#
-
-#
-# Protocol output format:
-# TODO
-#
-
-import sigrokdecode as srd
-import calendar
-
-# States
-WAIT_FOR_RISING_EDGE = 0
-GET_BIT = 1
-
-# Annotation feed formats
-ANN_ASCII = 0
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
- return (b & 0x0f) + ((b >> 4) * 10)
-
-class Decoder(srd.Decoder):
- id = 'dcf77'
- name = 'DCF77'
- longname = 'DCF77 time protocol'
- desc = 'TODO.'
- longdesc = 'TODO.'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['dcf77']
- probes = [
- {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
- ]
- options = {}
- annotations = [
- # ANN_ASCII
- ['ASCII', 'TODO: description'],
- ]
-
- def __init__(self, **kwargs):
- self.state = WAIT_FOR_RISING_EDGE
- self.oldval = None
- self.samplenum = 0
- self.bit_start = 0
- self.bit_start_old = 0
- self.bitcount = 0 # Counter for the DCF77 bits (0..58)
- self.dcf77_bitnumber_is_known = 0
-
- def start(self, metadata):
- self.samplerate = metadata['samplerate']
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'dcf77')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'dcf77')
-
- def report(self):
- pass
-
- # TODO: Which range to use? Only the 100ms/200ms or full second?
- def handle_dcf77_bit(self, bit):
- c = self.bitcount
- a = self.out_ann
- ss = es = 0 # FIXME
-
- # Create one annotation for each DCF77 bit (containing the 0/1 value).
- # Use 'Unknown DCF77 bit x: val' if we're not sure yet which of the
- # 0..58 bits it is (because we haven't seen a 'new minute' marker yet).
- # Otherwise, use 'DCF77 bit x: val'.
- s = '' if self.dcf77_bitnumber_is_known else 'Unknown '
- self.put(ss, es, a, [0, ['%sDCF77 bit %d: %d' % (s, c, bit)]])
-
- # If we're not sure yet which of the 0..58 DCF77 bits we have, return.
- # We don't want to decode bogus data.
- if not self.dcf77_bitnumber_is_known:
- return
-
- # Output specific "decoded" annotations for the respective DCF77 bits.
- if c == 0:
- # Start of minute: DCF bit 0.
- if bit == 0:
- self.put(ss, es, a, [0, ['Start of minute (always 0)']])
- else:
- self.put(ss, es, a, [0, ['ERROR: Start of minute != 0']])
- elif c in range(1, 14 + 1):
- # Special bits (civil warnings, weather forecast): DCF77 bits 1-14.
- if c == 1:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 1))
- if c == 14:
- self.put(ss, es, a, [0, ['Special bits: %s' % bin(self.tmp)]])
- elif c == 15:
- s = '' if (bit == 1) else 'not '
- self.put(ss, es, a, [0, ['Call bit is %sset' % s]])
- # TODO: Previously this bit indicated use of the backup antenna.
- elif c == 16:
- s = '' if (bit == 1) else 'not '
- self.put(ss, es, a, [0, ['Summer time announcement %sactive' % s]])
- elif c == 17:
- s = '' if (bit == 1) else 'not '
- self.put(ss, es, a, [0, ['CEST is %sin effect' % s]])
- elif c == 18:
- s = '' if (bit == 1) else 'not '
- self.put(ss, es, a, [0, ['CET is %sin effect' % s]])
- elif c == 19:
- s = '' if (bit == 1) else 'not '
- self.put(ss, es, a, [0, ['Leap second announcement %sactive' % s]])
- elif c == 20:
- # Start of encoded time: DCF bit 20.
- if bit == 1:
- self.put(ss, es, a, [0, ['Start of encoded time (always 1)']])
- else:
- self.put(ss, es, a,
- [0, ['ERROR: Start of encoded time != 1']])
- elif c in range(21, 27 + 1):
- # Minutes (0-59): DCF77 bits 21-27 (BCD format).
- if c == 21:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 21))
- if c == 27:
- self.put(ss, es, a, [0, ['Minutes: %d' % bcd2int(self.tmp)]])
- elif c == 28:
- # Even parity over minute bits (21-28): DCF77 bit 28.
- self.tmp |= (bit << (c - 21))
- parity = bin(self.tmp).count('1')
- s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
- self.put(ss, es, a, [0, ['Minute parity: %s' % s]])
- elif c in range(29, 34 + 1):
- # Hours (0-23): DCF77 bits 29-34 (BCD format).
- if c == 29:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 29))
- if c == 34:
- self.put(ss, es, a, [0, ['Hours: %d' % bcd2int(self.tmp)]])
- elif c == 35:
- # Even parity over hour bits (29-35): DCF77 bit 35.
- self.tmp |= (bit << (c - 29))
- parity = bin(self.tmp).count('1')
- s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
- self.put(ss, es, a, [0, ['Hour parity: %s' % s]])
- elif c in range(36, 41 + 1):
- # Day of month (1-31): DCF77 bits 36-41 (BCD format).
- if c == 36:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 36))
- if c == 41:
- self.put(ss, es, a, [0, ['Day: %d' % bcd2int(self.tmp)]])
- elif c in range(42, 44 + 1):
- # Day of week (1-7): DCF77 bits 42-44 (BCD format).
- # A value of 1 means Monday, 7 means Sunday.
- if c == 42:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 42))
- if c == 44:
- d = bcd2int(self.tmp)
- dn = calendar.day_name[d - 1] # day_name[0] == Monday
- self.put(ss, es, a, [0, ['Day of week: %d (%s)' % (d, dn)]])
- elif c in range(45, 49 + 1):
- # Month (1-12): DCF77 bits 45-49 (BCD format).
- if c == 45:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 45))
- if c == 49:
- m = bcd2int(self.tmp)
- mn = calendar.month_name[m] # month_name[1] == January
- self.put(ss, es, a, [0, ['Month: %d (%s)' % (m, mn)]])
- elif c in range(50, 57 + 1):
- # Year (0-99): DCF77 bits 50-57 (BCD format).
- if c == 50:
- self.tmp = bit
- else:
- self.tmp |= (bit << (c - 50))
- if c == 57:
- self.put(ss, es, a, [0, ['Year: %d' % bcd2int(self.tmp)]])
- elif c == 58:
- # Even parity over date bits (36-58): DCF77 bit 58.
- self.tmp |= (bit << (c - 50))
- parity = bin(self.tmp).count('1')
- s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
- self.put(ss, es, a, [0, ['Date parity: %s' % s]])
- else:
- raise Exception('Invalid DCF77 bit: %d' % c)
-
- def decode(self, ss, es, data):
- for samplenum, (pon, val) in data: # FIXME
-
- self.samplenum += 1 # FIXME. Use samplenum. Off-by-one?
-
- if self.state == WAIT_FOR_RISING_EDGE:
- # Wait until the next rising edge occurs.
- if not (self.oldval == 0 and val == 1):
- self.oldval = val
- continue
-
- # Save the sample number where the DCF77 bit begins.
- self.bit_start = self.samplenum
-
- # Calculate the length (in ms) between two rising edges.
- len_edges = self.bit_start - self.bit_start_old
- len_edges_ms = int((len_edges / self.samplerate) * 1000)
-
- # The time between two rising edges is usually around 1000ms.
- # For DCF77 bit 59, there is no rising edge at all, i.e. the
- # time between DCF77 bit 59 and DCF77 bit 0 (of the next
- # minute) is around 2000ms. Thus, if we see an edge with a
- # 2000ms distance to the last one, this edge marks the
- # beginning of a new minute (and DCF77 bit 0 of that minute).
- if len_edges_ms in range(1600, 2400 + 1):
- self.put(ss, es, self.out_ann, [0, ['New minute starts']])
- self.bitcount = 0
- self.bit_start_old = self.bit_start
- self.dcf77_bitnumber_is_known = 1
- # Don't switch to GET_BIT state this time.
- continue
-
- self.bit_start_old = self.bit_start
- self.state = GET_BIT
-
- elif self.state == GET_BIT:
- # Wait until the next falling edge occurs.
- if not (self.oldval == 1 and val == 0):
- self.oldval = val
- continue
-
- # Calculate the length (in ms) of the current high period.
- len_high = self.samplenum - self.bit_start
- len_high_ms = int((len_high / self.samplerate) * 1000)
-
- # If the high signal was 100ms long, that encodes a 0 bit.
- # If it was 200ms long, that encodes a 1 bit.
- if len_high_ms in range(40, 160 + 1):
- bit = 0
- elif len_high_ms in range(161, 260 + 1):
- bit = 1
- else:
- bit = -1 # TODO: Error?
-
- # TODO: There's no bit 59, make sure none is decoded.
- if bit in (0, 1) and self.bitcount in range(0, 58 + 1):
- self.handle_dcf77_bit(bit)
- self.bitcount += 1
-
- self.state = WAIT_FOR_RISING_EDGE
-
- else:
- raise Exception('Invalid state: %s' % self.state)
-
- self.oldval = val
-
--- /dev/null
+##
+## 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)/dcf77
+
+dist_pkgdata_DATA = __init__.py dcf77.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .dcf77 import *
+
--- /dev/null
+##
+## 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
+##
+
+#
+# DCF77 protocol decoder
+#
+# More information:
+# http://en.wikipedia.org/wiki/DCF77
+#
+
+#
+# Protocol output format:
+# TODO
+#
+
+import sigrokdecode as srd
+import calendar
+
+# States
+WAIT_FOR_RISING_EDGE = 0
+GET_BIT = 1
+
+# Annotation feed formats
+ANN_ASCII = 0
+
+# Return the specified BCD number (max. 8 bits) as integer.
+def bcd2int(b):
+ return (b & 0x0f) + ((b >> 4) * 10)
+
+class Decoder(srd.Decoder):
+ id = 'dcf77'
+ name = 'DCF77'
+ longname = 'DCF77 time protocol'
+ desc = 'TODO.'
+ longdesc = 'TODO.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['dcf77']
+ probes = [
+ {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
+ ]
+ options = {}
+ annotations = [
+ # ANN_ASCII
+ ['ASCII', 'TODO: description'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.state = WAIT_FOR_RISING_EDGE
+ self.oldval = None
+ self.samplenum = 0
+ self.bit_start = 0
+ self.bit_start_old = 0
+ self.bitcount = 0 # Counter for the DCF77 bits (0..58)
+ self.dcf77_bitnumber_is_known = 0
+
+ def start(self, metadata):
+ self.samplerate = metadata['samplerate']
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'dcf77')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'dcf77')
+
+ def report(self):
+ pass
+
+ # TODO: Which range to use? Only the 100ms/200ms or full second?
+ def handle_dcf77_bit(self, bit):
+ c = self.bitcount
+ a = self.out_ann
+ ss = es = 0 # FIXME
+
+ # Create one annotation for each DCF77 bit (containing the 0/1 value).
+ # Use 'Unknown DCF77 bit x: val' if we're not sure yet which of the
+ # 0..58 bits it is (because we haven't seen a 'new minute' marker yet).
+ # Otherwise, use 'DCF77 bit x: val'.
+ s = '' if self.dcf77_bitnumber_is_known else 'Unknown '
+ self.put(ss, es, a, [0, ['%sDCF77 bit %d: %d' % (s, c, bit)]])
+
+ # If we're not sure yet which of the 0..58 DCF77 bits we have, return.
+ # We don't want to decode bogus data.
+ if not self.dcf77_bitnumber_is_known:
+ return
+
+ # Output specific "decoded" annotations for the respective DCF77 bits.
+ if c == 0:
+ # Start of minute: DCF bit 0.
+ if bit == 0:
+ self.put(ss, es, a, [0, ['Start of minute (always 0)']])
+ else:
+ self.put(ss, es, a, [0, ['ERROR: Start of minute != 0']])
+ elif c in range(1, 14 + 1):
+ # Special bits (civil warnings, weather forecast): DCF77 bits 1-14.
+ if c == 1:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 1))
+ if c == 14:
+ self.put(ss, es, a, [0, ['Special bits: %s' % bin(self.tmp)]])
+ elif c == 15:
+ s = '' if (bit == 1) else 'not '
+ self.put(ss, es, a, [0, ['Call bit is %sset' % s]])
+ # TODO: Previously this bit indicated use of the backup antenna.
+ elif c == 16:
+ s = '' if (bit == 1) else 'not '
+ self.put(ss, es, a, [0, ['Summer time announcement %sactive' % s]])
+ elif c == 17:
+ s = '' if (bit == 1) else 'not '
+ self.put(ss, es, a, [0, ['CEST is %sin effect' % s]])
+ elif c == 18:
+ s = '' if (bit == 1) else 'not '
+ self.put(ss, es, a, [0, ['CET is %sin effect' % s]])
+ elif c == 19:
+ s = '' if (bit == 1) else 'not '
+ self.put(ss, es, a, [0, ['Leap second announcement %sactive' % s]])
+ elif c == 20:
+ # Start of encoded time: DCF bit 20.
+ if bit == 1:
+ self.put(ss, es, a, [0, ['Start of encoded time (always 1)']])
+ else:
+ self.put(ss, es, a,
+ [0, ['ERROR: Start of encoded time != 1']])
+ elif c in range(21, 27 + 1):
+ # Minutes (0-59): DCF77 bits 21-27 (BCD format).
+ if c == 21:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 21))
+ if c == 27:
+ self.put(ss, es, a, [0, ['Minutes: %d' % bcd2int(self.tmp)]])
+ elif c == 28:
+ # Even parity over minute bits (21-28): DCF77 bit 28.
+ self.tmp |= (bit << (c - 21))
+ parity = bin(self.tmp).count('1')
+ s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+ self.put(ss, es, a, [0, ['Minute parity: %s' % s]])
+ elif c in range(29, 34 + 1):
+ # Hours (0-23): DCF77 bits 29-34 (BCD format).
+ if c == 29:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 29))
+ if c == 34:
+ self.put(ss, es, a, [0, ['Hours: %d' % bcd2int(self.tmp)]])
+ elif c == 35:
+ # Even parity over hour bits (29-35): DCF77 bit 35.
+ self.tmp |= (bit << (c - 29))
+ parity = bin(self.tmp).count('1')
+ s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+ self.put(ss, es, a, [0, ['Hour parity: %s' % s]])
+ elif c in range(36, 41 + 1):
+ # Day of month (1-31): DCF77 bits 36-41 (BCD format).
+ if c == 36:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 36))
+ if c == 41:
+ self.put(ss, es, a, [0, ['Day: %d' % bcd2int(self.tmp)]])
+ elif c in range(42, 44 + 1):
+ # Day of week (1-7): DCF77 bits 42-44 (BCD format).
+ # A value of 1 means Monday, 7 means Sunday.
+ if c == 42:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 42))
+ if c == 44:
+ d = bcd2int(self.tmp)
+ dn = calendar.day_name[d - 1] # day_name[0] == Monday
+ self.put(ss, es, a, [0, ['Day of week: %d (%s)' % (d, dn)]])
+ elif c in range(45, 49 + 1):
+ # Month (1-12): DCF77 bits 45-49 (BCD format).
+ if c == 45:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 45))
+ if c == 49:
+ m = bcd2int(self.tmp)
+ mn = calendar.month_name[m] # month_name[1] == January
+ self.put(ss, es, a, [0, ['Month: %d (%s)' % (m, mn)]])
+ elif c in range(50, 57 + 1):
+ # Year (0-99): DCF77 bits 50-57 (BCD format).
+ if c == 50:
+ self.tmp = bit
+ else:
+ self.tmp |= (bit << (c - 50))
+ if c == 57:
+ self.put(ss, es, a, [0, ['Year: %d' % bcd2int(self.tmp)]])
+ elif c == 58:
+ # Even parity over date bits (36-58): DCF77 bit 58.
+ self.tmp |= (bit << (c - 50))
+ parity = bin(self.tmp).count('1')
+ s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+ self.put(ss, es, a, [0, ['Date parity: %s' % s]])
+ else:
+ raise Exception('Invalid DCF77 bit: %d' % c)
+
+ def decode(self, ss, es, data):
+ for samplenum, (pon, val) in data: # FIXME
+
+ self.samplenum += 1 # FIXME. Use samplenum. Off-by-one?
+
+ if self.state == WAIT_FOR_RISING_EDGE:
+ # Wait until the next rising edge occurs.
+ if not (self.oldval == 0 and val == 1):
+ self.oldval = val
+ continue
+
+ # Save the sample number where the DCF77 bit begins.
+ self.bit_start = self.samplenum
+
+ # Calculate the length (in ms) between two rising edges.
+ len_edges = self.bit_start - self.bit_start_old
+ len_edges_ms = int((len_edges / self.samplerate) * 1000)
+
+ # The time between two rising edges is usually around 1000ms.
+ # For DCF77 bit 59, there is no rising edge at all, i.e. the
+ # time between DCF77 bit 59 and DCF77 bit 0 (of the next
+ # minute) is around 2000ms. Thus, if we see an edge with a
+ # 2000ms distance to the last one, this edge marks the
+ # beginning of a new minute (and DCF77 bit 0 of that minute).
+ if len_edges_ms in range(1600, 2400 + 1):
+ self.put(ss, es, self.out_ann, [0, ['New minute starts']])
+ self.bitcount = 0
+ self.bit_start_old = self.bit_start
+ self.dcf77_bitnumber_is_known = 1
+ # Don't switch to GET_BIT state this time.
+ continue
+
+ self.bit_start_old = self.bit_start
+ self.state = GET_BIT
+
+ elif self.state == GET_BIT:
+ # Wait until the next falling edge occurs.
+ if not (self.oldval == 1 and val == 0):
+ self.oldval = val
+ continue
+
+ # Calculate the length (in ms) of the current high period.
+ len_high = self.samplenum - self.bit_start
+ len_high_ms = int((len_high / self.samplerate) * 1000)
+
+ # If the high signal was 100ms long, that encodes a 0 bit.
+ # If it was 200ms long, that encodes a 1 bit.
+ if len_high_ms in range(40, 160 + 1):
+ bit = 0
+ elif len_high_ms in range(161, 260 + 1):
+ bit = 1
+ else:
+ bit = -1 # TODO: Error?
+
+ # TODO: There's no bit 59, make sure none is decoded.
+ if bit in (0, 1) and self.bitcount in range(0, 58 + 1):
+ self.handle_dcf77_bit(bit)
+ self.bitcount += 1
+
+ self.state = WAIT_FOR_RISING_EDGE
+
+ else:
+ raise Exception('Invalid state: %s' % self.state)
+
+ self.oldval = val
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Bert Vermeulen <bert@biot.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 3 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, If not, see <http://www.gnu.org/licenses/>.
-##
-
-'''
-This decoder extracts a DDC stream from an I2C session between a computer
-and a display device. The stream is output as plain bytes.
-
-Details:
-https://en.wikipedia.org/wiki/Display_Data_Channel
-'''
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
- id = 'ddc'
- name = 'DDC'
- longname = 'Display Data Channel'
- desc = 'A protocol for communication between computers and displays.'
- longdesc = ''
- license = 'gplv3+'
- inputs = ['i2c']
- outputs = ['ddc']
- probes = []
- options = {}
- annotations = [
- ['Byte stream', 'DDC byte stream as read from display.'],
- ]
-
- def __init__(self, **kwargs):
- self.state = None
-
- def start(self, metadata):
- self.out_ann = self.add(srd.OUTPUT_ANN, 'ddc')
-
- def decode(self, ss, es, data):
- try:
- cmd, data, ack_bit = data
- except Exception as e:
- raise Exception('malformed I2C input: %s' % str(e)) from e
-
- if self.state is None:
- # Wait for the DDC session to start.
- if cmd in ('START', 'START REPEAT'):
- self.state = 'start'
- elif self.state == 'start':
- if cmd == 'ADDRESS READ' and data == 80:
- # 80 is the I2C slave address of a connected display,
- # so this marks the start of the DDC data transfer.
- self.state = 'transfer'
- elif cmd == 'STOP':
- # Got back to the idle state.
- self.state = None
- elif self.state == 'transfer':
- if cmd == 'DATA READ':
- # There shouldn't be anything but data reads on this
- # address, so ignore everything else.
- self.put(ss, es, self.out_ann, [0, ['0x%.2x' % data]])
-
--- /dev/null
+##
+## 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)/ddc
+
+dist_pkgdata_DATA = __init__.py ddc.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .ddc import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.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 3 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, If not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder extracts a DDC stream from an I2C session between a computer
+and a display device. The stream is output as plain bytes.
+
+Details:
+https://en.wikipedia.org/wiki/Display_Data_Channel
+'''
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ id = 'ddc'
+ name = 'DDC'
+ longname = 'Display Data Channel'
+ desc = 'A protocol for communication between computers and displays.'
+ longdesc = ''
+ license = 'gplv3+'
+ inputs = ['i2c']
+ outputs = ['ddc']
+ probes = []
+ options = {}
+ annotations = [
+ ['Byte stream', 'DDC byte stream as read from display.'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.state = None
+
+ def start(self, metadata):
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'ddc')
+
+ def decode(self, ss, es, data):
+ try:
+ cmd, data, ack_bit = data
+ except Exception as e:
+ raise Exception('malformed I2C input: %s' % str(e)) from e
+
+ if self.state is None:
+ # Wait for the DDC session to start.
+ if cmd in ('START', 'START REPEAT'):
+ self.state = 'start'
+ elif self.state == 'start':
+ if cmd == 'ADDRESS READ' and data == 80:
+ # 80 is the I2C slave address of a connected display,
+ # so this marks the start of the DDC data transfer.
+ self.state = 'transfer'
+ elif cmd == 'STOP':
+ # Got back to the idle state.
+ self.state = None
+ elif self.state == 'transfer':
+ if cmd == 'DATA READ':
+ # There shouldn't be anything but data reads on this
+ # address, so ignore everything else.
+ self.put(ss, es, self.out_ann, [0, ['0x%.2x' % data]])
+
+++ /dev/null
-##
-## 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
-##
-
-#
-# TrekStor EBR30-a I2C demux protocol decoder
-#
-# Takes an I2C stream as input and outputs 3 different I2C streams, for the
-# 3 different I2C devices on the TrekStor EBR30-a eBook reader (which are all
-# physically connected to the same SCL/SDA lines).
-#
-# I2C slave addresses:
-#
-# - AXP199 battery management chip: 0x69/0x68 (8bit R/W), 0x34 (7bit)
-# - H8563S RTC chip: 0xa3/0xa2 (8bit R/W), 0x51 (7bit)
-# - Unknown accelerometer chip: 0x2b/0x2a (8bit R/W), 0x15 (7bit)
-#
-
-import sigrokdecode as srd
-
-# I2C devices
-AXP199 = 0
-H8563S = 1
-ACCEL = 2
-
-class Decoder(srd.Decoder):
- id = 'ebr30a_i2c_demux'
- name = 'EBR30-a I2C demux'
- longname = 'TrekStor EBR30-a I2C demux'
- desc = 'TODO.'
- longdesc = 'TODO.'
- license = 'gplv2+'
- inputs = ['i2c']
- outputs = ['i2c-axp199', 'i2c-h8563s', 'i2c-accel']
- probes = []
- options = {}
- annotations = []
-
- def __init__(self, **kwargs):
- self.packets = []
- self.stream = -1
-
- def start(self, metadata):
- self.out_proto = []
- self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-axp199'))
- self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-h8563s'))
- self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-accel'))
- # TODO: Annotations?
-
- def report(self):
- pass
-
- # Grab I2C packets into a local cache, until an I2C STOP condition
- # packet comes along. At some point before that STOP condition, there
- # will have been an ADDRESS READ or ADDRESS WRITE which contains the
- # I2C address of the slave that the master wants to talk to.
- # We use this slave address to figure out which output stream should
- # get the whole chunk of packets (from START to STOP).
- def decode(self, ss, es, data):
-
- cmd, databyte, ack_bit = data
-
- # Add the I2C packet to our local cache.
- self.packets += [[ss, es, data]]
-
- if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
- # print(hex(databyte))
- if databyte == 0x34:
- self.stream = AXP199
- elif databyte == 0x51:
- self.stream = H8563S
- elif databyte == 0x15:
- self.stream = ACCEL
- else:
- pass # TODO: Error?
- # TODO: Can there be two ADDRESS READ/WRITE with two different
- # slave addresses before any STOP occurs?
- elif cmd == 'STOP':
- if self.stream != -1:
- # Send the whole chunk of I2C packets to the correct stream.
- for p in self.packets:
- # print(self.out_proto[self.stream], p)
- self.put(p[0], p[1], self.out_proto[self.stream], p[2])
- else:
- print('Error: Could not determine correct stream!') # FIXME
- self.packets = []
- self.stream = -1
-
--- /dev/null
+##
+## 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)/ebr30a_i2c_demux
+
+dist_pkgdata_DATA = __init__.py ebr30a_i2c_demux.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .ebr30a_i2c_demux import *
+
--- /dev/null
+##
+## 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
+##
+
+#
+# TrekStor EBR30-a I2C demux protocol decoder
+#
+# Takes an I2C stream as input and outputs 3 different I2C streams, for the
+# 3 different I2C devices on the TrekStor EBR30-a eBook reader (which are all
+# physically connected to the same SCL/SDA lines).
+#
+# I2C slave addresses:
+#
+# - AXP199 battery management chip: 0x69/0x68 (8bit R/W), 0x34 (7bit)
+# - H8563S RTC chip: 0xa3/0xa2 (8bit R/W), 0x51 (7bit)
+# - Unknown accelerometer chip: 0x2b/0x2a (8bit R/W), 0x15 (7bit)
+#
+
+import sigrokdecode as srd
+
+# I2C devices
+AXP199 = 0
+H8563S = 1
+ACCEL = 2
+
+class Decoder(srd.Decoder):
+ id = 'ebr30a_i2c_demux'
+ name = 'EBR30-a I2C demux'
+ longname = 'TrekStor EBR30-a I2C demux'
+ desc = 'TODO.'
+ longdesc = 'TODO.'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = ['i2c-axp199', 'i2c-h8563s', 'i2c-accel']
+ probes = []
+ options = {}
+ annotations = []
+
+ def __init__(self, **kwargs):
+ self.packets = []
+ self.stream = -1
+
+ def start(self, metadata):
+ self.out_proto = []
+ self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-axp199'))
+ self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-h8563s'))
+ self.out_proto.append(self.add(srd.OUTPUT_PROTO, 'i2c-accel'))
+ # TODO: Annotations?
+
+ def report(self):
+ pass
+
+ # Grab I2C packets into a local cache, until an I2C STOP condition
+ # packet comes along. At some point before that STOP condition, there
+ # will have been an ADDRESS READ or ADDRESS WRITE which contains the
+ # I2C address of the slave that the master wants to talk to.
+ # We use this slave address to figure out which output stream should
+ # get the whole chunk of packets (from START to STOP).
+ def decode(self, ss, es, data):
+
+ cmd, databyte, ack_bit = data
+
+ # Add the I2C packet to our local cache.
+ self.packets += [[ss, es, data]]
+
+ if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+ # print(hex(databyte))
+ if databyte == 0x34:
+ self.stream = AXP199
+ elif databyte == 0x51:
+ self.stream = H8563S
+ elif databyte == 0x15:
+ self.stream = ACCEL
+ else:
+ pass # TODO: Error?
+ # TODO: Can there be two ADDRESS READ/WRITE with two different
+ # slave addresses before any STOP occurs?
+ elif cmd == 'STOP':
+ if self.stream != -1:
+ # Send the whole chunk of I2C packets to the correct stream.
+ for p in self.packets:
+ # print(self.out_proto[self.stream], p)
+ self.put(p[0], p[1], self.out_proto[self.stream], p[2])
+ else:
+ print('Error: Could not determine correct stream!') # FIXME
+ self.packets = []
+ self.stream = -1
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010-2011 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
-##
-
-#
-# I2C protocol decoder
-#
-
-#
-# The Inter-Integrated Circuit (I2C) bus is a bidirectional, multi-master
-# bus using two signals (SCL = serial clock line, SDA = serial data line).
-#
-# There can be many devices on the same bus. Each device can potentially be
-# master or slave (and that can change during runtime). Both slave and master
-# can potentially play the transmitter or receiver role (this can also
-# change at runtime).
-#
-# Possible maximum data rates:
-# - Standard mode: 100 kbit/s
-# - Fast mode: 400 kbit/s
-# - Fast-mode Plus: 1 Mbit/s
-# - High-speed mode: 3.4 Mbit/s
-#
-# START condition (S): SDA = falling, SCL = high
-# Repeated START condition (Sr): same as S
-# Data bit sampling: SCL = rising
-# STOP condition (P): SDA = rising, SCL = high
-#
-# All data bytes on SDA are exactly 8 bits long (transmitted MSB-first).
-# Each byte has to be followed by a 9th ACK/NACK bit. If that bit is low,
-# that indicates an ACK, if it's high that indicates a NACK.
-#
-# After the first START condition, a master sends the device address of the
-# slave it wants to talk to. Slave addresses are 7 bits long (MSB-first).
-# After those 7 bits, a data direction bit is sent. If the bit is low that
-# indicates a WRITE operation, if it's high that indicates a READ operation.
-#
-# Later an optional 10bit slave addressing scheme was added.
-#
-# Documentation:
-# http://www.nxp.com/acrobat/literature/9398/39340011.pdf (v2.1 spec)
-# http://www.nxp.com/acrobat/usermanuals/UM10204_3.pdf (v3 spec)
-# http://en.wikipedia.org/wiki/I2C
-#
-
-# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
-# TODO: Handle clock stretching.
-# TODO: Handle combined messages / repeated START.
-# TODO: Implement support for 7bit and 10bit slave addresses.
-# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
-# TODO: Implement support for detecting various bus errors.
-# TODO: I2C address of slaves.
-# TODO: Handle multiple different I2C devices on same bus
-# -> we need to decode multiple protocols at the same time.
-
-'''
-Protocol output format:
-
-I2C packet:
-[<i2c_command>, <data>, <ack_bit>]
-
-<i2c_command> is one of:
- - 'START' (START condition)
- - 'START REPEAT' (Repeated START)
- - 'ADDRESS READ' (Address, read)
- - 'ADDRESS WRITE' (Address, write)
- - 'DATA READ' (Data, read)
- - 'DATA WRITE' (Data, write)
- - 'STOP' (STOP condition)
-
-<data> is the data or address byte associated with the 'ADDRESS*' and 'DATA*'
-command. For 'START', 'START REPEAT' and 'STOP', this is None.
-
-<ack_bit> is either 'ACK' or 'NACK', but may also be None.
-'''
-
-import sigrokdecode as srd
-
-# Annotation feed formats
-ANN_SHIFTED = 0
-ANN_SHIFTED_SHORT = 1
-ANN_RAW = 2
-
-# Values are verbose and short annotation, respectively.
-protocol = {
- 'START': ['START', 'S'],
- 'START REPEAT': ['START REPEAT', 'Sr'],
- 'STOP': ['STOP', 'P'],
- 'ACK': ['ACK', 'A'],
- 'NACK': ['NACK', 'N'],
- 'ADDRESS READ': ['ADDRESS READ', 'AR'],
- 'ADDRESS WRITE': ['ADDRESS WRITE', 'AW'],
- 'DATA READ': ['DATA READ', 'DR'],
- 'DATA WRITE': ['DATA WRITE', 'DW'],
-}
-
-# States
-FIND_START = 0
-FIND_ADDRESS = 1
-FIND_DATA = 2
-
-class Decoder(srd.Decoder):
- id = 'i2c'
- name = 'I2C'
- longname = 'Inter-Integrated Circuit'
- desc = 'I2C is a two-wire, multi-master, serial bus.'
- longdesc = '...'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['i2c']
- probes = [
- {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
- {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
- ]
- options = {
- 'addressing': ['Slave addressing (in bits)', 7], # 7 or 10
- }
- annotations = [
- # ANN_SHIFTED
- ['7-bit shifted hex',
- 'Read/write bit shifted out from the 8-bit I2C slave address'],
- # ANN_SHIFTED_SHORT
- ['7-bit shifted hex (short)',
- 'Read/write bit shifted out from the 8-bit I2C slave address'],
- # ANN_RAW
- ['Raw hex', 'Unaltered raw data'],
- ]
-
- def __init__(self, **kwargs):
- self.samplecnt = 0
- self.bitcount = 0
- self.databyte = 0
- self.wr = -1
- self.startsample = -1
- self.is_repeat_start = 0
- self.state = FIND_START
- self.oldscl = None
- self.oldsda = None
-
- # Set protocol decoder option defaults.
- self.addressing = Decoder.options['addressing'][1]
-
- def start(self, metadata):
- self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
-
- def report(self):
- pass
-
- def is_start_condition(self, scl, sda):
- # START condition (S): SDA = falling, SCL = high
- if (self.oldsda == 1 and sda == 0) and scl == 1:
- return True
- return False
-
- def is_data_bit(self, scl, sda):
- # Data sampling of receiver: SCL = rising
- if self.oldscl == 0 and scl == 1:
- return True
- return False
-
- def is_stop_condition(self, scl, sda):
- # STOP condition (P): SDA = rising, SCL = high
- if (self.oldsda == 0 and sda == 1) and scl == 1:
- return True
- return False
-
- def found_start(self, scl, sda):
- cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
-
- self.put(self.out_proto, [cmd, None, None])
- self.put(self.out_ann, [ANN_SHIFTED, [protocol[cmd][0]]])
- self.put(self.out_ann, [ANN_SHIFTED_SHORT, [protocol[cmd][1]]])
-
- self.state = FIND_ADDRESS
- self.bitcount = self.databyte = 0
- self.is_repeat_start = 1
- self.wr = -1
-
- def found_address_or_data(self, scl, sda):
- # Gather 8 bits of data plus the ACK/NACK bit.
-
- if self.startsample == -1:
- # TODO: Should be samplenum, as received from the feed.
- self.startsample = self.samplecnt
- self.bitcount += 1
-
- # Address and data are transmitted MSB-first.
- self.databyte <<= 1
- self.databyte |= sda
-
- # Return if we haven't collected all 8 + 1 bits, yet.
- if self.bitcount != 9:
- return
-
- # Send raw output annotation before we start shifting out
- # read/write and ack/nack bits.
- self.put(self.out_ann, [ANN_RAW, ['0x%.2x' % self.databyte]])
-
- # We received 8 address/data bits and the ACK/NACK bit.
- self.databyte >>= 1 # Shift out unwanted ACK/NACK bit here.
-
- if self.state == FIND_ADDRESS:
- # The READ/WRITE bit is only in address bytes, not data bytes.
- self.wr = 0 if (self.databyte & 1) else 1
- d = self.databyte >> 1
- elif self.state == FIND_DATA:
- d = self.databyte
- else:
- # TODO: Error?
- pass
-
- # Last bit that came in was the ACK/NACK bit (1 = NACK).
- ack_bit = 'NACK' if (sda == 1) else 'ACK'
-
- if self.state == FIND_ADDRESS and self.wr == 1:
- cmd = 'ADDRESS WRITE'
- elif self.state == FIND_ADDRESS and self.wr == 0:
- cmd = 'ADDRESS READ'
- elif self.state == FIND_DATA and self.wr == 1:
- cmd = 'DATA WRITE'
- elif self.state == FIND_DATA and self.wr == 0:
- cmd = 'DATA READ'
-
- self.put(self.out_proto, [cmd, d, ack_bit])
- self.put(self.out_ann, [ANN_SHIFTED,
- [protocol[cmd][0], '0x%02x' % d, protocol[ack_bit][0]]])
- self.put(self.out_ann, [ANN_SHIFTED_SHORT,
- [protocol[cmd][1], '0x%02x' % d, protocol[ack_bit][1]]])
-
- self.bitcount = self.databyte = 0
- self.startsample = -1
-
- if self.state == FIND_ADDRESS:
- self.state = FIND_DATA
- elif self.state == FIND_DATA:
- # There could be multiple data bytes in a row.
- # So, either find a STOP condition or another data byte next.
- pass
-
- def found_stop(self, scl, sda):
- self.put(self.out_proto, ['STOP', None, None])
- self.put(self.out_ann, [ANN_SHIFTED, [protocol['STOP'][0]]])
- self.put(self.out_ann, [ANN_SHIFTED_SHORT, [protocol['STOP'][1]]])
-
- self.state = FIND_START
- self.is_repeat_start = 0
- self.wr = -1
-
- def put(self, output_id, data):
- # Inject sample range into the call up to sigrok.
- # TODO: 0-0 sample range for now.
- super(Decoder, self).put(0, 0, output_id, data)
-
- def decode(self, ss, es, data):
- for samplenum, (scl, sda) in data:
- self.samplecnt += 1
-
- # First sample: Save SCL/SDA value.
- if self.oldscl == None:
- self.oldscl = scl
- self.oldsda = sda
- continue
-
- # TODO: Wait until the bus is idle (SDA = SCL = 1) first?
-
- # State machine.
- if self.state == FIND_START:
- if self.is_start_condition(scl, sda):
- self.found_start(scl, sda)
- elif self.state == FIND_ADDRESS:
- if self.is_data_bit(scl, sda):
- self.found_address_or_data(scl, sda)
- elif self.state == FIND_DATA:
- if self.is_data_bit(scl, sda):
- self.found_address_or_data(scl, sda)
- elif self.is_start_condition(scl, sda):
- self.found_start(scl, sda)
- elif self.is_stop_condition(scl, sda):
- self.found_stop(scl, sda)
- else:
- # TODO: Error?
- pass
-
- # Save current SDA/SCL values for the next round.
- self.oldscl = scl
- self.oldsda = sda
-
--- /dev/null
+##
+## 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)/i2c
+
+dist_pkgdata_DATA = __init__.py i2c.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .i2c import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010-2011 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
+##
+
+#
+# I2C protocol decoder
+#
+
+#
+# The Inter-Integrated Circuit (I2C) bus is a bidirectional, multi-master
+# bus using two signals (SCL = serial clock line, SDA = serial data line).
+#
+# There can be many devices on the same bus. Each device can potentially be
+# master or slave (and that can change during runtime). Both slave and master
+# can potentially play the transmitter or receiver role (this can also
+# change at runtime).
+#
+# Possible maximum data rates:
+# - Standard mode: 100 kbit/s
+# - Fast mode: 400 kbit/s
+# - Fast-mode Plus: 1 Mbit/s
+# - High-speed mode: 3.4 Mbit/s
+#
+# START condition (S): SDA = falling, SCL = high
+# Repeated START condition (Sr): same as S
+# Data bit sampling: SCL = rising
+# STOP condition (P): SDA = rising, SCL = high
+#
+# All data bytes on SDA are exactly 8 bits long (transmitted MSB-first).
+# Each byte has to be followed by a 9th ACK/NACK bit. If that bit is low,
+# that indicates an ACK, if it's high that indicates a NACK.
+#
+# After the first START condition, a master sends the device address of the
+# slave it wants to talk to. Slave addresses are 7 bits long (MSB-first).
+# After those 7 bits, a data direction bit is sent. If the bit is low that
+# indicates a WRITE operation, if it's high that indicates a READ operation.
+#
+# Later an optional 10bit slave addressing scheme was added.
+#
+# Documentation:
+# http://www.nxp.com/acrobat/literature/9398/39340011.pdf (v2.1 spec)
+# http://www.nxp.com/acrobat/usermanuals/UM10204_3.pdf (v3 spec)
+# http://en.wikipedia.org/wiki/I2C
+#
+
+# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
+# TODO: Handle clock stretching.
+# TODO: Handle combined messages / repeated START.
+# TODO: Implement support for 7bit and 10bit slave addresses.
+# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
+# TODO: Implement support for detecting various bus errors.
+# TODO: I2C address of slaves.
+# TODO: Handle multiple different I2C devices on same bus
+# -> we need to decode multiple protocols at the same time.
+
+'''
+Protocol output format:
+
+I2C packet:
+[<i2c_command>, <data>, <ack_bit>]
+
+<i2c_command> is one of:
+ - 'START' (START condition)
+ - 'START REPEAT' (Repeated START)
+ - 'ADDRESS READ' (Address, read)
+ - 'ADDRESS WRITE' (Address, write)
+ - 'DATA READ' (Data, read)
+ - 'DATA WRITE' (Data, write)
+ - 'STOP' (STOP condition)
+
+<data> is the data or address byte associated with the 'ADDRESS*' and 'DATA*'
+command. For 'START', 'START REPEAT' and 'STOP', this is None.
+
+<ack_bit> is either 'ACK' or 'NACK', but may also be None.
+'''
+
+import sigrokdecode as srd
+
+# Annotation feed formats
+ANN_SHIFTED = 0
+ANN_SHIFTED_SHORT = 1
+ANN_RAW = 2
+
+# Values are verbose and short annotation, respectively.
+protocol = {
+ 'START': ['START', 'S'],
+ 'START REPEAT': ['START REPEAT', 'Sr'],
+ 'STOP': ['STOP', 'P'],
+ 'ACK': ['ACK', 'A'],
+ 'NACK': ['NACK', 'N'],
+ 'ADDRESS READ': ['ADDRESS READ', 'AR'],
+ 'ADDRESS WRITE': ['ADDRESS WRITE', 'AW'],
+ 'DATA READ': ['DATA READ', 'DR'],
+ 'DATA WRITE': ['DATA WRITE', 'DW'],
+}
+
+# States
+FIND_START = 0
+FIND_ADDRESS = 1
+FIND_DATA = 2
+
+class Decoder(srd.Decoder):
+ id = 'i2c'
+ name = 'I2C'
+ longname = 'Inter-Integrated Circuit'
+ desc = 'I2C is a two-wire, multi-master, serial bus.'
+ longdesc = '...'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['i2c']
+ probes = [
+ {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
+ {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
+ ]
+ options = {
+ 'addressing': ['Slave addressing (in bits)', 7], # 7 or 10
+ }
+ annotations = [
+ # ANN_SHIFTED
+ ['7-bit shifted hex',
+ 'Read/write bit shifted out from the 8-bit I2C slave address'],
+ # ANN_SHIFTED_SHORT
+ ['7-bit shifted hex (short)',
+ 'Read/write bit shifted out from the 8-bit I2C slave address'],
+ # ANN_RAW
+ ['Raw hex', 'Unaltered raw data'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.samplecnt = 0
+ self.bitcount = 0
+ self.databyte = 0
+ self.wr = -1
+ self.startsample = -1
+ self.is_repeat_start = 0
+ self.state = FIND_START
+ self.oldscl = None
+ self.oldsda = None
+
+ # Set protocol decoder option defaults.
+ self.addressing = Decoder.options['addressing'][1]
+
+ def start(self, metadata):
+ self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
+
+ def report(self):
+ pass
+
+ def is_start_condition(self, scl, sda):
+ # START condition (S): SDA = falling, SCL = high
+ if (self.oldsda == 1 and sda == 0) and scl == 1:
+ return True
+ return False
+
+ def is_data_bit(self, scl, sda):
+ # Data sampling of receiver: SCL = rising
+ if self.oldscl == 0 and scl == 1:
+ return True
+ return False
+
+ def is_stop_condition(self, scl, sda):
+ # STOP condition (P): SDA = rising, SCL = high
+ if (self.oldsda == 0 and sda == 1) and scl == 1:
+ return True
+ return False
+
+ def found_start(self, scl, sda):
+ cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
+
+ self.put(self.out_proto, [cmd, None, None])
+ self.put(self.out_ann, [ANN_SHIFTED, [protocol[cmd][0]]])
+ self.put(self.out_ann, [ANN_SHIFTED_SHORT, [protocol[cmd][1]]])
+
+ self.state = FIND_ADDRESS
+ self.bitcount = self.databyte = 0
+ self.is_repeat_start = 1
+ self.wr = -1
+
+ def found_address_or_data(self, scl, sda):
+ # Gather 8 bits of data plus the ACK/NACK bit.
+
+ if self.startsample == -1:
+ # TODO: Should be samplenum, as received from the feed.
+ self.startsample = self.samplecnt
+ self.bitcount += 1
+
+ # Address and data are transmitted MSB-first.
+ self.databyte <<= 1
+ self.databyte |= sda
+
+ # Return if we haven't collected all 8 + 1 bits, yet.
+ if self.bitcount != 9:
+ return
+
+ # Send raw output annotation before we start shifting out
+ # read/write and ack/nack bits.
+ self.put(self.out_ann, [ANN_RAW, ['0x%.2x' % self.databyte]])
+
+ # We received 8 address/data bits and the ACK/NACK bit.
+ self.databyte >>= 1 # Shift out unwanted ACK/NACK bit here.
+
+ if self.state == FIND_ADDRESS:
+ # The READ/WRITE bit is only in address bytes, not data bytes.
+ self.wr = 0 if (self.databyte & 1) else 1
+ d = self.databyte >> 1
+ elif self.state == FIND_DATA:
+ d = self.databyte
+ else:
+ # TODO: Error?
+ pass
+
+ # Last bit that came in was the ACK/NACK bit (1 = NACK).
+ ack_bit = 'NACK' if (sda == 1) else 'ACK'
+
+ if self.state == FIND_ADDRESS and self.wr == 1:
+ cmd = 'ADDRESS WRITE'
+ elif self.state == FIND_ADDRESS and self.wr == 0:
+ cmd = 'ADDRESS READ'
+ elif self.state == FIND_DATA and self.wr == 1:
+ cmd = 'DATA WRITE'
+ elif self.state == FIND_DATA and self.wr == 0:
+ cmd = 'DATA READ'
+
+ self.put(self.out_proto, [cmd, d, ack_bit])
+ self.put(self.out_ann, [ANN_SHIFTED,
+ [protocol[cmd][0], '0x%02x' % d, protocol[ack_bit][0]]])
+ self.put(self.out_ann, [ANN_SHIFTED_SHORT,
+ [protocol[cmd][1], '0x%02x' % d, protocol[ack_bit][1]]])
+
+ self.bitcount = self.databyte = 0
+ self.startsample = -1
+
+ if self.state == FIND_ADDRESS:
+ self.state = FIND_DATA
+ elif self.state == FIND_DATA:
+ # There could be multiple data bytes in a row.
+ # So, either find a STOP condition or another data byte next.
+ pass
+
+ def found_stop(self, scl, sda):
+ self.put(self.out_proto, ['STOP', None, None])
+ self.put(self.out_ann, [ANN_SHIFTED, [protocol['STOP'][0]]])
+ self.put(self.out_ann, [ANN_SHIFTED_SHORT, [protocol['STOP'][1]]])
+
+ self.state = FIND_START
+ self.is_repeat_start = 0
+ self.wr = -1
+
+ def put(self, output_id, data):
+ # Inject sample range into the call up to sigrok.
+ # TODO: 0-0 sample range for now.
+ super(Decoder, self).put(0, 0, output_id, data)
+
+ def decode(self, ss, es, data):
+ for samplenum, (scl, sda) in data:
+ self.samplecnt += 1
+
+ # First sample: Save SCL/SDA value.
+ if self.oldscl == None:
+ self.oldscl = scl
+ self.oldsda = sda
+ continue
+
+ # TODO: Wait until the bus is idle (SDA = SCL = 1) first?
+
+ # State machine.
+ if self.state == FIND_START:
+ if self.is_start_condition(scl, sda):
+ self.found_start(scl, sda)
+ elif self.state == FIND_ADDRESS:
+ if self.is_data_bit(scl, sda):
+ self.found_address_or_data(scl, sda)
+ elif self.state == FIND_DATA:
+ if self.is_data_bit(scl, sda):
+ self.found_address_or_data(scl, sda)
+ elif self.is_start_condition(scl, sda):
+ self.found_start(scl, sda)
+ elif self.is_stop_condition(scl, sda):
+ self.found_stop(scl, sda)
+ else:
+ # TODO: Error?
+ pass
+
+ # Save current SDA/SCL values for the next round.
+ self.oldscl = scl
+ self.oldsda = sda
+
+++ /dev/null
-##
-## 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
-##
-
-#
-# Macronix MX25Lxx05D SPI (NOR) flash chip decoder.
-# Works for MX25L1605D/MX25L3205D/MX25L6405D.
-#
-
-#
-# TODO: Description
-#
-# Details:
-# http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
-#
-
-import sigrokdecode as srd
-
-# States
-IDLE = -1
-
-# Chip commands (also used as additional decoder states).
-CMD_WREN = 0x06
-CMD_WRDI = 0x04
-CMD_RDID = 0x9f
-CMD_RDSR = 0x05
-CMD_WRSR = 0x01
-CMD_READ = 0x03
-CMD_FAST_READ = 0x0b
-CMD_2READ = 0xbb
-CMD_SE = 0x20
-CMD_BE = 0xd8
-CMD_CE = 0x60
-CMD_CE2 = 0xc7
-CMD_PP = 0x02
-CMD_CP = 0xad
-CMD_DP = 0xb9
-# CMD_RDP = 0xab
-# CMD_RES = 0xab
-CMD_RDP_RES = 0xab # Note: RDP/RES have the same ID.
-CMD_REMS = 0x90
-CMD_REMS2 = 0xef
-CMD_ENSO = 0xb1
-CMD_EXSO = 0xc1
-CMD_RDSCUR = 0x2b
-CMD_WRSCUR = 0x2f
-CMD_ESRY = 0x70
-CMD_DSRY = 0x80
-
-# TODO: (Short) command names as strings in a dict, too?
-
-# Dict which maps command IDs to their description.
-cmds = {
- CMD_WREN: 'Write enable',
- CMD_WRDI: 'Write disable',
- CMD_RDID: 'Read identification',
- CMD_RDSR: 'Read status register',
- CMD_WRSR: 'Write status register',
- CMD_READ: 'Read data',
- CMD_FAST_READ: 'Fast read data',
- CMD_2READ: '2x I/O read',
- CMD_SE: 'Sector erase',
- CMD_BE: 'Block erase',
- CMD_CE: 'Chip erase',
- CMD_CE2: 'Chip erase', # Alternative command ID
- CMD_PP: 'Page program',
- CMD_CP: 'Continuously program mode',
- CMD_DP: 'Deep power down',
- # CMD_RDP: 'Release from deep powerdown',
- # CMD_RES: 'Read electronic ID',
- CMD_RDP_RES: 'Release from deep powerdown / Read electronic ID',
- CMD_REMS: 'Read electronic manufacturer & device ID',
- CMD_REMS2: 'Read ID for 2x I/O mode',
- CMD_ENSO: 'Enter secured OTP',
- CMD_EXSO: 'Exit secured OTP',
- CMD_RDSCUR: 'Read security register',
- CMD_WRSCUR: 'Write security register',
- CMD_ESRY: 'Enable SO to output RY/BY#',
- CMD_DSRY: 'Disable SO to output RY/BY#',
-}
-
-device_name = {
- 0x14: 'MX25L1605D',
- 0x15: 'MX25L3205D',
- 0x16: 'MX25L6405D',
-}
-
-class Decoder(srd.Decoder):
- id = 'mx25lxx05d'
- name = 'MX25Lxx05D'
- longname = 'Macronix MX25Lxx05D'
- desc = 'Macronix MX25Lxx05D SPI flash chip decoder'
- longdesc = 'TODO'
- license = 'gplv2+'
- inputs = ['spi', 'spi', 'logic']
- outputs = ['mx25lxx05d']
- probes = [] # TODO: HOLD#, WP#/ACC
- options = {} # TODO
- annotations = [
- ['TODO', 'TODO'],
- ]
-
- def __init__(self, **kwargs):
- self.state = IDLE
- self.cmdstate = 1 # TODO
-
- def start(self, metadata):
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mx25lxx05d')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'mx25lxx05d')
-
- def report(self):
- pass
-
- def putann(self, data):
- # Simplification, most annotations span extactly one SPI byte/packet.
- self.put(self.ss, self.es, self.out_ann, data)
-
- def handle_wren(self, mosi, miso):
- self.putann([0, ['Command: %s' % cmds[self.cmd]]])
- self.state = IDLE
-
- # TODO: Check/display device ID / name
- def handle_rdid(self, mosi, miso):
- if self.cmdstate == 1:
- # Byte 1: Master sends command ID.
- self.start_sample = self.ss
- self.putann([0, ['Command: %s' % cmds[self.cmd]]])
- elif self.cmdstate == 2:
- # Byte 2: Slave sends the JEDEC manufacturer ID.
- self.putann([0, ['Manufacturer ID: 0x%02x' % miso]])
- elif self.cmdstate == 3:
- # Byte 3: Slave sends the memory type (0x20 for this chip).
- self.putann([0, ['Memory type: 0x%02x' % miso]])
- elif self.cmdstate == 4:
- # Byte 4: Slave sends the device ID.
- self.device_id = miso
- self.putann([0, ['Device ID: 0x%02x' % miso]])
-
- if self.cmdstate == 4:
- # TODO: Check self.device_id is valid & exists in device_names.
- # TODO: Same device ID? Check!
- d = 'Device: Macronix %s' % device_name[self.device_id]
- self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
- self.state = IDLE
- else:
- self.cmdstate += 1
-
- # TODO: Warn/abort if we don't see the necessary amount of bytes.
- # TODO: Warn if WREN was not seen before.
- def handle_se(self, mosi, miso):
- if self.cmdstate == 1:
- # Byte 1: Master sends command ID.
- self.addr = 0
- self.start_sample = self.ss
- self.putann([0, ['Command: %s' % cmds[self.cmd]]])
- elif self.cmdstate in (2, 3, 4):
- # Bytes 2/3/4: Master sends address of the sector to erase.
- # Note: Assumes SPI data is 8 bits wide (it is for MX25Lxx05D).
- # TODO: LSB-first of MSB-first?
- self.addr <<= 8
- self.addr |= mosi
- self.putann([0, ['Address byte %d: 0x%02x' % (self.cmdstate - 1,
- miso)]]) # TODO: Count from 0 or 1?
-
- if self.cmdstate == 4:
- d = 'Erase sector %d' % self.addr
- self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
- # TODO: Max. size depends on chip, check that too if possible.
- if self.addr % 4096 != 0:
- # Sector addresses must be 4K-aligned (same for all 3 chips).
- d = 'Warning: Invalid sector address!' # TODO: type == WARN?
- self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
- self.state = IDLE
- else:
- self.cmdstate += 1
-
- def handle_rems(self, mosi, miso):
- if self.cmdstate == 1:
- # Byte 1: Master sends command ID.
- self.start_sample = self.ss
- self.putann([0, ['Command: %s' % cmds[self.cmd]]])
- elif self.cmdstate in (2, 3):
- # Bytes 2/3: Master sends two dummy bytes.
- # TODO: Check dummy bytes? Check reply from device?
- self.putann([0, ['Dummy byte: %s' % mosi]])
- elif self.cmdstate == 4:
- # Byte 4: Master sends 0x00 or 0x01.
- # 0x00: Master wants manufacturer ID as first reply byte.
- # 0x01: Master wants device ID as first reply byte.
- self.manufacturer_id_first = True if (mosi == 0x00) else False
- d = 'manufacturer' if (mosi == 0x00) else 'device'
- self.putann([0, ['Master wants %s ID first' % d]])
- elif self.cmdstate == 5:
- # Byte 5: Slave sends manufacturer ID (or device ID).
- self.ids = [miso]
- d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
- self.putann([0, ['%s ID' % d]])
- elif self.cmdstate == 6:
- # Byte 6: Slave sends device ID (or manufacturer ID).
- self.ids += [miso]
- d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
- self.putann([0, ['%s ID' % d]])
- else:
- # TODO: Error?
- pass
-
- if self.cmdstate == 6:
- self.end_sample = self.es
- id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
- self.putann([0, ['Device: Macronix %s' % device_name[id]]])
- self.state = IDLE
- else:
- self.cmdstate += 1
-
- def handle_rdsr(self, mosi, miso):
- self.putann([0, ['Command: %s (0x%02x)' % (cmds[self.cmd], miso)]])
- self.state = IDLE
-
- def decode(self, ss, es, data):
-
- ptype, mosi, miso = data
-
- if ptype != 'data':
- return
-
- cmd = mosi
- self.ss = ss
- self.es = es
-
- # If we encountered a known chip command, enter the resp. state.
- if self.state == IDLE:
- if cmd in cmds:
- self.state = cmd
- self.cmd = cmd # TODO: Eliminate?
- self.cmdstate = 1
- else:
- pass # TODO
- else:
- pass
-
- # Handle commands.
- # TODO: Use some generic way to invoke the resp. method.
- if self.state == CMD_WREN:
- self.handle_wren(mosi, miso)
- elif self.state == CMD_SE:
- self.handle_se(mosi, miso)
- elif self.state == CMD_RDID:
- self.handle_rdid(mosi, miso)
- if self.state == CMD_REMS:
- self.handle_rems(mosi, miso)
- if self.state == CMD_RDSR:
- self.handle_rdsr(mosi, miso)
- else:
- self.put(0, 0, self.out_ann, [0, ['Unknown command: 0x%02x' % cmd]])
- self.state = IDLE
-
--- /dev/null
+##
+## 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)/mx25lxx05d
+
+dist_pkgdata_DATA = __init__.py mx25lxx05d.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .mx25lxx05d import *
+
--- /dev/null
+##
+## 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
+##
+
+#
+# Macronix MX25Lxx05D SPI (NOR) flash chip decoder.
+# Works for MX25L1605D/MX25L3205D/MX25L6405D.
+#
+
+#
+# TODO: Description
+#
+# Details:
+# http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
+#
+
+import sigrokdecode as srd
+
+# States
+IDLE = -1
+
+# Chip commands (also used as additional decoder states).
+CMD_WREN = 0x06
+CMD_WRDI = 0x04
+CMD_RDID = 0x9f
+CMD_RDSR = 0x05
+CMD_WRSR = 0x01
+CMD_READ = 0x03
+CMD_FAST_READ = 0x0b
+CMD_2READ = 0xbb
+CMD_SE = 0x20
+CMD_BE = 0xd8
+CMD_CE = 0x60
+CMD_CE2 = 0xc7
+CMD_PP = 0x02
+CMD_CP = 0xad
+CMD_DP = 0xb9
+# CMD_RDP = 0xab
+# CMD_RES = 0xab
+CMD_RDP_RES = 0xab # Note: RDP/RES have the same ID.
+CMD_REMS = 0x90
+CMD_REMS2 = 0xef
+CMD_ENSO = 0xb1
+CMD_EXSO = 0xc1
+CMD_RDSCUR = 0x2b
+CMD_WRSCUR = 0x2f
+CMD_ESRY = 0x70
+CMD_DSRY = 0x80
+
+# TODO: (Short) command names as strings in a dict, too?
+
+# Dict which maps command IDs to their description.
+cmds = {
+ CMD_WREN: 'Write enable',
+ CMD_WRDI: 'Write disable',
+ CMD_RDID: 'Read identification',
+ CMD_RDSR: 'Read status register',
+ CMD_WRSR: 'Write status register',
+ CMD_READ: 'Read data',
+ CMD_FAST_READ: 'Fast read data',
+ CMD_2READ: '2x I/O read',
+ CMD_SE: 'Sector erase',
+ CMD_BE: 'Block erase',
+ CMD_CE: 'Chip erase',
+ CMD_CE2: 'Chip erase', # Alternative command ID
+ CMD_PP: 'Page program',
+ CMD_CP: 'Continuously program mode',
+ CMD_DP: 'Deep power down',
+ # CMD_RDP: 'Release from deep powerdown',
+ # CMD_RES: 'Read electronic ID',
+ CMD_RDP_RES: 'Release from deep powerdown / Read electronic ID',
+ CMD_REMS: 'Read electronic manufacturer & device ID',
+ CMD_REMS2: 'Read ID for 2x I/O mode',
+ CMD_ENSO: 'Enter secured OTP',
+ CMD_EXSO: 'Exit secured OTP',
+ CMD_RDSCUR: 'Read security register',
+ CMD_WRSCUR: 'Write security register',
+ CMD_ESRY: 'Enable SO to output RY/BY#',
+ CMD_DSRY: 'Disable SO to output RY/BY#',
+}
+
+device_name = {
+ 0x14: 'MX25L1605D',
+ 0x15: 'MX25L3205D',
+ 0x16: 'MX25L6405D',
+}
+
+class Decoder(srd.Decoder):
+ id = 'mx25lxx05d'
+ name = 'MX25Lxx05D'
+ longname = 'Macronix MX25Lxx05D'
+ desc = 'Macronix MX25Lxx05D SPI flash chip decoder'
+ longdesc = 'TODO'
+ license = 'gplv2+'
+ inputs = ['spi', 'spi', 'logic']
+ outputs = ['mx25lxx05d']
+ probes = [] # TODO: HOLD#, WP#/ACC
+ options = {} # TODO
+ annotations = [
+ ['TODO', 'TODO'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.state = IDLE
+ self.cmdstate = 1 # TODO
+
+ def start(self, metadata):
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mx25lxx05d')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'mx25lxx05d')
+
+ def report(self):
+ pass
+
+ def putann(self, data):
+ # Simplification, most annotations span extactly one SPI byte/packet.
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def handle_wren(self, mosi, miso):
+ self.putann([0, ['Command: %s' % cmds[self.cmd]]])
+ self.state = IDLE
+
+ # TODO: Check/display device ID / name
+ def handle_rdid(self, mosi, miso):
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.start_sample = self.ss
+ self.putann([0, ['Command: %s' % cmds[self.cmd]]])
+ elif self.cmdstate == 2:
+ # Byte 2: Slave sends the JEDEC manufacturer ID.
+ self.putann([0, ['Manufacturer ID: 0x%02x' % miso]])
+ elif self.cmdstate == 3:
+ # Byte 3: Slave sends the memory type (0x20 for this chip).
+ self.putann([0, ['Memory type: 0x%02x' % miso]])
+ elif self.cmdstate == 4:
+ # Byte 4: Slave sends the device ID.
+ self.device_id = miso
+ self.putann([0, ['Device ID: 0x%02x' % miso]])
+
+ if self.cmdstate == 4:
+ # TODO: Check self.device_id is valid & exists in device_names.
+ # TODO: Same device ID? Check!
+ d = 'Device: Macronix %s' % device_name[self.device_id]
+ self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
+ self.state = IDLE
+ else:
+ self.cmdstate += 1
+
+ # TODO: Warn/abort if we don't see the necessary amount of bytes.
+ # TODO: Warn if WREN was not seen before.
+ def handle_se(self, mosi, miso):
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.addr = 0
+ self.start_sample = self.ss
+ self.putann([0, ['Command: %s' % cmds[self.cmd]]])
+ elif self.cmdstate in (2, 3, 4):
+ # Bytes 2/3/4: Master sends address of the sector to erase.
+ # Note: Assumes SPI data is 8 bits wide (it is for MX25Lxx05D).
+ # TODO: LSB-first of MSB-first?
+ self.addr <<= 8
+ self.addr |= mosi
+ self.putann([0, ['Address byte %d: 0x%02x' % (self.cmdstate - 1,
+ miso)]]) # TODO: Count from 0 or 1?
+
+ if self.cmdstate == 4:
+ d = 'Erase sector %d' % self.addr
+ self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
+ # TODO: Max. size depends on chip, check that too if possible.
+ if self.addr % 4096 != 0:
+ # Sector addresses must be 4K-aligned (same for all 3 chips).
+ d = 'Warning: Invalid sector address!' # TODO: type == WARN?
+ self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
+ self.state = IDLE
+ else:
+ self.cmdstate += 1
+
+ def handle_rems(self, mosi, miso):
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.start_sample = self.ss
+ self.putann([0, ['Command: %s' % cmds[self.cmd]]])
+ elif self.cmdstate in (2, 3):
+ # Bytes 2/3: Master sends two dummy bytes.
+ # TODO: Check dummy bytes? Check reply from device?
+ self.putann([0, ['Dummy byte: %s' % mosi]])
+ elif self.cmdstate == 4:
+ # Byte 4: Master sends 0x00 or 0x01.
+ # 0x00: Master wants manufacturer ID as first reply byte.
+ # 0x01: Master wants device ID as first reply byte.
+ self.manufacturer_id_first = True if (mosi == 0x00) else False
+ d = 'manufacturer' if (mosi == 0x00) else 'device'
+ self.putann([0, ['Master wants %s ID first' % d]])
+ elif self.cmdstate == 5:
+ # Byte 5: Slave sends manufacturer ID (or device ID).
+ self.ids = [miso]
+ d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
+ self.putann([0, ['%s ID' % d]])
+ elif self.cmdstate == 6:
+ # Byte 6: Slave sends device ID (or manufacturer ID).
+ self.ids += [miso]
+ d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
+ self.putann([0, ['%s ID' % d]])
+ else:
+ # TODO: Error?
+ pass
+
+ if self.cmdstate == 6:
+ self.end_sample = self.es
+ id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
+ self.putann([0, ['Device: Macronix %s' % device_name[id]]])
+ self.state = IDLE
+ else:
+ self.cmdstate += 1
+
+ def handle_rdsr(self, mosi, miso):
+ self.putann([0, ['Command: %s (0x%02x)' % (cmds[self.cmd], miso)]])
+ self.state = IDLE
+
+ def decode(self, ss, es, data):
+
+ ptype, mosi, miso = data
+
+ if ptype != 'data':
+ return
+
+ cmd = mosi
+ self.ss = ss
+ self.es = es
+
+ # If we encountered a known chip command, enter the resp. state.
+ if self.state == IDLE:
+ if cmd in cmds:
+ self.state = cmd
+ self.cmd = cmd # TODO: Eliminate?
+ self.cmdstate = 1
+ else:
+ pass # TODO
+ else:
+ pass
+
+ # Handle commands.
+ # TODO: Use some generic way to invoke the resp. method.
+ if self.state == CMD_WREN:
+ self.handle_wren(mosi, miso)
+ elif self.state == CMD_SE:
+ self.handle_se(mosi, miso)
+ elif self.state == CMD_RDID:
+ self.handle_rdid(mosi, miso)
+ if self.state == CMD_REMS:
+ self.handle_rems(mosi, miso)
+ if self.state == CMD_RDSR:
+ self.handle_rdsr(mosi, miso)
+ else:
+ self.put(0, 0, self.out_ann, [0, ['Unknown command: 0x%02x' % cmd]])
+ self.state = IDLE
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010-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
-##
-
-#
-# Nintendo Wii Nunchuk decoder
-#
-
-#
-# TODO: Description
-#
-# http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck
-# http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
-# https://www.sparkfun.com/products/9281
-#
-
-import sigrokdecode as srd
-
-# States
-IDLE = 0
-START = 1
-NUNCHUK_SLAVE = 2
-INIT = 3
-INITIALIZED = 4
-
-class Decoder(srd.Decoder):
- id = 'nunchuk'
- name = 'Nunchuk'
- longname = 'Nintendo Wii Nunchuk'
- desc = 'Decodes the Nintendo Wii Nunchuk I2C-based protocol.'
- longdesc = '...'
- license = 'gplv2+'
- inputs = ['i2c']
- outputs = ['nunchuck']
- probes = [] # TODO
- options = {}
- annotations = [
- ['TODO', 'TODO'],
- ]
-
- def __init__(self, **kwargs):
- self.state = IDLE # TODO: Can we assume a certain initial state?
- self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = 0
- self.databytecount = 0
-
- def start(self, metadata):
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'nunchuk')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'nunchuk')
-
- def report(self):
- pass
-
- def decode(self, ss, es, data):
-
- cmd, databyte, ack_bit = data
-
- if cmd == 'START': # TODO: Handle 'Sr' here, too?
- self.state = START
-
- elif cmd == 'START REPEAT':
- pass # FIXME
-
- elif cmd == 'ADDRESS READ':
- # TODO: Error/Warning, not supported, I think.
- pass
-
- elif cmd == 'ADDRESS WRITE':
- # The Wii Nunchuk always has slave address 0x54.
- # TODO: Handle this stuff more correctly.
- if databyte == 0x54:
- pass # TODO
- else:
- pass # TODO: What to do here? Ignore? Error?
-
- elif cmd == 'DATA READ' and self.state == INITIALIZED:
- if self.databytecount == 0:
- self.sx = databyte
- elif self.databytecount == 1:
- self.sy = databyte
- elif self.databytecount == 2:
- self.ax = databyte << 2
- elif self.databytecount == 3:
- self.ay = databyte << 2
- elif self.databytecount == 4:
- self.az = databyte << 2
- elif self.databytecount == 5:
- self.bz = (databyte & (1 << 0)) >> 0
- self.bc = (databyte & (1 << 1)) >> 1
- self.ax |= (databyte & (3 << 2)) >> 2
- self.ay |= (databyte & (3 << 4)) >> 4
- self.az |= (databyte & (3 << 6)) >> 6
-
- d = 'sx = 0x%02x, sy = 0x%02x, ax = 0x%02x, ay = 0x%02x, ' \
- 'az = 0x%02x, bz = 0x%02x, bc = 0x%02x' % (self.sx, \
- self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
- self.put(ss, es, self.out_ann, [0, [d]])
-
- self.sx = self.sy = self.ax = self.ay = self.az = 0
- self.bz = self.bc = 0
- else:
- pass # TODO
-
- if 0 <= self.databytecount <= 5:
- self.databytecount += 1
-
- # TODO: If 6 bytes read -> save and reset
-
- # TODO
- elif cmd == 'DATA READ' and self.state != INITIALIZED:
- pass
-
- elif cmd == 'DATA WRITE':
- if self.state == IDLE:
- self.state = INITIALIZED
- return
-
- if databyte == 0x40 and self.state == START:
- self.state = INIT
- elif databyte == 0x00 and self.state == INIT:
- self.put(ss, es, self.out_ann, [0, ['Initialize nunchuk']])
- self.state = INITIALIZED
- else:
- pass # TODO
-
- elif cmd == 'STOP':
- self.state = INITIALIZED
- self.databytecount = 0
-
--- /dev/null
+##
+## 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)/nunchuk
+
+dist_pkgdata_DATA = __init__.py nunchuk.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .nunchuk import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010-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
+##
+
+#
+# Nintendo Wii Nunchuk decoder
+#
+
+#
+# TODO: Description
+#
+# http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck
+# http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
+# https://www.sparkfun.com/products/9281
+#
+
+import sigrokdecode as srd
+
+# States
+IDLE = 0
+START = 1
+NUNCHUK_SLAVE = 2
+INIT = 3
+INITIALIZED = 4
+
+class Decoder(srd.Decoder):
+ id = 'nunchuk'
+ name = 'Nunchuk'
+ longname = 'Nintendo Wii Nunchuk'
+ desc = 'Decodes the Nintendo Wii Nunchuk I2C-based protocol.'
+ longdesc = '...'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = ['nunchuck']
+ probes = [] # TODO
+ options = {}
+ annotations = [
+ ['TODO', 'TODO'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.state = IDLE # TODO: Can we assume a certain initial state?
+ self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = 0
+ self.databytecount = 0
+
+ def start(self, metadata):
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'nunchuk')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'nunchuk')
+
+ def report(self):
+ pass
+
+ def decode(self, ss, es, data):
+
+ cmd, databyte, ack_bit = data
+
+ if cmd == 'START': # TODO: Handle 'Sr' here, too?
+ self.state = START
+
+ elif cmd == 'START REPEAT':
+ pass # FIXME
+
+ elif cmd == 'ADDRESS READ':
+ # TODO: Error/Warning, not supported, I think.
+ pass
+
+ elif cmd == 'ADDRESS WRITE':
+ # The Wii Nunchuk always has slave address 0x54.
+ # TODO: Handle this stuff more correctly.
+ if databyte == 0x54:
+ pass # TODO
+ else:
+ pass # TODO: What to do here? Ignore? Error?
+
+ elif cmd == 'DATA READ' and self.state == INITIALIZED:
+ if self.databytecount == 0:
+ self.sx = databyte
+ elif self.databytecount == 1:
+ self.sy = databyte
+ elif self.databytecount == 2:
+ self.ax = databyte << 2
+ elif self.databytecount == 3:
+ self.ay = databyte << 2
+ elif self.databytecount == 4:
+ self.az = databyte << 2
+ elif self.databytecount == 5:
+ self.bz = (databyte & (1 << 0)) >> 0
+ self.bc = (databyte & (1 << 1)) >> 1
+ self.ax |= (databyte & (3 << 2)) >> 2
+ self.ay |= (databyte & (3 << 4)) >> 4
+ self.az |= (databyte & (3 << 6)) >> 6
+
+ d = 'sx = 0x%02x, sy = 0x%02x, ax = 0x%02x, ay = 0x%02x, ' \
+ 'az = 0x%02x, bz = 0x%02x, bc = 0x%02x' % (self.sx, \
+ self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
+ self.put(ss, es, self.out_ann, [0, [d]])
+
+ self.sx = self.sy = self.ax = self.ay = self.az = 0
+ self.bz = self.bc = 0
+ else:
+ pass # TODO
+
+ if 0 <= self.databytecount <= 5:
+ self.databytecount += 1
+
+ # TODO: If 6 bytes read -> save and reset
+
+ # TODO
+ elif cmd == 'DATA READ' and self.state != INITIALIZED:
+ pass
+
+ elif cmd == 'DATA WRITE':
+ if self.state == IDLE:
+ self.state = INITIALIZED
+ return
+
+ if databyte == 0x40 and self.state == START:
+ self.state = INIT
+ elif databyte == 0x00 and self.state == INIT:
+ self.put(ss, es, self.out_ann, [0, ['Initialize nunchuk']])
+ self.state = INITIALIZED
+ else:
+ pass # TODO
+
+ elif cmd == 'STOP':
+ self.state = INITIALIZED
+ self.databytecount = 0
+
+++ /dev/null
-##
-## 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
-##
-
-#
-# Panasonic PAN1321 Bluetooth module protocol decoder
-#
-# TODO
-#
-
-import sigrokdecode as srd
-
-# Annotation feed formats
-ANN_ASCII = 0
-
-# UART 'data' packet type.
-T_DATA = 1
-
-# ...
-RX = 0
-TX = 1
-
-class Decoder(srd.Decoder):
- id = 'pan1321'
- name = 'PAN1321'
- longname = 'Panasonic PAN1321'
- desc = 'TODO.'
- longdesc = 'TODO.'
- license = 'gplv2+'
- inputs = ['uart']
- outputs = ['pan1321']
- probes = []
- options = {}
- annotations = [
- ['ASCII', 'TODO: description'],
- ]
-
- def __init__(self, **kwargs):
- self.cmd = ['', '']
-
- def start(self, metadata):
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'pan1321')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'pan1321')
-
- def report(self):
- pass
-
- def handle_host_command(self, ss, es, rxtx, s):
- if s.startswith('AT+JSEC'):
- pin = s[s.find('\r\n') - 4:len(s) - 2]
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Host set the Bluetooth PIN to ' + pin]])
- elif s.startswith('AT+JSLN'):
- name = s[s.find(',') + 1:-2]
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Host set the Bluetooth name to ' + name]])
- else:
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Host sent unsupported command']])
- self.cmd[rxtx] = ''
-
- def handle_device_reply(self, ss, es, rxtx, s):
- if s == 'ROK\r\n':
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Device initialized correctly']])
- elif s == 'OK\r\n':
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Device acknowledged last command']])
- elif s.startswith('ERR'):
- error = s[s.find('=') + 1:]
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Device sent error code ' + error]])
- else:
- self.put(ss, es, self.out_ann,
- [ANN_ASCII, ['Device sent an unknown reply']])
- self.cmd[rxtx] = ''
-
- def decode(self, ss, es, data):
- ptype, rxtx, pdata = data
-
- # For now, ignore all UART packets except the actual data packets.
- if ptype != T_DATA:
- return
-
- # Append a new (ASCII) byte to the currently built/parsed command.
- self.cmd[rxtx] += chr(pdata)
-
- # Get packets/bytes until an \r\n sequence is found (end of command).
- if self.cmd[rxtx][-1:] != '\n':
- return
-
- # Handle host commands and device replies.
- if rxtx == RX:
- self.handle_device_reply(ss, es, rxtx, self.cmd[rxtx])
- elif rxtx == TX:
- self.handle_host_command(ss, es, rxtx, self.cmd[rxtx])
- else:
- pass # TODO: Error.
-
--- /dev/null
+##
+## 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)/pan1321
+
+dist_pkgdata_DATA = __init__.py pan1321.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .pan1321 import *
+
--- /dev/null
+##
+## 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
+##
+
+#
+# Panasonic PAN1321 Bluetooth module protocol decoder
+#
+# TODO
+#
+
+import sigrokdecode as srd
+
+# Annotation feed formats
+ANN_ASCII = 0
+
+# UART 'data' packet type.
+T_DATA = 1
+
+# ...
+RX = 0
+TX = 1
+
+class Decoder(srd.Decoder):
+ id = 'pan1321'
+ name = 'PAN1321'
+ longname = 'Panasonic PAN1321'
+ desc = 'TODO.'
+ longdesc = 'TODO.'
+ license = 'gplv2+'
+ inputs = ['uart']
+ outputs = ['pan1321']
+ probes = []
+ options = {}
+ annotations = [
+ ['ASCII', 'TODO: description'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.cmd = ['', '']
+
+ def start(self, metadata):
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'pan1321')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'pan1321')
+
+ def report(self):
+ pass
+
+ def handle_host_command(self, ss, es, rxtx, s):
+ if s.startswith('AT+JSEC'):
+ pin = s[s.find('\r\n') - 4:len(s) - 2]
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Host set the Bluetooth PIN to ' + pin]])
+ elif s.startswith('AT+JSLN'):
+ name = s[s.find(',') + 1:-2]
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Host set the Bluetooth name to ' + name]])
+ else:
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Host sent unsupported command']])
+ self.cmd[rxtx] = ''
+
+ def handle_device_reply(self, ss, es, rxtx, s):
+ if s == 'ROK\r\n':
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Device initialized correctly']])
+ elif s == 'OK\r\n':
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Device acknowledged last command']])
+ elif s.startswith('ERR'):
+ error = s[s.find('=') + 1:]
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Device sent error code ' + error]])
+ else:
+ self.put(ss, es, self.out_ann,
+ [ANN_ASCII, ['Device sent an unknown reply']])
+ self.cmd[rxtx] = ''
+
+ def decode(self, ss, es, data):
+ ptype, rxtx, pdata = data
+
+ # For now, ignore all UART packets except the actual data packets.
+ if ptype != T_DATA:
+ return
+
+ # Append a new (ASCII) byte to the currently built/parsed command.
+ self.cmd[rxtx] += chr(pdata)
+
+ # Get packets/bytes until an \r\n sequence is found (end of command).
+ if self.cmd[rxtx][-1:] != '\n':
+ return
+
+ # Handle host commands and device replies.
+ if rxtx == RX:
+ self.handle_device_reply(ss, es, rxtx, self.cmd[rxtx])
+ elif rxtx == TX:
+ self.handle_host_command(ss, es, rxtx, self.cmd[rxtx])
+ else:
+ pass # TODO: Error.
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-## 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
-##
-
-import sigrokdecode as srd
-
-# Chip-select options
-ACTIVE_LOW = 0
-ACTIVE_HIGH = 1
-
-# Clock polarity options
-CPOL_0 = 0 # Clock is low when inactive
-CPOL_1 = 1 # Clock is high when inactive
-
-# Clock phase options
-CPHA_0 = 0 # Data is valid on the leading clock edge
-CPHA_1 = 1 # Data is valid on the trailing clock edge
-
-# Bit order options
-MSB_FIRST = 0
-LSB_FIRST = 1
-
-spi_mode = {
- (0, 0): 0, # Mode 0
- (0, 1): 1, # Mode 1
- (1, 0): 2, # Mode 2
- (1, 1): 3, # Mode 3
-}
-
-# Annotation formats
-ANN_HEX = 0
-
-class Decoder(srd.Decoder):
- id = 'spi'
- name = 'SPI'
- longname = 'Serial Peripheral Interface'
- desc = '...desc...'
- longdesc = '...longdesc...'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['spi']
- probes = [
- {'id': 'mosi', 'name': 'MOSI',
- 'desc': 'SPI MOSI line (Master out, slave in)'},
- {'id': 'miso', 'name': 'MISO',
- 'desc': 'SPI MISO line (Master in, slave out)'},
- {'id': 'sck', 'name': 'CLK', 'desc': 'SPI clock line'},
- {'id': 'cs', 'name': 'CS#', 'desc': 'SPI CS (chip select) line'},
- ]
- options = {
- 'cs_polarity': ['CS# polarity', ACTIVE_LOW],
- 'cpol': ['Clock polarity', CPOL_0],
- 'cpha': ['Clock phase', CPHA_0],
- 'bitorder': ['Bit order within the SPI data', MSB_FIRST],
- 'wordsize': ['Word size of SPI data', 8], # 1-64?
- }
- annotations = [
- ['Hex', 'SPI data bytes in hex format'],
- ]
-
- def __init__(self):
- self.oldsck = 1
- self.bitcount = 0
- self.mosidata = 0
- self.misodata = 0
- self.bytesreceived = 0
- self.samplenum = -1
- self.cs_was_deasserted_during_data_word = 0
-
- # Set protocol decoder option defaults.
- self.cs_polarity = Decoder.options['cs_polarity'][1]
- self.cpol = Decoder.options['cpol'][1]
- self.cpha = Decoder.options['cpha'][1]
- self.bitorder = Decoder.options['bitorder'][1]
- self.wordsize = Decoder.options['wordsize'][1]
-
- def start(self, metadata):
- self.out_proto = self.add(srd.OUTPUT_PROTO, 'spi')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'spi')
-
- def report(self):
- return 'SPI: %d bytes received' % self.bytesreceived
-
- def decode(self, ss, es, data):
- # HACK! At the moment the number of probes is not handled correctly.
- # E.g. if an input file (-i foo.sr) has more than two probes enabled.
- # for (samplenum, (mosi, sck, x, y, z, a)) in data:
- # for (samplenum, (cs, miso, sck, mosi, wp, hold)) in data:
- for (samplenum, (cs, miso, sck, mosi, wp, hold)) in data:
-
- self.samplenum += 1 # FIXME
-
- # Ignore sample if the clock pin hasn't changed.
- if sck == self.oldsck:
- continue
-
- self.oldsck = sck
-
- # Sample data on rising/falling clock edge (depends on mode).
- mode = spi_mode[self.cpol, self.cpha]
- if mode == 0 and sck == 0: # Sample on rising clock edge
- continue
- elif mode == 1 and sck == 1: # Sample on falling clock edge
- continue
- elif mode == 2 and sck == 1: # Sample on falling clock edge
- continue
- elif mode == 3 and sck == 0: # Sample on rising clock edge
- continue
-
- # If this is the first bit, save its sample number.
- if self.bitcount == 0:
- self.start_sample = samplenum
- deasserted = cs if (self.cs_polarity == ACTIVE_LOW) else not c
- if deasserted:
- self.cs_was_deasserted_during_data_word = 1
-
- # Receive MOSI bit into our shift register.
- if self.bitorder == MSB_FIRST:
- self.mosidata |= mosi << (self.wordsize - 1 - self.bitcount)
- else:
- self.mosidata |= mosi << self.bitcount
-
- # Receive MISO bit into our shift register.
- if self.bitorder == MSB_FIRST:
- self.misodata |= miso << (self.wordsize - 1 - self.bitcount)
- else:
- self.misodata |= miso << self.bitcount
-
- self.bitcount += 1
-
- # Continue to receive if not a byte yet.
- if self.bitcount != self.wordsize:
- continue
-
- self.put(self.start_sample, self.samplenum, self.out_proto,
- ['data', self.mosidata, self.misodata])
- self.put(self.start_sample, self.samplenum, self.out_ann,
- [ANN_HEX, ['MOSI: 0x%02x, MISO: 0x%02x' % (self.mosidata,
- self.misodata)]])
-
- if self.cs_was_deasserted_during_data_word:
- self.put(self.start_sample, self.samplenum, self.out_ann,
- [ANN_HEX, ['WARNING: CS# was deasserted during this '
- 'SPI data byte!']])
-
- # Reset decoder state.
- self.mosidata = 0
- self.misodata = 0
- self.bitcount = 0
-
- # Keep stats for summary.
- self.bytesreceived += 1
-
--- /dev/null
+##
+## 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)/spi
+
+dist_pkgdata_DATA = __init__.py spi.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .spi import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+## 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
+##
+
+import sigrokdecode as srd
+
+# Chip-select options
+ACTIVE_LOW = 0
+ACTIVE_HIGH = 1
+
+# Clock polarity options
+CPOL_0 = 0 # Clock is low when inactive
+CPOL_1 = 1 # Clock is high when inactive
+
+# Clock phase options
+CPHA_0 = 0 # Data is valid on the leading clock edge
+CPHA_1 = 1 # Data is valid on the trailing clock edge
+
+# Bit order options
+MSB_FIRST = 0
+LSB_FIRST = 1
+
+spi_mode = {
+ (0, 0): 0, # Mode 0
+ (0, 1): 1, # Mode 1
+ (1, 0): 2, # Mode 2
+ (1, 1): 3, # Mode 3
+}
+
+# Annotation formats
+ANN_HEX = 0
+
+class Decoder(srd.Decoder):
+ id = 'spi'
+ name = 'SPI'
+ longname = 'Serial Peripheral Interface'
+ desc = '...desc...'
+ longdesc = '...longdesc...'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['spi']
+ probes = [
+ {'id': 'mosi', 'name': 'MOSI',
+ 'desc': 'SPI MOSI line (Master out, slave in)'},
+ {'id': 'miso', 'name': 'MISO',
+ 'desc': 'SPI MISO line (Master in, slave out)'},
+ {'id': 'sck', 'name': 'CLK', 'desc': 'SPI clock line'},
+ {'id': 'cs', 'name': 'CS#', 'desc': 'SPI CS (chip select) line'},
+ ]
+ options = {
+ 'cs_polarity': ['CS# polarity', ACTIVE_LOW],
+ 'cpol': ['Clock polarity', CPOL_0],
+ 'cpha': ['Clock phase', CPHA_0],
+ 'bitorder': ['Bit order within the SPI data', MSB_FIRST],
+ 'wordsize': ['Word size of SPI data', 8], # 1-64?
+ }
+ annotations = [
+ ['Hex', 'SPI data bytes in hex format'],
+ ]
+
+ def __init__(self):
+ self.oldsck = 1
+ self.bitcount = 0
+ self.mosidata = 0
+ self.misodata = 0
+ self.bytesreceived = 0
+ self.samplenum = -1
+ self.cs_was_deasserted_during_data_word = 0
+
+ # Set protocol decoder option defaults.
+ self.cs_polarity = Decoder.options['cs_polarity'][1]
+ self.cpol = Decoder.options['cpol'][1]
+ self.cpha = Decoder.options['cpha'][1]
+ self.bitorder = Decoder.options['bitorder'][1]
+ self.wordsize = Decoder.options['wordsize'][1]
+
+ def start(self, metadata):
+ self.out_proto = self.add(srd.OUTPUT_PROTO, 'spi')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'spi')
+
+ def report(self):
+ return 'SPI: %d bytes received' % self.bytesreceived
+
+ def decode(self, ss, es, data):
+ # HACK! At the moment the number of probes is not handled correctly.
+ # E.g. if an input file (-i foo.sr) has more than two probes enabled.
+ # for (samplenum, (mosi, sck, x, y, z, a)) in data:
+ # for (samplenum, (cs, miso, sck, mosi, wp, hold)) in data:
+ for (samplenum, (cs, miso, sck, mosi, wp, hold)) in data:
+
+ self.samplenum += 1 # FIXME
+
+ # Ignore sample if the clock pin hasn't changed.
+ if sck == self.oldsck:
+ continue
+
+ self.oldsck = sck
+
+ # Sample data on rising/falling clock edge (depends on mode).
+ mode = spi_mode[self.cpol, self.cpha]
+ if mode == 0 and sck == 0: # Sample on rising clock edge
+ continue
+ elif mode == 1 and sck == 1: # Sample on falling clock edge
+ continue
+ elif mode == 2 and sck == 1: # Sample on falling clock edge
+ continue
+ elif mode == 3 and sck == 0: # Sample on rising clock edge
+ continue
+
+ # If this is the first bit, save its sample number.
+ if self.bitcount == 0:
+ self.start_sample = samplenum
+ deasserted = cs if (self.cs_polarity == ACTIVE_LOW) else not c
+ if deasserted:
+ self.cs_was_deasserted_during_data_word = 1
+
+ # Receive MOSI bit into our shift register.
+ if self.bitorder == MSB_FIRST:
+ self.mosidata |= mosi << (self.wordsize - 1 - self.bitcount)
+ else:
+ self.mosidata |= mosi << self.bitcount
+
+ # Receive MISO bit into our shift register.
+ if self.bitorder == MSB_FIRST:
+ self.misodata |= miso << (self.wordsize - 1 - self.bitcount)
+ else:
+ self.misodata |= miso << self.bitcount
+
+ self.bitcount += 1
+
+ # Continue to receive if not a byte yet.
+ if self.bitcount != self.wordsize:
+ continue
+
+ self.put(self.start_sample, self.samplenum, self.out_proto,
+ ['data', self.mosidata, self.misodata])
+ self.put(self.start_sample, self.samplenum, self.out_ann,
+ [ANN_HEX, ['MOSI: 0x%02x, MISO: 0x%02x' % (self.mosidata,
+ self.misodata)]])
+
+ if self.cs_was_deasserted_during_data_word:
+ self.put(self.start_sample, self.samplenum, self.out_ann,
+ [ANN_HEX, ['WARNING: CS# was deasserted during this '
+ 'SPI data byte!']])
+
+ # Reset decoder state.
+ self.mosidata = 0
+ self.misodata = 0
+ self.bitcount = 0
+
+ # Keep stats for summary.
+ self.bytesreceived += 1
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010 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
-##
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
- id = 'transitioncounter'
- name = 'Transition counter'
- longname = 'Pin transition counter'
- desc = 'Counts rising/falling edges in the signal.'
- longdesc = '...'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['transitioncounts']
- probes = []
- options = {}
- annotations = [
- ['TODO', 'TODO'],
- ]
-
- def __init__(self, **kwargs):
- self.channels = -1
- self.lastsample = None
-
- def start(self, metadata):
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'transitioncounter')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'transitioncounter')
-
- def report(self):
- pass
-
- def decode(self, ss, es, data):
-
- for (samplenum, s) in data:
-
- # ...
- if self.channels == -1:
- self.channels = len(s)
- self.oldbit = [0] * self.channels
- self.transitions = [0] * self.channels
- self.rising = [0] * self.channels
- self.falling = [0] * self.channels
-
- # Optimization: Skip identical samples (no transitions).
- if self.lastsample == s:
- continue
-
- # Upon the first sample, store the initial values.
- if self.lastsample == None:
- self.lastsample = s
- for i in range(self.channels):
- self.oldbit[i] = self.lastsample[i]
-
- # Iterate over all channels/probes in this sample.
- # Count rising and falling edges for each channel.
- for i in range(self.channels):
- curbit = s[i]
- # Optimization: Skip identical bits (no transitions).
- if self.oldbit[i] == curbit:
- continue
- elif (self.oldbit[i] == 0 and curbit == 1):
- self.rising[i] += 1
- elif (self.oldbit[i] == 1 and curbit == 0):
- self.falling[i] += 1
- self.oldbit[i] = curbit
-
- # Save the current sample as 'lastsample' for the next round.
- self.lastsample = s
-
- # Total number of transitions = rising + falling edges.
- for i in range(self.channels):
- self.transitions[i] = self.rising[i] + self.falling[i]
-
- # TODO: Which output format?
- # TODO: How to only output something after the last chunk of data?
- outdata = []
- for i in range(self.channels):
- outdata += [[self.transitions[i], self.rising[i], self.falling[i]]]
-
- if outdata != []:
- # self.put(0, 0, self.out_proto, out_proto)
- self.put(0, 0, self.out_ann, [0, [str(outdata)]])
-
--- /dev/null
+##
+## 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)/transitioncounter
+
+dist_pkgdata_DATA = __init__.py transitioncounter.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .transitioncounter import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010 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
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ id = 'transitioncounter'
+ name = 'Transition counter'
+ longname = 'Pin transition counter'
+ desc = 'Counts rising/falling edges in the signal.'
+ longdesc = '...'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['transitioncounts']
+ probes = []
+ options = {}
+ annotations = [
+ ['TODO', 'TODO'],
+ ]
+
+ def __init__(self, **kwargs):
+ self.channels = -1
+ self.lastsample = None
+
+ def start(self, metadata):
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'transitioncounter')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'transitioncounter')
+
+ def report(self):
+ pass
+
+ def decode(self, ss, es, data):
+
+ for (samplenum, s) in data:
+
+ # ...
+ if self.channels == -1:
+ self.channels = len(s)
+ self.oldbit = [0] * self.channels
+ self.transitions = [0] * self.channels
+ self.rising = [0] * self.channels
+ self.falling = [0] * self.channels
+
+ # Optimization: Skip identical samples (no transitions).
+ if self.lastsample == s:
+ continue
+
+ # Upon the first sample, store the initial values.
+ if self.lastsample == None:
+ self.lastsample = s
+ for i in range(self.channels):
+ self.oldbit[i] = self.lastsample[i]
+
+ # Iterate over all channels/probes in this sample.
+ # Count rising and falling edges for each channel.
+ for i in range(self.channels):
+ curbit = s[i]
+ # Optimization: Skip identical bits (no transitions).
+ if self.oldbit[i] == curbit:
+ continue
+ elif (self.oldbit[i] == 0 and curbit == 1):
+ self.rising[i] += 1
+ elif (self.oldbit[i] == 1 and curbit == 0):
+ self.falling[i] += 1
+ self.oldbit[i] = curbit
+
+ # Save the current sample as 'lastsample' for the next round.
+ self.lastsample = s
+
+ # Total number of transitions = rising + falling edges.
+ for i in range(self.channels):
+ self.transitions[i] = self.rising[i] + self.falling[i]
+
+ # TODO: Which output format?
+ # TODO: How to only output something after the last chunk of data?
+ outdata = []
+ for i in range(self.channels):
+ outdata += [[self.transitions[i], self.rising[i], self.falling[i]]]
+
+ if outdata != []:
+ # self.put(0, 0, self.out_proto, out_proto)
+ self.put(0, 0, self.out_ann, [0, [str(outdata)]])
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 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
-##
-
-#
-# UART 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>:
-# - T_START: The data is the (integer) value of the start bit (0 or 1).
-# - T_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).
-# - T_PARITY: The data is the (integer) value of the parity bit (0 or 1).
-# - T_STOP: The data is the (integer) value of the stop bit (0 or 1).
-# - T_INVALID_START: The data is the (integer) value of the start bit (0 or 1).
-# - T_INVALID_STOP: The data is the (integer) value of the stop bit (0 or 1).
-# - T_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.
-#
-# The <rxtx> field is 0 for RX packets, 1 for TX packets.
-#
-
-import sigrokdecode as srd
-
-# States
-WAIT_FOR_START_BIT = 0
-GET_START_BIT = 1
-GET_DATA_BITS = 2
-GET_PARITY_BIT = 3
-GET_STOP_BITS = 4
-
-# Used for differentiating between the two data directions.
-RX = 0
-TX = 1
-
-# Parity options
-PARITY_NONE = 0
-PARITY_ODD = 1
-PARITY_EVEN = 2
-PARITY_ZERO = 3
-PARITY_ONE = 4
-
-# Stop bit options
-STOP_BITS_0_5 = 0
-STOP_BITS_1 = 1
-STOP_BITS_1_5 = 2
-STOP_BITS_2 = 3
-
-# Bit order options
-LSB_FIRST = 0
-MSB_FIRST = 1
-
-# Annotation feed formats
-ANN_ASCII = 0
-ANN_DEC = 1
-ANN_HEX = 2
-ANN_OCT = 3
-ANN_BITS = 4
-
-# Protocol output packet types
-T_START = 0
-T_DATA = 1
-T_PARITY = 2
-T_STOP = 3
-T_INVALID_START = 4
-T_INVALID_STOP = 5
-T_PARITY_ERROR = 6
-
-# 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.
-# PARITY_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 == PARITY_ZERO:
- return parity_bit == 0
- elif parity_type == PARITY_ONE:
- return parity_bit == 1
-
- # Count number of 1 (high) bits in the data (and the parity bit itself!).
- parity = bin(data).count('1') + parity_bit
-
- # Check for odd/even parity.
- if parity_type == PARITY_ODD:
- return (parity % 2) == 1
- elif parity_type == PARITY_EVEN:
- return (parity % 2) == 0
- else:
- raise Exception('Invalid parity type: %d' % parity_type)
-
-class Decoder(srd.Decoder):
- id = 'uart'
- name = 'UART'
- longname = 'Universal Asynchronous Receiver/Transmitter'
- desc = 'Universal Asynchronous Receiver/Transmitter (UART)'
- longdesc = 'TODO.'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['uart']
- probes = [
- # Allow specifying only one of the signals, e.g. if only one data
- # direction exists (or is relevant).
- {'id': 'rx', 'name': 'RX', 'desc': 'UART receive line'},
- {'id': 'tx', 'name': 'TX', 'desc': 'UART transmit line'},
- ]
- options = {
- 'baudrate': ['Baud rate', 115200],
- 'num_data_bits': ['Data bits', 8], # Valid: 5-9.
- 'parity': ['Parity', PARITY_NONE],
- 'parity_check': ['Check parity', True],
- 'num_stop_bits': ['Stop bit(s)', STOP_BITS_1],
- 'bit_order': ['Bit order', LSB_FIRST],
- # TODO: Options to invert the signal(s).
- }
- 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, rxtx, data):
- self.put(self.startsample[rxtx], self.samplenum - 1, self.out_ann, data)
-
- def __init__(self, **kwargs):
- self.samplenum = 0
- self.frame_start = [-1, -1]
- self.startbit = [-1, -1]
- self.cur_data_bit = [0, 0]
- self.databyte = [0, 0]
- self.stopbit1 = [-1, -1]
- self.startsample = [-1, -1]
-
- # Initial state.
- self.state = [WAIT_FOR_START_BIT, WAIT_FOR_START_BIT]
-
- self.oldbit = [None, None]
-
- # Set protocol decoder option defaults.
- self.baudrate = Decoder.options['baudrate'][1]
- self.num_data_bits = Decoder.options['num_data_bits'][1]
- self.parity = Decoder.options['parity'][1]
- self.check_parity = Decoder.options['parity_check'][1]
- self.num_stop_bits = Decoder.options['num_stop_bits'][1]
- self.bit_order = Decoder.options['bit_order'][1]
-
- def start(self, metadata):
- self.samplerate = metadata['samplerate']
- self.out_proto = self.add(srd.OUTPUT_PROTO, 'uart')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'uart')
-
- # TODO: Override PD options, if user wants that.
-
- # The width of one UART bit in number of samples.
- self.bit_width = float(self.samplerate) / float(self.baudrate)
-
- def report(self):
- pass
-
- # Return true if we reached the middle of the desired bit, false otherwise.
- def reached_bit(self, rxtx, bitnum):
- # bitpos is the samplenumber which is in the middle of the
- # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
- # (if used) or the first stop bit, and so on).
- bitpos = self.frame_start[rxtx] + (self.bit_width / 2.0)
- bitpos += bitnum * self.bit_width
- if self.samplenum >= bitpos:
- return True
- return False
-
- def reached_bit_last(self, rxtx, bitnum):
- bitpos = self.frame_start[rxtx] + ((bitnum + 1) * self.bit_width)
- if self.samplenum >= bitpos:
- return True
- return False
-
- def wait_for_start_bit(self, rxtx, old_signal, signal):
- # The start bit is always 0 (low). As the idle UART (and the stop bit)
- # level is 1 (high), the beginning of a start bit is a falling edge.
- if not (old_signal == 1 and signal == 0):
- return
-
- # Save the sample number where the start bit begins.
- self.frame_start[rxtx] = self.samplenum
-
- self.state[rxtx] = GET_START_BIT
-
- def get_start_bit(self, rxtx, signal):
- # Skip samples until we're in the middle of the start bit.
- if not self.reached_bit(rxtx, 0):
- return
-
- self.startbit[rxtx] = signal
-
- # The startbit must be 0. If not, we report an error.
- if self.startbit[rxtx] != 0:
- self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
- [T_INVALID_START, rxtx, self.startbit[rxtx]])
- # TODO: Abort? Ignore rest of the frame?
-
- self.cur_data_bit[rxtx] = 0
- self.databyte[rxtx] = 0
- self.startsample[rxtx] = -1
-
- self.state[rxtx] = GET_DATA_BITS
-
- self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
- [T_START, rxtx, self.startbit[rxtx]])
- self.put(self.frame_start[rxtx], self.samplenum, self.out_ann,
- [ANN_ASCII, ['Start bit', 'Start', 'S']])
-
- def get_data_bits(self, rxtx, signal):
- # Skip samples until we're in the middle of the desired data bit.
- if not self.reached_bit(rxtx, self.cur_data_bit[rxtx] + 1):
- return
-
- # Save the sample number where the data byte starts.
- if self.startsample[rxtx] == -1:
- self.startsample[rxtx] = self.samplenum
-
- # Get the next data bit in LSB-first or MSB-first fashion.
- if self.bit_order == LSB_FIRST:
- self.databyte[rxtx] >>= 1
- self.databyte[rxtx] |= (signal << (self.num_data_bits - 1))
- elif self.bit_order == MSB_FIRST:
- self.databyte[rxtx] <<= 1
- self.databyte[rxtx] |= (signal << 0)
- else:
- raise Exception('Invalid bit order value: %d', self.bit_order)
-
- # Return here, unless we already received all data bits.
- if self.cur_data_bit[rxtx] < self.num_data_bits - 1: # TODO? Off-by-one?
- self.cur_data_bit[rxtx] += 1
- return
-
- self.state[rxtx] = GET_PARITY_BIT
-
- self.put(self.startsample[rxtx], self.samplenum - 1, self.out_proto,
- [T_DATA, rxtx, self.databyte[rxtx]])
-
- s = 'RX: ' if (rxtx == RX) else 'TX: '
- self.putx(rxtx, [ANN_ASCII, [s + chr(self.databyte[rxtx])]])
- self.putx(rxtx, [ANN_DEC, [s + str(self.databyte[rxtx])]])
- self.putx(rxtx, [ANN_HEX, [s + hex(self.databyte[rxtx]),
- s + hex(self.databyte[rxtx])[2:]]])
- self.putx(rxtx, [ANN_OCT, [s + oct(self.databyte[rxtx]),
- s + oct(self.databyte[rxtx])[2:]]])
- self.putx(rxtx, [ANN_BITS, [s + bin(self.databyte[rxtx]),
- s + bin(self.databyte[rxtx])[2:]]])
-
- def get_parity_bit(self, rxtx, signal):
- # If no parity is used/configured, skip to the next state immediately.
- if self.parity == PARITY_NONE:
- self.state[rxtx] = GET_STOP_BITS
- return
-
- # Skip samples until we're in the middle of the parity bit.
- if not self.reached_bit(rxtx, self.num_data_bits + 1):
- return
-
- self.paritybit[rxtx] = signal
-
- self.state[rxtx] = GET_STOP_BITS
-
- if parity_ok(self.parity[rxtx], self.paritybit[rxtx],
- self.databyte[rxtx], self.num_data_bits):
- # TODO: Fix range.
- self.put(self.samplenum, self.samplenum, self.out_proto,
- [T_PARITY_BIT, rxtx, self.paritybit[rxtx]])
- self.put(self.samplenum, self.samplenum, self.out_ann,
- [ANN_ASCII, ['Parity bit', 'Parity', 'P']])
- else:
- # TODO: Fix range.
- # TODO: Return expected/actual parity values.
- self.put(self.samplenum, self.samplenum, self.out_proto,
- [T_PARITY_ERROR, rxtx, (0, 1)]) # FIXME: Dummy tuple...
- self.put(self.samplenum, self.samplenum, self.out_ann,
- [ANN_ASCII, ['Parity error', 'Parity err', 'PE']])
-
- # TODO: Currently only supports 1 stop bit.
- def get_stop_bits(self, rxtx, signal):
- # Skip samples until we're in the middle of the stop bit(s).
- skip_parity = 0 if self.parity == PARITY_NONE else 1
- if not self.reached_bit(rxtx, self.num_data_bits + 1 + skip_parity):
- return
-
- self.stopbit1[rxtx] = signal
-
- # Stop bits must be 1. If not, we report an error.
- if self.stopbit1[rxtx] != 1:
- self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
- [T_INVALID_STOP, rxtx, self.stopbit1[rxtx]])
- # TODO: Abort? Ignore the frame? Other?
-
- self.state[rxtx] = WAIT_FOR_START_BIT
-
- # TODO: Fix range.
- self.put(self.samplenum, self.samplenum, self.out_proto,
- [T_STOP, rxtx, self.stopbit1[rxtx]])
- self.put(self.samplenum, self.samplenum, self.out_ann,
- [ANN_ASCII, ['Stop bit', 'Stop', 'P']])
-
- def decode(self, ss, es, data): # TODO
- for (samplenum, (rx, tx)) in data:
-
- # TODO: Start counting at 0 or 1? Increase before or after?
- self.samplenum += 1
-
- # First sample: Save RX/TX value.
- if self.oldbit[RX] == None:
- self.oldbit[RX] = rx
- continue
- if self.oldbit[TX] == None:
- self.oldbit[TX] = tx
- continue
-
- # State machine.
- for rxtx in (RX, TX):
- signal = rx if (rxtx == RX) else tx
-
- if self.state[rxtx] == WAIT_FOR_START_BIT:
- self.wait_for_start_bit(rxtx, self.oldbit[rxtx], signal)
- elif self.state[rxtx] == GET_START_BIT:
- self.get_start_bit(rxtx, signal)
- elif self.state[rxtx] == GET_DATA_BITS:
- self.get_data_bits(rxtx, signal)
- elif self.state[rxtx] == GET_PARITY_BIT:
- self.get_parity_bit(rxtx, signal)
- elif self.state[rxtx] == GET_STOP_BITS:
- self.get_stop_bits(rxtx, signal)
- else:
- raise Exception('Invalid state: %s' % self.state[rxtx])
-
- # Save current RX/TX values for the next round.
- self.oldbit[rxtx] = signal
-
--- /dev/null
+##
+## 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)/uart
+
+dist_pkgdata_DATA = __init__.py uart.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .uart import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 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
+##
+
+#
+# UART 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>:
+# - T_START: The data is the (integer) value of the start bit (0 or 1).
+# - T_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).
+# - T_PARITY: The data is the (integer) value of the parity bit (0 or 1).
+# - T_STOP: The data is the (integer) value of the stop bit (0 or 1).
+# - T_INVALID_START: The data is the (integer) value of the start bit (0 or 1).
+# - T_INVALID_STOP: The data is the (integer) value of the stop bit (0 or 1).
+# - T_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.
+#
+# The <rxtx> field is 0 for RX packets, 1 for TX packets.
+#
+
+import sigrokdecode as srd
+
+# States
+WAIT_FOR_START_BIT = 0
+GET_START_BIT = 1
+GET_DATA_BITS = 2
+GET_PARITY_BIT = 3
+GET_STOP_BITS = 4
+
+# Used for differentiating between the two data directions.
+RX = 0
+TX = 1
+
+# Parity options
+PARITY_NONE = 0
+PARITY_ODD = 1
+PARITY_EVEN = 2
+PARITY_ZERO = 3
+PARITY_ONE = 4
+
+# Stop bit options
+STOP_BITS_0_5 = 0
+STOP_BITS_1 = 1
+STOP_BITS_1_5 = 2
+STOP_BITS_2 = 3
+
+# Bit order options
+LSB_FIRST = 0
+MSB_FIRST = 1
+
+# Annotation feed formats
+ANN_ASCII = 0
+ANN_DEC = 1
+ANN_HEX = 2
+ANN_OCT = 3
+ANN_BITS = 4
+
+# Protocol output packet types
+T_START = 0
+T_DATA = 1
+T_PARITY = 2
+T_STOP = 3
+T_INVALID_START = 4
+T_INVALID_STOP = 5
+T_PARITY_ERROR = 6
+
+# 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.
+# PARITY_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 == PARITY_ZERO:
+ return parity_bit == 0
+ elif parity_type == PARITY_ONE:
+ return parity_bit == 1
+
+ # Count number of 1 (high) bits in the data (and the parity bit itself!).
+ parity = bin(data).count('1') + parity_bit
+
+ # Check for odd/even parity.
+ if parity_type == PARITY_ODD:
+ return (parity % 2) == 1
+ elif parity_type == PARITY_EVEN:
+ return (parity % 2) == 0
+ else:
+ raise Exception('Invalid parity type: %d' % parity_type)
+
+class Decoder(srd.Decoder):
+ id = 'uart'
+ name = 'UART'
+ longname = 'Universal Asynchronous Receiver/Transmitter'
+ desc = 'Universal Asynchronous Receiver/Transmitter (UART)'
+ longdesc = 'TODO.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['uart']
+ probes = [
+ # Allow specifying only one of the signals, e.g. if only one data
+ # direction exists (or is relevant).
+ {'id': 'rx', 'name': 'RX', 'desc': 'UART receive line'},
+ {'id': 'tx', 'name': 'TX', 'desc': 'UART transmit line'},
+ ]
+ options = {
+ 'baudrate': ['Baud rate', 115200],
+ 'num_data_bits': ['Data bits', 8], # Valid: 5-9.
+ 'parity': ['Parity', PARITY_NONE],
+ 'parity_check': ['Check parity', True],
+ 'num_stop_bits': ['Stop bit(s)', STOP_BITS_1],
+ 'bit_order': ['Bit order', LSB_FIRST],
+ # TODO: Options to invert the signal(s).
+ }
+ 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, rxtx, data):
+ self.put(self.startsample[rxtx], self.samplenum - 1, self.out_ann, data)
+
+ def __init__(self, **kwargs):
+ self.samplenum = 0
+ self.frame_start = [-1, -1]
+ self.startbit = [-1, -1]
+ self.cur_data_bit = [0, 0]
+ self.databyte = [0, 0]
+ self.stopbit1 = [-1, -1]
+ self.startsample = [-1, -1]
+
+ # Initial state.
+ self.state = [WAIT_FOR_START_BIT, WAIT_FOR_START_BIT]
+
+ self.oldbit = [None, None]
+
+ # Set protocol decoder option defaults.
+ self.baudrate = Decoder.options['baudrate'][1]
+ self.num_data_bits = Decoder.options['num_data_bits'][1]
+ self.parity = Decoder.options['parity'][1]
+ self.check_parity = Decoder.options['parity_check'][1]
+ self.num_stop_bits = Decoder.options['num_stop_bits'][1]
+ self.bit_order = Decoder.options['bit_order'][1]
+
+ def start(self, metadata):
+ self.samplerate = metadata['samplerate']
+ self.out_proto = self.add(srd.OUTPUT_PROTO, 'uart')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'uart')
+
+ # TODO: Override PD options, if user wants that.
+
+ # The width of one UART bit in number of samples.
+ self.bit_width = float(self.samplerate) / float(self.baudrate)
+
+ def report(self):
+ pass
+
+ # Return true if we reached the middle of the desired bit, false otherwise.
+ def reached_bit(self, rxtx, bitnum):
+ # bitpos is the samplenumber which is in the middle of the
+ # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
+ # (if used) or the first stop bit, and so on).
+ bitpos = self.frame_start[rxtx] + (self.bit_width / 2.0)
+ bitpos += bitnum * self.bit_width
+ if self.samplenum >= bitpos:
+ return True
+ return False
+
+ def reached_bit_last(self, rxtx, bitnum):
+ bitpos = self.frame_start[rxtx] + ((bitnum + 1) * self.bit_width)
+ if self.samplenum >= bitpos:
+ return True
+ return False
+
+ def wait_for_start_bit(self, rxtx, old_signal, signal):
+ # The start bit is always 0 (low). As the idle UART (and the stop bit)
+ # level is 1 (high), the beginning of a start bit is a falling edge.
+ if not (old_signal == 1 and signal == 0):
+ return
+
+ # Save the sample number where the start bit begins.
+ self.frame_start[rxtx] = self.samplenum
+
+ self.state[rxtx] = GET_START_BIT
+
+ def get_start_bit(self, rxtx, signal):
+ # Skip samples until we're in the middle of the start bit.
+ if not self.reached_bit(rxtx, 0):
+ return
+
+ self.startbit[rxtx] = signal
+
+ # The startbit must be 0. If not, we report an error.
+ if self.startbit[rxtx] != 0:
+ self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+ [T_INVALID_START, rxtx, self.startbit[rxtx]])
+ # TODO: Abort? Ignore rest of the frame?
+
+ self.cur_data_bit[rxtx] = 0
+ self.databyte[rxtx] = 0
+ self.startsample[rxtx] = -1
+
+ self.state[rxtx] = GET_DATA_BITS
+
+ self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+ [T_START, rxtx, self.startbit[rxtx]])
+ self.put(self.frame_start[rxtx], self.samplenum, self.out_ann,
+ [ANN_ASCII, ['Start bit', 'Start', 'S']])
+
+ def get_data_bits(self, rxtx, signal):
+ # Skip samples until we're in the middle of the desired data bit.
+ if not self.reached_bit(rxtx, self.cur_data_bit[rxtx] + 1):
+ return
+
+ # Save the sample number where the data byte starts.
+ if self.startsample[rxtx] == -1:
+ self.startsample[rxtx] = self.samplenum
+
+ # Get the next data bit in LSB-first or MSB-first fashion.
+ if self.bit_order == LSB_FIRST:
+ self.databyte[rxtx] >>= 1
+ self.databyte[rxtx] |= (signal << (self.num_data_bits - 1))
+ elif self.bit_order == MSB_FIRST:
+ self.databyte[rxtx] <<= 1
+ self.databyte[rxtx] |= (signal << 0)
+ else:
+ raise Exception('Invalid bit order value: %d', self.bit_order)
+
+ # Return here, unless we already received all data bits.
+ if self.cur_data_bit[rxtx] < self.num_data_bits - 1: # TODO? Off-by-one?
+ self.cur_data_bit[rxtx] += 1
+ return
+
+ self.state[rxtx] = GET_PARITY_BIT
+
+ self.put(self.startsample[rxtx], self.samplenum - 1, self.out_proto,
+ [T_DATA, rxtx, self.databyte[rxtx]])
+
+ s = 'RX: ' if (rxtx == RX) else 'TX: '
+ self.putx(rxtx, [ANN_ASCII, [s + chr(self.databyte[rxtx])]])
+ self.putx(rxtx, [ANN_DEC, [s + str(self.databyte[rxtx])]])
+ self.putx(rxtx, [ANN_HEX, [s + hex(self.databyte[rxtx]),
+ s + hex(self.databyte[rxtx])[2:]]])
+ self.putx(rxtx, [ANN_OCT, [s + oct(self.databyte[rxtx]),
+ s + oct(self.databyte[rxtx])[2:]]])
+ self.putx(rxtx, [ANN_BITS, [s + bin(self.databyte[rxtx]),
+ s + bin(self.databyte[rxtx])[2:]]])
+
+ def get_parity_bit(self, rxtx, signal):
+ # If no parity is used/configured, skip to the next state immediately.
+ if self.parity == PARITY_NONE:
+ self.state[rxtx] = GET_STOP_BITS
+ return
+
+ # Skip samples until we're in the middle of the parity bit.
+ if not self.reached_bit(rxtx, self.num_data_bits + 1):
+ return
+
+ self.paritybit[rxtx] = signal
+
+ self.state[rxtx] = GET_STOP_BITS
+
+ if parity_ok(self.parity[rxtx], self.paritybit[rxtx],
+ self.databyte[rxtx], self.num_data_bits):
+ # TODO: Fix range.
+ self.put(self.samplenum, self.samplenum, self.out_proto,
+ [T_PARITY_BIT, rxtx, self.paritybit[rxtx]])
+ self.put(self.samplenum, self.samplenum, self.out_ann,
+ [ANN_ASCII, ['Parity bit', 'Parity', 'P']])
+ else:
+ # TODO: Fix range.
+ # TODO: Return expected/actual parity values.
+ self.put(self.samplenum, self.samplenum, self.out_proto,
+ [T_PARITY_ERROR, rxtx, (0, 1)]) # FIXME: Dummy tuple...
+ self.put(self.samplenum, self.samplenum, self.out_ann,
+ [ANN_ASCII, ['Parity error', 'Parity err', 'PE']])
+
+ # TODO: Currently only supports 1 stop bit.
+ def get_stop_bits(self, rxtx, signal):
+ # Skip samples until we're in the middle of the stop bit(s).
+ skip_parity = 0 if self.parity == PARITY_NONE else 1
+ if not self.reached_bit(rxtx, self.num_data_bits + 1 + skip_parity):
+ return
+
+ self.stopbit1[rxtx] = signal
+
+ # Stop bits must be 1. If not, we report an error.
+ if self.stopbit1[rxtx] != 1:
+ self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+ [T_INVALID_STOP, rxtx, self.stopbit1[rxtx]])
+ # TODO: Abort? Ignore the frame? Other?
+
+ self.state[rxtx] = WAIT_FOR_START_BIT
+
+ # TODO: Fix range.
+ self.put(self.samplenum, self.samplenum, self.out_proto,
+ [T_STOP, rxtx, self.stopbit1[rxtx]])
+ self.put(self.samplenum, self.samplenum, self.out_ann,
+ [ANN_ASCII, ['Stop bit', 'Stop', 'P']])
+
+ def decode(self, ss, es, data): # TODO
+ for (samplenum, (rx, tx)) in data:
+
+ # TODO: Start counting at 0 or 1? Increase before or after?
+ self.samplenum += 1
+
+ # First sample: Save RX/TX value.
+ if self.oldbit[RX] == None:
+ self.oldbit[RX] = rx
+ continue
+ if self.oldbit[TX] == None:
+ self.oldbit[TX] = tx
+ continue
+
+ # State machine.
+ for rxtx in (RX, TX):
+ signal = rx if (rxtx == RX) else tx
+
+ if self.state[rxtx] == WAIT_FOR_START_BIT:
+ self.wait_for_start_bit(rxtx, self.oldbit[rxtx], signal)
+ elif self.state[rxtx] == GET_START_BIT:
+ self.get_start_bit(rxtx, signal)
+ elif self.state[rxtx] == GET_DATA_BITS:
+ self.get_data_bits(rxtx, signal)
+ elif self.state[rxtx] == GET_PARITY_BIT:
+ self.get_parity_bit(rxtx, signal)
+ elif self.state[rxtx] == GET_STOP_BITS:
+ self.get_stop_bits(rxtx, signal)
+ else:
+ raise Exception('Invalid state: %s' % self.state[rxtx])
+
+ # Save current RX/TX values for the next round.
+ self.oldbit[rxtx] = signal
+
+++ /dev/null
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-##
-## 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
-##
-
-#
-# USB Full-speed protocol decoder
-#
-# Full-speed USB signalling consists of two signal lines, both driven at 3.3V
-# logic levels. The signals are DP (D+) and DM (D-), and normally operate in
-# differential mode.
-# The state where DP=1,DM=0 is J, the state DP=0,DM=1 is K.
-# A state SE0 is defined where DP=DM=0. This common mode signal is used to
-# signal a reset or end of packet.
-#
-# Data transmitted on the USB is encoded with NRZI. A transition from J to K
-# or vice-versa indicates a logic 0, while no transition indicates a logic 1.
-# If 6 ones are transmitted consecutively, a zero is inserted to force a
-# transition. This is known as bit stuffing. Data is transferred at a rate
-# of 12Mbit/s. The SE0 transmitted to signal an end-of-packet is two bit
-# intervals long.
-#
-# Details:
-# https://en.wikipedia.org/wiki/USB
-# http://www.usb.org/developers/docs/
-#
-
-import sigrokdecode as srd
-
-# States
-SE0, J, K, SE1 = 0, 1, 2, 3
-
-# ...
-syms = {
- (0, 0): SE0,
- (1, 0): J,
- (0, 1): K,
- (1, 1): SE1,
-}
-
-# ...
-pids = {
- '10000111': 'OUT', # Tokens
- '10010110': 'IN',
- '10100101': 'SOF',
- '10110100': 'SETUP',
- '11000011': 'DATA0', # Data
- '11010010': 'DATA1',
- '01001011': 'ACK', # Handshake
- '01011010': 'NAK',
- '01111000': 'STALL',
- '01101001': 'NYET',
-}
-
-def bitstr_to_num(bitstr):
- if not bitstr:
- return 0
- l = list(bitstr)
- l.reverse()
- return int(''.join(l), 2)
-
-def packet_decode(packet):
- sync = packet[:8]
- pid = packet[8:16]
- pid = pids.get(pid, pid)
-
- # Remove CRC.
- if pid in ('OUT', 'IN', 'SOF', 'SETUP'):
- data = packet[16:-5]
- if pid == 'SOF':
- data = str(bitstr_to_num(data))
- else:
- dev = bitstr_to_num(data[:7])
- ep = bitstr_to_num(data[7:])
- data = 'DEV %d EP %d' % (dev, ep)
-
- elif pid in ('DATA0', 'DATA1'):
- data = packet[16:-16]
- tmp = ''
- while data:
- tmp += '%02x ' % bitstr_to_num(data[:8])
- data = data[8:]
- data = tmp
- else:
- data = packet[16:]
-
- if sync != '00000001':
- return 'SYNC INVALID!'
-
- return pid + ' ' + data
-
-class Decoder(srd.Decoder):
- id = 'usb'
- name = 'USB'
- longname = 'Universal Serial Bus'
- desc = 'Universal Serial Bus'
- longdesc = '...longdesc...'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = ['usb']
- probes = [
- {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
- {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
- ]
- options = {}
- annotations = [
- ['TODO', 'TODO']
- ]
-
- def __init__(self):
- pass
-
- def start(self, metadata):
- self.rate = metadata['samplerate']
-
- # self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb')
- self.out_ann = self.add(srd.OUTPUT_ANN, 'usb')
-
- if self.rate < 48000000:
- raise Exception('Sample rate not sufficient for USB decoding')
-
- # Initialise decoder state.
- self.sym = J
- self.scount = 0
- self.packet = ''
-
- def decode(self, ss, es, data):
-
- # FIXME
- # for (samplenum, (dp, dm, x, y, z, a)) in data:
- for (samplenum, (dm, dp)) in data:
-
- self.scount += 1
-
- sym = syms[dp, dm]
-
- # ...
- if sym == self.sym:
- continue
-
- if self.scount == 1:
- # We ignore single sample width pulses.
- # I sometimes get these with the OLS.
- self.sym = sym
- self.scount = 0
- continue
-
- # How many bits since the last transition?
- if self.packet != '' or self.sym != J:
- bitcount = int((self.scount - 1) * 12000000 / self.rate)
- else:
- bitcount = 0
-
- if self.sym == SE0:
- if bitcount == 1:
- # End-Of-Packet (EOP)
- self.put(0, 0, self.out_ann,
- [0, [packet_decode(self.packet), self.packet]])
- else:
- # Longer than EOP, assume reset.
- self.put(0, 0, self.out_ann, [0, ['RESET']])
- self.scount = 0
- self.sym = sym
- self.packet = ''
- continue
-
- # Add bits to the packet string.
- self.packet += '1' * bitcount
-
- # Handle bit stuffing.
- if bitcount < 6 and sym != SE0:
- self.packet += '0'
- elif bitcount > 6:
- self.put(0, 0, self.out_ann, [0, ['BIT STUFF ERROR']])
-
- self.scount = 0
- self.sym = sym
-
--- /dev/null
+##
+## 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)/usb
+
+dist_pkgdata_DATA = __init__.py usb.py
+
+CLEANFILES = *.pyc
+
--- /dev/null
+##
+## 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
+##
+
+from .usb import *
+
--- /dev/null
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+##
+## 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
+##
+
+#
+# USB Full-speed protocol decoder
+#
+# Full-speed USB signalling consists of two signal lines, both driven at 3.3V
+# logic levels. The signals are DP (D+) and DM (D-), and normally operate in
+# differential mode.
+# The state where DP=1,DM=0 is J, the state DP=0,DM=1 is K.
+# A state SE0 is defined where DP=DM=0. This common mode signal is used to
+# signal a reset or end of packet.
+#
+# Data transmitted on the USB is encoded with NRZI. A transition from J to K
+# or vice-versa indicates a logic 0, while no transition indicates a logic 1.
+# If 6 ones are transmitted consecutively, a zero is inserted to force a
+# transition. This is known as bit stuffing. Data is transferred at a rate
+# of 12Mbit/s. The SE0 transmitted to signal an end-of-packet is two bit
+# intervals long.
+#
+# Details:
+# https://en.wikipedia.org/wiki/USB
+# http://www.usb.org/developers/docs/
+#
+
+import sigrokdecode as srd
+
+# States
+SE0, J, K, SE1 = 0, 1, 2, 3
+
+# ...
+syms = {
+ (0, 0): SE0,
+ (1, 0): J,
+ (0, 1): K,
+ (1, 1): SE1,
+}
+
+# ...
+pids = {
+ '10000111': 'OUT', # Tokens
+ '10010110': 'IN',
+ '10100101': 'SOF',
+ '10110100': 'SETUP',
+ '11000011': 'DATA0', # Data
+ '11010010': 'DATA1',
+ '01001011': 'ACK', # Handshake
+ '01011010': 'NAK',
+ '01111000': 'STALL',
+ '01101001': 'NYET',
+}
+
+def bitstr_to_num(bitstr):
+ if not bitstr:
+ return 0
+ l = list(bitstr)
+ l.reverse()
+ return int(''.join(l), 2)
+
+def packet_decode(packet):
+ sync = packet[:8]
+ pid = packet[8:16]
+ pid = pids.get(pid, pid)
+
+ # Remove CRC.
+ if pid in ('OUT', 'IN', 'SOF', 'SETUP'):
+ data = packet[16:-5]
+ if pid == 'SOF':
+ data = str(bitstr_to_num(data))
+ else:
+ dev = bitstr_to_num(data[:7])
+ ep = bitstr_to_num(data[7:])
+ data = 'DEV %d EP %d' % (dev, ep)
+
+ elif pid in ('DATA0', 'DATA1'):
+ data = packet[16:-16]
+ tmp = ''
+ while data:
+ tmp += '%02x ' % bitstr_to_num(data[:8])
+ data = data[8:]
+ data = tmp
+ else:
+ data = packet[16:]
+
+ if sync != '00000001':
+ return 'SYNC INVALID!'
+
+ return pid + ' ' + data
+
+class Decoder(srd.Decoder):
+ id = 'usb'
+ name = 'USB'
+ longname = 'Universal Serial Bus'
+ desc = 'Universal Serial Bus'
+ longdesc = '...longdesc...'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['usb']
+ probes = [
+ {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
+ {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
+ ]
+ options = {}
+ annotations = [
+ ['TODO', 'TODO']
+ ]
+
+ def __init__(self):
+ pass
+
+ def start(self, metadata):
+ self.rate = metadata['samplerate']
+
+ # self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb')
+ self.out_ann = self.add(srd.OUTPUT_ANN, 'usb')
+
+ if self.rate < 48000000:
+ raise Exception('Sample rate not sufficient for USB decoding')
+
+ # Initialise decoder state.
+ self.sym = J
+ self.scount = 0
+ self.packet = ''
+
+ def decode(self, ss, es, data):
+
+ # FIXME
+ # for (samplenum, (dp, dm, x, y, z, a)) in data:
+ for (samplenum, (dm, dp)) in data:
+
+ self.scount += 1
+
+ sym = syms[dp, dm]
+
+ # ...
+ if sym == self.sym:
+ continue
+
+ if self.scount == 1:
+ # We ignore single sample width pulses.
+ # I sometimes get these with the OLS.
+ self.sym = sym
+ self.scount = 0
+ continue
+
+ # How many bits since the last transition?
+ if self.packet != '' or self.sym != J:
+ bitcount = int((self.scount - 1) * 12000000 / self.rate)
+ else:
+ bitcount = 0
+
+ if self.sym == SE0:
+ if bitcount == 1:
+ # End-Of-Packet (EOP)
+ self.put(0, 0, self.out_ann,
+ [0, [packet_decode(self.packet), self.packet]])
+ else:
+ # Longer than EOP, assume reset.
+ self.put(0, 0, self.out_ann, [0, ['RESET']])
+ self.scount = 0
+ self.sym = sym
+ self.packet = ''
+ continue
+
+ # Add bits to the packet string.
+ self.packet += '1' * bitcount
+
+ # Handle bit stuffing.
+ if bitcount < 6 and sym != SE0:
+ self.packet += '0'
+ elif bitcount > 6:
+ self.put(0, 0, self.out_ann, [0, ['BIT STUFF ERROR']])
+
+ self.scount = 0
+ self.sym = sym
+