http://www.python.org/dev/peps/pep-0008/
+Exceptions:
+
+ - All strings should use single quotes ('foo' instead of "foo").
+
+ - No double-newlines between methods (or anywhere else).
+
Contributions
-------------
- Longer, multi-line descriptions should be placed in the protocol
decoder's __init__.py file as docstring. It can be viewed (for a specific
- protocol decoder, e.g., UART) via "sigrok-cli -a uart", or in various
+ protocol decoder, e.g., UART) via "sigrok-cli -P uart --show", or in various
other places in GUIs.
+ - Input IDs, output IDs, tags, channel IDs, option IDs, annotation class IDs,
+ annotation row IDs, and binary class IDs each must be unique.
+
+ - Annotation class IDs must not overlap with annotation row IDs.
+ For example, you cannot have an annotation row named "foo" if you already
+ have an annotation class named "foo". This avoids confusion for users
+ and simplifies e.g. command-line usage of decoders.
+
+ - Annotation class IDs should generally be singular, annotation row IDs
+ should generally be plural. Example: UART annotation classes could be
+ named "stop-bit" or "parity-bit" (singular), the annotation row containing
+ these annotation classes could be named "bits" (plural).
+
- Generally use strings for states (of the PD state machine), not integers.
This avoids having to keep a list of state definitions at the top of file.
The performance overhead for this is negligible in practice.
nodist_pkginclude_HEADERS = version.h
noinst_HEADERS = libsigrokdecode-internal.h
+if WITH_IRMP
+lib_LTLIBRARIES += libirmp.la
+libirmp_la_SOURCES = \
+ irmp/irmp-main-sharedlib.c \
+ irmp/irmp-main-sharedlib.h \
+ irmp/irmp.h \
+ irmp/irmpconfig.h \
+ irmp/irmpsystem.h \
+ irmp/irmpprotocols.h
+noinst_HEADERS += irmp/irmp.c
+libirmp_la_CFLAGS = $(LIBIRMP_CFLAGS)
+libirmp_la_LIBADD = $(LIBIRMP_LIBS)
+libirmp_la_LDFLAGS = -no-undefined -version-info 0:0:0
+endif
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsigrokdecode.pc
IRC
---
-You can find the sigrok developers in the #sigrok IRC channel on Freenode.
+You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat.
Website
AC_PROG_INSTALL
AC_PROG_LN_S
+AC_C_CONST
+
# Required for per-target flags or subdir-objects with C sources.
AM_PROG_CC_C_O
+# Support building Windows DLLs.
+AC_LIBTOOL_WIN32_DLL
+AM_PROG_CC_STDC
+AM_PROG_LIBTOOL
+
# Set the standard the C library headers should conform to.
AH_VERBATIM([_POSIX_C_SOURCE], [/* The targeted POSIX standard. */
#ifndef _POSIX_C_SOURCE
SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary])
# Python 3 is always needed.
+# Starting with Python 3.8 we need to check for "python-3.8-embed"
+# first, since usually only that variant will add "-lpython3.8".
+# https://docs.python.org/3/whatsnew/3.8.html#debug-build-uses-the-same-abi-as-release-build
SR_PKG_CHECK([python3], [SRD_PKGLIBS],
- [python3 >= 3.2], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2])
+ [python-3.12-embed], [python-3.11-embed],
+ [python-3.10-embed], [python-3.9-embed], [python-3.8-embed], [python3-embed],
+ [python-3.8 >= 3.8], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5],
+ [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2], [python3 >= 3.2])
AS_IF([test "x$sr_have_python3" = xno],
[AC_MSG_ERROR([Cannot find Python 3 development headers.])])
# We also need to find the name of the python3 executable (for 'make install').
# Some OSes call this python3, some call it python3.2, etc. etc.
AC_ARG_VAR([PYTHON3], [Python 3 interpreter])
-AC_CHECK_PROGS([PYTHON3], [python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3])
+AC_CHECK_PROGS([PYTHON3], [python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3])
AS_IF([test "x$PYTHON3" = x],
[AC_MSG_ERROR([Cannot find Python 3 interpreter.])])
AC_C_BIGENDIAN
+#########################
+## Optional features. ##
+#########################
+
+# Enable IRMP support by default. Accept user overrides.
+AC_ARG_ENABLE([irmp],
+ [AS_HELP_STRING([--enable-irmp], [enable IRMP shared object [default=yes]])],
+ [], [enable_irmp_so=yes])
+AM_CONDITIONAL([WITH_IRMP], [test "x$enable_irmp_so" = "xyes"])
+test -n "$enable_irmp_so" || enable_irmp_so=no
+
##############################
## Finalize configuration ##
##############################
# Retrieve the compile and link flags for all modules combined.
# Also, bail out at this point if any module dependency is not met.
PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
+PKG_CHECK_MODULES([LIBIRMP], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS])
srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD`
$srd_pkglibs_summary
Detected libraries (optional):
$srd_pkglibs_opt_summary
+Optional features:
+ - IRMP support library .......... $enable_irmp_so
_EOF
g_free(row);
}
+static void logic_output_channel_free(void *data)
+{
+ struct srd_decoder_logic_output_channel *logic_out_ch = data;
+
+ if (!logic_out_ch)
+ return;
+
+ g_free(logic_out_ch->desc);
+ g_free(logic_out_ch->id);
+ g_free(logic_out_ch);
+}
+
static void decoder_option_free(void *data)
{
struct srd_decoder_option *opt = data;
PyObject *py_channellist, *py_entry;
struct srd_channel *pdch;
GSList *pdchl;
- ssize_t i;
+ ssize_t ch_idx;
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
goto err_out;
}
- for (i = PyTuple_Size(py_channellist) - 1; i >= 0; i--) {
- py_entry = PyTuple_GetItem(py_channellist, i);
+ ch_idx = PyTuple_Size(py_channellist);
+ while (ch_idx--) {
+ py_entry = PyTuple_GetItem(py_channellist, ch_idx);
if (!py_entry)
goto except_out;
if (py_dictitem_as_str(py_entry, "desc", &pdch->desc) != SRD_OK)
goto err_out;
- pdch->order = offset + i;
+ pdch->order = offset + ch_idx;
}
Py_DECREF(py_channellist);
GSList *options;
struct srd_decoder_option *o;
GVariant *gvar;
- ssize_t opt, i;
+ ssize_t opt, val_idx;
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
goto err_out;
}
- for (i = PyTuple_Size(py_values) - 1; i >= 0; i--) {
- py_item = PyTuple_GetItem(py_values, i);
+ val_idx = PyTuple_Size(py_values);
+ while (val_idx--) {
+ py_item = PyTuple_GetItem(py_values, val_idx);
if (!py_item)
goto except_out;
}
/* Convert annotation class attribute to GSList of char **. */
-static int get_annotations(struct srd_decoder *dec)
+static int get_annotations(struct srd_decoder *dec, size_t *ret_count)
{
PyObject *py_annlist, *py_ann;
GSList *annotations;
char **annpair;
- ssize_t i;
+ ssize_t ann_idx;
PyGILState_STATE gstate;
+ if (ret_count)
+ *ret_count = 0;
+
gstate = PyGILState_Ensure();
if (!PyObject_HasAttrString(dec->py_dec, "annotations")) {
goto except_out;
if (!PyTuple_Check(py_annlist)) {
- srd_err("Protocol decoder %s annotations should "
- "be a tuple.", dec->name);
+ srd_err("Protocol decoder %s annotations should be a tuple.",
+ dec->name);
goto err_out;
}
- for (i = PyTuple_Size(py_annlist) - 1; i >= 0; i--) {
- py_ann = PyTuple_GetItem(py_annlist, i);
+ ann_idx = PyTuple_Size(py_annlist);
+ if (ret_count)
+ *ret_count = ann_idx;
+ while (ann_idx--) {
+ py_ann = PyTuple_GetItem(py_annlist, ann_idx);
if (!py_ann)
goto except_out;
if (!PyTuple_Check(py_ann) || PyTuple_Size(py_ann) != 2) {
- srd_err("Protocol decoder %s annotation %zd should "
- "be a tuple with two elements.",
- dec->name, i + 1);
+ srd_err("Protocol decoder %s annotation %zd should be a tuple with two elements.",
+ dec->name, ann_idx + 1);
goto err_out;
}
if (py_strseq_to_char(py_ann, &annpair) != SRD_OK)
}
/* Convert annotation_rows to GSList of 'struct srd_decoder_annotation_row'. */
-static int get_annotation_rows(struct srd_decoder *dec)
+static int get_annotation_rows(struct srd_decoder *dec, size_t cls_count)
{
+ const char *py_member_name = "annotation_rows";
+
PyObject *py_ann_rows, *py_ann_row, *py_ann_classes, *py_item;
GSList *annotation_rows;
struct srd_decoder_annotation_row *ann_row;
- ssize_t i, k;
+ ssize_t row_idx, item_idx;
size_t class_idx;
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
- if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) {
+ if (!PyObject_HasAttrString(dec->py_dec, py_member_name)) {
PyGILState_Release(gstate);
return SRD_OK;
}
annotation_rows = NULL;
- py_ann_rows = PyObject_GetAttrString(dec->py_dec, "annotation_rows");
+ py_ann_rows = PyObject_GetAttrString(dec->py_dec, py_member_name);
if (!py_ann_rows)
goto except_out;
if (!PyTuple_Check(py_ann_rows)) {
- srd_err("Protocol decoder %s annotation_rows "
- "must be a tuple.", dec->name);
+ srd_err("Protocol decoder %s %s must be a tuple.",
+ dec->name, py_member_name);
goto err_out;
}
- for (i = PyTuple_Size(py_ann_rows) - 1; i >= 0; i--) {
- py_ann_row = PyTuple_GetItem(py_ann_rows, i);
+ row_idx = PyTuple_Size(py_ann_rows);
+ while (row_idx--) {
+ py_ann_row = PyTuple_GetItem(py_ann_rows, row_idx);
if (!py_ann_row)
goto except_out;
if (!PyTuple_Check(py_ann_row) || PyTuple_Size(py_ann_row) != 3) {
- srd_err("Protocol decoder %s annotation_rows "
- "must contain only tuples of 3 elements.",
- dec->name);
+ srd_err("Protocol decoder %s %s must contain only tuples of 3 elements.",
+ dec->name, py_member_name);
goto err_out;
}
ann_row = g_malloc0(sizeof(struct srd_decoder_annotation_row));
goto except_out;
if (!PyTuple_Check(py_ann_classes)) {
- srd_err("Protocol decoder %s annotation_rows tuples "
- "must have a tuple of numbers as 3rd element.",
- dec->name);
+ srd_err("Protocol decoder %s %s tuples must have a tuple of numbers as 3rd element.",
+ dec->name, py_member_name);
goto err_out;
}
- for (k = PyTuple_Size(py_ann_classes) - 1; k >= 0; k--) {
- py_item = PyTuple_GetItem(py_ann_classes, k);
+ item_idx = PyTuple_Size(py_ann_classes);
+ while (item_idx--) {
+ py_item = PyTuple_GetItem(py_ann_classes, item_idx);
if (!py_item)
goto except_out;
if (!PyLong_Check(py_item)) {
- srd_err("Protocol decoder %s annotation row "
- "class tuple must only contain numbers.",
+ srd_err("Protocol decoder %s annotation row class tuple must only contain numbers.",
dec->name);
goto err_out;
}
class_idx = PyLong_AsSize_t(py_item);
if (PyErr_Occurred())
goto except_out;
+ if (class_idx >= cls_count) {
+ srd_err("Protocol decoder %s annotation row %zd references invalid class %zu.",
+ dec->name, row_idx, class_idx);
+ goto err_out;
+ }
ann_row->ann_classes = g_slist_prepend(ann_row->ann_classes,
GSIZE_TO_POINTER(class_idx));
PyObject *py_bin_classes, *py_bin_class;
GSList *bin_classes;
char **bin;
- ssize_t i;
+ ssize_t bin_idx;
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
goto err_out;
}
- for (i = PyTuple_Size(py_bin_classes) - 1; i >= 0; i--) {
- py_bin_class = PyTuple_GetItem(py_bin_classes, i);
+ bin_idx = PyTuple_Size(py_bin_classes);
+ while (bin_idx--) {
+ py_bin_class = PyTuple_GetItem(py_bin_classes, bin_idx);
if (!py_bin_class)
goto except_out;
return SRD_ERR_PYTHON;
}
+/* Convert logic_output_channels to GSList of 'struct srd_decoder_logic_output_channel'. */
+static int get_logic_output_channels(struct srd_decoder *dec)
+{
+ PyObject *py_logic_out_chs, *py_logic_out_ch, *py_item;
+ GSList *logic_out_chs;
+ struct srd_decoder_logic_output_channel *logic_out_ch;
+ ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(dec->py_dec, "logic_output_channels")) {
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ logic_out_chs = NULL;
+
+ py_logic_out_chs = PyObject_GetAttrString(dec->py_dec, "logic_output_channels");
+ if (!py_logic_out_chs)
+ goto except_out;
+
+ if (!PyTuple_Check(py_logic_out_chs)) {
+ srd_err("Protocol decoder %s logic_output_channels "
+ "must be a tuple.", dec->name);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_logic_out_chs) - 1; i >= 0; i--) {
+ py_logic_out_ch = PyTuple_GetItem(py_logic_out_chs, i);
+ if (!py_logic_out_ch)
+ goto except_out;
+
+ if (!PyTuple_Check(py_logic_out_ch) || PyTuple_Size(py_logic_out_ch) != 2) {
+ srd_err("Protocol decoder %s logic_output_channels "
+ "must contain only tuples of 2 elements.",
+ dec->name);
+ goto err_out;
+ }
+ logic_out_ch = g_malloc0(sizeof(*logic_out_ch));
+ /* Add to list right away so it doesn't get lost. */
+ logic_out_chs = g_slist_prepend(logic_out_chs, logic_out_ch);
+
+ py_item = PyTuple_GetItem(py_logic_out_ch, 0);
+ if (!py_item)
+ goto except_out;
+ if (py_str_as_str(py_item, &logic_out_ch->id) != SRD_OK)
+ goto err_out;
+
+ py_item = PyTuple_GetItem(py_logic_out_ch, 1);
+ if (!py_item)
+ goto except_out;
+ if (py_str_as_str(py_item, &logic_out_ch->desc) != SRD_OK)
+ goto err_out;
+ }
+ dec->logic_output_channels = logic_out_chs;
+ Py_DECREF(py_logic_out_chs);
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s decoder logic output channels",
+ dec->name);
+
+err_out:
+ g_slist_free_full(logic_out_chs, &logic_output_channel_free);
+ Py_XDECREF(py_logic_out_chs);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
/* Check whether the Decoder class defines the named method. */
static int check_method(PyObject *py_dec, const char *mod_name,
const char *method_name)
return apiver;
}
+static gboolean contains_duplicates(GSList *list)
+{
+ for (GSList *l1 = list; l1; l1 = l1->next) {
+ for (GSList *l2 = l1->next; l2; l2 = l2->next)
+ if (!strcmp(l1->data, l2->data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean contains_duplicate_ids(GSList *list1, GSList *list2)
+{
+ for (GSList *l1 = list1; l1; l1 = l1->next) {
+ unsigned int cnt = 0;
+ const char **s1 = l1->data;
+ for (GSList *l2 = list2; l2; l2 = l2->next) {
+ const char **s2 = l2->data;
+ if (!strcmp(s1[0], s2[0]))
+ cnt++;
+ if ((list1 == list2) && cnt > 1)
+ return TRUE;
+ if ((list1 != list2) && cnt > 0)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean contains_duplicate_row_ids(GSList *list1, GSList *list2)
+{
+ for (GSList *l1 = list1; l1; l1 = l1->next) {
+ unsigned int cnt = 0;
+ const struct srd_decoder_annotation_row *r1 = l1->data;
+ for (GSList *l2 = list2; l2; l2 = l2->next) {
+ const struct srd_decoder_annotation_row *r2 = l2->data;
+ if (!strcmp(r1->id, r2->id))
+ cnt++;
+ if (cnt > 1)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/**
* Load a protocol decoder module into the embedded Python interpreter.
*
int is_subclass;
const char *fail_txt;
PyGILState_STATE gstate;
+ size_t ann_cls_count;
if (!srd_check_init())
return SRD_ERR;
goto err_out;
}
- if (get_annotations(d) != SRD_OK) {
+ if (get_annotations(d, &ann_cls_count) != SRD_OK) {
fail_txt = "cannot get annotations";
goto err_out;
}
- if (get_annotation_rows(d) != SRD_OK) {
+ if (get_annotation_rows(d, ann_cls_count) != SRD_OK) {
fail_txt = "cannot get annotation rows";
goto err_out;
}
goto err_out;
}
+ if (contains_duplicates(d->inputs)) {
+ fail_txt = "duplicate input IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicates(d->outputs)) {
+ fail_txt = "duplicate output IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicates(d->tags)) {
+ fail_txt = "duplicate tags";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->channels, d->channels)) {
+ fail_txt = "duplicate channel IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->opt_channels, d->opt_channels)) {
+ fail_txt = "duplicate optional channel IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->channels, d->opt_channels)) {
+ fail_txt = "channel and optional channel IDs contain duplicates";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->options, d->options)) {
+ fail_txt = "duplicate option IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->annotations, d->annotations)) {
+ fail_txt = "duplicate annotation class IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicate_row_ids(d->annotation_rows, d->annotation_rows)) {
+ fail_txt = "duplicate annotation row IDs";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->annotations, d->annotation_rows)) {
+ fail_txt = "annotation class/row IDs contain duplicates";
+ goto err_out;
+ }
+
+ if (contains_duplicate_ids(d->binary, d->binary)) {
+ fail_txt = "duplicate binary class IDs";
+ goto err_out;
+ }
+
+ if (get_logic_output_channels(d) != SRD_OK) {
+ fail_txt = "cannot get logic output channels";
+ goto err_out;
+ }
+
PyGILState_Release(gstate);
/* Append it to the list of loaded decoders. */
/**
* Return a protocol decoder's docstring.
*
- * @param dec The loaded protocol decoder.
+ * @param dec The loaded protocol decoder. Must not be NULL.
*
* @return A newly allocated buffer containing the protocol decoder's
* documentation. The caller is responsible for free'ing the buffer.
if (!srd_check_init())
return NULL;
- if (!dec)
+ if (!dec || !dec->py_mod)
return NULL;
gstate = PyGILState_Ensure();
# decoded register access).
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
class ChannelError(Exception):
pass
-class Pins:
- (SYNC, BIT_CLK, SDATA_OUT, SDATA_IN, RESET) = range(5)
-
-class Ann:
- (
- BITS_OUT, BITS_IN,
- SLOT_OUT_RAW, SLOT_OUT_TAG, SLOT_OUT_ADDR, SLOT_OUT_DATA,
- SLOT_OUT_03, SLOT_OUT_04, SLOT_OUT_05, SLOT_OUT_06,
- SLOT_OUT_07, SLOT_OUT_08, SLOT_OUT_09, SLOT_OUT_10,
- SLOT_OUT_11, SLOT_OUT_IO,
- SLOT_IN_RAW, SLOT_IN_TAG, SLOT_IN_ADDR, SLOT_IN_DATA,
- SLOT_IN_03, SLOT_IN_04, SLOT_IN_05, SLOT_IN_06,
- SLOT_IN_07, SLOT_IN_08, SLOT_IN_09, SLOT_IN_10,
- SLOT_IN_11, SLOT_IN_IO,
- WARN, ERROR,
- ) = range(32)
- (
- BIN_FRAME_OUT,
- BIN_FRAME_IN,
- BIN_SLOT_RAW_OUT,
- BIN_SLOT_RAW_IN,
- ) = range(4)
+Pin = SrdIntEnum.from_str('Pin', 'SYNC BIT_CLK SDATA_OUT SDATA_IN RESET')
+
+slots = 'TAG ADDR DATA 03 04 05 06 07 08 09 10 11 IO'.split()
+a = 'BITS_OUT BITS_IN SLOT_RAW_OUT SLOT_RAW_IN WARN ERROR'.split() + \
+ ['SLOT_OUT_' + s for s in slots] + ['SLOT_IN_' + s for s in slots]
+Ann = SrdIntEnum.from_list('Ann', a)
+
+Bin = SrdIntEnum.from_str('Bin', 'FRAME_OUT FRAME_IN SLOT_RAW_OUT SLOT_RAW_IN')
class Decoder(srd.Decoder):
api_version = 3
{'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'},
)
annotations = (
- ('bit-out', 'Output bits'),
- ('bit-in', 'Input bits'),
+ ('bit-out', 'Output bit'),
+ ('bit-in', 'Input bit'),
('slot-out-raw', 'Output raw value'),
+ ('slot-in-raw', 'Input raw value'),
+ ('warning', 'Warning'),
+ ('error', 'Error'),
('slot-out-tag', 'Output TAG'),
('slot-out-cmd-addr', 'Output command address'),
('slot-out-cmd-data', 'Output command data'),
('slot-out-10', 'Output slot 10'),
('slot-out-11', 'Output slot 11'),
('slot-out-io-ctrl', 'Output I/O control'),
- ('slot-in-raw', 'Input raw value'),
('slot-in-tag', 'Input TAG'),
('slot-in-sts-addr', 'Input status address'),
('slot-in-sts-data', 'Input status data'),
# CMD ADDR: 'r/w', 'addr', 'unused'
# CMD DATA: 'data', 'unused'
# 3-11: 'data', 'unused', 'double data'
- ('warning', 'Warning'),
- ('error', 'Error'),
)
annotation_rows = (
('bits-out', 'Output bits', (Ann.BITS_OUT,)),
- ('slots-out-raw', 'Output numbers', (Ann.SLOT_OUT_RAW,)),
- ('slots-out', 'Output slots', (
- Ann.SLOT_OUT_TAG, Ann.SLOT_OUT_ADDR, Ann.SLOT_OUT_DATA,
- Ann.SLOT_OUT_03, Ann.SLOT_OUT_04, Ann.SLOT_OUT_05, Ann.SLOT_OUT_06,
- Ann.SLOT_OUT_07, Ann.SLOT_OUT_08, Ann.SLOT_OUT_09, Ann.SLOT_OUT_10,
- Ann.SLOT_OUT_11, Ann.SLOT_OUT_IO,)),
+ ('slots-out-raw', 'Output numbers', (Ann.SLOT_RAW_OUT,)),
+ ('slots-out', 'Output slots', Ann.prefixes('SLOT_OUT_')),
('bits-in', 'Input bits', (Ann.BITS_IN,)),
- ('slots-in-raw', 'Input numbers', (Ann.SLOT_IN_RAW,)),
- ('slots-in', 'Input slots', (
- Ann.SLOT_IN_TAG, Ann.SLOT_IN_ADDR, Ann.SLOT_IN_DATA,
- Ann.SLOT_IN_03, Ann.SLOT_IN_04, Ann.SLOT_IN_05, Ann.SLOT_IN_06,
- Ann.SLOT_IN_07, Ann.SLOT_IN_08, Ann.SLOT_IN_09, Ann.SLOT_IN_10,
- Ann.SLOT_IN_11, Ann.SLOT_IN_IO,)),
+ ('slots-in-raw', 'Input numbers', (Ann.SLOT_RAW_IN,)),
+ ('slots-in', 'Input slots', Ann.prefixes('SLOT_IN_')),
('warnings', 'Warnings', (Ann.WARN,)),
('errors', 'Errors', (Ann.ERROR,)),
)
self.put(ss, es, self.out_binary, [cls, data])
def __init__(self):
- self.out_binary = None
- self.out_ann = None
self.reset()
def reset(self):
}
def start(self):
- if not self.out_binary:
- self.out_binary = self.register(srd.OUTPUT_BINARY)
- if not self.out_ann:
- self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+ self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
def flush_frame_bits(self):
# Flush raw frame bits to binary annotation.
- anncls = Ann.BIN_FRAME_OUT
data = self.frame_bits_out[:]
count = len(data)
data = self.bits_to_bin_ann(data)
- self.putb(0, count, anncls, data)
+ self.putb(0, count, Bin.FRAME_OUT, data)
- anncls = Ann.BIN_FRAME_IN
data = self.frame_bits_in[:]
count = len(data)
data = self.bits_to_bin_ann(data)
- self.putb(0, count, anncls, data)
+ self.putb(0, count, Bin.FRAME_IN, data)
def start_frame(self, ss):
# Mark the start of a frame.
# that happens to be valid. For an improved implementation, it
# either takes user provided specs or more smarts like observing
# register access (if the capture includes it).
- anncls = Ann.BIN_SLOT_RAW_OUT if is_out else Ann.BIN_SLOT_RAW_IN
+ anncls = Bin.SLOT_RAW_OUT if is_out else Bin.SLOT_RAW_IN
data_bin = data >> 4
data_bin &= 0xffff
data_bin = data_bin.to_bytes(2, byteorder = 'big')
slot_es = self.frame_ss_list[have_len]
if slot_data_out is not None:
slot_text = self.int_to_nibble_text(slot_data_out, slot_len)
- self.putx(slot_ss, slot_es, Ann.SLOT_OUT_RAW, [slot_text])
+ self.putx(slot_ss, slot_es, Ann.SLOT_RAW_OUT, [slot_text])
if slot_data_in is not None:
slot_text = self.int_to_nibble_text(slot_data_in, slot_len)
- self.putx(slot_ss, slot_es, Ann.SLOT_IN_RAW, [slot_text])
+ self.putx(slot_ss, slot_es, Ann.SLOT_RAW_IN, [slot_text])
self.handle_slot(slot_idx, slot_data_out, slot_data_in)
def decode(self):
- have_sdo = self.has_channel(Pins.SDATA_OUT)
- have_sdi = self.has_channel(Pins.SDATA_IN)
+ have_sdo = self.has_channel(Pin.SDATA_OUT)
+ have_sdi = self.has_channel(Pin.SDATA_IN)
if not have_sdo and not have_sdi:
raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.')
- have_reset = self.has_channel(Pins.RESET)
+ have_reset = self.has_channel(Pin.RESET)
# Data is sampled at falling CLK edges. Annotations need to span
# the period between rising edges. SYNC rises one cycle _before_
# and advance to the start of a bit time. Then keep getting the
# samples and the end of all subsequent bit times.
prev_sync = [None, None, None]
- pins = self.wait({Pins.BIT_CLK: 'e'})
- if pins[Pins.BIT_CLK] == 0:
- prev_sync[-1] = pins[Pins.SYNC]
- pins = self.wait({Pins.BIT_CLK: 'r'})
+ pins = self.wait({Pin.BIT_CLK: 'e'})
+ if pins[Pin.BIT_CLK] == 0:
+ prev_sync[-1] = pins[Pin.SYNC]
+ pins = self.wait({Pin.BIT_CLK: 'r'})
bit_ss = self.samplenum
while True:
- pins = self.wait({Pins.BIT_CLK: 'f'})
+ pins = self.wait({Pin.BIT_CLK: 'f'})
prev_sync.pop(0)
- prev_sync.append(pins[Pins.SYNC])
- self.wait({Pins.BIT_CLK: 'r'})
+ prev_sync.append(pins[Pin.SYNC])
+ self.wait({Pin.BIT_CLK: 'r'})
if prev_sync[0] == 0 and prev_sync[1] == 1:
self.start_frame(bit_ss)
self.handle_bits(bit_ss, self.samplenum,
- pins[Pins.SDATA_OUT] if have_sdo else None,
- pins[Pins.SDATA_IN] if have_sdi else None)
+ pins[Pin.SDATA_OUT] if have_sdo else None,
+ pins[Pin.SDATA_IN] if have_sdi else None)
bit_ss = self.samplenum
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the
+Analog Devices AD5626 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ad5626'
+ name = 'AD5626'
+ longname = 'Analog Devices AD5626'
+ desc = 'Analog Devices AD5626 12-bit nanoDAC.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Analog/digital']
+ annotations = (
+ ('voltage', 'Voltage'),
+ )
+
+ def __init__(self,):
+ self.reset()
+
+ def reset(self):
+ self.data = 0
+ self.ss = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def decode(self, ss, es, data):
+ ptype = data[0]
+
+ if ptype == 'CS-CHANGE':
+ cs_old, cs_new = data[1:]
+ if cs_old is not None and cs_old == 0 and cs_new == 1:
+ self.data >>= 1
+ self.data /= 1000
+ self.put(self.ss, es, self.out_ann, [0, ['%.3fV' % self.data]])
+ self.data = 0
+ elif cs_old is not None and cs_old == 1 and cs_new == 0:
+ self.ss = ss
+ elif ptype == 'BITS':
+ mosi = data[1]
+ for bit in reversed(mosi):
+ self.data = self.data | bit[0]
+ self.data <<= 1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the
+Analog Devices AD7910/AD7920 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+modes = {
+ 0: ['Normal Mode', 'Normal', 'Norm', 'N'],
+ 1: ['Power Down Mode', 'Power Down', 'PD'],
+ 2: ['Power Up Mode', 'Power Up', 'PU'],
+}
+
+input_voltage_format = ['%.6fV', '%.2fV']
+
+validation = {
+ 'invalid': ['Invalid data', 'Invalid', 'N/A'],
+ 'incomplete': ['Incomplete conversion', 'Incomplete', 'I'],
+ 'complete': ['Complete conversion', 'Complete', 'C'],
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ad79x0'
+ name = 'AD79x0'
+ longname = 'Analog Devices AD79x0'
+ desc = 'Analog Devices AD7910/AD7920 12-bit ADC.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Analog/digital']
+ annotations = (
+ ('mode', 'Mode'),
+ ('voltage', 'Voltage'),
+ ('validation', 'Validation'),
+ )
+ annotation_rows = (
+ ('modes', 'Modes', (0,)),
+ ('voltages', 'Voltages', (1,)),
+ ('data_validation', 'Data validation', (2,)),
+ )
+ options = (
+ {'id': 'vref', 'desc': 'Reference voltage (V)', 'default': 1.5},
+ )
+
+ def __init__(self,):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = 0
+ self.samples_bit = -1
+ self.ss = -1
+ self.start_sample = 0
+ self.previous_state = 0
+ self.data = 0
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def put_validation(self, pos, msg):
+ self.put(pos[0], pos[1], self.out_ann, [2, validation[msg]])
+
+ def put_data(self, pos, input_voltage):
+ ann = []
+ for format in input_voltage_format:
+ ann.append(format % input_voltage)
+ self.put(pos[0], pos[1], self.out_ann, [1, ann])
+
+ def put_mode(self, pos, msg):
+ self.put(pos[0], pos[1], self.out_ann, [0, modes[msg]])
+
+ def decode(self, ss, es, data):
+ ptype = data[0]
+
+ if ptype == 'CS-CHANGE':
+ cs_old, cs_new = data[1:]
+ if cs_old is not None and cs_old == 0 and cs_new == 1:
+ if self.samples_bit == -1:
+ return
+ self.data >>= 1
+ nb_bits = (ss - self.ss) // self.samples_bit
+ if nb_bits >= 10:
+ if self.data == 0xFFF:
+ self.put_mode([self.start_sample, es], 2)
+ self.previous_state = 0
+ self.put_validation([self.start_sample, es], 'invalid')
+ else:
+ self.put_mode([self.start_sample, es], 0)
+ if nb_bits == 16:
+ self.put_validation([self.start_sample, es], 'complete')
+ elif nb_bits < 16:
+ self.put_validation([self.start_sample, es], 'incomplete')
+ vin = (self.data / ((2**12) - 1)) * self.options['vref']
+ self.put_data([self.start_sample, es], vin)
+ elif nb_bits < 10:
+ self.put_mode([self.start_sample, es], 1)
+ self.previous_state = 1
+ self.put_validation([self.start_sample, es], 'invalid')
+
+ self.ss = -1
+ self.samples_bit = -1
+ self.data = 0
+ elif cs_old is not None and cs_old == 1 and cs_new == 0:
+ self.start_sample = ss
+ self.samples_bit = -1
+
+ elif ptype == 'BITS':
+ if data[2] is None:
+ return
+ miso = data[2]
+ if self.samples_bit == -1:
+ self.samples_bit = miso[0][2] - miso[0][1]
+
+ if self.ss == -1:
+ self.ss = ss
+
+ for bit in reversed(miso):
+ self.data = self.data | bit[0]
+ self.data <<= 1
## 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
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
outputs = []
tags = ['Analog/digital', 'IC', 'Sensor']
annotations = (
- ('read', 'Register read commands'),
- ('write', 'Register write commands'),
- ('warning', 'Warnings'),
+ ('read', 'Register read'),
+ ('write', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('read', 'Read', (0,)),
- ('write', 'Write', (1,)),
+ ('reads', 'Reads', (0,)),
+ ('writes', 'Writes', (1,)),
('warnings', 'Warnings', (2,)),
)
##
import sigrokdecode as srd
+from common.srdhelper import bitpack_lsb
def disabled_enabled(v):
return ['Disabled', 'Enabled'][v]
def output_power(v):
- return '%+ddBm' % [-4, -1, 2, 5][v]
+ return '{:+d}dBm'.format([-4, -1, 2, 5][v])
+# Notes on the implementation:
+# - A register's description is an iterable of tuples which contain:
+# The starting bit position, the bit count, the name of a field, and
+# an optional parser which interprets the field's content. Parser are
+# expected to yield a single text string when they exist. Other types
+# of output are passed to Python's .format() routine as is.
+# - Bit fields' width in registers determines the range of indices in
+# table/tuple lookups. Keep the implementation as robust as possible
+# during future maintenance. Avoid Python runtime errors when adjusting
+# the decoder.
regs = {
-# reg: name offset width parser
- 0: [
- ('FRAC', 3, 12, None),
- ('INT', 15, 16, lambda v: 'Not Allowed' if v < 32 else v)
- ],
- 1: [
- ('MOD', 3, 12, None),
- ('Phase', 15, 12, None),
- ('Prescalar', 27, 1, lambda v: ['4/5', '8/9'][v]),
- ('Phase Adjust', 28, 1, lambda v: ['Off', 'On'][v]),
- ],
- 2: [
- ('Counter Reset', 3, 1, disabled_enabled),
- ('Charge Pump Three-State', 4, 1, disabled_enabled),
- ('Power-Down', 5, 1, disabled_enabled),
- ('PD Polarity', 6, 1, lambda v: ['Negative', 'Positive'][v]),
- ('LDP', 7, 1, lambda v: ['10ns', '6ns'][v]),
- ('LDF', 8, 1, lambda v: ['FRAC-N', 'INT-N'][v]),
- ('Charge Pump Current Setting', 9, 4, lambda v: '%0.2fmA @ 5.1kΩ' %
- [0.31, 0.63, 0.94, 1.25, 1.56, 1.88, 2.19, 2.50,
- 2.81, 3.13, 3.44, 3.75, 4.06, 4.38, 4.69, 5.00][v]),
- ('Double Buffer', 13, 1, disabled_enabled),
- ('R Counter', 14, 10, None),
- ('RDIV2', 24, 1, disabled_enabled),
- ('Reference Doubler', 25, 1, disabled_enabled),
- ('MUXOUT', 26, 3, lambda v:
- ['Three-State Output', 'DVdd', 'DGND', 'R Counter Output', 'N Divider Output',
- 'Analog Lock Detect', 'Digital Lock Detect', 'Reserved'][v]),
- ('Low Noise and Low Spur Modes', 29, 2, lambda v:
- ['Low Noise Mode', 'Reserved', 'Reserved', 'Low Spur Mode'][v])
- ],
- 3: [
- ('Clock Divider', 3, 12, None),
- ('Clock Divider Mode', 15, 2, lambda v:
- ['Clock Divider Off', 'Fast Lock Enable', 'Resync Enable', 'Reserved'][v]),
- ('CSR Enable', 18, 1, disabled_enabled),
- ('Charge Cancellation', 21, 1, disabled_enabled),
- ('ABP', 22, 1, lambda v: ['6ns (FRAC-N)', '3ns (INT-N)'][v]),
- ('Band Select Clock Mode', 23, 1, lambda v: ['Low', 'High'][v])
- ],
- 4: [
- ('Output Power', 3, 2, output_power),
- ('Output Enable', 5, 1, disabled_enabled),
- ('AUX Output Power', 6, 2, output_power),
- ('AUX Output Select', 8, 1, lambda v: ['Divided Output', 'Fundamental'][v]),
- ('AUX Output Enable', 9, 1, disabled_enabled),
- ('MTLD', 10, 1, disabled_enabled),
- ('VCO Power-Down', 11, 1, lambda v:
- 'VCO Powered ' + ('Down' if v == 1 else 'Up')),
- ('Band Select Clock Divider', 12, 8, None),
- ('RF Divider Select', 20, 3, lambda v: '÷' + str(2**v)),
- ('Feedback Select', 23, 1, lambda v: ['Divided', 'Fundamental'][v]),
- ],
- 5: [
- ('LD Pin Mode', 22, 2, lambda v:
- ['Low', 'Digital Lock Detect', 'Low', 'High'][v])
- ]
+ # Register description fields:
+ # offset, width, name, parser.
+ 0: (
+ ( 3, 12, 'FRAC'),
+ (15, 16, 'INT',
+ None, lambda v: 'Not Allowed' if v < 23 else None,
+ ),
+ ),
+ 1: (
+ ( 3, 12, 'MOD'),
+ (15, 12, 'Phase'),
+ (27, 1, 'Prescalar', lambda v: ('4/5', '8/9',)[v]),
+ (28, 1, 'Phase Adjust', lambda v: ('Off', 'On',)[v]),
+ ),
+ 2: (
+ ( 3, 1, 'Counter Reset', disabled_enabled),
+ ( 4, 1, 'Charge Pump Three-State', disabled_enabled),
+ ( 5, 1, 'Power-Down', disabled_enabled),
+ ( 6, 1, 'PD Polarity', lambda v: ('Negative', 'Positive',)[v]),
+ ( 7, 1, 'LDP', lambda v: ('10ns', '6ns',)[v]),
+ ( 8, 1, 'LDF', lambda v: ('FRAC-N', 'INT-N',)[v]),
+ ( 9, 4, 'Charge Pump Current Setting',
+ lambda v: '{curr:0.2f}mA @ 5.1kΩ'.format(curr = (
+ 0.31, 0.63, 0.94, 1.25, 1.56, 1.88, 2.19, 2.50,
+ 2.81, 3.13, 3.44, 3.75, 4.06, 4.38, 4.69, 5.00,
+ )[v])),
+ (13, 1, 'Double Buffer', disabled_enabled),
+ (14, 10, 'R Counter'),
+ (24, 1, 'RDIV2', disabled_enabled),
+ (25, 1, 'Reference Doubler', disabled_enabled),
+ (26, 3, 'MUXOUT',
+ lambda v: '{text}'.format(text = (
+ 'Three-State Output', 'DVdd', 'DGND',
+ 'R Counter Output', 'N Divider Output',
+ 'Analog Lock Detect', 'Digital Lock Detect',
+ 'Reserved',
+ )[v])),
+ (29, 2, 'Low Noise and Low Spur Modes',
+ lambda v: '{text}'.format(text = (
+ 'Low Noise Mode', 'Reserved', 'Reserved', 'Low Spur Mode',
+ )[v])),
+ ),
+ 3: (
+ ( 3, 12, 'Clock Divider'),
+ (15, 2, 'Clock Divider Mode',
+ lambda v: '{text}'.format(text = (
+ 'Clock Divider Off', 'Fast Lock Enable',
+ 'Resync Enable', 'Reserved',
+ )[v])),
+ (18, 1, 'CSR Enable', disabled_enabled),
+ (21, 1, 'Charge Cancellation', disabled_enabled),
+ (22, 1, 'ABP', lambda v: ('6ns (FRAC-N)', '3ns (INT-N)',)[v]),
+ (23, 1, 'Band Select Clock Mode', lambda v: ('Low', 'High',)[v]),
+ ),
+ 4: (
+ ( 3, 2, 'Output Power', output_power),
+ ( 5, 1, 'Output Enable', disabled_enabled),
+ ( 6, 2, 'AUX Output Power', output_power),
+ ( 8, 1, 'AUX Output Select',
+ lambda v: ('Divided Output', 'Fundamental',)[v]),
+ ( 9, 1, 'AUX Output Enable', disabled_enabled),
+ (10, 1, 'MTLD', disabled_enabled),
+ (11, 1, 'VCO Power-Down',
+ lambda v: 'VCO Powered {ud}'.format(ud = 'Down' if v else 'Up')),
+ (12, 8, 'Band Select Clock Divider'),
+ (20, 3, 'RF Divider Select', lambda v: '÷{:d}'.format(2 ** v)),
+ (23, 1, 'Feedback Select', lambda v: ('Divided', 'Fundamental',)[v]),
+ ),
+ 5: (
+ (22, 2, 'LD Pin Mode',
+ lambda v: '{text}'.format(text = (
+ 'Low', 'Digital Lock Detect', 'Low', 'High',
+ )[v])),
+ ),
}
-ANN_REG = 0
+( ANN_REG, ANN_WARN, ) = range(2)
class Decoder(srd.Decoder):
api_version = 3
tags = ['Clock/timing', 'IC', 'Wireless/RF']
annotations = (
# Sent from the host to the chip.
- ('register', 'Register written to the device'),
+ ('write', 'Register write'),
+ ('warning', "Warnings"),
)
annotation_rows = (
- ('registers', 'Register writes', (ANN_REG,)),
+ ('writes', 'Register writes', (ANN_REG,)),
+ ('warnings', 'Warnings', (ANN_WARN,)),
)
def __init__(self):
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ def putg(self, ss, es, cls, data):
+ self.put(ss, es, self.out_ann, [ cls, data, ])
+
def decode_bits(self, offset, width):
- return (sum([(1 << i) if self.bits[offset + i][0] else 0 for i in range(width)]),
- (self.bits[offset + width - 1][1], self.bits[offset][2]))
+ '''Extract a bit field. Expects LSB input data.'''
+ bits = self.bits[offset:][:width]
+ ss, es = bits[-1][1], bits[0][2]
+ value = bitpack_lsb(bits, 0)
+ return ( value, ( ss, es, ))
+
+ def decode_field(self, name, offset, width, parser = None, checker = None):
+ '''Interpret a bit field. Emits an annotation.'''
+ # Get the register field's content and position.
+ val, ( ss, es, ) = self.decode_bits(offset, width)
+ # Have the field's content formatted, emit an annotation.
+ formatted = parser(val) if parser else '{}'.format(val)
+ if formatted is not None:
+ text = ['{name}: {val}'.format(name = name, val = formatted)]
+ else:
+ text = ['{name}'.format(name = name)]
+ if text:
+ self.putg(ss, es, ANN_REG, text)
+ # Have the field's content checked, emit an optional warning.
+ warn = checker(val) if checker else None
+ if warn:
+ text = ['{}'.format(warn)]
+ self.putg(ss, es, ANN_WARN, text)
- def decode_field(self, name, offset, width, parser):
- val, pos = self.decode_bits(offset, width)
- self.put(pos[0], pos[1], self.out_ann, [ANN_REG,
- ['%s: %s' % (name, parser(val) if parser else str(val))]])
- return val
+ def decode_word(self, ss, es, bits):
+ '''Interpret a 32bit word after accumulation completes.'''
+ # SPI transfer content must be exactly one 32bit word.
+ count = len(self.bits)
+ if count != 32:
+ text = [
+ 'Frame error: Bit count: want 32, got {}'.format(count),
+ 'Frame error: Bit count',
+ 'Frame error',
+ ]
+ self.putg(ss, es, ANN_WARN, text)
+ return
+ # Holding bits in LSB order during interpretation simplifies
+ # bit field extraction. And annotation emitting routines expect
+ # this reverse order of bits' timestamps.
+ self.bits.reverse()
+ # Determine which register was accessed.
+ reg_addr, ( reg_ss, reg_es, ) = self.decode_bits(0, 3)
+ text = [
+ 'Register: {addr}'.format(addr = reg_addr),
+ 'Reg: {addr}'.format(addr = reg_addr),
+ '[{addr}]'.format(addr = reg_addr),
+ ]
+ self.putg(reg_ss, reg_es, ANN_REG, text)
+ # Interpret the register's content (when parsers are available).
+ field_descs = regs.get(reg_addr, None)
+ if not field_descs:
+ return
+ for field_desc in field_descs:
+ parser = None
+ checker = None
+ if len(field_desc) == 3:
+ start, count, name, = field_desc
+ elif len(field_desc) == 4:
+ start, count, name, parser = field_desc
+ elif len(field_desc) == 5:
+ start, count, name, parser, checker = field_desc
+ else:
+ # Unsupported regs{} syntax, programmer's error.
+ return
+ self.decode_field(name, start, count, parser, checker)
def decode(self, ss, es, data):
+ ptype, _, _ = data
+
+ if ptype == 'TRANSFER':
+ # Process accumulated bits after completion of a transfer.
+ self.decode_word(ss, es, self.bits)
+ self.bits.clear()
- ptype, data1, data2 = data
-
- if ptype == 'CS-CHANGE':
- if data1 == 1:
- if len(self.bits) == 32:
- reg_value, reg_pos = self.decode_bits(0, 3)
- self.put(reg_pos[0], reg_pos[1], self.out_ann, [ANN_REG,
- ['Register: %d' % reg_value, 'Reg: %d' % reg_value,
- '[%d]' % reg_value]])
- if reg_value < len(regs):
- field_descs = regs[reg_value]
- for field_desc in field_descs:
- field = self.decode_field(*field_desc)
- self.bits = []
if ptype == 'BITS':
- self.bits = data1 + self.bits
+ _, mosi_bits, miso_bits = data
+ # Accumulate bits in MSB order as they are seen in SPI frames.
+ msb_bits = mosi_bits.copy()
+ msb_bits.reverse()
+ self.bits.extend(msb_bits)
outputs = []
tags = ['IC', 'PC', 'Sensor']
annotations = (
- ('read', 'Register read commands'),
- ('write', 'Register write commands'),
- ('warning', 'Warnings'),
+ ('read', 'Register read'),
+ ('write', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('read', 'Read', (0,)),
- ('write', 'Write', (1,)),
+ ('reads', 'Reads', (0,)),
+ ('writes', 'Writes', (1,)),
('warnings', 'Warnings', (2,)),
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the
+Analog Devices ADXL345 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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
+##
+
+error_messages = {
+ 'interrupt': ['Interrupt'],
+ 'undesirable': ['Undesirable behavior'],
+ 'dis_single': ['Disable single tap'],
+ 'dis_double': ['Disable double tap'],
+ 'dis_single_double': ['Disable single/double tap'],
+}
+
+rate_code = {
+ 0x00: 0.1,
+ 0x01: 0.2,
+ 0x02: 0.39,
+ 0x03: 0.78,
+ 0x04: 1.56,
+ 0x05: 3.13,
+ 0x06: 6.25,
+ 0x07: 12.5,
+ 0x08: 25,
+ 0x09: 50,
+ 0x0A: 100,
+ 0x0B: 200,
+ 0x0C: 400,
+ 0x0D: 800,
+ 0x0E: 1600,
+ 0x0F: 3200,
+}
+
+fifo_modes = {
+ 0x00: 'Bypass',
+ 0x01: 'FIFO',
+ 0x02: 'Stream',
+ 0x03: 'Trigger',
+}
+
+operations = {
+ 0x00: ['WRITE REG', 'WRITE', 'W'],
+ 0x01: ['READ REG', 'READ', 'R'],
+}
+
+number_bytes = {
+ 0x00: ['SINGLE BYTE', 'SING BYTE', '1 BYTE', '1B'],
+ 0x01: ['MULTIPLE BYTES', 'MULTI BYTES', 'n*BYTES', 'n*B'],
+}
+
+registers = {
+ 0x00: ['DEVID', 'DID', 'ID'],
+ 0x1D: ['THRESH_TAP', 'TH_TAP', 'TH_T'],
+ 0x1E: ['OFSX', 'OFX'],
+ 0x1F: ['OFSY', 'OFY'],
+ 0x20: ['OFSZ', 'OFZ'],
+ 0x21: ['DUR'],
+ 0x22: ['Latent', 'Lat'],
+ 0x23: ['Window', 'Win'],
+ 0x24: ['THRESH_ACT', 'TH_ACT', 'TH_A'],
+ 0x25: ['THRESH_INACT', 'TH_INACT', 'TH_I'],
+ 0x26: ['TIME_INACT', 'TI_INACT', 'TI_I'],
+ 0x27: ['ACT_INACT_CTL', 'ACT_I_CTL', 'A_I_C'],
+ 0x28: ['THRESH_FF', 'TH_FF'],
+ 0x29: ['TIME_FF', 'TI_FF'],
+ 0x2A: ['TAP_AXES', 'TAP_AX', 'TP_AX'],
+ 0x2B: ['ACT_TAP_STATUS', 'ACT_TAP_STAT', 'ACT_TP_ST', 'A_T_S'],
+ 0x2C: ['BW_RATE', 'BW_R'],
+ 0x2D: ['POWER_CTL', 'PW_CTL', 'PW_C'],
+ 0x2E: ['INT_ENABLE', 'INT_EN', 'I_EN'],
+ 0x2F: ['INT_MAP', 'I_M'],
+ 0x30: ['INT_SOURCE', 'INT_SRC', 'I_SRC', 'I_S'],
+ 0x31: ['DATA_FORMAT', 'DATA_FRM', 'D_FRM', 'D_F'],
+ 0x32: ['DATAX0', 'DX0', 'X0'],
+ 0x33: ['DATAX1', 'DX1', 'X1'],
+ 0x34: ['DATAY0', 'DY0', 'Y0'],
+ 0x35: ['DATAY1', 'DY1', 'Y1'],
+ 0x36: ['DATAZ0', 'DZ0', 'Z0'],
+ 0x37: ['DATAZ1', 'DZ1', 'Z1'],
+ 0x38: ['FIFO_CTL', 'FIF_CT', 'F_C'],
+ 0x39: ['FIFO_STATUS', 'FIFO_STAT', 'FIF_ST', 'F_S'],
+}
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+from .lists import *
+
+WORD_SIZE = 8
+
+class Channel():
+ MISO, MOSI = range(2)
+
+class Operation():
+ READ, WRITE = range(2)
+
+class BitType():
+ ENABLE = {1: ['Enable %s', 'En %s', '%s '], 0: ['Disable %s', 'Dis %s', '!%s '],}
+ SOURCE = {1: ['Involve %s', 'Inv %s', '%s'], 0: ['Not involve %s', 'Not inv %s', '!%s'],}
+ INTERRUPT = {1: ['INT2 %s', 'I2: %s '], 0: ['INT1 %s', 'I1:%s '],}
+ AC_DC = {1: ['%s ac', 'ac'], 0: ['%s dc', 'dc'],}
+ UNUSED = {1: ['N/A'], 0: ['N/A'],}
+ OTHER = 0
+
+class Bit():
+ def __init__(self, name, type, values=None):
+ self.value = 0
+ self.name = name
+ self.type = type
+ self.values = values
+
+ def set_value(self, value):
+ self.value = value
+
+ def get_bit_annotation(self):
+ if self.type == BitType.OTHER:
+ annotation = self.values[self.value].copy()
+ else:
+ annotation = self.type[self.value].copy()
+
+ for index in range(len(annotation)):
+ if '%s' in annotation[index]:
+ annotation[index] = str(annotation[index] % self.name)
+ return annotation
+
+Ann = SrdIntEnum.from_str('Ann', 'READ WRITE MB REG_ADDRESS REG_DATA WARNING')
+
+St = SrdIntEnum.from_str('St', 'IDLE ADDRESS_BYTE DATA')
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'adxl345'
+ name = 'ADXL345'
+ longname = 'Analog Devices ADXL345'
+ desc = 'Analog Devices ADXL345 3-axis accelerometer.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Sensor']
+ annotations = (
+ ('read', 'Read'),
+ ('write', 'Write'),
+ ('mb', 'Multiple bytes'),
+ ('reg-address', 'Register address'),
+ ('reg-data', 'Register data'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('reg', 'Registers', (Ann.READ, Ann.WRITE, Ann.MB, Ann.REG_ADDRESS)),
+ ('data', 'Data', (Ann.REG_DATA, Ann.WARNING)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.mosi, self.miso = [], []
+ self.reg = []
+ self.operation = None
+ self.address = 0
+ self.data = -1
+ self.state = St.IDLE
+ self.ss, self.es = -1, -1
+ self.samples_per_bit = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def putb(self, data, index):
+ start = self.ss + (self.samples_per_bit * index)
+ self.put(start, start + self.samples_per_bit, self.out_ann, data)
+
+ def putbs(self, data, start_index, stop_index):
+ start_index = self.reverse_bit_index(start_index, WORD_SIZE)
+ stop_index = self.reverse_bit_index(stop_index, WORD_SIZE)
+ start = self.ss + (self.samples_per_bit * start_index)
+ stop = start + (self.samples_per_bit * (stop_index - start_index + 1))
+ self.put(start, stop, self.out_ann, data)
+
+ def handle_reg_with_scaling_factor(self, data, factor, name, unit, error_msg):
+ if data == 0 and error_msg is not None:
+ self.putx([Ann.WARNING, error_msg])
+ else:
+ result = (data * factor) / 1000
+ self.putx([Ann.REG_DATA, ['%s: %f %s' % (name, result, unit), '%f %s' % (result, unit)]])
+
+ def handle_reg_bit_msg(self, bit, index, en_msg, dis_msg):
+ self.putb([Ann.REG_DATA, [en_msg if bit else dis_msg]], index)
+
+ def interpret_bits(self, data, bits):
+ bits_values = []
+ for offset in range(8):
+ bits_values.insert(0, (data & (1 << offset)) >> offset)
+
+ for index in range(len(bits)):
+ if bits[index] is None:
+ continue
+ bit = bits[index]
+ bit.set_value(bits_values[index])
+ self.putb([Ann.REG_DATA, bit.get_bit_annotation()], index)
+
+ return list(reversed(bits_values))
+
+ def reverse_bit_index(self, index, word_size):
+ return word_size - index - 1
+
+ def get_decimal_number(self, bits, start_index, stop_index):
+ number = 0
+ interval = range(start_index, stop_index + 1, 1)
+ for index, offset in zip(interval, range(len(interval))):
+ bit = bits[index]
+ number = number | (bit << offset)
+ return number
+
+ def get_axis_value(self, data, axis):
+ if self.data != - 1:
+ data <<= 8
+ self.data |= data
+ self.put(self.start_index, self.es, self.out_ann,
+ [Ann.REG_DATA, ['%s: 0x%04X' % (axis, self.data), str(data)]])
+ self.data = -1
+ else:
+ self.putx([Ann.REG_DATA, [str(data)]])
+
+ def handle_reg_0x1d(self, data):
+ self.handle_reg_with_scaling_factor(data, 62.5, 'Threshold', 'g',
+ error_messages['undesirable'])
+
+ def handle_reg_0x1e(self, data):
+ self.handle_reg_with_scaling_factor(data, 15.6, 'OFSX', 'g', None)
+
+ def handle_reg_0x1f(self, data):
+ self.handle_reg_with_scaling_factor(data, 15.6, 'OFSY', 'g', None)
+
+ def handle_reg_0x20(self, data):
+ self.handle_reg_with_scaling_factor(data, 15.6, 'OFSZ', 'g', None)
+
+ def handle_reg_0x21(self, data):
+ self.handle_reg_with_scaling_factor(data, 0.625, 'Duration', 's',
+ error_messages['dis_single_double'])
+
+ def handle_reg_0x22(self, data):
+ self.handle_reg_with_scaling_factor(data, 1.25, 'Latency', 's',
+ error_messages['dis_double'])
+
+ def handle_reg_0x23(self, data):
+ self.handle_reg_with_scaling_factor(data, 1.25, 'Window', 's',
+ error_messages['dis_double'])
+
+ def handle_reg_0x24(self, data):
+ self.handle_reg_0x1d(data)
+
+ def handle_reg_0x25(self, data):
+ self.handle_reg_0x1d(data)
+
+ def handle_reg_0x26(self, data):
+ self.handle_reg_with_scaling_factor(data, 1000, 'Time', 's',
+ error_messages['interrupt'])
+
+ def handle_reg_0x27(self, data):
+ bits = [Bit('ACT', BitType.AC_DC),
+ Bit('ACT_X', BitType.ENABLE),
+ Bit('ACT_Y', BitType.ENABLE),
+ Bit('ACT_Z', BitType.ENABLE),
+ Bit('INACT', BitType.AC_DC),
+ Bit('INACT_X', BitType.ENABLE),
+ Bit('INACT_Y', BitType.ENABLE),
+ Bit('INACT_Z', BitType.ENABLE)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x28(self, data):
+ self.handle_reg_0x1d(data)
+
+ def handle_reg_0x29(self, data):
+ self.handle_reg_with_scaling_factor(data, 5, 'Time', 's',
+ error_messages['undesirable'])
+
+ def handle_reg_0x2a(self, data):
+ bits = [Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.OTHER, {1: ['Suppressed', 'Suppr', 'S'],
+ 0: ['Unsuppressed', 'Unsuppr', 'Uns'],}),
+ Bit('TAP_X', BitType.ENABLE),
+ Bit('TAP_Y', BitType.ENABLE),
+ Bit('TAP_Z', BitType.ENABLE)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x2b(self, data):
+ bits = [Bit('', BitType.UNUSED),
+ Bit('ACT_X', BitType.SOURCE),
+ Bit('ACT_Y', BitType.SOURCE),
+ Bit('ACT_Z', BitType.SOURCE),
+ Bit('', BitType.OTHER, {1: ['Asleep', 'Asl'],
+ 0: ['Not asleep', 'Not asl', '!Asl'],}),
+ Bit('TAP_X', BitType.SOURCE),
+ Bit('TAP_Y', BitType.SOURCE),
+ Bit('TAP_Z', BitType.SOURCE)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x2c(self, data):
+ bits = [Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.OTHER, {1: ['Reduce power', 'Reduce pw', 'Red pw'], 0: ['Normal operation', 'Normal op', 'Norm op'],})]
+ bits_values = self.interpret_bits(data, bits)
+
+ start_index, stop_index = 0, 3
+ rate = self.get_decimal_number(bits_values, start_index, stop_index)
+ self.putbs([Ann.REG_DATA, ['%f' % rate_code[rate]]], stop_index, start_index)
+
+ def handle_reg_0x2d(self, data):
+ bits = [Bit('', BitType.UNUSED),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.OTHER, {1: ['Link'], 0: ['Unlink'], }),
+ Bit('AUTO_SLEEP', BitType.ENABLE),
+ Bit('', BitType.OTHER, {1: ['Measurement mode', 'Measurement', 'Meas'], 0: ['Standby mode', 'Standby'], }),
+ Bit('', BitType.OTHER, {1: ['Sleep mode', 'Sleep', 'Slp'], 0: ['Normal mode', 'Normal', 'Nrm'],})]
+ bits_values = self.interpret_bits(data, bits)
+
+ start_index, stop_index = 0, 1
+ wakeup = self.get_decimal_number(bits_values, start_index, stop_index)
+ frequency = 2 ** (~wakeup & 0x03)
+ self.putbs([Ann.REG_DATA, ['%d Hz' % frequency]], stop_index, start_index)
+
+ def handle_reg_0x2e(self, data):
+ bits = [Bit('DATA_READY', BitType.ENABLE),
+ Bit('SINGLE_TAP', BitType.ENABLE),
+ Bit('DOUBLE_TAP', BitType.ENABLE),
+ Bit('Activity', BitType.ENABLE),
+ Bit('Inactivity', BitType.ENABLE),
+ Bit('FREE_FALL', BitType.ENABLE),
+ Bit('Watermark', BitType.ENABLE),
+ Bit('Overrun', BitType.ENABLE)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x2f(self, data):
+ bits = [Bit('DATA_READY', BitType.INTERRUPT),
+ Bit('SINGLE_TAP', BitType.INTERRUPT),
+ Bit('DOUBLE_TAP', BitType.INTERRUPT),
+ Bit('Activity', BitType.INTERRUPT),
+ Bit('Inactivity', BitType.INTERRUPT),
+ Bit('FREE_FALL', BitType.INTERRUPT),
+ Bit('Watermark', BitType.INTERRUPT),
+ Bit('Overrun', BitType.INTERRUPT)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x30(self, data):
+ bits = [Bit('DATA_READY', BitType.SOURCE),
+ Bit('SINGLE_TAP', BitType.SOURCE),
+ Bit('DOUBLE_TAP', BitType.SOURCE),
+ Bit('Activity', BitType.SOURCE),
+ Bit('Inactivity', BitType.SOURCE),
+ Bit('FREE_FALL', BitType.SOURCE),
+ Bit('Watermark', BitType.SOURCE),
+ Bit('Overrun', BitType.SOURCE)]
+ self.interpret_bits(data, bits)
+
+ def handle_reg_0x31(self, data):
+ bits = [Bit('SELF_TEST', BitType.ENABLE),
+ Bit('', BitType.OTHER, {1: ['3-wire SPI', '3-SPI'], 0: ['4-wire SPI', '4-SPI'],}),
+ Bit('', BitType.OTHER, {1: ['INT ACT LOW', 'INT LOW'], 0: ['INT ACT HIGH', 'INT HIGH'],}),
+ Bit('', BitType.UNUSED),
+ Bit('', BitType.OTHER, {1: ['Full resolution', 'Full res'], 0: ['10-bit mode', '10-bit'],}),
+ Bit('', BitType.OTHER, {1: ['MSB mode', 'MSB'], 0: ['LSB mode', 'LSB'],})]
+ bits_values = self.interpret_bits(data, bits)
+
+ start_index, stop_index = 0, 1
+ range_g = self.get_decimal_number(bits_values, start_index, stop_index)
+ result = 2 ** (range_g + 1)
+ self.putbs([Ann.REG_DATA, ['+/-%d g' % result]], stop_index, start_index)
+
+ def handle_reg_0x32(self, data):
+ self.data = data
+ self.putx([Ann.REG_DATA, [str(data)]])
+
+ def handle_reg_0x33(self, data):
+ self.get_axis_value(data, 'X')
+
+ def handle_reg_0x34(self, data):
+ self.handle_reg_0x32(data)
+
+ def handle_reg_0x35(self, data):
+ self.get_axis_value(data, 'Y')
+
+ def handle_reg_0x36(self, data):
+ self.handle_reg_0x32(data)
+
+ def handle_reg_0x37(self, data):
+ self.get_axis_value(data, 'Z')
+
+ def handle_reg_0x38(self, data):
+ bits = [None,
+ None,
+ Bit('', BitType.OTHER, {1: ['Trig-INT2', 'INT2'], 0: ['Trig-INT1', 'INT1'], })]
+ bits_values = self.interpret_bits(data, bits)
+
+ start_index, stop_index = 6, 7
+ fifo = self.get_decimal_number(bits_values, start_index, stop_index)
+ self.putbs([Ann.REG_DATA, [fifo_modes[fifo]]], stop_index, start_index)
+
+ start_index, stop_index = 0, 4
+ samples = self.get_decimal_number(bits_values, start_index, stop_index)
+ self.putbs([Ann.REG_DATA, ['Samples: %d' % samples, '%d' % samples]], stop_index, start_index)
+
+ def handle_reg_0x39(self, data):
+ bits = [Bit('', BitType.OTHER, {1: ['Triggered', 'Trigg'], 0: ['Not triggered', 'Not trigg'],}),
+ Bit('', BitType.UNUSED)]
+ bits_values = self.interpret_bits(data, bits)
+
+ start_index, stop_index = 0, 5
+ entries = self.get_decimal_number(bits_values, start_index, stop_index)
+ self.putbs([Ann.REG_DATA, ['Entries: %d' % entries, '%d' % entries]], stop_index, start_index)
+
+ def get_bit(self, channel):
+ if (channel == Channel.MOSI and self.mosi is None) or \
+ (channel == Channel.MISO and self.miso is None):
+ raise Exception('No available data')
+
+ mosi_bit, miso_bit = 0, 0
+ if self.miso is not None:
+ if len(self.mosi) < 0:
+ raise Exception('No available data')
+ miso_bit = self.miso.pop(0)
+ if self.miso is not None:
+ if len(self.miso) < 0:
+ raise Exception('No available data')
+ mosi_bit = self.mosi.pop(0)
+
+ if channel == Channel.MOSI:
+ return mosi_bit
+ return miso_bit
+
+ def decode(self, ss, es, data):
+ ptype = data[0]
+
+ if ptype == 'CS-CHANGE':
+ cs_old, cs_new = data[1:]
+ if cs_old is not None and cs_old == 1 and cs_new == 0:
+ self.ss, self.es = ss, es
+ self.state = St.ADDRESS_BYTE
+ else:
+ self.state = St.IDLE
+
+ elif ptype == 'BITS':
+ if data[1] is not None:
+ self.mosi = list(reversed(data[1]))
+ if data[2] is not None:
+ self.miso = list(reversed(data[2]))
+
+ if self.mosi is None and self.miso is None:
+ return
+
+ if self.state == St.ADDRESS_BYTE:
+ # OPERATION BIT
+ op_bit = self.get_bit(Channel.MOSI)
+ self.put(op_bit[1], op_bit[2], self.out_ann,
+ [Ann.READ if op_bit[0] else Ann.WRITE, operations[op_bit[0]]])
+ self.operation = Operation.READ if op_bit[0] else Operation.WRITE
+ # MULTIPLE-BYTE BIT
+ mb_bit = self.get_bit(Channel.MOSI)
+ self.put(mb_bit[1], mb_bit[2], self.out_ann, [Ann.MB, number_bytes[mb_bit[0]]])
+
+ # REGISTER 6-BIT ADDRESS
+ self.address = 0
+ start_sample = self.mosi[0][1]
+ addr_bit = []
+ for i in range(6):
+ addr_bit = self.get_bit(Channel.MOSI)
+ self.address |= addr_bit[0]
+ self.address <<= 1
+ self.address >>= 1
+ self.put(start_sample, addr_bit[2], self.out_ann,
+ [Ann.REG_ADDRESS, ['ADDRESS: 0x%02X' % self.address, 'ADDR: 0x%02X'
+ % self.address, '0x%02X' % self.address]])
+ self.ss = -1
+ self.state = St.DATA
+
+ elif self.state == St.DATA:
+ self.reg.extend(self.mosi if self.operation == Operation.WRITE else self.miso)
+
+ self.mosi, self.miso = [], []
+ if self.ss == -1:
+ self.ss, self.es = self.reg[0][1], es
+ self.samples_per_bit = self.reg[0][2] - self.ss
+
+ if len(self.reg) < 8:
+ return
+ else:
+ reg_value = 0
+ reg_bit = []
+ for offset in range(7, -1, -1):
+ reg_bit = self.reg.pop(0)
+
+ mask = reg_bit[0] << offset
+ reg_value |= mask
+
+ if self.address < 0x00 or self.address > 0x39:
+ return
+
+ if self.address in [0x32, 0x34, 0x36]:
+ self.start_index = self.ss
+
+ if 0x1D > self.address >= 0x00:
+ self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_ADDRESS, [str(self.address)]])
+ self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_DATA, [str(reg_value)]])
+ else:
+ self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_ADDRESS, registers[self.address]])
+ handle_reg = getattr(self, 'handle_reg_0x%02x' % self.address)
+ handle_reg(reg_value)
+
+ self.reg = []
+ self.address += 1
+ self.ss = -1
('bit', 'Bit'),
('end', 'End'),
('byte', 'Byte'),
- ('humidity', 'Relative humidity in percent'),
- ('temperature', 'Temperature in degrees Celsius'),
+ ('humidity', 'Relative humidity'),
+ ('temperature', 'Temperature'),
('checksum', 'Checksum'),
)
annotation_rows = (
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the ASCII protocol
+for Amulet LCD display controllers.
+
+Currently the decoder treats both RX and TX the same way, decoding all
+message types.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from collections import OrderedDict
+
+# OrderedDict which maps command IDs to their names and descriptions.
+cmds = OrderedDict([
+ (0xA0, ('PAGE', 'Jump to page')),
+ (0xD0, ('GBV', 'Get byte variable')),
+ (0xD1, ('GWV', 'Get word variable')),
+ (0xD2, ('GSV', 'Get string variable')),
+ (0xD3, ('GLV', 'Get label variable')),
+ (0xD4, ('GRPC', 'Get RPC buffer')),
+ (0xD5, ('SBV', 'Set byte variable')),
+ (0xD6, ('SWV', 'Set word variable')),
+ (0xD7, ('SSV', 'Set string variable')),
+ (0xD8, ('RPC', 'Invoke RPC')),
+ (0xD9, ('LINE', 'Draw line')),
+ (0xDA, ('RECT', 'Draw rectangle')),
+ (0xDB, ('FRECT', 'Draw filled rectangle')),
+ (0xDC, ('PIXEL', 'Draw pixel')),
+ (0xDD, ('GBVA', 'Get byte variable array')),
+ (0xDE, ('GWVA', 'Get word variable array')),
+ (0xDF, ('SBVA', 'Set byte variable array')),
+ (0xE0, ('GBVR', 'Get byte variable reply')),
+ (0xE1, ('GWVR', 'Get word variable reply')),
+ (0xE2, ('GSVR', 'Get string variable reply')),
+ (0xE3, ('GLVR', 'Get label variable reply')),
+ (0xE4, ('GRPCR', 'Get RPC buffer reply')),
+ (0xE5, ('SBVR', 'Set byte variable reply')),
+ (0xE6, ('SWVR', 'Set word variable reply')),
+ (0xE7, ('SSVR', 'Set string variable reply')),
+ (0xE8, ('RPCR', 'Invoke RPC reply')),
+ (0xE9, ('LINER', 'Draw line reply')),
+ (0xEA, ('RECTR', 'Draw rectangle')),
+ (0xEB, ('FRECTR', 'Draw filled rectangle reply')),
+ (0xEC, ('PIXELR', 'Draw pixel reply')),
+ (0xED, ('GBVAR', 'Get byte variable array reply')),
+ (0xEE, ('GWVAR', 'Get word variable array reply')),
+ (0xEF, ('SBVAR', 'Set byte variable array reply')),
+ (0xF0, ('ACK', 'Acknowledgment')),
+ (0xF1, ('NACK', 'Negative acknowledgment')),
+ (0xF2, ('SWVA', 'Set word variable array')),
+ (0xF3, ('SWVAR', 'Set word variable array reply')),
+ (0xF4, ('GCV', 'Get color variable')),
+ (0xF5, ('GCVR', 'Get color variable reply')),
+ (0xF6, ('SCV', 'Set color variable')),
+ (0xF7, ('SCVR', 'Set color variable reply')),
+])
+
+cmds_with_high_bytes = [
+ 0xA0, # PAGE - Page change
+ 0xD7, # SVV - Set string variable
+ 0xE7, # SVVR - Set string variable reply
+ 0xE2, # GSVR - Get string variable reply
+ 0xE3, # GLVR - Get label variable reply
+]
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from math import ceil
+from common.srdhelper import SrdIntEnum
+from .lists import *
+
+L = len(cmds)
+RX = 0
+TX = 1
+
+Ann = SrdIntEnum.from_list('Ann',
+ [c[0] for c in cmds.values()] + ['BIT', 'FIELD', 'WARN'])
+
+def cmd_annotation_classes():
+ return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'amulet_ascii'
+ name = 'Amulet ASCII'
+ longname = 'Amulet LCD ASCII'
+ desc = 'Amulet Technologies LCD controller ASCII protocol.'
+ license = 'gplv3+'
+ inputs = ['uart']
+ outputs = []
+ tags = ['Display']
+ annotations = cmd_annotation_classes() + (
+ ('bit', 'Bit'),
+ ('field', 'Field'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (L + 0,)),
+ ('fields', 'Fields', (L + 1,)),
+ ('commands', 'Commands', tuple(range(L))),
+ ('warnings', 'Warnings', (L + 2,)),
+ )
+ options = (
+ {'id': 'ms_chan', 'desc': 'Master -> slave channel',
+ 'default': 'RX', 'values': ('RX', 'TX')},
+ {'id': 'sm_chan', 'desc': 'Slave -> master channel',
+ 'default': 'TX', 'values': ('RX', 'TX')},
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = None
+ self.cmdstate = None
+
+ # Build dict mapping command keys to handler functions. Each
+ # command in 'cmds' (defined in lists.py) has a matching
+ # handler self.handle_<shortname>.
+ def get_handler(cmd):
+ s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_')
+ return getattr(self, s)
+ self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys())
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ # Simplification, most annotations span exactly one SPI byte/packet.
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def putf(self, data):
+ self.put(self.ss_field, self.es_field, self.out_ann, data)
+
+ def putc(self, data):
+ self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+ def cmd_ann_list(self):
+ x, s = cmds[self.state][0], cmds[self.state][1]
+ return ['Command: %s (%s)' % (s, x), 'Command: %s' % s,
+ 'Cmd: %s' % s, 'Cmd: %s' % x, x]
+
+ def emit_cmd_byte(self):
+ self.ss_cmd = self.ss
+ self.putx([Ann.FIELD, self.cmd_ann_list()])
+
+ def emit_addr_bytes(self, pdata):
+ if self.cmdstate == 2:
+ self.ss_field = self.ss
+ self.addr = chr(pdata)
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, 'Addr h 0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.es_field = self.es
+ self.addr += chr(pdata)
+ self.addr = int(self.addr, 16)
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, 'Addr l 0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+
+ def emit_cmd_end(self, data):
+ self.es_cmd = self.es
+ self.putc(data)
+ self.state = None
+
+ def handle_read(self, data):
+ if self.cmdstate == 1:
+ self.emit_cmd_byte()
+ self.addr = 0
+ elif self.cmdstate == 2:
+ self.emit_addr_bytes(pdata)
+ elif self.cmdstate == 3:
+ self.emit_addr_bytes(pdata)
+ self.cmdstate += 1
+
+ def handle_set_common(self, pdata):
+ if self.cmdstate == 1:
+ self.addr = 0
+ self.emit_addr_bytes(pdata)
+
+ def emit_not_implemented(self, data):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN, ['Command not decoded', 'Not decoded']])
+ self.emit_cmd_end(data)
+
+ def handle_string(self, pdata, ann_class):
+ # TODO: unicode / string modifiers...
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = ''
+ if pdata == 0x00:
+ # Null terminated string ends.
+ self.es_field = self.es
+ self.putx([Ann.BIT, ['NULL']])
+ self.putf([Ann.FIELD, ['Value: %s' % self.value,
+ 'Val: %s' % self.value, '%s' % self.value]])
+ self.emit_cmd_end([ann_class, self.cmd_ann_list()])
+ return
+ if self.cmdstate > 3:
+ self.value += chr(pdata)
+ self.putx([Ann.BIT, ['%c' % pdata]])
+ self.cmdstate += 1
+
+ # Command handlers
+
+ # Page change 0xA0, 0x02, index_high, index_low, checksum
+ def handle_page(self, pdata):
+ if self.cmdstate == 2:
+ if pdata == 0x02:
+ self.ss_field = self.ss_cmd
+ self.es_field = self.es
+ self.putf([Ann.FIELD, self.cmd_ann_list()])
+ self.checksum = 0xA0 + 0x02
+ else:
+ self.putx([Ann.WARN, ['Illegal second byte for page change',
+ 'Illegal byte']])
+ self.state = None
+ elif self.cmdstate == 3:
+ self.ss_field = self.ss
+ self.checksum += pdata
+ self.page[0] = pdata
+ elif self.cmdstate == 4:
+ self.checksum += pdata
+ self.page[1] = pdata
+ self.es_field = self.es
+ if self.page[0] == self.page [1] == 0xFF:
+ # Soft reset trigger
+ self.putf(Ann.WARN, ['Soft reset', 'Reset'])
+ else:
+ page = chr(self.page[0]) + chr(self.page[1])
+ self.putf(Ann.FIELD, ['Page index: 0x%s' % page,
+ 'Page: 0x%s' % page, '0x%s' % page])
+ elif self.cmdstate == 5:
+ self.checksum += pdata
+ if (self.checksum & 0xFF) != 0:
+ self.putx([Ann.WARN, ['Checksum error', 'Error', 'ERR']])
+ else:
+ self.putx([Ann.FIELD, ['Checksum OK', 'OK']])
+ self.emit_cmd_end(Ann.PAGE)
+ self.cmdstate += 1
+
+ # Value reads: command byte, address high nibble, address low nibble
+
+ # Get byte value
+ def handle_gbv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GBV, self.cmd_ann_list()])
+
+ # Get word value
+ def handle_gwv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GWV, self.cmd_ann_list()])
+
+ # Get string value
+ def handle_gsv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GSV, self.cmd_ann_list()])
+
+ # Get label value
+ def handle_glv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GLV, self.cmd_ann_list()])
+
+ # Get RPC buffer
+ def handle_grpc(self, pdata):
+ if self.cmdstate == 2:
+ self.ss_field = self.ss
+ self.flags = int(chr(pdata), 16) << 4
+ elif self.cmdstate == 3:
+ self.flags += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['RPC flag: 0x%02X' % self.flags]])
+ self.emit_cmd_end([Ann.GRPC, self.cmd_ann_list()])
+
+ # Get byte value array
+ def handle_gbva(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GBVA, self.cmd_ann_list()])
+
+ # Get word value array
+ def handle_gwva(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GWVA, self.cmd_ann_list()])
+
+ # Get color variable
+ def handle_gcv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GCV, self.cmd_ann_list()])
+
+ # Value setters: command byte, address high nibble, address low nibble, data bytes
+
+ # Set byte value data = high nibble, low nibble
+ def handle_sbv(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = chr(pdata)
+ elif self.cmdstate == 5:
+ self.value += chr(pdata)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+ 'Val: 0x%s' % self.value, '0x%s' % self.value]])
+ self.emit_cmd_end([Ann.SBV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # Set word value, msb high, msb low, lsb high, lsb low
+ def handle_swv(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate > 3:
+ nibble = self.cmdstate - 4
+ if nibble == 0:
+ self.ss_field = self.ss
+ self.value = 0
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ 'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+ self.emit_cmd_end([Ann.SWV, self.cmd_ann_list()])
+ return
+ self.cmdstate += 1
+
+ # Set string value, null terminated utf8 strings
+ def handle_ssv(self, pdata):
+ self.handle_string(pdata, Ann.SSV)
+
+ # Set byte value array
+ def handle_sbva(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SBVA, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ # Set word value array
+ def handle_swva(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SWVA, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ # Set color variable
+ def handle_scv(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # RPC trigger
+ def handle_rpc(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.RPC, self.cmd_ann_list()])
+
+ # Drawing
+
+ # Decode pair of (x,y) 16bit coordinates
+ def decode_coords(self, pdata):
+ if self.cmdstate == 1:
+ self.coords[0] = 0
+ self.coords[1] = 0
+ self.coords[2] = 0
+ self.coords[3] = 0
+ if self.cmdstate < 18:
+ # Coordinates
+ nibble = (self.cmdstate - 1) % 4
+ i = (self.cmdstate - 1) / 4
+ self.coords[i] += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ self.ss_field = self.ss
+ elif nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Coordinate 0x%04X' % self.coords[i]],
+ ['0x%04X' % self.coords[i]]])
+
+ # TODO: There are actually two protocol revisions for drawing.
+ # Both use 4 bytes for 16bit x and y pairs for start and end.
+ # The older follows this by a pattern selector and then line weight.
+ # Newer version has 6 bytes for 8bit RGB color...
+
+ # Draw line
+ def handle_line(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.LINE, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw rectange
+ def handle_rect(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.RECT, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw filled rectangle
+ def handle_frect(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.FRECT, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Fill pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw pixel
+ def handle_pixel(self, pdata):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN, ['Draw pixel documentation is missing.', 'Undocumented']])
+ self.state = None
+
+ # Replies
+ def handle_gbvr(self, pdata):
+ self.emit_add_bytes(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = int(chr(pdata), 16) << 4
+ self.putx([Ann.BIT, ['High nibble 0x%s' % pdata, '0x%s' % pdata]])
+ elif self.cmdstate == 5:
+ self.value += int(chr(pdata), 16)
+ self.putx([Ann.BIT, ['Low nibble 0x%s' % pdata, '0x%s' % pdata]])
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.emit_cmd_end([Ann.GBVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_gwvr(self, pdata):
+ self.emit_add_bytes(pdata)
+ if self.cmdstate > 3:
+ nibble = self.cmdstate - 3
+ if nibble == 0:
+ self.value = 0
+ self.ss_field = self.ss
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ if nibble == 3:
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ '0x%04X' % self.value]])
+ self.es_cmd = self.ss
+ self.emit_cmd_end([Ann.GWVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_gsvr(self, pdata):
+ self.handle_string(pdata, Ann.GSVR)
+
+ def handle_glvr(self, pdata):
+ self.handle_string(pdata, Ann.GLVR)
+
+ def handle_grpcr(self, pdata):
+ self.handle_addr(pdata)
+ if self.cmdstate > 3:
+ nibble = (self.cmdstate - 3) % 2
+ if nibble == 0:
+ if pdata == 0x00:
+ self.emit_cmd_end([Ann.GRPCR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ if nibble == 2:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ self.putf([Ann.FIELD, ['0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_sbvr(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = chr(pdata)
+ elif self.cmdstate == 5:
+ self.value += chr(pdata)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+ 'Val: 0x%s' % self.value, '0x%s' % self.value]])
+ self.emit_cmd_end([Ann.SBVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_swvr(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = (pdata - 0x30) << 4
+ elif self.cmdstate == 5:
+ self.value += (pdata - 0x30)
+ self.value = self.value << 8
+ elif self.cmdstate == 6:
+ self.value += (pdata - 0x30) << 4
+ elif self.cmdstate == 7:
+ self.value += (pdata - 0x30)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ 'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+ self.emit_cmd_end([Ann.SWVR, self.cmd_ann_list()])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_ssvr(self, pdata):
+ self.handle_string(pdata, Ann.SSVR)
+
+ def handle_rpcr(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.RPCR, self.cmd_ann_list()])
+
+ def handle_liner(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.LINER, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_rectr(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.RECTR, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_frectr(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.FRECTR, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_pixelr(self, pdata):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN,['Draw pixel documentation is missing.', 'Undocumented']])
+ self.state = None
+
+ def handle_gbvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.GBVAR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_gwvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.GWVAR, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ # Get byte variable array reply
+ def handle_sbvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SBVAR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ # Set word variable array reply
+ def handle_swvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SWVAR, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_gcvr(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_scvr(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # ACK & NACK
+
+ def handle_ack(self, pdata):
+ self.putx([Ann.ACK, self.cmd_ann_list()])
+ self.state = None
+
+ def handle_nack(self, pdata):
+ self.putx([Ann.NACK, self.cmd_ann_list()])
+ self.state = None
+
+ def decode(self, ss, es, data):
+ ptype, rxtx, pdata = data
+
+ self.ss, self.es = ss, es
+
+ if ptype != 'DATA':
+ return
+
+ # Handle commands.
+ try:
+ abort_current = (0xD0 <= pdata[0] <= 0xF7) and \
+ (not (self.state in cmds_with_high_bytes)) and \
+ self.state != None
+ if abort_current:
+ self.putx([Ann.WARN, ['Command aborted by invalid byte', 'Abort']])
+ self.state = pdata[0]
+ self.emit_cmd_byte()
+ self.cmdstate = 1
+ if self.state is None:
+ self.state = pdata[0]
+ self.emit_cmd_byte()
+ self.cmdstate = 1
+ self.cmd_handlers[self.state](pdata[0])
+ except KeyError:
+ self.putx([Ann.WARN, ['Unknown command: 0x%02x' % pdata[0]]])
+ self.state = None
tags = ['Debug/trace']
annotations = (
('trace', 'Trace info'),
- ('branch', 'Branches'),
- ('exception', 'Exceptions'),
+ ('branch', 'Branch'),
+ ('exception', 'Exception'),
('execution', 'Instruction execution'),
('data', 'Data access'),
('pc', 'Program counter'),
- ('instr_e', 'Executed instructions'),
- ('instr_n', 'Not executed instructions'),
+ ('instr_e', 'Executed instruction'),
+ ('instr_n', 'Not executed instruction'),
('source', 'Source code'),
('location', 'Current location'),
('function', 'Current function'),
)
annotation_rows = (
- ('trace', 'Trace info', (0,)),
+ ('traces', 'Trace info', (0,)),
('flow', 'Code flow', (1, 2, 3,)),
- ('data', 'Data access', (4,)),
- ('pc', 'Program counter', (5,)),
- ('instruction', 'Instructions', (6, 7,)),
- ('source', 'Source code', (8,)),
- ('location', 'Current location', (9,)),
- ('function', 'Current function', (10,)),
+ ('data-vals', 'Data access', (4,)),
+ ('pc-vals', 'Program counters', (5,)),
+ ('instructions', 'Instructions', (6, 7,)),
+ ('sources', 'Source code', (8,)),
+ ('locations', 'Current locations', (9,)),
+ ('functions', 'Current functions', (10,)),
)
options = (
{'id': 'objdump', 'desc': 'objdump path',
'default': ''},
)
annotations = (
- ('trace', 'Trace information'),
+ ('trace', 'Trace info'),
('timestamp', 'Timestamp'),
('software', 'Software message'),
('dwt_event', 'DWT event'),
('function', 'Current function'),
)
annotation_rows = (
- ('trace', 'Trace information', (0, 1)),
- ('software', 'Software trace', (2,)),
- ('dwt_event', 'DWT event', (3,)),
- ('dwt_watchpoint', 'DWT watchpoint', (4,)),
- ('dwt_exc', 'Exception trace', (5,)),
- ('dwt_pc', 'Program counter', (6,)),
- ('mode', 'Current mode', (7, 8, 9)),
- ('location', 'Current location', (10,)),
- ('function', 'Current function', (11,)),
+ ('traces', 'Trace info', (0, 1)),
+ ('softwares', 'Software traces', (2,)),
+ ('dwt_events', 'DWT events', (3,)),
+ ('dwt_watchpoints', 'DWT watchpoints', (4,)),
+ ('dwt_excs', 'Exception traces', (5,)),
+ ('dwt_pcs', 'Program counters', (6,)),
+ ('modes', 'Current modes', (7, 8, 9)),
+ ('locations', 'Current locations', (10,)),
+ ('functions', 'Current functions', (11,)),
)
def __init__(self):
('data', 'Stream data'),
)
annotation_rows = (
- ('stream', 'Current stream', (0,)),
- ('data', 'Stream data', (1,)),
+ ('streams', 'Current streams', (0,)),
+ ('data-vals', 'Stream data', (1,)),
)
def __init__(self):
('warning', 'Warning'),
)
annotation_rows = (
- ('frame', 'Frame', (0, 1, 2, 3, 4, 5, 6)),
- ('status', 'Status', (7,)),
+ ('frame', 'Frames', (0, 1, 2, 3, 4, 5, 6)),
+ ('status-vals', 'Status', (7,)),
('warnings', 'Warnings', (8,)),
)
# Vendor code
vendor_code = {
- 0x1e: 'Atmel',
+ 0x1E: 'Atmel',
0x00: 'Device locked',
}
# (Part family + flash size, part number)
part = {
(0x90, 0x01): 'AT90S1200',
+ (0x90, 0x05): 'ATtiny12',
+ (0x90, 0x06): 'ATtiny15',
+ (0x90, 0x07): 'ATtiny13',
(0x91, 0x01): 'AT90S2313',
+ (0x91, 0x02): 'AT90S2323',
+ (0x91, 0x03): 'AT90S2343',
+ (0x91, 0x05): 'AT90S2333',
+ (0x91, 0x06): 'ATtiny22',
+ (0x91, 0x07): 'ATtiny28',
+ (0x91, 0x08): 'ATtiny25',
+ (0x91, 0x09): 'ATtiny26',
+ (0x91, 0x0A): 'ATtiny2313',
+ (0x91, 0x0B): 'ATtiny24',
+ (0x91, 0x0C): 'ATtiny261',
(0x92, 0x01): 'AT90S4414',
- (0x92, 0x05): 'ATmega48', # 4kB flash
+ (0x92, 0x03): 'AT90S4433',
+ (0x92, 0x05): 'ATmega48(A)',
+ (0x92, 0x06): 'ATtiny45',
+ (0x92, 0x08): 'ATtiny461',
+ (0x92, 0x09): 'ATtiny48',
+ (0x92, 0x0A): 'ATmega48PA',
+ (0x92, 0x0D): 'ATtiny4313',
+ (0x92, 0x10): 'ATmega48PB',
(0x93, 0x01): 'AT90S8515',
- (0x93, 0x0a): 'ATmega88', # 8kB flash
- (0x94, 0x06): 'ATmega168', # 16kB flash
- (0xff, 0xff): 'Device code erased, or target missing',
+ (0x93, 0x03): 'AT90S8535',
+ (0x93, 0x07): 'ATmega8',
+ (0x93, 0x0A): 'ATmega88(A)',
+ (0x93, 0x0B): 'ATtiny85',
+ (0x93, 0x0D): 'ATtiny861',
+ (0x93, 0x0F): 'ATmega88PA',
+ (0x93, 0x11): 'ATtiny88',
+ (0x93, 0x16): 'ATmega88PB',
+ (0x93, 0x89): 'ATmega8U2',
+ (0x94, 0x01): 'ATmega161',
+ (0x94, 0x02): 'ATmega163',
+ (0x94, 0x03): 'ATmega16',
+ (0x94, 0x04): 'ATmega162',
+ (0x94, 0x06): 'ATmega168(A)',
+ (0x94, 0x0A): 'ATmega164PA',
+ (0x94, 0x0B): 'ATmega168PA',
+ (0x94, 0x0F): 'ATmega164A',
+ (0x94, 0x12): 'ATtiny1634',
+ (0x94, 0x15): 'ATmega168PB',
+ (0x94, 0x88): 'ATmega16U4',
+ (0x94, 0x89): 'ATmega16U2',
+ (0x95, 0x01): 'ATmega32',
+ (0x95, 0x01): 'ATmega323',
+ (0x95, 0x0F): 'ATmega328P',
+ (0x95, 0x11): 'ATmega324PA',
+ (0x95, 0x14): 'ATmega328',
+ (0x95, 0x15): 'ATmega324A',
+ (0x95, 0x87): 'ATmega32U4',
+ (0x95, 0x8A): 'ATmega32U2',
+ (0x96, 0x08): 'ATmega640',
+ (0x96, 0x09): 'ATmega644(A)',
+ (0x96, 0x0A): 'ATmega644PA',
+ (0x97, 0x01): 'ATmega103',
+ (0x97, 0x03): 'ATmega1280',
+ (0x97, 0x04): 'ATmega1281',
+ (0x97, 0x05): 'ATmega1284P',
+ (0x97, 0x06): 'ATmega1284',
+ (0x98, 0x01): 'ATmega2560',
+ (0x98, 0x02): 'ATmega2561',
+ (0xFF, 0xFF): 'Device code erased, or target missing',
(0x01, 0x02): 'Device locked',
- # TODO: Lots more entries.
}
import sigrokdecode as srd
from .parts import *
+class Ann:
+ PE, RSB0, RSB1, RSB2, CE, RFB, RHFB, REFB, \
+ RLB, REEM, RP, LPMP, WP, WARN, DEV, = range(15)
+
VENDOR_CODE_ATMEL = 0x1e
class Decoder(srd.Decoder):
('rfb', 'Read fuse bits'),
('rhfb', 'Read high fuse bits'),
('refb', 'Read extended fuse bits'),
- ('warnings', 'Warnings'),
+ ('rlb', 'Read lock bits'),
+ ('reem', 'Read EEPROM memory'),
+ ('rp', 'Read program memory'),
+ ('lpmp' , 'Load program memory page'),
+ ('wp', 'Write program memory'),
+ ('warning', 'Warning'),
('dev', 'Device'),
)
annotation_rows = (
- ('bits', 'Bits', ()),
- ('commands', 'Commands', tuple(range(7 + 1))),
- ('warnings', 'Warnings', (8,)),
- ('dev', 'Device', (9,)),
+ ('commands', 'Commands', (Ann.PE, Ann.RSB0, Ann.RSB1, Ann.RSB2,
+ Ann.CE, Ann.RFB, Ann.RHFB, Ann.REFB,
+ Ann.RLB, Ann.REEM, Ann.RP, Ann.LPMP, Ann.WP,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ ('devs', 'Devices', (Ann.DEV,)),
)
def __init__(self):
def handle_cmd_programming_enable(self, cmd, ret):
# Programming enable.
# Note: The chip doesn't send any ACK for 'Programming enable'.
- self.putx([0, ['Programming enable']])
+ self.putx([Ann.PE, ['Programming enable']])
# Sanity check on reply.
if ret[1:4] != [0xac, 0x53, cmd[2]]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_signature_byte_0x00(self, cmd, ret):
# Signature byte 0x00: vendor code.
self.vendor_code = ret[3]
v = vendor_code[self.vendor_code]
- self.putx([1, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
+ self.putx([Ann.RSB0, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
# Store for later.
self.xx = cmd[1] # Same as ret[2].
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != cmd[1]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
# Sanity check for the vendor code.
if self.vendor_code != VENDOR_CODE_ATMEL:
- self.putx([8, ['Warning: Vendor code was not 0x1e (Atmel)!']])
+ self.putx([Ann.WARN, ['Warning: Vendor code was not 0x1e (Atmel)!']])
def handle_cmd_read_signature_byte_0x01(self, cmd, ret):
# Signature byte 0x01: part family and memory size.
self.part_fam_flash_size = ret[3]
- self.putx([2, ['Part family / memory size: 0x%02x' % ret[3]]])
+ self.putx([Ann.RSB1, ['Part family / memory size: 0x%02x' % ret[3]]])
# Store for later.
self.mm = cmd[3]
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != cmd[1] or ret[0] != self.yy:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_signature_byte_0x02(self, cmd, ret):
# Signature byte 0x02: part number.
self.part_number = ret[3]
- self.putx([3, ['Part number: 0x%02x' % ret[3]]])
+ self.putx([Ann.RSB2, ['Part number: 0x%02x' % ret[3]]])
- p = part[(self.part_fam_flash_size, self.part_number)]
- data = [9, ['Device: Atmel %s' % p]]
- self.put(self.ss_device, self.es_cmd, self.out_ann, data)
+ # Part name if known
+ key = (self.part_fam_flash_size, self.part_number)
+ if key in part:
+ p = part[key]
+ data = [Ann.DEV, ['Device: Atmel %s' % p]]
+ self.put(self.ss_device, self.es_cmd, self.out_ann, data)
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != self.xx or ret[0] != self.mm:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
self.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
# Chip erase (erases both flash an EEPROM).
# Upon successful chip erase, the lock bits will also be erased.
# The only way to end a Chip Erase cycle is to release RESET#.
- self.putx([4, ['Chip erase']])
+ self.putx([Ann.CE, ['Chip erase']])
# TODO: Check/handle RESET#.
# Sanity check on reply.
bit = (ret[2] & (1 << 7)) >> 7
if ret[1] != 0xac or bit != 1 or ret[3] != cmd[2]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_fuse_bits(self, cmd, ret):
# Read fuse bits.
- self.putx([5, ['Read fuse bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.RFB, ['Read fuse bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
def handle_cmd_read_fuse_high_bits(self, cmd, ret):
# Read fuse high bits.
- self.putx([6, ['Read fuse high bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.RHFB, ['Read fuse high bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
def handle_cmd_read_extended_fuse_bits(self, cmd, ret):
# Read extended fuse bits.
- self.putx([7, ['Read extended fuse bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.REFB, ['Read extended fuse bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
+ def handle_cmd_read_lock_bits(self, cmd, ret):
+ # Read lock bits
+ self.putx([Ann.RLB, ['Read lock bits: 0x%02x' % ret[3]]])
+
+ def handle_cmd_read_eeprom_memory(self, cmd, ret):
+ # Read EEPROM Memory
+ _addr = ((cmd[1] & 1) << 8) + cmd[2]
+ self.putx([Ann.REEM, ['Read EEPROM Memory: [0x%03x]: 0x%02x' % (_addr, ret[3])]])
+
+ def handle_cmd_read_program_memory(self, cmd, ret):
+ # Read Program Memory
+ _HL = 'Low'
+ _H = 'L'
+ if cmd[0] & 0x08:
+ _HL = 'High'
+ _H = 'H'
+ _addr = ((cmd[1] & 0x0f) << 8) + cmd[2]
+ self.putx([Ann.RP, [
+ 'Read program memory %s: [0x%03x]: 0x%02x' % (_HL, _addr, ret[3]),
+ '[%03x%s]:%02x' % (_addr, _H, ret[3]),
+ '%02x' % ret[3]
+ ]])
+
+ def handle_cmd_load_program_memory_page(self, cmd, ret):
+ # Load Program Memory Page
+ _HL = 'Low'
+ _H = 'L'
+ if cmd[0] & 0x08:
+ _HL = 'High'
+ _H = 'H'
+ _addr = cmd[2] & 0x1F
+ self.putx([Ann.LPMP, [
+ 'Load program memory page %s: [0x%03x]: 0x%02x' % (_HL, _addr, cmd[3]),
+ '[%03x%s]=%02x' % (_addr, _H, cmd[3]),
+ '%02x' % cmd[3]
+ ]])
+
+ def handle_cmd_write_program_memory_page(self, cmd, ret):
+ # Write Program Memory Page
+ _addr = ((cmd[1] & 0x0F) << 3) + (cmd[2] << 5)
+ self.putx([Ann.WP, ['Write program memory page: 0x%02x' % _addr]])
+
def handle_command(self, cmd, ret):
if cmd[:2] == [0xac, 0x53]:
self.handle_cmd_programming_enable(cmd, ret)
self.handle_cmd_read_signature_byte_0x01(cmd, ret)
elif cmd[0] == 0x30 and cmd[2] == 0x02:
self.handle_cmd_read_signature_byte_0x02(cmd, ret)
+ elif cmd[:2] == [0x58, 0x00]:
+ self.handle_cmd_read_lock_bits(cmd,ret)
+ elif cmd[0] == 0xa0 and (cmd[1] & (3 << 6)) == (0 << 6):
+ self.handle_cmd_read_eeprom_memory(cmd, ret)
+ elif (cmd[0] == 0x20 or cmd[0] == 0x28) and ((cmd[1] & 0xf0) == 0x00):
+ self.handle_cmd_read_program_memory(cmd, ret)
+ elif (cmd[0] == 0x40 or cmd[0] == 0x48) and ((cmd[1] & 0xf0) == 0x00):
+ self.handle_cmd_load_program_memory_page(cmd, ret)
+ elif (cmd[0] == 0x4C and ((cmd[1] & 0xf0) == 0x00)):
+ self.handle_cmd_write_program_memory_page(cmd, ret)
else:
c = '%02x %02x %02x %02x' % tuple(cmd)
r = '%02x %02x %02x %02x' % tuple(ret)
- self.putx([0, ['Unknown command: %s (reply: %s)!' % (c, r)]])
+ self.putx([Ann.WARN, ['Unknown command: %s (reply: %s)!' % (c, r)]])
def decode(self, ss, es, data):
ptype, mosi, miso = data
Ann.PARITY_ERR, Ann.STOP_OK, Ann.STOP_ERR, Ann.BREAK)),
('pdi_fields', 'PDI fields', (Ann.OPCODE, Ann.DATA_PROG, Ann.DATA_DEV,
Ann.PDI_BREAK)),
- ('pdi_cmds', 'PDI Cmds', (Ann.ENABLE, Ann.DISABLE, Ann.COMMAND)),
+ ('pdi_cmds', 'PDI commands', (Ann.ENABLE, Ann.DISABLE, Ann.COMMAND)),
)
binary = (
('bytes', 'PDI protocol bytes'),
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder interprets the digital output of cheap generic calipers
+(usually made in China), and shows the measured value in millimeters
+or inches.
+
+Notice that these devices often communicate on voltage levels below
+3.3V and may require additional circuitry to capture the signal.
+
+This decoder does not work for calipers using the Digimatic protocol
+(eg. Mitutoyo and similar brands).
+
+For more information see:
+http://www.shumatech.com/support/chinese_scales.htm
+https://www.instructables.com/id/Reading-Digital-Callipers-with-an-Arduino-USB/
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+
+# Millimeters per inch.
+mm_per_inch = 25.4
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'caliper'
+ name = 'Caliper'
+ longname = 'Digital calipers'
+ desc = 'Protocol of cheap generic digital calipers.'
+ license = 'mit'
+ inputs = ['logic']
+ outputs = []
+ channels = (
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
+ {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
+ )
+ options = (
+ {'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
+ 'default': 10},
+ {'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
+ 'values': ('keep', 'mm', 'inch')},
+ {'id': 'changes', 'desc': 'Changes only', 'default': 'no',
+ 'values': ('no', 'yes')},
+ )
+ tags = ['Analog/digital', 'Sensor']
+ annotations = (
+ ('measurement', 'Measurement'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('measurements', 'Measurements', (0,)),
+ ('warnings', 'Warnings', (1,)),
+ )
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.ss, self.es = 0, 0
+ self.number_bits = []
+ self.flags_bits = []
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putg(self, ss, es, cls, data):
+ self.put(ss, es, self.out_ann, [cls, data])
+
+ def decode(self):
+ last_sent = None
+ timeout_ms = self.options['timeout_ms']
+ want_unit = self.options['unit']
+ show_all = self.options['changes'] == 'no'
+ wait_cond = [{0: 'r'}]
+ if timeout_ms:
+ snum_per_ms = self.samplerate / 1000
+ timeout_snum = timeout_ms * snum_per_ms
+ wait_cond.append({'skip': round(timeout_snum)})
+ while True:
+ # Sample data at the rising clock edge. Optionally timeout
+ # after inactivity for a user specified period. Present the
+ # number of unprocessed bits to the user for diagnostics.
+ clk, data = self.wait(wait_cond)
+ if timeout_ms and not self.matched[0]:
+ if self.number_bits or self.flags_bits:
+ count = len(self.number_bits) + len(self.flags_bits)
+ self.putg(self.ss, self.samplenum, 1, [
+ 'timeout with {} bits in buffer'.format(count),
+ 'timeout ({} bits)'.format(count),
+ 'timeout',
+ ])
+ self.reset()
+ continue
+
+ # Store position of first bit and last activity.
+ # Shift in measured number and flag bits.
+ if not self.ss:
+ self.ss = self.samplenum
+ self.es = self.samplenum
+ if len(self.number_bits) < 16:
+ self.number_bits.append(data)
+ continue
+ if len(self.flags_bits) < 8:
+ self.flags_bits.append(data)
+ if len(self.flags_bits) < 8:
+ continue
+
+ # Get raw values from received data bits. Run the number
+ # conversion, controlled by flags and/or user specs.
+ negative = bool(self.flags_bits[4])
+ is_inch = bool(self.flags_bits[7])
+ number = bitpack(self.number_bits)
+ if negative:
+ number = -number
+ if is_inch:
+ number /= 2000
+ if want_unit == 'mm':
+ number *= mm_per_inch
+ is_inch = False
+ else:
+ number /= 100
+ if want_unit == 'inch':
+ number = round(number / mm_per_inch, 4)
+ is_inch = True
+ unit = 'in' if is_inch else 'mm'
+
+ # Construct and emit an annotation.
+ if show_all or (number, unit) != last_sent:
+ self.putg(self.ss, self.es, 0, [
+ '{number}{unit}'.format(**locals()),
+ '{number}'.format(**locals()),
+ ])
+ last_sent = (number, unit)
+
+ # Reset internal state for the start of the next packet.
+ self.reset()
This decoder assumes that a single CAN_RX line is sampled (e.g. on
the digital output side of a CAN transceiver IC such as the Microchip
MCP-2515DM-BM).
+
+It also has support for CAN-FD.
'''
from .pd import Decoder
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
+from common.srdhelper import bitpack_msb
import sigrokdecode as srd
class SamplerateError(Exception):
pass
+def dlc2len(dlc):
+ return [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64][dlc]
+
class Decoder(srd.Decoder):
api_version = 3
id = 'can'
desc = 'Field bus protocol for distributed realtime control.'
license = 'gplv2+'
inputs = ['logic']
- outputs = []
+ outputs = ['can']
tags = ['Automotive']
channels = (
{'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
{'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0},
)
annotations = (
- ('data', 'CAN payload data'),
+ ('data', 'Payload data'),
('sof', 'Start of frame'),
('eof', 'End of frame'),
('id', 'Identifier'),
('ack-slot', 'ACK slot'),
('ack-delimiter', 'ACK delimiter'),
('stuff-bit', 'Stuff bit'),
- ('warnings', 'Human-readable warnings'),
+ ('warning', 'Warning'),
('bit', 'Bit'),
)
annotation_rows = (
def __init__(self):
self.reset()
- def dlc2len(self, dlc):
- return [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64][dlc]
-
def reset(self):
self.samplerate = None
self.reset_variables()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def set_bit_rate(self, bitrate):
+ self.bit_width = float(self.samplerate) / float(bitrate)
+ self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
+
+ def set_nominal_bitrate(self):
+ self.set_bit_rate(self.options['nominal_bitrate'])
+
+ def set_fast_bitrate(self):
+ self.set_bit_rate(self.options['fast_bitrate'])
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
def putb(self, data):
self.putg(self.ss_block, self.samplenum, data)
+ def putpy(self, data):
+ self.put(self.ss_packet, self.es_packet, self.out_python, data)
+
def reset_variables(self):
self.state = 'IDLE'
self.sof = self.frame_type = self.dlc = None
self.ss_bit12 = None
self.ss_bit32 = None
self.ss_databytebits = []
+ self.frame_bytes = []
+ self.rtr_type = None
self.fd = False
self.rtr = None
self.dom_edge_snum = self.samplenum
self.dom_edge_bcount = self.curbit
- def bit_sampled(self):
- # EMPTY
- pass
-
# Determine the position of the next desired bit's sample point.
def get_sample_point(self, bitnum):
samplenum = self.dom_edge_snum
- samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
- samplenum += int(self.sample_point)
- return samplenum
+ samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+ samplenum += self.sample_point
+ return int(samplenum)
def is_stuff_bit(self):
# CAN uses NRZ encoding and bit stuffing.
if bitnum == (self.last_databit + 1):
self.ss_block = self.samplenum
if self.fd:
- if self.dlc2len(self.dlc) < 16:
+ if dlc2len(self.dlc) < 16:
self.crc_len = 27 # 17 + SBC + stuff bits
else:
self.crc_len = 32 # 21 + SBC + stuff bits
# CRC sequence (15 bits, 17 bits or 21 bits)
elif bitnum == (self.last_databit + self.crc_len):
if self.fd:
- if self.dlc2len(self.dlc) < 16:
+ if dlc2len(self.dlc) < 16:
crc_type = "CRC-17"
else:
crc_type = "CRC-21"
else:
- crc_type = "CRC" # TODO: CRC-15 (will break existing tests)
+ crc_type = "CRC-15"
x = self.last_databit + 1
crc_bits = self.bits[x:x + self.crc_len + 1]
- self.crc = int(''.join(str(d) for d in crc_bits), 2)
+ self.crc = bitpack_msb(crc_bits)
self.putb([11, ['%s sequence: 0x%04x' % (crc_type, self.crc),
'%s: 0x%04x' % (crc_type, self.crc), '%s' % crc_type]])
if not self.is_valid_crc(crc_bits):
if can_rx != 1:
self.putx([16, ['CRC delimiter must be a recessive bit']])
+ if self.fd:
+ self.set_nominal_bitrate()
+
# ACK slot bit (dominant: ACK, recessive: NACK)
elif bitnum == (self.last_databit + self.crc_len + 2):
ack = 'ACK' if can_rx == 0 else 'NACK'
self.putb([2, ['End of frame', 'EOF', 'E']])
if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]:
self.putb([16, ['End of frame (EOF) must be 7 recessive bits']])
+ self.es_packet = self.samplenum
+ py_data = tuple([self.frame_type, self.fullid, self.rtr_type,
+ self.dlc, self.frame_bytes])
+ self.putpy(py_data)
self.reset_variables()
return True
rtr = 'remote' if self.bits[12] == 1 else 'data'
self.put12([8, ['Remote transmission request: %s frame' % rtr,
'RTR: %s frame' % rtr, 'RTR']])
+ self.rtr_type = rtr
self.dlc_start = 15
if bitnum == 15 and self.fd:
# Bits 15-18: Data length code (DLC), in number of bytes (0-8).
elif bitnum == self.dlc_start + 3:
- self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
+ self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
self.putb([10, ['Data length code: %d' % self.dlc,
'DLC: %d' % self.dlc, 'DLC']])
- self.last_databit = self.dlc_start + 3 + (self.dlc2len(self.dlc) * 8)
+ self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
if self.dlc > 8 and not self.fd:
self.putb([16, ['Data length code (DLC) > 8 is not allowed']])
# The bits within a data byte are transferred MSB-first.
elif bitnum == self.last_databit:
self.ss_databytebits.append(self.samplenum) # Last databyte bit.
- for i in range(self.dlc2len(self.dlc)):
+ for i in range(dlc2len(self.dlc)):
x = self.dlc_start + 4 + (8 * i)
- b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ b = bitpack_msb(self.bits[x:x + 8])
+ self.frame_bytes.append(b)
ss = self.ss_databytebits[i * 8]
es = self.ss_databytebits[((i + 1) * 8) - 1]
self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
# Bits 14-31: Extended identifier (EID[17..0])
elif bitnum == 31:
- self.eid = int(''.join(str(d) for d in self.bits[14:]), 2)
+ self.eid = bitpack_msb(self.bits[14:])
s = '%d (0x%x)' % (self.eid, self.eid)
self.putb([4, ['Extended Identifier: %s' % s,
'Extended ID: %s' % s, 'Extended ID', 'EID']])
- self.fullid = self.id << 18 | self.eid
+ self.fullid = self.ident << 18 | self.eid
s = '%d (0x%x)' % (self.fullid, self.fullid)
self.putb([5, ['Full Identifier: %s' % s, 'Full ID: %s' % s,
'Full ID', 'FID']])
self.rtr = can_rx
if not self.fd:
- rtr = 'remote' if can_rx == 1 else 'data'
- self.putx([8, ['Remote transmission request: %s frame' % rtr,
+ rtr = 'remote' if can_rx == 1 else 'data'
+ self.putx([8, ['Remote transmission request: %s frame' % rtr,
'RTR: %s frame' % rtr, 'RTR']])
+ self.rtr_type = rtr
# Bit 33: RB1 (reserved bit)
elif bitnum == 33:
# Bits 35-38: Data length code (DLC), in number of bytes (0-8).
elif bitnum == self.dlc_start + 3:
- self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
+ self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
self.putb([10, ['Data length code: %d' % self.dlc,
'DLC: %d' % self.dlc, 'DLC']])
- self.last_databit = self.dlc_start + 3 + (self.dlc2len(self.dlc) * 8)
+ self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
# Remember all databyte bits, except the very last one.
elif bitnum in range(self.dlc_start + 4, self.last_databit):
# The bits within a data byte are transferred MSB-first.
elif bitnum == self.last_databit:
self.ss_databytebits.append(self.samplenum) # Last databyte bit.
- for i in range(self.dlc2len(self.dlc)):
+ for i in range(dlc2len(self.dlc)):
x = self.dlc_start + 4 + (8 * i)
- b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ b = bitpack_msb(self.bits[x:x + 8])
+ self.frame_bytes.append(b)
ss = self.ss_databytebits[i * 8]
es = self.ss_databytebits[((i + 1) * 8) - 1]
self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
# Get the index of the current CAN frame bit (without stuff bits).
bitnum = len(self.bits) - 1
+ if self.fd and can_rx:
+ if bitnum == 16 and self.frame_type == 'standard' \
+ or bitnum == 35 and self.frame_type == 'extended':
+ self.dom_edge_seen(force=True)
+ self.set_fast_bitrate()
+
# If this is a stuff bit, remove it from self.bits and ignore it.
if self.is_stuff_bit():
self.putx([15, [str(can_rx)]])
# Bit 0: Start of frame (SOF) bit
if bitnum == 0:
+ self.ss_packet = self.samplenum
self.putx([1, ['Start of frame', 'SOF', 'S']])
if can_rx != 0:
self.putx([16, ['Start of frame (SOF) must be a dominant bit']])
# Bits 1-11: Identifier (ID[10..0])
# The bits ID[10..4] must NOT be all recessive.
elif bitnum == 11:
- self.id = int(''.join(str(d) for d in self.bits[1:]), 2)
- s = '%d (0x%x)' % (self.id, self.id),
+ # BEWARE! Don't clobber the decoder's .id field which is
+ # part of its boiler plate!
+ self.ident = bitpack_msb(self.bits[1:])
+ self.fullid = self.ident
+ s = '%d (0x%x)' % (self.ident, self.ident),
self.putb([3, ['Identifier: %s' % s, 'ID: %s' % s, 'ID']])
- if (self.id & 0x7f0) == 0x7f0:
+ if (self.ident & 0x7f0) == 0x7f0:
self.putb([16, ['Identifier bits 10..4 must not be all recessive']])
# RTR or SRR bit, depending on frame type (gets handled later).
self.dom_edge_seen()
if self.matched[0]:
self.handle_bit(can_rx)
- self.bit_sampled()
import sigrokdecode as srd
from collections import namedtuple
+from common.srdhelper import SrdIntEnum
from .lists import *
-ANN_STROBE, ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ, \
- ANN_BURST_WRITE, ANN_STATUS_READ, ANN_STATUS, ANN_WARN = range(8)
+Ann = SrdIntEnum.from_str('Ann', 'STROBE SINGLE_READ SINGLE_WRITE BURST_READ \
+ BURST_WRITE STATUS_READ STATUS WARN')
Pos = namedtuple('Pos', ['ss', 'es'])
Data = namedtuple('Data', ['mosi', 'miso'])
('single_write', 'Single register write'),
('burst_read', 'Burst register read'),
('burst_write', 'Burst register write'),
- ('status', 'Status register'),
+ ('status_read', 'Status read'),
+ ('status_reg', 'Status register'),
('warning', 'Warning'),
)
annotation_rows = (
- ('cmd', 'Commands', (ANN_STROBE,)),
- ('data', 'Data', (ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ,
- ANN_BURST_WRITE, ANN_STATUS_READ)),
- ('status', 'Status register', (ANN_STATUS,)),
- ('warnings', 'Warnings', (ANN_WARN,)),
+ ('cmds', 'Commands', (Ann.STROBE,)),
+ ('data', 'Data', (Ann.prefixes('SINGLE_ BURST_ STATUS_'))),
+ ('status', 'Status register', (Ann.STATUS,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
)
def __init__(self):
def warn(self, pos, msg):
'''Put a warning message 'msg' at 'pos'.'''
- self.put(pos.ss, pos.es, self.out_ann, [ANN_WARN, [msg]])
+ self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
def putp(self, pos, ann, msg):
'''Put an annotation message 'msg' at 'pos'.'''
self.cmd, self.dat, self.min, self.max = c
if self.cmd == 'Strobe':
- self.putp(pos, ANN_STROBE, self.format_command())
+ self.putp(pos, Ann.STROBE, self.format_command())
else:
# Don't output anything now, the command is merged with
# the data bytes following it.
else:
name = regid
- if regid == 'STATUS' and ann == ANN_STATUS:
+ if regid == 'STATUS' and ann == Ann.STATUS:
label = 'Status'
self.decode_status_reg(pos, ann, data, label)
else:
'''Decodes the remaining data bytes at position 'pos'.'''
if self.cmd == 'Write':
- self.decode_reg(pos, ANN_SINGLE_WRITE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.SINGLE_WRITE, self.dat, self.mosi_bytes())
elif self.cmd == 'Burst write':
- self.decode_reg(pos, ANN_BURST_WRITE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
elif self.cmd == 'Read':
- self.decode_reg(pos, ANN_SINGLE_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.SINGLE_READ, self.dat, self.miso_bytes())
elif self.cmd == 'Burst read':
- self.decode_reg(pos, ANN_BURST_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
elif self.cmd == 'Strobe':
- self.decode_reg(pos, ANN_STROBE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.STROBE, self.dat, self.mosi_bytes())
elif self.cmd == 'Status read':
- self.decode_reg(pos, ANN_STATUS_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.STATUS_READ, self.dat, self.miso_bytes())
else:
self.warn(pos, 'unhandled command')
# First MOSI byte is always the command.
self.decode_command(pos, mosi)
# First MISO byte is always the status register.
- self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ self.decode_reg(pos, Ann.STATUS, 'STATUS', [miso])
else:
if not self.cmd or len(self.mb) >= self.max:
self.warn(pos, 'excess byte')
('eom-1', 'Message continued'),
('nack', 'ACK not set'),
('ack', 'ACK set'),
- ('bits', 'Bits'),
- ('bytes', 'Bytes'),
- ('frames', 'Frames'),
- ('sections', 'Sections'),
- ('warnings', 'Warnings')
+ ('bit', 'Bit'),
+ ('byte', 'Byte'),
+ ('frame', 'Frame'),
+ ('section', 'Section'),
+ ('warning', 'Warning')
)
annotation_rows = (
('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+JTAG (Joint Test Action Group), a.k.a. "IEEE 1149.1: Standard Test Access Port
+and Boundary-Scan Architecture", is a protocol used for testing, debugging,
+and flashing various digital ICs.
+
+Details:
+https://en.wikipedia.org/wiki/Joint_Test_Action_Group
+http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
+
+This decoder handles a tiny part of IEEE 1149.7, the CJTAG OSCAN1 format.
+ZBS is currently not supported.
+
+Details:
+http://developers-club.com/posts/237885/
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2020 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2019 Zhiyuan Wan <dv.xw@qq.com>
+## Copyright (C) 2019 Kongou Hikari <hikari@iloli.bid>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import SrdStrEnum
+
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[<ptype>, <pdata>]
+
+<ptype>:
+ - 'NEW STATE': <pdata> is the new state of the JTAG state machine.
+ Valid values: 'TEST-LOGIC-RESET', 'RUN-TEST/IDLE', 'SELECT-DR-SCAN',
+ 'CAPTURE-DR', 'SHIFT-DR', 'EXIT1-DR', 'PAUSE-DR', 'EXIT2-DR', 'UPDATE-DR',
+ 'SELECT-IR-SCAN', 'CAPTURE-IR', 'SHIFT-IR', 'EXIT1-IR', 'PAUSE-IR',
+ 'EXIT2-IR', 'UPDATE-IR'.
+ - 'IR TDI': Bitstring that was clocked into the IR register.
+ - 'IR TDO': Bitstring that was clocked out of the IR register.
+ - 'DR TDI': Bitstring that was clocked into the DR register.
+ - 'DR TDO': Bitstring that was clocked out of the DR register.
+
+All bitstrings are a list consisting of two items. The first is a sequence
+of '1' and '0' characters (the right-most character is the LSB. Example:
+'01110001', where 1 is the LSB). The second item is a list of ss/es values
+for each bit that is in the bitstring.
+'''
+
+s = 'TEST-LOGIC-RESET RUN-TEST/IDLE \
+ SELECT-DR-SCAN CAPTURE-DR UPDATE-DR PAUSE-DR SHIFT-DR EXIT1-DR EXIT2-DR \
+ SELECT-IR-SCAN CAPTURE-IR UPDATE-IR PAUSE-IR SHIFT-IR EXIT1-IR EXIT2-IR'
+St = SrdStrEnum.from_str('St', s)
+
+jtag_states = [s.value for s in St]
+
+s = 'EC SPARE TPDEL TPREV TPST RDYC DLYC SCNFMT CP OAC'.split()
+s = ['CJTAG_' + x for x in s] + ['OSCAN1', 'FOUR_WIRE']
+CSt = SrdStrEnum.from_list('CSt', s)
+
+cjtag_states = [s.value for s in CSt]
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'cjtag'
+ name = 'cJTAG'
+ longname = 'Compact Joint Test Action Group (IEEE 1149.7)'
+ desc = 'Protocol for testing, debugging, and flashing ICs.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['jtag']
+ tags = ['Debug/trace']
+ channels = (
+ {'id': 'tckc', 'name': 'TCKC', 'desc': 'Test clock'},
+ {'id': 'tmsc', 'name': 'TMSC', 'desc': 'Test mode select'},
+ )
+ annotations = \
+ tuple([tuple([s.lower(), s]) for s in jtag_states]) + \
+ tuple([tuple([s.lower(), s]) for s in cjtag_states]) + ( \
+ ('bit-tdi', 'Bit (TDI)'),
+ ('bit-tdo', 'Bit (TDO)'),
+ ('bitstring-tdi', 'Bitstring (TDI)'),
+ ('bitstring-tdo', 'Bitstring (TDO)'),
+ ('bit-tms', 'Bit (TMS)'),
+ )
+ annotation_rows = (
+ ('bits-tdi', 'Bits (TDI)', (28,)),
+ ('bits-tdo', 'Bits (TDO)', (29,)),
+ ('bitstrings-tdi', 'Bitstrings (TDI)', (30,)),
+ ('bitstrings-tdo', 'Bitstrings (TDO)', (31,)),
+ ('bits-tms', 'Bits (TMS)', (32,)),
+ ('cjtag-states', 'CJTAG states',
+ tuple(range(len(jtag_states), len(jtag_states + cjtag_states)))),
+ ('jtag-states', 'JTAG states', tuple(range(len(jtag_states)))),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ # self.state = St.TEST_LOGIC_RESET
+ self.state = St.RUN_TEST_IDLE
+ self.cjtagstate = CSt.FOUR_WIRE
+ self.oldcjtagstate = None
+ self.escape_edges = 0
+ self.oaclen = 0
+ self.oldtms = 0
+ self.oacp = 0
+ self.oscan1cycle = 0
+ self.oldstate = None
+ self.bits_tdi = []
+ self.bits_tdo = []
+ self.bits_samplenums_tdi = []
+ self.bits_samplenums_tdo = []
+ self.ss_item = self.es_item = None
+ self.ss_bitstring = self.es_bitstring = None
+ self.saved_item = None
+ self.first = True
+ self.first_bit = True
+
+ def start(self):
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ self.put(self.ss_item, self.es_item, self.out_ann, data)
+
+ def putp(self, data):
+ self.put(self.ss_item, self.es_item, self.out_python, data)
+
+ def putx_bs(self, data):
+ self.put(self.ss_bitstring, self.es_bitstring, self.out_ann, data)
+
+ def putp_bs(self, data):
+ self.put(self.ss_bitstring, self.es_bitstring, self.out_python, data)
+
+ def advance_state_machine(self, tms):
+ self.oldstate = self.state
+
+ if self.cjtagstate.value.startswith('CJTAG_'):
+ self.oacp += 1
+ if self.oacp > 4 and self.oaclen == 12:
+ self.cjtagstate = CSt.CJTAG_EC
+
+ if self.oacp == 8 and tms == 0:
+ self.oaclen = 36
+ if self.oacp > 8 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_SPARE
+ if self.oacp > 13 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPDEL
+ if self.oacp > 16 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPREV
+ if self.oacp > 18 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPST
+ if self.oacp > 23 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_RDYC
+ if self.oacp > 25 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_DLYC
+ if self.oacp > 27 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_SCNFMT
+
+ if self.oacp > 8 and self.oaclen == 12:
+ self.cjtagstate = CSt.CJTAG_CP
+ if self.oacp > 32 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_CP
+
+ if self.oacp > self.oaclen:
+ self.cjtagstate = CSt.OSCAN1
+ self.oscan1cycle = 1
+ # Because Nuclei cJTAG device asserts a reset during cJTAG
+ # online activating.
+ self.state = St.TEST_LOGIC_RESET
+ return
+
+ # Intro "tree"
+ if self.state == St.TEST_LOGIC_RESET:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.RUN_TEST_IDLE
+ elif self.state == St.RUN_TEST_IDLE:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ # DR "tree"
+ elif self.state == St.SELECT_DR_SCAN:
+ self.state = St.SELECT_IR_SCAN if (tms) else St.CAPTURE_DR
+ elif self.state == St.CAPTURE_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.SHIFT_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.EXIT1_DR:
+ self.state = St.UPDATE_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.PAUSE_DR:
+ self.state = St.EXIT2_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.EXIT2_DR:
+ self.state = St.UPDATE_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.UPDATE_DR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ # IR "tree"
+ elif self.state == St.SELECT_IR_SCAN:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.CAPTURE_IR
+ elif self.state == St.CAPTURE_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.SHIFT_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.EXIT1_IR:
+ self.state = St.UPDATE_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.PAUSE_IR:
+ self.state = St.EXIT2_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.EXIT2_IR:
+ self.state = St.UPDATE_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.UPDATE_IR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ def handle_rising_tckc_edge(self, tdi, tdo, tck, tms):
+
+ # Rising TCK edges always advance the state machine.
+ self.advance_state_machine(tms)
+
+ if self.first:
+ # Save the start sample and item for later (no output yet).
+ self.ss_item = self.samplenum
+ self.first = False
+ else:
+ # Output the saved item (from the last CLK edge to the current).
+ self.es_item = self.samplenum
+ # Output the old state (from last rising TCK edge to current one).
+ self.putx([jtag_states.index(self.oldstate.value), [self.oldstate.value]])
+ self.putp(['NEW STATE', self.state.value])
+
+ self.putx([len(jtag_states) + cjtag_states.index(self.oldcjtagstate.value),
+ [self.oldcjtagstate.value]])
+ if (self.oldcjtagstate.value.startswith('CJTAG_')):
+ self.putx([32, [str(self.oldtms)]])
+ self.oldtms = tms
+
+ # Upon SHIFT-*/EXIT1-* collect the current TDI/TDO values.
+ if self.oldstate.value.startswith('SHIFT-') or \
+ self.oldstate.value.startswith('EXIT1-'):
+ if self.first_bit:
+ self.ss_bitstring = self.samplenum
+ self.first_bit = False
+ else:
+ self.putx([28, [str(self.bits_tdi[0])]])
+ self.putx([29, [str(self.bits_tdo[0])]])
+ # Use self.samplenum as ES of the previous bit.
+ self.bits_samplenums_tdi[0][1] = self.samplenum
+ self.bits_samplenums_tdo[0][1] = self.samplenum
+
+ self.bits_tdi.insert(0, tdi)
+ self.bits_tdo.insert(0, tdo)
+
+ # Use self.samplenum as SS of the current bit.
+ self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
+ self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
+
+ # Output all TDI/TDO bits if we just switched to UPDATE-*.
+ if self.state.value.startswith('UPDATE-'):
+
+ self.es_bitstring = self.samplenum
+
+ t = self.state.value[-2:] + ' TDI'
+ b = ''.join(map(str, self.bits_tdi[1:]))
+ h = ' (0x%x' % int('0b0' + b, 2) + ')'
+ s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi[1:])) + ' bits'
+ self.putx_bs([30, [s]])
+ self.putp_bs([t, [b, self.bits_samplenums_tdi[1:]]])
+ self.bits_tdi = []
+ self.bits_samplenums_tdi = []
+
+ t = self.state.value[-2:] + ' TDO'
+ b = ''.join(map(str, self.bits_tdo[1:]))
+ h = ' (0x%x' % int('0b0' + b, 2) + ')'
+ s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo[1:])) + ' bits'
+ self.putx_bs([31, [s]])
+ self.putp_bs([t, [b, self.bits_samplenums_tdo[1:]]])
+ self.bits_tdo = []
+ self.bits_samplenums_tdo = []
+
+ self.first_bit = True
+
+ self.ss_bitstring = self.samplenum
+
+ self.ss_item = self.samplenum
+
+ def handle_tmsc_edge(self):
+ self.escape_edges += 1
+
+ def handle_tapc_state(self):
+ self.oldcjtagstate = self.cjtagstate
+
+ if self.escape_edges >= 8:
+ self.cjtagstate = CSt.FOUR_WIRE
+ if self.escape_edges == 6:
+ self.cjtagstate = CSt.CJTAG_OAC
+ self.oacp = 0
+ self.oaclen = 12
+
+ self.escape_edges = 0
+
+ def decode(self):
+ tdi = tms = tdo = 0
+
+ while True:
+ # Wait for a rising edge on TCKC.
+ tckc, tmsc = self.wait({0: 'r'})
+ self.handle_tapc_state()
+
+ if self.cjtagstate == CSt.OSCAN1:
+ if self.oscan1cycle == 0: # nTDI
+ tdi = 1 if (tmsc == 0) else 0
+ self.oscan1cycle = 1
+ elif self.oscan1cycle == 1: # TMS
+ tms = tmsc
+ self.oscan1cycle = 2
+ elif self.oscan1cycle == 2: # TDO
+ tdo = tmsc
+ self.handle_rising_tckc_edge(tdi, tdo, tckc, tms)
+ self.oscan1cycle = 0
+ else:
+ self.handle_rising_tckc_edge(None, None, tckc, tmsc)
+
+ while (tckc == 1):
+ tckc, tmsc_n = self.wait([{0: 'f'}, {1: 'e'}])
+ if tmsc_n != tmsc:
+ tmsc = tmsc_n
+ self.handle_tmsc_edge()
# All other values: "not defined".
}
-card_status = {
- 0: 'Reserved for manufacturer test mode',
- 1: 'Reserved for manufacturer test mode',
- 2: 'Reserved for application specific commands',
- 3: 'AKE_SEQ_ERROR',
- 4: 'Reserved for SDIO card',
- 5: 'APP_CMD',
- 6: 'Unknown',
- 7: 'Unknown',
- 8: 'READY_FOR_DATA',
- 9: 'CURRENT_STATE', # CURRENT_STATE is a 4-bit value (decimal: 0..15).
- 10: 'CURRENT_STATE',
- 11: 'CURRENT_STATE',
- 12: 'CURRENT_STATE',
- 13: 'ERASE_RESET',
- 14: 'CARD_ECC_DISABLED',
- 15: 'WP_ERASE_SKIP',
- 16: 'CSD_OVERWRITE',
- 17: 'Reserved for DEFERRED_RESPONSE', # See eSD addendum
- 18: 'Reserved',
- 19: 'ERROR',
- 20: 'CC_ERROR',
- 21: 'CARD_ECC_FAILED',
- 22: 'ILLEGAL_COMMAND',
- 23: 'COM_CRC_ERROR',
- 24: 'LOCK_UNLOCK_FAILED',
- 25: 'CARD_IS_LOCKED',
- 26: 'WP_VIOLATION',
- 27: 'ERASE_PARAM',
- 28: 'ERASE_SEQ_ERROR',
- 29: 'BLOCK_LEN_ERROR',
- 30: 'ADDRESS_ERROR',
- 31: 'OUT_OF_RANGE',
-}
-
sd_status = {
# 311:0: Reserved for manufacturer
# 391:312: Reserved
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2012-2020 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
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
+from enum import Enum, IntEnum, unique
+from itertools import chain
+import re
+
# Return the specified BCD number (max. 8 bits) as integer.
def bcd2int(b):
return (b & 0x0f) + ((b >> 4) * 10)
def bitpack(bits):
return sum([b << i for i, b in enumerate(bits)])
+def bitpack_lsb(bits, idx=None):
+ '''Conversion from LSB first bit sequence to integer.'''
+ if idx is not None:
+ bits = [b[idx] for b in bits]
+ return bitpack(bits)
+
+def bitpack_msb(bits, idx=None):
+ '''Conversion from MSB first bit sequence to integer.'''
+ bits = bits[:]
+ if idx is not None:
+ bits = [b[idx] for b in bits]
+ bits.reverse()
+ return bitpack(bits)
+
def bitunpack(num, minbits=0):
res = []
while num or minbits > 0:
num >>= 1
minbits -= 1
return tuple(res)
+
+@unique
+class SrdStrEnum(Enum):
+ @classmethod
+ def from_list(cls, name, l):
+ # Keys are limited/converted to [A-Z0-9_], values can be any string.
+ items = [(re.sub('[^A-Z0-9_]', '_', l[i]), l[i]) for i in range(len(l))]
+ return cls(name, items)
+
+ @classmethod
+ def from_str(cls, name, s):
+ return cls.from_list(name, s.split())
+
+@unique
+class SrdIntEnum(IntEnum):
+ @classmethod
+ def _prefix(cls, p):
+ return tuple([a.value for a in cls if a.name.startswith(p)])
+
+ @classmethod
+ def prefixes(cls, prefix_list):
+ if isinstance(prefix_list, str):
+ prefix_list = prefix_list.split()
+ return tuple(chain(*[cls._prefix(p) for p in prefix_list]))
+
+ @classmethod
+ def _suffix(cls, s):
+ return tuple([a.value for a in cls if a.name.endswith(s)])
+
+ @classmethod
+ def suffixes(cls, suffix_list):
+ if isinstance(suffix_list, str):
+ suffix_list = suffix_list.split()
+ return tuple(chain(*[cls._suffix(s) for s in suffix_list]))
+
+ @classmethod
+ def from_list(cls, name, l):
+ # Manually construct (Python 3.4 is missing the 'start' argument).
+ # Python defaults to start=1, but we want start=0.
+ return cls(name, [(l[i], i) for i in range(len(l))])
+
+ @classmethod
+ def from_str(cls, name, s):
+ return cls.from_list(name, s.split())
)
annotations = (
('bit', 'Bit'),
- ('startbit', 'Startbit'),
+ ('startbit', 'Start bit'),
('sbit', 'Select bit'),
('ybit', 'Individual or group'),
('address', 'Address'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
- ('raw', 'Raw data', (7,)),
+ ('raw-data', 'Raw data', (7,)),
('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
)
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
self.dev_type = None
)
annotations = (
('start-of-minute', 'Start of minute'),
- ('special-bits', 'Special bits (civil warnings, weather forecast)'),
+ ('special-bit', 'Special bit (civil warnings, weather forecast)'),
('call-bit', 'Call bit'),
('summer-time', 'Summer time announcement'),
('cest', 'CEST bit'),
('month', 'Month'),
('year', 'Year'),
('date-parity', 'Date parity bit'),
- ('raw-bits', 'Raw bits'),
- ('unknown-bits', 'Unknown bits'),
- ('warnings', 'Human-readable warnings'),
+ ('raw-bit', 'Raw bit'),
+ ('unknown-bit', 'Unknown bit'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('bits', 'Bits', (17, 18)),
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
##
## 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
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
+## Copyright (C) 2019-2020 Gerhard Sittig <gerhard.sittig@gmx.net>
##
## 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
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[<ptype>, <pdata>]
+
+This is the list of <ptype> codes and their respective <pdata> values:
+ - 'PACKET': The data is a list of tuples with the bytes' start and end
+ positions as well as a byte value and a validity flag. This output
+ represents a DMX packet. The sample numbers span the range beginning
+ at the start of the start code and ending at the end of the last data
+ byte in the packet. The start code value resides at index 0.
+
+Developer notes on the DMX512 protocol:
+
+See Wikipedia for an overview:
+ https://en.wikipedia.org/wiki/DMX512#Electrical (physics, transport)
+ https://en.wikipedia.org/wiki/DMX512#Protocol (UART frames, DMX frames)
+ RS-485 transport, differential thus either polarity (needs user spec)
+ 8n2 UART frames at 250kbps, BREAK to start a new DMX frame
+ slot 0 carries start code, slot 1 up to 512 max carry data for peripherals
+ start code 0 for "boring lights", non-zero start code for extensions.
+
+TODO
+- Cover more DMX packet types beyond start code 0x00 (standard). See
+ https://en.wikipedia.org/wiki/DMX512#Protocol for a list (0x17 text,
+ 0xcc RDM, 0xcf sysinfo) and a reference to the ESTA database. These
+ can either get added here or can get implemented in a stacked decoder.
+- Run on more captures as these become available. Verify the min/max
+ BREAK, MARK, and RESET to RESET period checks. Add more conditions that
+ are worth checking to determine the health of the bus, see the (German)
+ http://www.soundlight.de/techtips/dmx512/dmx2000a.htm article for ideas.
+- Is there a more user friendly way of having the DMX512 decoder configure
+ the UART decoder's parameters? Currently users need to setup the polarity
+ (which is acceptable, and an essential feature), but also the bitrate and
+ frame format (which may or may not be considered acceptable).
+- (Not a DMX512 decoder TODO item) Current UART decoder implementation does
+ not handle two STOP bits, but DMX512 will transparently benefit when UART
+ gets adjusted. Until then the second STOP bit will be mistaken for a MARK
+ but that's just cosmetics, available data gets interpreted correctly.
+'''
+
import sigrokdecode as srd
+class Ann:
+ BREAK, MAB, INTERFRAME, INTERPACKET, STARTCODE, DATABYTE, CHANNEL_DATA, \
+ SLOT_DATA, RESET, WARN, ERROR = range(11)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'dmx512'
longname = 'Digital MultipleX 512'
desc = 'Digital MultipleX 512 (DMX512) lighting protocol.'
license = 'gplv2+'
- inputs = ['logic']
- outputs = []
+ inputs = ['uart']
+ outputs = ['dmx512']
tags = ['Embedded/industrial', 'Lighting']
- channels = (
- {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
+ options = (
+ {'id': 'min_break', 'desc': 'Minimum BREAK length (us)', 'default': 88},
+ {'id': 'max_mark', 'desc': 'Maximum MARK length (us)', 'default': 1000000},
+ {'id': 'min_break_break', 'desc': 'Minimum BREAK to BREAK interval (us)',
+ 'default': 1196},
+ {'id': 'max_reset_reset', 'desc': 'Maximum RESET to RESET interval (us)',
+ 'default': 1250000},
+ {'id': 'show_zero', 'desc': 'Display all-zero set-point values',
+ 'default': 'no', 'values': ('yes', 'no')},
+ {'id': 'format', 'desc': 'Data format', 'default': 'dec',
+ 'values': ('dec', 'hex', 'bin')},
)
annotations = (
- ('bit', 'Bit'),
+ # Lowest layer (above UART): BREAK MARK ( FRAME [MARK] )*
+ # with MARK being after-break or inter-frame or inter-packet.
('break', 'Break'),
('mab', 'Mark after break'),
- ('startbit', 'Start bit'),
- ('stopbits', 'Stop bit'),
- ('startcode', 'Start code'),
- ('channel', 'Channel'),
('interframe', 'Interframe'),
('interpacket', 'Interpacket'),
- ('data', 'Data'),
+ # Next layer: STARTCODE ( DATABYTE )*
+ ('startcode', 'Start code'),
+ ('databyte', 'Data byte'),
+ # Next layer: CHANNEL or SLOT values
+ ('chan_data', 'Channel data'),
+ ('slot_data', 'Slot data'),
+ # Next layer: RESET
+ ('reset', 'Reset sequence'),
+ # Warnings and errors.
+ ('warning', 'Warning'),
('error', 'Error'),
)
annotation_rows = (
- ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
- ('data', 'Data', (9,)),
- ('bits', 'Bits', (0, 3, 4)),
- ('errors', 'Errors', (10,)),
+ ('dmx_fields', 'Fields', (Ann.BREAK, Ann.MAB,
+ Ann.STARTCODE, Ann.INTERFRAME,
+ Ann.DATABYTE, Ann.INTERPACKET)),
+ ('chans_data', 'Channels data', (Ann.CHANNEL_DATA,)),
+ ('slots_data', 'Slots data', (Ann.SLOT_DATA,)),
+ ('resets', 'Reset sequences', (Ann.RESET,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ ('errors', 'Errors', (Ann.ERROR,)),
)
def __init__(self):
def reset(self):
self.samplerate = None
- self.sample_usec = None
- self.run_start = -1
- self.run_bit = 0
- self.state = 'FIND BREAK'
+ self.samples_per_usec = None
+ self.last_reset = None
+ self.last_break = None
+ self.packet = None
+ self.last_es = None
+ self.last_frame = None
+ self.start_code = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.sample_usec = 1 / value * 1000000
- self.skip_per_bit = int(4 / self.sample_usec)
-
- def putr(self, data):
- self.put(self.run_start, self.samplenum, self.out_ann, data)
-
- def decode(self):
- if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
- while True:
- # Seek for an interval with no state change with a length between
- # 88 and 1000000 us (BREAK).
- if self.state == 'FIND BREAK':
- (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
- runlen = (self.samplenum - self.run_start) * self.sample_usec
- if runlen > 88 and runlen < 1000000:
- self.putr([1, ['Break']])
- self.bit_break = self.run_bit
- self.state = 'MARK MAB'
- self.channel = 0
- elif runlen >= 1000000:
- # Error condition.
- self.putr([10, ['Invalid break length']])
- self.run_bit = dmx
- self.run_start = self.samplenum
- # Directly following the BREAK is the MARK AFTER BREAK.
- elif self.state == 'MARK MAB':
- (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
- self.putr([2, ['MAB']])
- self.state = 'READ BYTE'
- self.channel = 0
- self.bit = 0
- self.aggreg = dmx
- self.run_start = self.samplenum
- # Mark and read a single transmitted byte
- # (start bit, 8 data bits, 2 stop bits).
- elif self.state == 'READ BYTE':
- (dmx,) = self.wait()
- self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit
- self.aggreg += dmx
- if self.samplenum != self.next_sample:
- continue
- bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1
-
- if self.bit == 0:
- self.byte = 0
- self.putr([3, ['Start bit']])
- if bit_value != 0:
- # (Possibly) invalid start bit, mark but don't fail.
- self.put(self.samplenum, self.samplenum,
- self.out_ann, [10, ['Invalid start bit']])
- elif self.bit >= 9:
- self.put(self.samplenum - self.skip_per_bit,
- self.samplenum, self.out_ann, [4, ['Stop bit']])
- if bit_value != 1:
- # Invalid stop bit, mark.
- self.put(self.samplenum, self.samplenum,
- self.out_ann, [10, ['Invalid stop bit']])
- if self.bit == 10:
- # On invalid 2nd stop bit, search for new break.
- self.run_bit = dmx
- self.state = 'FIND BREAK'
- else:
- # Label and process one bit.
- self.put(self.samplenum - self.skip_per_bit,
- self.samplenum, self.out_ann, [0, [str(bit_value)]])
- self.byte |= bit_value << (self.bit - 1)
-
- # Label a complete byte.
- if self.bit == 10:
- if self.channel == 0:
- d = [5, ['Start code']]
- else:
- d = [6, ['Channel ' + str(self.channel)]]
- self.put(self.run_start, self.next_sample, self.out_ann, d)
- self.put(self.run_start + self.skip_per_bit,
- self.next_sample - 2 * self.skip_per_bit,
- self.out_ann, [9, [str(self.byte) + ' / ' + \
- str(hex(self.byte))]])
- # Continue by scanning the IFT.
- self.channel += 1
- self.run_start = self.samplenum
- self.run_bit = dmx
- self.state = 'MARK IFT'
-
- self.aggreg = dmx
- self.bit += 1
- # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
- elif self.state == 'MARK IFT':
- (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
- if self.channel > 512:
- self.putr([8, ['Interpacket']])
- self.state = 'FIND BREAK'
- self.run_bit = dmx
- self.run_start = self.samplenum
- else:
- self.putr([7, ['Interframe']])
- self.state = 'READ BYTE'
- self.bit = 0
- self.run_start = self.samplenum
+ self.samples_per_usec = value / 1000000
+
+ def have_samplerate(self):
+ return bool(self.samplerate)
+
+ def samples_to_usecs(self, count):
+ return count / self.samples_per_usec
+
+ def putg(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def putpy(self, ss, es, data):
+ self.put(ss, es, self.out_python, data)
+
+ def format_value(self, v):
+ fmt = self.options['format']
+ if fmt == 'dec':
+ return '{:d}'.format(v)
+ if fmt == 'hex':
+ return '{:02X}'.format(v)
+ if fmt == 'bin':
+ return '{:08b}'.format(v)
+ return '{}'.format(v)
+
+ def flush_packet(self):
+ if self.packet:
+ ss, es = self.packet[0][0], self.packet[-1][1]
+ self.putpy(ss, es, ['PACKET', self.packet])
+ self.packet = None
+
+ def flush_reset(self, ss, es):
+ if ss is not None and es is not None:
+ self.putg(ss, es, [Ann.RESET, ['RESET SEQUENCE', 'RESET', 'R']])
+ if self.last_reset and self.have_samplerate():
+ duration = self.samples_to_usecs(es - self.last_reset)
+ if duration > self.options['max_reset_reset']:
+ txts = ['Excessive RESET to RESET interval', 'RESET to RESET', 'RESET']
+ self.putg(self.last_reset, es, [Ann.WARN, txts])
+ self.last_reset = es
+
+ def flush_break(self, ss, es):
+ self.putg(ss, es, [Ann.BREAK, ['BREAK', 'B']])
+ if self.have_samplerate():
+ duration = self.samples_to_usecs(es - ss)
+ if duration < self.options['min_break']:
+ txts = ['Short BREAK period', 'Short BREAK', 'BREAK']
+ self.putg(ss, es, [Ann.WARN, txts])
+ if self.last_break:
+ duration = self.samples_to_usecs(ss - self.last_break)
+ if duration < self.options['min_break_break']:
+ txts = ['Short BREAK to BREAK interval', 'Short BREAK to BREAK', 'BREAK']
+ self.putg(ss, es, [Ann.WARN, txts])
+ self.last_break = ss
+ self.last_es = es
+
+ def flush_mark(self, ss, es, is_mab = False, is_if = False, is_ip = False):
+ '''Handle several kinds of MARK conditions.'''
+
+ if ss is None or es is None or ss >= es:
+ return
+
+ if is_mab:
+ ann = Ann.MAB
+ txts = ['MARK AFTER BREAK', 'MAB']
+ elif is_if:
+ ann = Ann.INTERFRAME
+ txts = ['INTER FRAME', 'IF']
+ elif is_ip:
+ ann = Ann.INTERPACKET
+ txts = ['INTER PACKET', 'IP']
+ else:
+ return
+ self.putg(ss, es, [ann, txts])
+
+ if self.have_samplerate():
+ duration = self.samples_to_usecs(es - ss)
+ if duration > self.options['max_mark']:
+ txts = ['Excessive MARK length', 'MARK length', 'MARK']
+ self.putg(ss, es, [Ann.ERROR, txts])
+
+ def flush_frame(self, ss, es, value, valid):
+ '''Handle UART frame content. Accumulate DMX packet.'''
+
+ if not valid:
+ txts = ['Invalid frame', 'Frame']
+ self.putg(ss, es, [Ann.ERROR, txts])
+
+ self.last_es = es
+
+ # Cease packet inspection before first BREAK.
+ if not self.last_break:
+ return
+
+ # Accumulate the sequence of bytes for the current DMX frame.
+ # Emit the annotation at the "DMX fields" level.
+ is_start = self.packet is None
+ if is_start:
+ self.packet = []
+ slot_nr = len(self.packet)
+ item = (ss, es, value, valid)
+ self.packet.append(item)
+ if is_start:
+ # Slot 0, the start code. Determines the DMX frame type.
+ self.start_code = value
+ ann = Ann.STARTCODE
+ val_text = self.format_value(value)
+ txts = [
+ 'STARTCODE {}'.format(val_text),
+ 'START {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ else:
+ # Slot 1+, the payload bytes.
+ ann = Ann.DATABYTE
+ val_text = self.format_value(value)
+ txts = [
+ 'DATABYTE {:d}: {}'.format(slot_nr, val_text),
+ 'DATA {:d}: {}'.format(slot_nr, val_text),
+ 'DATA {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ self.putg(ss, es, [ann, txts])
+
+ # Tell channel data for peripherals from arbitrary slot values.
+ # Can get extended for other start code types in case protocol
+ # extensions are handled here and not in stacked decoders.
+ if is_start:
+ ann = None
+ elif self.start_code == 0:
+ # Start code was 0. Slots carry values for channels.
+ # Optionally suppress zero-values to make used channels
+ # stand out, to help users focus their attention.
+ ann = Ann.CHANNEL_DATA
+ if value == 0 and self.options['show_zero'] == 'no':
+ ann = None
+ else:
+ val_text = self.format_value(value)
+ txts = [
+ 'CHANNEL {:d}: {}'.format(slot_nr, val_text),
+ 'CH {:d}: {}'.format(slot_nr, val_text),
+ 'CH {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ else:
+ # Unhandled start code. Provide "anonymous" values.
+ ann = Ann.SLOT_DATA
+ val_text = self.format_value(value)
+ txts = [
+ 'SLOT {:d}: {}'.format(slot_nr, val_text),
+ 'SL {:d}: {}'.format(slot_nr, val_text),
+ 'SL {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ if ann is not None:
+ self.putg(ss, es, [ann, txts])
+
+ if is_start and value == 0:
+ self.flush_reset(self.last_break, es)
+
+ def handle_break(self, ss, es):
+ '''Handle UART BREAK conditions.'''
+
+ # Check the last frame before BREAK if one was queued. It could
+ # have been "invalid" since the STOP bit check failed. If there
+ # is an invalid frame which happens to start at the start of the
+ # BREAK condition, then discard it. Otherwise flush its output.
+ last_frame = self.last_frame
+ self.last_frame = None
+ frame_invalid = last_frame and not last_frame[3]
+ frame_zero_data = last_frame and last_frame[2] == 0
+ frame_is_break = last_frame and last_frame[0] == ss
+ if frame_invalid and frame_zero_data and frame_is_break:
+ last_frame = None
+ if last_frame is not None:
+ self.flush_frame(*last_frame)
+
+ # Handle inter-packet MARK (works for zero length, too).
+ self.flush_mark(self.last_es, ss, is_ip = True)
+
+ # Handle accumulated packets.
+ self.flush_packet()
+ self.packet = None
+
+ # Annotate the BREAK condition. Start accumulation of a packet.
+ self.flush_break(ss, es)
+
+ def handle_frame(self, ss, es, value, valid):
+ '''Handle UART data frames.'''
+
+ # Flush previously deferred frame (if available). Can't have been
+ # BREAK if another data frame follows.
+ last_frame = self.last_frame
+ self.last_frame = None
+ if last_frame:
+ self.flush_frame(*last_frame)
+
+ # Handle inter-frame MARK (works for zero length, too).
+ is_mab = self.last_break and self.packet is None
+ is_if = self.packet
+ self.flush_mark(self.last_es, ss, is_mab = is_mab, is_if = is_if)
+
+ # Defer handling of invalid frames, because they may start a new
+ # BREAK which we will only learn about much later. Immediately
+ # annotate valid frames.
+ if valid:
+ self.flush_frame(ss, es, value, valid)
+ else:
+ self.last_frame = (ss, es, value, valid)
+
+ def decode(self, ss, es, data):
+ # Lack of a sample rate in the input capture only disables the
+ # optional warnings about exceeded timespans here at the DMX512
+ # decoder level. That the lower layer UART decoder depends on a
+ # sample rate is handled there, and is not relevant here.
+
+ ptype, rxtx, pdata = data
+ if ptype == 'BREAK':
+ self.handle_break(ss, es)
+ elif ptype == 'FRAME':
+ value, valid = pdata
+ self.handle_frame(ss, es, value, valid)
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2012-2020 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
import re
import sigrokdecode as srd
-from common.srdhelper import bcd2int
+from common.srdhelper import bcd2int, SrdIntEnum
days_of_week = (
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
DS1307_I2C_ADDRESS = 0x68
def regs_and_bits():
- l = [('reg-' + r.lower(), r + ' register') for r in regs]
- l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits]
+ l = [('reg_' + r.lower(), r + ' register') for r in regs]
+ l += [('bit_' + re.sub('\/| ', '_', b).lower(), b + ' bit') for b in bits]
return tuple(l)
+a = ['REG_' + r.upper() for r in regs] + \
+ ['BIT_' + re.sub('\/| ', '_', b).upper() for b in bits] + \
+ ['READ_DATE_TIME', 'WRITE_DATE_TIME', 'READ_REG', 'WRITE_REG', 'WARNING']
+Ann = SrdIntEnum.from_list('Ann', a)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'ds1307'
outputs = []
tags = ['Clock/timing', 'IC']
annotations = regs_and_bits() + (
- ('read-datetime', 'Read date/time'),
- ('write-datetime', 'Write date/time'),
- ('reg-read', 'Register read'),
- ('reg-write', 'Register write'),
- ('warnings', 'Warnings'),
+ ('read_date_time', 'Read date/time'),
+ ('write_date_time', 'Write date/time'),
+ ('read_reg', 'Register read'),
+ ('write_reg', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('bits', 'Bits', tuple(range(9, 24))),
- ('regs', 'Registers', tuple(range(9))),
- ('date-time', 'Date/time', (24, 25, 26, 27)),
- ('warnings', 'Warnings', (28,)),
+ ('bits', 'Bits', Ann.prefixes('BIT_')),
+ ('regs', 'Registers', Ann.prefixes('REG_')),
+ ('date_time', 'Date/time', Ann.prefixes('READ_ WRITE_')),
+ ('warnings', 'Warnings', (Ann.WARNING,)),
)
def __init__(self):
def putr(self, bit):
self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann,
- [11, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
+ [Ann.BIT_RESERVED, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
def handle_reg_0x00(self, b): # Seconds (0-59) / Clock halt bit
- self.putd(7, 0, [0, ['Seconds', 'Sec', 'S']])
+ self.putd(7, 0, [Ann.REG_SECONDS, ['Seconds', 'Sec', 'S']])
ch = 1 if (b & (1 << 7)) else 0
- self.putd(7, 7, [9, ['Clock halt: %d' % ch, 'Clk hlt: %d' % ch,
- 'CH: %d' % ch, 'CH']])
+ self.putd(7, 7, [Ann.BIT_CLOCK_HALT, ['Clock halt: %d' % ch,
+ 'Clk hlt: %d' % ch, 'CH: %d' % ch, 'CH']])
s = self.seconds = bcd2int(b & 0x7f)
- self.putd(6, 0, [10, ['Second: %d' % s, 'Sec: %d' % s, 'S: %d' % s, 'S']])
+ self.putd(6, 0, [Ann.BIT_SECONDS, ['Second: %d' % s, 'Sec: %d' % s,
+ 'S: %d' % s, 'S']])
def handle_reg_0x01(self, b): # Minutes (0-59)
- self.putd(7, 0, [1, ['Minutes', 'Min', 'M']])
+ self.putd(7, 0, [Ann.REG_MINUTES, ['Minutes', 'Min', 'M']])
self.putr(7)
m = self.minutes = bcd2int(b & 0x7f)
- self.putd(6, 0, [12, ['Minute: %d' % m, 'Min: %d' % m, 'M: %d' % m, 'M']])
+ self.putd(6, 0, [Ann.BIT_MINUTES, ['Minute: %d' % m, 'Min: %d' % m, 'M: %d' % m, 'M']])
def handle_reg_0x02(self, b): # Hours (1-12+AM/PM or 0-23)
- self.putd(7, 0, [2, ['Hours', 'H']])
+ self.putd(7, 0, [Ann.REG_HOURS, ['Hours', 'H']])
self.putr(7)
ampm_mode = True if (b & (1 << 6)) else False
if ampm_mode:
- self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']])
+ self.putd(6, 6, [Ann.BIT_12_24_HOURS, ['12-hour mode', '12h mode', '12h']])
a = 'PM' if (b & (1 << 5)) else 'AM'
- self.putd(5, 5, [14, [a, a[0]]])
+ self.putd(5, 5, [Ann.BIT_AM_PM, [a, a[0]]])
h = self.hours = bcd2int(b & 0x1f)
- self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
+ self.putd(4, 0, [Ann.BIT_HOURS, ['Hour: %d' % h, 'H: %d' % h, 'H']])
else:
- self.putd(6, 6, [13, ['24-hour mode', '24h mode', '24h']])
+ self.putd(6, 6, [Ann.BIT_12_24_HOURS, ['24-hour mode', '24h mode', '24h']])
h = self.hours = bcd2int(b & 0x3f)
- self.putd(5, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
+ self.putd(5, 0, [Ann.BIT_HOURS, ['Hour: %d' % h, 'H: %d' % h, 'H']])
def handle_reg_0x03(self, b): # Day / day of week (1-7)
- self.putd(7, 0, [3, ['Day of week', 'Day', 'D']])
+ self.putd(7, 0, [Ann.REG_DAY, ['Day of week', 'Day', 'D']])
for i in (7, 6, 5, 4, 3):
self.putr(i)
w = self.days = bcd2int(b & 0x07)
ws = days_of_week[self.days - 1]
- self.putd(2, 0, [16, ['Weekday: %s' % ws, 'WD: %s' % ws, 'WD', 'W']])
+ self.putd(2, 0, [Ann.BIT_DAY, ['Weekday: %s' % ws, 'WD: %s' % ws, 'WD', 'W']])
def handle_reg_0x04(self, b): # Date (1-31)
- self.putd(7, 0, [4, ['Date', 'D']])
+ self.putd(7, 0, [Ann.REG_DATE, ['Date', 'D']])
for i in (7, 6):
self.putr(i)
d = self.date = bcd2int(b & 0x3f)
- self.putd(5, 0, [17, ['Date: %d' % d, 'D: %d' % d, 'D']])
+ self.putd(5, 0, [Ann.BIT_DATE, ['Date: %d' % d, 'D: %d' % d, 'D']])
def handle_reg_0x05(self, b): # Month (1-12)
- self.putd(7, 0, [5, ['Month', 'Mon', 'M']])
+ self.putd(7, 0, [Ann.REG_MONTH, ['Month', 'Mon', 'M']])
for i in (7, 6, 5):
self.putr(i)
m = self.months = bcd2int(b & 0x1f)
- self.putd(4, 0, [18, ['Month: %d' % m, 'Mon: %d' % m, 'M: %d' % m, 'M']])
+ self.putd(4, 0, [Ann.BIT_MONTH, ['Month: %d' % m, 'Mon: %d' % m, 'M: %d' % m, 'M']])
def handle_reg_0x06(self, b): # Year (0-99)
- self.putd(7, 0, [6, ['Year', 'Y']])
+ self.putd(7, 0, [Ann.REG_YEAR, ['Year', 'Y']])
y = self.years = bcd2int(b & 0xff)
self.years += 2000
- self.putd(7, 0, [19, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
+ self.putd(7, 0, [Ann.BIT_YEAR, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
def handle_reg_0x07(self, b): # Control Register
- self.putd(7, 0, [7, ['Control', 'Ctrl', 'C']])
+ self.putd(7, 0, [Ann.REG_CONTROL, ['Control', 'Ctrl', 'C']])
for i in (6, 5, 3, 2):
self.putr(i)
o = 1 if (b & (1 << 7)) else 0
s = 1 if (b & (1 << 4)) else 0
s2 = 'en' if (b & (1 << 4)) else 'dis'
r = rates[b & 0x03]
- self.putd(7, 7, [20, ['Output control: %d' % o,
+ self.putd(7, 7, [Ann.BIT_OUT, ['Output control: %d' % o,
'OUT: %d' % o, 'O: %d' % o, 'O']])
- self.putd(4, 4, [21, ['Square wave output: %sabled' % s2,
+ self.putd(4, 4, [Ann.BIT_SQWE, ['Square wave output: %sabled' % s2,
'SQWE: %sabled' % s2, 'SQWE: %d' % s, 'S: %d' % s, 'S']])
- self.putd(1, 0, [22, ['Square wave output rate: %s' % r,
+ self.putd(1, 0, [Ann.BIT_RS, ['Square wave output rate: %s' % r,
'Square wave rate: %s' % r, 'SQW rate: %s' % r, 'Rate: %s' % r,
'RS: %s' % s, 'RS', 'R']])
def handle_reg_0x3f(self, b): # RAM (bytes 0x08-0x3f)
- self.putd(7, 0, [8, ['RAM', 'R']])
- self.putd(7, 0, [23, ['SRAM: 0x%02X' % b, '0x%02X' % b]])
+ self.putd(7, 0, [Ann.REG_RAM, ['RAM', 'R']])
+ self.putd(7, 0, [Ann.BIT_RAM, ['SRAM: 0x%02X' % b, '0x%02X' % b]])
def output_datetime(self, cls, rw):
# TODO: Handle read/write of only parts of these items.
if addr == DS1307_I2C_ADDRESS:
return True
self.put(self.ss_block, self.es, self.out_ann,
- [28, ['Ignoring non-DS1307 data (slave 0x%02X)' % addr]])
+ [Ann.WARNING, ['Ignoring non-DS1307 data (slave 0x%02X)' % addr]])
return False
def decode(self, ss, es, data):
if cmd == 'DATA WRITE':
self.handle_reg(databyte)
elif cmd == 'STOP':
- self.output_datetime(25, 'Written')
+ self.output_datetime(Ann.WRITE_DATE_TIME, 'Written')
self.state = 'IDLE'
elif self.state == 'READ RTC REGS':
# Wait for an address read operation.
if cmd == 'DATA READ':
self.handle_reg(databyte)
elif cmd == 'STOP':
- self.output_datetime(24, 'Read')
+ self.output_datetime(Ann.READ_DATE_TIME, 'Read')
self.state = 'IDLE'
outputs = []
tags = ['Embedded/industrial', 'IC']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
outputs = []
tags = ['IC', 'Memory']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
binary = (
('mem_read', 'Data read from memory'),
outputs = []
tags = ['IC', 'Sensor']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
)
annotation_rows = (
('bits', 'Bits', (0,)),
- ('raw', 'Raw data', (3,)),
+ ('raw-vals', 'Raw data', (3,)),
('fields', 'Fields', (1, 2)),
)
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
raise SamplerateError('Cannot decode without samplerate.')
bit = 0
while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
(self.dsi,) = self.wait()
if self.options['polarity'] == 'active-high':
self.dsi ^= 1 # Invert.
# - Extensions
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
import os
+St = SrdIntEnum.from_str('St', 'OFFSET EXTENSIONS HEADER EDID')
+
EDID_HEADER = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]
OFF_VENDOR = 8
OFF_VERSION = 18
outputs = []
tags = ['Display', 'Memory', 'PC']
annotations = (
- ('fields', 'EDID structure fields'),
- ('sections', 'EDID structure sections'),
+ ('field', 'Field'),
+ ('section', 'Section'),
)
annotation_rows = (
- ('sections', 'Sections', (1,)),
('fields', 'Fields', (0,)),
+ ('sections', 'Sections', (1,)),
)
def __init__(self):
cmd, data = data
if cmd == 'ADDRESS WRITE' and data == 0x50:
- self.state = 'offset'
+ self.state = St.OFFSET
self.ss = ss
return
if cmd == 'ADDRESS READ' and data == 0x50:
if self.extension > 0:
- self.state = 'extensions'
+ self.state = St.EXTENSIONS
s = str(self.extension)
t = ["Extension: " + s, "X: " + s, s]
else:
- self.state = 'header'
+ self.state = St.HEADER
t = ["EDID"]
self.put(ss, es, self.out_ann, [ANN_SECTIONS, t])
return
- if cmd == 'DATA WRITE' and self.state == 'offset':
+ if cmd == 'DATA WRITE' and self.state == St.OFFSET:
self.offset = data
self.extension = self.offset // 128
self.cnt = self.offset % 128
self.sn.append([ss, es])
self.cache.append(data)
- if self.state is None or self.state == 'header':
+ if self.state is None or self.state == St.HEADER:
# Wait for the EDID header
if self.cnt >= OFF_VENDOR:
if self.cache[-8:] == EDID_HEADER:
self.sn = self.sn[-8:]
self.cache = self.cache[-8:]
self.cnt = 8
- self.state = 'edid'
+ self.state = St.EDID
self.put(self.sn[0][0], es, self.out_ann,
[ANN_SECTIONS, ['Header']])
self.put(self.sn[0][0], es, self.out_ann,
[ANN_FIELDS, ['Header pattern']])
- elif self.state == 'edid':
+ elif self.state == St.EDID:
if self.cnt == OFF_VERSION:
self.decode_vid(-10)
self.decode_pid(-8)
csstr = 'WRONG!'
self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
self.cache[self.cnt-1], csstr)]])
- self.state = 'extensions'
+ self.state = St.EXTENSIONS
- elif self.state == 'extensions':
+ elif self.state == St.EXTENSIONS:
cache = self.ext_cache[self.extension - 1]
sn = self.ext_sn[self.extension - 1]
v = cache[self.cnt - 1]
'addr_pins': 3, # Called E0, E1, E2 on this chip.
'max_speed': 400,
},
+ 'st_m24c32': {
+ 'vendor': 'ST',
+ 'model': 'M24C32',
+ 'size': 4 * 1024,
+ 'page_size': 32,
+ 'page_wraparound': True,
+ 'addr_bytes': 2,
+ 'addr_pins': 3, # Called E0, E1, E2 on this chip.
+ 'max_speed': 1000,
+ },
# Xicor
'xicor_x24c02': {
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
+import copy
import sigrokdecode as srd
from .lists import *
)
annotations = (
# Warnings
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
# Bits/bytes
('control-code', 'Control code'),
('address-pin', 'Address pin (A0/A1/A2)'),
self.reset_variables()
def decode(self, ss, es, data):
- self.cmd, self.databyte = data
+ cmd, _ = data
# Collect the 'BITS' packet, then return. The next packet is
# guaranteed to belong to these bits we just stored.
- if self.cmd == 'BITS':
- self.bits = self.databyte
+ if cmd == 'BITS':
+ _, databits = data
+ self.bits = copy.deepcopy(databits)
return
- # Store the start/end samples of this I²C packet.
+ # Store the start/end samples of this I²C packet. Deep copy
+ # caller's data, assuming that implementation details of the
+ # above complex methods can access the data after returning
+ # from the .decode() invocation, with the data having become
+ # invalid by that time of access. This conservative approach
+ # can get weakened after close inspection of those methods.
self.ss, self.es = ss, es
+ _, databyte = data
+ databyte = copy.deepcopy(databyte)
+ self.cmd, self.databyte = cmd, databyte
# State machine.
s = 'handle_%s' % self.state.lower().replace(' ', '_')
('data', 'Data', (0, 1)),
('warnings', 'Warnings', (2,)),
)
+ binary = (
+ ('address', 'Address'),
+ ('data', 'Data'),
+ )
def __init__(self):
self.reset()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
self.addresssize = self.options['addresssize']
self.wordsize = self.options['wordsize']
for b in range(len(data)):
a += (data[b].si << (len(data) - b - 1))
self.put(data[0].ss, data[-1].es, self.out_ann,
- [0, ['Address: 0x%x' % a, 'Addr: 0x%x' % a, '0x%x' % a]])
+ [0, ['Address: 0x%04x' % a, 'Addr: 0x%04x' % a, '0x%04x' % a]])
+ self.put(data[0].ss, data[-1].es, self.out_binary, [0, bytes([a])])
def put_word(self, si, data):
# Decode word (MSb first).
else:
word_str = '[{:02X}]'.format(c) + word_str
self.put(data[0].ss, data[-1].es,
- self.out_ann, [idx, ['Data: %s' % word_str, '%s' % word_str]])
+ self.out_ann, [idx, ['Data: %s' % word_str, '%s' % word_str]])
else:
self.put(data[0].ss, data[-1].es,
- self.out_ann, [idx, ['Data: 0x%x' % word, '0x%x' % word]])
+ self.out_ann, [idx, ['Data: 0x%04x' % word, '0x%04x' % word]])
+ self.put(data[0].ss, data[-1].es, self.out_binary,
+ [1, bytes([(word & 0xff00) >> 8, word & 0xff])])
def decode(self, ss, es, data):
if len(data) < (2 + self.addresssize):
('warning', 'Warning'),
)
annotation_rows = (
+ ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
('commands', 'Commands',
(ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
- ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
('warnings', 'Warnings', (ANN_WARNING,)),
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+FlexRay is a fast, deterministic and fault-tolerant fieldbus system
+which is used in cars in high security related areas like X-by-Wire.
+
+It is the result of the FlexRay consortium which consisted of BMW,
+Daimler, Motorola (today Freescale) and Philips, with the goal of
+working out a common standard automotive bus system.
+
+This decoder assumes that at least one channel of a logic level RX line
+of a transceiver is sampled (e.g. NXP TJA1080).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Selection of constants as defined in FlexRay specification 3.0.1 Chapter A.1:
+class Const:
+ cChannelIdleDelimiter = 11
+ cCrcInitA = 0xFEDCBA
+ cCrcInitB = 0xABCDEF
+ cCrcPolynomial = 0x5D6DCB
+ cCrcSize = 24
+ cCycleCountMax = 63
+ cdBSS = 2
+ cdCAS = 30
+ cdFES = 2
+ cdFSS = 1
+ cHCrcInit = 0x01A
+ cHCrcPolynomial = 0x385
+ cHCrcSize = 11
+ cSamplesPerBit = 8
+ cSlotIDMax = 2047
+ cStaticSlotIDMax = 1023
+ cVotingSamples = 5
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'flexray'
+ name = 'FlexRay'
+ longname = 'FlexRay'
+ desc = 'Automotive network communications protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Automotive']
+ channels = (
+ {'id': 'channel', 'name': 'Channel', 'desc': 'FlexRay bus channel'},
+ )
+ options = (
+ {'id': 'channel_type', 'desc': 'Channel type', 'default': 'A',
+ 'values': ('A', 'B')},
+ {'id': 'bitrate', 'desc': 'Bitrate (bit/s)', 'default': 10000000,
+ 'values': (10000000, 5000000, 2500000)},
+ )
+ annotations = (
+ ('data', 'FlexRay payload data'),
+ ('tss', 'Transmission start sequence'),
+ ('fss', 'Frame start sequence'),
+ ('reserved-bit', 'Reserved bit'),
+ ('ppi', 'Payload preamble indicator'),
+ ('null-frame', 'Nullframe indicator'),
+ ('sync-frame', 'Full identifier'),
+ ('startup-frame', 'Startup frame indicator'),
+ ('id', 'Frame ID'),
+ ('length', 'Data length'),
+ ('header-crc', 'Header CRC'),
+ ('cycle', 'Cycle code'),
+ ('data-byte', 'Data byte'),
+ ('frame-crc', 'Frame CRC'),
+ ('fes', 'Frame end sequence'),
+ ('bss', 'Byte start sequence'),
+ ('warning', 'Warning'),
+ ('bit', 'Bit'),
+ ('cid', 'Channel idle delimiter'),
+ ('dts', 'Dynamic trailing sequence'),
+ ('cas', 'Collision avoidance symbol'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (15, 17)),
+ ('fields', 'Fields', tuple(range(15)) + (18, 19, 20)),
+ ('warnings', 'Warnings', (16,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.reset_variables()
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ bitrate = float(self.options['bitrate'])
+ self.samplerate = value
+ self.bit_width = float(self.samplerate) / bitrate
+ self.sample_point = (self.bit_width / 100.0) * self.sample_point_percent
+
+ # Generic helper for FlexRay bit annotations.
+ def putg(self, ss, es, data):
+ left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
+ self.put(ss - left, es + right, self.out_ann, data)
+
+ # Single-FlexRay-bit annotation using the current samplenum.
+ def putx(self, data):
+ self.putg(self.samplenum, self.samplenum, data)
+
+ # Multi-FlexRay-bit annotation from self.ss_block to current samplenum.
+ def putb(self, data):
+ self.putg(self.ss_block, self.samplenum, data)
+
+ # Generic CRC algorithm for any bit size and any data length. Used for
+ # 11-bit header and 24-bit trailer. Not very efficient but at least it
+ # works for now.
+ #
+ # TODO:
+ # - use precalculated tables to increase performance.
+ # - Add support for reverse CRC calculations.
+
+ @staticmethod
+ def crc(data, data_len_bits, polynom, crc_len_bits, iv=0, xor=0):
+ reg = iv ^ xor
+
+ for i in range(data_len_bits - 1, -1, -1):
+ bit = ((reg >> (crc_len_bits - 1)) & 0x1) ^ ((data >> i) & 0x1)
+ reg <<= 1
+ if bit:
+ reg ^= polynom
+
+ mask = (1 << crc_len_bits) - 1
+ crc = reg & mask
+
+ return crc ^ xor
+
+ def reset_variables(self):
+ self.sample_point_percent = 50 # TODO: use vote based sampling
+ self.state = 'IDLE'
+ self.tss_start = self.tss_end = self.frame_type = self.dlc = None
+ self.rawbits = [] # All bits, including byte start sequence bits
+ self.bits = [] # Only actual FlexRay frame bits (no byte start sequence bits)
+ self.curbit = 0 # Current bit of FlexRay frame (bit 0 == FSS)
+ self.last_databit = 999 # Positive value that bitnum+x will never match
+ self.last_xmit_bit = 999 # Positive value that bitnum+x will never match
+ self.ss_block = None
+ self.ss_databytebits = []
+ self.end_of_frame = False
+ self.dynamic_frame = False
+ self.ss_bit0 = None
+ self.ss_bit1 = None
+ self.ss_bit2 = None
+
+ # Poor man's clock synchronization. Use signal edges which change to
+ # dominant state in rather simple ways. This naive approach is neither
+ # aware of the SYNC phase's width nor the specific location of the edge,
+ # but improves the decoder's reliability when the input signal's bitrate
+ # does not exactly match the nominal rate.
+ def dom_edge_seen(self, force=False):
+ self.dom_edge_snum = self.samplenum
+ self.dom_edge_bcount = self.curbit
+
+ # Determine the position of the next desired bit's sample point.
+ def get_sample_point(self, bitnum):
+ samplenum = self.dom_edge_snum
+ samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+ samplenum += self.sample_point
+ return int(samplenum)
+
+ def is_bss_sequence(self):
+ # FlexRay uses NRZ encoding and adds a binary 10 sequence before each
+ # byte. After each 8 data bits, a BSS sequence is added but not after
+ # frame CRC.
+
+ if self.end_of_frame:
+ return False
+
+ if (len(self.rawbits) - 2) % 10 == 0:
+ return True
+ elif (len(self.rawbits) - 3) % 10 == 0:
+ return True
+
+ return False
+
+ def handle_bit(self, fr_rx):
+ self.rawbits.append(fr_rx)
+ self.bits.append(fr_rx)
+
+ # Get the index of the current FlexRay frame bit.
+ bitnum = len(self.bits) - 1
+
+ # If this is a byte start sequence remove it from self.bits and ignore it.
+ if self.is_bss_sequence():
+ self.bits.pop()
+
+ if bitnum > 1:
+ self.putx([15, [str(fr_rx)]])
+ else:
+ if len(self.rawbits) == 2:
+ self.ss_bit1 = self.samplenum
+ elif len(self.rawbits) == 3:
+ self.ss_bit2 = self.samplenum
+
+ self.curbit += 1 # Increase self.curbit (bitnum is not affected).
+ return
+ else:
+ if bitnum > 1:
+ self.putx([17, [str(fr_rx)]])
+
+ # Bit 0: Frame start sequence (FSS) bit
+ if bitnum == 0:
+ self.ss_bit0 = self.samplenum
+
+ # Bit 1: Start of header
+ elif bitnum == 1:
+ if self.rawbits[:3] == [1, 1, 0]:
+ self.put(self.tss_start, self.tss_end, self.out_ann,
+ [1, ['Transmission start sequence', 'TSS']])
+
+ self.putg(self.ss_bit0, self.ss_bit0, [17, [str(self.rawbits[:3][0])]])
+ self.putg(self.ss_bit0, self.ss_bit0, [2, ['FSS', 'Frame start sequence']])
+ self.putg(self.ss_bit1, self.ss_bit1, [15, [str(self.rawbits[:3][1])]])
+ self.putg(self.ss_bit2, self.ss_bit2, [15, [str(self.rawbits[:3][2])]])
+ self.putx([17, [str(fr_rx)]])
+ self.putx([3, ['Reserved bit: %d' % fr_rx, 'RB: %d' % fr_rx, 'RB']])
+ else:
+ self.put(self.tss_start, self.tss_end, self.out_ann,
+ [20, ['Collision avoidance symbol', 'CAS']])
+ self.reset_variables()
+
+ # TODO: warning, if sequence is neither [1, 1, 0] nor [1, 1, 1]
+
+ # Bit 2: Payload preamble indicator. Must be 0 if null frame indicator is 0.
+ elif bitnum == 2:
+ self.putx([4, ['Payload preamble indicator: %d' % fr_rx,
+ 'PPI: %d' % fr_rx]])
+
+ # Bit 3: Null frame indicator (inversed)
+ elif bitnum == 3:
+ data_type = 'data frame' if fr_rx else 'null frame'
+ self.putx([5, ['Null frame indicator: %s' % data_type,
+ 'NF: %d' % fr_rx, 'NF']])
+
+ # Bit 4: Sync frame indicator
+ # Must be 1 if startup frame indicator is 1.
+ elif bitnum == 4:
+ self.putx([6, ['Sync frame indicator: %d' % fr_rx,
+ 'Sync: %d' % fr_rx, 'Sync']])
+
+ # Bit 5: Startup frame indicator
+ elif bitnum == 5:
+ self.putx([7, ['Startup frame indicator: %d' % fr_rx,
+ 'Startup: %d' % fr_rx, 'Startup']])
+
+ # Remember start of ID (see below).
+ elif bitnum == 6:
+ self.ss_block = self.samplenum
+
+ # Bits 6-16: Frame identifier (ID[10..0])
+ # ID must NOT be 0.
+ elif bitnum == 16:
+ self.id = int(''.join(str(d) for d in self.bits[6:]), 2)
+ self.putb([8, ['Frame ID: %d' % self.id, 'ID: %d' % self.id,
+ '%d' % self.id]])
+
+ # Remember start of payload length (see below).
+ elif bitnum == 17:
+ self.ss_block = self.samplenum
+
+ # Bits 17-23: Payload length (Length[7..0])
+ # Payload length in header is the half of the real payload size.
+ elif bitnum == 23:
+ self.payload_length = int(''.join(str(d) for d in self.bits[17:]), 2)
+ self.putb([9, ['Payload length: %d' % self.payload_length,
+ 'Length: %d' % self.payload_length,
+ '%d' % self.payload_length]])
+
+ # Remember start of header CRC (see below).
+ elif bitnum == 24:
+ self.ss_block = self.samplenum
+
+ # Bits 24-34: Header CRC (11-bit) (HCRC[11..0])
+ # Calculation of header CRC is equal on both channels.
+ elif bitnum == 34:
+ bits = ''.join([str(b) for b in self.bits[4:24]])
+ header_to_check = int(bits, 2)
+ expected_crc = self.crc(header_to_check, len(bits),
+ Const.cHCrcPolynomial, Const.cHCrcSize, Const.cHCrcInit)
+ self.header_crc = int(''.join(str(d) for d in self.bits[24:]), 2)
+
+ crc_ok = self.header_crc == expected_crc
+ crc_ann = "OK" if crc_ok else "bad"
+
+ self.putb([10, ['Header CRC: 0x%X (%s)' % (self.header_crc, crc_ann),
+ '0x%X (%s)' % (self.header_crc, crc_ann),
+ '0x%X' % self.header_crc]])
+
+ # Remember start of cycle code (see below).
+ elif bitnum == 35:
+ self.ss_block = self.samplenum
+
+ # Bits 35-40: Cycle code (Cyc[6..0])
+ # Cycle code. Must be between 0 and 63.
+ elif bitnum == 40:
+ self.cycle = int(''.join(str(d) for d in self.bits[35:]), 2)
+ self.putb([11, ['Cycle: %d' % self.cycle, 'Cyc: %d' % self.cycle,
+ '%d' % self.cycle]])
+ self.last_databit = 41 + 2 * self.payload_length * 8
+
+ # Remember all databyte bits, except the very last one.
+ elif bitnum in range(41, self.last_databit):
+ self.ss_databytebits.append(self.samplenum)
+
+ # Bits 41-X: Data field (0-254 bytes, depending on length)
+ # The bits within a data byte are transferred MSB-first.
+ elif bitnum == self.last_databit:
+ self.ss_databytebits.append(self.samplenum) # Last databyte bit.
+ for i in range(2 * self.payload_length):
+ x = 40 + (8 * i) + 1
+ b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ ss = self.ss_databytebits[i * 8]
+ es = self.ss_databytebits[((i + 1) * 8) - 1]
+ self.putg(ss, es, [12, ['Data byte %d: 0x%02x' % (i, b),
+ 'DB%d: 0x%02x' % (i, b), '%02X' % b]])
+ self.ss_databytebits = []
+ self.ss_block = self.samplenum # Remember start of trailer CRC.
+
+ # Trailer CRC (24-bit) (CRC[11..0])
+ # Initialization vector of channel A and B are different, so CRCs are
+ # different for same data.
+ elif bitnum == self.last_databit + 23:
+ bits = ''.join([str(b) for b in self.bits[1:-24]])
+ frame_to_check = int(bits, 2)
+ iv = Const.cCrcInitA if self.options['channel_type'] == 'A' else Const.cCrcInitB
+ expected_crc = self.crc(frame_to_check, len(bits),
+ Const.cCrcPolynomial, Const.cCrcSize, iv=iv)
+ self.frame_crc = int(''.join(str(d) for d in self.bits[self.last_databit:]), 2)
+
+ crc_ok = self.frame_crc == expected_crc
+ crc_ann = "OK" if crc_ok else "bad"
+
+ self.putb([13, ['Frame CRC: 0x%X (%s)' % (self.frame_crc, crc_ann),
+ '0x%X (%s)' % (self.frame_crc, crc_ann),
+ '0x%X' % self.frame_crc]])
+ self.end_of_frame = True
+
+ # Remember start of frame end sequence (see below).
+ elif bitnum == self.last_databit + 24:
+ self.ss_block = self.samplenum
+
+ # Frame end sequence, must be 1 followed by 0.
+ elif bitnum == self.last_databit + 25:
+ self.putb([14, ['Frame end sequence', 'FES']])
+
+ # Check for DTS
+ elif bitnum == self.last_databit + 26:
+ if not fr_rx:
+ self.dynamic_frame = True
+ else:
+ self.last_xmit_bit = bitnum
+ self.ss_block = self.samplenum
+
+ # Remember start of channel idle delimiter (see below).
+ elif bitnum == self.last_xmit_bit:
+ self.ss_block = self.samplenum
+
+ # Channel idle limiter (CID[11..0])
+ elif bitnum == self.last_xmit_bit + Const.cChannelIdleDelimiter - 1:
+ self.putb([18, ['Channel idle delimiter', 'CID']])
+ self.reset_variables()
+
+ # DTS if dynamic frame
+ elif bitnum > self.last_databit + 27:
+ if self.dynamic_frame:
+ if fr_rx:
+ if self.last_xmit_bit == 999:
+ self.putb([19, ['Dynamic trailing sequence', 'DTS']])
+ self.last_xmit_bit = bitnum + 1
+ self.ss_block = self.samplenum
+
+ self.curbit += 1
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+
+ while True:
+ # State machine.
+ if self.state == 'IDLE':
+ # Wait for a dominant state (logic 0) on the bus.
+ (fr_rx,) = self.wait({0: 'l'})
+ self.tss_start = self.samplenum
+ (fr_rx,) = self.wait({0: 'h'})
+ self.tss_end = self.samplenum
+ self.dom_edge_seen(force = True)
+ self.state = 'GET BITS'
+ elif self.state == 'GET BITS':
+ # Wait until we're in the correct bit/sampling position.
+ pos = self.get_sample_point(self.curbit)
+ (fr_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
+ if self.matched[1]:
+ self.dom_edge_seen()
+ if self.matched[0]:
+ self.handle_bit(fr_rx)
+++ /dev/null
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-'''
-This protocol decoder can decode the GPIB (IEEE-488) protocol.
-'''
-
-from .pd import Decoder
+++ /dev/null
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
- api_version = 3
- id = 'gpib'
- name = 'GPIB'
- longname = 'General Purpose Interface Bus'
- desc = 'IEEE-488 General Purpose Interface Bus (GPIB / HPIB).'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = []
- tags = ['PC']
- channels = (
- {'id': 'dio1' , 'name': 'DIO1', 'desc': 'Data I/O bit 1'},
- {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
- {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
- {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
- {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
- {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
- {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
- {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
- {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
- {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
- {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
- {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
- {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
- {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
- {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
- {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
- )
- options = (
- {'id': 'sample_total', 'desc': 'Total number of samples', 'default': 0},
- )
- annotations = (
- ('items', 'Items'),
- ('gpib', 'DAT/CMD'),
- ('eoi', 'EOI'),
- )
- annotation_rows = (
- ('bytes', 'Bytes', (0,)),
- ('gpib', 'DAT/CMD', (1,)),
- ('eoi', 'EOI', (2,)),
- )
-
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.items = []
- self.itemcount = 0
- self.saved_item = None
- self.saved_ATN = False
- self.saved_EOI = False
- self.samplenum = 0
- self.ss_item = self.es_item = None
- self.first = True
-
- def start(self):
- self.out_ann = self.register(srd.OUTPUT_ANN)
-
- def putb(self, data):
- self.put(self.ss_item, self.es_item, self.out_ann, data)
-
- def handle_bits(self, datapins):
- dbyte = 0x20
- dATN = False
- item2 = False
- dEOI = False
- item3 = False
- # If this is the first item in a word, save its sample number.
- if self.itemcount == 0:
- self.ss_word = self.samplenum
-
- # Get the bits for this item.
- item = 0
- for i in range(8):
- item |= datapins[i] << i
-
- item = item ^ 0xff # Invert data byte.
- self.items.append(item)
- self.itemcount += 1
-
- if datapins[14] == 0:
- item2 = True
- if datapins[8] == 0:
- item3 = True
-
- if self.first:
- # Save the start sample and item for later (no output yet).
- self.ss_item = self.samplenum
- self.first = False
- self.saved_item = item
- self.saved_ATN = item2
- self.saved_EOI = item3
- else:
- # Output the saved item.
- dbyte = self.saved_item
- dATN = self.saved_ATN
- dEOI = self.saved_EOI
- self.es_item = self.samplenum
- self.putb([0, ['%02X' % self.saved_item]])
-
- # Encode item byte to GPIB convention.
- self.strgpib = ' '
- if dATN: # ATN, decode commands.
- if dbyte == 0x01: self.strgpib = 'GTL'
- if dbyte == 0x04: self.strgpib = 'SDC'
- if dbyte == 0x05: self.strgpib = 'PPC'
- if dbyte == 0x08: self.strgpib = 'GET'
- if dbyte == 0x09: self.strgpib = 'TCT'
- if dbyte == 0x11: self.strgpib = 'LLO'
- if dbyte == 0x14: self.strgpib = 'DCL'
- if dbyte == 0x15: self.strgpib = 'PPU'
- if dbyte == 0x18: self.strgpib = 'SPE'
- if dbyte == 0x19: self.strgpib = 'SPD'
- if dbyte == 0x3f: self.strgpib = 'UNL'
- if dbyte == 0x5f: self.strgpib = 'UNT'
- if dbyte > 0x1f and dbyte < 0x3f: # Address Listener.
- self.strgpib = 'L' + chr(dbyte + 0x10)
- if dbyte > 0x3f and dbyte < 0x5f: # Address Talker
- self.strgpib = 'T' + chr(dbyte - 0x10)
- else:
- if dbyte > 0x1f and dbyte < 0x7f:
- self.strgpib = chr(dbyte)
- if dbyte == 0x0a:
- self.strgpib = 'LF'
- if dbyte == 0x0d:
- self.strgpib = 'CR'
-
- self.putb([1, [self.strgpib]])
- self.strEOI = ' '
- if dEOI:
- self.strEOI = 'EOI'
- self.putb([2, [self.strEOI]])
-
- self.ss_item = self.samplenum
- self.saved_item = item
- self.saved_ATN = item2
- self.saved_EOI = item3
-
- if self.itemcount < 16:
- return
-
- self.itemcount, self.items = 0, []
-
- def decode(self):
-
- # Inspect samples at falling edge of DAV. But make sure to also
- # start inspection when the capture happens to start with low
- # DAV level. Optionally enforce processing when a user specified
- # sample number was reached.
- waitcond = [{9: 'l'}]
- lsn = self.options['sample_total']
- if lsn:
- waitcond.append({'skip': lsn})
- while True:
- if lsn:
- waitcond[1]['skip'] = lsn - self.samplenum - 1
- pins = self.wait(waitcond)
- self.handle_bits(pins)
- waitcond[0][9] = 'f'
('phase', 'Phase'),
('increment', 'Increment'),
('count', 'Count'),
- ('turns', 'Turns'),
+ ('turn', 'Turn'),
('interval', 'Interval'),
('average', 'Average'),
('rpm', 'Rate'),
)
- annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+ annotation_rows = tuple((u + 's', v + 's', (i,)) for i, (u, v) in enumerate(annotations))
def __init__(self):
self.reset()
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes HDCP messages.
+
+Details:
+https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+msg_ids = {
+ 2: 'AKE_Init',
+ 3: 'AKE_Send_Cert',
+ 4: 'AKE_No_stored_km',
+ 5: 'AKE_Stored_km',
+
+ 7: 'AKE_Send_H_prime',
+ 8: 'AKE_Send_Pairing_Info',
+
+ 9: 'LC_Init',
+ 10: 'LC_Send_L_prime',
+
+ 11: 'SKE_Send_Eks',
+ 12: 'RepeaterAuth_Send_ReceiverID_List',
+
+ 15: 'RepeaterAuth_Send_Ack',
+ 16: 'RepeaterAuth_Stream_Manage',
+ 17: 'RepeaterAuth_Stream_Ready',
+}
+
+write_items = {
+ 0x00: '1.4 Bksv - Receiver KSV',
+ 0x08: '1.4 Ri\' - Link Verification',
+ 0x0a: '1.4 Pj\' - Enhanced Link Verification',
+ 0x10: '1.4 Aksv - Transmitter KSV',
+ 0x15: '1.4 Ainfo - Transmitter KSV',
+ 0x18: '1.4 An - Session random number',
+ 0x20: '1.4 V\'H0',
+ 0x24: '1.4 V\'H1',
+ 0x28: '1.4 V\'H2',
+ 0x2c: '1.4 V\'H3',
+ 0x30: '1.4 V\'H4',
+ 0x40: '1.4 Bcaps',
+ 0x41: '1.4 Bstatus',
+ 0x43: '1.4 KSV FIFO',
+ 0x50: 'HDCP2Version',
+ 0x60: 'Write_Message',
+ 0x70: 'RxStatus',
+ 0x80: 'Read_Message',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'hdcp'
+ name = 'HDCP'
+ longname = 'HDCP over HDMI'
+ desc = 'HDCP protocol over HDMI.'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = ['hdcp']
+ tags = ['PC', 'Security/crypto']
+ annotations = \
+ tuple(('message-0x%02X' % i, 'Message 0x%02X' % i) for i in range(18)) + (
+ ('summary', 'Summary'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('messages', 'Messages', tuple(range(18))),
+ ('summaries', 'Summaries', (18,)),
+ ('warnings', 'Warnings', (19,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = 'IDLE'
+ self.stack = []
+ self.msg = -1
+ self.ss = self.es = self.ss_block = self.es_block = 0
+ self.init_seq = []
+ self.valid = 0
+ self.type = ''
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, data):
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+ def decode(self, ss, es, data):
+ cmd, databyte = data
+
+ # Collect the 'BITS' packet, then return. The next packet is
+ # guaranteed to belong to these bits we just stored.
+ if cmd == 'BITS':
+ self.bits = databyte
+ return
+
+ self.ss, self.es = ss, es
+
+ # State machine.
+ if self.state == 'IDLE':
+ # Wait for an I2C START condition.
+ if cmd == 'START':
+ self.reset()
+ self.ss_block = ss
+ elif cmd != 'START REPEAT':
+ return
+ self.state = 'GET SLAVE ADDR'
+ elif self.state == 'GET SLAVE ADDR':
+ if cmd == 'ADDRESS READ':
+ self.state = 'BUFFER DATA'
+ if databyte != 0x3a:
+ self.state = 'IDLE'
+ elif cmd == 'ADDRESS WRITE':
+ self.state = 'WRITE OFFSET'
+ if databyte != 0x3a:
+ self.state = 'IDLE'
+ elif self.state == 'WRITE OFFSET':
+ if cmd == 'DATA WRITE':
+ if databyte in write_items:
+ self.type = write_items[databyte]
+ if databyte in (0x10, 0x15, 0x18, 0x60):
+ self.state = 'BUFFER DATA'
+ # If we are reading, then jump back to IDLE for a start repeat.
+ # If we are writing, then just continue onwards.
+ if self.state == 'BUFFER DATA':
+ pass
+ elif self.type != '':
+ self.state = 'IDLE'
+ elif self.state == 'BUFFER DATA':
+ if cmd in ('STOP', 'NACK'):
+ self.es_block = es
+ self.state = 'IDLE'
+ if self.type == '':
+ return
+ if not self.stack:
+ self.putb([18, ['%s' % (self.type)]])
+ return
+ if self.type == 'RxStatus':
+ rxstatus = (self.stack.pop() << 8) | self.stack.pop()
+ reauth_req = (rxstatus & 0x800) != 0
+ ready = (rxstatus & 0x400) != 0
+ length = rxstatus & 0x3ff
+ text = '%s, reauth %s, ready %s, length %s' % \
+ (self.type, reauth_req, ready, length)
+ self.putb([18, [text]])
+ elif self.type == '1.4 Bstatus':
+ bstatus = (self.stack.pop() << 8) | self.stack.pop()
+ device_count = bstatus & 0x7f
+ max_devs_exceeded = (bstatus & 0x80) != 0
+ depth = ((bstatus & 0x700) >> 8)
+ max_cascase_exceeded = bstatus & 0x800
+ hdmi_mode = (bstatus & 0x1000) != 0
+ text = '%s, %s devices, depth %s, hdmi mode %s' % \
+ (self.type, device_count, depth, hdmi_mode)
+ self.putb([18, [text]])
+ elif self.type == 'Read_Message':
+ msg = self.stack.pop(0)
+ self.putb([msg, ['%s, %s' % (self.type,
+ msg_ids.get(msg, 'Invalid'))]])
+ elif self.type == 'Write_Message':
+ msg = self.stack.pop(0)
+ self.putb([msg, ['%s, %s' % (self.type,
+ msg_ids.get(msg, 'Invalid'))]])
+ elif self.type == 'HDCP2Version':
+ version = self.stack.pop(0)
+ if (version & 0x4):
+ self.putb([18, ['HDCP2']])
+ else:
+ self.putb([18, ['NOT HDCP2']])
+ else:
+ self.putb([18, ['%s' % (self.type)]])
+ elif cmd == 'DATA READ':
+ # Stack up our data bytes.
+ self.stack.append(databyte)
+ elif cmd == 'DATA WRITE':
+ # Stack up our data bytes.
+ self.stack.append(databyte)
# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
# TODO: Implement support for detecting various bus errors.
+from common.srdhelper import bitpack_msb
import sigrokdecode as srd
'''
command. Slave addresses do not include bit 0 (the READ/WRITE indication bit).
For example, a slave address field could be 0x51 (instead of 0xa2).
For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <pdata> is None.
+For 'BITS' <pdata> is a sequence of tuples of bit values and their start and
+stop positions, in LSB first order (although the I2C protocol is MSB first).
'''
-# CMD: [annotation-type-index, long annotation, short annotation]
+# Meaning of table items:
+# command -> [annotation class, annotation text in order of decreasing length]
proto = {
- 'START': [0, 'Start', 'S'],
- 'START REPEAT': [1, 'Start repeat', 'Sr'],
- 'STOP': [2, 'Stop', 'P'],
- 'ACK': [3, 'ACK', 'A'],
- 'NACK': [4, 'NACK', 'N'],
- 'BIT': [5, 'Bit', 'B'],
- 'ADDRESS READ': [6, 'Address read', 'AR'],
- 'ADDRESS WRITE': [7, 'Address write', 'AW'],
- 'DATA READ': [8, 'Data read', 'DR'],
- 'DATA WRITE': [9, 'Data write', 'DW'],
+ 'START': [0, 'Start', 'S'],
+ 'START REPEAT': [1, 'Start repeat', 'Sr'],
+ 'STOP': [2, 'Stop', 'P'],
+ 'ACK': [3, 'ACK', 'A'],
+ 'NACK': [4, 'NACK', 'N'],
+ 'BIT': [5, '{b:1d}'],
+ 'ADDRESS READ': [6, 'Address read: {b:02X}', 'AR: {b:02X}', '{b:02X}'],
+ 'ADDRESS WRITE': [7, 'Address write: {b:02X}', 'AW: {b:02X}', '{b:02X}'],
+ 'DATA READ': [8, 'Data read: {b:02X}', 'DR: {b:02X}', '{b:02X}'],
+ 'DATA WRITE': [9, 'Data write: {b:02X}', 'DW: {b:02X}', '{b:02X}'],
+ 'WARN': [10, '{text}'],
}
class Decoder(srd.Decoder):
('address-write', 'Address write'),
('data-read', 'Data read'),
('data-write', 'Data write'),
- ('warnings', 'Human-readable warnings'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('bits', 'Bits', (5,)),
- ('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 6, 7, 8, 9)),
+ ('addr-data', 'Address/data', (0, 1, 2, 3, 4, 6, 7, 8, 9)),
('warnings', 'Warnings', (10,)),
)
binary = (
def reset(self):
self.samplerate = None
- self.ss = self.es = self.ss_byte = -1
- self.bitcount = 0
- self.databyte = 0
- self.wr = -1
- self.is_repeat_start = 0
- self.state = 'FIND START'
+ self.is_write = None
+ self.rem_addr_bytes = None
+ self.slave_addr_7 = None
+ self.slave_addr_10 = None
+ self.is_repeat_start = False
self.pdu_start = None
self.pdu_bits = 0
- self.bits = []
+ self.data_bits = []
+ self.bitwidth = 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.out_bitrate = self.register(srd.OUTPUT_META,
meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit'))
- def putx(self, data):
- self.put(self.ss, self.es, self.out_ann, data)
-
- def putp(self, data):
- self.put(self.ss, self.es, self.out_python, data)
-
- def putb(self, data):
- self.put(self.ss, self.es, self.out_binary, data)
-
- def handle_start(self, pins):
- self.ss, self.es = self.samplenum, self.samplenum
- self.pdu_start = self.samplenum
- self.pdu_bits = 0
- cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
- self.putp([cmd, None])
- self.putx([proto[cmd][0], proto[cmd][1:]])
- self.state = 'FIND ADDRESS'
- self.bitcount = self.databyte = 0
- self.is_repeat_start = 1
- self.wr = -1
- self.bits = []
+ def putg(self, ss, es, cls, text):
+ self.put(ss, es, self.out_ann, [cls, text])
+
+ def putp(self, ss, es, data):
+ self.put(ss, es, self.out_python, data)
+
+ def putb(self, ss, es, data):
+ self.put(ss, es, self.out_binary, data)
+
+ def _wants_start(self):
+ # Check whether START is required (to sync to the input stream).
+ return self.pdu_start is None
+
+ def _collects_address(self):
+ # Check whether the transfer still is in the address phase (is
+ # still collecting address and r/w details, or has not started
+ # collecting it).
+ return self.rem_addr_bytes is None or self.rem_addr_bytes != 0
+
+ def _collects_byte(self):
+ # Check whether bits of a byte are being collected. Outside of
+ # the data byte, the bit is the ACK/NAK slot.
+ return self.data_bits is None or len(self.data_bits) < 8
+
+ def handle_start(self, ss, es):
+ if self.is_repeat_start:
+ cmd = 'START REPEAT'
+ else:
+ cmd = 'START'
+ self.pdu_start = ss
+ self.pdu_bits = 0
+ self.putp(ss, es, [cmd, None])
+ cls, texts = proto[cmd][0], proto[cmd][1:]
+ self.putg(ss, es, cls, texts)
+ self.is_repeat_start = True
+ self.is_write = None
+ self.slave_addr_7 = None
+ self.slave_addr_10 = None
+ self.rem_addr_bytes = None
+ self.data_bits.clear()
+ self.bitwidth = 0
# Gather 8 bits of data plus the ACK/NACK bit.
- def handle_address_or_data(self, pins):
- scl, sda = pins
+ def handle_address_or_data(self, ss, es, value):
self.pdu_bits += 1
- # Address and data are transmitted MSB-first.
- self.databyte <<= 1
- self.databyte |= sda
-
- # Remember the start of the first data/address bit.
- if self.bitcount == 0:
- self.ss_byte = self.samplenum
-
- # Store individual bits and their start/end samplenumbers.
- # In the list, index 0 represents the LSB (I²C transmits MSB-first).
- self.bits.insert(0, [sda, self.samplenum, self.samplenum])
- if self.bitcount > 0:
- self.bits[1][2] = self.samplenum
- if self.bitcount == 7:
- self.bitwidth = self.bits[1][2] - self.bits[2][2]
- self.bits[0][2] += self.bitwidth
-
- # Return if we haven't collected all 8 + 1 bits, yet.
- if self.bitcount < 7:
- self.bitcount += 1
+ # Accumulate a byte's bits, including its start position.
+ # Accumulate individual bits and their start/end sample numbers
+ # as we see them. Get the start sample number at the time when
+ # the bit value gets sampled. Assume the start of the next bit
+ # as the end sample number of the previous bit. Guess the last
+ # bit's end sample number from the second last bit's width.
+ # Keep the bits in receive order (MSB first) during accumulation.
+ # (gsi: Strictly speaking falling SCL would be the end of the
+ # bit value's validity. That'd break compatibility though.)
+ if self.data_bits:
+ self.data_bits[-1][2] = ss
+ self.data_bits.append([value, ss, es])
+ if len(self.data_bits) < 8:
return
-
- d = self.databyte
- 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
- if self.options['address_format'] == 'shifted':
- d = d >> 1
-
+ self.bitwidth = self.data_bits[-2][2] - self.data_bits[-3][2]
+ self.data_bits[-1][2] = self.data_bits[-1][1] + self.bitwidth
+
+ # Get the byte value. Address and data are transmitted MSB-first.
+ d = bitpack_msb(self.data_bits, 0)
+ ss_byte, es_byte = self.data_bits[0][1], self.data_bits[-1][2]
+
+ # Process the address bytes at the start of a transfer. The
+ # first byte will carry the R/W bit, and all of the 7bit address
+ # or part of a 10bit address. Bit pattern 0b11110xxx signals
+ # that another byte follows which carries the remaining bits of
+ # a 10bit slave address.
+ is_address = self._collects_address()
+ if is_address:
+ addr_byte = d
+ if self.rem_addr_bytes is None:
+ if (addr_byte & 0xf8) == 0xf0:
+ self.rem_addr_bytes = 2
+ self.slave_addr_7 = None
+ self.slave_addr_10 = addr_byte & 0x06
+ self.slave_addr_10 <<= 7
+ else:
+ self.rem_addr_bytes = 1
+ self.slave_addr_7 = addr_byte >> 1
+ self.slave_addr_10 = None
+ has_rw_bit = self.is_write is None
+ if self.is_write is None:
+ read_bit = bool(addr_byte & 1)
+ if self.options['address_format'] == 'shifted':
+ d >>= 1
+ self.is_write = False if read_bit else True
+ elif self.slave_addr_10 is not None:
+ self.slave_addr_10 |= addr_byte
+ else:
+ cls, texts = proto['WARN'][0], proto['WARN'][1:]
+ msg = 'Unhandled address byte'
+ texts = [t.format(text = msg) for t in texts]
+ self.putg(ss_byte, es_byte, cls, texts)
+ is_write = self.is_write
+ is_seven = self.slave_addr_7 is not None
+
+ # Determine annotation classes depending on whether the byte is
+ # an address or payload data, and whether it's written or read.
bin_class = -1
- if self.state == 'FIND ADDRESS' and self.wr == 1:
+ if is_address and is_write:
cmd = 'ADDRESS WRITE'
bin_class = 1
- elif self.state == 'FIND ADDRESS' and self.wr == 0:
+ elif is_address and not is_write:
cmd = 'ADDRESS READ'
bin_class = 0
- elif self.state == 'FIND DATA' and self.wr == 1:
+ elif not is_address and is_write:
cmd = 'DATA WRITE'
bin_class = 3
- elif self.state == 'FIND DATA' and self.wr == 0:
+ elif not is_address and not is_write:
cmd = 'DATA READ'
bin_class = 2
- self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
-
- self.putp(['BITS', self.bits])
- self.putp([cmd, d])
-
- self.putb([bin_class, bytes([d])])
-
- for bit in self.bits:
- self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]])
-
- if cmd.startswith('ADDRESS'):
- self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
- w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R']
- self.putx([proto[cmd][0], w])
- self.ss, self.es = self.ss_byte, self.samplenum
-
- self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], d),
- '%s: %02X' % (proto[cmd][2], d), '%02X' % d]])
-
- # Done with this packet.
- self.bitcount = self.databyte = 0
- self.bits = []
- self.state = 'FIND ACK'
-
- def get_ack(self, pins):
- scl, sda = pins
- self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
- cmd = 'NACK' if (sda == 1) else 'ACK'
- self.putp([cmd, None])
- self.putx([proto[cmd][0], proto[cmd][1:]])
- # There could be multiple data bytes in a row, so either find
- # another data byte or a STOP condition next.
- self.state = 'FIND DATA'
-
- def handle_stop(self, pins):
+ # Reverse the list of bits to LSB first order before emitting
+ # annotations and passing bits to upper layers. This may be
+ # unexpected because the protocol is MSB first, but it keeps
+ # backwards compatibility.
+ lsb_bits = self.data_bits[:]
+ lsb_bits.reverse()
+ self.putp(ss_byte, es_byte, ['BITS', lsb_bits])
+ self.putp(ss_byte, es_byte, [cmd, d])
+
+ self.putb(ss_byte, es_byte, [bin_class, bytes([d])])
+
+ for bit_value, ss_bit, es_bit in lsb_bits:
+ cls, texts = proto['BIT'][0], proto['BIT'][1:]
+ texts = [t.format(b = bit_value) for t in texts]
+ self.putg(ss_bit, es_bit, cls, texts)
+
+ if is_address and has_rw_bit:
+ # Assign the last bit's location to the R/W annotation.
+ # Adjust the address value's location to the left.
+ ss_bit, es_bit = self.data_bits[-1][1], self.data_bits[-1][2]
+ es_byte = self.data_bits[-2][2]
+ cls = proto[cmd][0]
+ w = ['Write', 'Wr', 'W'] if self.is_write else ['Read', 'Rd', 'R']
+ self.putg(ss_bit, es_bit, cls, w)
+
+ cls, texts = proto[cmd][0], proto[cmd][1:]
+ texts = [t.format(b = d) for t in texts]
+ self.putg(ss_byte, es_byte, cls, texts)
+
+ def get_ack(self, ss, es, value):
+ ss_bit, es_bit = ss, es
+ cmd = 'ACK' if value == 0 else 'NACK'
+ self.putp(ss_bit, es_bit, [cmd, None])
+ cls, texts = proto[cmd][0], proto[cmd][1:]
+ self.putg(ss_bit, es_bit, cls, texts)
+ # Slave addresses can span one or two bytes, before data bytes
+ # follow. There can be an arbitrary number of data bytes. Stick
+ # with getting more address bytes if applicable, or enter or
+ # remain in the data phase of the transfer otherwise.
+ if self.rem_addr_bytes:
+ self.rem_addr_bytes -= 1
+ self.data_bits.clear()
+
+ def handle_stop(self, ss, es):
# Meta bitrate
- if self.samplerate:
- elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
+ if self.samplerate and self.pdu_start:
+ elapsed = es - self.pdu_start + 1
+ elapsed /= self.samplerate
bitrate = int(1 / elapsed * self.pdu_bits)
- self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
+ ss_meta, es_meta = self.pdu_start, es
+ self.put(ss_meta, es_meta, self.out_bitrate, bitrate)
+ self.pdu_start = None
+ self.pdu_bits = 0
cmd = 'STOP'
- self.ss, self.es = self.samplenum, self.samplenum
- self.putp([cmd, None])
- self.putx([proto[cmd][0], proto[cmd][1:]])
- self.state = 'FIND START'
- self.is_repeat_start = 0
- self.wr = -1
- self.bits = []
+ self.putp(ss, es, [cmd, None])
+ cls, texts = proto[cmd][0], proto[cmd][1:]
+ self.putg(ss, es, cls, texts)
+ self.is_repeat_start = False
+ self.is_write = None
+ self.data_bits.clear()
def decode(self):
+ # Check for several bus conditions. Determine sample numbers
+ # here and pass ss, es, and bit values to handling routines.
while True:
# State machine.
- if self.state == 'FIND START':
+ # BEWARE! This implementation expects to see valid traffic,
+ # is rather picky in which phase which symbols get handled.
+ # This attempts to support severely undersampled captures,
+ # which a previous implementation happened to read instead
+ # of rejecting the inadequate input data.
+ # NOTE that handling bits at the start of their validity,
+ # and assuming that they remain valid until the next bit
+ # starts, is also done for backwards compatibility.
+ if self._wants_start():
# Wait for a START condition (S): SCL = high, SDA = falling.
- self.handle_start(self.wait({0: 'h', 1: 'f'}))
- elif self.state == 'FIND ADDRESS':
+ pins = self.wait({0: 'h', 1: 'f'})
+ ss, es = self.samplenum, self.samplenum
+ self.handle_start(ss, es)
+ elif self._collects_address() and self._collects_byte():
# Wait for a data bit: SCL = rising.
- self.handle_address_or_data(self.wait({0: 'r'}))
- elif self.state == 'FIND DATA':
+ pins = self.wait({0: 'r'})
+ _, sda = pins
+ ss, es = self.samplenum, self.samplenum + self.bitwidth
+ self.handle_address_or_data(ss, es, sda)
+ elif self._collects_byte():
# Wait for any of the following conditions (or combinations):
# a) Data sampling of receiver: SCL = rising, and/or
# b) START condition (S): SCL = high, SDA = falling, and/or
# Check which of the condition(s) matched and handle them.
if self.matched[0]:
- self.handle_address_or_data(pins)
+ _, sda = pins
+ ss, es = self.samplenum, self.samplenum + self.bitwidth
+ self.handle_address_or_data(ss, es, sda)
elif self.matched[1]:
- self.handle_start(pins)
+ ss, es = self.samplenum, self.samplenum
+ self.handle_start(ss, es)
elif self.matched[2]:
- self.handle_stop(pins)
- elif self.state == 'FIND ACK':
+ ss, es = self.samplenum, self.samplenum
+ self.handle_stop(ss, es)
+ else:
# Wait for a data/ack bit: SCL = rising.
- self.get_ack(self.wait({0: 'r'}))
+ pins = self.wait({0: 'r'})
+ _, sda = pins
+ ss, es = self.samplenum, self.samplenum + self.bitwidth
+ self.get_ack(ss, es, sda)
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
-# TODO: Support for filtering out multiple slave/direction pairs?
+# TODO
+# - Accept other slave address forms than decimal numbers?
+# - Support for filtering out multiple slave/direction pairs?
+# - Support 10bit slave addresses?
+import copy
import sigrokdecode as srd
class Decoder(srd.Decoder):
outputs = ['i2c']
tags = ['Util']
options = (
- {'id': 'address', 'desc': 'Address to filter out of the I²C stream',
+ {'id': 'address', 'desc': 'Slave address to filter (decimal)',
'default': 0},
{'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
'values': ('read', 'write', 'both')}
self.reset()
def reset(self):
- self.curslave = -1
- self.curdirection = None
- self.packets = [] # Local cache of I²C packets
+ self.seen_packets = []
+ self.do_forward = None
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON, proto_id='i2c')
if self.options['address'] not in range(0, 127 + 1):
raise Exception('Invalid slave (must be 0..127).')
+ self.want_addrs = []
+ if self.options['address']:
+ self.want_addrs.append(self.options['address'])
+ self.want_dir = {
+ 'read': 'READ', 'write': 'WRITE',
+ }.get(self.options['direction'], None)
- # Grab I²C packets into a local cache, until an I²C 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
- # I²C address of the slave that the master wants to talk to.
- # If that slave shall be filtered, output the cache (all packets from
- # START to STOP) as proto 'i2c', otherwise drop it.
- def decode(self, ss, es, data):
+ def _need_to_forward(self, slave_addr, direction):
+ if self.want_addrs and slave_addr not in self.want_addrs:
+ return False
+ if self.want_dir and direction != self.want_dir:
+ return False
+ return True
- cmd, databyte = data
+ # Accumulate observed I2C packets until a STOP or REPEATED START
+ # condition is seen. These are conditions where transfers end or
+ # where direction potentially changes. Forward all previously
+ # accumulated traffic if it passes the slave address and direction
+ # filter. This assumes that the slave address as well as the read
+ # or write direction was part of the observed traffic. There should
+ # be no surprise when incomplete traffic does not match the filter
+ # condition.
+ def decode(self, ss, es, data):
- # Add the I²C packet to our local cache.
- self.packets.append([ss, es, data])
+ # Unconditionally accumulate every lower layer packet we see.
+ # Keep deep copies for later, only reference caller's values
+ # as long as this .decode() invocation executes.
+ self.seen_packets.append([ss, es, copy.deepcopy(data)])
+ cmd, _ = data
+ # Check the slave address and transfer direction early when
+ # we see them. Keep accumulating packets while it's already
+ # known here whether to forward them. This simplifies other
+ # code paths. Including future handling of 10bit addresses.
if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
- self.curslave = databyte
- self.curdirection = cmd[8:].lower()
- elif cmd in ('STOP', 'START REPEAT'):
- # If this chunk was not for the correct slave, drop it.
- if self.options['address'] == 0:
- pass
- elif self.curslave != self.options['address']:
- self.packets = []
- return
-
- # If this chunk was not in the right direction, drop it.
- if self.options['direction'] == 'both':
- pass
- elif self.options['direction'] != self.curdirection:
- self.packets = []
- return
-
- # TODO: START->STOP chunks with both read and write (Repeat START)
- # Otherwise, send out the whole chunk of I²C packets.
- for p in self.packets:
- self.put(p[0], p[1], self.out_python, p[2])
+ direction = cmd[len('ADDRESS '):]
+ _, slave_addr = data
+ self.do_forward = self._need_to_forward(slave_addr, direction)
+ return
- self.packets = []
- else:
- pass # Do nothing, only add the I²C packet to our cache.
+ # Forward previously accumulated packets as we see their
+ # completion, and when they pass the filter condition. Prepare
+ # to handle the next transfer (the next read/write part of it).
+ if cmd in ('STOP', 'START REPEAT'):
+ if self.do_forward:
+ for ss, es, data in self.seen_packets:
+ self.put(ss, es, self.out_python, data)
+ self.seen_packets.clear()
+ self.do_forward = None
+ return
+++ /dev/null
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-'''
-This protocol decoder can decode the Commodore serial IEEE-488 (IEC) protocol.
-'''
-
-from .pd import Decoder
+++ /dev/null
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-import sigrokdecode as srd
-
-step_wait_conds = (
- [{2: 'f'}, {0: 'l', 1: 'h'}],
- [{2: 'f'}, {0: 'h', 1: 'h'}, {1: 'l'}],
- [{2: 'f'}, {0: 'f'}, {1: 'l'}],
- [{2: 'f'}, {1: 'e'}],
-)
-
-class Decoder(srd.Decoder):
- api_version = 3
- id = 'iec'
- name = 'IEC'
- longname = 'Commodore IEC bus'
- desc = 'Commodore serial IEEE-488 (IEC) bus protocol.'
- license = 'gplv2+'
- inputs = ['logic']
- outputs = []
- tags = ['PC', 'Retro computing']
- channels = (
- {'id': 'data', 'name': 'DATA', 'desc': 'Data I/O'},
- {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
- {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
- )
- optional_channels = (
- {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
- )
- annotations = (
- ('items', 'Items'),
- ('gpib', 'DAT/CMD'),
- ('eoi', 'EOI'),
- )
- annotation_rows = (
- ('bytes', 'Bytes', (0,)),
- ('gpib', 'DAT/CMD', (1,)),
- ('eoi', 'EOI', (2,)),
- )
-
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.saved_ATN = False
- self.saved_EOI = False
- self.ss_item = self.es_item = None
- self.step = 0
- self.bits = 0
- self.numbits = 0
-
- def start(self):
- self.out_ann = self.register(srd.OUTPUT_ANN)
-
- def putb(self, data):
- self.put(self.ss_item, self.es_item, self.out_ann, data)
-
- def handle_bits(self):
- # Output the saved item.
- dbyte = self.bits
- dATN = self.saved_ATN
- dEOI = self.saved_EOI
- self.es_item = self.samplenum
- self.putb([0, ['%02X' % dbyte]])
-
- # Encode item byte to GPIB convention.
- self.strgpib = ' '
- if dATN: # ATN, decode commands.
- # Note: Commands < 0x20 are not used on IEC bus.
- if dbyte == 0x01: self.strgpib = 'GTL'
- if dbyte == 0x04: self.strgpib = 'SDC'
- if dbyte == 0x05: self.strgpib = 'PPC'
- if dbyte == 0x08: self.strgpib = 'GET'
- if dbyte == 0x09: self.strgpib = 'TCT'
- if dbyte == 0x11: self.strgpib = 'LLO'
- if dbyte == 0x14: self.strgpib = 'DCL'
- if dbyte == 0x15: self.strgpib = 'PPU'
- if dbyte == 0x18: self.strgpib = 'SPE'
- if dbyte == 0x19: self.strgpib = 'SPD'
-
- if dbyte == 0x3f: self.strgpib = 'UNL'
- if dbyte == 0x5f: self.strgpib = 'UNT'
- if dbyte > 0x1f and dbyte < 0x3f: # Address listener.
- self.strgpib = 'L' + chr(dbyte + 0x10)
- if dbyte > 0x3f and dbyte < 0x5f: # Address talker.
- self.strgpib = 'T' + chr(dbyte - 0x10)
- if dbyte > 0x5f and dbyte < 0x70: # Channel reopen.
- self.strgpib = 'R' + chr(dbyte - 0x30)
- if dbyte > 0xdf and dbyte < 0xf0: # Channel close.
- self.strgpib = 'C' + chr(dbyte - 0xb0)
- if dbyte > 0xef: # Channel open.
- self.strgpib = 'O' + chr(dbyte - 0xc0)
- else:
- if dbyte > 0x1f and dbyte < 0x7f:
- self.strgpib = chr(dbyte)
- if dbyte == 0x0a:
- self.strgpib = 'LF'
- if dbyte == 0x0d:
- self.strgpib = 'CR'
-
- self.putb([1, [self.strgpib]])
- self.strEOI = ' '
- if dEOI:
- self.strEOI = 'EOI'
- self.putb([2, [self.strEOI]])
-
- def decode(self):
- while True:
-
- data, clk, atn, srq = self.wait(step_wait_conds[self.step])
-
- if self.matched[0]:
- # Falling edge on ATN, reset step.
- self.step = 0
-
- if self.step == 0:
- # Don't use self.matched[1] here since we might come from
- # a step with different conds due to the code above.
- if data == 0 and clk == 1:
- # Rising edge on CLK while DATA is low: Ready to send.
- self.step = 1
- elif self.step == 1:
- if data == 1 and clk == 1:
- # Rising edge on DATA while CLK is high: Ready for data.
- self.ss_item = self.samplenum
- self.saved_ATN = not atn
- self.saved_EOI = False
- self.bits = 0
- self.numbits = 0
- self.step = 2
- elif clk == 0:
- # CLK low again, transfer aborted.
- self.step = 0
- elif self.step == 2:
- if data == 0 and clk == 1:
- # DATA goes low while CLK is still high, EOI confirmed.
- self.saved_EOI = True
- elif clk == 0:
- self.step = 3
- elif self.step == 3:
- if self.matched[1]:
- if clk == 1:
- # Rising edge on CLK; latch DATA.
- self.bits |= data << self.numbits
- elif clk == 0:
- # Falling edge on CLK; end of bit.
- self.numbits += 1
- if self.numbits == 8:
- self.handle_bits()
- self.step = 0
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder can decode the GPIB (IEEE-488) protocol. Both variants
+of (up to) 16 parallel lines as well as serial communication (IEC bus) are
+supported in this implementation.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# This file was created from earlier implementations of the 'gpib' and
+# the 'iec' protocol decoders. It combines the parallel and the serial
+# transmission variants in a single instance with optional inputs for
+# maximum code re-use.
+
+# TODO
+# - Extend annotations for improved usability.
+# - Keep talkers' data streams on separate annotation rows? Is this useful
+# here at the GPIB level, or shall stacked decoders dispatch these? May
+# depend on how often captures get inspected which involve multiple peers.
+# - Make serial bit annotations optional? Could slow down interactive
+# exploration for long captures (see USB).
+# - Move the inlined Commodore IEC peripherals support to a stacked decoder
+# when more peripherals get added.
+# - SCPI over GPIB may "represent somewhat naturally" here already when
+# text lines are a single run of data at the GPIB layer (each line between
+# the address spec and either EOI or ATN). So a stacked SCPI decoder may
+# only become necessary when the text lines' content shall get inspected.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+
+'''
+OUTPUT_PYTHON format for stacked decoders:
+
+General packet format:
+[<ptype>, <addr>, <pdata>]
+
+This is the list of <ptype>s and their respective <pdata> values:
+
+Raw bits and bytes at the physical transport level:
+ - 'IEC_BIT': <addr> is not applicable, <pdata> is the transport's bit value.
+ - 'GPIB_RAW': <addr> is not applicable, <pdata> is the transport's
+ byte value. Data bytes are in the 0x00-0xff range, command/address
+ bytes are in the 0x100-0x1ff range.
+
+GPIB level byte fields (commands, addresses, pieces of data):
+ - 'COMMAND': <addr> is not applicable, <pdata> is the command's byte value.
+ - 'LISTEN': <addr> is the listener address (0-30), <pdata> is the raw
+ byte value (including the 0x20 offset).
+ - 'TALK': <addr> is the talker address (0-30), <pdata> is the raw byte
+ value (including the 0x40 offset).
+ - 'SECONDARY': <addr> is the secondary address (0-31), <pdata> is the
+ raw byte value (including the 0x60 offset).
+ - 'MSB_SET': <addr> as well as <pdata> are the raw byte value (including
+ the 0x80 offset). This usually does not happen for GPIB bytes with ATN
+ active, but was observed with the IEC bus and Commodore floppy drives,
+ when addressing channels within the device.
+ - 'DATA_BYTE': <addr> is the talker address (when available), <pdata>
+ is the raw data byte (transport layer, ATN inactive).
+ - 'PPOLL': <addr> is not applicable, <pdata> is a list of bit indices
+ (DIO1 to DIO8 order) which responded to the PP request.
+
+Extracted payload information (peers and their communicated data):
+ - 'TALK_LISTEN': <addr> is the current talker, <pdata> is the list of
+ current listeners. These updates for the current "connected peers"
+ are sent when the set of peers changes, i.e. after talkers/listeners
+ got selected or deselected. Of course the data only covers what could
+ be gathered from the input data. Some controllers may not explicitly
+ address themselves, or captures may not include an early setup phase.
+ - 'TALKER_BYTES': <addr> is the talker address (when available), <pdata>
+ is the accumulated byte sequence between addressing a talker and EOI,
+ or the next command/address.
+ - 'TALKER_TEXT': <addr> is the talker address (when available), <pdata>
+ is the accumulated text sequence between addressing a talker and EOI,
+ or the next command/address.
+'''
+
+class ChannelError(Exception):
+ pass
+
+def _format_ann_texts(fmts, **args):
+ if not fmts:
+ return None
+ return [fmt.format(**args) for fmt in fmts]
+
+_cmd_table = {
+ # Command codes in the 0x00-0x1f range.
+ 0x01: ['Go To Local', 'GTL'],
+ 0x04: ['Selected Device Clear', 'SDC'],
+ 0x05: ['Parallel Poll Configure', 'PPC'],
+ 0x08: ['Global Execute Trigger', 'GET'],
+ 0x09: ['Take Control', 'TCT'],
+ 0x11: ['Local Lock Out', 'LLO'],
+ 0x14: ['Device Clear', 'DCL'],
+ 0x15: ['Parallel Poll Unconfigure', 'PPU'],
+ 0x18: ['Serial Poll Enable', 'SPE'],
+ 0x19: ['Serial Poll Disable', 'SPD'],
+ # Unknown type of command.
+ None: ['Unknown command 0x{cmd:02x}', 'command 0x{cmd:02x}', 'cmd {cmd:02x}', 'C{cmd_ord:c}'],
+ # Special listener/talker "addresses" (deselecting previous peers).
+ 0x3f: ['Unlisten', 'UNL'],
+ 0x5f: ['Untalk', 'UNT'],
+}
+
+def _is_command(b):
+ # Returns a tuple of booleans (or None when not applicable) whether
+ # the raw GPIB byte is: a command, an un-listen, an un-talk command.
+ if b in range(0x00, 0x20):
+ return True, None, None
+ if b in range(0x20, 0x40) and (b & 0x1f) == 31:
+ return True, True, False
+ if b in range(0x40, 0x60) and (b & 0x1f) == 31:
+ return True, False, True
+ return False, None, None
+
+def _is_listen_addr(b):
+ if b in range(0x20, 0x40):
+ return b & 0x1f
+ return None
+
+def _is_talk_addr(b):
+ if b in range(0x40, 0x60):
+ return b & 0x1f
+ return None
+
+def _is_secondary_addr(b):
+ if b in range(0x60, 0x80):
+ return b & 0x1f
+ return None
+
+def _is_msb_set(b):
+ if b & 0x80:
+ return b
+ return None
+
+def _get_raw_byte(b, atn):
+ # "Decorate" raw byte values for stacked decoders.
+ return b | 0x100 if atn else b
+
+def _get_raw_text(b, atn):
+ return ['{leader}{data:02x}'.format(leader = '/' if atn else '', data = b)]
+
+def _get_command_texts(b):
+ fmts = _cmd_table.get(b, None)
+ known = fmts is not None
+ if not fmts:
+ fmts = _cmd_table.get(None, None)
+ if not fmts:
+ return known, None
+ return known, _format_ann_texts(fmts, cmd = b, cmd_ord = ord('0') + b)
+
+def _get_address_texts(b):
+ laddr = _is_listen_addr(b)
+ taddr = _is_talk_addr(b)
+ saddr = _is_secondary_addr(b)
+ msb = _is_msb_set(b)
+ fmts = None
+ if laddr is not None:
+ fmts = ['Listen {addr:d}', 'L {addr:d}', 'L{addr_ord:c}']
+ addr = laddr
+ elif taddr is not None:
+ fmts = ['Talk {addr:d}', 'T {addr:d}', 'T{addr_ord:c}']
+ addr = taddr
+ elif saddr is not None:
+ fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+ addr = saddr
+ elif msb is not None: # For IEC bus compat.
+ fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+ addr = msb
+ return _format_ann_texts(fmts, addr = addr, addr_ord = ord('0') + addr)
+
+def _get_data_text(b):
+ # TODO Move the table of ASCII control characters to a common location?
+ # TODO Move the "printable with escapes" logic to a common helper?
+ _control_codes = {
+ 0x00: 'NUL',
+ 0x01: 'SOH',
+ 0x02: 'STX',
+ 0x03: 'ETX',
+ 0x04: 'EOT',
+ 0x05: 'ENQ',
+ 0x06: 'ACK',
+ 0x07: 'BEL',
+ 0x08: 'BS',
+ 0x09: 'TAB',
+ 0x0a: 'LF',
+ 0x0b: 'VT',
+ 0x0c: 'FF',
+ 0x0d: 'CR',
+ 0x0e: 'SO',
+ 0x0f: 'SI',
+ 0x10: 'DLE',
+ 0x11: 'DC1',
+ 0x12: 'DC2',
+ 0x13: 'DC3',
+ 0x14: 'DC4',
+ 0x15: 'NAK',
+ 0x16: 'SYN',
+ 0x17: 'ETB',
+ 0x18: 'CAN',
+ 0x19: 'EM',
+ 0x1a: 'SUB',
+ 0x1b: 'ESC',
+ 0x1c: 'FS',
+ 0x1d: 'GS',
+ 0x1e: 'RS',
+ 0x1f: 'US',
+ }
+ # Yes, exclude 0x7f (DEL) here. It's considered non-printable.
+ if b in range(0x20, 0x7f) and b not in ('[', ']'):
+ return '{:s}'.format(chr(b))
+ elif b in _control_codes:
+ return '[{:s}]'.format(_control_codes[b])
+ # Use a compact yet readable and unambigous presentation for bytes
+ # which contain non-printables. The format that is used here is
+ # compatible with 93xx EEPROM and UART decoders.
+ return '[{:02x}]'.format(b)
+
+(
+ PIN_DIO1, PIN_DIO2, PIN_DIO3, PIN_DIO4,
+ PIN_DIO5, PIN_DIO6, PIN_DIO7, PIN_DIO8,
+ PIN_EOI, PIN_DAV, PIN_NRFD, PIN_NDAC,
+ PIN_IFC, PIN_SRQ, PIN_ATN, PIN_REN,
+ PIN_CLK,
+) = range(17)
+PIN_DATA = PIN_DIO1
+
+(
+ ANN_RAW_BIT, ANN_RAW_BYTE,
+ ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
+ ANN_EOI,
+ ANN_PP,
+ ANN_TEXT,
+ # TODO Want to provide one annotation class per talker address (0-30)?
+ ANN_IEC_PERIPH,
+ ANN_WARN,
+) = range(12)
+
+(
+ BIN_RAW,
+ BIN_DATA,
+ # TODO Want to provide one binary annotation class per talker address (0-30)?
+) = range(2)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ieee488'
+ name = 'IEEE-488'
+ longname = 'IEEE-488 GPIB/HPIB/IEC'
+ desc = 'IEEE-488 General Purpose Interface Bus (GPIB/HPIB or IEC).'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['ieee488']
+ tags = ['PC', 'Retro computing']
+ channels = (
+ {'id': 'dio1' , 'name': 'DIO1/DATA',
+ 'desc': 'Data I/O bit 1, or serial data'},
+ )
+ optional_channels = (
+ {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
+ {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
+ {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
+ {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
+ {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
+ {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
+ {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
+ {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
+ {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
+ {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
+ {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
+ {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
+ {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
+ {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
+ {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
+ )
+ options = (
+ {'id': 'iec_periph', 'desc': 'Decode Commodore IEC peripherals',
+ 'default': 'no', 'values': ('no', 'yes')},
+ {'id': 'delim', 'desc': 'Payload data delimiter',
+ 'default': 'eol', 'values': ('none', 'eol')},
+ {'id': 'atn_parity', 'desc': 'ATN commands use parity',
+ 'default': 'no', 'values': ('no', 'yes')},
+ )
+ annotations = (
+ ('bit', 'IEC bit'),
+ ('raw', 'Raw byte'),
+ ('cmd', 'Command'),
+ ('laddr', 'Listener address'),
+ ('taddr', 'Talker address'),
+ ('saddr', 'Secondary address'),
+ ('data', 'Data byte'),
+ ('eoi', 'EOI'),
+ ('pp', 'Parallel poll'),
+ ('text', 'Talker text'),
+ ('periph', 'IEC bus peripherals'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'IEC bits', (ANN_RAW_BIT,)),
+ ('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
+ ('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
+ ('eois', 'EOI', (ANN_EOI,)),
+ ('polls', 'Polls', (ANN_PP,)),
+ ('texts', 'Talker texts', (ANN_TEXT,)),
+ ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
+ ('warnings', 'Warnings', (ANN_WARN,)),
+ )
+ binary = (
+ ('raw', 'Raw bytes'),
+ ('data', 'Talker bytes'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.curr_raw = None
+ self.curr_atn = None
+ self.curr_eoi = None
+ self.latch_atn = None
+ self.latch_eoi = None
+ self.accu_bytes = []
+ self.accu_text = []
+ self.ss_raw = None
+ self.es_raw = None
+ self.ss_eoi = None
+ self.es_eoi = None
+ self.ss_text = None
+ self.es_text = None
+ self.ss_pp = None
+ self.last_talker = None
+ self.last_listener = []
+ self.last_iec_addr = None
+ self.last_iec_sec = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_bin = self.register(srd.OUTPUT_BINARY)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def putg(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def putbin(self, ss, es, data):
+ self.put(ss, es, self.out_bin, data)
+
+ def putpy(self, ss, es, ptype, addr, pdata):
+ self.put(ss, es, self.out_python, [ptype, addr, pdata])
+
+ def emit_eoi_ann(self, ss, es):
+ self.putg(ss, es, [ANN_EOI, ['EOI']])
+
+ def emit_bin_ann(self, ss, es, ann_cls, data):
+ self.putbin(ss, es, [ann_cls, bytes(data)])
+
+ def emit_data_ann(self, ss, es, ann_cls, data):
+ self.putg(ss, es, [ann_cls, data])
+
+ def emit_warn_ann(self, ss, es, data):
+ self.putg(ss, es, [ANN_WARN, data])
+
+ def flush_bytes_text_accu(self):
+ if self.accu_bytes and self.ss_text is not None and self.es_text is not None:
+ self.emit_bin_ann(self.ss_text, self.es_text, BIN_DATA, bytearray(self.accu_bytes))
+ self.putpy(self.ss_text, self.es_text, 'TALKER_BYTES', self.last_talker, bytearray(self.accu_bytes))
+ self.accu_bytes = []
+ if self.accu_text and self.ss_text is not None and self.es_text is not None:
+ text = ''.join(self.accu_text)
+ self.emit_data_ann(self.ss_text, self.es_text, ANN_TEXT, [text])
+ self.putpy(self.ss_text, self.es_text, 'TALKER_TEXT', self.last_talker, text)
+ self.accu_text = []
+ self.ss_text = self.es_text = None
+
+ def check_extra_flush(self, b):
+ # Optionally flush previously accumulated runs of payload data
+ # according to user specified conditions.
+ if self.options['delim'] == 'none':
+ return
+ if not self.accu_bytes:
+ return
+
+ # This implementation exlusively handles "text lines", but adding
+ # support for more variants here is straight forward.
+ #
+ # Search for the first data byte _after_ a user specified text
+ # line termination sequence was seen. The termination sequence's
+ # alphabet may be variable, and the sequence may span multiple
+ # data bytes. We accept either CR or LF, and combine the CR+LF
+ # sequence to strive for maximum length annotations for improved
+ # readability at different zoom levels. It's acceptable that this
+ # implementation would also combine multiple line terminations
+ # like LF+LF.
+ term_chars = (10, 13)
+ is_eol = b in term_chars
+ had_eol = self.accu_bytes[-1] in term_chars
+ if had_eol and not is_eol:
+ self.flush_bytes_text_accu()
+
+ def check_pp(self, dio = None):
+ # The combination of ATN and EOI means PP (parallel poll). Track
+ # this condition's start and end, and keep grabing the DIO lines'
+ # state as long as the condition is seen, since DAV is not used
+ # in the PP communication.
+ capture_in_pp = self.curr_atn and self.curr_eoi
+ decoder_in_pp = self.ss_pp is not None
+ if capture_in_pp and not decoder_in_pp:
+ # Phase starts. Track its ss. Start collecting DIO state.
+ self.ss_pp = self.samplenum
+ self.dio_pp = []
+ return 'enter'
+ if not capture_in_pp and decoder_in_pp:
+ # Phase ends. Void its ss. Process collected DIO state.
+ ss, es = self.ss_pp, self.samplenum
+ dio = self.dio_pp or []
+ self.ss_pp, self.dio_pp = None, None
+ if ss == es:
+ # False positive, caused by low oversampling.
+ return 'leave'
+ # Emit its annotation. Translate bit indices 0..7 for the
+ # DIO1..DIO8 signals to display text. Pass bit indices in
+ # the Python output for upper layers.
+ #
+ # TODO The presentation of this information may need more
+ # adjustment. The bit positions need not translate to known
+ # device addresses. Bits need not even belong to a single
+ # device. Participants and their location in the DIO pattern
+ # is configurable. Leave the interpretation to upper layers.
+ bits = [i for i, b in enumerate(dio) if b]
+ bits_text = ' '.join(['{}'.format(i + 1) for i in bits])
+ dios = ['DIO{}'.format(i + 1) for i in bits]
+ dios_text = ' '.join(dios or ['-'])
+ text = [
+ 'PPOLL {}'.format(dios_text),
+ 'PP {}'.format(bits_text),
+ 'PP',
+ ]
+ self.emit_data_ann(ss, es, ANN_PP, text)
+ self.putpy(ss, es, 'PPOLL', None, bits)
+ # Cease collecting DIO state.
+ return 'leave'
+ if decoder_in_pp:
+ # Keep collecting DIO state for each individual sample in
+ # the PP phase. Logically OR all DIO values that were seen.
+ # This increases robustness for low oversampling captures,
+ # where DIO may no longer be asserted when ATN/EOI deassert,
+ # and DIO was not asserted yet when ATN/EOI start asserting.
+ if dio is None:
+ dio = []
+ if len(dio) > len(self.dio_pp):
+ self.dio_pp.extend([ 0, ] * (len(dio) - len(self.dio_pp)))
+ for i, b in enumerate(dio):
+ self.dio_pp[i] |= b
+ return 'keep'
+ return 'idle'
+
+ def handle_ifc_change(self, ifc):
+ # Track IFC line for parallel input.
+ # Assertion of IFC de-selects all talkers and listeners.
+ if ifc:
+ self.last_talker = None
+ self.last_listener = []
+ self.flush_bytes_text_accu()
+
+ def handle_eoi_change(self, eoi):
+ # Track EOI line for parallel and serial input.
+ if eoi:
+ self.ss_eoi = self.samplenum
+ self.curr_eoi = eoi
+ else:
+ self.es_eoi = self.samplenum
+ if self.ss_eoi and self.latch_eoi:
+ self.emit_eoi_ann(self.ss_eoi, self.es_eoi)
+ self.es_text = self.es_eoi
+ self.flush_bytes_text_accu()
+ self.ss_eoi = self.es_eoi = None
+ self.curr_eoi = None
+
+ def handle_atn_change(self, atn):
+ # Track ATN line for parallel and serial input.
+ self.curr_atn = atn
+ if atn:
+ self.flush_bytes_text_accu()
+
+ def handle_iec_periph(self, ss, es, addr, sec, data):
+ # The annotation is optional.
+ if self.options['iec_periph'] != 'yes':
+ return
+ # Void internal state.
+ if addr is None and sec is None and data is None:
+ self.last_iec_addr = None
+ self.last_iec_sec = None
+ return
+ # Grab and evaluate new input.
+ _iec_addr_names = {
+ # TODO Add more items here. See the "Device numbering" section
+ # of the https://en.wikipedia.org/wiki/Commodore_bus page.
+ 8: 'Disk 0',
+ 9: 'Disk 1',
+ }
+ _iec_disk_range = range(8, 16)
+ if addr is not None:
+ self.last_iec_addr = addr
+ name = _iec_addr_names.get(addr, None)
+ if name:
+ self.emit_data_ann(ss, es, ANN_IEC_PERIPH, [name])
+ addr = self.last_iec_addr # Simplify subsequent logic.
+ if sec is not None:
+ # BEWARE! The secondary address is a full byte and includes
+ # the 0x60 offset, to also work when the MSB was set.
+ self.last_iec_sec = sec
+ subcmd, channel = sec & 0xf0, sec & 0x0f
+ channel_ord = ord('0') + channel
+ if addr is not None and addr in _iec_disk_range:
+ subcmd_fmts = {
+ 0x60: ['Reopen {ch:d}', 'Re {ch:d}', 'R{ch_ord:c}'],
+ 0xe0: ['Close {ch:d}', 'Cl {ch:d}', 'C{ch_ord:c}'],
+ 0xf0: ['Open {ch:d}', 'Op {ch:d}', 'O{ch_ord:c}'],
+ }.get(subcmd, None)
+ if subcmd_fmts:
+ texts = _format_ann_texts(subcmd_fmts, ch = channel, ch_ord = channel_ord)
+ self.emit_data_ann(ss, es, ANN_IEC_PERIPH, texts)
+ sec = self.last_iec_sec # Simplify subsequent logic.
+ if data is not None:
+ if addr is None or sec is None:
+ return
+ # TODO Process data depending on peripheral type and channel?
+
+ def handle_data_byte(self):
+ if not self.curr_atn:
+ self.check_extra_flush(self.curr_raw)
+ b = self.curr_raw
+ texts = _get_raw_text(b, self.curr_atn)
+ self.emit_data_ann(self.ss_raw, self.es_raw, ANN_RAW_BYTE, texts)
+ self.emit_bin_ann(self.ss_raw, self.es_raw, BIN_RAW, b.to_bytes(1, byteorder='big'))
+ self.putpy(self.ss_raw, self.es_raw, 'GPIB_RAW', None, _get_raw_byte(b, self.curr_atn))
+ if self.curr_atn:
+ ann_cls = None
+ upd_iec = False,
+ py_type = None
+ py_peers = False
+ if self.options['atn_parity'] == 'yes':
+ par = 1 if b & 0x80 else 0
+ b &= ~0x80
+ ones = bin(b).count('1') + par
+ if ones % 2:
+ warn_texts = ['Command parity error', 'parity', 'PAR']
+ self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
+ is_cmd, is_unl, is_unt = _is_command(b)
+ laddr = _is_listen_addr(b)
+ taddr = _is_talk_addr(b)
+ saddr = _is_secondary_addr(b)
+ msb = _is_msb_set(b)
+ if is_cmd:
+ known, texts = _get_command_texts(b)
+ if not known:
+ warn_texts = ['Unknown GPIB command', 'unknown', 'UNK']
+ self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
+ ann_cls = ANN_CMD
+ py_type, py_addr = 'COMMAND', None
+ if is_unl:
+ self.last_listener = []
+ py_peers = True
+ if is_unt:
+ self.last_talker = None
+ py_peers = True
+ if is_unl or is_unt:
+ upd_iec = True, None, None, None
+ elif laddr is not None:
+ addr = laddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_LADDR
+ py_type, py_addr = 'LISTEN', addr
+ if addr == self.last_talker:
+ self.last_talker = None
+ self.last_listener.append(addr)
+ upd_iec = True, addr, None, None
+ py_peers = True
+ elif taddr is not None:
+ addr = taddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_TADDR
+ py_type, py_addr = 'TALK', addr
+ if addr in self.last_listener:
+ self.last_listener.remove(addr)
+ self.last_talker = addr
+ upd_iec = True, addr, None, None
+ py_peers = True
+ elif saddr is not None:
+ addr = saddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_SADDR
+ upd_iec = True, None, b, None
+ py_type, py_addr = 'SECONDARY', addr
+ elif msb is not None:
+ # These are not really "secondary addresses", but they
+ # are used by the Commodore IEC bus (floppy channels).
+ texts = _get_address_texts(b)
+ ann_cls = ANN_SADDR
+ upd_iec = True, None, b, None
+ py_type, py_addr = 'MSB_SET', b
+ if ann_cls is not None and texts is not None:
+ self.emit_data_ann(self.ss_raw, self.es_raw, ann_cls, texts)
+ if upd_iec[0]:
+ self.handle_iec_periph(self.ss_raw, self.es_raw, upd_iec[1], upd_iec[2], upd_iec[3])
+ if py_type:
+ self.putpy(self.ss_raw, self.es_raw, py_type, py_addr, b)
+ if py_peers:
+ self.last_listener.sort()
+ self.putpy(self.ss_raw, self.es_raw, 'TALK_LISTEN', self.last_talker, self.last_listener)
+ else:
+ self.accu_bytes.append(b)
+ text = _get_data_text(b)
+ if not self.accu_text:
+ self.ss_text = self.ss_raw
+ self.accu_text.append(text)
+ self.es_text = self.es_raw
+ self.emit_data_ann(self.ss_raw, self.es_raw, ANN_DATA, [text])
+ self.handle_iec_periph(self.ss_raw, self.es_raw, None, None, b)
+ self.putpy(self.ss_raw, self.es_raw, 'DATA_BYTE', self.last_talker, b)
+
+ def handle_dav_change(self, dav, data):
+ if dav:
+ # Data availability starts when the flag goes active.
+ self.ss_raw = self.samplenum
+ self.curr_raw = bitpack(data)
+ self.latch_atn = self.curr_atn
+ self.latch_eoi = self.curr_eoi
+ return
+ # Data availability ends when the flag goes inactive. Handle the
+ # previously captured data byte according to associated flags.
+ self.es_raw = self.samplenum
+ self.handle_data_byte()
+ self.ss_raw = self.es_raw = None
+ self.curr_raw = None
+
+ def inject_dav_phase(self, ss, es, data):
+ # Inspection of serial input has resulted in one raw byte which
+ # spans a given period of time. Pretend we had seen a DAV active
+ # phase, to re-use code for the parallel transmission.
+ self.ss_raw = ss
+ self.curr_raw = bitpack(data)
+ self.latch_atn = self.curr_atn
+ self.latch_eoi = self.curr_eoi
+ self.es_raw = es
+ self.handle_data_byte()
+ self.ss_raw = self.es_raw = None
+ self.curr_raw = None
+
+ def invert_pins(self, pins):
+ # All lines (including data bits!) are low active and thus need
+ # to get inverted to receive their logical state (high active,
+ # regular data bit values). Cope with inputs being optional.
+ return [1 - p if p in (0, 1) else p for p in pins]
+
+ def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
+ if not has_clk or not has_data_1 or not has_atn:
+ raise ChannelError('IEC bus needs at least ATN and serial CLK and DATA.')
+
+ # This is a rephrased version of decoders/iec/pd.py:decode().
+ # SRQ was not used there either. Magic numbers were eliminated.
+ (
+ STEP_WAIT_READY_TO_SEND,
+ STEP_WAIT_READY_FOR_DATA,
+ STEP_PREP_DATA_TEST_EOI,
+ STEP_CLOCK_DATA_BITS,
+ ) = range(4)
+ step_wait_conds = (
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'l', PIN_CLK: 'h'}],
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'h', PIN_CLK: 'h'}, {PIN_CLK: 'l'}],
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'f'}, {PIN_CLK: 'l'}],
+ [{PIN_ATN: 'f'}, {PIN_CLK: 'e'}],
+ )
+ step = STEP_WAIT_READY_TO_SEND
+ bits = []
+
+ while True:
+
+ # Sample input pin values. Keep DATA/CLK in verbatim form to
+ # re-use 'iec' decoder logic. Turn ATN to positive logic for
+ # easier processing. The data bits get handled during byte
+ # accumulation.
+ pins = self.wait(step_wait_conds[step])
+ data, clk = pins[PIN_DATA], pins[PIN_CLK]
+ atn, = self.invert_pins([pins[PIN_ATN]])
+
+ if self.matched[0]:
+ # Falling edge on ATN, reset step.
+ step = STEP_WAIT_READY_TO_SEND
+
+ if step == STEP_WAIT_READY_TO_SEND:
+ # Don't use self.matched[1] here since we might come from
+ # a step with different conds due to the code above.
+ if data == 0 and clk == 1:
+ # Rising edge on CLK while DATA is low: Ready to send.
+ step = STEP_WAIT_READY_FOR_DATA
+ elif step == STEP_WAIT_READY_FOR_DATA:
+ if data == 1 and clk == 1:
+ # Rising edge on DATA while CLK is high: Ready for data.
+ ss_byte = self.samplenum
+ self.handle_atn_change(atn)
+ if self.curr_eoi:
+ self.handle_eoi_change(False)
+ bits = []
+ step = STEP_PREP_DATA_TEST_EOI
+ elif clk == 0:
+ # CLK low again, transfer aborted.
+ step = STEP_WAIT_READY_TO_SEND
+ elif step == STEP_PREP_DATA_TEST_EOI:
+ if data == 0 and clk == 1:
+ # DATA goes low while CLK is still high, EOI confirmed.
+ self.handle_eoi_change(True)
+ elif clk == 0:
+ step = STEP_CLOCK_DATA_BITS
+ ss_bit = self.samplenum
+ elif step == STEP_CLOCK_DATA_BITS:
+ if self.matched[1]:
+ if clk == 1:
+ # Rising edge on CLK; latch DATA.
+ bits.append(data)
+ elif clk == 0:
+ # Falling edge on CLK; end of bit.
+ es_bit = self.samplenum
+ self.emit_data_ann(ss_bit, es_bit, ANN_RAW_BIT, ['{:d}'.format(bits[-1])])
+ self.putpy(ss_bit, es_bit, 'IEC_BIT', None, bits[-1])
+ ss_bit = self.samplenum
+ if len(bits) == 8:
+ es_byte = self.samplenum
+ self.inject_dav_phase(ss_byte, es_byte, bits)
+ if self.curr_eoi:
+ self.handle_eoi_change(False)
+ step = STEP_WAIT_READY_TO_SEND
+
+ def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
+
+ if False in has_data_n or not has_dav or not has_atn:
+ raise ChannelError('IEEE-488 needs at least ATN and DAV and eight DIO lines.')
+ has_ifc = self.has_channel(PIN_IFC)
+
+ # Capture data lines at the falling edge of DAV, process their
+ # values at rising DAV edge (when data validity ends). Also make
+ # sure to start inspection when the capture happens to start with
+ # low signal levels, i.e. won't include the initial falling edge.
+ # Scan for ATN/EOI edges as well (including the trick which works
+ # around initial pin state).
+ #
+ # Use efficient edge based wait conditions for most activities,
+ # though some phases may require individual inspection of each
+ # sample (think parallel poll in combination with slow sampling).
+ #
+ # Map low-active physical transport lines to positive logic here,
+ # to simplify logical inspection/decoding of communicated data,
+ # and to avoid redundancy and inconsistency in later code paths.
+ waitcond = []
+ idx_dav = len(waitcond)
+ waitcond.append({PIN_DAV: 'l'})
+ idx_atn = len(waitcond)
+ waitcond.append({PIN_ATN: 'l'})
+ idx_eoi = None
+ if has_eoi:
+ idx_eoi = len(waitcond)
+ waitcond.append({PIN_EOI: 'l'})
+ idx_ifc = None
+ if has_ifc:
+ idx_ifc = len(waitcond)
+ waitcond.append({PIN_IFC: 'l'})
+ idx_pp_check = None
+ def add_data_cond(conds):
+ idx = len(conds)
+ conds.append({'skip': 1})
+ return idx
+ def del_data_cond(conds, idx):
+ conds.pop(idx)
+ return None
+ while True:
+ pins = self.wait(waitcond)
+ pins = self.invert_pins(pins)
+
+ # BEWARE! Order of evaluation does matter. For low samplerate
+ # captures, many edges fall onto the same sample number. So
+ # we process active edges of flags early (before processing
+ # data bits), and inactive edges late (after data got processed).
+ want_pp_check = False
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
+ self.handle_ifc_change(pins[PIN_IFC])
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
+ self.handle_eoi_change(pins[PIN_EOI])
+ want_pp_check = True
+ if self.matched[idx_atn] and pins[PIN_ATN] == 1:
+ self.handle_atn_change(pins[PIN_ATN])
+ want_pp_check = True
+ if want_pp_check and not idx_pp_check:
+ pp = self.check_pp()
+ if pp in ('enter',):
+ idx_pp_check = add_data_cond(waitcond)
+ if self.matched[idx_dav]:
+ self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
+ if idx_pp_check:
+ pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+ want_pp_check = False
+ if self.matched[idx_atn] and pins[PIN_ATN] == 0:
+ self.handle_atn_change(pins[PIN_ATN])
+ want_pp_check = True
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
+ self.handle_eoi_change(pins[PIN_EOI])
+ want_pp_check = True
+ if idx_pp_check is not None and want_pp_check:
+ pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+ if pp in ('leave',) and idx_pp_check is not None:
+ idx_pp_check = del_data_cond(waitcond, idx_pp_check)
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
+ self.handle_ifc_change(pins[PIN_IFC])
+
+ waitcond[idx_dav][PIN_DAV] = 'e'
+ waitcond[idx_atn][PIN_ATN] = 'e'
+ if has_eoi:
+ waitcond[idx_eoi][PIN_EOI] = 'e'
+ if has_ifc:
+ waitcond[idx_ifc][PIN_IFC] = 'e'
+
+ def decode(self):
+ # The decoder's boilerplate declares some of the input signals as
+ # optional, but only to support both serial and parallel variants.
+ # The CLK signal discriminates the two. For either variant some
+ # of the "optional" signals are not really optional for proper
+ # operation of the decoder. Check these conditions here.
+ has_clk = self.has_channel(PIN_CLK)
+ has_data_1 = self.has_channel(PIN_DIO1)
+ has_data_n = [bool(self.has_channel(pin) for pin in range(PIN_DIO1, PIN_DIO8 + 1))]
+ has_dav = self.has_channel(PIN_DAV)
+ has_atn = self.has_channel(PIN_ATN)
+ has_eoi = self.has_channel(PIN_EOI)
+ has_srq = self.has_channel(PIN_SRQ)
+ if has_clk:
+ self.decode_serial(has_clk, has_data_1, has_atn, has_srq)
+ else:
+ self.decode_parallel(has_data_n, has_dav, has_atn, has_eoi, has_srq)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Rene Staffen
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+IRMP is a multi protocol infrared remote protocol decoder. See
+https://www.mikrocontroller.net/articles/IRMP for details.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Python binding for the IRMP library.
+'''
+
+import ctypes
+import platform
+
+class IrmpLibrary:
+ '''
+ Library instance for an infrared protocol detector.
+ '''
+
+ __usable_instance = None
+
+ class ResultData(ctypes.Structure):
+ _fields_ = [
+ ( 'protocol', ctypes.c_uint32, ),
+ ( 'protocol_name', ctypes.c_char_p, ),
+ ( 'address', ctypes.c_uint32, ),
+ ( 'command', ctypes.c_uint32, ),
+ ( 'flags', ctypes.c_uint32, ),
+ ( 'start_sample', ctypes.c_uint32, ),
+ ( 'end_sample', ctypes.c_uint32, ),
+ ]
+
+ FLAG_REPETITION = 1 << 0
+ FLAG_RELEASE = 1 << 1
+
+ def _library_filename(self):
+ '''
+ Determine the library filename depending on the platform.
+ '''
+
+ if platform.uname()[0] == 'Linux':
+ return 'libirmp.so'
+ if platform.uname()[0] == 'Darwin':
+ return 'libirmp.dylib'
+ return 'irmp.dll'
+
+ def _library_setup_api(self):
+ '''
+ Lookup the C library's API routines. Declare their prototypes.
+ '''
+
+ self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
+ self._lib.irmp_get_sample_rate.argtypes = []
+
+ self._lib.irmp_instance_alloc.restype = ctypes.c_void_p
+ self._lib.irmp_instance_alloc.argtypes = []
+
+ self._lib.irmp_instance_free.restype = None
+ self._lib.irmp_instance_free.argtypes = [ ctypes.c_void_p, ]
+
+ self._lib.irmp_instance_id.restype = ctypes.c_size_t
+ self._lib.irmp_instance_id.argtypes = [ ctypes.c_void_p, ]
+
+ self._lib.irmp_instance_lock.restype = ctypes.c_int
+ self._lib.irmp_instance_lock.argtypes = [ ctypes.c_void_p, ctypes.c_int, ]
+
+ self._lib.irmp_instance_unlock.restype = None
+ self._lib.irmp_instance_unlock.argtypes = [ ctypes.c_void_p, ]
+
+ self._lib.irmp_reset_state.restype = None
+ self._lib.irmp_reset_state.argtypes = []
+
+ self._lib.irmp_add_one_sample.restype = ctypes.c_int
+ self._lib.irmp_add_one_sample.argtypes = [ ctypes.c_int, ]
+
+ if False:
+ self._lib.irmp_detect_buffer.restype = self.ResultData
+ self._lib.irmp_detect_buffer.argtypes = [ ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t, ]
+
+ self._lib.irmp_get_result_data.restype = ctypes.c_int
+ self._lib.irmp_get_result_data.argtypes = [ ctypes.POINTER(self.ResultData), ]
+
+ self._lib.irmp_get_protocol_name.restype = ctypes.c_char_p
+ self._lib.irmp_get_protocol_name.argtypes = [ ctypes.c_uint32, ]
+
+ # Create a result buffer that's local to the library instance.
+ self._data = self.ResultData()
+ self._inst = None
+
+ return True
+
+ def __init__(self):
+ '''
+ Create a library instance.
+ '''
+
+ filename = self._library_filename()
+ self._lib = ctypes.cdll.LoadLibrary(filename)
+ self._library_setup_api()
+
+ def __del__(self):
+ '''
+ Release a disposed library instance.
+ '''
+
+ if self._inst:
+ self._lib.irmp_instance_free(self._inst)
+ self._inst = None
+
+ def __enter__(self):
+ '''
+ Enter a context (lock management).
+ '''
+
+ if self._inst is None:
+ self._inst = self._lib.irmp_instance_alloc()
+ self._lib.irmp_instance_lock(self._inst, 1)
+ return self
+
+ def __exit__(self, extype, exvalue, trace):
+ '''
+ Leave a context (lock management).
+ '''
+
+ self._lib.irmp_instance_unlock(self._inst)
+ return False
+
+ def client_id(self):
+ return self._lib.irmp_instance_id(self._inst)
+
+ def get_sample_rate(self):
+ return self._lib.irmp_get_sample_rate()
+
+ def reset_state(self):
+ self._lib.irmp_reset_state()
+
+ def add_one_sample(self, level):
+ if not self._lib.irmp_add_one_sample(int(level)):
+ return False
+ self._lib.irmp_get_result_data(ctypes.byref(self._data))
+ return True
+
+ def get_result_data(self):
+ if not self._data:
+ return None
+ return {
+ 'proto_nr': self._data.protocol,
+ 'proto_name': self._data.protocol_name.decode('UTF-8', 'ignore'),
+ 'address': self._data.address,
+ 'command': self._data.command,
+ 'repeat': bool(self._data.flags & self.FLAG_REPETITION),
+ 'release': bool(self._data.flags & self.FLAG_RELEASE),
+ 'start': self._data.start_sample,
+ 'end': self._data.end_sample,
+ }
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Gump Yang <gump.yang@gmail.com>
+## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from . import irmp_library
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class LibraryError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ir_irmp'
+ name = 'IR IRMP'
+ longname = 'IR IRMP'
+ desc = 'IRMP infrared remote control multi protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['IR']
+ channels = (
+ {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
+ 'values': ('active-low', 'active-high')},
+ )
+ annotations = (
+ ('packet', 'Packet'),
+ )
+ annotation_rows = (
+ ('packets', 'IR Packets', (0,)),
+ )
+
+ def putframe(self, data):
+ '''Emit annotation for an IR frame.'''
+
+ # Cache result data fields in local variables. Get the ss/es
+ # timestamps, scaled to sample numbers.
+ nr = data['proto_nr']
+ name = data['proto_name']
+ addr = data['address']
+ cmd = data['command']
+ repeat = data['repeat']
+ release = data['release']
+ ss = data['start'] * self.rate_factor
+ es = data['end'] * self.rate_factor
+
+ # Prepare display texts for several zoom levels.
+ # Implementor's note: Keep list lengths for flags aligned during
+ # maintenance. Make sure there are as many flags text variants
+ # as are referenced by annotation text variants. Differing list
+ # lengths or dynamic refs will severely complicate the logic.
+ rep_txts = ['repeat', 'rep', 'r']
+ rel_txts = ['release', 'rel', 'R']
+ flag_txts = [None,] * len(rep_txts)
+ for zoom in range(len(flag_txts)):
+ flag_txts[zoom] = []
+ if repeat:
+ flag_txts[zoom].append(rep_txts[zoom])
+ if release:
+ flag_txts[zoom].append(rel_txts[zoom])
+ flag_txts = [' '.join(t) or '-' for t in flag_txts]
+ flg = flag_txts # Short name for .format() references.
+ txts = [
+ 'Protocol: {name} ({nr}), Address 0x{addr:04x}, Command: 0x{cmd:04x}, Flags: {flg[0]}'.format(**locals()),
+ 'P: {name} ({nr}), Addr: 0x{addr:x}, Cmd: 0x{cmd:x}, Flg: {flg[1]}'.format(**locals()),
+ 'P: {nr} A: 0x{addr:x} C: 0x{cmd:x} F: {flg[1]}'.format(**locals()),
+ 'C:{cmd:x} A:{addr:x} {flg[2]}'.format(**locals()),
+ 'C:{cmd:x}'.format(**locals()),
+ ]
+
+ # Emit the annotation from details which were constructed above.
+ self.put(ss, es, self.out_ann, [0, txts])
+
+ def __init__(self):
+ self.irmp = None
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def decode(self):
+ if not self.irmp:
+ try:
+ self.irmp = irmp_library.IrmpLibrary()
+ except Exception as e:
+ txt = e.args[0]
+ raise LibraryError(txt)
+ if not self.irmp:
+ raise LibraryError('Cannot access IRMP library.')
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ lib_rate = self.irmp.get_sample_rate()
+ if not lib_rate:
+ raise LibraryError('Cannot determine IRMP library\'s samplerate.')
+ if self.samplerate % lib_rate:
+ raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(lib_rate))
+
+ self.rate_factor = int(self.samplerate / lib_rate)
+ active = 0 if self.options['polarity'] == 'active-low' else 1
+
+ ir, = self.wait()
+ with self.irmp:
+ self.irmp.reset_state()
+ while True:
+ if active == 1:
+ ir = 1 - ir
+ if self.irmp.add_one_sample(ir):
+ data = self.irmp.get_result_data()
+ self.putframe(data)
+ ir, = self.wait([{'skip': self.rate_factor}])
'''
NEC is a pulse-distance based infrared remote control protocol.
+See https://www.sbprojects.net/knowledge/ir/nec.php for a description.
'''
from .pd import Decoder
# Addresses/devices. Items that are not listed are reserved/unknown.
address = {
+ 0x00: 'Joy-it SBC-IRC01',
0x40: 'Matsui TV',
+ 0xEA41: 'Unknown LED Panel',
}
digits = {
31: ['Program down', 'P-'],
68: ['AV', 'AV'],
}.items())),
+
+ # This is most likely a generic remote control. The PCB
+ # has space for 16 buttons total, of which not all are
+ # connected. The PCB is marked "JSY", "XSK-5462", and
+ # "2014-6-12 JW". It consists of only a single IC, marked
+ # "BJEC107BNE" or similar. The following buttons are
+ # marked for the remote control of a LED panel this was
+ # found in.
+ 0xEA41: {
+ 0x10: ['Warmer', 'T+'],
+ 0x11: ['Colder', 'T-'],
+ 0x12: ['Brighter', '+'],
+ 0x13: ['Darker', '-'],
+ 0x14: ['Off', 'O'],
+ 0x15: ['On', 'I'],
+ 0x41: ['Min Brightness', 'Min'],
+ 0x48: ['Max Brightness', 'Max'],
+ },
+ 0x00: {
+ 0x45: ['Volume down', 'Vol-'],
+ 0x46: ['Play/Pause', 'P/P'],
+ 0x47: ['Volume up', 'Vol+'],
+ 0x44: ['Setup', 'Set'],
+ 0x40: ['Up', 'U'],
+ 0x43: ['Stop / Mode', 'S/M'],
+ 0x07: ['Left', 'L'],
+ 0x15: ['Enter', 'E'],
+ 0x09: ['Right', 'R'],
+ 0x16: ['0 / 10+', '0'],
+ 0x19: ['Down', 'D'],
+ 0x0D: ['Back', 'B'],
+ 0x0C: ['1', '1'],
+ 0x18: ['2', '2'],
+ 0x5E: ['3', '3'],
+ 0x08: ['4', '4'],
+ 0x1C: ['5', '5'],
+ 0x5A: ['6', '6'],
+ 0x42: ['7', '7'],
+ 0x52: ['8', '8'],
+ 0x4A: ['9', '9'],
+ }
}
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
-import sigrokdecode as srd
+from common.srdhelper import bitpack
from .lists import *
+import sigrokdecode as srd
+
+# Concentrate all timing constraints of the IR protocol here in a single
+# location at the top of the source, to raise awareness and to simplify
+# review and adjustment. The tolerance is an arbitrary choice, available
+# literature does not mention any. The inter-frame timeout is not a part
+# of the protocol, but an implementation detail of this sigrok decoder.
+_TIME_TOL = 8 # tolerance, in percent
+_TIME_IDLE = 20.0 # inter-frame timeout, in ms
+_TIME_LC = 13.5 # leader code, in ms
+_TIME_RC = 11.25 # repeat code, in ms
+_TIME_ONE = 2.25 # one data bit, in ms
+_TIME_ZERO = 1.125 # zero data bit, in ms
+_TIME_STOP = 0.562 # stop bit, in ms
class SamplerateError(Exception):
pass
+class Pin:
+ IR, = range(1)
+
+class Ann:
+ BIT, AGC, LONG_PAUSE, SHORT_PAUSE, STOP_BIT, \
+ LEADER_CODE, ADDR, ADDR_INV, CMD, CMD_INV, REPEAT_CODE, \
+ REMOTE, WARN = range(13)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'ir_nec'
)
options = (
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
- 'values': ('active-low', 'active-high')},
+ 'values': ('auto', 'active-low', 'active-high')},
{'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
+ {'id': 'extended', 'desc': 'Extended NEC Protocol',
+ 'default': 'no', 'values': ('yes', 'no')},
)
annotations = (
('bit', 'Bit'),
('cmd-inv', 'Command#'),
('repeat-code', 'Repeat code'),
('remote', 'Remote'),
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('bits', 'Bits', (0, 1, 2, 3, 4)),
- ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
- ('remote', 'Remote', (11,)),
- ('warnings', 'Warnings', (12,)),
+ ('bits', 'Bits', (Ann.BIT, Ann.AGC, Ann.LONG_PAUSE, Ann.SHORT_PAUSE, Ann.STOP_BIT)),
+ ('fields', 'Fields', (Ann.LEADER_CODE, Ann.ADDR, Ann.ADDR_INV, Ann.CMD, Ann.CMD_INV, Ann.REPEAT_CODE)),
+ ('remote-vals', 'Remote', (Ann.REMOTE,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
)
def putx(self, data):
def putb(self, data):
self.put(self.ss_bit, self.samplenum, self.out_ann, data)
- def putd(self, data):
+ def putd(self, data, bit_count):
name = self.state.title()
- d = {'ADDRESS': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
+ d = {'ADDRESS': Ann.ADDR, 'ADDRESS#': Ann.ADDR_INV,
+ 'COMMAND': Ann.CMD, 'COMMAND#': Ann.CMD_INV}
s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
- self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
- '%s: 0x%02X' % (s[self.state][0], data),
- '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
+ fmt = '{{}}: 0x{{:0{}X}}'.format(bit_count // 4)
+ self.putx([d[self.state], [
+ fmt.format(name, data),
+ fmt.format(s[self.state][0], data),
+ fmt.format(s[self.state][1], data),
+ s[self.state][1],
+ ]])
def putstop(self, ss):
self.put(ss, ss + self.stop, self.out_ann,
- [4, ['Stop bit', 'Stop', 'St', 'S']])
+ [Ann.STOP_BIT, ['Stop bit', 'Stop', 'St', 'S']])
def putpause(self, p):
self.put(self.ss_start, self.ss_other_edge, self.out_ann,
- [1, ['AGC pulse', 'AGC', 'A']])
- idx = 2 if p == 'Long' else 3
- self.put(self.ss_other_edge, self.samplenum, self.out_ann,
- [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
+ [Ann.AGC, ['AGC pulse', 'AGC', 'A']])
+ idx = Ann.LONG_PAUSE if p == 'Long' else Ann.SHORT_PAUSE
+ self.put(self.ss_other_edge, self.samplenum, self.out_ann, [idx, [
+ '{} pause'.format(p),
+ '{}-pause'.format(p[0]),
+ '{}P'.format(p[0]),
+ 'P',
+ ]])
def putremote(self):
dev = address.get(self.addr, 'Unknown device')
- buttons = command.get(self.addr, None)
- if buttons is None:
- btn = ['Unknown', 'Unk']
- else:
- btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
- self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
- [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
- '%s' % btn[1]]])
+ buttons = command.get(self.addr, {})
+ btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
+ self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann, [Ann.REMOTE, [
+ '{}: {}'.format(dev, btn[0]),
+ '{}: {}'.format(dev, btn[1]),
+ '{}'.format(btn[1]),
+ ]])
def __init__(self):
self.reset()
def reset(self):
self.state = 'IDLE'
self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
- self.data = self.count = self.active = None
+ self.data = []
self.addr = self.cmd = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.active = 0 if self.options['polarity'] == 'active-low' else 1
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.tolerance = 0.05 # +/-5%
- self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
- self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
- self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
- self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
- self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
+
+ def calc_rate(self):
+ self.tolerance = _TIME_TOL / 100
+ self.lc = int(self.samplerate * _TIME_LC / 1000) - 1
+ self.rc = int(self.samplerate * _TIME_RC / 1000) - 1
+ self.dazero = int(self.samplerate * _TIME_ZERO / 1000) - 1
+ self.daone = int(self.samplerate * _TIME_ONE / 1000) - 1
+ self.stop = int(self.samplerate * _TIME_STOP / 1000) - 1
+ self.idle_to = int(self.samplerate * _TIME_IDLE / 1000) - 1
def compare_with_tolerance(self, measured, base):
return (measured >= base * (1 - self.tolerance)
elif self.compare_with_tolerance(tick, self.daone):
ret = 1
if ret in (0, 1):
- self.putb([0, ['%d' % ret]])
- self.data |= (ret << self.count) # LSB-first
- self.count = self.count + 1
+ self.putb([Ann.BIT, ['{:d}'.format(ret)]])
+ self.data.append(ret)
self.ss_bit = self.samplenum
- def data_ok(self):
- ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
- if self.count == 8:
+ def data_ok(self, check, want_len):
+ name = self.state.title()
+ normal, inverted = bitpack(self.data[:8]), bitpack(self.data[8:])
+ valid = (normal ^ inverted) == 0xff
+ show = inverted if self.state.endswith('#') else normal
+ is_ext_addr = self.is_extended and self.state == 'ADDRESS'
+ if is_ext_addr:
+ normal = bitpack(self.data)
+ show = normal
+ valid = True
+ if len(self.data) == want_len:
if self.state == 'ADDRESS':
- self.addr = self.data
+ self.addr = normal
if self.state == 'COMMAND':
- self.cmd = self.data
- self.putd(self.data)
+ self.cmd = normal
+ self.putd(show, want_len)
self.ss_start = self.samplenum
+ if is_ext_addr:
+ self.data = []
+ self.ss_bit = self.ss_start = self.samplenum
return True
- if ret == 0:
- self.putd(self.data >> 8)
- else:
- self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
- self.data = self.count = 0
+ self.putd(show, want_len)
+ if check and not valid:
+ warn_show = bitpack(self.data)
+ self.putx([Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]])
+ self.data = []
self.ss_bit = self.ss_start = self.samplenum
- return ret == 0
+ return valid
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
+ self.calc_rate()
cd_count = None
if self.options['cd_freq']:
cd_count = int(self.samplerate / self.options['cd_freq']) + 1
- prev_ir = None
+ prev_ir = None
+
+ if self.options['polarity'] == 'auto':
+ # Take sample 0 as reference.
+ curr_level, = self.wait({'skip': 0})
+ active = 1 - curr_level
+ else:
+ active = 0 if self.options['polarity'] == 'active-low' else 1
+ self.is_extended = self.options['extended'] == 'yes'
+ want_addr_len = 16 if self.is_extended else 8
while True:
# Detect changes in the presence of an active input signal.
# active period, but will shift their signal changes by one
# carrier period before they get passed to decoding logic.
if cd_count:
- (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
+ (cur_ir,) = self.wait([{Pin.IR: 'e'}, {'skip': cd_count}])
if self.matched[0]:
- cur_ir = self.active
+ cur_ir = active
if cur_ir == prev_ir:
continue
prev_ir = cur_ir
self.ir = cur_ir
else:
- (self.ir,) = self.wait({0: 'e'})
+ (self.ir,) = self.wait({Pin.IR: 'e'})
- if self.ir != self.active:
- # Save the non-active edge, then wait for the next edge.
+ if self.ir != active:
+ # Save the location of the non-active edge (recessive),
+ # then wait for the next edge. Immediately process the
+ # end of the STOP bit which completes an IR frame.
self.ss_other_edge = self.samplenum
- continue
+ if self.state != 'STOP':
+ continue
- b = self.samplenum - self.ss_bit
+ # Reset internal state for long periods of idle level.
+ width = self.samplenum - self.ss_bit
+ if width >= self.idle_to and self.state != 'STOP':
+ self.reset()
# State machine.
if self.state == 'IDLE':
- if self.compare_with_tolerance(b, self.lc):
+ if self.compare_with_tolerance(width, self.lc):
self.putpause('Long')
- self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
+ self.putx([Ann.LEADER_CODE, ['Leader code', 'Leader', 'LC', 'L']])
self.ss_remote = self.ss_start
- self.data = self.count = 0
+ self.data = []
self.state = 'ADDRESS'
- elif self.compare_with_tolerance(b, self.rc):
+ elif self.compare_with_tolerance(width, self.rc):
self.putpause('Short')
self.putstop(self.samplenum)
self.samplenum += self.stop
- self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
- self.data = self.count = 0
+ self.putx([Ann.REPEAT_CODE, ['Repeat code', 'Repeat', 'RC', 'R']])
+ self.data = []
self.ss_bit = self.ss_start = self.samplenum
elif self.state == 'ADDRESS':
- self.handle_bit(b)
- if self.count == 8:
- self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == want_addr_len:
+ self.data_ok(False, want_addr_len)
+ self.state = 'COMMAND' if self.is_extended else 'ADDRESS#'
elif self.state == 'ADDRESS#':
- self.handle_bit(b)
- if self.count == 16:
- self.state = 'COMMAND' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 16:
+ self.data_ok(True, 8)
+ self.state = 'COMMAND'
elif self.state == 'COMMAND':
- self.handle_bit(b)
- if self.count == 8:
- self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 8:
+ self.data_ok(False, 8)
+ self.state = 'COMMAND#'
elif self.state == 'COMMAND#':
- self.handle_bit(b)
- if self.count == 16:
- self.state = 'STOP' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 16:
+ self.data_ok(True, 8)
+ self.state = 'STOP'
elif self.state == 'STOP':
self.putstop(self.ss_bit)
self.putremote()
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.old_ir = 1 if self.options['polarity'] == 'active-low' else 0
+ self.next_edge = 'l' if self.options['polarity'] == 'active-low' else 'h'
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
raise SamplerateError('Cannot decode without samplerate.')
while True:
- (self.ir,) = self.wait()
-
- # Wait for any edge (rising or falling).
- if self.old_ir == self.ir:
- continue
+ (self.ir,) = self.wait({0: self.next_edge})
# State machine.
if self.state == 'IDLE':
self.edges.append(self.samplenum)
self.bits.append([self.samplenum, bit])
self.state = 'MID1'
- self.old_ir = self.ir
+ self.next_edge = 'l' if self.ir else 'h'
continue
edge = self.edge_type()
if edge == 'e':
self.handle_bits()
self.reset_decoder_state()
- self.old_ir = self.ir
+ self.next_edge = 'l' if self.ir else 'h'
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+RC-6 is a biphase/manchester based infrared remote control protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ir_rc6'
+ name = 'IR RC-6'
+ longname = 'IR RC-6'
+ desc = 'RC-6 infrared remote control protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['IR']
+ channels = (
+ {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Polarity', 'default': 'auto',
+ 'values': ('auto', 'active-low', 'active-high')},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('sync', 'Sync'),
+ ('startbit', 'Startbit'),
+ ('field', 'Field'),
+ ('togglebit', 'Togglebit'),
+ ('address', 'Address'),
+ ('command', 'Command'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0,)),
+ ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.edges, self.deltas, self.bits = [], [], []
+ self.state = 'IDLE'
+ self.mode = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ # One bit: 0.889ms (one half low, one half high).
+ self.halfbit = int((self.samplerate * 0.000889) / 2.0)
+
+ def putb(self, bit, data):
+ self.put(bit[0], bit[1], self.out_ann, data)
+
+ def putbits(self, bit1, bit2, data):
+ self.put(bit1[0], bit2[1], self.out_ann, data)
+
+ def putx(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def handle_bit(self):
+ if len(self.bits) != 6:
+ return
+ if self.bits[0][2] == 8 and self.bits[0][3] == 1:
+ self.putb(self.bits[0], [1, ['Synchronisation', 'Sync']])
+ else:
+ return
+ if self.bits[1][3] == 1:
+ self.putb(self.bits[1], [2, ['Startbit', 'Start']])
+ else:
+ return
+ self.mode = sum([self.bits[2 + i][3] << (2 - i) for i in range(3)])
+ self.putbits(self.bits[2], self.bits[4], [3, ['Field: %d' % self.mode]])
+ self.putb(self.bits[5], [4, ['Toggle: %d' % self.bits[5][3]]])
+
+ def handle_package(self):
+ # Sync and start bits have to be 1.
+ if self.bits[0][3] == 0 or self.bits[1][3] == 0:
+ return
+ if len(self.bits) <= 6:
+ return
+
+ if self.mode == 0 and len(self.bits) == 22: # Mode 0 standard
+ value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+ value = sum([self.bits[14 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[14], self.bits[21], [6, ['Data: %0.2X' % value]])
+
+ self.bits = []
+
+ if self.mode == 6 and len(self.bits) >= 15: # Mode 6
+ if self.bits[6][3] == 0: # Short addr, Mode 6A
+ value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+ num_data_bits = len(self.bits) - 14
+ value = sum([self.bits[14 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+ self.putbits(self.bits[14], self.bits[-1], [6, ['Data: %X' % value]])
+
+ self.bits = []
+
+ elif len(self.bits) >= 23: # Long addr, Mode 6B
+ value = sum([self.bits[6 + i][3] << (15 - i) for i in range(16)])
+ self.putbits(self.bits[6], self.bits[21], [5, ['Address: %0.2X' % value]])
+
+ num_data_bits = len(self.bits) - 22
+ value = sum([self.bits[22 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+ self.putbits(self.bits[22], self.bits[-1], [6, ['Data: %X' % value]])
+
+ self.bits = []
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ value = 0
+ num_edges = -1
+ self.invert = False
+
+ while True:
+ conditions = [{0: 'e'}]
+ if self.state == 'DATA':
+ conditions.append({'skip': self.halfbit * 6})
+ (self.ir,) = self.wait(conditions)
+
+ if len(conditions) == 2:
+ if self.matched[1]:
+ self.state = 'IDLE'
+
+ self.edges.append(self.samplenum)
+ if len(self.edges) < 2:
+ continue
+
+ delta = (self.edges[-1] - self.edges[-2]) / self.halfbit
+ delta = int(delta + 0.5)
+ self.deltas.append(delta)
+
+ if len(self.deltas) < 2:
+ continue
+
+ if self.deltas[-2:] == [6, 2]:
+ self.state = 'SYNC'
+ num_edges = 0
+ self.bits = []
+
+ if self.options['polarity'] == 'auto':
+ value = 1
+ else:
+ value = self.ir if self.options['polarity'] == 'active-high' else 1 - self.ir
+
+ self.bits.append((self.edges[-3], self.edges[-1], 8, value))
+ self.invert = self.ir == 0
+ self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+ if (num_edges % 2) == 0: # Only count every second edge.
+ if self.deltas[-2] in [1, 2, 3] and self.deltas[-1] in [1, 2, 3, 6]:
+ self.state = 'DATA'
+ if self.deltas[-2] != self.deltas[-1]:
+ # Insert border between 2 bits.
+ self.edges.insert(-1, self.edges[-2] + self.deltas[-2] * self.halfbit)
+ total = self.deltas[-1]
+ self.deltas[-1] = self.deltas[-2]
+ self.deltas.append(total - self.deltas[-1])
+
+ self.bits.append((self.edges[-4], self.edges[-2], self.deltas[-2] * 2, value))
+
+ num_edges += 1
+ else:
+ self.bits.append((self.edges[-3], self.edges[-1], self.deltas[-1] * 2, value))
+
+ self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+ if len(self.bits) > 0:
+ self.handle_bit()
+ if self.state == 'IDLE':
+ self.handle_package()
+
+ if self.options['polarity'] == 'auto':
+ value = self.ir if self.invert else 1 - self.ir
+ else:
+ value = self.ir if self.options['polarity'] == 'active-low' else 1 - self.ir
+
+ num_edges += 1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Decoder for the Sony IR remote control protocol (SIRC).
+
+https://www.sbprojects.net/knowledge/ir/sirc.php
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+NUMBERS = {
+ 0x00: '1',
+ 0x01: '2',
+ 0x02: '3',
+ 0x03: '4',
+ 0x04: '5',
+ 0x05: '6',
+ 0x06: '7',
+ 0x07: '8',
+ 0x08: '9',
+ 0x09: '0/10',
+}
+
+ADDRESSES = {
+ # TV
+ (0x01, None): (['TV: ', 'TV:'], {
+ 0x15: 'Power',
+ 0x25: 'Input',
+
+ 0x33: 'Right',
+ 0x34: 'Left',
+ 0x3A: 'Display',
+
+ 0x60: 'Home',
+ 0x65: 'Enter',
+
+ 0x74: 'Up',
+ 0x75: 'Down',
+
+ }),
+
+ # Video
+ (0x0B, None): (['Video: ', 'V:'], {
+ 0x18: 'Stop',
+ 0x19: 'Pause',
+ 0x1A: 'Play',
+ 0x1B: 'Rewind',
+ 0x1C: 'Fast Forward',
+
+ 0x42: 'Up',
+ 0x43: 'Down',
+ 0x4D: 'Home',
+
+ 0x51: 'Enter',
+ 0x5A: 'Display',
+
+ 0x61: 'Right',
+ 0x62: 'Left',
+ }),
+
+ # BR Input select
+ (0x10, 0x28): (['BlueRay: ', 'BR:'], {
+ 0x16: 'BlueRay',
+ }),
+
+ # Amp, Game, Sat, Tuner, USB
+ (0x10, 0x08): (['Playback: ', 'PB:'], {
+ 0x2A: 'Shuffle',
+ 0x2C: 'Repeat',
+ 0x2E: 'Folder Down',
+ 0x2F: 'Folder Up',
+
+ 0x30: 'Previous',
+ 0x31: 'Next',
+ 0x32: 'Play',
+ 0x33: 'Rewind',
+ 0x34: 'Fast Forward',
+ 0x38: 'Stop',
+ 0x39: 'Pause',
+
+ 0x73: 'Options',
+ 0x7D: 'Return',
+ }),
+
+ # CD
+ (0x11, None): (['CD: ', 'CD:'], {
+ 0x28: 'Display',
+
+ 0x30: 'Previous',
+ 0x31: 'Next',
+ 0x32: 'Play',
+ 0x33: 'Rewind',
+ 0x34: 'Fast Forward',
+ 0x38: 'Stop',
+ 0x39: 'Pause',
+ }),
+
+ # BD
+ (0x1A, 0xE2): (['BlueRay: ', 'BD:'], {
+ 0x18: 'Stop',
+ 0x19: 'Pause',
+ 0x1A: 'Play',
+ 0x1B: 'Rewind',
+ 0x1C: 'Fast Forward',
+
+ 0x29: 'Menu',
+ 0x2C: 'Top Menu',
+
+ 0x39: 'Up',
+ 0x3A: 'Down',
+ 0x3B: 'Left',
+ 0x3C: 'Right',
+ 0x3D: 'Enter',
+ 0x3F: 'Options',
+
+ 0x41: 'Display',
+ 0x42: 'Home',
+ 0x43: 'Return',
+
+ 0x56: 'Next',
+ 0x57: 'Previous',
+ }),
+
+ # DVD
+ (0x1A, 0x49): (['DVD: ', 'DVD:'], {
+ 0x0B: 'Enter',
+ 0x0E: 'Return',
+ 0x17: 'Options',
+
+ 0x1A: 'Top Menu',
+ 0x1B: 'Menu',
+
+ 0x30: 'Previous',
+ 0x31: 'Next',
+ 0x32: 'Play',
+ 0x33: 'Rewind',
+ 0x34: 'Fast Forward',
+ 0x38: 'Stop',
+ 0x39: 'Pause',
+
+ 0x54: 'Display',
+
+ 0x7B: 'Left',
+ 0x7C: 'Right',
+ 0x79: 'Up',
+ 0x7A: 'Down',
+ }),
+
+ # Amp, Game, Sat, Tuner, USB modes
+ (0x30, None): (['Keypad: ', 'KP:'], {
+ 0x0C: 'Enter',
+
+ 0x12: 'Volume Up',
+ 0x13: 'Volume Down',
+ 0x14: 'Mute',
+ 0x15: 'Power',
+
+ 0x21: 'Tuner',
+ 0x22: 'Video',
+ 0x25: 'CD',
+
+ 0x4D: 'Home',
+ 0x4B: 'Display',
+
+ 0x60: 'Sleep',
+ 0x6A: 'TV',
+
+ 0x53: 'Home',
+
+ 0x7C: 'Game',
+ 0x7D: 'DVD',
+ }),
+
+ # Amp, Game, Sat, Tuner, USB modes
+ (0xB0, None): (['Arrows: ', 'Ar:'], {
+ 0x7A: 'Left',
+ 0x7B: 'Right',
+ 0x78: 'Up',
+ 0x79: 'Down',
+ 0x77: 'Amp Menu',
+ }),
+
+ # TV mode
+ (0x97, None): (['TV Extra', 'TV:'], {
+ 0x23: 'Return',
+ 0x36: 'Options',
+
+ }),
+}
+
+for (address, extended), (name, commands) in ADDRESSES.items():
+ commands.update(NUMBERS)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from common.srdhelper import bitpack_lsb
+from .lists import ADDRESSES
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class SIRCError(Exception):
+ pass
+
+class SIRCErrorSilent(SIRCError):
+ pass
+
+class Ann:
+ BIT, AGC, PAUSE, START, CMD, ADDR, EXT, REMOTE, WARN = range(9)
+
+AGC_USEC = 2400
+ONE_USEC = 1200
+ZERO_USEC = 600
+PAUSE_USEC = 600
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ir_sirc'
+ name = 'IR SIRC'
+ longname = 'Sony IR (SIRC)'
+ desc = 'Sony infrared remote control protocol (SIRC).'
+ license = 'gplv2+'
+ tags = ['IR']
+ inputs = ['logic']
+ outputs = []
+ channels = (
+ {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
+ 'values': ('active-low', 'active-high')},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('agc', 'AGC'),
+ ('pause', 'Pause'),
+ ('start', 'Start'),
+ ('command', 'Command'),
+ ('address', 'Address'),
+ ('extended', 'Extended'),
+ ('remote', 'Remote'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (Ann.BIT, Ann.AGC, Ann.PAUSE)),
+ ('fields', 'Fields', (Ann.START, Ann.CMD, Ann.ADDR, Ann.EXT)),
+ ('remotes', 'Remotes', (Ann.REMOTE,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.active = self.options['polarity'] == 'active-high'
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ self.snum_per_us = self.samplerate / 1e6
+
+ def putg(self, ss, es, cls, texts):
+ self.put(ss, es, self.out_ann, [cls, texts])
+
+ def tolerance(self, ss, es, expected):
+ microseconds = (es - ss) / self.snum_per_us
+ tolerance = expected * 0.30
+ return (expected - tolerance) < microseconds < (expected + tolerance)
+
+ def wait_wrap(self, conds, timeout):
+ if timeout is not None:
+ to = int(timeout * self.snum_per_us)
+ conds.append({'skip': to})
+ ss = self.samplenum
+ pins = self.wait(conds)
+ es = self.samplenum
+ return pins, ss, es, self.matched
+
+ def read_pulse(self, high, time):
+ e = 'f' if high else 'r'
+ max_time = int(time * 1.30)
+ (ir,), ss, es, (edge, timeout) = self.wait_wrap([{0: e}], max_time)
+ if timeout or not self.tolerance(ss, es, time):
+ raise SIRCError('Timeout')
+ return ir, ss, es, (edge, timeout)
+
+ def read_bit(self):
+ e = 'f' if self.active else 'r'
+ _, high_ss, high_es, (edge, timeout) = self.wait_wrap([{0: e}], 2000)
+ if timeout:
+ raise SIRCError('Bit High Timeout')
+ if self.tolerance(high_ss, high_es, ONE_USEC):
+ bit = 1
+ elif self.tolerance(high_ss, high_es, ZERO_USEC):
+ bit = 0
+ else:
+ raise SIRCError('Bit Low Timeout')
+ try:
+ _, low_ss, low_es, _ = self.read_pulse(not self.active, PAUSE_USEC)
+ good = True
+ except SIRCError:
+ low_es = high_es + int(PAUSE_USEC * self.snum_per_us)
+ good = False
+ self.putg(high_ss, low_es, Ann.BIT, ['{}'.format(bit)])
+ return bit, high_ss, low_es, good
+
+ def read_signal(self):
+ # Start code
+ try:
+ _, agc_ss, agc_es, _ = self.read_pulse(self.active, AGC_USEC)
+ _, pause_ss, pause_es, _ = self.read_pulse(not self.active, PAUSE_USEC)
+ except SIRCError:
+ raise SIRCErrorSilent('not an SIRC message')
+ self.putg(agc_ss, agc_es, Ann.AGC, ['AGC', 'A'])
+ self.putg(pause_ss, pause_es, Ann.PAUSE, ['Pause', 'P'])
+ self.putg(agc_ss, pause_es, Ann.START, ['Start', 'S'])
+
+ # Read bits
+ bits = []
+ while True:
+ bit, ss, es, good = self.read_bit()
+ bits.append((bit, ss, es))
+ if len(bits) > 20:
+ raise SIRCError('too many bits')
+ if not good:
+ if len(bits) == 12:
+ command = bits[0:7]
+ address = bits[7:12]
+ extended = []
+ elif len(bits) == 15:
+ command = bits[0:7]
+ address = bits[7:15]
+ extended = []
+ elif len(bits) == 20:
+ command = bits[0:7]
+ address = bits[7:12]
+ extended = bits[12:20]
+ else:
+ raise SIRCError('incorrect bits count {}'.format(len(bits)))
+ break
+
+ command_num = bitpack_lsb(command, 0)
+ address_num = bitpack_lsb(address, 0)
+ command_str = '0x{:02X}'.format(command_num)
+ address_str = '0x{:02X}'.format(address_num)
+ self.putg(command[0][1], command[-1][2], Ann.CMD, [
+ 'Command: {}'.format(command_str),
+ 'C:{}'.format(command_str),
+ ])
+ self.putg(address[0][1], address[-1][2], Ann.ADDR, [
+ 'Address: {}'.format(address_str),
+ 'A:{}'.format(address_str),
+ ])
+ extended_num = None
+ if extended:
+ extended_num = bitpack_lsb(extended, 0)
+ extended_str = '0x{:02X}'.format(extended_num)
+ self.putg(extended[0][1], extended[-1][2], Ann.EXT, [
+ 'Extended: {}'.format(extended_str),
+ 'E:{}'.format(extended_str),
+ ])
+ return address_num, command_num, extended_num, bits[0][1], bits[-1][2]
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+
+ unknown = (['Unknown Device: ', 'UNK: '], {})
+ while True:
+ e = 'h' if self.active else 'l'
+ _, _, frame_ss, _ = self.wait_wrap([{0: e}], None)
+ try:
+ addr, cmd, ext, payload_ss, payload_es = self.read_signal()
+ names, cmds = ADDRESSES.get((addr, ext), unknown)
+ text = cmds.get(cmd, 'Unknown')
+ self.putg(frame_ss, payload_es, Ann.REMOTE, [
+ n + text for n in names
+ ])
+ except SIRCErrorSilent as e:
+ pass
+ except SIRCError as e:
+ self.putg(frame_ss, self.samplenum, Ann.WARN, [
+ 'Error: {}'.format(e),
+ 'Error',
+ 'E',
+ ])
)
annotations = (
('jitter', 'Jitter value'),
- ('clk_missed', 'Clock missed'),
- ('sig_missed', 'Signal missed'),
+ ('clk_miss', 'Clock miss'),
+ ('sig_miss', 'Signal miss'),
)
annotation_rows = (
- ('jitter', 'Jitter values', (0,)),
- ('clk_missed', 'Clock missed', (1,)),
- ('sig_missed', 'Signal missed', (2,)),
+ ('jitter_vals', 'Jitter values', (0,)),
+ ('clk_misses', 'Clock misses', (1,)),
+ ('sig_misses', 'Signal misses', (2,)),
)
binary = (
('ascii-float', 'Jitter values as newline-separated ASCII floats'),
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2012-2015 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2012-2020 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
##
import sigrokdecode as srd
+from common.srdhelper import SrdStrEnum
'''
OUTPUT_PYTHON format:
for each bit that is in the bitstring.
'''
-jtag_states = [
- # Intro "tree"
- 'TEST-LOGIC-RESET', 'RUN-TEST/IDLE',
- # DR "tree"
- 'SELECT-DR-SCAN', 'CAPTURE-DR', 'UPDATE-DR', 'PAUSE-DR',
- 'SHIFT-DR', 'EXIT1-DR', 'EXIT2-DR',
- # IR "tree"
- 'SELECT-IR-SCAN', 'CAPTURE-IR', 'UPDATE-IR', 'PAUSE-IR',
- 'SHIFT-IR', 'EXIT1-IR', 'EXIT2-IR',
-]
+s = 'TEST-LOGIC-RESET RUN-TEST/IDLE \
+ SELECT-DR-SCAN CAPTURE-DR UPDATE-DR PAUSE-DR SHIFT-DR EXIT1-DR EXIT2-DR \
+ SELECT-IR-SCAN CAPTURE-IR UPDATE-IR PAUSE-IR SHIFT-IR EXIT1-IR EXIT2-IR'
+St = SrdStrEnum.from_str('St', s)
+
+jtag_states = [s.value for s in St]
class Decoder(srd.Decoder):
api_version = 3
annotation_rows = (
('bits-tdi', 'Bits (TDI)', (16,)),
('bits-tdo', 'Bits (TDO)', (17,)),
- ('bitstrings-tdi', 'Bitstring (TDI)', (18,)),
- ('bitstrings-tdo', 'Bitstring (TDO)', (19,)),
+ ('bitstrings-tdi', 'Bitstrings (TDI)', (18,)),
+ ('bitstrings-tdo', 'Bitstrings (TDO)', (19,)),
('states', 'States', tuple(range(15 + 1))),
)
self.reset()
def reset(self):
- # self.state = 'TEST-LOGIC-RESET'
- self.state = 'RUN-TEST/IDLE'
+ # self.state = St.TEST_LOGIC_RESET
+ self.state = St.RUN_TEST_IDLE
self.oldstate = None
self.bits_tdi = []
self.bits_tdo = []
self.oldstate = self.state
# Intro "tree"
- if self.state == 'TEST-LOGIC-RESET':
- self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE'
- elif self.state == 'RUN-TEST/IDLE':
- self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+ if self.state == St.TEST_LOGIC_RESET:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.RUN_TEST_IDLE
+ elif self.state == St.RUN_TEST_IDLE:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
# DR "tree"
- elif self.state == 'SELECT-DR-SCAN':
- self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR'
- elif self.state == 'CAPTURE-DR':
- self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
- elif self.state == 'SHIFT-DR':
- self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
- elif self.state == 'EXIT1-DR':
- self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR'
- elif self.state == 'PAUSE-DR':
- self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR'
- elif self.state == 'EXIT2-DR':
- self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR'
- elif self.state == 'UPDATE-DR':
- self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+ elif self.state == St.SELECT_DR_SCAN:
+ self.state = St.SELECT_IR_SCAN if (tms) else St.CAPTURE_DR
+ elif self.state == St.CAPTURE_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.SHIFT_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.EXIT1_DR:
+ self.state = St.UPDATE_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.PAUSE_DR:
+ self.state = St.EXIT2_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.EXIT2_DR:
+ self.state = St.UPDATE_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.UPDATE_DR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
# IR "tree"
- elif self.state == 'SELECT-IR-SCAN':
- self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR'
- elif self.state == 'CAPTURE-IR':
- self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
- elif self.state == 'SHIFT-IR':
- self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
- elif self.state == 'EXIT1-IR':
- self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR'
- elif self.state == 'PAUSE-IR':
- self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR'
- elif self.state == 'EXIT2-IR':
- self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR'
- elif self.state == 'UPDATE-IR':
- self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+ elif self.state == St.SELECT_IR_SCAN:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.CAPTURE_IR
+ elif self.state == St.CAPTURE_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.SHIFT_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.EXIT1_IR:
+ self.state = St.UPDATE_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.PAUSE_IR:
+ self.state = St.EXIT2_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.EXIT2_IR:
+ self.state = St.UPDATE_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.UPDATE_IR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
def handle_rising_tck_edge(self, pins):
(tdi, tdo, tck, tms, trst, srst, rtck) = pins
# Output the saved item (from the last CLK edge to the current).
self.es_item = self.samplenum
# Output the old state (from last rising TCK edge to current one).
- self.putx([jtag_states.index(self.oldstate), [self.oldstate]])
- self.putp(['NEW STATE', self.state])
+ self.putx([jtag_states.index(self.oldstate.value), [self.oldstate.value]])
+ self.putp(['NEW STATE', self.state.value])
# Upon SHIFT-*/EXIT1-* collect the current TDI/TDO values.
- if self.oldstate.startswith('SHIFT-') or \
- self.oldstate.startswith('EXIT1-'):
+ if self.oldstate.value.startswith('SHIFT-') or \
+ self.oldstate.value.startswith('EXIT1-'):
if self.first_bit:
self.ss_bitstring = self.samplenum
self.first_bit = False
else:
- self.putx([16, [str(self.bits_tdi[0])]])
- self.putx([17, [str(self.bits_tdo[0])]])
+ self.putx([16, [str(self.bits_tdi[-1])]])
+ self.putx([17, [str(self.bits_tdo[-1])]])
# Use self.samplenum as ES of the previous bit.
- self.bits_samplenums_tdi[0][1] = self.samplenum
- self.bits_samplenums_tdo[0][1] = self.samplenum
+ self.bits_samplenums_tdi[-1][1] = self.samplenum
+ self.bits_samplenums_tdo[-1][1] = self.samplenum
- self.bits_tdi.insert(0, tdi)
- self.bits_tdo.insert(0, tdo)
+ self.bits_tdi.append(tdi)
+ self.bits_tdo.append(tdo)
# Use self.samplenum as SS of the current bit.
- self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
- self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
+ self.bits_samplenums_tdi.append([self.samplenum, -1])
+ self.bits_samplenums_tdo.append([self.samplenum, -1])
# Output all TDI/TDO bits if we just switched to UPDATE-*.
- if self.state.startswith('UPDATE-'):
+ if self.state.value.startswith('UPDATE-'):
self.es_bitstring = self.samplenum
- t = self.state[-2:] + ' TDI'
+ t = self.state.value[-2:] + ' TDI'
+ self.bits_tdi.reverse()
+ self.bits_samplenums_tdi.reverse()
b = ''.join(map(str, self.bits_tdi[1:]))
h = ' (0x%x' % int('0b0' + b, 2) + ')'
s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi[1:])) + ' bits'
self.bits_tdi = []
self.bits_samplenums_tdi = []
- t = self.state[-2:] + ' TDO'
+ t = self.state.value[-2:] + ' TDO'
+ self.bits_tdo.reverse()
+ self.bits_samplenums_tdo.reverse()
b = ''.join(map(str, self.bits_tdo[1:]))
h = ' (0x%x' % int('0b0' + b, 2) + ')'
s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo[1:])) + ' bits'
##
import sigrokdecode as srd
-from common.srdhelper import bin2int
+from common.srdhelper import bin2int, SrdIntEnum
class Instruction(object):
IDCODE = 0x01
PRACC = (1 << 18)
PRNW = (1 << 19)
-class Ann(object):
+class Ann(SrdIntEnum):
INSTRUCTION = 0
REGISTER = 1
CONTROL_FIELD_IN = 10
)
annotation_rows = (
('instructions', 'Instructions', (0,)),
- ('regs', 'Registers', regs_items['rows_range']),
('control_fields_in', 'Control fields in', (10,)),
('control_fields_out', 'Control fields out', (11,)),
- ('pracc', 'PrAcc', (12,)),
+ ('regs', 'Registers', regs_items['rows_range']),
+ ('pracc-vals', 'PrAcc', (12,)),
)
def __init__(self):
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+LFAST is a physical communication interface used mainly by the NXP Zipwire
+interface. It's a framed asynchronous serial interface using differential
+TX/RX pairs, capable of data rates of up to 320 MBit/s.
+
+This interface is also provided by Infineon as HSCT.
+
+As with most differential signals, it's sufficient to measure TXP or RXP, no
+need for a differential probe. The REFCLK used by the hardware isn't needed by
+this protocol decoder either.
+
+For details see https://www.nxp.com/docs/en/application-note/AN5134.pdf and
+https://hitex.co.uk/fileadmin/uk-files/downloads/ShieldBuddy/tc27xD_um_v2.2.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+import decimal
+
+'''
+OUTPUT_PYTHON format:
+
+[ss, es, data] where data is a data byte of the LFAST payload. All bytes of
+the payload are sent at once, each with its start and end sample.
+'''
+
+# See tc27xD_um_v2.2.pdf, Table 20-10
+payload_sizes = {
+ 0b000: '8 bit',
+ 0b001: '32 bit / 4 byte',
+ 0b010: '64 bit / 8 byte',
+ 0b011: '96 bit / 12 byte',
+ 0b100: '128 bit / 16 byte',
+ 0b101: '256 bit / 32 byte',
+ 0b110: '512 bit / 64 byte',
+ 0b111: '288 bit / 36 byte'
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-10
+payload_byte_sizes = {
+ 0b000: 1,
+ 0b001: 4,
+ 0b010: 8,
+ 0b011: 12,
+ 0b100: 16,
+ 0b101: 32,
+ 0b110: 64,
+ 0b111: 36
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-11
+channel_types = {
+ 0b0000: 'Interface Control / PING',
+ 0b0001: 'Unsolicited Status (32 bit)',
+ 0b0010: 'Slave Interface Control / Read',
+ 0b0011: 'CTS Transfer',
+ 0b0100: 'Data Channel A',
+ 0b0101: 'Data Channel B',
+ 0b0110: 'Data Channel C',
+ 0b0111: 'Data Channel D',
+ 0b1000: 'Data Channel E',
+ 0b1001: 'Data Channel F',
+ 0b1010: 'Data Channel G',
+ 0b1011: 'Data Channel H',
+ 0b1100: 'Reserved',
+ 0b1101: 'Reserved',
+ 0b1110: 'Reserved',
+ 0b1111: 'Reserved',
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-12
+control_payloads = {
+ 0x00: 'PING',
+ 0x01: 'Reserved',
+ 0x02: 'Slave interface clock multiplier start',
+ 0x04: 'Slave interface clock multiplier stop',
+ 0x08: 'Use 5 MBaud for M->S',
+ 0x10: 'Use 320 MBaud for M->S',
+ 0x20: 'Use 5 MBaud for S->M',
+ 0x40: 'Use 20 MBaud for S->M (needs 20 MHz SysClk)',
+ 0x80: 'Use 320 MBaud for S->M',
+ 0x31: 'Enable slave interface transmitter',
+ 0x32: 'Disable slave interface transmitter',
+ 0x34: 'Enable clock test mode',
+ 0x38: 'Disable clock test mode and payload loopback',
+ 0xFF: 'Enable payload loopback',
+}
+
+
+ann_bit, ann_sync, ann_header_pl_size, ann_header_ch_type, ann_header_cts, \
+ ann_payload, ann_control_data, ann_sleepbit, ann_warning = range(9)
+state_sync, state_header, state_payload, state_sleepbit = range(4)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'lfast'
+ name = 'LFAST'
+ longname = 'NXP LFAST interface'
+ desc = 'Differential high-speed P2P interface'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['lfast']
+ tags = ['Embedded/industrial']
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'TXP or RXP'},
+ )
+ annotations = (
+ ('bit', 'Bits'),
+ ('sync', 'Sync Pattern'),
+ ('header_pl_size', 'Payload Size'),
+ ('header_ch_type', 'Logical Channel Type'),
+ ('header_cts', 'Clear To Send'),
+ ('payload', 'Payload'),
+ ('ctrl_data', 'Control Data'),
+ ('sleep', 'Sleep Bit'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (ann_bit,)),
+ ('fields', 'Fields', (ann_sync, ann_header_pl_size, ann_header_ch_type,
+ ann_header_cts, ann_payload, ann_control_data, ann_sleepbit,)),
+ ('warnings', 'Warnings', (ann_warning,)),
+ )
+
+ def __init__(self):
+ decimal.getcontext().rounding = decimal.ROUND_HALF_UP
+ self.bit_len = 0xFFFFFFFF
+ self.reset()
+
+ def reset(self):
+ self.prev_bit_len = self.bit_len
+ self.ss = self.es = 0
+ self.ss_payload = self.es_payload = 0
+ self.ss_byte = 0
+ self.bits = []
+ self.payload = []
+ self.payload_size = 0 # Expected number of bytes, as read from header
+ self.bit_len = 0 # Length of one bit time, in samples
+ self.timeout = 0 # Desired timeout for next edge, in samples
+ self.ch_type_id = 0 # ID of channel type
+ self.state = state_sync
+
+ def metadata(self, key, value):
+ pass
+
+ def start(self):
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def put_ann(self, ss, es, ann_class, value):
+ self.put(ss, es, self.out_ann, [ann_class, value])
+
+ def put_payload(self):
+ self.put(self.ss_payload, self.es_payload, self.out_python, self.payload)
+
+ def handle_sync(self):
+ if len(self.bits) == 1:
+ self.ss_sync = self.ss_bit
+
+ if len(self.bits) == 16:
+ value = bitpack(self.bits)
+ if value == 0xA84B:
+ self.put_ann(self.ss_sync, self.es_bit, ann_sync, ['Sync OK'])
+ else:
+ self.put_ann(self.ss_sync, self.es_bit, ann_warning, ['Wrong Sync Value: {:02X}'.format(value)])
+ self.reset()
+
+ # Only continue if we didn't just reset
+ if self.ss > 0:
+ self.bits = []
+ self.state = state_header
+ self.timeout = int(9.4 * self.bit_len)
+
+ def handle_header(self):
+ if len(self.bits) == 1:
+ self.ss_header = self.ss_bit
+
+ if len(self.bits) == 8:
+ # See tc27xD_um_v2.2.pdf, Figure 20-47, for the header structure
+ bit_len = (self.es_bit - self.ss_header) / 8
+ value = bitpack(self.bits)
+
+ ss = self.ss_header
+ es = ss + 3 * bit_len
+ size_id = (value & 0xE0) >> 5
+ size = payload_sizes.get(size_id)
+ self.payload_size = payload_byte_sizes.get(size_id)
+ self.put_ann(int(ss), int(es), ann_header_pl_size, [size])
+
+ ss = es
+ es = ss + 4 * bit_len
+ self.ch_type_id = (value & 0x1E) >> 1
+ ch_type = channel_types.get(self.ch_type_id)
+ self.put_ann(int(ss), int(es), ann_header_ch_type, [ch_type])
+
+ ss = es
+ es = ss + bit_len
+ cts = value & 0x01
+ self.put_ann(int(ss), int(es), ann_header_cts, ['{}'.format(cts)])
+
+ self.bits = []
+ self.state = state_payload
+ self.timeout = int(9.4 * self.bit_len)
+
+ def handle_payload(self):
+ self.timeout = int((self.payload_size - len(self.payload)) * 8 * self.bit_len)
+
+ if len(self.bits) == 1:
+ self.ss_byte = self.ss_bit
+ if self.ss_payload == 0:
+ self.ss_payload = self.ss_bit
+
+ if len(self.bits) == 8:
+ value = bitpack(self.bits)
+ value_hex = '{:02X}'.format(value)
+
+ # Control transfers have no SIPI payload, show them as control transfers
+ # Check the channel_types list for the meaning of the magic values
+ if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011):
+ self.put_ann(self.ss_byte, self.es_bit, ann_payload, [value_hex])
+ else:
+ # Control transfers are 8-bit transfers, so only evaluate the first byte
+ if len(self.payload) == 0:
+ ctrl_data = control_payloads.get(value, value_hex)
+ self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [ctrl_data])
+ else:
+ self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [value_hex])
+
+ self.bits = []
+ self.es_payload = self.es_bit
+ self.payload.append((self.ss_byte, self.es_payload, value))
+
+ if (len(self.payload) == self.payload_size):
+ self.timeout = int(1.4 * self.bit_len)
+ self.state = state_sleepbit
+
+ def handle_sleepbit(self):
+ if len(self.bits) == 0:
+ self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N'])
+ elif len(self.bits) > 1:
+ self.put_ann(self.ss_bit, self.es_bit, ann_warning, ['Expected only the sleep bit, got {} bits instead'.format(len(self.bits))])
+ else:
+ if self.bits[0] == 1:
+ self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['LVDS sleep mode request', 'Sleep', 'Y'])
+ else:
+ self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N'])
+
+ # We only send the payload out if this is an actual data transfer;
+ # check the channel_types list for the meaning of the magic values
+ if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011):
+ if len(self.payload) > 0:
+ self.put_payload()
+
+ def decode(self):
+ while True:
+ if self.timeout == 0:
+ rising_edge, = self.wait({0: 'e'})
+ else:
+ rising_edge, = self.wait([{0: 'e'}, {'skip': self.timeout}])
+
+ # If this is the first edge, we only update ss
+ if self.ss == 0:
+ self.ss = self.samplenum
+ # Let's set the timeout for the sync pattern as well
+ self.timeout = int(16.2 * self.prev_bit_len)
+ continue
+
+ self.es = self.samplenum
+
+ # Check for the sleep bit if this is a timeout condition
+ if (len(self.matched) == 2) and self.matched[1]:
+ rising_edge = ~rising_edge
+ if self.state == state_sync:
+ self.reset()
+ continue
+ elif self.state == state_sleepbit:
+ self.ss_bit += self.bit_len
+ self.es_bit = self.ss_bit + self.bit_len
+ self.handle_sleepbit()
+ self.reset()
+ continue
+
+ # Shouldn't happen but we check just in case
+ if int(self.es - self.ss) == 0:
+ continue
+
+ # We use the first bit to deduce the bit length
+ if self.bit_len == 0:
+ self.bit_len = self.es - self.ss
+
+ # Determine number of bits covered by this edge
+ bit_count = (self.es - self.ss) / self.bit_len
+ bit_count = int(decimal.Decimal(bit_count).to_integral_value())
+
+ if bit_count == 0:
+ self.put_ann(self.ss, self.es, ann_warning, ['Bit time too short'])
+ self.reset()
+ continue
+
+ bit_value = '0' if rising_edge else '1'
+
+ divided_len = (self.es - self.ss) / bit_count
+ for i in range(bit_count):
+ self.ss_bit = int(self.ss + i * divided_len)
+ self.es_bit = int(self.ss_bit + divided_len)
+ self.put_ann(self.ss_bit, self.es_bit, ann_bit, [bit_value])
+
+ # Place the new bit at the front of the bit list
+ self.bits.insert(0, (0 if rising_edge else 1))
+
+ if self.state == state_sync:
+ self.handle_sync()
+ elif self.state == state_header:
+ self.handle_header()
+ elif self.state == state_payload:
+ self.handle_payload()
+ elif self.state == state_sleepbit:
+ self.handle_sleepbit()
+ self.reset()
+
+ if self.ss == 0:
+ break # Because reset() was called, invalidating everything
+
+ # Only update ss if we didn't just perform a reset
+ if self.ss > 0:
+ self.ss = self.samplenum
+
+ # If we got here when a timeout occurred, we have processed all null
+ # bits that we could and should reset now to find the next packet
+ if (len(self.matched) == 2) and self.matched[1]:
+ self.reset()
def reset(self):
self.state = LinFsm.State.WaitForBreak
+ self.uart_idle_count = 0
def __init__(self):
a = dict()
self.allowed_state = a
self.state = None
+ self.uart_idle_count = 0
self.reset()
class Decoder(srd.Decoder):
annotations = (
('data', 'LIN data'),
('control', 'Protocol info'),
- ('error', 'Error descriptions'),
- ('inline_error', 'Protocol violations and errors'),
+ ('error', 'Error description'),
+ ('inline_error', 'Protocol violation or error'),
)
annotation_rows = (
- ('data', 'Data', (0, 1, 3)),
- ('error', 'Error', (2,)),
+ ('data_vals', 'Data', (0, 1, 3)),
+ ('errors', 'Errors', (2,)),
)
def __init__(self):
self.lin_header = []
self.lin_rsp = []
self.lin_version = None
- self.out_ann = None
self.ss_block = None
self.es_block = None
return True
+ def handle_uart_idle(self):
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ self.fsm.uart_idle_count += 1
+
+ if self.fsm.uart_idle_count == 2:
+ self.fsm.transit(LinFsm.State.Checksum)
+ self.handle_checksum()
+ self.fsm.reset()
+
def handle_wait_for_break(self, value):
self.wipe_break_null_byte(value)
self.ss_block, self.es_block = ss, es
# Ignore all UART packets except the actual data packets or BREAK.
+ if ptype == 'IDLE':
+ self.handle_uart_idle()
if ptype == 'BREAK':
self.handle_break(pdata)
if ptype != 'DATA':
'values': (9, 10, 11, 12)},
)
annotations = (
- ('celsius', 'Temperature in degrees Celsius'),
- ('kelvin', 'Temperature in Kelvin'),
- ('text-verbose', 'Human-readable text (verbose)'),
- ('text', 'Human-readable text'),
- ('warnings', 'Human-readable warnings'),
+ ('celsius', 'Temperature / °C'),
+ ('kelvin', 'Temperature / Kelvin'),
+ ('text-verbose', 'Text (verbose)'),
+ ('text', 'Text'),
+ ('warning', 'Warning'),
)
def __init__(self):
{'id': 'lsmi', 'name': 'LSMI#', 'desc': 'System Management Interrupt'},
)
annotations = (
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
('start', 'Start'),
('cycle-type', 'Cycle-type/direction'),
('addr', 'Address'),
('tar2', 'Turn-around cycle 2'),
)
annotation_rows = (
- ('data', 'Data', (1, 2, 3, 4, 5, 6, 7)),
+ ('data-vals', 'Data', (1, 2, 3, 4, 5, 6, 7)),
('warnings', 'Warnings', (0,)),
)
def reset(self):
self.state = 'IDLE'
self.oldlclk = -1
- self.samplenum = 0
self.lad = -1
self.addr = 0
self.cur_nibble = 0
self.state = 'IDLE'
def decode(self):
+ conditions = [{i: 'e'} for i in range(6)]
while True:
- # TODO: Come up with more appropriate self.wait() conditions.
- pins = self.wait()
-
- # If none of the pins changed, there's nothing to do.
- if self.oldpins == pins:
- continue
+ pins = self.wait(conditions)
# Store current pin values for the next round.
self.oldpins = pins
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the
+Linear Technology LTC2421/LTC2422 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+input_voltage_format = ['%.6fV', '%.2fV']
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ltc242x'
+ name = 'LTC242x'
+ longname = 'Linear Technology LTC242x'
+ desc = 'Linear Technology LTC2421/LTC2422 1-/2-channel 20-bit ADC.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Analog/digital']
+ annotations = (
+ ('ch0_voltage', 'CH0 voltage'),
+ ('ch1_voltage', 'CH1 voltage'),
+ )
+ annotation_rows = (
+ ('ch0_voltages', 'CH0 voltages', (0,)),
+ ('ch1_voltages', 'CH1 voltages', (1,)),
+ )
+ options = (
+ {'id': 'vref', 'desc': 'Reference voltage (V)', 'default': 1.5},
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.data = 0
+ self.ss, self.es = 0, 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def handle_input_voltage(self, data):
+ input_voltage = data & 0x3FFFFF
+ input_voltage = -(2**21 - input_voltage)
+ input_voltage = (input_voltage / 0xfffff) * self.options['vref']
+ ann = []
+ for format in input_voltage_format:
+ ann.append(format % input_voltage)
+
+ channel = (data & (1 << 22)) >> 22
+ self.put(self.ss, self.es, self.out_ann, [channel, ann])
+
+ def decode(self, ss, es, data):
+ ptype = data[0]
+
+ if ptype == 'CS-CHANGE':
+ cs_old, cs_new = data[1:]
+ if cs_old is not None and cs_old == 0 and cs_new == 1:
+ self.es = es
+ self.data >>= 1
+ self.handle_input_voltage(self.data)
+
+ self.data = 0
+ elif cs_old is not None and cs_old == 1 and cs_new == 0:
+ self.ss = ss
+
+ elif ptype == 'BITS':
+ miso = data[2]
+ for bit in reversed(miso):
+ self.data = self.data | bit[0]
+
+ self.data <<= 1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes the
+Linear Technology LTC2607/LTC2617/LTC2627 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Analog Devices Inc.
+##
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+slave_address = {
+ 0x00: ['GND', 'GND', 'GND', 'G'],
+ 0x01: ['FLOAT', 'FLOAT', 'FLOAT', 'F'],
+ 0x02: ['VCC', 'VCC', 'VCC', 'V'],
+}
+
+commands = {
+ 0x00: ['Write Input Register', 'Write In Reg', 'Wr In Reg', 'WIR'],
+ 0x01: ['Update DAC', 'Update', 'U'],
+ 0x03: ['Write and Power Up DAC', 'Write & Power Up', 'W&PU'],
+ 0x04: ['Power Down DAC', 'Power Down', 'PD'],
+ 0x0F: ['No Operation', 'No Op', 'NO'],
+}
+
+addresses = {
+ 0x00: ['DAC A', 'A'],
+ 0x01: ['DAC B', 'B'],
+ 0x0F: ['All DACs', 'All'],
+}
+
+input_voltage_format = ['%.6fV', '%.2fV']
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ltc26x7'
+ name = 'LTC26x7'
+ longname = 'Linear Technology LTC26x7'
+ desc = 'Linear Technology LTC26x7 16-/14-/12-bit rail-to-rail DACs.'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = []
+ tags = ['IC', 'Analog/digital']
+ options = (
+ {'id': 'chip', 'desc': 'Chip', 'default': 'ltc2607',
+ 'values': ('ltc2607', 'ltc2617', 'ltc2627')},
+ {'id': 'vref', 'desc': 'Reference voltage (V)', 'default': 1.5},
+ )
+ annotations = (
+ ('slave_addr', 'Slave address'),
+ ('command', 'Command'),
+ ('address', 'Address'),
+ ('dac_a_voltage', 'DAC A voltage'),
+ ('dac_b_voltage', 'DAC B voltage'),
+ )
+ annotation_rows = (
+ ('addr_cmd', 'Address/command', (0, 1, 2)),
+ ('dac_a_voltages', 'DAC A voltages', (3,)),
+ ('dac_b_voltages', 'DAC B voltages', (4,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = 'IDLE'
+ self.ss = -1
+ self.data = 0x00
+ self.dac_val = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def convert_ternary_str(self, n):
+ if n == 0:
+ return [0, 0, 0]
+ nums = []
+ while n:
+ n, r = divmod(n, 3)
+ nums.append(r)
+ while len(nums) < 3:
+ nums.append(0)
+ return list(reversed(nums))
+
+ def handle_slave_addr(self, data):
+ if data == 0x73:
+ ann = ['Global address', 'Global addr', 'Glob addr', 'GA']
+ self.put(self.ss, self.es, self.out_ann, [0, ann])
+ return
+ ann = ['CA2=%s CA1=%s CA0=%s', '2=%s 1=%s 0=%s', '%s %s %s', '%s %s %s']
+ addr = 0
+ for i in range(7):
+ if i in [2, 3]:
+ continue
+ offset = i
+ if i > 3:
+ offset -= 2
+ mask = 1 << i
+ if data & mask:
+ mask = 1 << offset
+ addr |= mask
+
+ addr -= 0x04
+ ternary_values = self.convert_ternary_str(addr)
+ for i in range(len(ann)):
+ ann[i] = ann[i] % (slave_address[ternary_values[0]][i],
+ slave_address[ternary_values[1]][i],
+ slave_address[ternary_values[2]][i])
+ self.put(self.ss, self.es, self.out_ann, [0, ann])
+
+ def handle_cmd_addr(self, data):
+ cmd_val = (data >> 4) & 0x0F
+ self.dac_val = (data & 0x0F)
+ sm = (self.ss + self.es) // 2
+
+ self.put(self.ss, sm, self.out_ann, [1, commands[cmd_val]])
+ self.put(sm, self.es, self.out_ann, [2, addresses[self.dac_val]])
+
+ def handle_data(self, data):
+ self.data = (self.data << 8) & 0xFF00
+ self.data += data
+ if self.options['chip'] == 'ltc2617':
+ self.data = (self.data >> 2)
+ self.data = (self.options['vref'] * self.data) / 0x3FFF
+ elif self.options['chip'] == 'ltc2627':
+ self.data = (self.data >> 4)
+ self.data = (self.options['vref'] * self.data) / 0x0FFF
+ else:
+ self.data = (self.options['vref'] * self.data) / 0xFFFF
+ ann = []
+ for format in input_voltage_format:
+ ann.append(format % self.data)
+ self.data = 0
+
+ if self.dac_val == 0x0F: # All DACs (A and B).
+ self.put(self.ss, self.es, self.out_ann, [3 + 0, ann])
+ self.put(self.ss, self.es, self.out_ann, [3 + 1, ann])
+ else:
+ self.put(self.ss, self.es, self.out_ann, [3 + self.dac_val, ann])
+
+ def decode(self, ss, es, data):
+ cmd, databyte = data
+ self.es = es
+
+ # State machine.
+ if self.state == 'IDLE':
+ # Wait for an I²C START condition.
+ if cmd != 'START':
+ return
+ self.state = 'GET SLAVE ADDR'
+ elif self.state == 'GET SLAVE ADDR':
+ # Wait for an address write operation.
+ if cmd != 'ADDRESS WRITE':
+ return
+ self.ss = ss
+ self.handle_slave_addr(databyte)
+ self.ss = -1
+ self.state = 'GET CMD ADDR'
+ elif self.state == 'GET CMD ADDR':
+ if cmd != 'DATA WRITE':
+ return
+ self.ss = ss
+ self.handle_cmd_addr(databyte)
+ self.ss = -1
+ self.state = 'WRITE DATA'
+ elif self.state == 'WRITE DATA':
+ if cmd == 'DATA WRITE':
+ if self.ss == -1:
+ self.ss = ss
+ self.data = databyte
+ return
+ self.handle_data(databyte)
+ self.ss = -1
+ elif cmd == 'STOP':
+ self.state = 'IDLE'
+ else:
+ return
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Pin = SrdIntEnum.from_str('Pin', 'SDCKA SDCKB')
ann = [
['Size', 'L'],
self.putx([7, ['Frame error', 'F error', 'FE']])
def handle_start(self):
- self.wait({0: 'l', 1: 'h'})
+ self.wait({Pin.SDCKA: 'l', Pin.SDCKB: 'h'})
self.ss = self.samplenum
count = 0
while True:
- sdcka, sdckb = self.wait([{1: 'f'}, {0: 'r'}])
+ sdcka, sdckb = self.wait([{Pin.SDCKB: 'f'}, {Pin.SDCKA: 'r'}])
if self.matched[0]:
count = count + 1
if self.matched[1]:
countb = 0
self.data = 0
while countb < 4:
- sdcka, sdckb = self.wait([{0: 'f'}, {1: 'f'}])
+ sdcka, sdckb = self.wait([{Pin.SDCKA: 'f'}, {Pin.SDCKB: 'f'}])
self.es = self.samplenum
if self.matched[0]:
if counta == countb:
self.got_bit(sdckb)
counta = counta + 1
elif counta == 1 and countb == 0 and self.data == 0 and sdckb == 0:
- self.wait([{0: 'h', 1: 'h'}, {0: 'f'}, {1: 'f'}])
+ self.wait([{Pin.SDCKA: 'h', Pin.SDCKB: 'h'},
+ {Pin.SDCKA: 'f'}, {Pin.SDCKB: 'f'}])
self.es = self.samplenum
if self.matched[0]:
self.got_end()
else:
self.frame_error()
return False
- self.wait({0: 'h'})
+ self.wait({Pin.SDCKA: 'h'})
self.es = self.samplenum
self.got_byte()
return True
outputs = []
tags = ['Display']
annotations = (
- ('register', 'Registers written to the device'),
- ('digit', 'Digits displayed on the device'),
- ('warnings', 'Human-readable warnings'),
+ ('register', 'Register write'),
+ ('digit', 'Digit displayed'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('commands', 'Commands', (ann_reg, ann_digit)),
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Ann = SrdIntEnum.from_str('Ann', 'ROMDATA')
+Bin = SrdIntEnum.from_str('Bin', 'ROMDATA')
class ChannelError(Exception):
pass
self.data_s = self.samplenum
if self.started:
anntext = '{:04X}:{:02X}'.format(self.addr, self.data)
- self.put(self.addr_s, self.data_s, self.out_ann, [0, [anntext]])
+ self.put(self.addr_s, self.data_s, self.out_ann, [Ann.ROMDATA, [anntext]])
bindata = self.addr.to_bytes(2, byteorder='big')
bindata += self.data.to_bytes(1, byteorder='big')
- self.put(self.addr_s, self.data_s, self.out_bin, [0, bindata])
+ self.put(self.addr_s, self.data_s, self.out_bin, [Bin.ROMDATA, bindata])
def decode(self):
# Address bits above A11 are optional, and are considered to be A12+.
('decode', 'Decode'),
)
annotation_rows = (
- ('bit-val', 'Bit value', (0,)),
- ('bit-num', 'Bit number', (1,)),
- ('frame', 'Frame', (2, 3)),
- ('frame-error', 'Frame error', (4,)),
- ('decode', 'Decode', (5,)),
+ ('bit-vals', 'Bit values', (0,)),
+ ('bit-nums', 'Bit numbers', (1,)),
+ ('frames', 'Frames', (2, 3)),
+ ('frame-errors', 'Frame errors', (4,)),
+ ('decode-vals', 'Decode', (5,)),
)
def __init__(self):
def reset(self):
self.illegal_bus = 0
- self.samplenum = -1
self.clause45_addr = -1 # Clause 45 is context sensitive.
self.reset_decoder_state()
outputs = []
tags = ['Audio', 'PC']
annotations = (
- ('text-verbose', 'Human-readable text (verbose)'),
- ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
- ('text-error', 'Human-readable Error text'),
+ ('text-verbose', 'Text (verbose)'),
+ ('text-sysreal-verbose', 'SysReal text (verbose)'),
+ ('text-error', 'Error text'),
)
annotation_rows = (
('normal', 'Normal', (0, 2)),
('bit', 'Bit'),
('bitstring', 'Bitstring'),
)
- annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+ annotation_rows = tuple((u + 's', v + 's', (i,)) for i, (u, v) in enumerate(annotations))
binary = (
('raw', 'Raw binary'),
)
outputs = []
tags = ['IC', 'Sensor']
annotations = (
- ('celsius', 'Temperature in degrees Celsius'),
- ('kelvin', 'Temperature in Kelvin'),
+ ('celsius', 'Temperature / °C'),
+ ('kelvin', 'Temperature / K'),
+ )
+ annotation_rows = (
+ ('temps-celsius', 'Temperature / °C', (0,)),
+ ('temps-kelvin', 'Temperature / K', (1,)),
)
def __init__(self):
outputs = ['modbus']
tags = ['Embedded/industrial']
annotations = (
- ('sc-server-id', ''),
- ('sc-function', ''),
- ('sc-crc', ''),
- ('sc-address', ''),
- ('sc-data', ''),
- ('sc-length', ''),
- ('sc-error', ''),
- ('cs-server-id', ''),
- ('cs-function', ''),
- ('cs-crc', ''),
- ('cs-address', ''),
- ('cs-data', ''),
- ('cs-length', ''),
- ('cs-error', ''),
- ('error-indication', ''),
+ ('sc-server-id', 'SC server ID'),
+ ('sc-function', 'SC function'),
+ ('sc-crc', 'SC CRC'),
+ ('sc-address', 'SC address'),
+ ('sc-data', 'SC data'),
+ ('sc-length', 'SC length'),
+ ('sc-error', 'SC error'),
+ ('cs-server-id', 'CS server ID'),
+ ('cs-function', 'CS function'),
+ ('cs-crc', 'CS CRC'),
+ ('cs-address', 'CS address'),
+ ('cs-data', 'CS data'),
+ ('cs-length', 'CS length'),
+ ('cs-error', 'CS error'),
+ ('error-indication', 'Error indication'),
)
annotation_rows = (
('sc', 'Server->client', (0, 1, 2, 3, 4, 5, 6)),
('cs', 'Client->server', (7, 8, 9, 10, 11, 12, 13)),
- ('error-indicator', 'Errors in frame', (14,)),
+ ('error-indicators', 'Errors in frame', (14,)),
)
options = (
{'id': 'scchannel', 'desc': 'Server -> client channel',
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
+ # Ignore unknown/unsupported ptypes.
+ if ptype not in ('STARTBIT', 'DATA', 'STOPBIT'):
+ return
+
# Decide what ADU(s) we need this packet to go to.
# Note that it's possible to go to both ADUs.
if rxtx_channels[rxtx] == self.options['scchannel']:
)
annotations = (
('time', 'Time'),
- ('units', 'Units'),
+ ('unit', 'Unit'),
('symbol', 'Symbol'),
('letter', 'Letter'),
('word', 'Word'),
)
- annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+ annotation_rows = tuple((u + 's', v + 's', (i,)) for i, (u, v) in enumerate(annotations))
def __init__(self):
self.reset()
outputs = []
tags = ['IC', 'Wireless/RF']
annotations = (
- ('sread', 'Short register read commands'),
- ('swrite', 'Short register write commands'),
- ('lread', 'Long register read commands'),
- ('lwrite', 'Long register write commands'),
- ('warning', 'Warnings'),
+ ('sread', 'Short register read'),
+ ('swrite', 'Short register write'),
+ ('lread', 'Long register read'),
+ ('lwrite', 'Long register write'),
+ ('warning', 'Warning'),
('tx-frame', 'TX frame'),
('rx-frame', 'RX frame'),
('tx-retry-1', '1x TX retry'),
('ccafail', 'CCAFAIL (channel busy)'),
)
annotation_rows = (
- ('read', 'Read', (0, 2)),
- ('write', 'Write', (1, 3)),
+ ('reads', 'Reads', (0, 2)),
+ ('writes', 'Writes', (1, 3)),
('warnings', 'Warnings', (4,)),
('tx-frames', 'TX frames', (5,)),
('rx-frames', 'RX frames', (6,)),
outputs = []
tags = ['IC', 'Sensor']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the button states
+of an NES gamepad.
+
+The SPI decoder needs to be configured as follows:
+
+Clock polarity = 1
+Clock phase = 0
+Bit order = msb-first
+Word size = 8
+
+Chip-select is not used and must not be assigned to any channel.
+
+Hardware setup is as follows:
+ ___
+ GND |o \
+ CUP |o o| VCC
+ OUT 0 |o o| D3
+ D1 |o o| D4
+ -----
+NES Gamepad Connector
+
+VCC - Power 5V
+GND - Ground
+CUP - Shift register clock (CLK)
+OUT 0 - Shift register latch (optional)
+D1 - Gamepad data (MOSI)
+D3 - Data (unused)
+D4 - Data (unused)
+
+Data pins D3 and D4 are not used by the standard gamepad but
+by special controllers like the Nintento Zapper light gun.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'nes_gamepad'
+ name = 'NES gamepad'
+ longname = 'Nintendo Entertainment System gamepad'
+ desc = 'NES gamepad button states.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['Retro computing']
+ options = (
+ # Currently only the standard controller is supported. This might be
+ # extended by special controllers like the Nintendo Zapper light gun.
+ {'id': 'variant', 'desc': 'Gamepad variant',
+ 'default': 'Standard gamepad', 'values': ('Standard gamepad',)},
+ )
+ annotations = (
+ ('button', 'Button state'),
+ ('no-press', 'No button press'),
+ ('not-connected', 'Gamepad unconnected')
+ )
+ annotation_rows = (
+ ('buttons', 'Button states', (0,)),
+ ('no-presses', 'No button presses', (1,)),
+ ('not-connected-vals', 'Gamepad unconnected', (2,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.variant = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.variant = self.options['variant']
+
+ def putg(self, ss, es, cls, text):
+ self.put(ss, es, self.out_ann, [cls, [text]])
+
+ def handle_data(self, ss, es, value):
+ if value == 0xff:
+ self.putg(ss, es, 1, 'No button is pressed')
+ return
+
+ if value == 0x00:
+ self.putg(ss, es, 2, 'Gamepad is not connected')
+ return
+
+ buttons = [
+ 'A',
+ 'B',
+ 'Select',
+ 'Start',
+ 'North',
+ 'South',
+ 'West',
+ 'East',
+ ]
+
+ bits = '{:08b}'.format(value)
+ text = [buttons[i] for i, b in enumerate(bits) if b == '0']
+ text = ' + '.join(text)
+ self.putg(ss, es, 0, text)
+
+ def decode(self, ss, es, data):
+ ptype, _, _ = data
+ if ptype == 'DATA':
+ _, _, miso = data
+ self.handle_data(ss, es, miso)
+ return
)
annotations = (
# Sent from the host to the chip.
- ('cmd', 'Commands sent to the device'),
- ('tx-data', 'Payload sent to the device'),
+ ('cmd', 'Command'),
+ ('tx-data', 'Payload'),
# Returned by the chip.
- ('register', 'Registers read from the device'),
- ('rx-data', 'Payload read from the device'),
+ ('register', 'Register read'),
+ ('rx-data', 'Payload read'),
- ('warning', 'Warnings'),
+ ('warning', 'Warning'),
)
ann_cmd = 0
ann_tx = 1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Jorge Solla Rubiales <jorgesolla@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the Nordic Semiconductor
+NRF905 (433/868/915MHz transceiver) command/responses.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Jorge Solla Rubiales <jorgesolla@gmail.com>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+CFG_REGS = {
+ 0: [{'name': 'CH_NO', 'stbit': 7, 'nbits': 8}],
+ 1: [
+ {'name': 'AUTO_RETRAN', 'stbit': 5, 'nbits': 1,
+ 'opts': {0: 'No retransmission', 1: 'Retransmission of data packet'}},
+ {'name': 'RX_RED_PWR', 'stbit': 4, 'nbits': 1,
+ 'opts': {0: 'Normal operation', 1: 'Reduced power'}},
+ {'name': 'PA_PWR', 'stbit': 3, 'nbits': 2,
+ 'opts': {0: '-10 dBm', 1: '-2 dBm', 2: '+6 dBm', 3: '+10 dBm'}},
+ {'name': 'HFREQ_PLL', 'stbit': 1, 'nbits': 1,
+ 'opts': {0: '433 MHz', 1: '868 / 915 MHz'}},
+ {'name': 'CH_NO_8', 'stbit': 0, 'nbits': 1},
+ ],
+ 2: [
+ {'name': 'TX_AFW (TX addr width)', 'stbit': 6, 'nbits': 3},
+ {'name': 'RX_AFW (RX addr width)', 'stbit': 2, 'nbits': 3},
+ ],
+ 3: [{'name': 'RW_PW (RX payload width)', 'stbit': 5, 'nbits': 6}],
+ 4: [{'name': 'TX_PW (TX payload width)', 'stbit': 5, 'nbits': 6}],
+ 5: [{'name': 'RX_ADDR_0', 'stbit': 7, 'nbits': 8}],
+ 6: [{'name': 'RX_ADDR_1', 'stbit': 7, 'nbits': 8}],
+ 7: [{'name': 'RX_ADDR_2', 'stbit': 7, 'nbits': 8}],
+ 8: [{'name': 'RX_ADDR_3', 'stbit': 7, 'nbits': 8}],
+ 9: [
+ {'name': 'CRC_MODE', 'stbit': 7, 'nbits': 1,
+ 'opts': {0: '8 CRC check bit', 1: '16 CRC check bit'}},
+ {'name': 'CRC_EN', 'stbit': 6, 'nbits': 1,
+ 'opts': {0: 'Disabled', 1: 'Enabled'}},
+ {'name': 'XOR', 'stbit': 5, 'nbits': 3,
+ 'opts': {0: '4 MHz', 1: '8 MHz', 2: '12 MHz',
+ 3: '16 MHz', 4: '20 MHz'}},
+ {'name': 'UP_CLK_EN', 'stbit': 2, 'nbits': 1,
+ 'opts': {0: 'No external clock signal avail.',
+ 1: 'External clock signal enabled'}},
+ {'name': 'UP_CLK_FREQ', 'stbit': 1, 'nbits': 2,
+ 'opts': {0: '4 MHz', 1: '2 MHz', 2: '1 MHz', 3: '500 kHz'}},
+ ],
+}
+
+CHN_CFG = [
+ {'name': 'PA_PWR', 'stbit': 3, 'nbits': 2,
+ 'opts': {0: '-10 dBm', 1: '-2 dBm', 2: '+6 dBm', 3: '+10 dBm'}},
+ {'name': 'HFREQ_PLL', 'stbit': 1, 'nbits': 1,
+ 'opts': {0: '433 MHz', 1: '868 / 915 MHz'}},
+]
+
+STAT_REG = [
+ {'name': 'AM', 'stbit': 7, 'nbits': 1},
+ {'name': 'DR', 'stbit': 5, 'nbits': 1},
+]
+
+Ann = SrdIntEnum.from_str('Ann', 'CMD REG_WR REG_RD TX RX RESP WARN')
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'nrf905'
+ name = 'nRF905'
+ longname = 'Nordic Semiconductor nRF905'
+ desc = '433/868/933MHz transceiver chip.'
+ license = 'mit'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Wireless/RF']
+ annotations = (
+ ('cmd', 'Command sent to the device'),
+ ('reg-write', 'Config register written to the device'),
+ ('reg-read', 'Config register read from the device'),
+ ('tx-data', 'Payload sent to the device'),
+ ('rx-data', 'Payload read from the device'),
+ ('resp', 'Response to commands received from the device'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('commands', 'Commands', (Ann.CMD,)),
+ ('responses', 'Responses', (Ann.RESP,)),
+ ('registers', 'Registers', (Ann.REG_WR, Ann.REG_RD)),
+ ('tx', 'Transmitted data', (Ann.TX,)),
+ ('rx', 'Received data', (Ann.RX,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.ss_cmd, self.es_cmd = 0, 0
+ self.cs_asserted = False
+ self.reset()
+
+ def reset(self):
+ self.mosi_bytes, self.miso_bytes = [], []
+ self.cmd_samples = {'ss': 0, 'es': 0}
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def extract_bits(self, byte, start_bit, num_bits):
+ begin = 7 - start_bit
+ end = begin + num_bits
+ if begin < 0 or end > 8:
+ return 0
+ binary = format(byte, '08b')[begin:end]
+ return int(binary, 2)
+
+ def extract_vars(self, reg_vars, reg_value):
+ # Iterate all vars on current register.
+ data = ''
+ for var in reg_vars:
+ var_value = self.extract_bits(reg_value, var['stbit'], var['nbits'])
+ data += var['name'] + ' = ' + str(var_value)
+ opt = ''
+
+ # If var has options, just add the option meaning.
+ if 'opts' in var:
+ opt = var['opts'].get(var_value, 'unknown')
+ data += ' (' + opt + ')'
+
+ # Add var separator.
+ if reg_vars.index(var) != len(reg_vars) - 1:
+ data += ' | '
+ return data
+
+ def parse_config_register(self, addr, value, is_write):
+ reg_value = value[0]
+ data = 'CFG_REG[' + hex(addr) + '] -> '
+
+ # Get register vars for this register.
+ if addr in CFG_REGS:
+ reg_vars = CFG_REGS[addr]
+ else:
+ # Invalid register address.
+ self.put(value[1], value[2],
+ self.out_ann, [Ann.WARN, ['Invalid reg. addr']])
+ return
+
+ data += self.extract_vars(reg_vars, reg_value)
+
+ ann = Ann.REG_WR if is_write else Ann.REG_RD
+ self.put(value[1], value[2], self.out_ann, [ann, [data]])
+
+ def parse_config_registers(self, addr, registers, is_write):
+ i = 0
+ while i < len(registers):
+ reg_addr = i + addr
+ if reg_addr <= 9:
+ self.parse_config_register(reg_addr, registers[i], is_write)
+ i += 1
+
+ def dump_cmd_bytes(self, prefix, cmd_bytes, ann):
+ ss, es = cmd_bytes[1][1], 0
+ data = ''
+ for byte in cmd_bytes[1:]:
+ data += format(byte[0], '02X') + ' '
+ es = byte[2]
+ self.put(ss, es, self.out_ann, [ann, [prefix + data]])
+
+ def handle_WC(self):
+ start_addr = self.mosi_bytes[0][0] & 0x0F
+ if start_addr > 9:
+ return
+ self.parse_config_registers(start_addr, self.mosi_bytes[1:], True)
+
+ def handle_RC(self):
+ start_addr = self.mosi_bytes[0][0] & 0x0F
+ if start_addr > 9:
+ return
+ self.parse_config_registers(start_addr, self.miso_bytes[1:], False)
+
+ def handle_WTP(self):
+ self.dump_cmd_bytes('Write TX payload.: ', self.mosi_bytes, Ann.TX)
+
+ def handle_RTP(self):
+ self.dump_cmd_bytes('Read TX payload: ', self.miso_bytes, Ann.RESP)
+
+ def handle_WTA(self):
+ self.dump_cmd_bytes('Write TX addr: ', self.mosi_bytes, Ann.REG_WR)
+
+ def handle_RTA(self):
+ self.dump_cmd_bytes('Read TX addr: ', self.miso_bytes, Ann.RESP)
+
+ def handle_RRP(self):
+ self.dump_cmd_bytes('Read RX payload: ', self.miso_bytes, Ann.RX)
+
+ def handle_CC(self):
+ cmd, dta = self.mosi_bytes[0], self.mosi_bytes[1]
+ channel = ((cmd[0] & 0x01) << 8) + dta
+ data = self.extract_vars(CHN_CFG, cmd[0])
+ data += '| CHN = ' + str(channel)
+ self.put(self.mosi_bytes[0][1], self.mosi_bytes[1][2],
+ self.out_ann, [Ann.REG_WR, [data]])
+
+ def handle_STAT(self):
+ status = 'STAT = ' + self.extract_vars(STAT_REG, self.miso_bytes[0][0])
+ self.put(self.miso_bytes[0][1], self.miso_bytes[0][2],
+ self.out_ann, [Ann.REG_RD, [status]])
+
+ def process_cmd(self):
+ cmd, cmd_name, cmd_hnd = '', '', None
+
+ for byte in self.mosi_bytes:
+ cmd += hex(byte[0]) + ' '
+
+ cmd = self.mosi_bytes[0][0]
+
+ if (cmd & 0xF0) == 0x00:
+ cmd_name, cmd_hnd = 'CMD: W_CONFIG (WC)', self.handle_WC
+ elif (cmd & 0xF0) == 0x10:
+ cmd_name, cmd_hnd = 'CMD: R_CONFIG (RC)', self.handle_RC
+ elif cmd == 0x20:
+ cmd_name, cmd_hnd = 'CMD: W_TX_PAYLOAD (WTP)', self.handle_WTP
+ elif cmd == 0x21:
+ cmd_name, cmd_hnd = 'CMD: R_TX_PAYLOAD (RTP)', self.handle_RTP
+ elif cmd == 0x22:
+ cmd_name, cmd_hnd = 'CMD: W_TX_ADDRESS (WTA)', self.handle_WTA
+ elif cmd == 0x23:
+ cmd_name, cmd_hnd = 'CMD: R_TX_ADDRESS (RTA)', self.handle_RTA
+ elif cmd == 0x24:
+ cmd_name, cmd_hnd = 'CMD: R_RX_PAYLOAD (RRP)', self.handle_RRP
+ elif (cmd & 0xF0 == 0x80):
+ cmd_name, cmd_hnd = 'CMD: CHANNEL_CONFIG (CC)', self.handle_CC
+
+ # Report command name.
+ self.put(self.cmd_samples['ss'], self.cmd_samples['es'],
+ self.out_ann, [Ann.CMD, [cmd_name]])
+
+ # Handle status byte.
+ self.handle_STAT()
+
+ # Handle command.
+ if cmd_hnd is not None:
+ cmd_hnd()
+
+ def set_cs_status(self, sample, asserted):
+ if self.cs_asserted == asserted:
+ return
+
+ if asserted:
+ self.cmd_samples['ss'] = sample
+ self.cmd_samples['es'] = -1
+ else:
+ self.cmd_samples['es'] = sample
+
+ self.cs_asserted = asserted
+
+ def decode(self, ss, es, data):
+ ptype, data1, data2 = data
+
+ if ptype == 'CS-CHANGE':
+ if data1 is None and data2 is None:
+ self.requirements_met = False
+ raise ChannelError('CS# pin required.')
+
+ if data1 is None and data2 == 0:
+ self.set_cs_status(ss, True)
+
+ elif data1 is None and data2 == 1:
+ self.set_cs_status(ss, False)
+
+ elif data1 == 1 and data2 == 0:
+ self.set_cs_status(ss, True)
+
+ elif data1 == 0 and data2 == 1:
+ self.set_cs_status(ss, False)
+ if len(self.mosi_bytes):
+ self.process_cmd()
+ self.reset()
+
+ elif ptype == 'DATA':
+ # Ignore traffic if CS is not asserted.
+ if self.cs_asserted is False:
+ return
+
+ mosi, miso = data1, data2
+ if miso is None or mosi is None:
+ raise ChannelError('Both MISO and MOSI pins required.')
+
+ self.mosi_bytes.append((mosi, ss, es))
+ self.miso_bytes.append((miso, ss, es))
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Comlab AG
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder takes a set of logic input signals, and interprets
+their bit pattern according to user specifications as different kinds of
+numbers, or an enumeration of e.g. machine states.
+
+Supported formats are: signed and unsigned integers, fixed point numbers,
+IEEE754 floating point numbers, and number to text mapping controlled by
+external data files. (Support for half precision floats depends on the
+Python runtime, and may not universally be available.)
+
+User provided text mapping files can either use the JSON format:
+ {"one": 1, "two": 2, "four": 4}
+or the Python programming language:
+ enumtext = { 1: "one", 2: "two", 3: "three", }
+
+In addition to all enum values on one row (sequential presentation of
+the data), a limited number of enum values also are shown in tabular
+presentation, which can help visualize state machines or task switches.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Comlab AG
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# This implementation started as a "vector slicer", then turned into the
+# "numbers and states" decoder, because users always had the freedom to
+# connect any logic signal to either of the decoder inputs. That's when
+# slicing vectors took second seat, and just was not needed any longer
+# in the strict sense.
+#
+# TODO
+# - Find an appropriate number of input channels, and maximum enum slots.
+# - Re-check correctness of signed integers. Signed fixed point is based
+# on integers and transparently benefits from fixes and improvements.
+# - Local formatting in individual decoders becomes obsolete when common
+# support for user selected formatting gets introduced.
+# - There is overlap with the 'parallel' decoder. Ideally the numbers
+# decoder could stack on top of parallel, but parallel currently is
+# severely limited in its number of input channels, and dramatically
+# widening the parallel decoder may be undesirable.
+
+from common.srdhelper import bitpack
+import json
+import sigrokdecode as srd
+import struct
+
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[<ptype>, <pdata>]
+
+This is a list of <ptype>s and their respective <pdata> values:
+ - 'RAW': The data is a tuple of bit count and bit pattern (a number,
+ assuming unsigned integer presentation of the input data bit pattern).
+ - 'NUMBER': The data is the conversion result of the bit pattern.
+ - 'ENUM': The data is a tuple of the raw number and its mapped text.
+'''
+
+# TODO Better raise the number of channels to 32. This allows access to
+# IEEE754 single precision numbers, and shall cover most busses, _and_
+# remains within most logic analyzers' capabilities, and keeps the UI
+# dialog somewhat managable. What's a good default for the number of
+# enum slots (which translate to annotation rows)? Notice that 2 to the
+# power of the channel count is way out of the question. :)
+_max_channels = 16
+_max_enum_slots = 32
+
+class ChannelError(Exception):
+ pass
+
+class Pin:
+ CLK, BIT_0 = range(2)
+ BIT_N = BIT_0 + _max_channels
+
+class Ann:
+ RAW, NUM = range(2)
+ ENUM_0 = NUM + 1
+ ENUM_OVR = ENUM_0 + _max_enum_slots
+ ENUMS = range(ENUM_0, ENUM_OVR)
+ WARN = ENUM_OVR + 1
+
+ @staticmethod
+ def enum_indices():
+ return [i for i in range(Ann.ENUMS)]
+
+ @staticmethod
+ def get_enum_idx(code):
+ if code in range(_max_enum_slots):
+ return Ann.ENUM_0 + code
+ return Ann.ENUM_OVR
+
+def _channel_decl(count):
+ return tuple([
+ {'id': 'bit{}'.format(i), 'name': 'Bit{}'.format(i), 'desc': 'Bit position {}'.format(i)}
+ for i in range(count)
+ ])
+
+def _enum_cls_decl(count):
+ return tuple([
+ ('enum{}'.format(i), 'Enumeration slot {}'.format(i))
+ for i in range(count)
+ ] + [('enumovr', 'Enumeration overflow')])
+
+def _enum_rows_decl(count):
+ return tuple([
+ ('enums{}'.format(i), 'Enumeration slots {}'.format(i), (Ann.ENUM_0 + i,))
+ for i in range(count)
+ ] + [('enumsovr', 'Enumeration overflows', (Ann.ENUM_OVR,))])
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'numbers_and_state'
+ name = 'Numbers and State'
+ longname = 'Interpret bit patters as numbers or state enums'
+ desc = 'Interpret bit patterns as different kinds of numbers (integer, float, enum).'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['numbers_and_state']
+ tags = ['Encoding', 'Util']
+ optional_channels = (
+ {'id': 'clk', 'name': 'Clock', 'desc': 'Clock'},
+ ) + _channel_decl(_max_channels)
+ options = (
+ {'id': 'clkedge', 'desc': 'Clock edge', 'default': 'rising',
+ 'values': ('rising', 'falling', 'either')},
+ {'id': 'count', 'desc': 'Total bits count', 'default': 0},
+ {'id': 'interp', 'desc': 'Interpretation', 'default': 'unsigned',
+ 'values': ('unsigned', 'signed', 'fixpoint', 'fixsigned', 'ieee754', 'enum')},
+ {'id': 'fracbits', 'desc': 'Fraction bits count', 'default': 0},
+ {'id': 'mapping', 'desc': 'Enum to text map file',
+ 'default': 'enumtext.json'},
+ {'id': 'format', 'desc': 'Number format', 'default': '-',
+ 'values': ('-', 'bin', 'oct', 'dec', 'hex')},
+ )
+ annotations = (
+ ('raw', 'Raw pattern'),
+ ('number', 'Number'),
+ ) + _enum_cls_decl(_max_enum_slots) + (
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('raws', 'Raw bits', (Ann.RAW,)),
+ ('numbers', 'Numbers', (Ann.NUM,)),
+ ) + _enum_rows_decl(_max_enum_slots) + (
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def putg(self, ss, es, cls, data):
+ self.put(ss, es, self.out_ann, [cls, data])
+
+ def putpy(self, ss, es, ptype, pdata):
+ self.put(ss, es, self.out_python, (ptype, pdata))
+
+ def grab_pattern(self, pins):
+ '''Get a bit pattern from potentially incomplete probes' values.'''
+
+ # Pad and trim the input data, to achieve the user specified
+ # total number of bits. Map all unassigned signals to 0 (low).
+ # Return raw number (unsigned integer interpreation).
+ bits = pins + (None,) * self.bitcount
+ bits = bits[:self.bitcount]
+ bits = [b if b in (0, 1) else 0 for b in bits]
+ pattern = bitpack(bits)
+ return pattern
+
+ def handle_pattern(self, ss, es, pattern):
+ fmt = '{{:0{}b}}'.format(self.bitcount)
+ txt = fmt.format(pattern)
+ self.putg(ss, es, Ann.RAW, [txt])
+ self.putpy(ss, es, 'RAW', (self.bitcount, pattern))
+
+ try:
+ value = self.interpreter(ss, es, pattern)
+ except:
+ value = None
+ if value is None:
+ return
+ self.putpy(ss, es, 'NUMBER', value)
+ try:
+ formatted = self.formatter(ss, es, value)
+ except:
+ formatted = None
+ if formatted:
+ self.putg(ss, es, Ann.NUM, formatted)
+ if self.interpreter == self.interp_enum:
+ cls = Ann.get_enum_idx(pattern)
+ self.putg(ss, es, cls, formatted)
+ self.putpy(ss, es, 'ENUM', (value, formatted))
+
+ def interp_unsigned(self, ss, es, pattern):
+ value = pattern
+ return value
+
+ def interp_signed(self, ss, es, pattern):
+ if not 'signmask' in self.interp_state:
+ self.interp_state.update({
+ 'signmask': 1 << (self.bitcount - 1),
+ 'signfull': 1 << self.bitcount,
+ })
+ is_neg = pattern & self.interp_state['signmask']
+ if is_neg:
+ value = -(self.interp_state['signfull'] - pattern)
+ else:
+ value = pattern
+ return value
+
+ def interp_fixpoint(self, ss, es, pattern):
+ if not 'fixdiv' in self.interp_state:
+ self.interp_state.update({
+ 'fixsign': self.options['interp'] == 'fixsigned',
+ 'fixdiv': 2 ** self.options['fracbits'],
+ })
+ if self.interp_state['fixsign']:
+ value = self.interp_signed(ss, es, pattern)
+ else:
+ value = self.interp_unsigned(ss, es, pattern)
+ value /= self.interp_state['fixdiv']
+ return value
+
+ def interp_ieee754(self, ss, es, pattern):
+ if not 'ieee_has_16bit' in self.interp_state:
+ self.interp_state.update({
+ 'ieee_fmt_int_16': '=H',
+ 'ieee_fmt_flt_16': '=e',
+ 'ieee_fmt_int_32': '=L',
+ 'ieee_fmt_flt_32': '=f',
+ 'ieee_fmt_int_64': '=Q',
+ 'ieee_fmt_flt_64': '=d',
+ })
+ try:
+ fmt = self.interp_state.update['ieee_fmt_flt_16']
+ has_16bit_support = 8 * struct.calcsize(fmt) == 16
+ except:
+ has_16bit_support = False
+ self.interp_state['ieee_has_16bit'] = has_16bit_support
+ if self.bitcount == 16:
+ if not self.interp_state['ieee_has_16bit']:
+ return None
+ buff = struct.pack(self.interp_state['ieee_fmt_int_16'], pattern)
+ value, = struct.unpack(self.interp_state['ieee_fmt_flt_16'], buff)
+ return value
+ if self.bitcount == 32:
+ buff = struct.pack(self.interp_state['ieee_fmt_int_32'], pattern)
+ value, = struct.unpack(self.interp_state['ieee_fmt_flt_32'], buff)
+ return value
+ if self.bitcount == 64:
+ buff = struct.pack(self.interp_state['ieee_fmt_int_64'], pattern)
+ value, = struct.unpack(self.interp_state['ieee_fmt_flt_64'], buff)
+ return value
+ return None
+
+ def interp_enum(self, ss, es, pattern):
+ if not 'enum_map' in self.interp_state:
+ self.interp_state.update({
+ 'enum_fn': self.options['mapping'],
+ 'enum_map': {},
+ 'enum_have_map': False,
+ })
+ try:
+ fn = self.interp_state['enum_fn']
+ # TODO Optionally try in several locations? Next to the
+ # decoder implementation? Where else? Expect users to
+ # enter absolute paths?
+ with open(fn, 'r') as f:
+ maptext = f.read()
+ maptable = {}
+ if fn.endswith('.js') or fn.endswith('.json'):
+ # JSON requires string literals on the LHS, so the
+ # table is written "in reverse order".
+ js_table = json.loads(maptext)
+ for k, v in js_table.items():
+ maptable[v] = k
+ elif fn.endswith('.py'):
+ # Expect a specific identifier at the Python module
+ # level, and assume that it's a dictionary.
+ py_table = {}
+ exec(maptext, py_table)
+ maptable.update(py_table['enumtext'])
+ self.interp_state['enum_map'].update(maptable)
+ self.interp_state['enum_have_map'] = True
+ except:
+ # Silently ignore failure. This happens while the user
+ # is typing the filename, and is non-fatal. If the file
+ # exists and is not readable or not valid or of unknown
+ # format, the worst thing that can happen is that the
+ # decoder implementation keeps using "anonymous" phrases
+ # until a mapping has become available. No harm is done.
+ # This decoder cannot tell intermediate from final file
+ # read attempts, so we cannot raise severity here.
+ pass
+ value = self.interp_state['enum_map'].get(pattern, None)
+ if value is None:
+ value = pattern
+ return value
+
+ def format_native(self, ss, es, value):
+ return ['{}'.format(value),]
+
+ def format_bin(self, ss, es, value):
+ if not self.format_string:
+ self.format_string = '{{:0{}b}}'.format(self.bitcount)
+ return [self.format_string.format(value)]
+
+ def format_oct(self, ss, es, value):
+ if not self.format_string:
+ self.format_string = '{{:0{}o}}'.format((self.bitcount + 3 - 1) // 3)
+ return [self.format_string.format(value)]
+
+ def format_dec(self, ss, es, value):
+ if not self.format_string:
+ self.format_string = '{:d}'
+ return [self.format_string.format(value)]
+
+ def format_hex(self, ss, es, value):
+ if not self.format_string:
+ self.format_string = '{{:0{}x}}'.format((self.bitcount + 4 - 1) // 4)
+ return [self.format_string.format(value)]
+
+ def decode(self):
+ channels = [ch for ch in range(_max_channels) if self.has_channel(ch)]
+ have_clk = Pin.CLK in channels
+ if have_clk:
+ channels.remove(Pin.CLK)
+ if not channels:
+ raise ChannelError("Need at least one bit channel.")
+ if have_clk:
+ clkedge = {
+ 'rising': 'r',
+ 'falling': 'f',
+ 'either': 'e',
+ }.get(self.options['clkedge'])
+ wait_cond = {Pin.CLK: clkedge}
+ else:
+ wait_cond = [{ch: 'e'} for ch in channels]
+
+ bitcount = self.options['count']
+ if not bitcount:
+ bitcount = channels[-1] - Pin.BIT_0 + 1
+ self.bitcount = bitcount
+
+ self.interpreter = {
+ 'unsigned': self.interp_unsigned,
+ 'signed': self.interp_signed,
+ 'fixpoint': self.interp_fixpoint,
+ 'fixsigned': self.interp_fixpoint,
+ 'ieee754': self.interp_ieee754,
+ 'enum': self.interp_enum,
+ }.get(self.options['interp'])
+ self.interp_state = {}
+ self.formatter = {
+ '-': self.format_native,
+ 'bin': self.format_bin,
+ 'oct': self.format_oct,
+ 'dec': self.format_dec,
+ 'hex': self.format_hex,
+ }.get(self.options['format'])
+ self.format_string = None
+
+ pins = self.wait()
+ ss = self.samplenum
+ prev_pattern = self.grab_pattern(pins[Pin.BIT_0:])
+ while True:
+ pins = self.wait(wait_cond)
+ es = self.samplenum
+ pattern = self.grab_pattern(pins[Pin.BIT_0:])
+ if pattern == prev_pattern:
+ continue
+ self.handle_pattern(ss, es, prev_pattern)
+ ss = es
+ prev_pattern = pattern
('nunchuk-write', 'Nunchuk write'),
('cmd-init', 'Init command'),
('summary', 'Summary'),
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('regs', 'Registers', tuple(range(13))),
- ('summary', 'Summary', (13,)),
+ ('summaries', 'Summaries', (13,)),
('warnings', 'Warnings', (14,)),
)
)
annotations = (
('bit', 'Bit'),
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
('reset', 'Reset'),
('presence', 'Presence'),
- ('overdrive', 'Overdrive speed notifications'),
+ ('overdrive', 'Overdrive speed notification'),
)
annotation_rows = (
('bits', 'Bits', (0, 2, 3)),
outputs = ['onewire_network']
tags = ['Embedded/industrial']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
('info', 'Info'),
('1111', '1111'),
('1010', '1010'),
- ('diffman', 'Diff Man'),
+ ('diffman', 'Diff man'),
('nrz', 'NRZ'),
)
annotation_rows = (
- ('frame', 'Framing',(0,)),
- ('info', 'Info', (1,)),
+ ('frames', 'Framing', (0,)),
+ ('info-vals', 'Info', (1,)),
('man1111', 'Man 1111', (2,)),
('man1010', 'Man 1010', (3,)),
- ('diffman', 'Diff Man', (4,)),
- ('nrz', 'NRZ', (5,)),
+ ('diffmans', 'Diff man', (4,)),
+ ('nrz-vals', 'NRZ', (5,)),
)
binary = (
('pulse-lengths', 'Pulse lengths'),
annotation_rows = (
('bits', 'Bits', (0,)),
('fields', 'Fields', (1, 3, 4)),
- ('l2', 'Level 2', (2,)),
+ ('l2vals', 'Level 2', (2,)),
)
binary = (
('data-hex', 'Hex data'),
('compare', 'Compare', (1,)),
('fields', 'Fields', (2,)),
('ref_fields', 'Ref fields', (3,)),
- ('level2', 'L2', (4,)),
- ('ref_level2', 'Ref L2', (5,)),
+ ('level2_vals', 'L2', (4,)),
+ ('ref_level2_vals', 'Ref L2', (5,)),
)
options = (
{'id': 'displayas', 'desc': 'Display as', 'default': 'Nibble - Hex',
outputs = []
tags = ['Wireless/RF']
annotations = (
- ('text-verbose', 'Human-readable text (verbose)'),
- ('text', 'Human-readable text'),
- ('warnings', 'Human-readable warnings'),
+ ('text-verbose', 'Text (verbose)'),
+ ('text', 'Text'),
+ ('warning', 'Warning'),
)
def __init__(self):
'''
This protocol decoder can decode synchronous parallel buses with various
-number of data bits/channels and one (optional) clock line.
+data bits/channels counts, an (optional) clock line, and an (optional)
+select/enable/reset line.
-If no clock line is supplied, the decoder works slightly differently in
-that it interprets every transition on any of the supplied data channels
-like there had been a clock transition.
+Data bits are taken from the decoder's lowest connected input pins. The
+input signal's data lines count need not span the full amount of the
+decoder's maximum supported data lines count. Not connected data lines
+are assumed to be low.
-It is required to use the lowest data channels, and use consecutive ones.
-For example, for a 4-bit sync parallel bus, channels D0/D1/D2/D3 (and CLK)
-should be used. Using combinations like D7/D12/D3/D15 is not supported.
-For an 8-bit bus you should use D0-D7, for a 16-bit bus use D0-D15 and so on.
+Example use cases are: Connect D3/D2/D1/D0 (and CLK) to a 4-bit bus.
+Connect D7 and D6 to inspect the two most significant bits of an 8-bit
+bus (and have 8-bit values shown instead of just 2-bit values).
+
+When provided, the specified clock edge determines when data lines get
+sampled. Without a clock spec, each transition on any of the data lines
+will be shown, which can become busy/noisy depending on the input data.
+
+Another signal optionally can control the period of time within which
+the data lines' bit pattern gets interpreted. Typical use cases would be
+reset, or select, or enable signals that are related to the bus' data
+communication. This optional signal can also improve synchronization to
+wider payload data which spans several bus cycles (multiplexing).
'''
from .pd import Decoder
word <worditemcount> is 7, and so on.
'''
-def channel_list(num_channels):
- l = [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}]
- for i in range(num_channels):
- d = {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data line %d' % i}
- l.append(d)
- return tuple(l)
+NUM_CHANNELS = 16
+
+class Pin:
+ CLOCK = 0
+ DATA_0 = CLOCK + 1
+ DATA_N = DATA_0 + NUM_CHANNELS
+ # BEWARE! DATA_N points _beyond_ the data partition (Python range(3)
+ # semantics, useful to have to simplify other code locations).
+ RESET = DATA_N
+
+class Ann:
+ ITEM, WORD, WARN = range(3)
class ChannelError(Exception):
pass
-NUM_CHANNELS = 8
-
class Decoder(srd.Decoder):
api_version = 3
id = 'parallel'
inputs = ['logic']
outputs = ['parallel']
tags = ['Util']
- optional_channels = channel_list(NUM_CHANNELS)
+ optional_channels = tuple(
+ [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}] +
+ [
+ {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data line %d' % i}
+ for i in range(NUM_CHANNELS)
+ ] +
+ [{'id': 'rst', 'name': 'RST', 'desc': 'RESET line'}]
+ )
options = (
{'id': 'clock_edge', 'desc': 'Clock edge to sample on',
- 'default': 'rising', 'values': ('rising', 'falling')},
+ 'default': 'rising', 'values': ('rising', 'falling', 'either')},
+ {'id': 'reset_polarity', 'desc': 'Reset line polarity',
+ 'default': 'low-active', 'values': ('low-active', 'high-active')},
{'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
'default': 0},
{'id': 'endianness', 'desc': 'Data endianness',
'default': 'little', 'values': ('little', 'big')},
)
annotations = (
- ('items', 'Items'),
- ('words', 'Words'),
+ ('item', 'Item'),
+ ('word', 'Word'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('items', 'Items', (0,)),
- ('words', 'Words', (1,)),
+ ('items', 'Items', (Ann.ITEM,)),
+ ('words', 'Words', (Ann.WORD,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+ binary = (
+ ('binary', 'Binary'),
)
def __init__(self):
self.reset()
def reset(self):
- self.items = []
- self.saved_item = None
- self.ss_item = self.es_item = None
- self.saved_word = None
- self.ss_word = self.es_word = None
- self.first = True
+ self.pend_item = None
+ self.word_items = []
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_ann = self.register(srd.OUTPUT_ANN)
- def putpb(self, data):
- self.put(self.ss_item, self.es_item, self.out_python, data)
-
- def putb(self, data):
- self.put(self.ss_item, self.es_item, self.out_ann, data)
-
- def putpw(self, data):
- self.put(self.ss_word, self.es_word, self.out_python, data)
-
- def putw(self, data):
- self.put(self.ss_word, self.es_word, self.out_ann, data)
-
- def handle_bits(self, item, used_pins):
-
- # If a word was previously accumulated, then emit its annotation
- # now after its end samplenumber became available.
- if self.saved_word is not None:
- if self.options['wordsize'] > 0:
- self.es_word = self.samplenum
- self.putw([1, [self.fmt_word.format(self.saved_word)]])
- self.putpw(['WORD', self.saved_word])
- self.saved_word = None
-
- # Defer annotations for individual items until the next sample
- # is taken, and the previous sample's end samplenumber has
- # become available.
- if self.first:
- # Save the start sample and item for later (no output yet).
- self.ss_item = self.samplenum
- self.first = False
- self.saved_item = item
- else:
- # Output the saved item (from the last CLK edge to the current).
- self.es_item = self.samplenum
- self.putpb(['ITEM', self.saved_item])
- self.putb([0, [self.fmt_item.format(self.saved_item)]])
- self.ss_item = self.samplenum
- self.saved_item = item
-
- # Get as many items as the configured wordsize specifies.
- if not self.items:
- self.ss_word = self.samplenum
- self.items.append(item)
- ws = self.options['wordsize']
- if len(self.items) < ws:
+ def putg(self, ss, es, ann, txts):
+ self.put(ss, es, self.out_ann, [ann, txts])
+
+ def putpy(self, ss, es, ann, data):
+ self.put(ss, es, self.out_python, [ann, data])
+
+ def putbin(self, ss, es, ann_class, data):
+ self.put(ss, es, self.out_binary, [ann_class, data])
+
+ def flush_word(self, bus_width):
+ if not self.word_items:
return
+ word_size = self.options['wordsize']
+
+ items = self.word_items
+ ss, es = items[0][0], items[-1][1]
+ items = [i[2] for i in items]
+ if self.options['endianness'] == 'big':
+ items.reverse()
+ word = sum([d << (i * bus_width) for i, d in enumerate(items)])
+
+ txts = [self.fmt_word.format(word)]
+ self.putg(ss, es, Ann.WORD, txts)
+ self.putpy(ss, es, 'WORD', (word, bus_width, word_size))
+
+ if len(items) != word_size:
+ txts = ['incomplete word size', 'word size', 'ws']
+ self.putg(ss, es, Ann.WARN, txts)
+
+ self.word_items.clear()
+
+ def queue_word(self, now, item, bus_width):
+ wordsize = self.options['wordsize']
+ if not wordsize:
+ return
+
+ # Terminate a previously seen item of a word first. Emit the
+ # word's annotation when the last item's end was seen.
+ if self.word_items:
+ ss, _, data = self.word_items[-1]
+ es = now
+ self.word_items[-1] = (ss, es, data)
+ if len(self.word_items) == wordsize:
+ self.flush_word(bus_width)
+
+ # Start tracking the currently seen item (yet unknown end time).
+ if item is not None:
+ pend = (now, None, item)
+ self.word_items.append(pend)
+
+ def handle_bits(self, now, item, bus_width):
- # Collect words and prepare annotation details, but defer emission
- # until the end samplenumber becomes available.
- endian = self.options['endianness']
- if endian == 'big':
- self.items.reverse()
- word = sum([self.items[i] << (i * used_pins) for i in range(ws)])
- self.saved_word = word
- self.items = []
+ # Optionally flush a previously started item.
+ if self.pend_item:
+ ss, _, data = self.pend_item
+ self.pend_item = None
+ es = now
+ txts = [self.fmt_item.format(data)]
+ self.putg(ss, es, Ann.ITEM, txts)
+ self.putpy(ss, es, 'ITEM', (data, bus_width))
+ self.putbin(ss, es, 0, data.to_bytes(1, byteorder='big'))
+
+ # Optionally queue the currently seen item.
+ if item is not None:
+ self.pend_item = (now, None, item)
+
+ # Pass the current item to the word accumulation logic.
+ self.queue_word(now, item, bus_width)
def decode(self):
# Determine which (optional) channels have input data. Insist in
# a non-empty input data set. Cope with sparse connection maps.
# Store enough state to later "compress" sampled input data.
- max_possible = len(self.optional_channels)
- idx_channels = [
+ data_indices = [
idx if self.has_channel(idx) else None
- for idx in range(max_possible)
+ for idx in range(Pin.DATA_0, Pin.DATA_N)
]
- has_channels = [idx for idx in idx_channels if idx is not None]
- if not has_channels:
- raise ChannelError('At least one channel has to be supplied.')
- max_connected = max(has_channels)
-
- # Determine .wait() conditions, depending on the presence of a
- # clock signal. Either inspect samples on the configured edge of
- # the clock, or inspect samples upon ANY edge of ANY of the pins
- # which provide input data.
- if self.has_channel(0):
- edge = self.options['clock_edge'][0]
- conds = {0: edge}
- else:
- conds = [{idx: 'e'} for idx in has_channels]
+ has_data = [idx for idx in data_indices if idx is not None]
+ if not has_data:
+ raise ChannelError('Need at least one data channel.')
+ max_connected = max(has_data)
# Pre-determine which input data to strip off, the width of
# individual items and multiplexed words, as well as format
# strings here. This simplifies call sites which run in tight
# loops later.
- idx_strip = max_connected + 1
- num_item_bits = idx_strip - 1
+ upper_data_bound = max_connected + 1
+ num_item_bits = upper_data_bound - Pin.DATA_0
num_word_items = self.options['wordsize']
num_word_bits = num_item_bits * num_word_items
- num_digits = (num_item_bits + 3) // 4
+ num_digits = (num_item_bits + 4 - 1) // 4
self.fmt_item = "{{:0{}x}}".format(num_digits)
- num_digits = (num_word_bits + 3) // 4
+ num_digits = (num_word_bits + 4 - 1) // 4
self.fmt_word = "{{:0{}x}}".format(num_digits)
+ # Determine .wait() conditions, depending on the presence of a
+ # clock signal. Either inspect samples on the configured edge of
+ # the clock, or inspect samples upon ANY edge of ANY of the pins
+ # which provide input data.
+ conds = []
+ cond_idx_clock = None
+ cond_idx_data_0 = None
+ cond_idx_data_N = None
+ cond_idx_reset = None
+ has_clock = self.has_channel(Pin.CLOCK)
+ if has_clock:
+ cond_idx_clock = len(conds)
+ edge = {
+ 'rising': 'r',
+ 'falling': 'f',
+ 'either': 'e',
+ }.get(self.options['clock_edge'])
+ conds.append({Pin.CLOCK: edge})
+ else:
+ cond_idx_data_0 = len(conds)
+ conds.extend([{idx: 'e'} for idx in has_data])
+ cond_idx_data_N = len(conds)
+ has_reset = self.has_channel(Pin.RESET)
+ if has_reset:
+ cond_idx_reset = len(conds)
+ conds.append({Pin.RESET: 'e'})
+ reset_active = {
+ 'low-active': 0,
+ 'high-active': 1,
+ }.get(self.options['reset_polarity'])
+
# Keep processing the input stream. Assume "always zero" for
# not-connected input lines. Pass data bits (all inputs except
- # clock) to the handle_bits() method.
+ # clock and reset) to the handle_bits() method. Handle reset
+ # edges first and data changes then, within the same iteration.
+ # This results in robust operation for low-oversampled input.
+ in_reset = False
while True:
- pins = self.wait(conds)
- bits = [0 if idx is None else pins[idx] for idx in idx_channels]
- item = bitpack(bits[1:idx_strip])
- self.handle_bits(item, num_item_bits)
+ try:
+ pins = self.wait(conds)
+ except EOFError as e:
+ break
+ clock_edge = cond_idx_clock is not None and self.matched[cond_idx_clock]
+ data_edge = cond_idx_data_0 is not None and [idx for idx in range(cond_idx_data_0, cond_idx_data_N) if self.matched[idx]]
+ reset_edge = cond_idx_reset is not None and self.matched[cond_idx_reset]
+
+ if reset_edge:
+ in_reset = pins[Pin.RESET] == reset_active
+ if in_reset:
+ self.handle_bits(self.samplenum, None, num_item_bits)
+ self.flush_word(num_item_bits)
+ if in_reset:
+ continue
+
+ if clock_edge or data_edge:
+ data_bits = [0 if idx is None else pins[idx] for idx in data_indices]
+ data_bits = data_bits[:num_item_bits]
+ item = bitpack(data_bits)
+ self.handle_bits(self.samplenum, item, num_item_bits)
+
+ self.handle_bits(self.samplenum, None, num_item_bits)
+ # TODO Determine whether a WARN annotation needs to get emitted.
+ # The decoder has not seen the end of the last accumulated item.
+ # Instead it just ran out of input data.
# TODO: Other I²C functions: general call / reset address, device ID address.
+def logic_channels(num_channels):
+ l = []
+ for i in range(num_channels):
+ l.append(tuple(['p%d' % i, 'P%d' % i]))
+ return tuple(l)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'pca9571'
annotations = (
('register', 'Register type'),
('value', 'Register value'),
- ('warning', 'Warning messages'),
+ ('warning', 'Warning'),
)
+ logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS)
annotation_rows = (
('regs', 'Registers', (0, 1)),
('warnings', 'Warnings', (2,)),
def reset(self):
self.state = 'IDLE'
self.last_write = 0xFF # Chip port default state is high.
+ self.last_write_es = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_logic = self.register(srd.OUTPUT_LOGIC)
+
+ def flush(self):
+ self.put_logic_states()
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
+ def put_logic_states(self):
+ if (self.es > self.last_write_es):
+ data = bytes([self.last_write])
+ self.put(self.last_write_es, self.es, self.out_logic, [0, data])
+ self.last_write_es = self.es
+
def handle_io(self, b):
if self.state == 'READ DATA':
operation = ['Outputs read', 'R']
'(%02X) are different' % self.last_write]])
else:
operation = ['Outputs set', 'W']
+ self.put_logic_states()
self.last_write = b
+
self.putx([1, [operation[0] + ': %02X' % b,
operation[1] + ': %02X' % b]])
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder interprets the PJDL data link of the PJON protocol.
+Bytes and frames get extracted from single wire serial communication
+(which often is referred to as "software bitbang" because that's what
+the Arduino reference implementation happens to do).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# See the https://www.pjon.org/ PJON project page and especially the
+# https://www.pjon.org/PJDL-specification-v4.1.php PJDL v4.1 spec for
+# the "Padded Jittering Data Link" single wire serial data link layer.
+
+# TODO
+# - Improve (fix, and extend) carrier sense support. Detection of the
+# idle/busy connection state is incomplete and fragile. Getting 'IDLE'
+# operational in the PJDL decoder would greatly help the PJON decoder
+# to flush ACK details before the start of new frames.
+# - Check the correctness of timing assumptions. This implementation has
+# support for tolerances, which the spec does not discuss. Though real
+# world traffic was found to not decode at all with strict spec values
+# and without tolerances, while communication peers were able to talk
+# to each other. This needs more attention.
+# - Check robustness when input data contains glitches. The spec does
+# not discuss how to handle these. Data bit sampling happens to work
+# because their value is taken at the center of the bit time. But
+# pad bits suffer badly from glitches, which breaks frame inspection
+# as well.
+# - Cleanup the decoder implementation in general terms. Some details
+# have become obsolete ("edges", "pads"), and/or are covered by other
+# code paths.
+# - Implement more data link decoders which can feed their output into
+# the PJON protocol decoder. Candidates are: PJDLR, PJDLS, TSDL.
+# - Determine whether or not the data link layer should interpret any
+# frame content. From my perspective it should not, and needs not in
+# the strict sense. Possible gains would be getting the (expected!)
+# packet length, or whether a synchronous response is requested. But
+# this would duplicate knowledge which should remain internal to the
+# PJON decoder. And this link layer decoder neither shall assume that
+# the input data would be correct, or complete. Instead the decoder
+# shall remain usable on captures which demonstrate faults, and be
+# helpful in pointing them out. The design goal is to extract the
+# maximum of information possible, and pass it on as transparently
+# as possible.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+from math import ceil, floor
+
+'''
+OUTPUT_PYTHON format for stacked decoders:
+
+General packet format:
+[<ptype>, <pdata>]
+
+This is the list of <ptype>s and their respective <pdata> values:
+
+Carrier sense:
+- 'IDLE': <pdata> is the pin level (always 0).
+- 'BUSY': <pdata> is always True.
+
+Raw bit slots:
+- 'PAD_BIT': <pdata> is the pin level (always 1).
+- 'DATA_BIT': <pdata> is the pin level (0, or 1).
+- 'SHORT_BIT': <pdata> is the pin level (always 1).
+- 'SYNC_LOSS': <pdata> is an arbitrary text (internal use only).
+
+Date bytes and frames:
+- 'SYNC_PAD': <pdata> is True. Spans the high pad bit as well as the
+ low data bit.
+- 'DATA_BYTE': <pdata> is the byte value (0..255).
+- 'FRAME_INIT': <pdata> is True. Spans three sync pads.
+- 'FRAME_DATA': <pdata> is the sequence of bytes in the frame. Non-data
+ phases in the frame get represented by strings instead of numbers
+ ('INIT', 'SYNC', 'SHORT', 'WAIT'). Frames can be incomplete, depending
+ on the decoder's input data.
+- 'SYNC_RESP_WAIT': <pdata> is always True.
+
+Notice that this link layer decoder is not aware of frame content. Will
+neither check packet length, nor variable width fields, nor verify the
+presence of requested synchronous responses. Cannot tell the sequence of
+frame bytes then ACK bytes (without wait phase) from just frame bytes.
+An upper layer protocol decoder will interpret content, the link layer
+decoder remains as transparent as possible, and will neither assume
+correct nor complete input data.
+'''
+
+# Carrier sense, and synchronization loss implementation is currently
+# incomplete, and results in too many too short annotations, some of
+# them spurious and confusing. TODO Improve the implementation.
+_with_ann_carrier = False
+_with_ann_sync_loss = False
+
+PIN_DATA, = range(1)
+ANN_CARRIER_BUSY, ANN_CARRIER_IDLE, \
+ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA, ANN_SYNC_LOSS, \
+ANN_DATA_BYTE, \
+ANN_FRAME_INIT, ANN_FRAME_BYTES, ANN_FRAME_WAIT, \
+ = range(11)
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'pjdl'
+ name = 'PJDL'
+ longname = 'Padded Jittering Data Link'
+ desc = 'PJDL, a single wire serial link layer for PJON.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['pjon_link']
+ tags = ['Embedded/industrial']
+ channels = (
+ {'id': 'data' , 'name': 'DATA', 'desc': 'Single wire data'},
+ )
+ options = (
+ {'id': 'mode', 'desc': 'Communication mode',
+ 'default': 1, 'values': (1, 2, 3, 4)},
+ {'id': 'idle_add_us', 'desc': 'Added idle time (us)', 'default': 4},
+ )
+ annotations = (
+ ('cs_busy', 'Carrier busy'),
+ ('cs_idle', 'Carrier idle'),
+ ('bit_pad', 'Pad bit'),
+ ('bit_low', 'Low bit'),
+ ('bit_data', 'Data bit'),
+ ('bit_short', 'Short data'),
+ ('sync_loss', 'Sync loss'),
+ ('byte', 'Data byte'),
+ ('frame_init', 'Frame init'),
+ ('frame_bytes', 'Frame bytes'),
+ ('frame_wait', 'Frame wait'),
+ )
+ annotation_rows = (
+ ('carriers', 'Carriers', (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE,)),
+ ('bits', 'Bits', (ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA,)),
+ ('bytes', 'Bytes', (ANN_FRAME_INIT, ANN_DATA_BYTE, ANN_FRAME_WAIT,)),
+ ('frames', 'Frames', (ANN_FRAME_BYTES,)),
+ ('warns', 'Warnings', (ANN_SYNC_LOSS,)),
+ )
+
+ # Communication modes' data bit and pad bit duration (in us), and
+ # tolerances in percent and absolute (us).
+ mode_times = {
+ 1: (44, 116),
+ 2: (40, 92),
+ 3: (28, 88),
+ 4: (26, 60),
+ }
+ time_tol_perc = 10
+ time_tol_abs = 1.5
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.reset_state()
+
+ def reset_state(self):
+ self.carrier_want_idle = True
+ self.carrier_is_busy = False
+ self.carrier_is_idle = False
+ self.carrier_idle_ss = None
+ self.carrier_busy_ss = None
+ self.syncpad_fall_ss = None
+
+ self.edges = None
+ self.symbols = None
+ self.sync_pads = None
+ self.data_bits = None
+ self.frame_bytes = None
+ self.short_bits = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ self.span_prepare()
+
+ def putg(self, ss, es, data):
+ cls = data[0]
+ if not _with_ann_carrier and cls in (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE):
+ return
+ if not _with_ann_sync_loss and cls in (ANN_SYNC_LOSS,):
+ return
+ self.put(ss, es, self.out_ann, data)
+
+ def putpy(self, ss, es, ptype, pdata):
+ self.put(ss, es, self.out_python, [ptype, pdata])
+
+ def symbols_clear(self):
+ syms = self.symbols or []
+ self.symbols = []
+ return syms
+
+ def symbols_append(self, ss, es, symbol, data = None):
+ if self.symbols is None:
+ self.symbols = []
+ item = (ss, es, symbol, data)
+ self.symbols.append(item)
+
+ def symbols_get_last(self, count = None):
+ if not self.symbols:
+ return None
+ if count is None:
+ count = 1
+ if len(self.symbols) < count:
+ return None
+ items = self.symbols[-count:]
+ if count == 1:
+ items = items[0]
+ return items
+
+ def symbols_update_last(self, ss, es, symbol, data = None):
+ if not self.symbols:
+ return None
+ item = list(self.symbols[-1])
+ if ss is not None:
+ item[0] = ss
+ if es is not None:
+ item[1] = es
+ if symbol is not None:
+ item[2] = symbol
+ if data is not None:
+ item[3] = data
+ self.symbols[-1] = tuple(item)
+
+ def symbols_has_prev(self, want_items):
+ if not isinstance(want_items, (list, tuple,)):
+ want_items = [want_items]
+ if self.symbols is None:
+ return False
+ if len(self.symbols) < len(want_items):
+ return False
+ sym_off = len(self.symbols) - len(want_items)
+ for idx, want_item in enumerate(want_items):
+ if self.symbols[sym_off + idx][2] != want_item:
+ return False
+ return True
+
+ def symbols_collapse(self, count, symbol, data = None, squeeze = None):
+ if self.symbols is None:
+ return None
+ if len(self.symbols) < count:
+ return None
+ self.symbols, last_data = self.symbols[:-count], self.symbols[-count:]
+ while squeeze and self.symbols and self.symbols[-1][2] == squeeze:
+ last_data.insert(0, self.symbols.pop())
+ ss, es = last_data[0][0], last_data[-1][1]
+ if data is None:
+ data = last_data
+ item = (ss, es, symbol, data)
+ self.symbols.append(item)
+
+ def frame_flush(self):
+ syms = self.symbols_clear()
+ while syms and syms[0][2] == 'IDLE':
+ syms.pop(0)
+ while syms and syms[-1][2] == 'IDLE':
+ syms.pop(-1)
+ if not syms:
+ return
+ text = []
+ data = []
+ for sym in syms:
+ if sym[2] == 'FRAME_INIT':
+ text.append('INIT')
+ data.append('INIT')
+ continue
+ if sym[2] == 'SYNC_PAD':
+ if not text or text[-1] != 'SYNC':
+ text.append('SYNC')
+ data.append('SYNC')
+ continue
+ if sym[2] == 'DATA_BYTE':
+ b = [bit[3] for bit in sym[3] if bit[2] == 'DATA_BIT']
+ b = bitpack(b)
+ text.append('{:02x}'.format(b))
+ data.append(b)
+ continue
+ if sym[2] == 'SHORT_BIT':
+ if not text or text[-1] != 'SHORT':
+ text.append('SHORT')
+ data.append('SHORT')
+ continue
+ if sym[2] == 'WAIT_ACK':
+ text.append('WAIT')
+ data.append('WAIT')
+ continue
+ text = ' '.join(text)
+ ss, es = syms[0][0], syms[-1][1]
+ self.putg(ss, es, [ANN_FRAME_BYTES, [text]])
+ self.putpy(ss, es, 'FRAME_DATA', data)
+
+ def carrier_flush(self):
+ # Force annotations if BUSY started, or if IDLE tracking started
+ # and kept running for long enough. This will be called before
+ # internal state reset, so we won't manipulate internal variables,
+ # and can afford to emit annotations which haven't met their
+ # proper end condition yet.
+ if self.carrier_busy_ss:
+ ss, es = self.carrier_busy_ss, self.samplenum
+ self.putg(ss, es, [ANN_CARRIER_BUSY, ['BUSY']])
+ if self.carrier_idle_ss:
+ ss, es = self.carrier_idle_ss, self.samplenum
+ ss += int(self.idle_width)
+ if ss < es:
+ self.putg(ss, es, [ANN_CARRIER_IDLE, ['IDLE']])
+
+ def carrier_set_idle(self, on, ss, es):
+ if on:
+ # IDLE starts here, or continues.
+ if not self.carrier_idle_ss:
+ self.carrier_idle_ss = int(ss)
+ if not self.symbols_has_prev('IDLE'):
+ self.symbols_append(ss, ss, 'IDLE')
+ self.symbols_update_last(None, es, None)
+ # HACK We have seen an IDLE condition. This implementation
+ # loses details which are used to track IDLE, but it's more
+ # important to start accumulation of a new frame here.
+ self.frame_flush()
+ self.reset_state()
+ # end of HACK
+ self.carrier_is_idle = True
+ self.carrier_want_idle = False
+ return
+ # IDLE ends here.
+ if self.symbols_has_prev('IDLE'):
+ self.symbols_update_last(None, es, None)
+ self.carrier_flush()
+ self.carrier_is_idle = False
+ self.carrier_idle_ss = None
+
+ def carrier_set_busy(self, on, snum):
+ self.carrier_is_busy = on
+ if on:
+ self.carrier_is_idle = None
+ if not self.carrier_busy_ss:
+ self.carrier_busy_ss = snum
+ return
+ if self.carrier_busy_ss:
+ self.putg(self.carrier_busy_ss, snum, [ANN_CARRIER_BUSY, ['BUSY']])
+ self.carrier_busy_ss = None
+ self.carrier_is_busy = False
+
+ def carrier_check(self, level, snum):
+
+ # When HIGH is seen, immediately end IDLE and switch to BUSY.
+ if level:
+ self.carrier_set_idle(False, snum, snum)
+ self.carrier_set_busy(True, snum)
+ return
+
+ # LOW is seen. Start tracking an IDLE period if not done yet.
+ if not self.carrier_idle_ss:
+ self.carrier_idle_ss = int(snum)
+
+ # End BUSY when LOW persisted for an exact data byte's length.
+ # Start IDLE when LOW persisted for a data byte's length plus
+ # the user specified additional period.
+ span = snum - self.carrier_idle_ss
+ if span >= self.byte_width:
+ self.carrier_set_busy(False, snum)
+ if span >= self.idle_width:
+ self.carrier_set_idle(True, self.carrier_idle_ss + self.idle_width, snum)
+
+ def span_prepare(self):
+ '''Prepare calculation of durations in terms of samples.'''
+
+ # Determine samples per microsecond, and sample counts for
+ # several bit types, and sample count for a data byte's
+ # length, including optional extra time. Determine ranges
+ # for bit widths (tolerance margin).
+
+ # Get times in microseconds.
+ mode_times = self.mode_times[self.options['mode']]
+ mode_times = [t * 1.0 for t in mode_times]
+ self.data_width, self.pad_width = mode_times
+ self.byte_width = self.pad_width + 9 * self.data_width
+ self.add_idle_width = self.options['idle_add_us']
+ self.idle_width = self.byte_width + self.add_idle_width
+
+ # Derive ranges (add tolerance) and scale to sample counts.
+ self.usec_width = self.samplerate / 1e6
+ self.hold_high_width = 9 * self.time_tol_abs * self.usec_width
+
+ def _get_range(width):
+ reladd = self.time_tol_perc / 100
+ absadd = self.time_tol_abs
+ lower = min(width * (1 - reladd), width - absadd)
+ upper = max(width * (1 + reladd), width + absadd)
+ lower = floor(lower * self.usec_width)
+ upper = ceil(upper * self.usec_width)
+ return (lower, upper + 1)
+
+ self.data_bit_1_range = _get_range(self.data_width * 1)
+ self.data_bit_2_range = _get_range(self.data_width * 2)
+ self.data_bit_3_range = _get_range(self.data_width * 3)
+ self.data_bit_4_range = _get_range(self.data_width * 4)
+ self.short_data_range = _get_range(self.data_width / 4)
+ self.pad_bit_range = _get_range(self.pad_width)
+
+ self.data_width *= self.usec_width
+ self.pad_width *= self.usec_width
+ self.byte_width *= self.usec_width
+ self.idle_width *= self.usec_width
+
+ self.lookahead_width = int(4 * self.data_width)
+
+ def span_snum_to_us(self, count):
+ return count / self.usec_width
+
+ def span_is_pad(self, span):
+ return span in range(*self.pad_bit_range)
+
+ def span_is_data(self, span):
+ if span in range(*self.data_bit_1_range):
+ return 1
+ if span in range(*self.data_bit_2_range):
+ return 2
+ if span in range(*self.data_bit_3_range):
+ return 3
+ if span in range(*self.data_bit_4_range):
+ return 4
+ return False
+
+ def span_is_short(self, span):
+ return span in range(*self.short_data_range)
+
+ def wait_until(self, want):
+ '''Wait until a given location, but keep sensing carrier.'''
+
+ # Implementor's note: Avoids skip values below 1. This version
+ # "may overshoot" by one sample. Which should be acceptable for
+ # this specific use case (can put the sample point of a bit time
+ # out of the center by some 4% under worst case conditions).
+
+ want = int(want)
+ while True:
+ diff = max(want - self.samplenum, 1)
+ pins = self.wait([{PIN_DATA: 'e'}, {'skip': diff}])
+ self.carrier_check(pins[PIN_DATA], self.samplenum)
+ if self.samplenum >= want:
+ return pins
+ # UNREACH
+
+ def decode(self):
+ if not self.samplerate or self.samplerate < 1e6:
+ raise SamplerateError('Need a samplerate of at least 1MSa/s')
+
+ # As a special case the first low period in the input capture is
+ # saught regardless of whether we can see its falling edge. This
+ # approach is also used to recover after synchronization was lost.
+ #
+ # The important condition here in the main loop is: Get the next
+ # edge's position, but time out after a maximum period of four
+ # data bits. This allows for the detection of SYNC pulses, also
+ # responds "soon enough" to DATA bits where edges can be few
+ # within a data byte. Also avoids excessive waits for unexpected
+ # communication errors.
+ #
+ # DATA bits within a byte are taken at fixed intervals relative
+ # to the SYNC-PAD's falling edge. It's essential to check the
+ # carrier at every edge, also during DATA bit sampling. Simple
+ # skips to the desired sample point could break that feature.
+ while True:
+
+ # Help kick-start the IDLE condition detection after
+ # decoder state reset.
+ if not self.edges:
+ curr_level, = self.wait({PIN_DATA: 'l'})
+ self.carrier_check(curr_level, self.samplenum)
+ self.edges = [self.samplenum]
+ continue
+
+ # Advance to the next edge, or over a medium span without an
+ # edge. Prepare to classify the distance to derive bit types
+ # from these details.
+ last_snum = self.samplenum
+ curr_level, = self.wait([{PIN_DATA: 'e'}, {'skip': self.lookahead_width}])
+ self.carrier_check(curr_level, self.samplenum)
+ bit_level = curr_level
+ edge_seen = self.matched[0]
+ if edge_seen:
+ bit_level = 1 - bit_level
+ if not self.edges:
+ self.edges = [self.samplenum]
+ continue
+ self.edges.append(self.samplenum)
+ curr_snum = self.samplenum
+
+ # Check bit width (can also be multiple data bits).
+ span = self.edges[-1] - self.edges[-2]
+ is_pad = bit_level and self.span_is_pad(span)
+ is_data = self.span_is_data(span)
+ is_short = bit_level and self.span_is_short(span)
+
+ if is_pad:
+ # BEWARE! Use ss value of last edge (genuinely seen, or
+ # inserted after a DATA byte) for PAD bit annotations.
+ ss, es = self.edges[-2], curr_snum
+ texts = ['PAD', '{:d}'.format(bit_level)]
+ self.putg(ss, es, [ANN_PAD_BIT, texts])
+ self.symbols_append(ss, es, 'PAD_BIT', bit_level)
+ ss, es = self.symbols_get_last()[:2]
+ self.putpy(ss, es, 'PAD_BIT', bit_level)
+ continue
+
+ if is_short:
+ ss, es = last_snum, curr_snum
+ texts = ['SHORT', '{:d}'.format(bit_level)]
+ self.putg(ss, es, [ANN_SHORT_DATA, texts])
+ self.symbols_append(ss, es, 'SHORT_BIT', bit_level)
+ ss, es = self.symbols_get_last()[:2]
+ self.putpy(ss, es, 'SHORT_BIT', bit_level)
+ continue
+
+ # Force IDLE period check when the decoder seeks to sync
+ # to the input data stream.
+ if not bit_level and not self.symbols and self.carrier_want_idle:
+ continue
+
+ # Accept arbitrary length LOW phases after DATA bytes(!) or
+ # SHORT pulses, but not within a DATA byte or SYNC-PAD etc.
+ # This covers the late start of the next SYNC-PAD (byte of
+ # a frame, or ACK byte after a frame, or the start of the
+ # next frame).
+ if not bit_level:
+ if self.symbols_has_prev('DATA_BYTE'):
+ continue
+ if self.symbols_has_prev('SHORT_BIT'):
+ continue
+ if self.symbols_has_prev('WAIT_ACK'):
+ continue
+
+ # Get (consume!) the LOW DATA bit after a PAD.
+ took_low = False
+ if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'):
+ took_low = True
+ is_data -= 1
+ next_snum = int(last_snum + self.data_width)
+ ss, es = last_snum, next_snum
+ texts = ['ZERO', '{:d}'.format(bit_level)]
+ self.putg(ss, es, [ANN_LOW_BIT, texts])
+ self.symbols_append(ss, es, 'ZERO_BIT', bit_level)
+ ss, es = self.symbols_get_last()[:2]
+ self.putpy(ss, es, 'DATA_BIT', bit_level)
+ self.data_fall_time = last_snum
+ last_snum = next_snum
+ # Turn the combination of PAD and LOW DATA into SYNC-PAD.
+ # Start data bit accumulation after a SYNC-PAD was seen.
+ sync_pad_seq = ['PAD_BIT', 'ZERO_BIT']
+ if self.symbols_has_prev(sync_pad_seq):
+ self.symbols_collapse(len(sync_pad_seq), 'SYNC_PAD')
+ ss, es = self.symbols_get_last()[:2]
+ self.putpy(ss, es, 'SYNC_PAD', True)
+ self.data_bits = []
+ # Turn three subsequent SYNC-PAD into FRAME-INIT. Start the
+ # accumulation of frame bytes when FRAME-INIT was seen.
+ frame_init_seq = 3 * ['SYNC_PAD']
+ if self.symbols_has_prev(frame_init_seq):
+ self.symbols_collapse(len(frame_init_seq), 'FRAME_INIT')
+ # Force a flush of the previous frame after we have
+ # reliably detected the start of another one. This is a
+ # workaround for this decoder's inability to detect the
+ # end of a frame after an ACK was seen or byte counts
+ # have been reached. We cannot assume perfect input,
+ # thus we leave all interpretation of frame content to
+ # upper layers. Do keep the recently queued FRAME_INIT
+ # symbol across the flush operation.
+ if len(self.symbols) > 1:
+ keep = self.symbols.pop(-1)
+ self.frame_flush()
+ self.symbols.clear()
+ self.symbols.append(keep)
+ ss, es = self.symbols_get_last()[:2]
+ texts = ['FRAME INIT', 'INIT', 'I']
+ self.putg(ss, es, [ANN_FRAME_INIT, texts])
+ self.putpy(ss, es, 'FRAME_INIT', True)
+ self.frame_bytes = []
+ # Collapse SYNC-PAD after SHORT+ into a WAIT-ACK. Include
+ # all leading SHORT bits in the WAIT as well.
+ wait_ack_seq = ['SHORT_BIT', 'SYNC_PAD']
+ if self.symbols_has_prev(wait_ack_seq):
+ self.symbols_collapse(len(wait_ack_seq), 'WAIT_ACK',
+ squeeze = 'SHORT_BIT')
+ ss, es = self.symbols_get_last()[:2]
+ texts = ['WAIT for sync response', 'WAIT response', 'WAIT', 'W']
+ self.putg(ss, es, [ANN_FRAME_WAIT, texts])
+ self.putpy(ss, es, 'SYNC_RESP_WAIT', True)
+ if took_low and not is_data:
+ # Start at the very next edge if we just consumed a LOW
+ # after a PAD bit, and the DATA bit count is exhausted.
+ # This improves robustness, deals with inaccurate edge
+ # positions. (Motivated by real world captures, the spec
+ # would not discuss bit time tolerances.)
+ continue
+
+ # When we get here, the only remaining (the only supported)
+ # activity is the collection of a data byte's DATA bits.
+ # These are not taken by the main loop's "edge search, with
+ # a timeout" approach, which is "too tolerant". Instead all
+ # DATA bits get sampled at a fixed interval and relative to
+ # the SYNC-PAD's falling edge. We expect to have seen the
+ # data byte' SYNC-PAD before. If we haven't, the decoder is
+ # not yet synchronized to the input data.
+ if not is_data:
+ fast_cont = edge_seen and curr_level
+ ss, es = last_snum, curr_snum
+ texts = ['failed pulse length check', 'pulse length', 'length']
+ self.putg(ss, es, [ANN_SYNC_LOSS, texts])
+ self.frame_flush()
+ self.carrier_flush()
+ self.reset_state()
+ if fast_cont:
+ self.edges = [self.samplenum]
+ continue
+ if not self.symbols_has_prev('SYNC_PAD'):
+ # Fast reponse to the specific combination of: no-sync,
+ # edge seen, and current high level. In this case we
+ # can reset internal state, but also can continue the
+ # interpretation right after the most recently seen
+ # rising edge, which could start the next PAD time.
+ # Otherwise continue slow interpretation after reset.
+ fast_cont = edge_seen and curr_level
+ self.frame_flush()
+ self.carrier_flush()
+ self.reset_state()
+ if fast_cont:
+ self.edges = [self.samplenum]
+ continue
+
+ # The main loop's "edge search with period timeout" approach
+ # can have provided up to three more DATA bits after the LOW
+ # bit of the SYNC-PAD. Consume them immediately in that case,
+ # otherwise .wait() for their sample point. Stick with float
+ # values for bit sample points and bit time boundaries for
+ # improved accuracy, only round late to integers when needed.
+ bit_field = []
+ bit_ss = self.data_fall_time + self.data_width
+ for bit_idx in range(8):
+ bit_es = bit_ss + self.data_width
+ bit_snum = (bit_es + bit_ss) / 2
+ if bit_snum > self.samplenum:
+ bit_level, = self.wait_until(bit_snum)
+ ss, es = ceil(bit_ss), floor(bit_es)
+ texts = ['{:d}'.format(bit_level)]
+ self.putg(ss, es, [ANN_DATA_BIT, texts])
+ self.symbols_append(ss, es, 'DATA_BIT', bit_level)
+ ss, es = self.symbols_get_last()[:2]
+ self.putpy(ss, es, 'DATA_BIT', bit_level)
+ bit_field.append(bit_level)
+ if self.data_bits is not None:
+ self.data_bits.append(bit_level)
+ bit_ss = bit_es
+ end_snum = bit_es
+ curr_level, = self.wait_until(end_snum)
+ curr_snum = self.samplenum
+
+ # We are at the exact _calculated_ boundary of the last DATA
+ # bit time. Improve robustness for those situations where
+ # the transmitter's and the sender's timings differ within a
+ # margin, and the transmitter may hold the last DATA bit's
+ # HIGH level for a little longer.
+ #
+ # When no falling edge is seen within the maximum tolerance
+ # for the last DATA bit, then this could be the combination
+ # of a HIGH DATA bit and a PAD bit without a LOW in between.
+ # Fake an edge in that case, to re-use existing code paths.
+ # Make sure to keep referencing times to the last SYNC pad's
+ # falling edge. This is the last reliable condition we have.
+ if curr_level:
+ hold = self.hold_high_width
+ curr_level, = self.wait([{PIN_DATA: 'l'}, {'skip': int(hold)}])
+ self.carrier_check(curr_level, self.samplenum)
+ if self.matched[1]:
+ self.edges.append(curr_snum)
+ curr_level = 1 - curr_level
+ curr_snum = self.samplenum
+
+ # Get the byte value from the bits (when available).
+ # TODO Has the local 'bit_field' become obsolete, or should
+ # self.data_bits go away?
+ data_byte = bitpack(bit_field)
+ if self.data_bits is not None:
+ data_byte = bitpack(self.data_bits)
+ self.data_bits.clear()
+ if self.frame_bytes is not None:
+ self.frame_bytes.append(data_byte)
+
+ # Turn a sequence of a SYNC-PAD and eight DATA bits into a
+ # DATA-BYTE symbol.
+ byte_seq = ['SYNC_PAD'] + 8 * ['DATA_BIT']
+ if self.symbols_has_prev(byte_seq):
+ self.symbols_collapse(len(byte_seq), 'DATA_BYTE')
+ ss, es = self.symbols_get_last()[:2]
+ texts = ['{:02x}'.format(data_byte)]
+ self.putg(ss, es, [ANN_DATA_BYTE, texts])
+ self.putpy(ss, es, 'DATA_BYTE', data_byte)
+
+ # Optionally terminate the accumulation of a frame when a
+ # WAIT-ACK period was followed by a DATA-BYTE? This could
+ # flush the current packet before the next FRAME-INIT or
+ # IDLE are seen, and increases usability for short input
+ # data (aggressive trimming). It won't help when WAIT is
+ # not seen, though.
+ sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE']
+ if self.symbols_has_prev(sync_resp_seq):
+ self.frame_flush()
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder interprets the PJON protocol on top of the PJDL
+link layer (and potentially other link layers).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# See the https://www.pjon.org/ PJON project page and especially the
+# https://www.pjon.org/PJON-protocol-specification-v3.2.php protocol
+# specification, which can use different link layers.
+
+# TODO
+# - Check for the correct order of optional fields (the spec is not as
+# explicit on these details as I'd expect).
+# - Check decoder's robustness, completeness, and correctness when more
+# captures become available. Currently there are only few, which only
+# cover minimal communication, and none of the protocol's flexibility.
+# The decoder was essentially written based on the available docs, and
+# then took some arbitrary choices and liberties to cope with real life
+# data from an example setup. Strictly speaking this decoder violates
+# the spec, and errs towards the usability side.
+
+import sigrokdecode as srd
+import struct
+
+ANN_RX_INFO, ANN_HDR_CFG, ANN_PKT_LEN, ANN_META_CRC, ANN_TX_INFO, \
+ANN_SVC_ID, ANN_PKT_ID, ANN_ANON_DATA, ANN_PAYLOAD, ANN_END_CRC, \
+ANN_SYN_RSP, \
+ANN_RELATION, \
+ANN_WARN, \
+ = range(13)
+
+def calc_crc8(data):
+ crc = 0
+ for b in data:
+ crc ^= b
+ for i in range(8):
+ odd = crc % 2
+ crc >>= 1
+ if odd:
+ crc ^= 0x97
+ return crc
+
+def calc_crc32(data):
+ crc = 0xffffffff
+ for b in data:
+ crc ^= b
+ for i in range(8):
+ odd = crc % 2
+ crc >>= 1
+ if odd:
+ crc ^= 0xedb88320
+ crc ^= 0xffffffff
+ return crc
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'pjon'
+ name = 'PJON'
+ longname = 'PJON'
+ desc = 'The PJON protocol.'
+ license = 'gplv2+'
+ inputs = ['pjon_link']
+ outputs = []
+ tags = ['Embedded/industrial']
+ annotations = (
+ ('rx_info', 'Receiver ID'),
+ ('hdr_cfg', 'Header config'),
+ ('pkt_len', 'Packet length'),
+ ('meta_crc', 'Meta CRC'),
+ ('tx_info', 'Sender ID'),
+ ('port', 'Service ID'),
+ ('pkt_id', 'Packet ID'),
+ ('anon', 'Anonymous data'),
+ ('payload', 'Payload'),
+ ('end_crc', 'End CRC'),
+ ('syn_rsp', 'Sync response'),
+ ('relation', 'Relation'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('fields', 'Fields', (
+ ANN_RX_INFO, ANN_HDR_CFG, ANN_PKT_LEN, ANN_META_CRC, ANN_TX_INFO,
+ ANN_SVC_ID, ANN_ANON_DATA, ANN_PAYLOAD, ANN_END_CRC, ANN_SYN_RSP,
+ )),
+ ('relations', 'Relations', (ANN_RELATION,)),
+ ('warnings', 'Warnings', (ANN_WARN,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.reset_frame()
+
+ def reset_frame(self):
+ self.frame_ss = None
+ self.frame_es = None
+ self.frame_rx_id = None
+ self.frame_tx_id = None
+ self.frame_payload_text = None
+ self.frame_bytes = None
+ self.frame_has_ack = None
+ self.ack_bytes = None
+ self.ann_ss = None
+ self.ann_es = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putg(self, ss, es, ann, data):
+ self.put(ss, es, self.out_ann, [ann, data])
+
+ def frame_flush(self):
+ if not self.frame_bytes:
+ return
+ if not self.frame_ss or not self.frame_es:
+ return
+
+ # Emit "communication relation" details.
+ # TODO Include the service ID (port number) as well?
+ text = []
+ if self.frame_rx_id is not None:
+ text.append("RX {}".format(self.frame_rx_id[-1]))
+ if self.frame_tx_id is not None:
+ text.append("TX {}".format(self.frame_tx_id[-1]))
+ if self.frame_payload_text is not None:
+ text.append("DATA {}".format(self.frame_payload_text))
+ if self.frame_has_ack is not None:
+ text.append("ACK {:02x}".format(self.frame_has_ack))
+ if text:
+ text = " - ".join(text)
+ self.putg(self.frame_ss, self.frame_es, ANN_RELATION, [text])
+
+ def handle_field_get_desc(self, idx = None):
+ '''Lookup description of a PJON frame field.'''
+ if not self.field_desc:
+ return None
+ if idx is None:
+ idx = self.field_desc_idx
+ if idx >= 0 and idx >= len(self.field_desc):
+ return None
+ if idx < 0 and abs(idx) > len(self.field_desc):
+ return None
+ desc = self.field_desc[idx]
+ return desc
+
+ def handle_field_add_desc(self, fmt, hdl, cls = None):
+ '''Register description for a PJON frame field.'''
+ item = {
+ 'format': fmt,
+ 'width': struct.calcsize(fmt),
+ 'handler': hdl,
+ 'anncls': cls,
+ }
+ self.field_desc.append(item)
+
+ def handle_field_seed_desc(self):
+ '''Seed list of PJON frame fields' descriptions.'''
+
+ # At the start of a PJON frame, the layout of only two fields
+ # is known. Subsequent fields (their presence, and width) depend
+ # on the content of the header config field.
+
+ self.field_desc = []
+ self.handle_field_add_desc('<B', self.handle_field_rx_id, ANN_RX_INFO)
+ self.handle_field_add_desc('<B', self.handle_field_config, ANN_HDR_CFG)
+
+ self.field_desc_idx = 0
+ self.field_desc_got = 0
+
+ self.frame_ss = None
+ self.frame_es = None
+ self.frame_rx_id = None
+ self.frame_is_broadcast = None
+ self.frame_tx_id = None
+ self.frame_payload = None
+ self.frame_payload_text = None
+ self.frame_has_ack = None
+
+ def handle_field_rx_id(self, b):
+ '''Process receiver ID field of a PJON frame.'''
+
+ b = b[0]
+
+ # Provide text presentation, caller emits frame field annotation.
+ if b == 255: # "not assigned"
+ id_txt = 'NA'
+ elif b == 0: # "broadcast"
+ id_txt = 'BC'
+ else: # unicast
+ id_txt = '{:d}'.format(b)
+ texts = [
+ 'RX_ID {}'.format(id_txt),
+ '{}'.format(id_txt),
+ ]
+
+ # Track RX info for communication relation emission.
+ self.frame_rx_id = (b, id_txt)
+ self.frame_is_broadcast = b == 0
+
+ return texts
+
+ def handle_field_config(self, b):
+ '''Process header config field of a PJON frame.'''
+
+ # Caller provides a list of values. We want a single scalar.
+ b = b[0]
+
+ # Get the config flags.
+ self.cfg_shared = b & (1 << 0)
+ self.cfg_tx_info = b & (1 << 1)
+ self.cfg_sync_ack = b & (1 << 2)
+ self.cfg_async_ack = b & (1 << 3)
+ self.cfg_port = b & (1 << 4)
+ self.cfg_crc32 = b & (1 << 5)
+ self.cfg_len16 = b & (1 << 6)
+ self.cfg_pkt_id = b & (1 << 7)
+
+ # Get a textual presentation of the flags.
+ text = []
+ text.append('pkt_id' if self.cfg_pkt_id else '-') # packet number
+ text.append('len16' if self.cfg_len16 else '-') # 16bit length not 8bit
+ text.append('crc32' if self.cfg_crc32 else '-') # 32bit CRC not 8bit
+ text.append('svc_id' if self.cfg_port else '-') # port aka service ID
+ text.append('ack_mode' if self.cfg_async_ack else '-') # async response
+ text.append('ack' if self.cfg_sync_ack else '-') # synchronous response
+ text.append('tx_info' if self.cfg_tx_info else '-') # sender address
+ text.append('bus_id' if self.cfg_shared else '-') # "shared" vs "local"
+ text = ' '.join(text)
+ bits = '{:08b}'.format(b)
+ texts = [
+ 'CFG {:s}'.format(text),
+ 'CFG {}'.format(bits),
+ bits
+ ]
+
+ # TODO Come up with the most appropriate phrases for this logic.
+ # Are separate instruction groups with repeated conditions more
+ # readable than one common block which registers fields _and_
+ # updates the overhead size? Or is the latter preferrable due to
+ # easier maintenance and less potential for inconsistency?
+
+ # Get the size of variable width fields, to calculate the size
+ # of the packet overhead (the part that is not the payload data).
+ # This lets us derive the payload length when we later receive
+ # the frame's total length.
+ u8_fmt = '>B'
+ u16_fmt = '>H'
+ u32_fmt = '>L'
+ len_fmt = u16_fmt if self.cfg_len16 else u8_fmt
+ bus_fmt = '>4B'
+ crc_fmt = u32_fmt if self.cfg_crc32 else u8_fmt
+ self.cfg_overhead = 0
+ self.cfg_overhead += struct.calcsize(u8_fmt) # receiver ID
+ self.cfg_overhead += struct.calcsize(u8_fmt) # header config
+ self.cfg_overhead += struct.calcsize(len_fmt) # packet length
+ self.cfg_overhead += struct.calcsize(u8_fmt) # initial CRC, always CRC8
+ # TODO Check for completeness and correctness.
+ if self.cfg_shared:
+ self.cfg_overhead += struct.calcsize(u32_fmt) # receiver bus
+ if self.cfg_tx_info:
+ if self.cfg_shared:
+ self.cfg_overhead += struct.calcsize(u32_fmt) # sender bus
+ self.cfg_overhead += struct.calcsize(u8_fmt) # sender ID
+ if self.cfg_port:
+ self.cfg_overhead += struct.calcsize(u16_fmt) # service ID
+ if self.cfg_pkt_id:
+ self.cfg_overhead += struct.calcsize(u16_fmt) # packet ID
+ self.cfg_overhead += struct.calcsize(crc_fmt) # end CRC
+
+ # Register more frame fields as we learn about their presence and
+ # format. Up to this point only receiver ID and header config were
+ # registered since their layout is fixed.
+ #
+ # Packet length and meta CRC are always present but can be of
+ # variable width. Optional fields follow the meta CRC and preceed
+ # the payload bytes. Notice that payload length isn't known here
+ # either, though its position is known already. The packet length
+ # is yet to get received. Subtracting the packet overhead from it
+ # (which depends on the header configuration) will provide that
+ # information.
+ #
+ # TODO Check for completeness and correctness.
+ # TODO Optionally fold overhead size arith and field registration
+ # into one block of instructions, to reduce the redundancy in the
+ # condition checks, and raise awareness for incomplete sequences
+ # during maintenance.
+ self.handle_field_add_desc(len_fmt, self.handle_field_pkt_len, ANN_PKT_LEN)
+ self.handle_field_add_desc(u8_fmt, self.handle_field_meta_crc, ANN_META_CRC)
+ if self.cfg_shared:
+ self.handle_field_add_desc(bus_fmt, self.handle_field_rx_bus, ANN_ANON_DATA)
+ if self.cfg_tx_info:
+ if self.cfg_shared:
+ self.handle_field_add_desc(bus_fmt, self.handle_field_tx_bus, ANN_ANON_DATA)
+ self.handle_field_add_desc(u8_fmt, self.handle_field_tx_id, ANN_ANON_DATA)
+ if self.cfg_port:
+ self.handle_field_add_desc(u16_fmt, ['PORT {:d}', '{:d}'], ANN_ANON_DATA)
+ if self.cfg_pkt_id:
+ self.handle_field_add_desc(u16_fmt, ['PKT {:04x}', '{:04x}'], ANN_ANON_DATA)
+ pl_fmt = '>{:d}B'.format(0)
+ self.handle_field_add_desc(pl_fmt, self.handle_field_payload, ANN_PAYLOAD)
+ self.handle_field_add_desc(crc_fmt, self.handle_field_end_crc, ANN_END_CRC)
+
+ # Emit warning annotations for invalid flag combinations.
+ warn_texts = []
+ wants_ack = self.cfg_sync_ack or self.cfg_async_ack
+ if wants_ack and not self.cfg_tx_info:
+ warn_texts.append('ACK request without TX info')
+ if wants_ack and self.frame_is_broadcast:
+ warn_texts.append('ACK request for broadcast')
+ if self.cfg_sync_ack and self.cfg_async_ack:
+ warn_texts.append('sync and async ACK request')
+ if self.cfg_len16 and not self.cfg_crc32:
+ warn_texts.append('extended length needs CRC32')
+ if warn_texts:
+ warn_texts = ', '.join(warn_texts)
+ self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+
+ # Have the caller emit the annotation for configuration data.
+ return texts
+
+ def handle_field_pkt_len(self, b):
+ '''Process packet length field of a PJON frame.'''
+
+ # Caller provides a list of values. We want a single scalar.
+ b = b[0]
+
+ # The wire communicates the total packet length. Some of it is
+ # overhead (non-payload data), while its volume is variable in
+ # size (depends on the header configuration).
+ #
+ # Derive the payload size from previously observed flags. Update
+ # the previously registered field description (the second last
+ # item in the list, before the end CRC).
+
+ pkt_len = b
+ pl_len = b - self.cfg_overhead
+ warn_texts = []
+ if pkt_len not in range(self.cfg_overhead, 65536):
+ warn_texts.append('suspicious packet length')
+ if pkt_len > 15 and not self.cfg_crc32:
+ warn_texts.append('length above 15 needs CRC32')
+ if pl_len < 1:
+ warn_texts.append('suspicious payload length')
+ pl_len = 0
+ if warn_texts:
+ warn_texts = ', '.join(warn_texts)
+ self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+ pl_fmt = '>{:d}B'.format(pl_len)
+
+ desc = self.handle_field_get_desc(-2)
+ desc['format'] = pl_fmt
+ desc['width'] = struct.calcsize(pl_fmt)
+
+ # Have the caller emit the annotation for the packet length.
+ # Provide information of different detail level for zooming.
+ texts = [
+ 'LENGTH {:d} (PAYLOAD {:d})'.format(pkt_len, pl_len),
+ 'LEN {:d} (PL {:d})'.format(pkt_len, pl_len),
+ '{:d} ({:d})'.format(pkt_len, pl_len),
+ '{:d}'.format(pkt_len),
+ ]
+ return texts
+
+ def handle_field_common_crc(self, have, is_meta):
+ '''Process a CRC field of a PJON frame.'''
+
+ # CRC algorithm and width are configurable, and can differ
+ # across meta and end checksums in a frame's fields.
+ caption = 'META' if is_meta else 'END'
+ crc_len = 8 if is_meta else 32 if self.cfg_crc32 else 8
+ crc_bytes = crc_len // 8
+ crc_fmt = '{:08x}' if crc_len == 32 else '{:02x}'
+ have_text = crc_fmt.format(have)
+
+ # Check received against expected checksum. Emit warnings.
+ warn_texts = []
+ data = self.frame_bytes[:-crc_bytes]
+ want = calc_crc32(data) if crc_len == 32 else calc_crc8(data)
+ if want != have:
+ want_text = crc_fmt.format(want)
+ warn_texts.append('CRC mismatch - want {} have {}'.format(want_text, have_text))
+ if warn_texts:
+ warn_texts = ', '.join(warn_texts)
+ self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+
+ # Provide text representation for frame field, caller emits
+ # the annotation.
+ texts = [
+ '{}_CRC {}'.format(caption, have_text),
+ 'CRC {}'.format(have_text),
+ have_text,
+ ]
+ return texts
+
+ def handle_field_meta_crc(self, b):
+ '''Process initial CRC (meta) field of a PJON frame.'''
+ # Caller provides a list of values. We want a single scalar.
+ b = b[0]
+ return self.handle_field_common_crc(b, True)
+
+ def handle_field_end_crc(self, b):
+ '''Process end CRC (total frame) field of a PJON frame.'''
+ # Caller provides a list of values. We want a single scalar.
+ b = b[0]
+ return self.handle_field_common_crc(b, False)
+
+ def handle_field_common_bus(self, b):
+ '''Common handling of bus ID details. Used for RX and TX.'''
+ bus_id = b[:4]
+ bus_num = struct.unpack('>L', bytearray(bus_id))
+ bus_txt = '.'.join(['{:d}'.format(b) for b in bus_id])
+ return bus_num, bus_txt
+
+ def handle_field_rx_bus(self, b):
+ '''Process receiver bus ID field of a PJON frame.'''
+
+ # When we get here, there always should be an RX ID already.
+ bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+ rx_txt = "{} {}".format(bus_txt, self.frame_rx_id[-1])
+ self.frame_rx_id = (bus_num, self.frame_rx_id[0], rx_txt)
+
+ # Provide text representation for frame field, caller emits
+ # the annotation.
+ texts = [
+ 'RX_BUS {}'.format(bus_txt),
+ bus_txt,
+ ]
+ return texts
+
+ def handle_field_tx_bus(self, b):
+ '''Process transmitter bus ID field of a PJON frame.'''
+
+ # The TX ID field is optional, as is the use of bus ID fields.
+ # In the TX info case the TX bus ID is seen before the TX ID.
+ bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+ self.frame_tx_id = (bus_num, None, bus_txt)
+
+ # Provide text representation for frame field, caller emits
+ # the annotation.
+ texts = [
+ 'TX_BUS {}'.format(bus_txt),
+ bus_txt,
+ ]
+ return texts
+
+ def handle_field_tx_id(self, b):
+ '''Process transmitter ID field of a PJON frame.'''
+
+ b = b[0]
+
+ id_txt = "{:d}".format(b)
+ if self.frame_tx_id is None:
+ self.frame_tx_id = (b, id_txt)
+ else:
+ tx_txt = "{} {}".format(self.frame_tx_id[-1], id_txt)
+ self.frame_tx_id = (self.frame_tx_id[0], b, tx_txt)
+
+ # Provide text representation for frame field, caller emits
+ # the annotation.
+ texts = [
+ 'TX_ID {}'.format(id_txt),
+ id_txt,
+ ]
+ return texts
+
+ def handle_field_payload(self, b):
+ '''Process payload data field of a PJON frame.'''
+
+ text = ' '.join(['{:02x}'.format(v) for v in b])
+ self.frame_payload = b[:]
+ self.frame_payload_text = text
+
+ texts = [
+ 'PAYLOAD {}'.format(text),
+ text,
+ ]
+ return texts
+
+ def handle_field_sync_resp(self, b):
+ '''Process synchronous response for a PJON frame.'''
+
+ self.frame_has_ack = b
+
+ texts = [
+ 'ACK {:02x}'.format(b),
+ '{:02x}'.format(b),
+ ]
+ return texts
+
+ def decode(self, ss, es, data):
+ ptype, pdata = data
+
+ # Start frame bytes accumulation when FRAME_INIT is seen. Flush
+ # previously accumulated frame bytes when a new frame starts.
+ if ptype == 'FRAME_INIT':
+ self.frame_flush()
+ self.reset_frame()
+ self.frame_bytes = []
+ self.handle_field_seed_desc()
+ self.frame_ss = ss
+ self.frame_es = es
+ return
+
+ # Use IDLE as another (earlier) trigger to flush frames. Also
+ # trigger flushes on FRAME-DATA which mean that the link layer
+ # inspection has seen the end of a protocol frame.
+ #
+ # TODO Improve usability? Emit warnings for PJON frames where
+ # FRAME_DATA was seen but FRAME_INIT wasn't? So that users can
+ # become aware of broken frames.
+ if ptype in ('IDLE', 'FRAME_DATA'):
+ self.frame_flush()
+ self.reset_frame()
+ return
+
+ # Switch from data bytes to response bytes when WAIT is seen.
+ if ptype == 'SYNC_RESP_WAIT':
+ self.ack_bytes = []
+ self.ann_ss, self.ann_es = None, None
+ return
+
+ # Accumulate data bytes as they arrive. Put them in the bucket
+ # which corresponds to its most recently seen leader.
+ if ptype == 'DATA_BYTE':
+ b = pdata
+ self.frame_es = es
+
+ # Are we collecting response bytes (ACK)?
+ if self.ack_bytes is not None:
+ if not self.ann_ss:
+ self.ann_ss = ss
+ self.ack_bytes.append(b)
+ self.ann_es = es
+ text = self.handle_field_sync_resp(b)
+ if text:
+ self.putg(self.ann_ss, self.ann_es, ANN_SYN_RSP, text)
+ self.ann_ss, self.ann_es = None, None
+ return
+
+ # Are we collecting frame content?
+ if self.frame_bytes is not None:
+ if not self.ann_ss:
+ self.ann_ss = ss
+ self.frame_bytes.append(b)
+ self.ann_es = es
+
+ # Has the field value become available yet?
+ desc = self.handle_field_get_desc()
+ if not desc:
+ return
+ width = desc.get('width', None)
+ if not width:
+ return
+ self.field_desc_got += 1
+ if self.field_desc_got != width:
+ return
+
+ # Grab most recent received field as a byte array. Get
+ # the values that it contains.
+ fmt = desc.get('format', '>B')
+ raw = bytearray(self.frame_bytes[-width:])
+ values = struct.unpack(fmt, raw)
+
+ # Process the value, and get its presentation. Can be
+ # mere formatting, or serious execution of logic.
+ hdl = desc.get('handler', '{!r}')
+ if isinstance(hdl, str):
+ text = [hdl.format(*values)]
+ elif isinstance(hdl, (list, tuple)):
+ text = [f.format(*values) for f in hdl]
+ elif hdl:
+ text = hdl(values)
+ cls = desc.get('anncls', ANN_ANON_DATA)
+
+ # Emit annotation unless the handler routine already did.
+ if cls is not None and text:
+ self.putg(self.ann_ss, self.ann_es, cls, text)
+ self.ann_ss, self.ann_es = None, None
+
+ # Advance scan position for to-get-received field.
+ self.field_desc_idx += 1
+ self.field_desc_got = 0
+ return
+
+ # Unknown phase, not collecting. Not synced yet to the input?
+ return
+
+ # Unknown or unhandled kind of link layer output.
+ return
def reset(self):
self.bits = []
- self.samplenum = 0
self.bitcount = 0
def start(self):
def decode(self):
while True:
- # Sample data bits on falling clock edge.
- clock_pin, data_pin = self.wait({0: 'f'})
+ # Sample data bits on the falling clock edge (assume the device
+ # is the transmitter). Expect the data byte transmission to end
+ # at the rising clock edge. Cope with the absence of host activity.
+ _, data_pin = self.wait({0: 'f'})
self.handle_bits(data_pin)
+ if self.bitcount == 1 + 8 + 1 + 1:
+ _, data_pin = self.wait({0: 'r'})
+ self.handle_bits(data_pin)
('period', 'Period'),
)
annotation_rows = (
- ('duty-cycle', 'Duty cycle', (0,)),
- ('period', 'Period', (1,)),
+ ('duty-cycle-vals', 'Duty cycles', (0,)),
+ ('periods', 'Periods', (1,)),
)
binary = (
('raw', 'RAW file'),
{'id': 'qi', 'name': 'Qi', 'desc': 'Demodulated Qi data line'},
)
annotations = (
- ('bits', 'Bits'),
- ('bytes-errors', 'Bit errors'),
- ('bytes-start', 'Start bits'),
- ('bytes-info', 'Info bits'),
- ('bytes-data', 'Data bytes'),
- ('packets-data', 'Packet data'),
- ('packets-checksum-ok', 'Packet checksum'),
- ('packets-checksum-err', 'Packet checksum'),
+ ('bit', 'Bit'),
+ ('byte-error', 'Bit error'),
+ ('byte-start', 'Start bit'),
+ ('byte-info', 'Info bit'),
+ ('byte-data', 'Data byte'),
+ ('packet-data', 'Packet data'),
+ ('packet-checksum-ok', 'Packet checksum OK'),
+ ('packet-checksum-err', 'Packet checksum error'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
transmitted over whatever frequency and modulation that the designer
chooses. These devices operate at a number of frequencies including 433MHz.
-This PD should also decode the HX2262 and SC5262 which are equivalents.
+This PD should also decode the HX2262 and SC5262 which are equivalents, as
+well as the 2272 variants of these ICs. Support for the EV1527, RT1527, FP1527
+and HS1527 is also present.
-The decoder also contains some additional decoding for a Maplin L95AR
-remote control and will turn the received signal into which button was
-pressed and what the address code DIP switches are set to.
+The decoder can additionaly decoding the Maplin L95AR remote control and will
+turn the received signal into which button was pressed and what the address
+code DIP switches are set to.
+Please contact the sigrok team if you want decoding for further remote
+controls to be added.
'''
from .pd import Decoder
bitvals = ('0', '1', 'f', 'U')
-def decode_bit(edges):
- # Datasheet says long pulse is 3 times short pulse.
- lmin = 2 # long min multiplier
- lmax = 5 # long max multiplier
- eqmin = 0.5 # equal min multiplier
- eqmax = 1.5 # equal max multiplier
- if ( # 0 -___-___
- (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
- (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
- (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
- return '0'
- elif ( # 1 ---_---_
- (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
- (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
- (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
- return '1'
- elif ( # float ---_-___
- (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
- (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
- (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
- return 'f'
- else:
- return 'U'
-
-def pinlabels(bit_count):
- if bit_count <= 6:
- return 'A%i' % (bit_count - 1)
- else:
- return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+def decode_bit(edges, pulses_per_bit):
+ if pulses_per_bit == 2:
+ # Datasheet says long pulse is 3 times short pulse.
+ lmin = 1.5 # long min multiplier
+ lmax = 5 # long max multiplier
+ if (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax): # 0 -___
+ return '0'
+ elif (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax): # 1 ---_
+ return '1'
+ # No float type for this line encoding
+ else:
+ return 'U'
+
+ if pulses_per_bit == 4:
+ # Datasheet says long pulse is 3 times short pulse.
+ lmin = 2 # long min multiplier
+ lmax = 5 # long max multiplier
+ eqmin = 0.5 # equal min multiplier
+ eqmax = 1.5 # equal max multiplier
+ if ( # 0 -___-___
+ (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+ (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
+ (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
+ return '0'
+ elif ( # 1 ---_---_
+ (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
+ (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
+ (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
+ return '1'
+ elif ( # float ---_-___
+ (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+ (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
+ (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
+ return 'f'
+ else:
+ return 'U'
+
+def pinlabels(bit_count, packet_bit_count):
+ if packet_bit_count == 12:
+ if bit_count <= 6:
+ return 'A%i' % (bit_count - 1)
+ else:
+ return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+
+ if packet_bit_count == 24:
+ if bit_count <= 20:
+ return 'A%i' % (bit_count - 1)
+ else:
+ return 'D%i' % (bit_count - 21)
def decode_model(model, bits):
if model == 'maplin_l95ar':
- address = 'Addr' # Address pins A0 to A5
+ address = 'Addr' # Address bits A0 to A5
for i in range(0, 6):
address += ' %i:' % (i + 1) + ('on' if bits[i][0] == '0' else 'off')
button = 'Button'
- # Button pins A6/D5 to A11/D0
+ # Button bits A6/D5 to A11/D0
if bits[6][0] == '0' and bits[11][0] == '0':
button += ' A ON/OFF'
elif bits[7][0] == '0' and bits[11][0] == '0':
button += ' D ON/OFF'
else:
button += ' Unknown'
- return ['%s' % address, bits[0][1], bits[5][2], \
- '%s' % button, bits[6][1], bits[11][2]]
+ return [address, bits[0][1], bits[5][2], \
+ button, bits[6][1], bits[11][2]]
+
+ if model == 'xx1527':
+ addr = 0
+ addr_valid = 1
+ for i in range(0, 20):
+ if bits[i][0] != 'U':
+ addr += int(bits[i][0]) * 2 ** i
+ else:
+ addr_valid = 0
+
+ if addr_valid == 1:
+ address = 'Address 0x%X %X %X' % (addr & 0xFF, (addr >> 8) & 0xFF, addr >> 16)
+ else:
+ address = 'Invalid address as not all bits are 0 or 1'
+
+ output = ' K0 = ' + bits[20][0] + ','
+ output += ' K1 = ' + bits[21][0] + ','
+ output += ' K2 = ' + bits[22][0] + ','
+ output += ' K3 = ' + bits[23][0]
+ return [address, bits[0][1], bits[19][2], \
+ output, bits[20][1], bits[23][2]]
class Decoder(srd.Decoder):
api_version = 3
id = 'rc_encode'
name = 'RC encode'
longname = 'Remote control encoder'
- desc = 'PT2262/HX2262/SC5262 remote control encoder protocol.'
+ desc = 'PT22x2/HX22x2/SC52x2 and xx1527 remote control encoder protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
('code-words', 'Code words', (6, 7)),
)
options = (
- {'id': 'remote', 'desc': 'Remote', 'default': 'none',
- 'values': ('none', 'maplin_l95ar')},
+ {'id': 'linecoding', 'desc': 'Encoding', 'default': 'SC52x2/HX22x2', 'values': ('SC52x2/HX22x2', 'xx1527')},
+ {'id': 'remote', 'desc': 'Remote', 'default': 'none', 'values': ('none', 'maplin_l95ar')},
)
def __init__(self):
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.model = self.options['remote']
+ if self.options['linecoding'] == 'xx1527':
+ self.pulses_per_bit = 2
+ self.packet_bits = 24
+ self.model = 'xx1527'
+ else:
+ self.pulses_per_bit = 4 # Each bit is repeated
+ self.packet_bits = 12
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
self.ss = self.samplenum
continue
- if self.bit_count < 12: # Decode A0 to A11.
+ if self.bit_count < self.packet_bits: # Decode A0 to A11 / A23.
self.bit_count += 1
- for i in range(0, 4): # Get four pulses for each bit.
+ for i in range(0, self.pulses_per_bit):
if i > 0:
- pin = self.wait({0: 'e'}) # Get next 3 edges.
+ pin = self.wait({0: 'e'}) # Get next edges if we need more.
samples = self.samplenum - self.samplenumber_last
self.pulses.append(samples) # Save the pulse width.
self.samplenumber_last = self.samplenum
self.es = self.samplenum
- self.bits.append([decode_bit(self.pulses), self.ss,
+ self.bits.append([decode_bit(self.pulses, self.pulses_per_bit), self.ss,
self.es]) # Save states and times.
- idx = bitvals.index(decode_bit(self.pulses))
- self.putx([idx, [decode_bit(self.pulses)]]) # Write decoded bit.
- self.putx([5, [pinlabels(self.bit_count)]]) # Write pin labels.
+ idx = bitvals.index(decode_bit(self.pulses, self.pulses_per_bit))
+ self.putx([idx, [decode_bit(self.pulses, self.pulses_per_bit)]]) # Write decoded bit.
+ self.putx([5, [pinlabels(self.bit_count, self.packet_bits)]]) # Write pin labels.
self.pulses = []
self.ss = self.samplenum
else:
tags = ['Wireless/RF']
annotations = (
('cmd', 'Command'),
- ('params', 'Command parameters'),
- ('disabled', 'Disabled bits'),
- ('return', 'Returned values'),
- ('disabled_return', 'Disabled returned values'),
+ ('param', 'Command parameter'),
+ ('disabled', 'Disabled bit'),
+ ('return', 'Returned value'),
+ ('disabled_return', 'Disabled returned value'),
('interpretation', 'Interpretation'),
)
annotation_rows = (
('commands', 'Commands', (0, 1, 2)),
- ('return', 'Return', (3, 4)),
- ('interpretation', 'Interpretation', (5,)),
+ ('returns', 'Returns', (3, 4)),
+ ('interpretations', 'Interpretations', (5,)),
)
def __init__(self):
import sigrokdecode as srd
+( ANN_RGB, ) = range(1)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'rgb_led_spi'
outputs = []
tags = ['Display']
annotations = (
- ('rgb', 'RGB values'),
+ ('rgb', 'RGB value'),
)
def __init__(self):
self.reset()
def reset(self):
- self.ss_cmd, self.es_cmd = 0, 0
+ self.ss_cmd = None
self.mosi_bytes = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- def putx(self, data):
- self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+ def putg(self, ss, es, cls, text):
+ self.put(ss, es, self.out_ann, [cls, text])
def decode(self, ss, es, data):
- ptype, mosi, miso = data
+ ptype = data[0]
- # Only care about data packets.
+ # Grab the payload of three DATA packets. These hold the
+ # RGB values (in this very order).
if ptype != 'DATA':
return
- self.ss, self.es = ss, es
-
- if len(self.mosi_bytes) == 0:
+ _, mosi, _ = data
+ if not self.mosi_bytes:
self.ss_cmd = ss
self.mosi_bytes.append(mosi)
-
- # RGB value == 3 bytes
- if len(self.mosi_bytes) != 3:
+ if len(self.mosi_bytes) < 3:
return
- red, green, blue = self.mosi_bytes
+ # Emit annotations. Invalidate accumulated details as soon as
+ # they were processed, to prepare the next iteration.
+ ss_cmd, es_cmd = self.ss_cmd, es
+ self.ss_cmd = None
+ red, green, blue = self.mosi_bytes[:3]
+ self.mosi_bytes.clear()
rgb_value = int(red) << 16 | int(green) << 8 | int(blue)
-
- self.es_cmd = es
- self.putx([0, ['#%.6x' % rgb_value]])
- self.mosi_bytes = []
+ self.putg(ss_cmd, es_cmd, ANN_RGB, ['#{:06x}'.format(rgb_value)])
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
+# Implementor's notes on the wire format:
+# - World Semi vendor, (Adafruit copy of the) datasheet
+# https://cdn-shop.adafruit.com/datasheets/WS2812.pdf
+# - reset pulse is 50us (or more) of low pin level
+# - 24bits per WS281x item, 3x 8bits, MSB first, GRB sequence,
+# cascaded WS281x items, all "excess bits" are passed through
+# - bit time starts with high period, continues with low period,
+# high to low periods' ratio determines bit value, datasheet
+# mentions 0.35us/0.8us for value 0, 0.7us/0.6us for value 1
+# (huge 150ns tolerances, un-even 0/1 value length, hmm)
+# - experience suggests the timing "is variable", rough estimation
+# often is good enough, microcontroller firmware got away with
+# four quanta per bit time, or even with three quanta (30%/60%),
+# Adafruit learn article suggests 1.2us total and 0.4/0.8 or
+# 0.8/0.4 high/low parts, four quanta are easier to handle when
+# the bit stream is sent via SPI to avoid MCU bit banging and its
+# inaccurate timing (when interrupts are used in the firmware)
+# - RGBW datasheet (Adafruit copy) for SK6812
+# https://cdn-shop.adafruit.com/product-files/2757/p2757_SK6812RGBW_REV01.pdf
+# also 1.2us total, shared across 0.3/0.9 for 0, 0.6/0.6 for 1,
+# 80us reset pulse, R8/G8/B8/W8 format per 32bits
+# - WS2815, RGB LED, uses GRB wire format, 280us RESET pulse width
+# - more vendors and models available and in popular use,
+# suggests "one third" or "two thirds" ratio would be most robust,
+# sample "a little before" the bit half? reset pulse width may need
+# to become an option? matrices and/or fast refresh environments
+# may want to experiment with back to back pixel streams
+
import sigrokdecode as srd
-from functools import reduce
+from common.srdhelper import bitpack_msb
class SamplerateError(Exception):
pass
+class DecoderError(Exception):
+ pass
+
+(
+ ANN_BIT, ANN_RESET, ANN_RGB,
+ ANN_COMP_R, ANN_COMP_G, ANN_COMP_B, ANN_COMP_W,
+) = range(7)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'rgb_led_ws281x'
('bit', 'Bit'),
('reset', 'RESET'),
('rgb', 'RGB'),
+ ('r', 'R'),
+ ('g', 'G'),
+ ('b', 'B'),
+ ('w', 'W'),
)
annotation_rows = (
- ('bit', 'Bits', (0, 1)),
- ('rgb', 'RGB', (2,)),
+ ('bits', 'Bits', (ANN_BIT, ANN_RESET,)),
+ ('rgb-comps', 'RGB components', (ANN_COMP_R, ANN_COMP_G, ANN_COMP_B, ANN_COMP_W,)),
+ ('rgb-vals', 'RGB values', (ANN_RGB,)),
+ )
+ options = (
+ {'id': 'wireorder', 'desc': 'colour components order (wire)',
+ 'default': 'GRB',
+ 'values': ('BGR', 'BRG', 'GBR', 'GRB', 'RBG', 'RGB', 'RWBG', 'RGBW')},
+ {'id': 'textorder', 'desc': 'components output order (text)',
+ 'default': 'RGB[W]', 'values': ('wire', 'RGB[W]', 'RGB', 'RGBW', 'RGWB')},
)
def __init__(self):
def reset(self):
self.samplerate = None
- self.oldpin = None
- self.ss_packet = None
- self.ss = None
- self.es = None
self.bits = []
- self.inreset = False
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- def handle_bits(self, samplenum):
- if len(self.bits) == 24:
- grb = reduce(lambda a, b: (a << 1) | b, self.bits)
- rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff)
- self.put(self.ss_packet, samplenum, self.out_ann,
- [2, ['#%06x' % rgb]])
- self.bits = []
- self.ss_packet = None
+ def putg(self, ss, es, cls, text):
+ self.put(ss, es, self.out_ann, [cls, text])
+
+ def handle_bits(self):
+ if len(self.bits) < self.need_bits:
+ return
+ ss_packet, es_packet = self.bits[0][1], self.bits[-1][2]
+ r, g, b, w = 0, 0, 0, None
+ comps = []
+ for i, c in enumerate(self.wireformat):
+ first_idx, after_idx = 8 * i, 8 * i + 8
+ comp_bits = self.bits[first_idx:after_idx]
+ comp_ss, comp_es = comp_bits[0][1], comp_bits[-1][2]
+ comp_value = bitpack_msb(comp_bits, 0)
+ comp_text = '{:02x}'.format(comp_value)
+ comp_ann = {
+ 'r': ANN_COMP_R, 'g': ANN_COMP_G,
+ 'b': ANN_COMP_B, 'w': ANN_COMP_W,
+ }.get(c.lower(), None)
+ comp_item = (comp_ss, comp_es, comp_ann, comp_value, comp_text)
+ comps.append(comp_item)
+ if c.lower() == 'r':
+ r = comp_value
+ elif c.lower() == 'g':
+ g = comp_value
+ elif c.lower() == 'b':
+ b = comp_value
+ elif c.lower() == 'w':
+ w = comp_value
+ wt = '' if w is None else '{:02x}'.format(w)
+ if self.textformat == 'wire':
+ rgb_text = '#' + ''.join([c[-1] for c in comps])
+ else:
+ rgb_text = self.textformat.format(r = r, g = g, b = b, w = w, wt = wt)
+ for ss_comp, es_comp, cls_comp, value_comp, text_comp in comps:
+ self.putg(ss_comp, es_comp, cls_comp, [text_comp])
+ if rgb_text:
+ self.putg(ss_packet, es_packet, ANN_RGB, [rgb_text])
+ self.bits.clear()
+
+ def handle_bit(self, ss, es, value, ann_late = False):
+ if not ann_late:
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, ANN_BIT, text)
+ item = (value, ss, es)
+ self.bits.append(item)
+ self.handle_bits()
+ if ann_late:
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, ANN_BIT, text)
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- while True:
- # TODO: Come up with more appropriate self.wait() conditions.
- (pin,) = self.wait()
-
- if self.oldpin is None:
- self.oldpin = pin
- continue
-
- # Check RESET condition (manufacturer recommends 50 usec minimal,
- # but real minimum is ~10 usec).
- if not self.inreset and not pin and self.es is not None and \
- (self.samplenum - self.es) / self.samplerate > 50e-6:
-
- # Decode last bit value.
- tH = (self.es - self.ss) / self.samplerate
- bit_ = True if tH >= 625e-9 else False
-
- self.bits.append(bit_)
- self.handle_bits(self.es)
-
- self.put(self.ss, self.es, self.out_ann, [0, ['%d' % bit_]])
- self.put(self.es, self.samplenum, self.out_ann,
- [1, ['RESET', 'RST', 'R']])
-
- self.inreset = True
- self.bits = []
- self.ss_packet = None
- self.ss = None
-
- if not self.oldpin and pin:
- # Rising edge.
- if self.ss and self.es:
- period = self.samplenum - self.ss
- duty = self.es - self.ss
- # Ideal duty for T0H: 33%, T1H: 66%.
- bit_ = (duty / period) > 0.5
+ # Preprocess options here, to simplify logic which executes
+ # much later in loops while settings have the same values.
+ wireorder = self.options['wireorder'].lower()
+ self.wireformat = [c for c in wireorder if c in 'rgbw']
+ self.need_bits = len(self.wireformat) * 8
+ textorder = self.options['textorder'].lower()
+ if textorder == 'wire':
+ self.textformat = 'wire'
+ elif textorder == 'rgb[w]':
+ self.textformat = '#{r:02x}{g:02x}{b:02x}{wt:s}'
+ else:
+ self.textformat = {
+ # "Obvious" permutations of R/G/B.
+ 'bgr': '#{b:02x}{g:02x}{r:02x}',
+ 'brg': '#{b:02x}{r:02x}{g:02x}',
+ 'gbr': '#{g:02x}{b:02x}{r:02x}',
+ 'grb': '#{g:02x}{r:02x}{b:02x}',
+ 'rbg': '#{r:02x}{b:02x}{g:02x}',
+ 'rgb': '#{r:02x}{g:02x}{b:02x}',
+ # RGB plus White. Only one of them useful?
+ 'rgbw': '#{r:02x}{g:02x}{b:02x}{w:02x}',
+ # Weird RGBW permutation for compatibility to test case.
+ # Neither used RGBW nor the 'wire' order. Obsolete now?
+ 'rgwb': '#{r:02x}{g:02x}{w:02x}{b:02x}',
+ }.get(textorder, None)
+ if self.textformat is None:
+ raise DecoderError('Unsupported text output format.')
- self.put(self.ss, self.samplenum, self.out_ann,
- [0, ['%d' % bit_]])
+ # Either check for edges which communicate bit values, or for
+ # long periods of idle level which represent a reset pulse.
+ # Track the left-most, right-most, and inner edge positions of
+ # a bit. The positive period's width determines the bit's value.
+ # Initially synchronize to the input stream by searching for a
+ # low period, which preceeds a data bit or starts a reset pulse.
+ # Don't annotate the very first reset pulse, but process it. We
+ # may not see the right-most edge of a data bit when reset is
+ # adjacent to that bit time.
+ cond_bit_starts = {0: 'r'}
+ cond_inbit_edge = {0: 'f'}
+ samples_625ns = int(self.samplerate * 625e-9)
+ samples_50us = round(self.samplerate * 50e-6)
+ cond_reset_pulse = {'skip': samples_50us + 1}
+ conds = [cond_bit_starts, cond_inbit_edge, cond_reset_pulse]
+ ss_bit, inv_bit, es_bit = None, None, None
+ pin, = self.wait({0: 'l'})
+ inv_bit = self.samplenum
+ check_reset = False
+ while True:
+ pin, = self.wait(conds)
- self.bits.append(bit_)
- self.handle_bits(self.samplenum)
+ # Check RESET condition. Manufacturers may disagree on the
+ # minimal pulse width. 50us are recommended in datasheets,
+ # experiments suggest the limit is around 10us.
+ # When the RESET pulse is adjacent to the low phase of the
+ # last bit time, we have no appropriate condition for the
+ # bit time's end location. That's why this BIT's annotation
+ # is shorter (only spans the high phase), and the RESET
+ # annotation immediately follows (spans from the falling edge
+ # to the end of the minimum RESET pulse width).
+ if check_reset and self.matched[2]:
+ es_bit = inv_bit
+ ss_rst, es_rst = inv_bit, self.samplenum
- if self.ss_packet is None:
- self.ss_packet = self.samplenum
+ if ss_bit and inv_bit and es_bit:
+ # Decode last bit value. Use the last processed bit's
+ # width for comparison when available. Fallback to an
+ # arbitrary threshold otherwise (which can result in
+ # false detection of value 1 for those captures where
+ # high and low pulses are of similar width).
+ duty = inv_bit - ss_bit
+ thres = samples_625ns
+ if self.bits:
+ period = self.bits[-1][2] - self.bits[-1][1]
+ thres = period * 0.5
+ bit_value = 1 if duty >= thres else 0
+ self.handle_bit(ss_bit, inv_bit, bit_value, True)
- self.ss = self.samplenum
+ if ss_rst and es_rst:
+ text = ['RESET', 'RST', 'R']
+ self.putg(ss_rst, es_rst, ANN_RESET, text)
+ check_reset = False
- elif self.oldpin and not pin:
- # Falling edge.
- self.inreset = False
- self.es = self.samplenum
+ self.bits.clear()
+ ss_bit, inv_bit, es_bit = None, None, None
- self.oldpin = pin
+ # Rising edge starts a bit time. Falling edge ends its high
+ # period. Get the previous bit's duty cycle and thus its
+ # bit value when the next bit starts.
+ if self.matched[0]: # and pin:
+ check_reset = False
+ if ss_bit and inv_bit:
+ # Got a previous bit? Handle it.
+ es_bit = self.samplenum
+ period = es_bit - ss_bit
+ duty = inv_bit - ss_bit
+ # Ideal duty for T0H: 33%, T1H: 66%.
+ bit_value = 1 if (duty / period) > 0.5 else 0
+ self.handle_bit(ss_bit, es_bit, bit_value)
+ ss_bit, inv_bit, es_bit = self.samplenum, None, None
+ if self.matched[1]: # and not pin:
+ check_reset = True
+ inv_bit = self.samplenum
)
annotation_rows = (
('bits', 'Bits', tuple(range(0, 8 + 1)) + (11, 12, 13)),
- ('regs', 'Register access', (14, 15)),
+ ('regs', 'Register accesses', (14, 15)),
('date-time', 'Date/time', (9, 10)),
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Anthony Symons <antus@pcmhacking.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+SAE J1850 Variable Pulse Width decoder. Decode GM VPW 1X and 4X Vehicle Bus.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Anthony Symons <antus@pcmhacking.net>
+## Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack_msb
+
+# VPW Timings. From the SAE J1850 1995 rev section 23.406 documentation.
+# Ideal, minimum and maximum tolerances.
+VPW_SOF = 200
+VPW_SOFL = 164
+VPW_SOFH = 245 # 240 by the spec, 245 so a 60us 4x sample will pass
+VPW_LONG = 128
+VPW_LONGL = 97
+VPW_LONGH = 170 # 164 by the spec but 170 for low sample rate tolerance.
+VPW_SHORT = 64
+VPW_SHORTL = 24 # 35 by the spec, 24 to allow down to 6us as measured in practice for 4x @ 1mhz sampling
+VPW_SHORTH = 97
+VPW_IFS = 240
+
+class SamplerateError(Exception):
+ pass
+
+(
+ ANN_SOF, ANN_BIT, ANN_IFS, ANN_BYTE,
+ ANN_PRIO, ANN_DEST, ANN_SRC, ANN_MODE, ANN_DATA, ANN_CSUM,
+ ANN_M1_PID,
+ ANN_WARN,
+) = range(12)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sae_j1850_vpw'
+ name = 'SAE J1850 VPW'
+ longname = 'SAE J1850 VPW.'
+ desc = 'SAE J1850 Variable Pulse Width 1x and 4x.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Automotive']
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ annotations = (
+ ('sof', 'SOF'),
+ ('bit', 'Bit'),
+ ('ifs', 'EOF/IFS'),
+ ('byte', 'Byte'),
+ ('prio', 'Priority'),
+ ('dest', 'Destination'),
+ ('src', 'Source'),
+ ('mode', 'Mode'),
+ ('data', 'Data'),
+ ('csum', 'Checksum'),
+ ('m1_pid', 'Pid'),
+ ('warn', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (ANN_SOF, ANN_BIT, ANN_IFS,)),
+ ('bytes', 'Bytes', (ANN_BYTE,)),
+ ('fields', 'Fields', (ANN_PRIO, ANN_DEST, ANN_SRC, ANN_MODE, ANN_DATA, ANN_CSUM,)),
+ ('values', 'Values', (ANN_M1_PID,)),
+ ('warns', 'Warnings', (ANN_WARN,)),
+ )
+ # TODO Add support for options? Polarity. Glitch length.
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.active = 0 # Signal polarity. Needs to become an option?
+ self.bits = []
+ self.fields = {}
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def putg(self, ss, es, cls, texts):
+ self.put(ss, es, self.out_ann, [cls, texts])
+
+ def invalidate_frame_details(self):
+ self.bits.clear()
+ self.fields.clear()
+
+ def handle_databytes(self, fields, data):
+ # TODO Deep inspection of header fields and data values, including
+ # checksum verification results.
+ mode = fields.get('mode', None)
+ if mode is None:
+ return
+ if mode == 1:
+ # An earlier implementation commented that for mode 1 the
+ # first data byte would be the PID. But example captures
+ # have no data bytes in packets for that mode. This position
+ # is taken by the checksum. Is this correct?
+ pid = data[0] if data else fields.get('csum', None)
+ if pid is None:
+ text = ['PID missing']
+ self.putg(ss, es, ANN_WARN, text)
+ else:
+ byte_text = '{:02x}'.format(pid)
+ self.putg(ss, es, ANN_M1_PID, [byte_text])
+
+ def handle_byte(self, ss, es, b):
+ # Annotate all raw byte values. Inspect and process the first
+ # bytes in a frame already. Cease inspection and only accumulate
+ # all other bytes after the mode. The checksum's position and
+ # thus the data bytes' span will only be known when EOF or IFS
+ # were seen. Implementor's note: This method just identifies
+ # header fields. Processing is left to the .handle_databytes()
+ # method. Until then validity will have been checked, too (CS).
+ byte_text = '{:02x}'.format(b)
+ self.putg(ss, es, ANN_BYTE, [byte_text])
+
+ if not 'prio' in self.fields:
+ self.fields.update({'prio': b})
+ self.putg(ss, es, ANN_PRIO, [byte_text])
+ return
+ if not 'dest' in self.fields:
+ self.fields.update({'dest': b})
+ self.putg(ss, es, ANN_DEST, [byte_text])
+ return
+ if not 'src' in self.fields:
+ self.fields.update({'src': b})
+ self.putg(ss, es, ANN_SRC, [byte_text])
+ return
+ if not 'mode' in self.fields:
+ self.fields.update({'mode': b})
+ self.putg(ss, es, ANN_MODE, [byte_text])
+ return
+ if not 'data' in self.fields:
+ self.fields.update({'data': [], 'csum': None})
+ self.fields['data'].append((b, ss, es))
+
+ def handle_sof(self, ss, es, speed):
+ text = ['{speed:d}x SOF', 'S{speed:d}', 'S']
+ text = [f.format(speed = speed) for f in text]
+ self.putg(ss, es, ANN_SOF, text)
+ self.invalidate_frame_details()
+ self.fields.update({'speed': speed})
+
+ def handle_bit(self, ss, es, b):
+ self.bits.append((b, ss, es))
+ self.putg(ss, es, ANN_BIT, ['{:d}'.format(b)])
+ if len(self.bits) < 8:
+ return
+ ss, es = self.bits[0][1], self.bits[-1][2]
+ b = bitpack_msb(self.bits, 0)
+ self.bits.clear()
+ self.handle_byte(ss, es, b)
+
+ def handle_eof(self, ss, es, is_ifs = False):
+ # EOF or IFS were seen. Post process the data bytes sequence.
+ # Separate the checksum from the data bytes. Emit annotations.
+ # Pass data bytes and header fields to deeper inspection.
+ data = self.fields.get('data', {})
+ if not data:
+ text = ['Short data phase', 'Data']
+ self.putg(ss, es, ANN_WARN, text)
+ csum = None
+ if len(data) >= 1:
+ csum, ss_csum, es_csum = data.pop()
+ self.fields.update({'csum': csum})
+ # TODO Verify checksum's correctness?
+ if data:
+ ss_data, es_data = data[0][1], data[-1][2]
+ text = ' '.join(['{:02x}'.format(b[0]) for b in data])
+ self.putg(ss_data, es_data, ANN_DATA, [text])
+ if csum is not None:
+ text = '{:02x}'.format(csum)
+ self.putg(ss_csum, es_csum, ANN_CSUM, [text])
+ text = ['IFS', 'I'] if is_ifs else ['EOF', 'E']
+ self.putg(ss, es, ANN_IFS, text)
+ self.handle_databytes(self.fields, data);
+ self.invalidate_frame_details()
+
+ def handle_unknown(self, ss, es):
+ text = ['Unknown condition', 'Unknown', 'UNK']
+ self.putg(ss, es, ANN_WARN, text)
+ self.invalidate_frame_details()
+
+ def usecs_to_samples(self, us):
+ us *= 1e-6
+ us *= self.samplerate
+ return int(us)
+
+ def samples_to_usecs(self, n):
+ n /= self.samplerate
+ n *= 1000.0 * 1000.0
+ return int(n)
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+
+ # Get the distance between edges. Classify the distance
+ # to derive symbols and data bit values. Prepare waiting
+ # for an interframe gap as well, while this part of the
+ # condition is optional (switches in and out at runtime).
+ conds_edge = {0: 'e'}
+ conds_edge_only = [conds_edge]
+ conds_edge_idle = [conds_edge, {'skip': 0}]
+ conds = conds_edge_only
+ self.wait(conds)
+ es = self.samplenum
+ spd = None
+ while True:
+ ss = es
+ pin, = self.wait(conds)
+ es = self.samplenum
+ count = es - ss
+ t = self.samples_to_usecs(count)
+
+ # Synchronization to the next frame. Wait for SOF.
+ # Silently keep synchronizing until SOF was seen.
+ if spd is None:
+ if not self.matched[0]:
+ continue
+ if pin != self.active:
+ continue
+
+ # Detect the frame's speed from the SOF length. Adjust
+ # the expected BIT lengths to the SOF derived speed.
+ # Arrange for the additional supervision of EOF/IFS.
+ if t in range(VPW_SOFL // 1, VPW_SOFH // 1):
+ spd = 1
+ elif t in range(VPW_SOFL // 4, VPW_SOFH // 4):
+ spd = 4
+ else:
+ continue
+ short_lower, short_upper = VPW_SHORTL // spd, VPW_SHORTH // spd
+ long_lower, long_upper = VPW_LONGL // spd, VPW_LONGH // spd
+ samples = self.usecs_to_samples(VPW_IFS // spd)
+ conds_edge_idle[-1]['skip'] = samples
+ conds = conds_edge_idle
+
+ # Emit the SOF annotation. Start collecting DATA.
+ self.handle_sof(ss, es, spd)
+ continue
+
+ # Inside the DATA phase. Get data bits. Handle EOF/IFS.
+ if len(conds) > 1 and self.matched[1]:
+ # TODO The current implementation gets here after a
+ # pre-determined minimum wait time. Does not differ
+ # between EOF and IFS. An earlier implementation had
+ # this developer note: EOF=239-280 IFS=281+
+ self.handle_eof(ss, es)
+ # Enter the IDLE phase. Wait for the next SOF.
+ spd = None
+ conds = conds_edge_only
+ continue
+ if t in range(short_lower, short_upper):
+ value = 1 if pin == self.active else 0
+ self.handle_bit(ss, es, value)
+ continue
+ if t in range(long_lower, long_upper):
+ value = 0 if pin == self.active else 1
+ self.handle_bit(ss, es, value)
+ continue
+
+ # Implementation detail: An earlier implementation used to
+ # ignore everything that was not handled above. This would
+ # be motivated by the noisy environment the protocol is
+ # typically used in. This more recent implementation accepts
+ # short glitches, but by design falls back to synchronization
+ # to the input stream for other unhandled conditions. This
+ # wants to improve usability of the decoder, by presenting
+ # potential issues to the user. The threshold (microseconds
+ # between edges that are not valid symbols that are handled
+ # above) is an arbitrary choice.
+ if t <= 2:
+ continue
+ self.handle_unknown(ss, es)
+ spd = None
+ conds = conds_edge_only
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+SBUS by Futaba, a hobby remote control protocol on top of UART.
+Sometimes referred to as "Serial BUS" or S-BUS.
+
+UART communication typically runs at 100kbps with 8e2 frame format and
+inverted signals (high voltage level is logic low).
+
+SBUS messages take 3ms to transfer, and typically repeat in intervals
+of 7ms or 14ms. An SBUS message consists of 25 UART bytes, and carries
+16 proportional channels with 11 bits each, and 2 digital channels
+(boolean, 1 bit), and flags which represent current communication state.
+Proportional channel values typically are in the 192..1792 range, but
+individual implementations may differ.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+"""
+OUTPUT_PYTHON format:
+
+Packet:
+(<ptype>, <pdata>)
+
+This is the list of <ptype> codes and their respective <pdata> values:
+ - 'HEADER': The data is the header byte's value.
+ - 'PROPORTIONAL': The data is a tuple of the channel number (1-based)
+ and the channel's value.
+ - 'DIGITAL': The data is a tuple of the channel number (1-based)
+ and the channel's value.
+ - 'FLAG': The data is a tuple of the flag's name, and the flag's value.
+ - 'FOOTER': The data is the footer byte's value.
+"""
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack_lsb
+
+class Ann:
+ HEADER, PROPORTIONAL, DIGITAL, FRAME_LOST, FAILSAFE, FOOTER, \
+ WARN = range(7)
+ FLAG_LSB = FRAME_LOST
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sbus_futaba'
+ name = 'SBUS (Futaba)'
+ longname = 'Futaba SBUS (Serial bus)'
+ desc = 'Serial bus for hobby remote control by Futaba'
+ license = 'gplv2+'
+ inputs = ['uart']
+ outputs = ['sbus_futaba']
+ tags = ['Remote Control']
+ options = (
+ {'id': 'prop_val_min', 'desc': 'Proportional value lower boundary', 'default': 0},
+ {'id': 'prop_val_max', 'desc': 'Proportional value upper boundary', 'default': 2047},
+ )
+ annotations = (
+ ('header', 'Header'),
+ ('proportional', 'Proportional'),
+ ('digital', 'Digital'),
+ ('framelost', 'Frame Lost'),
+ ('failsafe', 'Failsafe'),
+ ('footer', 'Footer'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('framing', 'Framing', (Ann.HEADER, Ann.FOOTER,
+ Ann.FRAME_LOST, Ann.FAILSAFE)),
+ ('channels', 'Channels', (Ann.PROPORTIONAL, Ann.DIGITAL)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.bits_accum = []
+ self.sent_fields = None
+ self.msg_complete = None
+ self.failed = None
+ self.reset()
+
+ def reset(self):
+ self.bits_accum.clear()
+ self.sent_fields = 0
+ self.msg_complete = False
+ self.failed = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_py = self.register(srd.OUTPUT_PYTHON)
+
+ def putg(self, ss, es, data):
+ # Put a graphical annotation.
+ self.put(ss, es, self.out_ann, data)
+
+ def putpy(self, ss, es, data):
+ # Pass Python to upper layers.
+ self.put(ss, es, self.out_py, data)
+
+ def get_ss_es_bits(self, bitcount):
+ # Get start/end times, and bit values of given length.
+ # Gets all remaining data when 'bitcount' is None.
+ if bitcount is None:
+ bitcount = len(self.bits_accum)
+ if len(self.bits_accum) < bitcount:
+ return None, None, None
+ bits = self.bits_accum[:bitcount]
+ self.bits_accum = self.bits_accum[bitcount:]
+ ss, es = bits[0][1], bits[-1][2]
+ bits = [b[0] for b in bits]
+ return ss, es, bits
+
+ def flush_accum_bits(self):
+ # Valid data was queued. See if we got full SBUS fields so far.
+ # Annotate them early, cease inspection of failed messages. The
+ # implementation is phrased to reduce the potential for clipboard
+ # errors: 'upto' is the next supported field count, 'want' is one
+ # field's bit count. Grab as many as we find in an invocation.
+ upto = 0
+ if self.failed:
+ return
+ # Annotate the header byte. Not seeing the expected bit pattern
+ # emits a warning annotation, but by design won't fail the SBUS
+ # message. It's considered more useful to present the channels'
+ # values instead. The warning still raises awareness.
+ upto += 1
+ want = 8
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['0x{:02x}'.format(value)]
+ self.putg(ss, es, [Ann.HEADER, text])
+ if value != 0x0f:
+ text = ['Unexpected header', 'Header']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.putpy(ss, es, ['HEADER', value])
+ self.sent_fields += 1
+ # Annotate the proportional channels' data. Check for user
+ # provided value range violations. Channel numbers are in
+ # the 1..18 range (1-based).
+ upto += 16
+ want = 11
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, [Ann.PROPORTIONAL, text])
+ if value < self.options['prop_val_min']:
+ text = ['Low proportional value', 'Low value', 'Low']
+ self.putg(ss, es, [Ann.WARN, text])
+ if value > self.options['prop_val_max']:
+ text = ['High proportional value', 'High value', 'High']
+ self.putg(ss, es, [Ann.WARN, text])
+ idx = self.sent_fields - (upto - 16)
+ ch_nr = 1 + idx
+ self.putpy(ss, es, ['PROPORTIONAL', (ch_nr, value)])
+ self.sent_fields += 1
+ # Annotate the digital channels' data.
+ upto += 2
+ want = 1
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, [Ann.DIGITAL, text])
+ idx = self.sent_fields - (upto - 2)
+ ch_nr = 17 + idx
+ self.putpy(ss, es, ['DIGITAL', (ch_nr, value)])
+ self.sent_fields += 1
+ # Annotate the flags' state. Index starts from LSB.
+ flag_names = ['framelost', 'failsafe', 'msb']
+ upto += 2
+ want = 1
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ idx = self.sent_fields - (upto - 2)
+ cls = Ann.FLAG_LSB + idx
+ self.putg(ss, es, [cls, text])
+ flg_name = flag_names[idx]
+ self.putpy(ss, es, ['FLAG', (flg_name, value)])
+ self.sent_fields += 1
+ # Warn when flags' padding (bits [7:4]) is unexpexted.
+ upto += 1
+ want = 4
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ if value != 0x0:
+ text = ['Unexpected MSB flags', 'Flags']
+ self.putg(ss, es, [Ann.WARN, text])
+ flg_name = flag_names[-1]
+ self.putpy(ss, es, ['FLAG', (flg_name, value)])
+ self.sent_fields += 1
+ # Annotate the footer byte. Warn when unexpected.
+ upto += 1
+ want = 8
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['0x{:02x}'.format(value)]
+ self.putg(ss, es, [Ann.FOOTER, text])
+ if value != 0x00:
+ text = ['Unexpected footer', 'Footer']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.putpy(ss, es, ['FOOTER', value])
+ self.sent_fields += 1
+ # Check for the completion of an SBUS message. Warn when more
+ # UART data is seen after the message. Defer the warning until
+ # more bits were collected, flush at next IDLE or BREAK, which
+ # spans all unprocessed data, and improves perception.
+ if self.sent_fields >= upto:
+ self.msg_complete = True
+ if self.msg_complete and self.bits_accum:
+ self.failed = ['Excess data bits', 'Excess']
+
+ def handle_bits(self, ss, es, bits):
+ # UART data bits were seen. Store them, validity is yet unknown.
+ self.bits_accum.extend(bits)
+
+ def handle_frame(self, ss, es, value, valid):
+ # A UART frame became complete. Get its validity. Process its bits.
+ if not valid:
+ self.failed = ['Invalid data', 'Invalid']
+ self.flush_accum_bits()
+
+ def handle_idle(self, ss, es):
+ # An IDLE period was seen in the UART level. Flush, reset state.
+ if self.bits_accum and not self.failed:
+ self.failed = ['Unprocessed data bits', 'Unprocessed']
+ if self.bits_accum and self.failed:
+ ss, es, _ = self.get_ss_es_bits(None)
+ self.putg(ss, es, [Ann.WARN, self.failed])
+ self.reset()
+
+ def handle_break(self, ss, es):
+ # A BREAK period was seen in the UART level. Warn, reset state.
+ break_ss, break_es = ss, es
+ if not self.failed:
+ self.failed = ['BREAK condition', 'Break']
+ # Re-use logic for "annotated bits warning".
+ self.handle_idle(None, None)
+ # Unconditionally annotate BREAK as warning.
+ text = ['BREAK condition', 'Break']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.reset()
+
+ def decode(self, ss, es, data):
+ # Implementor's note: Expects DATA bits to arrive before FRAME
+ # validity. Either of IDLE or BREAK terminates an SBUS message.
+ ptype, rxtx, pdata = data
+ if ptype == 'DATA':
+ _, bits = pdata
+ self.handle_bits(ss, es, bits)
+ elif ptype == 'FRAME':
+ value, valid = pdata
+ self.handle_frame(ss, es, value, valid)
+ elif ptype == 'IDLE':
+ self.handle_idle(ss, es)
+ elif ptype == 'BREAK':
+ self.handle_break(ss, es)
import re
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Pin = SrdIntEnum.from_str('Pin', 'CLK DATA CE')
ann_cmdbit, ann_databit, ann_cmd, ann_data, ann_warning = range(5)
('cmdbit', 'Command bit'),
('databit', 'Data bit'),
('cmd', 'Command'),
- ('data', 'Data byte'),
- ('warnings', 'Human-readable warnings'),
+ ('databyte', 'Data byte'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('bits', 'Bits', (ann_cmdbit, ann_databit)),
- ('commands', 'Commands', (ann_cmd,)),
('data', 'Data', (ann_data,)),
+ ('commands', 'Commands', (ann_cmd,)),
('warnings', 'Warnings', (ann_warning,)),
)
def decode(self):
while True:
- # Wait for CLK edge or CE edge.
- clk, d, ce = self.wait([{0: 'e'}, {2: 'e'}])
+ # Wait for CLK edge or CE# edge.
+ clk, d, ce = self.wait([{Pin.CLK: 'e'}, {Pin.CE: 'e'}])
if self.matched[0] and ce == 1 and clk == 1:
# Rising clk edge and command mode.
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2015-2020 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
##
import sigrokdecode as srd
-from common.sdcard import (cmd_names, acmd_names, accepted_voltages, card_status, sd_status)
+from common.srdhelper import SrdIntEnum, SrdStrEnum
+from common.sdcard import (cmd_names, acmd_names, accepted_voltages, sd_status)
+
+responses = '1 1b 2 3 6 7'.split()
+token_fields = 'START TRANSMISSION CMD ARG CRC END'.split()
+reg_card_status = 'OUT_OF_RANGE ADDRESS_ERROR BLOCK_LEN_ERROR ERASE_SEQ_ERROR \
+ ERASE_PARAM WP_VIOLATION CARD_IS_LOCKED LOCK_UNLOCK_FAILED COM_CRC_ERROR \
+ ILLEGAL_COMMAND CARD_ECC_FAILED CC_ERROR ERROR RSVD_DEFERRED_RESPONSE \
+ CSD_OVERWRITE WP_ERASE_SKIP CARD_ECC_DISABLED ERASE_RESET CURRENT_STATE \
+ READY_FOR_DATA RSVD FX_EVENT APP_CMD RSVD_SDIO AKE_SEQ_ERROR RSVD_APP_CMD \
+ RSVD_TESTMODE'.split()
+reg_cid = 'MID OID PNM PRV PSN RSVD MDT CRC ONE'.split()
+reg_csd = 'CSD_STRUCTURE RSVD TAAC NSAC TRAN_SPEED CCC READ_BL_LEN \
+ READ_BL_PARTIAL WRITE_BLK_MISALIGN READ_BLK_MISALIGN DSR_IMP C_SIZE \
+ VDD_R_CURR_MIN VDD_R_CURR_MAX VDD_W_CURR_MIN VDD_W_CURR_MAX C_SIZE_MULT \
+ ERASE_BLK_EN SECTOR_SIZE WP_GRP_SIZE WP_GRP_ENABLE R2W_FACTOR \
+ WRITE_BL_LEN WRITE_BL_PARTIAL FILE_FORMAT_GRP COPY PERM_WRITE_PROTECT \
+ TMP_WRITE_PROTECT FILE_FORMAT CRC ONE'.split()
+
+Pin = SrdIntEnum.from_str('Pin', 'CMD CLK DAT0 DAT1 DAT2 DAT3')
+
+a = ['CMD%d' % i for i in range(64)] + ['ACMD%d' % i for i in range(64)] + \
+ ['RESPONSE_R' + r.upper() for r in responses] + \
+ ['R_STATUS_' + r for r in reg_card_status] + \
+ ['R_CID_' + r for r in reg_cid] + \
+ ['R_CSD_' + r for r in reg_csd] + \
+ ['BIT_' + r for r in ('0', '1')] + \
+ ['F_' + f for f in token_fields] + \
+ ['DECODED_BIT', 'DECODED_F']
+Ann = SrdIntEnum.from_list('Ann', a)
+
+s = ['GET_COMMAND_TOKEN', 'HANDLE_CMD999'] + \
+ ['HANDLE_CMD%d' % i for i in range(64)] + \
+ ['HANDLE_ACMD%d' % i for i in range(64)] + \
+ ['GET_RESPONSE_R%s' % r.upper() for r in responses]
+St = SrdStrEnum.from_list('St', s)
+
+class Bit:
+ def __init__(self, s, e, b):
+ self.ss, self.es, self.bit = s, e ,b
class Decoder(srd.Decoder):
api_version = 3
)
annotations = \
tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \
- tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \
- ('bits', 'Bits'),
- ('field-start', 'Start bit'),
- ('field-transmission', 'Transmission bit'),
- ('field-cmd', 'Command'),
- ('field-arg', 'Argument'),
- ('field-crc', 'CRC'),
- ('field-end', 'End bit'),
- ('decoded-bits', 'Decoded bits'),
- ('decoded-fields', 'Decoded fields'),
+ tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + \
+ tuple(('response_r%s' % r, 'R%s' % r) for r in responses) + \
+ tuple(('reg_status_' + r.lower(), 'Status: ' + r) for r in reg_card_status) + \
+ tuple(('reg_cid_' + r.lower(), 'CID: ' + r) for r in reg_cid) + \
+ tuple(('reg_csd_' + r.lower(), 'CSD: ' + r) for r in reg_csd) + \
+ tuple(('bit_' + r, 'Bit ' + r) for r in ('0', '1')) + \
+ tuple(('field-' + r.lower(), r) for r in token_fields) + \
+ ( \
+ ('decoded-bit', 'Decoded bit'),
+ ('decoded-field', 'Decoded field'),
)
annotation_rows = (
- ('raw-bits', 'Raw bits', (128,)),
- ('decoded-bits', 'Decoded bits', (135,)),
- ('decoded-fields', 'Decoded fields', (136,)),
- ('fields', 'Fields', tuple(range(129, 135))),
- ('cmd', 'Commands', tuple(range(128))),
+ ('raw-bits', 'Raw bits', Ann.prefixes('BIT_')),
+ ('decoded-bits', 'Decoded bits', (Ann.DECODED_BIT,) + Ann.prefixes('R_')),
+ ('decoded-fields', 'Decoded fields', (Ann.DECODED_F,)),
+ ('fields', 'Fields', Ann.prefixes('F_')),
+ ('commands', 'Commands', Ann.prefixes('CMD ACMD RESPONSE_')),
)
def __init__(self):
self.reset()
def reset(self):
- self.state = 'GET COMMAND TOKEN'
+ self.state = St.GET_COMMAND_TOKEN
self.token = []
self.is_acmd = False # Indicates CMD vs. ACMD
self.cmd = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- def putbit(self, b, data):
- self.put(self.token[b][0], self.token[b][1], self.out_ann, [135, data])
-
def putt(self, data):
- self.put(self.token[0][0], self.token[47][1], self.out_ann, data)
+ self.put(self.token[0].ss, self.token[47].es, self.out_ann, data)
def putf(self, s, e, data):
- self.put(self.token[s][0], self.token[e][1], self.out_ann, data)
+ self.put(self.token[s].ss, self.token[e].es, self.out_ann, data)
def puta(self, s, e, data):
- self.put(self.token[47 - 8 - e][0], self.token[47 - 8 - s][1],
+ self.put(self.token[47 - 8 - e].ss, self.token[47 - 8 - s].es,
self.out_ann, data)
- def putc(self, cmd, desc):
+ def putc(self, desc):
+ cmd = Ann.ACMD0 + self.cmd if self.is_acmd else self.cmd
self.last_cmd = cmd
self.putt([cmd, ['%s: %s' % (self.cmd_str, desc), self.cmd_str,
self.cmd_str.split(' ')[0]]])
- def putr(self, desc):
- self.putt([self.last_cmd, ['Reply: %s' % desc]])
+ def putr(self, r):
+ self.putt([r, ['Response: %s' % r.name.split('_')[1]]])
def cmd_name(self, cmd):
c = acmd_names if self.is_acmd else cmd_names
return c.get(cmd, 'Unknown')
- def get_token_bits(self, cmd, n):
+ def get_token_bits(self, cmd_pin, n):
# Get a bit, return True if we already got 'n' bits, False otherwise.
- self.token.append([self.samplenum, self.samplenum, cmd])
+ self.token.append(Bit(self.samplenum, self.samplenum, cmd_pin))
if len(self.token) > 0:
- self.token[len(self.token) - 2][1] = self.samplenum
+ self.token[len(self.token) - 2].es = self.samplenum
if len(self.token) < n:
return False
- self.token[n - 1][1] += self.token[n - 1][0] - self.token[n - 2][0]
+ self.token[n - 1].es += self.token[n - 1].ss - self.token[n - 2].ss
return True
def handle_common_token_fields(self):
# Annotations for each individual bit.
for bit in range(len(self.token)):
- self.putf(bit, bit, [128, ['%d' % s[bit][2]]])
+ self.putf(bit, bit, [Ann.BIT_0 + s[bit].bit, ['%d' % s[bit].bit]])
# CMD[47:47]: Start bit (always 0)
- self.putf(0, 0, [129, ['Start bit', 'Start', 'S']])
+ self.putf(0, 0, [Ann.F_START, ['Start bit', 'Start', 'S']])
# CMD[46:46]: Transmission bit (1 == host)
- t = 'host' if s[1][2] == 1 else 'card'
- self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']])
+ t = 'host' if s[1].bit == 1 else 'card'
+ self.putf(1, 1, [Ann.F_TRANSMISSION, ['Transmission: ' + t, 'T: ' + t, 'T']])
# CMD[45:40]: Command index (BCD; valid: 0-63)
- self.cmd = int('0b' + ''.join([str(s[i][2]) for i in range(2, 8)]), 2)
+ self.cmd = int('0b' + ''.join([str(s[i].bit) for i in range(2, 8)]), 2)
c = '%s (%d)' % (self.cmd_name(self.cmd), self.cmd)
- self.putf(2, 7, [131, ['Command: ' + c, 'Cmd: ' + c,
+ self.putf(2, 7, [Ann.F_CMD, ['Command: ' + c, 'Cmd: ' + c,
'CMD%d' % self.cmd, 'Cmd', 'C']])
# CMD[39:08]: Argument
- self.arg = int('0b' + ''.join([str(s[i][2]) for i in range(8, 40)]), 2)
- self.putf(8, 39, [132, ['Argument: 0x%08x' % self.arg, 'Arg', 'A']])
+ self.arg = int('0b' + ''.join([str(s[i].bit) for i in range(8, 40)]), 2)
+ self.putf(8, 39, [Ann.F_ARG, ['Argument: 0x%08x' % self.arg, 'Arg', 'A']])
# CMD[07:01]: CRC7
- self.crc = int('0b' + ''.join([str(s[i][2]) for i in range(40, 47)]), 2)
- self.putf(40, 46, [133, ['CRC: 0x%x' % self.crc, 'CRC', 'C']])
+ self.crc = int('0b' + ''.join([str(s[i].bit) for i in range(40, 47)]), 2)
+ self.putf(40, 46, [Ann.F_CRC, ['CRC: 0x%x' % self.crc, 'CRC', 'C']])
# CMD[00:00]: End bit (always 1)
- self.putf(47, 47, [134, ['End bit', 'End', 'E']])
+ self.putf(47, 47, [Ann.F_END, ['End bit', 'End', 'E']])
- def get_command_token(self, cmd):
+ def get_command_token(self, cmd_pin):
# Command tokens (48 bits) are sent serially (MSB-first) by the host
# (over the CMD line), either to one SD card or to multiple ones.
#
# - Bits[07:01]: CRC7
# - Bits[00:00]: End bit (always 1)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
self.handle_common_token_fields()
# Handle command.
s = 'ACMD' if self.is_acmd else 'CMD'
self.cmd_str = '%s%d (%s)' % (s, self.cmd, self.cmd_name(self.cmd))
- if self.cmd in (0, 2, 3, 6, 7, 8, 9, 10, 13, 41, 51, 55):
- self.state = 'HANDLE CMD%d' % self.cmd
+ if hasattr(self, 'handle_%s%d' % (s.lower(), self.cmd)):
+ self.state = St['HANDLE_CMD%d' % self.cmd]
else:
- self.state = 'HANDLE CMD999'
- self.putc(self.cmd, '%s%d' % (s, self.cmd))
+ self.state = St.HANDLE_CMD999
+ self.putc('%s%d' % (s, self.cmd))
def handle_cmd0(self):
# CMD0 (GO_IDLE_STATE) -> no response
- self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(0, 'Reset all SD cards')
- self.token, self.state = [], 'GET COMMAND TOKEN'
+ self.puta(0, 31, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Reset all SD cards')
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
def handle_cmd2(self):
# CMD2 (ALL_SEND_CID) -> R2
- self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(2, 'Ask card for CID number')
- self.token, self.state = [], 'GET RESPONSE R2'
+ self.puta(0, 31, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Ask card for CID number')
+ self.token, self.state = [], St.GET_RESPONSE_R2
def handle_cmd3(self):
# CMD3 (SEND_RELATIVE_ADDR) -> R6
- self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(3, 'Ask card for new relative card address (RCA)')
- self.token, self.state = [], 'GET RESPONSE R6'
+ self.puta(0, 31, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Ask card for new relative card address (RCA)')
+ self.token, self.state = [], St.GET_RESPONSE_R6
def handle_cmd6(self):
# CMD6 (SWITCH_FUNC) -> R1
- self.putc(6, 'Switch/check card function')
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.putc('Switch/check card function')
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_cmd7(self):
# CMD7 (SELECT/DESELECT_CARD) -> R1b
- self.putc(7, 'Select / deselect card')
- self.token, self.state = [], 'GET RESPONSE R6'
+ self.putc('Select / deselect card')
+ self.token, self.state = [], St.GET_RESPONSE_R6
def handle_cmd8(self):
# CMD8 (SEND_IF_COND) -> R7
- self.puta(12, 31, [136, ['Reserved', 'Res', 'R']])
- self.puta(8, 11, [136, ['Supply voltage', 'Voltage', 'VHS', 'V']])
- self.puta(0, 7, [136, ['Check pattern', 'Check pat', 'Check', 'C']])
- self.putc(8, 'Send interface condition to card')
- self.token, self.state = [], 'GET RESPONSE R7'
+ self.puta(12, 31, [Ann.DECODED_F, ['Reserved', 'Res', 'R']])
+ self.puta(8, 11, [Ann.DECODED_F, ['Supply voltage', 'Voltage', 'VHS', 'V']])
+ self.puta(0, 7, [Ann.DECODED_F, ['Check pattern', 'Check pat', 'Check', 'C']])
+ self.putc('Send interface condition to card')
+ self.token, self.state = [], St.GET_RESPONSE_R7
# TODO: Handle case when card doesn't reply with R7 (no reply at all).
def handle_cmd9(self):
# CMD9 (SEND_CSD) -> R2
- self.puta(16, 31, [136, ['RCA', 'R']])
- self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(9, 'Send card-specific data (CSD)')
- self.token, self.state = [], 'GET RESPONSE R2'
+ self.puta(16, 31, [Ann.DECODED_F, ['RCA', 'R']])
+ self.puta(0, 15, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Send card-specific data (CSD)')
+ self.token, self.state = [], St.GET_RESPONSE_R2
def handle_cmd10(self):
# CMD10 (SEND_CID) -> R2
- self.puta(16, 31, [136, ['RCA', 'R']])
- self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(9, 'Send card identification data (CID)')
- self.token, self.state = [], 'GET RESPONSE R2'
+ self.puta(16, 31, [Ann.DECODED_F, ['RCA', 'R']])
+ self.puta(0, 15, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Send card identification data (CID)')
+ self.token, self.state = [], St.GET_RESPONSE_R2
def handle_cmd13(self):
# CMD13 (SEND_STATUS) -> R1
- self.puta(16, 31, [136, ['RCA', 'R']])
- self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(13, 'Send card status register')
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.puta(16, 31, [Ann.DECODED_F, ['RCA', 'R']])
+ self.puta(0, 15, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Send card status register')
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_cmd16(self):
# CMD16 (SET_BLOCKLEN) -> R1
- self.puta(0, 31, [136, ['Block length', 'Blocklen', 'BL', 'B']])
- self.putc(16, 'Set the block length to %d bytes' % self.arg)
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.puta(0, 31, [Ann.DECODED_F, ['Block length', 'Blocklen', 'BL', 'B']])
+ self.putc('Set the block length to %d bytes' % self.arg)
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_cmd55(self):
# CMD55 (APP_CMD) -> R1
- self.puta(16, 31, [136, ['RCA', 'R']])
- self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(55, 'Next command is an application-specific command')
+ self.puta(16, 31, [Ann.DECODED_F, ['RCA', 'R']])
+ self.puta(0, 15, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Next command is an application-specific command')
self.is_acmd = True
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_acmd6(self):
# ACMD6 (SET_BUS_WIDTH) -> R1
- self.putc(64 + 6, 'Read SD config register (SCR)')
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.putc('Read SD config register (SCR)')
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_acmd13(self):
# ACMD13 (SD_STATUS) -> R1
- self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']])
- self.putc(64 + 13, 'Set SD status')
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.puta(0, 31, [Ann.DECODED_F, ['Stuff bits', 'Stuff', 'SB', 'S']])
+ self.putc('Set SD status')
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_acmd41(self):
# ACMD41 (SD_SEND_OP_COND) -> R3
- self.puta(0, 23, [136, ['VDD voltage window', 'VDD volt', 'VDD', 'V']])
- self.puta(24, 24, [136, ['S18R']])
- self.puta(25, 27, [136, ['Reserved', 'Res', 'R']])
- self.puta(28, 28, [136, ['XPC']])
- self.puta(29, 29, [136, ['Reserved for eSD', 'Reserved', 'Res', 'R']])
- self.puta(30, 30, [136, ['Host capacity support info', 'Host capacity',
- 'HCS', 'H']])
- self.puta(31, 31, [136, ['Reserved', 'Res', 'R']])
- self.putc(64 + 41, 'Send HCS info and activate the card init process')
- self.token, self.state = [], 'GET RESPONSE R3'
+ self.puta(0, 23, [Ann.DECODED_F,
+ ['VDD voltage window', 'VDD volt', 'VDD', 'V']])
+ self.puta(24, 24, [Ann.DECODED_F, ['S18R']])
+ self.puta(25, 27, [Ann.DECODED_F, ['Reserved', 'Res', 'R']])
+ self.puta(28, 28, [Ann.DECODED_F, ['XPC']])
+ self.puta(29, 29, [Ann.DECODED_F,
+ ['Reserved for eSD', 'Reserved', 'Res', 'R']])
+ self.puta(30, 30, [Ann.DECODED_F,
+ ['Host capacity support info', 'Host capacity', 'HCS', 'H']])
+ self.puta(31, 31, [Ann.DECODED_F, ['Reserved', 'Res', 'R']])
+ self.putc('Send HCS info and activate the card init process')
+ self.token, self.state = [], St.GET_RESPONSE_R3
def handle_acmd51(self):
# ACMD51 (SEND_SCR) -> R1
- self.putc(64 + 51, 'Read SD config register (SCR)')
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.putc('Read SD config register (SCR)')
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_cmd999(self):
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.token, self.state = [], St.GET_RESPONSE_R1
def handle_acmd999(self):
- self.token, self.state = [], 'GET RESPONSE R1'
+ self.token, self.state = [], St.GET_RESPONSE_R1
+
+ def handle_reg_status(self):
+ self.putf(8, 8, [Ann.R_STATUS_OUT_OF_RANGE, ['OUT_OF_RANGE']])
+ self.putf(9, 9, [Ann.R_STATUS_ADDRESS_ERROR, ['ADDRESS_ERROR']])
+ self.putf(10, 10, [Ann.R_STATUS_BLOCK_LEN_ERROR, ['BLOCK_LEN_ERROR']])
+ self.putf(11, 11, [Ann.R_STATUS_ERASE_SEQ_ERROR, ['ERASE_SEQ_ERROR']])
+ self.putf(12, 12, [Ann.R_STATUS_ERASE_PARAM, ['ERASE_PARAM']])
+ self.putf(13, 13, [Ann.R_STATUS_WP_VIOLATION, ['WP_VIOLATION']])
+ self.putf(14, 14, [Ann.R_STATUS_CARD_IS_LOCKED, ['CARD_IS_LOCKED']])
+ self.putf(15, 15, [Ann.R_STATUS_LOCK_UNLOCK_FAILED, ['LOCK_UNLOCK_FAILED']])
+ self.putf(16, 16, [Ann.R_STATUS_COM_CRC_ERROR, ['COM_CRC_ERROR']])
+ self.putf(17, 17, [Ann.R_STATUS_ILLEGAL_COMMAND, ['ILLEGAL_COMMAND']])
+ self.putf(18, 18, [Ann.R_STATUS_CARD_ECC_FAILED, ['CARD_ECC_FAILED']])
+ self.putf(19, 19, [Ann.R_STATUS_CC_ERROR, ['CC_ERROR']])
+ self.putf(20, 20, [Ann.R_STATUS_ERROR, ['ERROR']])
+ self.putf(21, 21, [Ann.R_STATUS_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(22, 22, [Ann.R_STATUS_RSVD_DEFERRED_RESPONSE, ['Reserved for DEFERRED_RESPONSE', 'RSVD_DEFERRED_RESPONSE']])
+ self.putf(23, 23, [Ann.R_STATUS_CSD_OVERWRITE, ['CSD_OVERWRITE']])
+ self.putf(24, 24, [Ann.R_STATUS_WP_ERASE_SKIP, ['WP_ERASE_SKIP']])
+ self.putf(25, 25, [Ann.R_STATUS_CARD_ECC_DISABLED, ['CARD_ECC_DISABLED']])
+ self.putf(26, 26, [Ann.R_STATUS_ERASE_RESET, ['ERASE_RESET']])
+ self.putf(27, 30, [Ann.R_STATUS_CURRENT_STATE, ['CURRENT_STATE']])
+ self.putf(31, 31, [Ann.R_STATUS_READY_FOR_DATA, ['READY_FOR_DATA']])
+ self.putf(32, 32, [Ann.R_STATUS_RSVD, ['RSVD']])
+ self.putf(33, 33, [Ann.R_STATUS_FX_EVENT, ['FX_EVENT']])
+ self.putf(34, 34, [Ann.R_STATUS_APP_CMD, ['APP_CMD']])
+ self.putf(35, 35, [Ann.R_STATUS_RSVD_SDIO, ['Reserved for SDIO card', 'RSVD_SDIO']])
+ self.putf(36, 36, [Ann.R_STATUS_AKE_SEQ_ERROR, ['AKE_SEQ_ERROR']])
+ self.putf(37, 37, [Ann.R_STATUS_RSVD_APP_CMD, ['Reserved for application specific commands', 'RSVD_APP_CMD']])
+ self.putf(38, 39, [Ann.R_STATUS_RSVD_TESTMODE, ['Reserved for manufacturer test mode', 'RSVD_TESTMODE']])
+
+ def handle_reg_cid(self):
+ self.putf(8, 15, [Ann.R_CID_MID, ['Manufacturer ID', 'MID']])
+ self.putf(16, 31, [Ann.R_CID_OID, ['OEM/application ID', 'OID']])
+ self.putf(32, 71, [Ann.R_CID_PNM, ['Product name', 'PNM']])
+ self.putf(72, 79, [Ann.R_CID_PRV, ['Product revision', 'PRV']])
+ self.putf(80, 111, [Ann.R_CID_PSN, ['Product serial number', 'PSN']])
+ self.putf(112, 115, [Ann.R_CID_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(116, 127, [Ann.R_CID_MDT, ['Manufacturing date', 'MDT']])
+ self.putf(128, 134, [Ann.R_CID_CRC, ['CRC7 checksum', 'CRC']])
+ self.putf(135, 135, [Ann.R_CID_ONE, ['Always 1', '1']])
+
+ def handle_reg_csd(self):
+ self.putf(8, 9, [Ann.R_CSD_CSD_STRUCTURE, ['CSD structure', 'CSD_STRUCTURE']])
+ self.putf(10, 15, [Ann.R_CSD_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(16, 23, [Ann.R_CSD_TAAC, ['Data read access-time - 1', 'TAAC']])
+ self.putf(24, 31, [Ann.R_CSD_NSAC, ['Data read access-time - 2 in CLK cycles (NSAC * 100)', 'NSAC']])
+ self.putf(32, 39, [Ann.R_CSD_TRAN_SPEED, ['Max. data transfer rate', 'TRAN_SPEED']])
+ self.putf(40, 51, [Ann.R_CSD_CCC, ['Card command classes', 'CCC']])
+ self.putf(52, 55, [Ann.R_CSD_READ_BL_LEN, ['Max. read data block length', 'READ_BL_LEN']])
+ self.putf(56, 56, [Ann.R_CSD_READ_BL_PARTIAL, ['Partial blocks for read allowed', 'READ_BL_PARTIAL']])
+ self.putf(57, 57, [Ann.R_CSD_WRITE_BLK_MISALIGN, ['Write block misalignment', 'WRITE_BLK_MISALIGN']])
+ self.putf(58, 58, [Ann.R_CSD_READ_BLK_MISALIGN, ['Read block misalignment', 'READ_BLK_MISALIGN']])
+ self.putf(59, 59, [Ann.R_CSD_DSR_IMP, ['DSR implemented', 'DSR_IMP']])
+ self.putf(60, 61, [Ann.R_CSD_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(62, 73, [Ann.R_CSD_C_SIZE, ['Device size', 'C_SIZE']])
+ self.putf(74, 76, [Ann.R_CSD_VDD_R_CURR_MIN, ['Max. read current @VDD min', 'VDD_R_CURR_MIN']])
+ self.putf(77, 79, [Ann.R_CSD_VDD_R_CURR_MAX, ['Max. read current @VDD max', 'VDD_R_CURR_MAX']])
+ self.putf(80, 82, [Ann.R_CSD_VDD_W_CURR_MIN, ['Max. write current @VDD min', 'VDD_W_CURR_MIN']])
+ self.putf(83, 85, [Ann.R_CSD_VDD_W_CURR_MAX, ['Max. write current @VDD max', 'VDD_W_CURR_MAX']])
+ self.putf(86, 88, [Ann.R_CSD_C_SIZE_MULT, ['Device size multiplier', 'C_SIZE_MULT']])
+ self.putf(89, 89, [Ann.R_CSD_ERASE_BLK_EN, ['Erase single block enable', 'ERASE_BLK_EN']])
+ self.putf(90, 96, [Ann.R_CSD_SECTOR_SIZE, ['Erase sector size', 'SECTOR_SIZE']])
+ self.putf(97, 103, [Ann.R_CSD_WP_GRP_SIZE, ['Write protect group size', 'WP_GRP_SIZE']])
+ self.putf(104, 104, [Ann.R_CSD_WP_GRP_ENABLE, ['Write protect group enable', 'WP_GRP_ENABLE']])
+ self.putf(105, 106, [Ann.R_CSD_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(107, 109, [Ann.R_CSD_R2W_FACTOR, ['Write speed factor', 'R2W_FACTOR']])
+ self.putf(110, 113, [Ann.R_CSD_WRITE_BL_LEN, ['Max. write data block length', 'WRITE_BL_LEN']])
+ self.putf(114, 114, [Ann.R_CSD_WRITE_BL_PARTIAL, ['Partial blocks for write allowed', 'WRITE_BL_PARTIAL']])
+ self.putf(115, 119, [Ann.R_CSD_RSVD, ['Reserved', 'RSVD']])
+ self.putf(120, 120, [Ann.R_CSD_FILE_FORMAT_GRP, ['File format group', 'FILE_FORMAT_GRP']])
+ self.putf(121, 121, [Ann.R_CSD_COPY, ['Copy flag', 'COPY']])
+ self.putf(122, 122, [Ann.R_CSD_PERM_WRITE_PROTECT, ['Permanent write protection', 'PERM_WRITE_PROTECT']])
+ self.putf(123, 123, [Ann.R_CSD_TMP_WRITE_PROTECT, ['Temporary write protection', 'TMP_WRITE_PROTECT']])
+ self.putf(124, 125, [Ann.R_CSD_FILE_FORMAT, ['File format', 'FILE_FORMAT']])
+ self.putf(126, 127, [Ann.R_CSD_RSVD, ['Reserved', 'RSVD', 'R']])
+ self.putf(128, 134, [Ann.R_CSD_CRC, ['CRC', 'CRC', 'C']])
+ self.putf(135, 135, [Ann.R_CSD_ONE, ['Always 1', '1']])
# Response tokens can have one of four formats (depends on content).
# They can have a total length of 48 or 136 bits.
# They're sent serially (MSB-first) by the card that the host
# addressed previously, or (synchronously) by all connected cards.
- def handle_response_r1(self, cmd):
+ def handle_response_r1(self, cmd_pin):
# R1: Normal response command
# - Bits[47:47]: Start bit (always 0)
# - Bits[46:46]: Transmission bit (0 == card)
# - Bits[39:08]: Card status
# - Bits[07:01]: CRC7
# - Bits[00:00]: End bit (always 1)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
self.handle_common_token_fields()
- self.putr('R1')
- self.puta(0, 31, [136, ['Card status', 'Status', 'S']])
- for i in range(32):
- self.putbit(8 + i, [card_status[31 - i]])
- self.token, self.state = [], 'GET COMMAND TOKEN'
+ self.putr(Ann.RESPONSE_R1)
+ self.puta(0, 31, [Ann.DECODED_F, ['Card status', 'Status', 'S']])
+ self.handle_reg_status()
+
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
- def handle_response_r1b(self, cmd):
+ def handle_response_r1b(self, cmd_pin):
# R1b: Same as R1 with an optional busy signal (on the data line)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
self.handle_common_token_fields()
- self.puta(0, 31, [136, ['Card status', 'Status', 'S']])
- self.putr('R1b')
- self.token, self.state = [], 'GET COMMAND TOKEN'
+ self.puta(0, 31, [Ann.DECODED_F, ['Card status', 'Status', 'S']])
+ self.putr(Ann.RESPONSE_R1B)
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
- def handle_response_r2(self, cmd):
+ def handle_response_r2(self, cmd_pin):
# R2: CID/CSD register
# - Bits[135:135]: Start bit (always 0)
# - Bits[134:134]: Transmission bit (0 == card)
# - Bits[133:128]: Reserved (always 0b111111)
# - Bits[127:001]: CID or CSD register including internal CRC7
# - Bits[000:000]: End bit (always 1)
- if not self.get_token_bits(cmd, 136):
+ if not self.get_token_bits(cmd_pin, 136):
return
# Annotations for each individual bit.
for bit in range(len(self.token)):
- self.putf(bit, bit, [128, ['%d' % self.token[bit][2]]])
- self.putf(0, 0, [129, ['Start bit', 'Start', 'S']])
- t = 'host' if self.token[1][2] == 1 else 'card'
- self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']])
- self.putf(2, 7, [131, ['Reserved', 'Res', 'R']])
- self.putf(8, 134, [132, ['Argument', 'Arg', 'A']])
- self.putf(135, 135, [134, ['End bit', 'End', 'E']])
- self.putf(8, 134, [136, ['CID/CSD register', 'CID/CSD', 'C']])
- self.putf(0, 135, [55, ['R2']])
- self.token, self.state = [], 'GET COMMAND TOKEN'
-
- def handle_response_r3(self, cmd):
+ self.putf(bit, bit, [Ann.BIT_0 + self.token[bit].bit, ['%d' % self.token[bit].bit]])
+ self.putf(0, 0, [Ann.F_START, ['Start bit', 'Start', 'S']])
+ t = 'host' if self.token[1].bit == 1 else 'card'
+ self.putf(1, 1, [Ann.F_TRANSMISSION, ['Transmission: ' + t, 'T: ' + t, 'T']])
+ self.putf(2, 7, [Ann.F_CMD, ['Reserved', 'Res', 'R']])
+ self.putf(8, 134, [Ann.F_ARG, ['Argument', 'Arg', 'A']])
+ self.putf(135, 135, [Ann.F_END, ['End bit', 'End', 'E']])
+ self.putf(8, 134, [Ann.DECODED_F, ['CID/CSD register', 'CID/CSD', 'C']])
+ self.putf(0, 135, [Ann.RESPONSE_R2, ['Response: R2']])
+
+ if self.last_cmd in (Ann.CMD2, Ann.CMD10):
+ self.handle_reg_cid()
+
+ if self.last_cmd == Ann.CMD9:
+ self.handle_reg_csd()
+
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
+
+ def handle_response_r3(self, cmd_pin):
# R3: OCR register
# - Bits[47:47]: Start bit (always 0)
# - Bits[46:46]: Transmission bit (0 == card)
# - Bits[39:08]: OCR register
# - Bits[07:01]: Reserved (always 0b111111)
# - Bits[00:00]: End bit (always 1)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
- self.putr('R3')
+ self.putr(Ann.RESPONSE_R3)
# Annotations for each individual bit.
for bit in range(len(self.token)):
- self.putf(bit, bit, [128, ['%d' % self.token[bit][2]]])
- self.putf(0, 0, [129, ['Start bit', 'Start', 'S']])
- t = 'host' if self.token[1][2] == 1 else 'card'
- self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']])
- self.putf(2, 7, [131, ['Reserved', 'Res', 'R']])
- self.putf(8, 39, [132, ['Argument', 'Arg', 'A']])
- self.putf(40, 46, [133, ['Reserved', 'Res', 'R']])
- self.putf(47, 47, [134, ['End bit', 'End', 'E']])
- self.puta(0, 31, [136, ['OCR register', 'OCR reg', 'OCR', 'O']])
- self.token, self.state = [], 'GET COMMAND TOKEN'
-
- def handle_response_r6(self, cmd):
+ self.putf(bit, bit, [Ann.BIT_0 + self.token[bit].bit, ['%d' % self.token[bit].bit]])
+ self.putf(0, 0, [Ann.F_START, ['Start bit', 'Start', 'S']])
+ t = 'host' if self.token[1].bit == 1 else 'card'
+ self.putf(1, 1, [Ann.F_TRANSMISSION, ['Transmission: ' + t, 'T: ' + t, 'T']])
+ self.putf(2, 7, [Ann.F_CMD, ['Reserved', 'Res', 'R']])
+ self.putf(8, 39, [Ann.F_ARG, ['Argument', 'Arg', 'A']])
+ self.putf(40, 46, [Ann.F_CRC, ['Reserved', 'Res', 'R']])
+ self.putf(47, 47, [Ann.F_END, ['End bit', 'End', 'E']])
+ self.puta(0, 31, [Ann.DECODED_F, ['OCR register', 'OCR reg', 'OCR', 'O']])
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
+
+ def handle_response_r6(self, cmd_pin):
# R6: Published RCA response
# - Bits[47:47]: Start bit (always 0)
# - Bits[46:46]: Transmission bit (0 == card)
# - Bits[23:08]: Argument[15:0]: Card status bits
# - Bits[07:01]: CRC7
# - Bits[00:00]: End bit (always 1)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
self.handle_common_token_fields()
- self.puta(0, 15, [136, ['Card status bits', 'Status', 'S']])
- self.puta(16, 31, [136, ['Relative card address', 'RCA', 'R']])
- self.putr('R6')
- self.token, self.state = [], 'GET COMMAND TOKEN'
+ self.puta(0, 15, [Ann.DECODED_F, ['Card status bits', 'Status', 'S']])
+ self.puta(16, 31, [Ann.DECODED_F, ['Relative card address', 'RCA', 'R']])
+ self.putr(Ann.RESPONSE_R6)
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
- def handle_response_r7(self, cmd):
+ def handle_response_r7(self, cmd_pin):
# R7: Card interface condition
# - Bits[47:47]: Start bit (always 0)
# - Bits[46:46]: Transmission bit (0 == card)
# - Bits[15:08]: Echo-back of check pattern
# - Bits[07:01]: CRC7
# - Bits[00:00]: End bit (always 1)
- if not self.get_token_bits(cmd, 48):
+ if not self.get_token_bits(cmd_pin, 48):
return
self.handle_common_token_fields()
- self.putr('R7')
+ self.putr(Ann.RESPONSE_R7)
# Arg[31:12]: Reserved bits (all-zero)
- self.puta(12, 31, [136, ['Reserved', 'Res', 'R']])
+ self.puta(12, 31, [Ann.DECODED_F, ['Reserved', 'Res', 'R']])
# Arg[11:08]: Voltage accepted
- v = ''.join(str(i[2]) for i in self.token[28:32])
+ v = ''.join(str(i.bit) for i in self.token[28:32])
av = accepted_voltages.get(int('0b' + v, 2), 'Unknown')
- self.puta(8, 11, [136, ['Voltage accepted: ' + av, 'Voltage', 'Volt', 'V']])
+ self.puta(8, 11, [Ann.DECODED_F,
+ ['Voltage accepted: ' + av, 'Voltage', 'Volt', 'V']])
# Arg[07:00]: Echo-back of check pattern
- self.puta(0, 7, [136, ['Echo-back of check pattern', 'Echo', 'E']])
+ self.puta(0, 7, [Ann.DECODED_F,
+ ['Echo-back of check pattern', 'Echo', 'E']])
- self.token, self.state = [], 'GET COMMAND TOKEN'
+ self.token, self.state = [], St.GET_COMMAND_TOKEN
def decode(self):
while True:
# Wait for a rising CLK edge.
- (cmd, clk, dat0, dat1, dat2, dat3) = self.wait({1: 'r'})
+ (cmd_pin, clk, dat0, dat1, dat2, dat3) = self.wait({Pin.CLK: 'r'})
# State machine.
- if self.state == 'GET COMMAND TOKEN':
+ if self.state == St.GET_COMMAND_TOKEN:
if len(self.token) == 0:
# Wait for start bit (CMD = 0).
- if cmd != 0:
+ if cmd_pin != 0:
continue
- self.get_command_token(cmd)
- elif self.state.startswith('HANDLE CMD'):
+ self.get_command_token(cmd_pin)
+ elif self.state.value.startswith('HANDLE_CMD'):
# Call the respective handler method for the command.
- a, cmdstr = 'a' if self.is_acmd else '', self.state[10:].lower()
+ a, cmdstr = 'a' if self.is_acmd else '', self.state.value[10:].lower()
handle_cmd = getattr(self, 'handle_%scmd%s' % (a, cmdstr))
handle_cmd()
# Leave ACMD mode again after the first command after CMD55.
if self.is_acmd and cmdstr not in ('55', '63'):
self.is_acmd = False
- elif self.state.startswith('GET RESPONSE'):
+ elif self.state.value.startswith('GET_RESPONSE'):
if len(self.token) == 0:
# Wait for start bit (CMD = 0).
- if cmd != 0:
+ if cmd_pin != 0:
continue
# Call the respective handler method for the response.
- s = 'handle_response_%s' % self.state[13:].lower()
+ s = 'handle_response_%s' % self.state.value[13:].lower()
handle_response = getattr(self, s)
- handle_response(cmd)
+ handle_response(cmd_pin)
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2012-2020 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
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
from common.sdcard import (cmd_names, acmd_names)
+responses = '1 1b 2 3 7'.split()
+
+a = ['CMD%d' % i for i in range(64)] + ['ACMD%d' % i for i in range(64)] + \
+ ['R' + r.upper() for r in responses] + ['BIT', 'BIT_WARNING']
+Ann = SrdIntEnum.from_list('Ann', a)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'sdcard_spi'
tags = ['Memory']
annotations = \
tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \
- tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \
- ('r1', 'R1 reply'),
- ('r1b', 'R1B reply'),
- ('r2', 'R2 reply'),
- ('r3', 'R3 reply'),
- ('r7', 'R7 reply'),
- ('bits', 'Bits'),
- ('bit-warnings', 'Bit warnings'),
+ tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + \
+ tuple(('r%s' % r, 'R%s response' % r) for r in responses) + ( \
+ ('bit', 'Bit'),
+ ('bit-warning', 'Bit warning'),
)
annotation_rows = (
- ('bits', 'Bits', (133, 134)),
- ('cmd-reply', 'Commands/replies', tuple(range(133))),
+ ('bits', 'Bits', (Ann.BIT, Ann.BIT_WARNING)),
+ ('commands-replies', 'Commands/replies', Ann.prefixes('CMD ACMD R')),
)
def __init__(self):
# Bits[47:47]: Start bit (always 0)
bit, self.ss_bit, self.es_bit = tb(5, 7)[0], tb(5, 7)[1], tb(5, 7)[2]
if bit == 0:
- self.putb([134, ['Start bit: %d' % bit]])
+ self.putb([Ann.BIT, ['Start bit: %d' % bit]])
else:
- self.putb([135, ['Start bit: %s (Warning: Must be 0!)' % bit]])
+ self.putb([Ann.BIT_WARNING, ['Start bit: %s (Warning: Must be 0!)' % bit]])
# Bits[46:46]: Transmitter bit (1 == host)
bit, self.ss_bit, self.es_bit = tb(5, 6)[0], tb(5, 6)[1], tb(5, 6)[2]
if bit == 1:
- self.putb([134, ['Transmitter bit: %d' % bit]])
+ self.putb([Ann.BIT, ['Transmitter bit: %d' % bit]])
else:
- self.putb([135, ['Transmitter bit: %d (Warning: Must be 1!)' % bit]])
+ self.putb([Ann.BIT_WARNING, ['Transmitter bit: %d (Warning: Must be 1!)' % bit]])
# Bits[45:40]: Command index (BCD; valid: 0-63)
cmd = self.cmd_index = t[0] & 0x3f
self.ss_bit, self.es_bit = tb(5, 5)[1], tb(5, 0)[2]
- self.putb([134, ['Command: %s%d (%s)' % (s, cmd, self.cmd_name(cmd))]])
+ self.putb([Ann.BIT, ['Command: %s%d (%s)' % (s, cmd, self.cmd_name(cmd))]])
# Bits[39:8]: Argument
self.arg = (t[1] << 24) | (t[2] << 16) | (t[3] << 8) | t[4]
self.ss_bit, self.es_bit = tb(4, 7)[1], tb(1, 0)[2]
- self.putb([134, ['Argument: 0x%04x' % self.arg]])
+ self.putb([Ann.BIT, ['Argument: 0x%04x' % self.arg]])
# Bits[7:1]: CRC7
# TODO: Check CRC7.
crc = t[5] >> 1
self.ss_bit, self.es_bit = tb(0, 7)[1], tb(0, 1)[2]
- self.putb([134, ['CRC7: 0x%01x' % crc]])
+ self.putb([Ann.BIT, ['CRC7: 0x%01x' % crc]])
# Bits[0:0]: End bit (always 1)
bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
if bit == 1:
- self.putb([134, ['End bit: %d' % bit]])
+ self.putb([Ann.BIT, ['End bit: %d' % bit]])
else:
- self.putb([135, ['End bit: %d (Warning: Must be 1!)' % bit]])
+ self.putb([Ann.BIT_WARNING, ['End bit: %d (Warning: Must be 1!)' % bit]])
# Handle command.
if cmd in (0, 1, 9, 16, 17, 24, 41, 49, 55, 59):
def handle_cmd0(self):
# CMD0: GO_IDLE_STATE
- self.putc(0, 'Reset the SD card')
+ self.putc(Ann.CMD0, 'Reset the SD card')
self.state = 'GET RESPONSE R1'
def handle_cmd1(self):
# CMD1: SEND_OP_COND
- self.putc(1, 'Send HCS info and activate the card init process')
+ self.putc(Ann.CMD1, 'Send HCS info and activate the card init process')
hcs = (self.arg & (1 << 30)) >> 30
self.ss_bit = self.cmd_token_bits[5 - 4][6][1]
self.es_bit = self.cmd_token_bits[5 - 4][6][2]
- self.putb([134, ['HCS: %d' % hcs]])
+ self.putb([Ann.BIT, ['HCS: %d' % hcs]])
self.state = 'GET RESPONSE R1'
def handle_cmd9(self):
# CMD9: SEND_CSD (128 bits / 16 bytes)
- self.putc(9, 'Ask card to send its card specific data (CSD)')
+ self.putc(Ann.CMD9, 'Ask card to send its card specific data (CSD)')
if len(self.read_buf) == 0:
self.ss_cmd = self.ss
self.read_buf.append(self.miso)
return
self.es_cmd = self.es
self.read_buf = self.read_buf[4:] # TODO: Document or redo.
- self.putx([9, ['CSD: %s' % self.read_buf]])
+ self.putx([Ann.CMD9, ['CSD: %s' % self.read_buf]])
# TODO: Decode all bits.
self.read_buf = []
### self.state = 'GET RESPONSE R1'
def handle_cmd10(self):
# CMD10: SEND_CID (128 bits / 16 bytes)
- self.putc(10, 'Ask card to send its card identification (CID)')
+ self.putc(Ann.CMD10, 'Ask card to send its card identification (CID)')
self.read_buf.append(self.miso)
if len(self.read_buf) < 16:
return
- self.putx([10, ['CID: %s' % self.read_buf]])
+ self.putx([Ann.CMD10, ['CID: %s' % self.read_buf]])
# TODO: Decode all bits.
self.read_buf = []
self.state = 'GET RESPONSE R1'
# CMD16: SET_BLOCKLEN
self.blocklen = self.arg
# TODO: Sanity check on block length.
- self.putc(16, 'Set the block length to %d bytes' % self.blocklen)
+ self.putc(Ann.CMD16, 'Set the block length to %d bytes' % self.blocklen)
self.state = 'GET RESPONSE R1'
def handle_cmd17(self):
# CMD17: READ_SINGLE_BLOCK
- self.putc(17, 'Read a block from address 0x%04x' % self.arg)
+ self.putc(Ann.CMD17, 'Read a block from address 0x%04x' % self.arg)
self.is_cmd17 = True
self.state = 'GET RESPONSE R1'
def handle_cmd24(self):
# CMD24: WRITE_BLOCK
- self.putc(24, 'Write a block to address 0x%04x' % self.arg)
+ self.putc(Ann.CMD24, 'Write a block to address 0x%04x' % self.arg)
self.is_cmd24 = True
self.state = 'GET RESPONSE R1'
def handle_cmd55(self):
# CMD55: APP_CMD
- self.putc(55, 'Next command is an application-specific command')
+ self.putc(Ann.CMD55, 'Next command is an application-specific command')
self.is_acmd = True
self.state = 'GET RESPONSE R1'
# CMD59: CRC_ON_OFF
crc_on_off = self.arg & (1 << 0)
s = 'on' if crc_on_off == 1 else 'off'
- self.putc(59, 'Turn the SD card CRC option %s' % s)
+ self.putc(Ann.CMD59, 'Turn the SD card CRC option %s' % s)
self.state = 'GET RESPONSE R1'
def handle_acmd41(self):
# ACMD41: SD_SEND_OP_COND
- self.putc(64 + 41, 'Send HCS info and activate the card init process')
+ self.putc(Ann.ACMD41, 'Send HCS info and activate the card init process')
self.state = 'GET RESPONSE R1'
def handle_cmd999(self):
# Sent by the card after every command except for SEND_STATUS.
self.ss_cmd, self.es_cmd = self.miso_bits[7][1], self.miso_bits[0][2]
- self.putx([65, ['R1: 0x%02x' % res]])
+ self.putx([Ann.R1, ['R1: 0x%02x' % res]])
def putbit(bit, data):
b = self.miso_bits[bit]
self.ss_bit, self.es_bit = b[1], b[2]
- self.putb([134, data])
+ self.putb([Ann.BIT, data])
# Bit 0: 'In idle state' bit
s = '' if (res & (1 << 0)) else 'not '
return
if len(self.read_buf) == self.blocklen:
self.es_data = self.es
- self.put(self.ss_data, self.es_data, self.out_ann, [17, ['Block data: %s' % self.read_buf]])
+ self.put(self.ss_data, self.es_data, self.out_ann, [Ann.CMD17, ['Block data: %s' % self.read_buf]])
elif len(self.read_buf) == (self.blocklen + 1):
self.ss_crc = self.ss
elif len(self.read_buf) == (self.blocklen + 2):
self.es_crc = self.es
# TODO: Check CRC.
- self.put(self.ss_crc, self.es_crc, self.out_ann, [17, ['CRC']])
+ self.put(self.ss_crc, self.es_crc, self.out_ann, [Ann.CMD17, ['CRC']])
self.state = 'IDLE'
elif miso == 0xfe:
- self.put(self.ss, self.es, self.out_ann, [17, ['Start Block']])
+ self.put(self.ss, self.es, self.out_ann, [Ann.CMD17, ['Start Block']])
self.cmd17_start_token_found = True
def handle_data_cmd24(self, mosi):
if len(self.read_buf) < self.blocklen:
return
self.es_data = self.es
- self.put(self.ss_data, self.es_data, self.out_ann, [24, ['Block data: %s' % self.read_buf]])
+ self.put(self.ss_data, self.es_data, self.out_ann, [Ann.CMD24, ['Block data: %s' % self.read_buf]])
self.read_buf = []
self.state = 'DATA RESPONSE'
elif mosi == 0xfe:
- self.put(self.ss, self.es, self.out_ann, [24, ['Start Block']])
+ self.put(self.ss, self.es, self.out_ann, [Ann.CMD24, ['Start Block']])
self.cmd24_start_token_found = True
def handle_data_response(self, miso):
# Should we return to IDLE here?
return
m = self.miso_bits
- self.put(m[7][1], m[5][2], self.out_ann, [134, ['Don\'t care']])
- self.put(m[4][1], m[4][2], self.out_ann, [134, ['Always 0']])
+ self.put(m[7][1], m[5][2], self.out_ann, [Ann.BIT, ['Don\'t care']])
+ self.put(m[4][1], m[4][2], self.out_ann, [Ann.BIT, ['Always 0']])
if miso == 0x05:
- self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data accepted']])
+ self.put(m[3][1], m[1][2], self.out_ann, [Ann.BIT, ['Data accepted']])
elif miso == 0x0b:
- self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (CRC error)']])
+ self.put(m[3][1], m[1][2], self.out_ann, [Ann.BIT, ['Data rejected (CRC error)']])
elif miso == 0x0d:
- self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (write error)']])
- self.put(m[0][1], m[0][2], self.out_ann, [134, ['Always 1']])
- ann_class = None
- if self.is_cmd24:
- ann_class = 24
- if ann_class is not None:
- self.put(self.ss, self.es, self.out_ann, [ann_class, ['Data Response']])
+ self.put(m[3][1], m[1][2], self.out_ann, [Ann.BIT, ['Data rejected (write error)']])
+ self.put(m[0][1], m[0][2], self.out_ann, [Ann.BIT, ['Always 1']])
+ cls = Ann.CMD24 if self.is_cmd24 else None
+ if cls is not None:
+ self.put(self.ss, self.es, self.out_ann, [cls, ['Data Response']])
if self.is_cmd24:
# We just send a block of data to be written to the card,
# this takes some time.
def wait_while_busy(self, miso):
if miso != 0x00:
- ann_class = None
- if self.is_cmd24:
- ann_class = 24
- if ann_class is not None:
- self.put(self.ss_busy, self.es_busy, self.out_ann, [24, ['Card is busy']])
+ cls = Ann.CMD24 if self.is_cmd24 else None
+ if cls is not None:
+ self.put(self.ss_busy, self.es_busy, self.out_ann, [cls, ['Card is busy']])
self.state = 'IDLE'
return
else:
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Philip Ã…kesson <philip.akesson@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+The SDQ protocol was developed by Texas Instruments, and is used in
+devices like battery pack authentication. Apple uses SDQ in MagSafe
+and Lightning connectors, as well as some batteries.
+
+See https://www.ti.com/lit/ds/symlink/bq26100.pdf for details.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Philip Ã…kesson <philip.akesson@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from common.srdhelper import bitpack
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class Pin:
+ SDQ, = range(1)
+
+class Ann:
+ BIT, BYTE, BREAK, = range(3)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sdq'
+ name = 'SDQ'
+ longname = 'Texas Instruments SDQ'
+ desc = 'Texas Instruments SDQ. The SDQ protocol is also used by Apple.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Embedded/industrial']
+ channels = (
+ {'id': 'sdq', 'name': 'SDQ', 'desc': 'Single wire SDQ data line.'},
+ )
+ options = (
+ {'id': 'bitrate', 'desc': 'Bit rate', 'default': 98425},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('byte', 'Byte'),
+ ('break', 'Break'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (Ann.BIT,)),
+ ('bytes', 'Bytes', (Ann.BYTE,)),
+ ('breaks', 'Breaks', (Ann.BREAK,)),
+ )
+
+ def puts(self, data):
+ self.put(self.startsample, self.samplenum, self.out_ann, data)
+
+ def putetu(self, data):
+ self.put(self.startsample, self.startsample + int(self.bit_width), self.out_ann, data)
+
+ def putbetu(self, data):
+ self.put(self.bytepos, self.startsample + int(self.bit_width), self.out_ann, data)
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.startsample = 0
+ self.bits = []
+ self.bytepos = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def handle_bit(self, bit):
+ self.bits.append(bit)
+ self.putetu([Ann.BIT, [
+ 'Bit: {:d}'.format(bit),
+ '{:d}'.format(bit),
+ ]])
+
+ if len(self.bits) == 8:
+ byte = bitpack(self.bits)
+ self.putbetu([Ann.BYTE, [
+ 'Byte: 0x{:02x}'.format(byte),
+ '0x{:02x}'.format(byte),
+ ]])
+ self.bits = []
+ self.bytepos = 0
+
+ def handle_break(self):
+ self.puts([Ann.BREAK, ['Break', 'BR']])
+ self.bits = []
+ self.startsample = self.samplenum
+ self.bytepos = 0
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
+ self.half_bit_width = self.bit_width / 2.0
+ # BREAK if the line is low for longer than this.
+ break_threshold = self.bit_width * 1.2
+
+ # Wait until the line is high before inspecting input data.
+ sdq, = self.wait({Pin.SDQ: 'h'})
+ while True:
+ # Get the length of a low pulse (falling to rising edge).
+ sdq, = self.wait({Pin.SDQ: 'f'})
+ self.startsample = self.samplenum
+ if self.bytepos == 0:
+ self.bytepos = self.samplenum
+ sdq, = self.wait({Pin.SDQ: 'r'})
+
+ # Check for 0 or 1 data bits, or the BREAK symbol.
+ delta = self.samplenum - self.startsample
+ if delta > break_threshold:
+ self.handle_break()
+ elif delta > self.half_bit_width:
+ self.handle_bit(0)
+ else:
+ self.handle_bit(1)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder decodes the output of a 7-segment display.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+ pass
+
+# This table is sorted by ASCII code numbers, with the exception
+# of letters having their upper/lower case ignored.
+#
+# Traditional LED segment names and layout:
+#
+# A
+# F B
+# G
+# E C
+# D
+#
+# A B C D E F G
+digits = {
+ (0, 0, 0, 0, 0, 0, 0): ' ',
+ (0, 1, 0, 0, 0, 1, 0): '"',
+ (1, 1, 0, 1, 1, 1, 1): "&",
+ (0, 0, 0, 0, 0, 1, 0): "'",
+ (0, 1, 0, 0, 0, 0, 0): "'",
+ (0, 0, 1, 1, 0, 0, 0): ',',
+ (0, 0, 0, 0, 0, 0, 1): '-',
+ (0, 0, 0, 0, 1, 0, 0): '.',
+ (1, 1, 1, 1, 1, 1, 0): '0',
+ (0, 1, 1, 0, 0, 0, 0): '1',
+ (1, 1, 0, 1, 1, 0, 1): '2',
+ (1, 1, 1, 1, 0, 0, 1): '3',
+ (0, 1, 1, 0, 0, 1, 1): '4',
+ (1, 0, 1, 1, 0, 1, 1): '5',
+ (1, 0, 1, 1, 1, 1, 1): '6',
+ (1, 1, 1, 0, 0, 1, 0): '7',
+ (1, 1, 1, 0, 0, 0, 0): '7',
+ (1, 1, 1, 1, 1, 1, 1): '8',
+ (1, 1, 1, 1, 0, 1, 1): '9',
+ (1, 0, 0, 0, 0, 0, 1): '=',
+ (0, 0, 0, 1, 0, 0, 1): '=',
+ (1, 1, 0, 0, 1, 0, 1): '?',
+ (1, 1, 1, 0, 1, 1, 1): 'A',
+ (1, 1, 1, 1, 1, 0, 1): 'a',
+ (0, 0, 1, 1, 1, 1, 1): 'b',
+ (1, 0, 0, 1, 1, 1, 0): 'C',
+ (0, 0, 0, 1, 1, 0, 1): 'c',
+ (0, 1, 1, 1, 1, 0, 1): 'd',
+ (1, 0, 0, 1, 1, 1, 1): 'E',
+ (1, 0, 0, 0, 1, 1, 1): 'F',
+ (1, 0, 1, 1, 1, 1, 0): 'G',
+ (0, 1, 1, 0, 1, 1, 1): 'H',
+ (0, 0, 1, 0, 1, 1, 1): 'h',
+ (0, 0, 0, 0, 1, 1, 0): 'I',
+ (1, 0, 0, 0, 1, 0, 0): 'i',
+ (0, 0, 1, 0, 0, 0, 0): 'i',
+ (0, 1, 1, 1, 1, 0, 0): 'J',
+ (0, 1, 1, 1, 0, 0, 0): 'J',
+ (1, 0, 1, 1, 0, 0, 0): 'j',
+ (1, 0, 1, 0, 1, 1, 1): 'K',
+ (0, 0 ,0, 1, 1, 1, 0): 'L',
+ (1, 0, 1, 0, 1, 0, 0): 'M',
+ (1, 0, 1, 0, 1, 0, 1): 'M',
+ (1, 1, 1, 0, 1, 1, 0): 'N',
+ (0, 0, 1, 0, 1, 0, 1): 'n',
+ (0, 0, 1, 1, 1, 0, 1): 'o',
+ (1, 1, 0, 0, 1, 1, 1): 'p',
+ (1, 1, 1, 0, 0, 1, 1): 'q',
+ (1, 1, 0, 0, 1, 1, 0): 'R',
+ (0, 0, 0, 0, 1, 0, 1): 'r',
+ (0, 0, 0, 1, 1, 1, 1): 't',
+ (0, 0, 1, 1, 1, 0, 0): 'u',
+ (0, 1, 0, 1, 0, 1, 0): 'V',
+ (0, 1, 0, 0, 1, 1, 1): 'V',
+ (0, 1, 1, 1, 1, 1, 0): 'V',
+ (0, 1, 0, 0, 0, 1, 1): 'v',
+ (0, 1, 0, 1, 0, 1, 1): 'W',
+ (0, 0, 1, 0, 1, 0, 0): 'x',
+ (0, 1, 1, 1, 0, 1, 1): 'y',
+ (1, 1, 0, 1, 1, 0, 0): 'Z',
+ (1, 1, 0, 0, 0, 1, 0): '^',
+ (0, 0, 0, 1, 0, 0, 0): '_',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'seven_segment'
+ name = '7-segment'
+ longname = '7-segment display'
+ desc = '7-segment display protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Display']
+ channels = (
+ {'id': 'a', 'name': 'A', 'desc': 'Segment A'},
+ {'id': 'b', 'name': 'B', 'desc': 'Segment B'},
+ {'id': 'c', 'name': 'C', 'desc': 'Segment C'},
+ {'id': 'd', 'name': 'D', 'desc': 'Segment D'},
+ {'id': 'e', 'name': 'E', 'desc': 'Segment E'},
+ {'id': 'f', 'name': 'F', 'desc': 'Segment F'},
+ {'id': 'g', 'name': 'G', 'desc': 'Segment G'},
+ )
+ optional_channels = (
+ {'id': 'dp', 'name': 'DP', 'desc': 'Decimal point'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Expected polarity',
+ 'default': 'common-cathode', 'values': ('common-cathode', 'common-anode')},
+ {'id': 'show_unknown', 'desc': 'Display Unknown characters as #',
+ 'default': 'no', 'values': ('yes', 'no')},
+ )
+ annotations = (
+ ('decoded-digit', 'Decoded digit'),
+ )
+ annotation_rows = (
+ ('decoded-digits', 'Decoded digits', (0,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, ss_block, es_block, data):
+ self.put(ss_block, es_block, self.out_ann, data)
+
+ def pins_to_hex(self, pins):
+ return digits.get(pins, None)
+
+ def decode(self):
+ oldpins = self.wait()
+ lastpos = self.samplenum
+
+ # Check mandatory and optional decoder input signals.
+ if False in [p in (0, 1) for p in oldpins[:7]]:
+ raise ChannelError('Need at least segments A-G.')
+ self.have_dp = self.has_channel(7)
+ seg_count = 8 if self.have_dp else 7
+
+ conditions = [{i: 'e'} for i in range(seg_count)]
+ while True:
+ # Wait for any change.
+ pins = self.wait(conditions)
+
+ # Invert all data lines if a common anode display is used.
+ if self.options['polarity'] == 'common-anode':
+ oldpins = tuple((1 - state for state in oldpins[:seg_count]))
+
+ # Convert to character string.
+ digit = self.pins_to_hex(oldpins[:7])
+ if digit is None and self.options['show_unknown'] == 'yes':
+ digit = '#'
+
+ # Emit annotation when conversion succeeded.
+ # Optionally present the decimal point when active.
+ if digit is not None:
+ if self.have_dp:
+ dp = oldpins[7]
+ if dp == 1:
+ digit += '.'
+ self.putb(lastpos, self.samplenum, [0, [digit]])
+
+ oldpins = pins
+ lastpos = self.samplenum
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Signature analysis function for troubleshooting logic circuits.
+This generates the same signature as Hewlett-Packard 5004A.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+symbol_map = {
+ 0b0000: '0',
+ 0b1000: '1',
+ 0b0100: '2',
+ 0b1100: '3',
+ 0b0010: '4',
+ 0b1010: '5',
+ 0b0110: '6',
+ 0b1110: '7',
+ 0b0001: '8',
+ 0b1001: '9',
+ 0b0101: 'A',
+ 0b1101: 'C',
+ 0b0011: 'F',
+ 0b1011: 'H',
+ 0b0111: 'P',
+ 0b1111: 'U',
+}
+
+START, STOP, CLOCK, DATA = range(4)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'signature'
+ name = 'Signature'
+ longname = 'Signature analysis'
+ desc = 'Annotate signature of logic patterns.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Debug/trace', 'Util', 'Encoding']
+ channels = (
+ {'id': 'start', 'name': 'START', 'desc': 'START channel'},
+ {'id': 'stop', 'name': 'STOP', 'desc': 'STOP channel'},
+ {'id': 'clk', 'name': 'CLOCK', 'desc': 'CLOCK channel'},
+ {'id': 'data', 'name': 'DATA', 'desc': 'DATA channel'},
+ )
+ options = (
+ {'id': 'start_edge', 'desc': 'START edge polarity',
+ 'default': 'rising', 'values': ('rising', 'falling')},
+ {'id': 'stop_edge', 'desc': 'STOP edge polarity',
+ 'default': 'rising', 'values': ('rising', 'falling')},
+ {'id': 'clk_edge', 'desc': 'CLOCK edge polarity',
+ 'default': 'falling', 'values': ('rising', 'falling')},
+ {'id': 'annbits', 'desc': 'Enable bit level annotations',
+ 'default': 'no', 'values': ('yes', 'no')},
+ )
+ annotations = (
+ ('bit0', 'Bit0'),
+ ('bit1', 'Bit1'),
+ ('start', 'START'),
+ ('stop', 'STOP'),
+ ('signature', 'Signature')
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0, 1, 2, 3)),
+ ('signatures', 'Signatures', (4,))
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putsig(self, ss, es, signature):
+ s = ''.join([symbol_map[(signature >> 0) & 0x0f],
+ symbol_map[(signature >> 4) & 0x0f],
+ symbol_map[(signature >> 8) & 0x0f],
+ symbol_map[(signature >> 12) & 0x0f]])
+ self.put(ss, es, self.out_ann, [4, [s]])
+
+ def putb(self, ss, ann):
+ self.put(ss, self.samplenum, self.out_ann, ann)
+
+ def decode(self):
+ opt = self.options
+ start_edge_mode_rising = opt['start_edge'] == 'rising'
+ stop_edge_mode_rising = opt['stop_edge'] == 'rising'
+ annbits = opt['annbits'] == 'yes'
+ gate_is_open = False
+ sample_start = None
+ started = False
+ last_samplenum = 0
+ prev_start = 0 if start_edge_mode_rising else 1
+ prev_stop = 0 if stop_edge_mode_rising else 1
+ shiftreg = 0
+
+ while True:
+ start, stop, _, data = self.wait({CLOCK: opt['clk_edge']})
+ if start != prev_start and not gate_is_open:
+ gate_is_open = (start == 1) if start_edge_mode_rising else (start == 0)
+ if gate_is_open:
+ # Start sampling.
+ sample_start = self.samplenum
+ started = True
+ elif stop != prev_stop and gate_is_open:
+ gate_is_open = not ((stop == 1) if stop_edge_mode_rising else (stop == 0))
+ if not gate_is_open:
+ # Stop sampling.
+ if annbits:
+ self.putb(last_samplenum, [3, ['STOP', 'STP', 'P']])
+ self.putsig(sample_start, self.samplenum, shiftreg)
+ shiftreg = 0
+ sample_start = None
+ if gate_is_open:
+ if annbits:
+ if started:
+ s = '<{}>'.format(data)
+ self.putb(last_samplenum, [2, ['START' + s, 'STR' + s, 'S' + s]])
+ started = False
+ else:
+ self.putb(last_samplenum, [data, [str(data)]])
+ incoming = (bin(shiftreg & 0x0291).count('1') + data) & 1
+ shiftreg = (incoming << 15) | (shiftreg >> 1)
+ prev_start = start
+ prev_stop = stop
+ last_samplenum = self.samplenum
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+The Serial Inter-Processor Interface (SIPI) is a higher-level protocol that runs
+over the LFAST physical interface. Together, they form the NXP Zipwire interface.
+
+The SIPI interface is also provided by Infineon as HSST, using HSCT for transport.
+
+For details see https://www.nxp.com/docs/en/application-note/AN5134.pdf and
+https://hitex.co.uk/fileadmin/uk-files/downloads/ShieldBuddy/tc27xD_um_v2.2.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from binascii import crc_hqx
+
+# See tc27xD_um_v2.2.pdf, Table 20-2
+# (name, addr byte count, data byte count)
+command_codes = {
+ 0b00000: ('Read byte', 4, 0),
+ 0b00001: ('Read 2 byte', 4, 0),
+ 0b00010: ('Read 4 byte', 4, 0),
+ # Reserved
+ 0b00100: ('Write byte with ACK', 4, 4),
+ 0b00101: ('Write 2 byte with ACK', 4, 4),
+ 0b00110: ('Write 4 byte with ACK', 4, 4),
+ # Reserved
+ 0b01000: ('ACK', 0, 0),
+ 0b01001: ('NACK (Target Error)', 0, 0),
+ 0b01010: ('Read Answer with ACK', 4, 4),
+ # Reserved
+ 0b01100: ('Trigger with ACK', 0, 0),
+ # Reserved
+ # Reserved
+ # Reserved
+ # Reserved
+ # Reserved
+ 0b10010: ('Read 4-byte JTAG ID', 0, 0),
+ # Reserved
+ # Reserved
+ # Reserved
+ # Reserved
+ 0b10111: ('Stream 32 byte with ACK', 0, 32)
+ # Rest is reserved
+}
+
+
+ann_header_tag, ann_header_cmd, ann_header_ch, ann_address, ann_data, \
+ ann_crc, ann_warning = range(7)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sipi'
+ name = 'SIPI (Zipwire)'
+ longname = 'NXP SIPI interface'
+ desc = 'Serial Inter-Processor Interface (SIPI) aka Zipwire, aka HSSL'
+ license = 'gplv2+'
+ inputs = ['lfast']
+ outputs = []
+ tags = ['Embedded/industrial']
+ annotations = (
+ ('header_tag', 'Transaction Tag'),
+ ('header_cmd', 'Command Code'),
+ ('header_ch', 'Channel'),
+ ('address', 'Address'),
+ ('data', 'Data'),
+ ('crc', 'CRC'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('fields', 'Fields', (ann_header_tag, ann_header_cmd,
+ ann_header_ch, ann_address, ann_data, ann_crc,)),
+ ('warnings', 'Warnings', (ann_warning,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.byte_len = 0
+ self.frame_len = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+ def put_ann(self, ss, es, ann_class, value):
+ self.put(int(ss), int(es), self.out_ann, [ann_class, value])
+
+ def put_header(self, ss_header, es_header, value):
+ ss = ss_header
+ es = ss + 3 * self.bit_len
+ tag = (value & 0xE000) >> 13
+ self.put_ann(ss, es, ann_header_tag, ['{:02X}'.format(tag)])
+
+ ss = es
+ es = ss + 5 * self.bit_len
+ cmd_id = (value & 0x1F00) >> 8
+ cmd_name, self.addr_len, self.data_len = \
+ command_codes.get(cmd_id, ('Reserved ({:02X})'.format(cmd_id), 0, 0))
+ self.frame_len = 2 + 2 + self.addr_len + self.data_len # +Header +CRC
+ self.put_ann(ss, es, ann_header_cmd, [cmd_name])
+
+ # Bits 4..7 are reserved and should be 0, warn if they're not
+ ss = es
+ es = ss + 4 * self.bit_len
+ reserved_bits = (value & 0x00F0) >> 4
+ if reserved_bits > 0:
+ self.put_ann(ss, es, ann_warning, ['Reserved bits #4..7 should be 0'])
+
+ ss = es
+ es = ss + 3 * self.bit_len
+ ch = (value & 0x000E) >> 1 # See tc27xD_um_v2.2.pdf, Table 20-1
+ self.put_ann(ss, es, ann_header_ch, [str(ch)])
+
+ # Bit 0 is reserved and should be 0, warn if it's not
+ if (value & 0x0001) == 0x0001:
+ ss = es
+ es = ss + self.bit_len
+ self.put_ann(ss, es, ann_warning, ['Reserved bit #0 should be 0'])
+
+ def put_payload(self, data):
+ byte_idx = 0
+ if self.addr_len > 0:
+ for value_tuple in data[:self.addr_len]:
+ ss, es, value = value_tuple
+ self.put_ann(ss, es, ann_address, ['{:02X}'.format(value)])
+ byte_idx = self.addr_len
+
+ if self.data_len > 0:
+ for value_tuple in data[byte_idx:]:
+ ss, es, value = value_tuple
+ self.put_ann(ss, es, ann_data, ['{:02X}'.format(value)])
+
+ def put_crc(self, ss, es, crc_value, crc_payload_data):
+ crc_payload = []
+ for value_tuple in crc_payload_data:
+ crc_payload.append(value_tuple[2])
+
+ calculated_crc = crc_hqx(bytes(crc_payload), 0xFFFF)
+
+ if calculated_crc == crc_value:
+ self.put_ann(ss, es, ann_crc, ['CRC OK'])
+ else:
+ self.put_ann(ss, es, ann_crc, ['Have {:02X} but calculated {:02X}'.format(crc_value, calculated_crc)])
+ self.put_ann(ss, es, ann_warning, ['CRC mismatch'])
+
+ def decode(self, ss, es, data):
+ if len(data) == 1:
+ self.put_ann(ss, es, ann_warning, ['Header too short'])
+ return
+
+ # ss and es are now unused, we use them as local variables instead
+
+ self.bit_len = (data[0][1] - data[0][0]) / 8.0
+
+ byte_idx = 0
+
+ ss = data[byte_idx][0]
+ es = data[byte_idx + 1][1]
+ self.put_header(ss, es, (data[byte_idx][2] << 8) + data[byte_idx + 1][2])
+ byte_idx += 2
+
+ payload_len = self.frame_len - 2 - 2 # -Header -CRC
+ if payload_len > 0:
+ self.put_payload(data[byte_idx:-2])
+ byte_idx += payload_len
+
+ ss = data[byte_idx][0]
+ es = data[byte_idx + 1][1]
+ if byte_idx == len(data) - 2:
+ # CRC is calculated over header + payload bytes
+ self.put_crc(ss, es, (data[byte_idx][2] << 8) + data[byte_idx + 1][2], data[0:-2])
+ else:
+ self.put_ann(ss, es, ann_warning, ['CRC incomplete or missing'])
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+SLE 4418/28/32/42 memory cards implement a 2-wire protocol (CLK and I/O)
+for data communication, along with the RST signal which resets the card's
+internal state, and can terminate currently executing long memory reads.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from common.srdhelper import bitpack_lsb
+import sigrokdecode as srd
+
+class Pin:
+ RST, CLK, IO, = range(3)
+
+class Ann:
+ RESET_SYM, INTR_SYM, START_SYM, STOP_SYM, BIT_SYM, \
+ ATR_BYTE, CMD_BYTE, OUT_BYTE, PROC_BYTE, \
+ ATR_DATA, CMD_DATA, OUT_DATA, PROC_DATA, \
+ = range(13)
+
+class Bin:
+ BYTES, = range(1)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sle44xx'
+ name = 'SLE 44xx'
+ longname = 'SLE44xx memory card'
+ desc = 'SLE 4418/28/32/42 memory card serial protocol'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Memory']
+ channels = (
+ {'id': 'rst', 'name': 'RST', 'desc': 'Reset line'},
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'},
+ {'id': 'io', 'name': 'I/O', 'desc': 'I/O data line'},
+ )
+ annotations = (
+ ('reset_sym', 'Reset Symbol'),
+ ('intr_sym', 'Interrupt Symbol'),
+ ('start_sym', 'Start Symbol'),
+ ('stop_sym', 'Stop Symbol'),
+ ('bit_sym', 'Bit Symbol'),
+ ('atr_byte', 'ATR Byte'),
+ ('cmd_byte', 'Command Byte'),
+ ('out_byte', 'Outgoing Byte'),
+ ('proc_byte', 'Processing Byte'),
+ ('atr_data', 'ATR data'),
+ ('cmd_data', 'Command data'),
+ ('out_data', 'Outgoing data'),
+ ('proc_data', 'Processing data'),
+ )
+ annotation_rows = (
+ ('symbols', 'Symbols', (Ann.RESET_SYM, Ann.INTR_SYM,
+ Ann.START_SYM, Ann.STOP_SYM, Ann.BIT_SYM,)),
+ ('fields', 'Fields', (Ann.ATR_BYTE,
+ Ann.CMD_BYTE, Ann.OUT_BYTE, Ann.PROC_BYTE,)),
+ ('operations', 'Operations', (Ann.ATR_DATA,
+ Ann.CMD_DATA, Ann.OUT_DATA, Ann.PROC_DATA,)),
+ )
+ binary = (
+ ('bytes', 'Bytes'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.max_addr = 256
+ self.bits = []
+ self.atr_bytes = []
+ self.cmd_bytes = []
+ self.cmd_proc = None
+ self.out_len = None
+ self.out_bytes = []
+ self.proc_state = None
+ self.state = None
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+ def putx(self, ss, es, cls, data):
+ self.put(ss, es, self.out_ann, [cls, data,])
+
+ def putb(self, ss, es, cls , data):
+ self.put(ss, es, self.out_binary, [cls, data,])
+
+ def snums_to_usecs(self, snum_count):
+ if not self.samplerate:
+ return None
+ snums_per_usec = self.samplerate / 1e6
+ usecs = snum_count / snums_per_usec
+ return usecs
+
+ def lookup_proto_ann_txt(self, key, variables):
+ ann = {
+ 'RESET_SYM': [Ann.RESET_SYM, 'Reset', 'R',],
+ 'INTR_SYM': [Ann.INTR_SYM, 'Interrupt', 'Intr', 'I',],
+ 'START_SYM': [Ann.START_SYM, 'Start', 'ST', 'S',],
+ 'STOP_SYM': [Ann.STOP_SYM, 'Stop', 'SP', 'P',],
+ 'BIT_SYM': [Ann.BIT_SYM, '{bit}',],
+ 'ATR_BYTE': [Ann.ATR_BYTE,
+ 'Answer To Reset: {data:02x}',
+ 'ATR: {data:02x}',
+ '{data:02x}',
+ ],
+ 'CMD_BYTE': [Ann.CMD_BYTE,
+ 'Command: {data:02x}',
+ 'Cmd: {data:02x}',
+ '{data:02x}',
+ ],
+ 'OUT_BYTE': [Ann.OUT_BYTE,
+ 'Outgoing data: {data:02x}',
+ 'Data: {data:02x}',
+ '{data:02x}',
+ ],
+ 'PROC_BYTE': [Ann.PROC_BYTE,
+ 'Internal processing: {data:02x}',
+ 'Proc: {data:02x}',
+ '{data:02x}',
+ ],
+ 'ATR_DATA': [Ann.ATR_DATA,
+ 'Answer To Reset: {data}',
+ 'ATR: {data}',
+ '{data}',
+ ],
+ 'CMD_DATA': [Ann.CMD_DATA,
+ 'Command: {data}',
+ 'Cmd: {data}',
+ '{data}',
+ ],
+ 'OUT_DATA': [Ann.OUT_DATA,
+ 'Outgoing: {data}',
+ 'Out: {data}',
+ '{data}',
+ ],
+ 'PROC_DATA': [Ann.PROC_DATA,
+ 'Processing: {data}',
+ 'Proc: {data}',
+ '{data}',
+ ],
+ }.get(key, None)
+ if ann is None:
+ return None, []
+ cls, texts = ann[0], ann[1:]
+ texts = [t.format(**variables) for t in texts]
+ return cls, texts
+
+ def text_for_accu_bytes(self, accu):
+ if not accu:
+ return None, None, None, None
+ ss, es = accu[0][1], accu[-1][2]
+ data = [a[0] for a in accu]
+ text = " ".join(['{:02x}'.format(a) for a in data])
+ return ss, es, data, text
+
+ def flush_queued(self):
+ '''Flush previously accumulated operations details.'''
+
+ # Can be called when either the completion of an operation got
+ # detected (reliably), or when some kind of reset condition was
+ # met while a potential previously observed operation has not
+ # been postprocessed yet (best effort). Should not harm when the
+ # routine gets invoked while no data was collected yet, or was
+ # flushed already.
+ # BEWARE! Will void internal state. Should really only get called
+ # "between operations", NOT between fields of an operation.
+
+ if self.atr_bytes:
+ key = 'ATR_DATA'
+ ss, es, _, text = self.text_for_accu_bytes(self.atr_bytes)
+ cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+ self.putx(ss, es, cls, texts)
+
+ if self.cmd_bytes:
+ key = 'CMD_DATA'
+ ss, es, _, text = self.text_for_accu_bytes(self.cmd_bytes)
+ cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+ self.putx(ss, es, cls, texts)
+
+ if self.out_bytes:
+ key = 'OUT_DATA'
+ ss, es, _, text = self.text_for_accu_bytes(self.out_bytes)
+ cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+ self.putx(ss, es, cls, texts)
+
+ if self.proc_state:
+ key = 'PROC_DATA'
+ ss = self.proc_state['ss']
+ es = self.proc_state['es']
+ clk = self.proc_state['clk']
+ high = self.proc_state['io1']
+ text = '{clk} clocks, I/O {high}'.format(clk = clk, high = int(high))
+ usecs = self.snums_to_usecs(es - ss)
+ if usecs:
+ msecs = usecs / 1000
+ text = '{msecs:.2f} ms, {text}'.format(msecs = msecs, text = text)
+ cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+ self.putx(ss, es, cls, texts)
+
+ self.atr_bytes = None
+ self.cmd_bytes = None
+ self.cmd_proc = None
+ self.out_len = None
+ self.out_bytes = None
+ self.proc_state = None
+ self.state = None
+
+ def handle_reset(self, ss, es, has_clk):
+ self.flush_queued()
+ key = '{}_SYM'.format('RESET' if has_clk else 'INTR')
+ cls, texts = self.lookup_proto_ann_txt(key, {})
+ self.putx(ss, es, cls, texts)
+ self.bits = []
+ self.state = 'ATR' if has_clk else None
+
+ def handle_command(self, ss, is_start):
+ if is_start:
+ self.flush_queued()
+ key = '{}_SYM'.format('START' if is_start else 'STOP')
+ cls, texts = self.lookup_proto_ann_txt(key, {})
+ self.putx(ss, ss, cls, texts)
+ self.bits = []
+ self.state = 'CMD' if is_start else 'DATA'
+
+ def command_check(self, ctrl, addr, data):
+ '''Interpret CTRL/ADDR/DATA command entry.'''
+
+ # See the Siemens Datasheet section 2.3 Commands. The abbreviated
+ # text variants are my guesses, terse for readability at coarser
+ # zoom levels.
+ codes_table = {
+ 0x30: {
+ 'fmt': [
+ 'read main memory, addr {addr:02x}',
+ 'RD-M @{addr:02x}',
+ ],
+ 'len': lambda ctrl, addr, data: self.max_addr - addr,
+ },
+ 0x31: {
+ 'fmt': [
+ 'read security memory',
+ 'RD-S',
+ ],
+ 'len': 4,
+ },
+ 0x33: {
+ 'fmt': [
+ 'compare verification data, addr {addr:02x}, data {data:02x}',
+ 'CMP-V @{addr:02x} ={data:02x}',
+ ],
+ 'proc': True,
+ },
+ 0x34: {
+ 'fmt': [
+ 'read protection memory, addr {addr:02x}',
+ 'RD-P @{addr:02x}',
+ ],
+ 'len': 4,
+ },
+ 0x38: {
+ 'fmt': [
+ 'update main memory, addr {addr:02x}, data {data:02x}',
+ 'WR-M @{addr:02x} ={data:02x}',
+ ],
+ 'proc': True,
+ },
+ 0x39: {
+ 'fmt': [
+ 'update security memory, addr {addr:02x}, data {data:02x}',
+ 'WR-S @{addr:02x} ={data:02x}',
+ ],
+ 'proc': True,
+ },
+ 0x3c: {
+ 'fmt': [
+ 'write protection memory, addr {addr:02x}, data {data:02x}',
+ 'WR-P @{addr:02x} ={data:02x}',
+ ],
+ 'proc': True,
+ },
+ }
+ code = codes_table.get(ctrl, {})
+ dflt_fmt = [
+ 'unknown, ctrl {ctrl:02x}, addr {addr:02x}, data {data:02x}',
+ 'UNK-{ctrl:02x} @{addr:02x}, ={data:02x}',
+ ]
+ fmt = code.get('fmt', dflt_fmt)
+ if not isinstance(fmt, (list, tuple,)):
+ fmt = [fmt,]
+ texts = [f.format(ctrl = ctrl, addr = addr, data = data) for f in fmt]
+ length = code.get('len', None)
+ if callable(length):
+ length = length(ctrl, addr, data)
+ is_proc = code.get('proc', False)
+ return texts, length, is_proc
+
+ def processing_start(self, ss, es, io_high):
+ self.proc_state = {
+ 'ss': ss or es,
+ 'es': es or ss,
+ 'clk': 0,
+ 'io1': bool(io_high),
+ }
+
+ def processing_update(self, es, clk_inc, io_high):
+ if es is not None and es > self.proc_state['es']:
+ self.proc_state['es'] = es
+ self.proc_state['clk'] += clk_inc
+ if io_high:
+ self.proc_state['io1'] = True
+
+ def handle_data_byte(self, ss, es, data, bits):
+ '''Accumulate CMD or OUT data bytes.'''
+
+ if self.state == 'ATR':
+ if not self.atr_bytes:
+ self.atr_bytes = []
+ self.atr_bytes.append([data, ss, es, bits,])
+ if len(self.atr_bytes) == 4:
+ self.flush_queued()
+ return
+
+ if self.state == 'CMD':
+ if not self.cmd_bytes:
+ self.cmd_bytes = []
+ self.cmd_bytes.append([data, ss, es, bits,])
+ if len(self.cmd_bytes) == 3:
+ ctrl, addr, data = [c[0] for c in self.cmd_bytes]
+ texts, length, proc = self.command_check(ctrl, addr, data)
+ # Immediately emit the annotation to not lose the text,
+ # and to support zoom levels for this specific case.
+ ss, es = self.cmd_bytes[0][1], self.cmd_bytes[-1][2]
+ cls = Ann.CMD_DATA
+ self.putx(ss, es, cls, texts)
+ self.cmd_bytes = []
+ # Prepare to continue either at OUT or PROC after CMD.
+ self.out_len = length
+ self.cmd_proc = bool(proc)
+ self.state = None
+ return
+
+ if self.state == 'OUT':
+ if not self.out_bytes:
+ self.out_bytes = []
+ self.out_bytes.append([data, ss, es, bits,])
+ if self.out_len is not None and len(self.out_bytes) == self.out_len:
+ self.flush_queued()
+ return
+
+ def handle_data_bit(self, ss, es, bit):
+ '''Gather 8 bits of data (or track processing progress).'''
+
+ # Switch late from DATA to either OUT or PROC. We can tell the
+ # type and potentially fixed length at the end of CMD already,
+ # but a START/STOP condition may void this information. So we
+ # do the switch at the first data bit after CMD.
+ # In the OUT case data bytes get accumulated, until either the
+ # expected byte count is reached, or another CMD starts. In the
+ # PROC case a high I/O level terminates execution.
+ if self.state == 'DATA':
+ if self.out_len:
+ self.state = 'OUT'
+ elif self.cmd_proc:
+ self.state = 'PROC'
+ self.processing_start(ss or es, es or ss, bit == 1)
+ else:
+ # Implementor's note: Handle unknown situations like
+ # outgoing data bytes, for the user's convenience. This
+ # will show OUT bytes even if it's just processing CLK
+ # cycles with constant or irrelevant I/O bit patterns.
+ self.state = 'OUT'
+ if self.state == 'PROC':
+ high = bit == 1
+ if ss is not None:
+ self.processing_update(ss, 0, high)
+ if es is not None:
+ self.processing_update(es, 1, high)
+ if high:
+ self.flush_queued()
+ return
+
+ # This routine gets called two times per bit value. Track the
+ # bit's value and ss timestamp when the bit period starts. And
+ # update the es timestamp at the end of the bit's validity.
+ if ss is not None:
+ self.bits.append([bit, ss, es or ss])
+ return
+ if es is None:
+ # Unexpected invocation. Could be a glitch or invalid input
+ # data, or an interaction with RESET/START/STOP conditions.
+ self.bits = []
+ return
+ if not self.bits:
+ return
+ if bit is not None:
+ self.bits[-1][0] = bit
+ # TODO Check for consistent bit level at ss and es when
+ # the information was available? Is bit data sampled at
+ # different clock edges depending whether data is sent
+ # or received?
+ self.bits[-1][2] = es
+ # Emit the bit's annotation. See if a byte was received.
+ bit, ss, es = self.bits[-1]
+ cls, texts = self.lookup_proto_ann_txt('BIT_SYM', {'bit': bit})
+ self.putx(ss, es, cls, texts)
+ if len(self.bits) < 8:
+ return
+
+ # Get the data byte value, and the byte's ss/es. Emit the byte's
+ # annotation and binary output. Pass the byte to upper layers.
+ # TODO Vary annotation classes with the byte's position within
+ # a field? To tell CTRL/ADDR/DATA of a CMD entry apart?
+ bits = self.bits
+ self.bits = []
+ data = bitpack_lsb(bits, 0)
+ ss = bits[0][1]
+ es = bits[-1][2]
+
+ key = '{}_BYTE'.format(self.state)
+ cls, texts = self.lookup_proto_ann_txt(key, {'data': data})
+ if cls:
+ self.putx(ss, es, cls, texts)
+ self.putb(ss, es, Bin.BYTES, bytes([data]))
+
+ self.handle_data_byte(ss, es, data, bits)
+
+ def decode(self):
+ '''Decoder's main data interpretation loop.'''
+
+ # Signal conditions tracked by the protocol decoder:
+ # - Rising and falling RST edges, which span the width of a
+ # high-active RESET pulse. RST has highest priority, no
+ # other activity can take place in this period.
+ # - Rising and falling CLK edges when RST is active. The
+ # CLK pulse when RST is asserted will reset the card's
+ # address counter. RST alone can terminate memory reads.
+ # - Rising and falling CLK edges when RST is inactive. This
+ # determines the period where BIT values are valid.
+ # - I/O edges during high CLK. These are START and STOP
+ # conditions that tell COMMAND and DATA phases apart.
+ # - Rise of I/O during internal processing. This expression
+ # is an unconditional part of the .wait() condition set. It
+ # is assumed that skipping this match in many cases is more
+ # efficient than the permanent re-construction of the .wait()
+ # condition list in every loop iteration, and preferrable to
+ # the maintainance cost of duplicating RST and CLK handling
+ # when checking I/O during internal processing.
+ (
+ COND_RESET_START, COND_RESET_STOP,
+ COND_RSTCLK_START, COND_RSTCLK_STOP,
+ COND_DATA_START, COND_DATA_STOP,
+ COND_CMD_START, COND_CMD_STOP,
+ COND_PROC_IOH,
+ ) = range(9)
+ conditions = [
+ {Pin.RST: 'r'},
+ {Pin.RST: 'f'},
+ {Pin.RST: 'h', Pin.CLK: 'r'},
+ {Pin.RST: 'h', Pin.CLK: 'f'},
+ {Pin.RST: 'l', Pin.CLK: 'r'},
+ {Pin.RST: 'l', Pin.CLK: 'f'},
+ {Pin.CLK: 'h', Pin.IO: 'f'},
+ {Pin.CLK: 'h', Pin.IO: 'r'},
+ {Pin.RST: 'l', Pin.IO: 'r'},
+ ]
+
+ ss_reset = es_reset = ss_clk = es_clk = None
+ while True:
+
+ is_outgoing = self.state == 'OUT'
+ is_processing = self.state == 'PROC'
+ pins = self.wait(conditions)
+ io = pins[Pin.IO]
+
+ # Handle RESET conditions, including an optional CLK pulse
+ # while RST is asserted.
+ if self.matched[COND_RESET_START]:
+ self.flush_queued()
+ ss_reset = self.samplenum
+ es_reset = ss_clk = es_clk = None
+ continue
+ if self.matched[COND_RESET_STOP]:
+ es_reset = self.samplenum
+ self.handle_reset(ss_reset or 0, es_reset, ss_clk and es_clk)
+ ss_reset = es_reset = ss_clk = es_clk = None
+ continue
+ if self.matched[COND_RSTCLK_START]:
+ ss_clk = self.samplenum
+ es_clk = None
+ continue
+ if self.matched[COND_RSTCLK_STOP]:
+ es_clk = self.samplenum
+ continue
+
+ # Handle data bits' validity boundaries. Also covers the
+ # periodic check for high I/O level and update of details
+ # during internal processing.
+ if self.matched[COND_DATA_START]:
+ self.handle_data_bit(self.samplenum, None, io)
+ continue
+ if self.matched[COND_DATA_STOP]:
+ self.handle_data_bit(None, self.samplenum, None)
+ continue
+
+ # Additional check for idle I/O during internal processing,
+ # independent of CLK edges this time. This assures that the
+ # decoder ends processing intervals as soon as possible, at
+ # the most precise timestamp.
+ if is_processing and self.matched[COND_PROC_IOH]:
+ self.handle_data_bit(self.samplenum, self.samplenum, io)
+ continue
+
+ # The START/STOP conditions are only applicable outside of
+ # "outgoing data" or "internal processing" periods. This is
+ # what the data sheet specifies.
+ if not is_outgoing and not is_processing:
+ if self.matched[COND_CMD_START]:
+ self.handle_command(self.samplenum, True)
+ continue
+ if self.matched[COND_CMD_STOP]:
+ self.handle_command(self.samplenum, False)
+ continue
annotations = (
('bitrate', 'Bitrate / baudrate'),
('preamble', 'Preamble'),
- ('bits', 'Bits'),
- ('aux', 'Auxillary-audio-databits'),
- ('samples', 'Audio Samples'),
+ ('bit', 'Bit'),
+ ('aux', 'Auxillary-audio-databit'),
+ ('sample', 'Audio Sample'),
('validity', 'Data Valid'),
('subcode', 'Subcode data'),
('chan_stat', 'Channnel Status'),
('parity', 'Parity Bit'),
)
annotation_rows = (
- ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
('bits', 'Bits', (2,)),
+ ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
('samples', 'Samples', (4,)),
)
self.seen_preamble = False
self.last_preamble = 0
+ self.bitrate_message_start = 0
+ self.bitrate_message_end = 0
+ self.frame_counter = 0
+ self.frame_start = 0
+ self.frame_length = 0
+
+ self.sampleratetmp = 1
+
self.first_one = True
self.subframe = []
self.samplerate = value
def get_pulse_type(self):
- if self.range1 == 0 or self.range2 == 0:
- return -1
if self.pulse_width >= self.range2:
return 2
elif self.pulse_width >= self.range1:
if self.pulse_width != 0:
self.clocks.append(self.pulse_width)
self.state = 'GET SECOND PULSE WIDTH'
+ self.puty([2, ['Found width 1: %d' % self.pulse_width, 'W1: %d' % self.pulse_width]])
+ self.ss_edge = self.samplenum
def find_second_pulse_width(self):
if self.pulse_width > (self.clocks[0] * 1.3) or \
- self.pulse_width < (self.clocks[0] * 0.7):
+ self.pulse_width <= (self.clocks[0] * 0.75):
+ self.puty([2, ['Found width 2: %d' % self.pulse_width, 'W2: %d' % self.pulse_width]])
self.clocks.append(self.pulse_width)
self.state = 'GET THIRD PULSE WIDTH'
+ else:
+ self.puty([2, ['Search width 2: %d' % self.pulse_width, 'SW2: %d' % self.pulse_width]])
+ self.ss_edge = self.samplenum
def find_third_pulse_width(self):
if not ((self.pulse_width > (self.clocks[0] * 1.3) or \
- self.pulse_width < (self.clocks[0] * 0.7)) \
+ self.pulse_width <= (self.clocks[0] * 0.75)) \
and (self.pulse_width > (self.clocks[1] * 1.3) or \
- self.pulse_width < (self.clocks[1] * 0.7))):
+ self.pulse_width <= (self.clocks[1] * 0.75))):
+ self.puty([2, ['Search width 3: %d' % self.pulse_width, 'SW3: %d' % self.pulse_width]])
+ self.ss_edge = self.samplenum
return
+ else:
+ self.puty([2, ['Found width 3: %d' % self.pulse_width, 'W3: %d' % self.pulse_width]])
+ self.ss_edge = self.samplenum
+ # The message of the calculated bitrate should start at this sample
+ # (right after the synchronisation).
+ self.bitrate_message_start = self.samplenum
self.clocks.append(self.pulse_width)
self.clocks.sort()
self.range1 = (self.clocks[0] + self.clocks[1]) / 2
self.range2 = (self.clocks[1] + self.clocks[2]) / 2
- spdif_bitrate = int(self.samplerate / (self.clocks[2] / 1.5))
+ # Give some feedback during synchronisation and inform if sample rate
+ # is too low.
+ if self.clocks[0] <= 3:
+ self.putx(0, self.samplenum, [0, ['Short pulses detected. Increase sample rate!']])
+ raise SamplerateError('Short pulses detected')
+ else:
+ self.putx(0, self.samplenum, [0, ['Synchronisation']])
self.ss_edge = 0
- self.puty([0, ['Signal Bitrate: %d Mbit/s (=> %d kHz)' % \
- (spdif_bitrate, (spdif_bitrate/ (2 * 32)))]])
-
- clock_period_nsec = 1000000000 / spdif_bitrate
+ # Mostly, the synchronisation ends with a long pulse because they
+ # appear rarely. A skip of the next pulse will then prevent a 'M'
+ # frame to be labeled an unknown preamble for the first decoded frame.
+ (data,) = self.wait({0: 'e'})
+ self.pulse_width = self.samplenum - self.samplenum_prev_edge
+ self.samplenum_prev_edge = self.samplenum
self.last_preamble = self.samplenum
# We are done recovering the clock, now let's decode the data stream.
if pulse == 2:
self.preamble.append(self.get_pulse_type())
self.state = 'DECODE PREAMBLE'
- self.ss_edge = self.samplenum - self.pulse_width - 1
+ self.ss_edge = self.samplenum - self.pulse_width
+ # Use the first ten frames to calculate bit rates
+ if self.frame_counter == 0:
+ # This is the first preamble to be decoded. Measurement of
+ # bit rates starts here.
+ self.frame_start = self.samplenum
+ # The bit rate message should end here.
+ self.bitrate_message_end = self.ss_edge
+ elif self.frame_counter == 10:
+ self.frame_length = self.samplenum - self.frame_start
+ # Use section between end of synchronisation and start of
+ # first preamble to show measured bit rates.
+ if self.samplerate:
+ self.putx(self.bitrate_message_start, self.bitrate_message_end,\
+ [0, ['Audio samplingrate: %6.2f kHz; Bit rate: %6.3f MBit/s' %\
+ ((self.samplerate / 200 / self.frame_length), (self.samplerate / 200 * 64 / 1000 / self.frame_length))]])
+ else:
+ self.putx(self.bitrate_message_start, self.bitrate_message_end, [0, ['No sample rate given']])
+ self.frame_counter += 1
return
# We've seen a preamble.
if pulse == 1 and self.first_one:
self.first_one = False
- self.subframe.append([pulse, self.samplenum - \
- self.pulse_width - 1, self.samplenum])
+ self.subframe.append([pulse, self.samplenum - self.pulse_width, self.samplenum])
elif pulse == 1 and not self.first_one:
self.subframe[-1][2] = self.samplenum
self.putx(self.subframe[-1][1], self.samplenum, [2, ['1']])
self.bitcount += 1
self.first_one = True
else:
- self.subframe.append([pulse, self.samplenum - \
- self.pulse_width - 1, self.samplenum])
- self.putx(self.samplenum - self.pulse_width - 1,
- self.samplenum, [2, ['0']])
+ self.subframe.append([pulse, self.samplenum - self.pulse_width, self.samplenum])
+ self.putx(self.samplenum - self.pulse_width, self.samplenum, [2, ['0']])
self.bitcount += 1
if self.bitcount == 28:
self.last_preamble = self.samplenum
def decode(self):
+ # Set samplerate to 0 if it is not given. Decoding is still possible.
if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
+ self.samplerate = 0
- # Throw away first detected edge as it might be mangled data.
+ # Throw away first two edges as it might be mangled data.
self.wait({0: 'e'})
+ self.wait({0: 'e'})
+ self.ss_edge = 0
+ self.puty([2, ['Skip']])
+ self.ss_edge = self.samplenum
+ self.samplenum_prev_edge = self.samplenum
while True:
# Wait for any edge (rising or falling).
(data,) = self.wait({0: 'e'})
- self.pulse_width = self.samplenum - self.samplenum_prev_edge - 1
+ self.pulse_width = self.samplenum - self.samplenum_prev_edge
self.samplenum_prev_edge = self.samplenum
if self.state == 'GET FIRST PULSE WIDTH':
annotations = (
('miso-data', 'MISO data'),
('mosi-data', 'MOSI data'),
- ('miso-bits', 'MISO bits'),
- ('mosi-bits', 'MOSI bits'),
- ('warnings', 'Human-readable warnings'),
+ ('miso-bit', 'MISO bit'),
+ ('mosi-bit', 'MOSI bit'),
+ ('warning', 'Warning'),
('miso-transfer', 'MISO transfer'),
('mosi-transfer', 'MOSI transfer'),
)
annotation_rows = (
('miso-bits', 'MISO bits', (2,)),
- ('miso-data', 'MISO data', (0,)),
- ('miso-transfer', 'MISO transfer', (5,)),
+ ('miso-data-vals', 'MISO data', (0,)),
+ ('miso-transfers', 'MISO transfers', (5,)),
('mosi-bits', 'MOSI bits', (3,)),
- ('mosi-data', 'MOSI data', (1,)),
- ('mosi-transfer', 'MOSI transfer', (6,)),
+ ('mosi-data-vals', 'MOSI data', (1,)),
+ ('mosi-transfers', 'MOSI transfers', (6,)),
('other', 'Other', (4,)),
)
binary = (
self.misobytes = []
self.mosibytes = []
self.ss_block = -1
- self.samplenum = -1
self.ss_transfer = -1
self.cs_was_deasserted = False
self.have_cs = self.have_miso = self.have_mosi = None
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2015-2020 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
# OrderedDict which maps command IDs to their names and descriptions.
# Please keep this sorted by command ID.
-# Don't forget to update 'Ann' in pd.py if you add/remove items here.
cmds = OrderedDict([
(0x01, ('WRSR', 'Write status register')),
(0x02, ('PP', 'Page program')),
(0xb9, ('DP', 'Deep power down')),
(0xbb, ('2READ', '2x I/O read')), # a.k.a. "Fast read dual I/O".
(0xc1, ('EXSO', 'Exit secured OTP')),
- (0xc7, ('CE2', 'Chip erase')), # Alternative command ID
+ (0xc7, ('CE2', 'Chip erase 2')), # Alternative command ID
(0xd7, ('STATUS', 'Status register read')),
(0xd8, ('BE', 'Block erase')),
(0xef, ('REMS2', 'Read ID for 2x I/O mode')),
0x15: 'FM25Q32',
},
'macronix': {
+ 0x13: 'MX25L8006',
0x14: 'MX25L1605D',
0x15: 'MX25L3205D',
0x16: 'MX25L6405D',
'adesto_at45db161e': {
'vendor': 'Adesto',
'model': 'AT45DB161E',
- 'res_id': 0xff, # The chip doesn't emit an ID here.
- 'rems_id': 0xffff, # Not supported by the chip.
- 'rems2_id': 0xffff, # Not supported by the chip.
+ 'res_id': None, # The chip doesn't emit an ID here.
+ 'rems_id': None, # Not supported by the chip.
+ 'rems2_id': None, # Not supported by the chip.
'rdid_id': 0x1f26000100, # RDID and 2 extra "EDI" bytes.
'page_size': 528, # Configurable, could also be 512 bytes.
'sector_size': 128 * 1024,
'block_size': 4 * 1024,
},
+ # Atmel
+ 'atmel_at25128': {
+ 'vendor': 'Atmel',
+ 'model': 'AT25128',
+ 'res_id': None, # Not supported by the chip.
+ 'rems_id': None, # Not supported by the chip.
+ 'rems2_id': None, # Not supported by the chip.
+ 'rdid_id': None, # Not supported by the chip.
+ 'page_size': 64,
+ 'sector_size': None, # The chip doesn't have sectors.
+ 'block_size': None, # The chip doesn't have blocks.
+ },
+ 'atmel_at25256': {
+ 'vendor': 'Atmel',
+ 'model': 'AT25256',
+ 'res_id': None, # Not supported by the chip.
+ 'rems_id': None, # Not supported by the chip.
+ 'rems2_id': None, # Not supported by the chip.
+ 'rdid_id': None, # Not supported by the chip.
+ 'page_size': 64,
+ 'sector_size': None, # The chip doesn't have sectors.
+ 'block_size': None, # The chip doesn't have blocks.
+ },
# FIDELIX
'fidelix_fm25q32': {
'vendor': 'FIDELIX',
'sector_size': 4 * 1024,
'block_size': 64 * 1024,
},
+ 'macronix_mx25l8006': {
+ 'vendor': 'Macronix',
+ 'model': 'MX25L8006',
+ 'res_id': 0x13,
+ 'rems_id': 0xc213,
+ 'rems2_id': 0xc213,
+ 'rdid_id': 0xc22013,
+ 'page_size': 256,
+ 'sector_size': 4 * 1024,
+ 'block_size': 64 * 1024,
+ },
# Winbond
'winbond_w25q80dv': {
'vendor': 'Winbond',
'model': 'W25Q80DV',
'res_id': 0x13,
'rems_id': 0xef13,
- 'rems2_id': 0xffff, # Not supported by the chip.
+ 'rems2_id': None, # Not supported by the chip.
'rdid_id': 0xef4014,
'page_size': 256,
'sector_size': 4 * 1024,
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2011-2016 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2011-2020 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
##
import sigrokdecode as srd
+import re
+from common.srdhelper import SrdIntEnum
from .lists import *
L = len(cmds)
-# Don't forget to keep this in sync with 'cmds' is lists.py.
-class Ann:
- WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
- RDSR2, CE, ESRY, DSRY, WRITE1, WRITE2, REMS, RDID, RDP_RES, CP, ENSO, DP, \
- READ2X, EXSO, CE2, STATUS, BE, REMS2, \
- BIT, FIELD, WARN = range(L + 3)
+a = [re.sub('\/', '_', c[0]).replace('2READ', 'READ2X') for c in cmds.values()] + ['BIT', 'FIELD', 'WARN']
+Ann = SrdIntEnum.from_list('Ann', a)
def cmd_annotation_classes():
return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
class Decoder(srd.Decoder):
api_version = 3
id = 'spiflash'
- name = 'SPI flash'
- longname = 'SPI flash chips'
- desc = 'xx25 series SPI (NOR) flash chip protocol.'
+ name = 'SPI flash/EEPROM'
+ longname = 'SPI flash/EEPROM chips'
+ desc = 'xx25 series SPI (NOR) flash/EEPROM chip protocol.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the SPI PD and decodes the st25r39xx High performance NFC universal device and EMVCo reader protocol (SPI mode).
+
+It has been successfully tested with the st25r3916, other chips of this family may or may not be fully supported but not been verified.
+
+Please note that the SPI interface uses clock polarity 0 and clock phase 1, which is not the default setting.
+
+Details:
+https://www.st.com/resource/en/datasheet/st25r3916.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+## v0.1 - 17 September 2019 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 1 (January 2019)
+## v0.2 - 28 April 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 2 (December 2019) https://www.st.com/resource/en/datasheet/st25r3916.pdf
+## v0.3 - 17 June 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020) https://www.st.com/resource/en/datasheet/st25r3916.pdf
+
+## ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020) §4.4 Direct commands
+dir_cmd = {
+# addr: 'name'
+# Set Default
+ 0xC0: 'SET_DEFAULT',
+ 0xC1: 'SET_DEFAULT',
+# Stop All Activities
+ 0xC2: 'STOP',
+ 0xC3: 'STOP',
+# Transmit With CRC
+ 0xC4: 'TXCRC',
+# Transmit Without CRC
+ 0xC5: 'TXNOCRC',
+# Transmit REQA
+ 0xC6: 'TXREQA',
+# Transmit WUPA
+ 0xC7: 'TXWUPA',
+# NFC Initial Field ON
+ 0xC8: 'NFCINITFON',
+# NFC Response Field ON
+ 0xC9: 'NFCRESFON',
+# Go to Sense (Idle)
+ 0xCD: 'GOIDLE',
+# Go to Sleep (Halt)
+ 0xCE: 'GOHALT',
+# Mask Receive Data / Stops receivers and RX decoders
+ 0xD0: 'STOPRX',
+# Unmask Receive Data / Starts receivers and RX decoders
+ 0xD1: 'STARRX',
+# Change AM Modulation state
+ 0xD2: 'SETAMSTATE',
+# Measure Amplitude
+ 0xD3: 'MAMP',
+# Reset RX Gain
+ 0xD5: 'RSTRXGAIN',
+# Adjust Regulators
+ 0xD6: 'ADJREG',
+# Calibrate Driver Timing
+ 0xD8: 'CALDRVTIM',
+# Measure Phase
+ 0xD9: 'MPHASE',
+# Clear RSSI
+ 0xDA: 'CLRRSSI',
+# Clear FIFO
+ 0xDB: 'CLRFIFO',
+# Enter Transparent Mode
+ 0xDC: 'TRMODE',
+# Calibrate Capacitive Sensor
+ 0xDD: 'CALCAPA',
+# Measure Capacitance
+ 0xDE: 'MCAPA',
+# Measure Power Supply
+ 0xDF: 'MPOWER',
+# Start General Purpose Timer
+ 0xE0: 'STARGPTIM',
+# Start Wake-up Timer
+ 0xE1: 'STARWTIM',
+# Start Mask-receive Timer
+ 0xE2: 'STARMSKTIM',
+# Start No-response Timer
+ 0xE3: 'STARNRESPTIM',
+# Start PPON2 Timer
+ 0xE4: 'STARPPON2TIM',
+# Stop No-response Timer
+ 0xE8: 'STOPNRESTIM',
+# RFU / Not Used
+ 0xFA: 'RFU',
+# Register Space-B Access
+ 0xFB: 'REGSPACEB',
+# Register Test access
+ 0xFC: 'TESTACCESS'
+# Other codes => RFU / Not Used
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) §4.5 Registers Table 17. List of registers - Space A
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) §4.3.3 Serial peripheral interface (SPI) Table 11. SPI operation modes
+regsSpaceA = {
+# addr: 'name'
+# §4.5 Registers Table 17. List of registers - Space A
+# IO configuration
+ 0x00: 'IOCFG1',
+ 0x01: 'IOCFG2',
+# Operation control and mode definition
+ 0x02: 'OPCTRL',
+ 0x03: 'MODEDEF',
+ 0x04: 'BITRATE',
+# Protocol configuration
+ 0x05: 'TYPEA',
+ 0x06: 'TYPEB',
+ 0x07: 'TYPEBF',
+ 0x08: 'NFCIP1',
+ 0x09: 'STREAM',
+ 0x0A: 'AUX',
+# Receiver configuration
+ 0x0B: 'RXCFG1',
+ 0x0C: 'RXCFG2',
+ 0x0D: 'RXCFG3',
+ 0x0E: 'RXCFG4',
+# Timer definition
+ 0x0F: 'MSKRXTIM',
+ 0x10: 'NRESPTIM1',
+ 0x11: 'NRESPTIM2',
+ 0x12: 'TIMEMV',
+ 0x13: 'GPTIM1',
+ 0x14: 'GPTIM2',
+ 0x15: 'PPON2',
+# Interrupt and associated reporting
+ 0x16: 'MSKMAINIRQ',
+ 0x17: 'MSKTIMNFCIRQ',
+ 0x18: 'MSKERRWAKEIRQ',
+ 0x19: 'TARGIRQ',
+ 0x1A: 'MAINIRQ',
+ 0x1B: 'TIMNFCIRQ',
+ 0x1C: 'ERRWAKEIRQ',
+ 0x1D: 'TARGIRQ',
+ 0x1E: 'FIFOSTAT1',
+ 0x1F: 'FIFOSTAT2',
+ 0x20: 'COLLDISP',
+ 0x21: 'TARGDISP',
+# Definition of number of transmitted bytes
+ 0x22: 'NBTXB1',
+ 0x23: 'NBTXB2',
+ 0x24: 'BITRATEDET',
+# A/D converter output
+ 0x25: 'ADCONVOUT',
+# Antenna calibration
+ 0x26: 'ANTTUNECTRL1',
+ 0x27: 'ANTTUNECTRL2',
+# Antenna driver and modulation
+ 0x28: 'TXDRV',
+ 0x29: 'TARGMOD',
+# External field detector threshold
+ 0x2A: 'EXTFIELDON',
+ 0x2B: 'EXTFIELDOFF',
+# Regulator
+ 0x2C: 'REGVDDCTRL',
+# Receiver state display
+ 0x2D: 'RSSIDISP',
+ 0x2E: 'GAINSTATE',
+# Capacitive sensor
+ 0x2F: 'CAPACTRL',
+ 0x30: 'CAPADISP',
+# Auxiliary display
+ 0x31: 'AUXDISP',
+# Wake-up
+ 0x32: 'WAKETIMCTRL',
+ 0x33: 'AMPCFG',
+ 0x34: 'AMPREF',
+ 0x35: 'AMPAAVGDISP',
+ 0x36: 'AMPDISP',
+ 0x37: 'PHASECFG',
+ 0x38: 'PHASEREF',
+ 0x39: 'PHASEAAVGDISP',
+ 0x3A: 'PHASEDISP',
+ 0x3B: 'CAPACFG',
+ 0x3C: 'CAPAREF',
+ 0x3D: 'CAPAAAVGDISP',
+ 0x3E: 'CAPADISP',
+# IC identity
+ 0x3F: 'ICIDENT',
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) §4.3.3 Serial peripheral interface (SPI) Table 11. SPI operation modes
+ 0xA0: 'PT_memLoadA',
+ 0xA8: 'PT_memLoadF',
+ 0xAC: 'PT_memLoadTSN',
+ 0xBF: 'PT_memRead'
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) §4.5 Registers Table 18. List of registers - Space B
+regsSpaceB = {
+# addr: 'name'
+# §4.5 Registers Table 18. List of registers - Space B
+# Protocol configuration
+ 0x05: 'EMDSUPPRCONF',
+ 0x06: 'SUBCSTARTIM',
+# Receiver configuration
+ 0x0B: 'P2PRXCONF',
+ 0x0C: 'CORRCONF1',
+ 0x0D: 'CORRCONF2',
+# Timer definition
+ 0x0F: 'SQUELSHTIM',
+ 0x15: 'NFCGUARDTIM',
+# Antenna driver and modulation
+ 0x28: 'AUXMODSET',
+ 0x29: 'TXDRVTIM',
+# External field detector threshold
+ 0x2A: 'RESAMMODE',
+ 0x2B: 'TXDRVTIMDISP',
+# Regulator
+ 0x2C: 'REGDISP',
+# Protection
+ 0x30: 'OSHOOTCONF1',
+ 0x31: 'OSHOOTCONF2',
+ 0x32: 'USHOOTCONF1',
+ 0x33: 'USHOOTCONF2'
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) §4.4.17 Test access
+regsTest = {
+# addr: 'name'
+# §4.4.17 Test access (Typo in datasheet it is not register 0x00 but 0x01)
+ 0x01: 'ANTSTOBS'
+}
+
+## Optional TODO add important status bit fields / ANN_STATUS
+## Interrupt and associated reporting => Registers Space A from Address (hex) 0x16 to 0x21
+## §4.5.58 RSSI display register
+## §4.5.59 Gain reduction state register
+## ...
+
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2021 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+## v0.1 - 17 September 2019 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 1 (January 2019)
+## v0.2 - 28 April 2020 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 2 (December 2019)
+## v0.3 - 17 June 2020 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020)
+## v0.4 - 10 Aug 2021 B.VERNOUX
+### Fix FIFOR/FIFOW issues with Pulseview (with "Tabular Output View")
+### because of FIFO Read/FIFO Write commands, was not returning the
+### annotations short name FIFOR/FIFOW
+
+import sigrokdecode as srd
+from collections import namedtuple
+from common.srdhelper import SrdIntEnum
+from .lists import *
+
+Ann = SrdIntEnum.from_str('Ann', 'BURST_READ BURST_WRITE \
+ BURST_READB BURST_WRITEB BURST_READT BURST_WRITET \
+ DIRECTCMD FIFO_WRITE FIFO_READ STATUS WARN')
+
+Pos = namedtuple('Pos', ['ss', 'es'])
+Data = namedtuple('Data', ['mosi', 'miso'])
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'st25r39xx_spi'
+ name = 'ST25R39xx (SPI mode)'
+ longname = 'STMicroelectronics ST25R39xx'
+ desc = 'High performance NFC universal device and EMVCo reader protocol.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Wireless/RF']
+ annotations = (
+ ('Read', 'Burst register read'),
+ ('Write', 'Burst register write'),
+ ('ReadB', 'Burst register SpaceB read'),
+ ('WriteB', 'Burst register SpaceB write'),
+ ('ReadT', 'Burst register Test read'),
+ ('WriteT', 'Burst register Test write'),
+ ('Cmd', 'Direct command'),
+ ('FIFOW', 'FIFO write'),
+ ('FIFOR', 'FIFO read'),
+ ('status_reg', 'Status register'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('regs', 'Regs', (Ann.prefixes('BURST_'))),
+ ('cmds', 'Commands', (Ann.DIRECTCMD,)),
+ ('data', 'Data', (Ann.prefixes('FIFO_'))),
+ ('status', 'Status register', (Ann.STATUS,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.next()
+ self.requirements_met = True
+ self.cs_was_released = False
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def warn(self, pos, msg):
+ '''Put a warning message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
+
+ def putp(self, pos, ann, msg):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
+
+ def putp2(self, pos, ann, msg1, msg2):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
+
+ def next(self):
+ '''Resets the decoder after a complete command was decoded.'''
+ # 'True' for the first byte after CS# went low.
+ self.first = True
+
+ # The current command, and the minimum and maximum number
+ # of data bytes to follow.
+ self.cmd = None
+ self.min = 0
+ self.max = 0
+
+ # Used to collect the bytes after the command byte
+ # (and the start/end sample number).
+ self.mb = []
+ self.ss_mb = -1
+ self.es_mb = -1
+
+ def mosi_bytes(self):
+ '''Returns the collected MOSI bytes of a multi byte command.'''
+ return [b.mosi for b in self.mb]
+
+ def miso_bytes(self):
+ '''Returns the collected MISO bytes of a multi byte command.'''
+ return [b.miso for b in self.mb]
+
+ def decode_command(self, pos, b):
+ '''Decodes the command byte 'b' at position 'pos' and prepares
+ the decoding of the following data bytes.'''
+ c = self.parse_command(b)
+ if c is None:
+ self.warn(pos, 'Unknown command')
+ return
+
+ self.cmd, self.dat, self.min, self.max = c
+
+ if self.cmd == 'Cmd':
+ self.putp(pos, Ann.DIRECTCMD, self.format_command())
+ else:
+ # Don't output anything now, the command is merged with
+ # the data bytes following it.
+ self.ss_mb = pos.ss
+
+ def format_command(self):
+ '''Returns the label for the current command.'''
+ if self.cmd in ('Write', 'Read', 'WriteB', 'ReadB', 'WriteT', 'ReadT', 'FIFOW', 'FIFOR'):
+ return self.cmd
+ if self.cmd == 'Cmd':
+ reg = dir_cmd.get(self.dat, 'Unknown direct command')
+ return '{} {}'.format(self.cmd, reg)
+ else:
+ return 'TODO Cmd {}'.format(self.cmd)
+
+ def parse_command(self, b):
+ '''Parses the command byte.
+ Returns a tuple consisting of:
+ - the name of the command
+ - additional data needed to dissect the following bytes
+ - minimum number of following bytes
+ - maximum number of following bytes (None for infinite)
+ '''
+ addr = b & 0x3F
+ # previous command was 'Space B'
+ if self.cmd == 'Space B':
+ if (b & 0xC0) == 0x00:
+ return ('WriteB', addr, 1, 99999)
+ if (b & 0xC0) == 0x40:
+ return ('ReadB', addr, 1, 99999)
+ else:
+ self.warn(pos, 'Unknown address/command combination')
+ # previous command was 'TestAccess'
+ elif self.cmd == 'TestAccess':
+ if (b & 0xC0) == 0x00:
+ return ('WriteT', addr, 1, 99999)
+ if (b & 0xC0) == 0x40:
+ return ('ReadT', addr, 1, 99999)
+ else:
+ self.warn(pos, 'Unknown address/command combination')
+ else:
+ # Space A regs or other operation modes (except Space B)
+ # Register Write 0b00xxxxxx 0x00 to 0x3F => 'Write'
+ # Register Read 0b01xxxxxx 0x40 to 0x7F => 'Read'
+ if (b <= 0x7F):
+ if (b & 0xC0) == 0x00:
+ return ('Write', addr, 1, 99999)
+ if (b & 0xC0) == 0x40:
+ return ('Read', addr, 1, 99999)
+ else:
+ self.warn(pos, 'Unknown address/command combination')
+ else:
+ # FIFO Load 0b10000000 0x80 => 'FIFO Write'
+ # PT_memory loadA-config 0b10100000 0xA0 => 'Write'
+ # PT_memory loadF-config 0b10101000 0xA8 => 'Write'
+ # PT_memory loadTSN data 0b10101100 0xAC => 'Write'
+ # PT_memory Read 0b10111111 0xBF => 'Read'
+ # FIFO Read 0b10011111 0x9F => 'FIFO Read'
+ # Direct Command 0b11xxx1xx 0xC0 to 0xE8 => 'Cmd'
+ # Register Space-B Access 0b11111011 0xFB => 'Space B'
+ # Register Test Access 0b11111100 0xFC => 'TestAccess'
+ if b == 0x80:
+ return ('FIFOW', b, 1, 99999)
+ if b == 0xA0:
+ return ('Write', b, 1, 99999)
+ if b == 0xA8:
+ return ('Write', b, 1, 99999)
+ if b == 0xAC:
+ return ('Write', b, 1, 99999)
+ if b == 0xBF:
+ return ('Read', b, 1, 99999)
+ if b == 0x9F:
+ return ('FIFOR', b, 1, 99999)
+ if (b >= 0x0C and b <= 0xE8) :
+ return ('Cmd', b, 0, 0)
+ if b == 0xFB:
+ return ('Space B', b, 0, 0)
+ if b == 0xFC:
+ return ('TestAccess', b, 0, 0)
+ else:
+ self.warn(pos, 'Unknown address/command combination')
+
+ def decode_reg(self, pos, ann, regid, data):
+ '''Decodes a register.
+ pos -- start and end sample numbers of the register
+ ann -- the annotation number that is used to output the register.
+ regid -- may be either an integer used as a key for the 'regs'
+ dictionary, or a string directly containing a register name.'
+ data -- the register content.
+ '''
+ if type(regid) == int:
+ if (ann == Ann.FIFO_READ) or (ann == Ann.FIFO_WRITE):
+ name = ''
+ elif (ann == Ann.BURST_READB) or (ann == Ann.BURST_WRITEB):
+ # Get the name of the register.
+ if regid not in regsSpaceB:
+ self.warn(pos, 'Unknown register SpaceB')
+ return
+ name = '{} ({:02X})'.format(regsSpaceB[regid], regid)
+ elif (ann == Ann.BURST_READT) or (ann == Ann.BURST_WRITET):
+ # Get the name of the register.
+ if regid not in regsTest:
+ self.warn(pos, 'Unknown register Test')
+ return
+ name = '{} ({:02X})'.format(regsTest[regid], regid)
+ else:
+ # Get the name of the register.
+ if regid not in regsSpaceA:
+ self.warn(pos, 'Unknown register SpaceA')
+ return
+ name = '{} ({:02X})'.format(regsSpaceA[regid], regid)
+ else:
+ name = regid
+
+ if regid == 'STATUS' and ann == Ann.STATUS:
+ label = 'Status'
+ self.decode_status_reg(pos, ann, data, label)
+ else:
+ label = '{}: {}'.format(self.format_command(), name)
+ self.decode_mb_data(pos, ann, data, label)
+
+ def decode_status_reg(self, pos, ann, data, label):
+ '''Decodes the data bytes 'data' of a status register at position
+ 'pos'. The decoded data is prefixed with 'label'.'''
+
+ def decode_mb_data(self, pos, ann, data, label):
+ '''Decodes the data bytes 'data' of a multibyte command at position
+ 'pos'. The decoded data is prefixed with 'label'.'''
+
+ def escape(b):
+ return '{:02X}'.format(b)
+
+ data = ' '.join([escape(b) for b in data])
+ if (ann == Ann.FIFO_WRITE) or (ann == Ann.FIFO_READ):
+ text = '{}{}'.format(label, data)
+ else:
+ text = '{} = {}'.format(label, data)
+ self.putp(pos, ann, text)
+
+ def finish_command(self, pos):
+ '''Decodes the remaining data bytes at position 'pos'.'''
+ if self.cmd == 'Write':
+ self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
+ elif self.cmd == 'Read':
+ self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
+ elif self.cmd == 'WriteB':
+ self.decode_reg(pos, Ann.BURST_WRITEB, self.dat, self.mosi_bytes())
+ elif self.cmd == 'ReadB':
+ self.decode_reg(pos, Ann.BURST_READB, self.dat, self.miso_bytes())
+ elif self.cmd == 'WriteT':
+ self.decode_reg(pos, Ann.BURST_WRITET, self.dat, self.mosi_bytes())
+ elif self.cmd == 'ReadT':
+ self.decode_reg(pos, Ann.BURST_READT, self.dat, self.miso_bytes())
+ elif self.cmd == 'FIFOW':
+ self.decode_reg(pos, Ann.FIFO_WRITE, self.dat, self.mosi_bytes())
+ elif self.cmd == 'FIFOR':
+ self.decode_reg(pos, Ann.FIFO_READ, self.dat, self.miso_bytes())
+ elif self.cmd == 'Cmd':
+ self.decode_reg(pos, Ann.DIRECTCMD, self.dat, self.mosi_bytes())
+ else:
+ self.warn(pos, 'Unhandled command {}'.format(self.cmd))
+
+ def decode(self, ss, es, data):
+ if not self.requirements_met:
+ return
+
+ ptype, data1, data2 = data
+
+ if ptype == 'CS-CHANGE':
+ if data1 is None:
+ if data2 is None:
+ self.requirements_met = False
+ raise ChannelError('CS# pin required.')
+ elif data2 == 1:
+ self.cs_was_released = True
+
+ if data1 == 0 and data2 == 1:
+ # Rising edge, the complete command is transmitted, process
+ # the bytes that were sent after the command byte.
+ if self.cmd:
+ # Check if we got the minimum number of data bytes
+ # after the command byte.
+ if len(self.mb) < self.min:
+ self.warn((ss, ss), 'Missing data bytes')
+ elif self.mb:
+ self.finish_command(Pos(self.ss_mb, self.es_mb))
+
+ self.next()
+ self.cs_was_released = True
+
+ elif ptype == 'DATA' and self.cs_was_released:
+ mosi, miso = data1, data2
+ pos = Pos(ss, es)
+
+ if miso is None or mosi is None:
+ self.requirements_met = False
+ raise ChannelError('Both MISO and MOSI pins are required.')
+
+ if self.first:
+ # Register Space-B Access 0b11111011 0xFB => 'Space B'
+ if mosi == 0xFB:
+ self.first = True
+ # First MOSI byte 'Space B' command.
+ self.decode_command(pos, mosi)
+ # First MISO byte is always the status register.
+ #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ # Register TestAccess Access 0b11111100 0xFC => 'TestAccess'
+ elif mosi == 0xFC:
+ self.first = True
+ # First MOSI byte 'TestAccess' command.
+ self.decode_command(pos, mosi)
+ # First MISO byte is always the status register.
+ #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ else:
+ self.first = False
+ # First MOSI byte is always the command.
+ self.decode_command(pos, mosi)
+ # First MISO byte is always the status register.
+ #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ else:
+ if not self.cmd or len(self.mb) >= self.max:
+ self.warn(pos, 'Excess byte')
+ else:
+ # Collect the bytes after the command byte.
+ if self.ss_mb == -1:
+ self.ss_mb = ss
+ self.es_mb = es
+ self.mb.append(Data(mosi, miso))
annotation_rows = (
('bits', 'Bits', (Ann.BITS,)),
('fields', 'Fields', (Ann.CMD, Ann.DATA)),
- ('description', 'Description', (Ann.DESC,)),
+ ('descriptions', 'Descriptions', (Ann.DESC,)),
)
def __init__(self):
def put_desc(self, ss, es, cmd, data):
if cmd == -1:
return
- if META[cmd]:
+ if cmd in META:
self.put(ss, es, self.out_ann, [Ann.DESC,
['%s: %s' % (META[cmd]['name'].strip(), META[cmd]['desc'])]])
else:
('position', 'Position')
)
annotation_rows = (
- ('speed', 'Speed', (0,)),
- ('position', 'Position', (1,)),
+ ('speeds', 'Speeds', (0,)),
+ ('positions', 'Positions', (1,)),
)
def __init__(self):
## 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
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
## 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
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import math
('address', 'Address'),
('data-write', 'Data write'),
('data-read', 'Data read'),
- ('debug', 'Debug'),
+ ('debug-msg', 'Debug message'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
import sigrokdecode as srd
+NUM_OUTPUT_CHANNELS = 8
+
+def logic_channels(num_channels):
+ l = []
+ for i in range(num_channels):
+ l.append(tuple(['p%d' % i, 'P-port input/output %d' % i]))
+ return tuple(l)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'tca6408a'
annotations = (
('register', 'Register type'),
('value', 'Register value'),
- ('warnings', 'Warning messages'),
+ ('warning', 'Warning'),
)
+ logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS)
annotation_rows = (
('regs', 'Registers', (0, 1)),
('warnings', 'Warnings', (2,)),
self.state = 'IDLE'
self.chip = -1
+ self.logic_output_es = 0
+ self.logic_value = 0
+
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_logic = self.register(srd.OUTPUT_LOGIC)
+
+ def flush(self):
+ self.put_logic_states()
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
+ def put_logic_states(self):
+ if (self.es > self.logic_output_es):
+ data = bytes([self.logic_value])
+ self.put(self.logic_output_es, self.es, self.out_logic, [0, data])
+ self.logic_output_es = self.es
+
def handle_reg_0x00(self, b):
self.putx([1, ['State of inputs: %02X' % b]])
+ # TODO
def handle_reg_0x01(self, b):
- self.putx([1, ['Outputs set: %02X' % b ]])
+ self.put_logic_states()
+ self.putx([1, ['Outputs set: %02X' % b]])
+ self.logic_value = b
def handle_reg_0x02(self, b):
self.putx([1, ['Polarity inverted: %02X' % b]])
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+TDM Audio is an audio serial bus for moving audio data between devices
+(usually on the same board) which can carry one or more channels of data.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+MAX_CHANNELS = 8
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'tdm_audio'
+ name = 'TDM audio'
+ longname = 'Time division multiplex audio'
+ desc = 'TDM multi-channel audio protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Audio']
+ channels = (
+ { 'id': 'clock', 'name': 'Bitclk', 'desc': 'Data bit clock' },
+ { 'id': 'frame', 'name': 'Framesync', 'desc': 'Frame sync' },
+ { 'id': 'data', 'name': 'Data', 'desc': 'Serial data' },
+ )
+ options = (
+ {'id': 'bps', 'desc': 'Bits per sample', 'default': 16 },
+ {'id': 'channels', 'desc': 'Channels per frame', 'default': MAX_CHANNELS },
+ {'id': 'edge', 'desc': 'Clock edge to sample on', 'default': 'rising', 'values': ('rising', 'falling') }
+ )
+ annotations = tuple(('ch%d' % i, 'Ch%d' % i) for i in range(MAX_CHANNELS))
+ annotation_rows = tuple(('ch%d-vals' % i, 'Ch%d' % i, (i,)) for i in range(MAX_CHANNELS))
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.channels = MAX_CHANNELS
+ self.channel = 0
+ self.bitdepth = 16
+ self.bitcount = 0
+ self.samplecount = 0
+ self.lastsync = 0
+ self.lastframe = 0
+ self.data = 0
+ self.ss_block = None
+
+ def metdatadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.bitdepth = self.options['bps']
+ self.edge = self.options['edge']
+
+ def decode(self):
+ while True:
+ # Wait for edge of clock (sample on rising/falling edge).
+ clock, frame, data = self.wait({0: self.edge[0]})
+
+ self.data = (self.data << 1) | data
+ self.bitcount += 1
+
+ if self.ss_block is not None:
+ if self.bitcount >= self.bitdepth:
+ self.bitcount = 0
+ self.channel += 1
+
+ c1 = 'Channel %d' % self.channel
+ c2 = 'C%d' % self.channel
+ c3 = '%d' % self.channel
+ if self.bitdepth <= 8:
+ v = '%02x' % self.data
+ elif self.bitdepth <= 16:
+ v = '%04x' % self.data
+ else:
+ v = '%08x' % self.data
+
+ if self.channel < self.channels:
+ ch = self.channel
+ else:
+ ch = 0
+
+ self.put(self.ss_block, self.samplenum, self.out_ann,
+ [ch, ['%s: %s' % (c1, v), '%s: %s' % (c2, v),
+ '%s: %s' % (c3, v)]])
+ self.data = 0
+ self.ss_block = self.samplenum
+ self.samplecount += 1
+
+ # Check for new frame.
+ # Note, frame may be a single clock, or active for the first
+ # sample in the frame.
+ if frame != self.lastframe and frame == 1:
+ self.channel = 0
+ self.bitcount = 0
+ self.data = 0
+ if self.ss_block is None:
+ self.ss_block = 0
+
+ self.lastframe = frame
else:
return '%f' % t
+def terse_times(t, fmt):
+ # Strictly speaking these variants are not used in the current
+ # implementation, but can reduce diffs during future maintenance.
+ if fmt == 'full':
+ return [normalize_time(t)]
+ # End of "forward compatibility".
+
+ if fmt == 'samples':
+ # See below. No unit text, on purpose.
+ return ['{:d}'.format(t)]
+
+ # Use caller specified scale, or automatically find one.
+ scale, unit = None, None
+ if fmt == 'terse-auto':
+ if abs(t) >= 1e0:
+ scale, unit = 1e0, 's'
+ elif abs(t) >= 1e-3:
+ scale, unit = 1e3, 'ms'
+ elif abs(t) >= 1e-6:
+ scale, unit = 1e6, 'us'
+ elif abs(t) >= 1e-9:
+ scale, unit = 1e9, 'ns'
+ elif abs(t) >= 1e-12:
+ scale, unit = 1e12, 'ps'
+ # Beware! Uses unit-less text when the user picked the scale. For
+ # more consistent output with less clutter, thus faster navigation
+ # by humans. Can also un-hide text at higher distance zoom levels.
+ elif fmt == 'terse-s':
+ scale, unit = 1e0, ''
+ elif fmt == 'terse-ms':
+ scale, unit = 1e3, ''
+ elif fmt == 'terse-us':
+ scale, unit = 1e6, ''
+ elif fmt == 'terse-ns':
+ scale, unit = 1e9, ''
+ elif fmt == 'terse-ps':
+ scale, unit = 1e12, ''
+ if scale:
+ t *= scale
+ return ['{:.0f}{}'.format(t, unit), '{:.0f}'.format(t)]
+
+ # Unspecified format, and nothing auto-detected.
+ return ['{:f}'.format(t)]
+
+class Pin:
+ (DATA,) = range(1)
+
+class Ann:
+ (TIME, TERSE, AVG, DELTA,) = range(4)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'timing'
)
annotations = (
('time', 'Time'),
+ ('terse', 'Terse'),
('average', 'Average'),
('delta', 'Delta'),
)
annotation_rows = (
- ('time', 'Time', (0,)),
- ('average', 'Average', (1,)),
- ('delta', 'Delta', (2,)),
+ ('times', 'Times', (Ann.TIME, Ann.TERSE,)),
+ ('averages', 'Averages', (Ann.AVG,)),
+ ('deltas', 'Deltas', (Ann.DELTA,)),
)
options = (
{ 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 },
- { 'id': 'edge', 'desc': 'Edges to check', 'default': 'any', 'values': ('any', 'rising', 'falling') },
- { 'id': 'delta', 'desc': 'Show delta from last', 'default': 'no', 'values': ('yes', 'no') },
+ { 'id': 'edge', 'desc': 'Edges to check',
+ 'default': 'any', 'values': ('any', 'rising', 'falling') },
+ { 'id': 'delta', 'desc': 'Show delta from last',
+ 'default': 'no', 'values': ('yes', 'no') },
+ { 'id': 'format', 'desc': 'Format of \'time\' annotation',
+ 'default': 'full', 'values': ('full', 'terse-auto',
+ 'terse-s', 'terse-ms', 'terse-us', 'terse-ns', 'terse-ps',
+ 'samples') },
)
def __init__(self):
def reset(self):
self.samplerate = None
- self.last_samplenum = None
- self.last_n = deque()
- self.chunks = 0
- self.level_changed = False
- self.last_t = None
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.edge = self.options['edge']
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
+ edge = self.options['edge']
+ avg_period = self.options['avg_period']
+ delta = self.options['delta'] == 'yes'
+ fmt = self.options['format']
+ ss = None
+ last_n = deque()
+ last_t = None
while True:
- if self.edge == 'rising':
- pin = self.wait({0: 'r'})
- elif self.edge == 'falling':
- pin = self.wait({0: 'f'})
+ if edge == 'rising':
+ pin = self.wait({Pin.DATA: 'r'})
+ elif edge == 'falling':
+ pin = self.wait({Pin.DATA: 'f'})
else:
- pin = self.wait({0: 'e'})
+ pin = self.wait({Pin.DATA: 'e'})
- if not self.last_samplenum:
- self.last_samplenum = self.samplenum
+ if not ss:
+ ss = self.samplenum
continue
- samples = self.samplenum - self.last_samplenum
- t = samples / self.samplerate
-
- if t > 0:
- self.last_n.append(t)
- if len(self.last_n) > self.options['avg_period']:
- self.last_n.popleft()
-
- self.put(self.last_samplenum, self.samplenum, self.out_ann,
- [0, [normalize_time(t)]])
- if self.options['avg_period'] > 0:
- self.put(self.last_samplenum, self.samplenum, self.out_ann,
- [1, [normalize_time(sum(self.last_n) / len(self.last_n))]])
- if self.last_t and self.options['delta'] == 'yes':
- self.put(self.last_samplenum, self.samplenum, self.out_ann,
- [2, [normalize_time(t - self.last_t)]])
-
- self.last_t = t
- self.last_samplenum = self.samplenum
+ es = self.samplenum
+ sa = es - ss
+ t = sa / self.samplerate
+
+ if fmt == 'full':
+ cls, txt = Ann.TIME, [normalize_time(t)]
+ elif fmt == 'samples':
+ cls, txt = Ann.TERSE, terse_times(sa, fmt)
+ else:
+ cls, txt = Ann.TERSE, terse_times(t, fmt)
+ if txt:
+ self.put(ss, es, self.out_ann, [cls, txt])
+
+ if avg_period > 0:
+ if t > 0:
+ last_n.append(t)
+ if len(last_n) > avg_period:
+ last_n.popleft()
+ average = sum(last_n) / len(last_n)
+ cls, txt = Ann.AVG, normalize_time(average)
+ self.put(ss, es, self.out_ann, [cls, [txt]])
+ if last_t and delta:
+ cls, txt = Ann.DELTA, normalize_time(t - last_t)
+ self.put(ss, es, self.out_ann, [cls, [txt]])
+
+ last_t = t
+ ss = es
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Pin = SrdIntEnum.from_str('Pin', 'CLK DATA LOAD LDAC')
dacs = {
0: 'DACA',
# a) Falling edge on CLK, and/or
# b) Falling edge on LOAD, and/or
# b) Falling edge on LDAC
- pins = self.wait([{0: 'f'}, {2: 'f'}, {3: 'f'}])
+ pins = self.wait([{Pin.CLK: 'f'}, {Pin.LOAD: 'f'}, {Pin.LDAC: 'f'}])
self.ldac = pins[3]
# Handle those conditions (one or more) that matched this time.
- 'FRAME': The data is always a tuple containing two items: The (integer)
value of the UART data, and a boolean which reflects the validity of the
UART frame.
+ - 'IDLE': The data is always 0.
The <rxtx> field is 0 for RX packets, 1 for TX packets.
'''
# 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.
# 'none' is _not_ allowed as value for 'parity_type'.
-def parity_ok(parity_type, parity_bit, data, num_data_bits):
+def parity_ok(parity_type, parity_bit, data, data_bits):
+
+ if parity_type == 'ignore':
+ return True
# Handle easy cases first (parity bit is always 1 or 0).
if parity_type == 'zero':
class ChannelError(Exception):
pass
+class Ann:
+ RX_DATA, TX_DATA, RX_START, TX_START, RX_PARITY_OK, TX_PARITY_OK, \
+ RX_PARITY_ERR, TX_PARITY_ERR, RX_STOP, TX_STOP, RX_WARN, TX_WARN, \
+ RX_DATA_BIT, TX_DATA_BIT, RX_BREAK, TX_BREAK, RX_PACKET, TX_PACKET = \
+ range(18)
+
+class Bin:
+ RX, TX, RXTX = range(3)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'uart'
)
options = (
{'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
- {'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,
+ {'id': 'data_bits', 'desc': 'Data bits', 'default': 8,
'values': (5, 6, 7, 8, 9)},
- {'id': 'parity_type', 'desc': 'Parity type', 'default': 'none',
- 'values': ('none', 'odd', 'even', 'zero', 'one')},
- {'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes',
- 'values': ('yes', 'no')},
- {'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,
- 'values': (0.0, 0.5, 1.0, 1.5)},
+ {'id': 'parity', 'desc': 'Parity', 'default': 'none',
+ 'values': ('none', 'odd', 'even', 'zero', 'one', 'ignore')},
+ {'id': 'stop_bits', 'desc': 'Stop bits', 'default': 1.0,
+ 'values': (0.0, 0.5, 1.0, 1.5, 2.0)},
{'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
'values': ('lsb-first', 'msb-first')},
{'id': 'format', 'desc': 'Data format', 'default': 'hex',
'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
- {'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
+ {'id': 'invert_rx', 'desc': 'Invert RX', 'default': 'no',
'values': ('yes', 'no')},
- {'id': 'invert_tx', 'desc': 'Invert TX?', 'default': 'no',
+ {'id': 'invert_tx', 'desc': 'Invert TX', 'default': 'no',
'values': ('yes', 'no')},
+ {'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 50},
+ {'id': 'rx_packet_delim', 'desc': 'RX packet delimiter (decimal)',
+ 'default': -1},
+ {'id': 'tx_packet_delim', 'desc': 'TX packet delimiter (decimal)',
+ 'default': -1},
+ {'id': 'rx_packet_len', 'desc': 'RX packet length', 'default': -1},
+ {'id': 'tx_packet_len', 'desc': 'TX packet length', 'default': -1},
)
annotations = (
('rx-data', 'RX data'),
('tx-data', 'TX data'),
- ('rx-start', 'RX start bits'),
- ('tx-start', 'TX start bits'),
- ('rx-parity-ok', 'RX parity OK bits'),
- ('tx-parity-ok', 'TX parity OK bits'),
- ('rx-parity-err', 'RX parity error bits'),
- ('tx-parity-err', 'TX parity error bits'),
- ('rx-stop', 'RX stop bits'),
- ('tx-stop', 'TX stop bits'),
- ('rx-warnings', 'RX warnings'),
- ('tx-warnings', 'TX warnings'),
- ('rx-data-bits', 'RX data bits'),
- ('tx-data-bits', 'TX data bits'),
+ ('rx-start', 'RX start bit'),
+ ('tx-start', 'TX start bit'),
+ ('rx-parity-ok', 'RX parity OK bit'),
+ ('tx-parity-ok', 'TX parity OK bit'),
+ ('rx-parity-err', 'RX parity error bit'),
+ ('tx-parity-err', 'TX parity error bit'),
+ ('rx-stop', 'RX stop bit'),
+ ('tx-stop', 'TX stop bit'),
+ ('rx-warning', 'RX warning'),
+ ('tx-warning', 'TX warning'),
+ ('rx-data-bit', 'RX data bit'),
+ ('tx-data-bit', 'TX data bit'),
('rx-break', 'RX break'),
('tx-break', 'TX break'),
+ ('rx-packet', 'RX packet'),
+ ('tx-packet', 'TX packet'),
)
annotation_rows = (
- ('rx-data', 'RX', (0, 2, 4, 6, 8)),
- ('rx-data-bits', 'RX bits', (12,)),
- ('rx-warnings', 'RX warnings', (10,)),
- ('rx-break', 'RX break', (14,)),
- ('tx-data', 'TX', (1, 3, 5, 7, 9)),
- ('tx-data-bits', 'TX bits', (13,)),
- ('tx-warnings', 'TX warnings', (11,)),
- ('tx-break', 'TX break', (15,)),
+ ('rx-data-bits', 'RX bits', (Ann.RX_DATA_BIT,)),
+ ('rx-data-vals', 'RX data', (Ann.RX_DATA, Ann.RX_START, Ann.RX_PARITY_OK, Ann.RX_PARITY_ERR, Ann.RX_STOP)),
+ ('rx-warnings', 'RX warnings', (Ann.RX_WARN,)),
+ ('rx-breaks', 'RX breaks', (Ann.RX_BREAK,)),
+ ('rx-packets', 'RX packets', (Ann.RX_PACKET,)),
+ ('tx-data-bits', 'TX bits', (Ann.TX_DATA_BIT,)),
+ ('tx-data-vals', 'TX data', (Ann.TX_DATA, Ann.TX_START, Ann.TX_PARITY_OK, Ann.TX_PARITY_ERR, Ann.TX_STOP)),
+ ('tx-warnings', 'TX warnings', (Ann.TX_WARN,)),
+ ('tx-breaks', 'TX breaks', (Ann.TX_BREAK,)),
+ ('tx-packets', 'TX packets', (Ann.TX_PACKET,)),
)
binary = (
('rx', 'RX dump'),
s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
+ def putx_packet(self, rxtx, data):
+ s, halfbit = self.ss_packet[rxtx], self.bit_width / 2.0
+ self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
+
def putpx(self, rxtx, data):
s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)
def reset(self):
self.samplerate = None
- self.samplenum = 0
self.frame_start = [-1, -1]
self.frame_valid = [None, None]
+ self.cur_frame_bit = [None, None]
self.startbit = [-1, -1]
self.cur_data_bit = [0, 0]
self.datavalue = [0, 0]
self.paritybit = [-1, -1]
- self.stopbit1 = [-1, -1]
+ self.stopbits = [[], []]
self.startsample = [-1, -1]
self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
self.databits = [[], []]
self.break_start = [None, None]
+ self.packet_cache = [[], []]
+ self.ss_packet, self.es_packet = [None, None], [None, None]
+ self.idle_start = [None, None]
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.bw = (self.options['num_data_bits'] + 7) // 8
+ self.bw = (self.options['data_bits'] + 7) // 8
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
def get_sample_point(self, rxtx, bitnum):
# Determine absolute sample number of a bit slot's sample point.
- # 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).
- # The samples within bit are 0, 1, ..., (bit_width - 1), therefore
- # index of the middle sample within bit window is (bit_width - 1) / 2.
- bitpos = self.frame_start[rxtx] + (self.bit_width - 1) / 2.0
+ # Counts for UART bits start from 0 (0 = start bit, 1..x = data,
+ # x+1 = parity bit (if used) or the first stop bit, and so on).
+ # Accept a position in the range of 1-99% of the full bit width.
+ # Assume 50% for invalid input specs for backwards compatibility.
+ perc = self.options['sample_point'] or 50
+ if not perc or perc not in range(1, 100):
+ perc = 50
+ perc /= 100.0
+ bitpos = (self.bit_width - 1) * perc
+ bitpos += self.frame_start[rxtx]
bitpos += bitnum * self.bit_width
return bitpos
# Save the sample number where the start bit begins.
self.frame_start[rxtx] = self.samplenum
self.frame_valid[rxtx] = True
+ self.cur_frame_bit[rxtx] = 0
- self.state[rxtx] = 'GET START BIT'
+ self.advance_state(rxtx, signal)
def get_start_bit(self, rxtx, signal):
self.startbit[rxtx] = signal
+ self.cur_frame_bit[rxtx] += 1
# The startbit must be 0. If not, we report an error and wait
# for the next start bit (assuming this one was spurious).
if self.startbit[rxtx] != 0:
self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
- self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
+ self.putg([Ann.RX_WARN + rxtx, ['Frame error', 'Frame err', 'FE']])
self.frame_valid[rxtx] = False
es = self.samplenum + ceil(self.bit_width / 2.0)
self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
(self.datavalue[rxtx], self.frame_valid[rxtx])])
- self.state[rxtx] = 'WAIT FOR START BIT'
+ self.advance_state(rxtx, signal, fatal = True, idle = es)
return
+ # Reset internal state for the pending UART frame.
self.cur_data_bit[rxtx] = 0
self.datavalue[rxtx] = 0
+ self.paritybit[rxtx] = -1
+ self.stopbits[rxtx].clear()
self.startsample[rxtx] = -1
+ self.databits[rxtx].clear()
self.putp(['STARTBIT', rxtx, self.startbit[rxtx]])
- self.putg([rxtx + 2, ['Start bit', 'Start', 'S']])
+ self.putg([Ann.RX_START + rxtx, ['Start bit', 'Start', 'S']])
- self.state[rxtx] = 'GET DATA BITS'
+ self.advance_state(rxtx, signal)
+
+ def handle_packet(self, rxtx):
+ d = 'rx' if (rxtx == RX) else 'tx'
+ delim = self.options[d + '_packet_delim']
+ plen = self.options[d + '_packet_len']
+ if delim == -1 and plen == -1:
+ return
+
+ # Cache data values until we see the delimiter and/or the specified
+ # packet length has been reached (whichever happens first).
+ if len(self.packet_cache[rxtx]) == 0:
+ self.ss_packet[rxtx] = self.startsample[rxtx]
+ self.packet_cache[rxtx].append(self.datavalue[rxtx])
+ if self.datavalue[rxtx] == delim or len(self.packet_cache[rxtx]) == plen:
+ self.es_packet[rxtx] = self.samplenum
+ s = ''
+ for b in self.packet_cache[rxtx]:
+ s += self.format_value(b)
+ if self.options['format'] != 'ascii':
+ s += ' '
+ if self.options['format'] != 'ascii' and s[-1] == ' ':
+ s = s[:-1] # Drop trailing space.
+ self.putx_packet(rxtx, [Ann.RX_PACKET + rxtx, [s]])
+ self.packet_cache[rxtx] = []
def get_data_bits(self, rxtx, signal):
# Save the sample number of the middle of the first data bit.
if self.startsample[rxtx] == -1:
self.startsample[rxtx] = self.samplenum
- self.putg([rxtx + 12, ['%d' % signal]])
+ self.putg([Ann.RX_DATA_BIT + rxtx, ['%d' % signal]])
# Store individual data bits and their start/end samplenumbers.
s, halfbit = self.samplenum, int(self.bit_width / 2)
self.databits[rxtx].append([signal, s - halfbit, s + halfbit])
+ self.cur_frame_bit[rxtx] += 1
# Return here, unless we already received all data bits.
self.cur_data_bit[rxtx] += 1
- if self.cur_data_bit[rxtx] < self.options['num_data_bits']:
+ if self.cur_data_bit[rxtx] < self.options['data_bits']:
return
# Convert accumulated data bits to a data value.
self.putx(rxtx, [rxtx, [formatted]])
bdata = b.to_bytes(self.bw, byteorder='big')
- self.putbin(rxtx, [rxtx, bdata])
- self.putbin(rxtx, [2, bdata])
+ self.putbin(rxtx, [Bin.RX + rxtx, bdata])
+ self.putbin(rxtx, [Bin.RXTX, bdata])
+
+ self.handle_packet(rxtx)
self.databits[rxtx] = []
- # Advance to either reception of the parity bit, or reception of
- # the STOP bits if parity is not applicable.
- self.state[rxtx] = 'GET PARITY BIT'
- if self.options['parity_type'] == 'none':
- self.state[rxtx] = 'GET STOP BITS'
+ self.advance_state(rxtx, signal)
def format_value(self, v):
# Format value 'v' according to configured options.
# Reflects the user selected kind of representation, as well as
# the number of data bits in the UART frames.
- fmt, bits = self.options['format'], self.options['num_data_bits']
+ fmt, bits = self.options['format'], self.options['data_bits']
# Assume "is printable" for values from 32 to including 126,
# below 32 is "control" and thus not printable, above 127 is
def get_parity_bit(self, rxtx, signal):
self.paritybit[rxtx] = signal
+ self.cur_frame_bit[rxtx] += 1
- if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
- self.datavalue[rxtx], self.options['num_data_bits']):
+ if parity_ok(self.options['parity'], self.paritybit[rxtx],
+ self.datavalue[rxtx], self.options['data_bits']):
self.putp(['PARITYBIT', rxtx, self.paritybit[rxtx]])
- self.putg([rxtx + 4, ['Parity bit', 'Parity', 'P']])
+ self.putg([Ann.RX_PARITY_OK + rxtx, ['Parity bit', 'Parity', 'P']])
else:
# TODO: Return expected/actual parity values.
self.putp(['PARITY ERROR', rxtx, (0, 1)]) # FIXME: Dummy tuple...
- self.putg([rxtx + 6, ['Parity error', 'Parity err', 'PE']])
+ self.putg([Ann.RX_PARITY_ERR + rxtx, ['Parity error', 'Parity err', 'PE']])
self.frame_valid[rxtx] = False
- self.state[rxtx] = 'GET STOP BITS'
+ self.advance_state(rxtx, signal)
- # TODO: Currently only supports 1 stop bit.
def get_stop_bits(self, rxtx, signal):
- self.stopbit1[rxtx] = signal
+ self.stopbits[rxtx].append(signal)
+ self.cur_frame_bit[rxtx] += 1
# Stop bits must be 1. If not, we report an error.
- if self.stopbit1[rxtx] != 1:
- self.putp(['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
- self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
+ if signal != 1:
+ self.putp(['INVALID STOPBIT', rxtx, signal])
+ self.putg([Ann.RX_WARN + rxtx, ['Frame error', 'Frame err', 'FE']])
self.frame_valid[rxtx] = False
- self.putp(['STOPBIT', rxtx, self.stopbit1[rxtx]])
- self.putg([rxtx + 4, ['Stop bit', 'Stop', 'T']])
+ self.putp(['STOPBIT', rxtx, signal])
+ self.putg([Ann.RX_STOP + rxtx, ['Stop bit', 'Stop', 'T']])
+ # Postprocess the UART frame after all STOP bits were seen.
+ if len(self.stopbits[rxtx]) < self.options['stop_bits']:
+ return
+ self.advance_state(rxtx, signal)
+
+ def advance_state(self, rxtx, signal = None, fatal = False, idle = None):
+ # Advances the protocol decoder's internal state for all regular
+ # UART frame inspection. Deals with either edges, sample points,
+ # or other .wait() conditions. Also gracefully handles extreme
+ # undersampling. Each turn takes one .wait() call which in turn
+ # corresponds to at least one sample. That is why as many state
+ # transitions are done here as required within a single call.
+ frame_end = self.frame_start[rxtx] + self.frame_len_sample_count
+ if idle is not None:
+ # When requested by the caller, start another (potential)
+ # IDLE period after the caller specified position.
+ self.idle_start[rxtx] = idle
+ if fatal:
+ # When requested by the caller, don't advance to the next
+ # UART frame's field, but to the start of the next START bit
+ # instead.
+ self.state[rxtx] = 'WAIT FOR START BIT'
+ return
+ # Advance to the next UART frame's field that we expect. Cope
+ # with absence of optional fields. Force scan for next IDLE
+ # after the (optional) STOP bit field, so that callers need
+ # not deal with optional field presence. Also handles the cases
+ # where the decoder navigates to edges which are not strictly
+ # a field's sampling point.
+ if self.state[rxtx] == 'WAIT FOR START BIT':
+ self.state[rxtx] = 'GET START BIT'
+ return
+ if self.state[rxtx] == 'GET START BIT':
+ self.state[rxtx] = 'GET DATA BITS'
+ return
+ if self.state[rxtx] == 'GET DATA BITS':
+ self.state[rxtx] = 'GET PARITY BIT'
+ if self.options['parity'] != 'none':
+ return
+ # FALLTHROUGH
+ if self.state[rxtx] == 'GET PARITY BIT':
+ self.state[rxtx] = 'GET STOP BITS'
+ if self.options['stop_bits']:
+ return
+ # FALLTHROUGH
+ if self.state[rxtx] == 'GET STOP BITS':
+ # Postprocess the previously received UART frame. Advance
+ # the read position to after the frame's last bit time. So
+ # that the start of the next START bit won't fall into the
+ # end of the previously received UART frame. This improves
+ # robustness in the presence of glitchy input data.
+ ss = self.frame_start[rxtx]
+ es = self.samplenum + ceil(self.bit_width / 2.0)
+ self.handle_frame(rxtx, ss, es)
+ self.state[rxtx] = 'WAIT FOR START BIT'
+ self.idle_start[rxtx] = frame_end
+ return
+ # Unhandled state, actually a programming error. Emit diagnostics?
+ self.state[rxtx] = 'WAIT FOR START BIT'
+
+ def handle_frame(self, rxtx, ss, es):
# Pass the complete UART frame to upper layers.
- es = self.samplenum + ceil(self.bit_width / 2.0)
- self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
+ self.putpse(ss, es, ['FRAME', rxtx,
(self.datavalue[rxtx], self.frame_valid[rxtx])])
- self.state[rxtx] = 'WAIT FOR START BIT'
+ def handle_idle(self, rxtx, ss, es):
+ self.putpse(ss, es, ['IDLE', rxtx, 0])
- def handle_break(self, rxtx):
- self.putpse(self.frame_start[rxtx], self.samplenum,
- ['BREAK', rxtx, 0])
- self.putgse(self.frame_start[rxtx], self.samplenum,
- [rxtx + 14, ['Break condition', 'Break', 'Brk', 'B']])
+ def handle_break(self, rxtx, ss, es):
+ self.putpse(ss, es, ['BREAK', rxtx, 0])
+ self.putgse(ss, es, [Ann.RX_BREAK + rxtx,
+ ['Break condition', 'Break', 'Brk', 'B']])
self.state[rxtx] = 'WAIT FOR START BIT'
def get_wait_cond(self, rxtx, inv):
state = self.state[rxtx]
if state == 'WAIT FOR START BIT':
return {rxtx: 'r' if inv else 'f'}
- if state == 'GET START BIT':
- bitnum = 0
- elif state == 'GET DATA BITS':
- bitnum = 1 + self.cur_data_bit[rxtx]
- elif state == 'GET PARITY BIT':
- bitnum = 1 + self.options['num_data_bits']
- elif state == 'GET STOP BITS':
- bitnum = 1 + self.options['num_data_bits']
- bitnum += 0 if self.options['parity_type'] == 'none' else 1
- want_num = ceil(self.get_sample_point(rxtx, bitnum))
- return {'skip': want_num - self.samplenum}
+ if state in ('GET START BIT', 'GET DATA BITS',
+ 'GET PARITY BIT', 'GET STOP BITS'):
+ bitnum = self.cur_frame_bit[rxtx]
+ # TODO: Currently does not support half STOP bits.
+ want_num = ceil(self.get_sample_point(rxtx, bitnum))
+ return {'skip': want_num - self.samplenum}
+
+ def get_idle_cond(self, rxtx, inv):
+ # Return a condition that corresponds to the (expected) end of
+ # the next frame, assuming that it will be an "idle frame"
+ # (constant high input level for the frame's length).
+ if self.idle_start[rxtx] is None:
+ return None
+ end_of_frame = self.idle_start[rxtx] + self.frame_len_sample_count
+ if end_of_frame < self.samplenum:
+ return None
+ return {'skip': end_of_frame - self.samplenum}
def inspect_sample(self, rxtx, signal, inv):
# Inspect a sample returned by .wait() for the specified UART line.
return
diff = self.samplenum - self.break_start[rxtx]
if diff >= self.break_min_sample_count:
- self.handle_break(rxtx)
+ ss, es = self.frame_start[rxtx], self.samplenum
+ self.handle_break(rxtx, ss, es)
self.break_start[rxtx] = None
+ def inspect_idle(self, rxtx, signal, inv):
+ # Check each edge and each period of stable input (either level).
+ # Can derive the "idle frame period has passed" condition.
+ if inv:
+ signal = not signal
+ if not signal:
+ # Low input, cease inspection.
+ self.idle_start[rxtx] = None
+ return
+ # High input, either just reached, or still stable.
+ if self.idle_start[rxtx] is None:
+ self.idle_start[rxtx] = self.samplenum
+ diff = self.samplenum - self.idle_start[rxtx]
+ if diff < self.frame_len_sample_count:
+ return
+ ss, es = self.idle_start[rxtx], self.samplenum
+ self.handle_idle(rxtx, ss, es)
+ self.idle_start[rxtx] = es
+
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
has_pin = [self.has_channel(ch) for ch in (RX, TX)]
- if has_pin == [False, False]:
- raise ChannelError('Either TX or RX (or both) pins required.')
+ if not True in has_pin:
+ raise ChannelError('Need at least one of TX or RX pins.')
opt = self.options
inv = [opt['invert_rx'] == 'yes', opt['invert_tx'] == 'yes']
# Determine the number of samples for a complete frame's time span.
# A period of low signal (at least) that long is a break condition.
frame_samples = 1 # START
- frame_samples += self.options['num_data_bits']
- frame_samples += 0 if self.options['parity_type'] == 'none' else 1
- frame_samples += self.options['num_stop_bits']
+ frame_samples += self.options['data_bits']
+ frame_samples += 0 if self.options['parity'] == 'none' else 1
+ frame_samples += self.options['stop_bits']
frame_samples *= self.bit_width
- self.break_min_sample_count = ceil(frame_samples)
+ self.frame_len_sample_count = ceil(frame_samples)
+ self.break_min_sample_count = self.frame_len_sample_count
cond_edge_idx = [None] * len(has_pin)
+ cond_idle_idx = [None] * len(has_pin)
while True:
conds = []
conds.append(self.get_wait_cond(RX, inv[RX]))
cond_edge_idx[RX] = len(conds)
conds.append({RX: 'e'})
+ cond_idle_idx[RX] = None
+ idle_cond = self.get_idle_cond(RX, inv[RX])
+ if idle_cond:
+ cond_idle_idx[RX] = len(conds)
+ conds.append(idle_cond)
if has_pin[TX]:
cond_data_idx[TX] = len(conds)
conds.append(self.get_wait_cond(TX, inv[TX]))
cond_edge_idx[TX] = len(conds)
conds.append({TX: 'e'})
+ cond_idle_idx[TX] = None
+ idle_cond = self.get_idle_cond(TX, inv[TX])
+ if idle_cond:
+ cond_idle_idx[TX] = len(conds)
+ conds.append(idle_cond)
(rx, tx) = self.wait(conds)
if cond_data_idx[RX] is not None and self.matched[cond_data_idx[RX]]:
self.inspect_sample(RX, rx, inv[RX])
if cond_edge_idx[RX] is not None and self.matched[cond_edge_idx[RX]]:
self.inspect_edge(RX, rx, inv[RX])
+ self.inspect_idle(RX, rx, inv[RX])
+ if cond_idle_idx[RX] is not None and self.matched[cond_idle_idx[RX]]:
+ self.inspect_idle(RX, rx, inv[RX])
if cond_data_idx[TX] is not None and self.matched[cond_data_idx[TX]]:
self.inspect_sample(TX, tx, inv[TX])
if cond_edge_idx[TX] is not None and self.matched[cond_edge_idx[TX]]:
self.inspect_edge(TX, tx, inv[TX])
+ self.inspect_idle(TX, tx, inv[TX])
+ if cond_idle_idx[TX] is not None and self.matched[cond_idle_idx[TX]]:
+ self.inspect_idle(TX, tx, inv[TX])
('crc', 'Checksum'),
('eop', 'End Of Packet'),
('sym', '4b5b symbols'),
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
('src', 'Source Message'),
('snk', 'Sink Message'),
('payload', 'Payload'),
)
annotation_rows = (
('4b5b', 'Symbols', (7,)),
- ('phase', 'Parts', (1, 2, 3, 4, 5, 6)),
- ('payload', 'Payload', (11,)),
- ('type', 'Type', (0, 9, 10)),
+ ('parts', 'Parts', (1, 2, 3, 4, 5, 6)),
+ ('payloads', 'Payloads', (11,)),
+ ('types', 'Types', (0, 9, 10)),
('warnings', 'Warnings', (8,)),
- ('text', 'Full text', (12,)),
+ ('texts', 'Full text', (12,)),
)
binary = (
('raw-data', 'RAW binary data'),
('request-setup-write', 'Setup: Host-to-device'),
('request-bulk-read', 'Bulk: Device-to-host'),
('request-bulk-write', 'Bulk: Host-to-device'),
- ('errors', 'Unexpected packets'),
+ ('error', 'Unexpected packet'),
)
annotation_rows = (
('request-setup', 'USB SETUP', (0, 1)),
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2012-2020 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
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
'''
OUTPUT_PYTHON format:
'SE1': [3, ['SE1', '1']],
}
+St = SrdIntEnum.from_str('St', 'IDLE GET_BIT GET_EOP WAIT_IDLE')
+
class SamplerateError(Exception):
pass
self.samplerate = None
self.oldsym = 'J' # The "idle" state is J.
self.ss_block = None
- self.samplenum = 0
self.bitrate = None
self.bitwidth = None
self.samplepos = None
self.edgepins = None
self.consecutive_ones = 0
self.bits = None
- self.state = 'IDLE'
+ self.state = St.IDLE
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.set_new_target_samplenum()
self.putpx(['SOP', None])
self.putx([4, ['SOP', 'S']])
- self.state = 'GET BIT'
+ self.state = St.GET_BIT
def handle_bit(self, b):
if self.consecutive_ones == 6:
else:
self.putpb(['ERR', None])
self.putb([8, ['Bit stuff error', 'BS ERR', 'B']])
- self.state = 'IDLE'
+ self.state = St.IDLE
else:
# Normal bit (not a stuff bit).
self.putpb(['BIT', b])
# Got an EOP.
self.putpm(['EOP', None])
self.putm([5, ['EOP', 'E']])
- self.state = 'WAIT IDLE'
+ self.state = St.WAIT_IDLE
else:
self.putpm(['ERR', None])
self.putm([8, ['EOP Error', 'EErr', 'E']])
- self.state = 'IDLE'
+ self.state = St.IDLE
def get_bit(self, sym):
self.set_new_target_samplenum()
self.oldsym = sym
if sym == 'SE0':
# Start of an EOP. Change state, save edge
- self.state = 'GET EOP'
+ self.state = St.GET_EOP
self.ss_block = self.samplenum_lastedge
else:
self.handle_bit(b)
if len(self.bits) == 16 and self.bits == '0000000100111100':
# Sync and low-speed PREamble seen
self.putpx(['EOP', None])
- self.state = 'IDLE'
+ self.state = St.IDLE
self.signalling = 'low-speed-rp'
self.update_bitrate()
self.oldsym = 'J'
self.signalling = 'low-speed'
self.update_bitrate()
self.oldsym = 'J'
- self.state = 'IDLE'
+ self.state = St.IDLE
def decode(self):
if not self.samplerate:
while True:
# State machine.
- if self.state == 'IDLE':
+ if self.state == St.IDLE:
# Wait for any edge on either DP and/or DM.
pins = self.wait([{0: 'e'}, {1: 'e'}])
sym = symbols[self.signalling][pins]
if sym == 'SE0':
self.samplenum_lastedge = self.samplenum
- self.state = 'WAIT IDLE'
+ self.state = St.WAIT_IDLE
else:
self.wait_for_sop(sym)
self.edgepins = pins
- elif self.state in ('GET BIT', 'GET EOP'):
+ elif self.state in (St.GET_BIT, St.GET_EOP):
# Wait until we're in the middle of the desired bit.
self.edgepins = self.wait([{'skip': self.samplenum_edge - self.samplenum}])
pins = self.wait([{'skip': self.samplenum_target - self.samplenum}])
sym = symbols[self.signalling][pins]
- if self.state == 'GET BIT':
+ if self.state == St.GET_BIT:
self.get_bit(sym)
- elif self.state == 'GET EOP':
+ elif self.state == St.GET_EOP:
self.get_eop(sym)
- elif self.state == 'WAIT IDLE':
+ elif self.state == St.WAIT_IDLE:
# Skip "all-low" input. Wait for high level on either DP or DM.
pins = self.wait()
while not pins[0] and not pins[1]:
'default': 4, 'values': (1, 2, 4, 8, 16, 32)},
)
annotations = (
- ('bits', 'Bits'),
+ ('bit', 'Bit'),
('state', 'State'),
)
annotation_rows = (
- ('bits', 'Binary value', (0,)),
- ('state', 'Stream state', (1,)),
+ ('bits', 'Bits', (0,)),
+ ('states', 'Stream states', (1,)),
)
def __init__(self):
0x84: ['WREN', 4, lambda _: ''],
0x85: ['RCL', 5, lambda _: ''],
0x86: ['READ', 6, lambda v: '0x%x' % v],
- 0x87: ['READ', 7, lambda v: '0x%x' % v],
+ 0x87: ['READ', 6, lambda v: '0x%x' % v],
+ # 0x86/0x87 are both valid READ commands (bit 0 is "don't care").
}
class Decoder(srd.Decoder):
('wren', 'Write enable'),
('rcl', 'Recall EEPROM data into RAM'),
('read', 'Data read from RAM'),
- ('read', 'Data read from RAM'),
)
def __init__(self):
outputs = []
tags = ['Networking']
annotations = (
- ('fieldnames-and-values', 'XFP structure field names and values'),
- ('fields', 'XFP structure fields'),
+ ('field-name-and-val', 'Field name and value'),
+ ('field-val', 'Field value'),
+ )
+ annotation_rows = (
+ ('field-names-and-vals', 'Field names and values', (0,)),
+ ('field-vals', 'Field values', (1,)),
)
def __init__(self):
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Uli Huber
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+XY2-100 is a serial bus for connecting galvo systems to controllers
+
+Details:
+
+http://www.newson.be/doc.php?id=XY2-100
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Uli Huber
+## Copyright (C) 2020 Soeren Apel
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+ann_bit, ann_stat_bit, ann_type, ann_command, ann_parameter, ann_parity, ann_pos, ann_status, ann_warning = range(9)
+frame_type_none, frame_type_command, frame_type_16bit_pos, frame_type_18bit_pos = range(4)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'xy2-100'
+ name = 'XY2-100'
+ longname = 'XY2-100(E) and XY-200(E) galvanometer protocol'
+ desc = 'Serial protocol for galvanometer positioning in laser systems'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+
+ tags = ['Embedded/industrial']
+
+ channels = (
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
+ {'id': 'sync', 'name': 'SYNC', 'desc': 'Sync'},
+ {'id': 'data', 'name': 'DATA', 'desc': 'X, Y or Z axis data'},
+ )
+ optional_channels = (
+ {'id': 'status', 'name': 'STAT', 'desc': 'X, Y or Z axis status'},
+ )
+
+ annotations = (
+ ('bit', 'Data Bit'),
+ ('stat_bit', 'Status Bit'),
+ ('type', 'Frame Type'),
+ ('command', 'Command'),
+ ('parameter', 'Parameter'),
+ ('parity', 'Parity'),
+ ('position', 'Position'),
+ ('status', 'Status'),
+ ('warning', 'Human-readable warnings'),
+ )
+ annotation_rows = (
+ ('bits', 'Data Bits', (ann_bit,)),
+ ('stat_bits', 'Status Bits', (ann_stat_bit,)),
+ ('data', 'Data', (ann_type, ann_command, ann_parameter, ann_parity)),
+ ('positions', 'Positions', (ann_pos,)),
+ ('statuses', 'Statuses', (ann_status,)),
+ ('warnings', 'Warnings', (ann_warning,)),
+ )
+
+ def __init__(self):
+ self.samplerate = None
+ self.reset()
+
+ def reset(self):
+ self.bits = []
+ self.stat_bits = []
+ self.stat_skip_bit = True
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def put_ann(self, ss, es, ann_class, value):
+ self.put(ss, es, self.out_ann, [ann_class, value])
+
+ def process_bit(self, sync, bit_ss, bit_es, bit_value):
+ self.put_ann(bit_ss, bit_es, ann_bit, ['%d' % bit_value])
+ self.bits.append((bit_ss, bit_es, bit_value))
+
+ if sync == 0:
+ if len(self.bits) < 20:
+ self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Not enough data bits'])
+ self.reset()
+ return
+
+ # Bit structure:
+ # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+ # T --------------- 18-bit pos ----------------- PARITY or
+ # -TYPE-- ------------ 16-bit pos -------------- PARITY or
+ # -TYPE-- -8-bit command -8-bit parameter value- PARITY
+
+ # Calculate parity, excluding the parity bit itself
+ parity = 0
+ for ss, es, value in self.bits[:-1]:
+ parity ^= value
+
+ par_ss, par_es, par_value = self.bits[19]
+ parity_even = 0
+ parity_odd = 0
+ if (par_value == parity):
+ parity_even = 1
+ else:
+ parity_odd = 1
+
+ type_1_value = self.bits[0][2]
+ type_3_value = (self.bits[0][2] << 2) | (self.bits[1][2] << 1) | self.bits[2][2]
+
+ # Determine frame type
+ type = frame_type_none
+ parity_status = ['X', 'Unknown']
+ type_ss = self.bits[0][0]
+ type_es = self.bits[2][1]
+
+ ### 18-bit position
+ if (type_1_value == 1) and (parity_odd == 1):
+ type = frame_type_18bit_pos
+ type_es = self.bits[0][1]
+ self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+ ### 16-bit position
+ elif (type_3_value == 1):
+ type = frame_type_16bit_pos
+ if (parity_even == 1):
+ parity_status = ['OK']
+ else:
+ parity_status = ['NOK']
+ self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Parity error', 'PE'])
+ ### Command
+ elif (type_3_value == 7) and (parity_even == 1):
+ type = frame_type_command
+ self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+ ### Other
+ else:
+ self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Error', 'Unknown command or parity error'])
+ self.reset()
+ return
+
+ # Output command and parity annotations
+ if (type == frame_type_16bit_pos):
+ self.put_ann(type_ss, type_es, ann_type, ['16 bit Position Frame', '16 bit Pos', 'Pos', 'P'])
+ if (type == frame_type_18bit_pos):
+ self.put_ann(type_ss, type_es, ann_type, ['18 bit Position Frame', '18 bit Pos', 'Pos', 'P'])
+ if (type == frame_type_command):
+ self.put_ann(type_ss, type_es, ann_type, ['Command Frame', 'Command', 'C'])
+
+ self.put_ann(par_ss, par_es, ann_parity, parity_status)
+
+ # Output value
+ if (type == frame_type_16bit_pos) or (type == frame_type_18bit_pos):
+ pos = 0
+
+ if (type == frame_type_16bit_pos):
+ count = 15
+ for ss, es, value in self.bits[3:19]:
+ pos |= value << count
+ count -= 1
+ pos = pos if pos < 32768 else pos - 65536
+ else:
+ count = 17
+ for ss, es, value in self.bits[3:19]:
+ pos |= value << count
+ count -= 1
+ pos = pos if pos < 131072 else pos - 262144
+
+ self.put_ann(type_es, par_ss, ann_pos, ['%d' % pos])
+
+ if (type == frame_type_command):
+ count = 7
+ cmd = 0
+ cmd_es = 0
+ for ss, es, value in self.bits[3:11]:
+ cmd |= value << count
+ count -= 1
+ cmd_es = es
+ self.put_ann(type_es, cmd_es, ann_command, ['Command 0x%X' % cmd, 'Cmd 0x%X' % cmd, '0x%X' % cmd])
+
+ count = 7
+ param = 0
+ for ss, es, value in self.bits[11:19]:
+ param |= value << count
+ count -= 1
+ self.put_ann(cmd_es, par_ss, ann_parameter, ['Parameter 0x%X / %d' % (param, param), '0x%X / %d' % (param, param),'0x%X' % param])
+
+ self.reset()
+
+ def process_stat_bit(self, sync, bit_ss, bit_es, bit_value):
+ if self.stat_skip_bit:
+ self.stat_skip_bit = False
+ return
+
+ self.put_ann(bit_ss, bit_es, ann_stat_bit, ['%d' % bit_value])
+ self.stat_bits.append((bit_ss, bit_es, bit_value))
+
+ if (sync == 0) and (len(self.stat_bits) == 19):
+ stat_ss = self.stat_bits[0][0]
+ stat_es = self.stat_bits[18][1]
+
+ status = 0
+ count = 18
+ for ss, es, value in self.stat_bits:
+ status |= value << count
+ count -= 1
+ self.put_ann(stat_ss, stat_es, ann_status, ['Status 0x%X' % status, '0x%X' % status])
+
+ def decode(self):
+ bit_ss = None
+ bit_es = None
+ bit_value = 0
+ stat_ss = None
+ stat_es = None
+ stat_value = 0
+ sync_value = 0
+ has_stat = self.has_channel(3)
+
+ while True:
+ # Wait for any edge on clk
+ clk, sync, data, stat = self.wait({0: 'e'})
+
+ if clk == 1:
+ stat_value = stat
+
+ bit_es = self.samplenum
+ if bit_ss:
+ self.process_bit(sync_value, bit_ss, bit_es, bit_value)
+ bit_ss = self.samplenum
+ else:
+ bit_value = data
+ sync_value = sync
+
+ stat_es = self.samplenum
+ if stat_ss and has_stat:
+ self.process_stat_bit(sync_value, stat_ss, stat_es, stat_value)
+ stat_ss = self.samplenum
} for i in range(16)
)
annotations = (
- ('addr', 'Memory or I/O address'),
+ ('addr', 'Memory or I/O address'),
('memrd', 'Byte read from memory'),
('memwr', 'Byte written to memory'),
- ('iord', 'Byte read from I/O port'),
- ('iowr', 'Byte written to I/O port'),
+ ('iord', 'Byte read from I/O port'),
+ ('iowr', 'Byte written to I/O port'),
('instr', 'Z80 CPU instruction'),
- ('rop', 'Value of input operand'),
- ('wop', 'Value of output operand'),
- ('warn', 'Warning message'),
+ ('rop', 'Value of input operand'),
+ ('wop', 'Value of output operand'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('addrbus', 'Address bus', (Ann.ADDR,)),
count = di->dec_num_channels;
arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count);
g_array_set_size(arr, count);
- memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
+ if (arr->data)
+ memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
di->old_pins_array = arr;
}
Py_DECREF(py_di_options);
py_di_options = PyDict_New();
PyObject_SetAttrString(di->py_inst, "options", py_di_options);
+ Py_DECREF(py_di_options);
for (l = di->decoder->options; l; l = l->next) {
sdo = l->data;
}
} else if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT64)) {
val_int = g_variant_get_int64(value);
- if (!(py_optval = PyLong_FromLong(val_int))) {
+ if (!(py_optval = PyLong_FromLongLong(val_int))) {
/* ValueError Exception */
PyErr_Clear();
srd_err("Option '%s' has invalid integer value.", sdo->id);
goto err_out;
}
}
- if (PyDict_SetItemString(py_di_options, sdo->id, py_optval) == -1)
+ if (PyDict_SetItemString(py_di_options, sdo->id, py_optval) == -1) {
+ Py_XDECREF(py_optval);
goto err_out;
+ }
/* Not harmful even if we used the default. */
g_hash_table_remove(options, sdo->id);
+ Py_XDECREF(py_optval);
}
if (g_hash_table_size(options) != 0)
srd_warn("Unknown options specified for '%s'", di->inst_id);
ret = SRD_OK;
err_out:
- Py_XDECREF(py_optval);
if (PyErr_Occurred()) {
srd_exception_catch("Stray exception in srd_inst_option_set()");
ret = SRD_ERR_PYTHON;
di->got_new_samples = FALSE;
di->handled_all_samples = FALSE;
di->want_wait_terminate = FALSE;
+ di->communicate_eof = FALSE;
di->decoder_state = SRD_OK;
/*
di->got_new_samples = FALSE;
di->handled_all_samples = FALSE;
di->want_wait_terminate = FALSE;
+ di->communicate_eof = FALSE;
di->decoder_state = SRD_OK;
/* Conditions and mutex got reset after joining the thread. */
}
/** @private */
SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
{
- PyObject *py_res;
+ PyObject *py_res, *py_samplenum;
GSList *l;
struct srd_decoder_inst *next_di;
int ret;
PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
- Py_DecRef(py_res);
+ Py_DECREF(py_res);
/* Set self.samplenum to 0. */
- PyObject_SetAttrString(di->py_inst, "samplenum", PyLong_FromLong(0));
+ py_samplenum = PyLong_FromUnsignedLongLong(0);
+ PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+ Py_DECREF(py_samplenum);
/* Set self.matched to None. */
PyObject_SetAttrString(di->py_inst, "matched", Py_None);
g_slist_free_full(ll, g_free);
}
+ g_slist_free(di->condition_list);
di->condition_list = NULL;
}
oldpins_array_seed(di);
for (i = 0; i < di->dec_num_channels; i++) {
+ if (di->dec_channelmap[i] == -1)
+ continue; /* Ignore unused optional channels. */
byte_offset = di->dec_channelmap[i] / 8;
bit_offset = di->dec_channelmap[i] % 8;
sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
for (i = 0; i < di->dec_num_channels; i++) {
if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
continue;
+ if (di->dec_channelmap[i] == -1)
+ continue; /* Ignore unused optional channels. */
byte_offset = di->dec_channelmap[i] / 8;
bit_offset = di->dec_channelmap[i] % 8;
sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
for (l = cond; l; l = l->next) {
term = l->data;
+ if (term->type == SRD_TERM_ALWAYS_FALSE)
+ return FALSE;
if (!term_matches(di, term, sample_pos))
return FALSE;
}
* Call self.decode(). Only returns if the PD throws an exception.
* "Regular" termination of the decode() method is not expected.
*/
- Py_IncRef(di->py_inst);
+ Py_INCREF(di->py_inst);
srd_dbg("%s: Calling decode().", di->inst_id);
py_res = PyObject_CallMethod(di->py_inst, "decode", NULL);
srd_dbg("%s: decode() terminated.", di->inst_id);
+ /*
+ * Termination with an EOFError exception is accepted to simplify
+ * the implementation of decoders and for backwards compatibility.
+ */
+ if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_EOFError)) {
+ srd_dbg("%s: ignoring EOFError during decode() execution.",
+ di->inst_id);
+ PyErr_Clear();
+ if (!py_res)
+ py_res = Py_None;
+ }
if (!py_res)
di->decoder_state = SRD_ERR;
* decode() will re-start another thread transparently.
*/
srd_dbg("%s: decode() terminated (req %d).", di->inst_id, wanted_term);
- Py_DecRef(py_res);
+ Py_DECREF(py_res);
PyErr_Clear();
PyGILState_Release(gstate);
g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
g_mutex_unlock(&di->data_mutex);
+ /* Flush all PDs in the stack that can be flushed */
+ srd_inst_flush(di);
+
if (di->want_wait_terminate)
return SRD_ERR_TERM_REQ;
return SRD_OK;
}
+
+/**
+ * Flush all data that is pending, bottom decoder first up to the top of the stack.
+ *
+ * @param di The decoder instance to call. Must not be NULL.
+ *
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @private
+ */
+SRD_PRIV int srd_inst_flush(struct srd_decoder_inst *di)
+{
+ PyGILState_STATE gstate;
+ PyObject *py_ret;
+ GSList *l;
+ int ret;
+
+ if (!di)
+ return SRD_ERR_ARG;
+
+ gstate = PyGILState_Ensure();
+ if (PyObject_HasAttrString(di->py_inst, "flush")) {
+ srd_dbg("Calling flush() of instance %s", di->inst_id);
+ py_ret = PyObject_CallMethod(di->py_inst, "flush", NULL);
+ Py_XDECREF(py_ret);
+ }
+ PyGILState_Release(gstate);
+
+ /* Pass the "flush" request to all stacked decoders. */
+ for (l = di->next_di; l; l = l->next) {
+ ret = srd_inst_flush(l->data);
+ if (ret != SRD_OK)
+ return ret;
+ }
+
+ return di->decoder_state;
+}
+
+/**
+ * Communicate the end of the stream of sample data to a decoder instance.
+ *
+ * @param[in] di The decoder instance to call. Must not be NULL.
+ *
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @private
+ */
+SRD_PRIV int srd_inst_send_eof(struct srd_decoder_inst *di)
+{
+ GSList *l;
+ int ret;
+
+ if (!di)
+ return SRD_ERR_ARG;
+
+ /*
+ * Send EOF to the caller specified decoder instance. Only
+ * communicate EOF to currently executing decoders. Never
+ * started or previously finished is perfectly acceptable.
+ */
+ srd_dbg("End of sample data: instance %s.", di->inst_id);
+ if (!di->thread_handle) {
+ srd_dbg("No worker thread, nothing to do.");
+ return SRD_OK;
+ }
+
+ /* Signal the thread about the EOF condition. */
+ g_mutex_lock(&di->data_mutex);
+ di->inbuf = NULL;
+ di->inbuflen = 0;
+ di->got_new_samples = TRUE;
+ di->handled_all_samples = FALSE;
+ di->want_wait_terminate = TRUE;
+ di->communicate_eof = TRUE;
+ g_cond_signal(&di->got_new_samples_cond);
+ g_mutex_unlock(&di->data_mutex);
+
+ /* Only return from here when the condition was handled. */
+ g_mutex_lock(&di->data_mutex);
+ while (!di->handled_all_samples && !di->want_wait_terminate)
+ g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
+ g_mutex_unlock(&di->data_mutex);
+
+ /* Flush the decoder instance which handled EOF. */
+ srd_inst_flush(di);
+
+ /* Pass EOF to all stacked decoders. */
+ for (l = di->next_di; l; l = l->next) {
+ ret = srd_inst_send_eof(l->data);
+ if (ret != SRD_OK)
+ return ret;
+ }
+
+ return SRD_OK;
+}
+
/**
* Terminate current decoder work, prepare for re-use on new input data.
*
srd_inst_reset_state(di);
gstate = PyGILState_Ensure();
- Py_DecRef(di->py_inst);
+ Py_DECREF(di->py_inst);
PyGILState_Release(gstate);
g_free(di->inst_id);
--- /dev/null
+This directory contains a subset of the IRMP project source code. Then
+extends it to allow sigrok protocol decoders to use the IRMP backend.
+See README.txt for information on the IRMP project itself.
--- /dev/null
+IRMP - Infrared Multi Protocol Decoder
+--------------------------------------
+
+Version IRMP: 3.1.5 2019-08-27
+Version IRSND: 3.1.5 2019-08-28
+
+Documentation:
+
+ http://www.mikrocontroller.net/articles/IRMP
+ http://www.mikrocontroller.net/articles/IRMP_-_english
+
+ http://www.mikrocontroller.net/articles/IRSND
+ http://www.mikrocontroller.net/articles/IRSND_-_english
--- /dev/null
+/*
+ * irmp-main-sharedlib.c
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de
+ * Copyright (c) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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.
+ */
+
+/*
+ * Declare the library's public API first. Prove it's consistent and
+ * complete as a standalone header file.
+ */
+#include "irmp-main-sharedlib.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <Python.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Include the IRMP core logic. This approach is required because of
+ * static variables which hold internal state. The core logic started
+ * as an MCU project where resources are severely constrained.
+ *
+ * This libsigrokdecode incarnation of IRMP will always be used in the
+ * UNIX_OR_WINDOWS configuration. But libtool(1) breaks the upstream
+ * logic's platform detection. Check reliably available conditions here
+ * and provide expected symbols to the library, to reduce changes to the
+ * upstream project.
+ */
+#if defined _WIN32
+# if !defined WIN32
+# define WIN32
+# endif
+#else
+# if !defined unix
+# define unix
+# endif
+#endif
+#include "irmp.h"
+#include "irmp.c"
+
+/*
+ * The remaining source code implements the PC library, which accepts
+ * sample data from API callers, and provides detector results as they
+ * become available after seeing input data.
+ *
+ * TODO items, known constraints
+ * - Counters in the IRMP core logic and the library wrapper are 32bit
+ * only. In the strictest sense they only need to cover the span of
+ * an IR frame. In the PC side library case they need to cover "a
+ * detection phase", which happens to be under calling applications'
+ * control. The library shall not mess with the core's internal state,
+ * and may even not be able to reliably tell whether detection of a
+ * frame started in the core. Fortunately the 32bit counters only roll
+ * over after some 2.5 days at the highest available sample rate. So
+ * this limitation is not a blocker.
+ * - The IRMP core keeps internal state in global variables. Which is
+ * appropriate for MCU configurations. For the PC library use case
+ * this constraint prevents concurrency, only a single data stream
+ * can get processed at any time. This limitation can get addressed
+ * later, making the flexible and featureful IRMP detection available
+ * in the first place is considered highly desirable, and is a great
+ * improvement in itself.
+ * - The detection of IR frames from buffered data is both limited and
+ * complicated at the same time. The routine re-uses the caller's
+ * buffer _and_ internal state across multiple calls. Thus windowed
+ * operation over a larger set of input data is not available. The
+ * API lacks a flag for failed detection, thus applications need to
+ * guess from always returned payload data.
+ * - Is it worth adding a "detection in progress" query to the API? Is
+ * the information available to the library wrapper, and reliable?
+ * Shall applications be able to "poll" the started, and completed
+ * state for streamed operation including periodic state resets which
+ * won't interfere with pending detection? (It's assumed that this
+ * is only required when feeding single values in individual calls is
+ * found to be rather expensive.
+ * - Some of the result data reflects the core's internal presentation
+ * while there is no declaration in the library's API. This violates
+ * API layers, and needs to get addressed properly.
+ * - The IRMP core logic (strictly speaking the specific details of
+ * preprocessor symbol arrangements in the current implementation)
+ * appears to assume either to run on an MCU and capture IR signals
+ * from hardware pins, falling back to AVR if no other platform got
+ * detected. Or assumes to run on a (desktop) PC, and automatically
+ * enables ANALYZE mode, which results in lots of stdio traffic that
+ * is undesirable for application code which uses the shared library
+ * for strict detection purposes but no further analysis or research.
+ * It's a pity that turning off ANALYZE switches to MCU mode, and that
+ * keeping ANALYZE enabled but silencing the output is rather messy
+ * and touches the innards of the core logic (the irmp.c source file
+ * and its dependency header files).
+ */
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+static int irmp_lib_initialized;
+static size_t irmp_lib_client_id;
+static GMutex irmp_lib_mutex;
+
+struct irmp_instance {
+ size_t client_id;
+ GMutex *mutex;
+};
+
+static void irmp_lib_autoinit(void)
+{
+ if (irmp_lib_initialized)
+ return;
+
+ irmp_lib_client_id = 0;
+ g_mutex_init(&irmp_lib_mutex);
+
+ irmp_lib_initialized = 1;
+}
+
+static size_t irmp_next_client_id(void)
+{
+ size_t id;
+
+ do {
+ id = ++irmp_lib_client_id;
+ } while (!id);
+
+ return id;
+}
+
+IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void)
+{
+ struct irmp_instance *inst;
+
+ irmp_lib_autoinit();
+
+ inst = g_malloc0(sizeof(*inst));
+ if (!inst)
+ return NULL;
+
+ inst->client_id = irmp_next_client_id();
+ inst->mutex = &irmp_lib_mutex;
+
+ return inst;
+}
+
+IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
+{
+
+ irmp_lib_autoinit();
+
+ if (!state)
+ return;
+
+ g_free(state);
+}
+
+IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
+{
+
+ irmp_lib_autoinit();
+
+ return state ? state->client_id : 0;
+}
+
+IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
+{
+ int rc;
+ PyGILState_STATE pyst;
+
+ irmp_lib_autoinit();
+
+ if (!state || !state->mutex)
+ return -EINVAL;
+
+ pyst = PyGILState_Ensure();
+ Py_BEGIN_ALLOW_THREADS
+ if (wait) {
+ g_mutex_lock(state->mutex);
+ rc = 0;
+ } else {
+ rc = g_mutex_trylock(state->mutex);
+ }
+ Py_END_ALLOW_THREADS
+ PyGILState_Release(pyst);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state)
+{
+
+ irmp_lib_autoinit();
+
+ if (!state || !state->mutex)
+ return;
+
+ g_mutex_unlock(state->mutex);
+}
+
+static uint32_t s_end_sample;
+
+IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void)
+{
+ return F_INTERRUPTS;
+}
+
+IRMP_DLLEXPORT void irmp_reset_state(void)
+{
+ size_t i;
+ IRMP_DATA data;
+
+ /*
+ * Provide the equivalent of 1s idle input signal level. Then
+ * drain any potentially accumulated result data. This clears
+ * the internal decoder state.
+ */
+ IRMP_PIN = 0xff;
+ i = F_INTERRUPTS;
+ while (i-- > 0) {
+ (void)irmp_ISR();
+ }
+ (void)irmp_get_data(&data);
+
+ time_counter = 0;
+ s_startBitSample = 0;
+ s_curSample = 0;
+ s_end_sample = 0;
+
+ /*
+ * TODO This is not the most appropriate location to control the
+ * core logic's verbosity. But out of the public set of library
+ * routines this call is closest to some initialization routine.
+ * The query for compile time parameter values is optional, the
+ * state reset is not. Multiple verbosity setup activities in
+ * the same program lifetime won't harm. This HACK is clearly
+ * preferrable over more fiddling with core logic innards, or
+ * the introduction of yet another DLL routine.
+ */
+ silent = 1;
+ verbose = 0;
+}
+
+IRMP_DLLEXPORT int irmp_add_one_sample(int sample)
+{
+ int ret;
+
+ IRMP_PIN = sample ? 0xff : 0x00;
+ ret = irmp_ISR() ? 1 : 0;
+ s_end_sample = s_curSample++;
+ return ret;
+}
+
+IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data)
+{
+ IRMP_DATA d;
+
+ if (!irmp_get_data(&d))
+ return 0;
+
+ data->address = d.address;
+ data->command = d.command;
+ data->protocol = d.protocol;
+ data->protocol_name = irmp_get_protocol_name(d.protocol);
+ data->flags = d.flags;
+ data->start_sample = s_startBitSample;
+ data->end_sample = s_end_sample;
+ return 1;
+}
+
+#if WITH_IRMP_DETECT_BUFFER
+IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buff, size_t len)
+{
+ struct irmp_result_data ret;
+
+ memset(&ret, 0, sizeof(ret));
+ while (s_curSample < len) {
+ if (irmp_add_one_sample(buff[s_curSample])) {
+ irmp_get_result_data(&ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+#endif
+
+IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol)
+{
+ const char *name;
+
+ if (protocol >= ARRAY_SIZE(irmp_protocol_names))
+ return "unknown";
+ name = irmp_protocol_names[protocol];
+ if (!name || !*name)
+ return "unknown";
+ return name;
+}
+
+static __attribute__((constructor)) void init(void)
+{
+ irmp_lib_autoinit();
+}
--- /dev/null
+/*
+ * irmp-main-sharedlib.h
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de
+ * Copyright (c) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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.
+ */
+
+#ifndef IRMP_SHAREDLIB_H
+#define IRMP_SHAREDLIB_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Export the public API routines. */
+#ifndef IRMP_DLLEXPORT
+# if defined WIN32 && defined _MSC_VER
+# define IRMP_DLLEXPORT __declspec(dllexport)
+# else
+# define IRMP_DLLEXPORT __attribute__((visibility("default")))
+# endif
+#endif
+
+/* Part of the library API is optional. */
+#define WITH_IRMP_DETECT_BUFFER 0
+
+/**
+ * @brief State container for a decoder core instance. Opaque to clients.
+ */
+struct irmp_instance;
+
+/**
+ * @brief Allocate a decoder instance.
+ *
+ * @returns Reference to the allocated instance state.
+ */
+IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void);
+
+/**
+ * @brief Release a decoder instance.
+ *
+ * @param[in] state Reference to the instance's state.
+ */
+IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state);
+
+/**
+ * @brief Get the client ID of an IRMP decoder core instance.
+ */
+IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state);
+
+/**
+ * @brief Acquire a decoder instance's lock.
+ *
+ * @param[in] state Reference to the instance's state.
+ * @param[in] wait Whether to block until the lock is acquired.
+ *
+ * @returns 0 upon success, non-zero upon failure
+ */
+IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait);
+
+/**
+ * @brief Release a decoder instance's lock.
+ *
+ * @param[in] state Reference to the instance's state.
+ *
+ * @returns 0 upon success, non-zero upon failure
+ */
+IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state);
+
+/**
+ * @brief IR decoder result data at the library's public API.
+ */
+struct irmp_result_data {
+ uint32_t protocol; /**!< protocol, e.g. NEC_PROTOCOL */
+ const char *protocol_name; /**!< name of the protocol */
+ uint32_t address; /**!< address */
+ uint32_t command; /**!< command */
+ uint32_t flags; /**!< flags currently only repetition (bit 0) */
+ uint32_t start_sample; /**!< the sampleindex there the detected command started */
+ uint32_t end_sample; /**!< the sampleindex there the detected command ended */
+};
+
+#define IRMP_DATA_FLAG_REPETITION (1 << 0)
+#define IRMP_DATA_FLAG_RELEASE (1 << 1)
+
+/**
+ * @brief Query the IRMP library's configured sample rate.
+ *
+ * The internally used sample rate is a compile time option. Any data
+ * that is provided at runtime needs to match this rate, or detection
+ * will fail.
+ */
+IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void);
+
+/**
+ * @brief Reset internal decoder state.
+ *
+ * This must be called before data processing starts.
+ */
+IRMP_DLLEXPORT void irmp_reset_state(void);
+
+/**
+ * @brief Feed an individual sample to the detector.
+ *
+ * See @ref irmp_get_result_data() for result retrieval when detection
+ * of an IR frame completes. Make sure @ref irmp_reset_state() was
+ * called before providing the first sample.
+ *
+ * @param[in] sample The pin value to feed to the detector.
+ *
+ * @returns Non-zero when an IR frame was detected.
+ */
+IRMP_DLLEXPORT int irmp_add_one_sample(int sample);
+
+#if WITH_IRMP_DETECT_BUFFER
+/**
+ * @brief Process the given buffer until an IR frame is found.
+ *
+ * Stops at the first detected IR frame, and returns its data. Subsequent
+ * calls resume processing at the previously stopped position. Make sure
+ * @ref irmp_reset_state() was called before the first detect call.
+ *
+ * @param[in] buf Pointer to the data buffer.
+ * @param[in] len Number of samples in the Buffer.
+ */
+IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buf, size_t len);
+#endif
+
+/**
+ * @brief Query result data after detection succeeded.
+ *
+ * @param[out] data The caller provided result buffer.
+ *
+ * @returns Non-zero if data was available, zero otherwise.
+ */
+IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data);
+
+/**
+ * @brief Resolve the protocol identifer to the protocol's name.
+ *
+ * @param[in] protocol The numerical identifier.
+ *
+ * @returns A pointer to the string literal, or #NULL in case of failure.
+ */
+IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmp.c - infrared multi-protocol decoder, supports several remote control protocols
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ *
+ * Supported AVR mikrocontrollers:
+ *
+ * ATtiny87, ATtiny167
+ * ATtiny45, ATtiny85
+ * ATtiny44, ATtiny84
+ * ATmega8, ATmega16, ATmega32
+ * ATmega162
+ * ATmega164, ATmega324, ATmega644, ATmega644P, ATmega1284, ATmega1284P
+ * ATmega88, ATmega88P, ATmega168, ATmega168P, ATmega328P
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#include "irmp.h"
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 || IRMP_SUPPORT_NOKIA_PROTOCOL == 1 || IRMP_SUPPORT_IR60_PROTOCOL == 1
+# define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 1
+#else
+# define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 || IRMP_SUPPORT_RUWIDO_PROTOCOL == 1
+# define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 1
+#else
+# define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || \
+ IRMP_SUPPORT_RCII_PROTOCOL == 1 || \
+ IRMP_SUPPORT_S100_PROTOCOL == 1 || \
+ IRMP_SUPPORT_RC6_PROTOCOL == 1 || \
+ IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 || \
+ IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 || \
+ IRMP_SUPPORT_IR60_PROTOCOL == 1 || \
+ IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1 || \
+ IRMP_SUPPORT_MERLIN_PROTOCOL == 1 || \
+ IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+# define IRMP_SUPPORT_MANCHESTER 1
+#else
+# define IRMP_SUPPORT_MANCHESTER 0
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+# define IRMP_SUPPORT_SERIAL 1
+#else
+# define IRMP_SUPPORT_SERIAL 0
+#endif
+
+#define IRMP_KEY_REPETITION_LEN (uint_fast16_t)(F_INTERRUPTS * 150.0e-3 + 0.5) // autodetect key repetition within 150 msec
+
+#define MIN_TOLERANCE_00 1.0 // -0%
+#define MAX_TOLERANCE_00 1.0 // +0%
+
+#define MIN_TOLERANCE_02 0.98 // -2%
+#define MAX_TOLERANCE_02 1.02 // +2%
+
+#define MIN_TOLERANCE_03 0.97 // -3%
+#define MAX_TOLERANCE_03 1.03 // +3%
+
+#define MIN_TOLERANCE_05 0.95 // -5%
+#define MAX_TOLERANCE_05 1.05 // +5%
+
+#define MIN_TOLERANCE_10 0.9 // -10%
+#define MAX_TOLERANCE_10 1.1 // +10%
+
+#define MIN_TOLERANCE_15 0.85 // -15%
+#define MAX_TOLERANCE_15 1.15 // +15%
+
+#define MIN_TOLERANCE_20 0.8 // -20%
+#define MAX_TOLERANCE_20 1.2 // +20%
+
+#define MIN_TOLERANCE_30 0.7 // -30%
+#define MAX_TOLERANCE_30 1.3 // +30%
+
+#define MIN_TOLERANCE_40 0.6 // -40%
+#define MAX_TOLERANCE_40 1.4 // +40%
+
+#define MIN_TOLERANCE_50 0.5 // -50%
+#define MAX_TOLERANCE_50 1.5 // +50%
+
+#define MIN_TOLERANCE_60 0.4 // -60%
+#define MAX_TOLERANCE_60 1.6 // +60%
+
+#define MIN_TOLERANCE_70 0.3 // -70%
+#define MAX_TOLERANCE_70 1.7 // +70%
+
+#define SIRCS_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#if IRMP_SUPPORT_NETBOX_PROTOCOL // only 5% to avoid conflict with NETBOX:
+# define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#else // only 5% + 1 to avoid conflict with RC6:
+# define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#endif
+#define SIRCS_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NEC_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+// autodetect nec repetition frame within 50 msec:
+// NEC seems to send the first repetition frame after 40ms, further repetition frames after 100 ms
+#if 0
+#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * NEC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+#else
+#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5)
+#endif
+
+#define SAMSUNG_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNG_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNG_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNG_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNG_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNG_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNG_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define SAMSUNGAH_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNGAH_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNGAH_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNGAH_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNGAH_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNGAH_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNGAH_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define MATSUSHITA_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MATSUSHITA_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MATSUSHITA_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MATSUSHITA_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MATSUSHITA_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MATSUSHITA_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MATSUSHITA_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+
+#define KASEIKYO_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define KASEIKYO_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define KASEIKYO_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define MITSU_HEAVY_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MITSU_HEAVY_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MITSU_HEAVY_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MITSU_HEAVY_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MITSU_HEAVY_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MITSU_HEAVY_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MITSU_HEAVY_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MITSU_HEAVY_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define VINCENT_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define VINCENT_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define VINCENT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define VINCENT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define VINCENT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * VINCENT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define VINCENT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * VINCENT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define VINCENT_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * VINCENT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define VINCENT_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * VINCENT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define VINCENT_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * VINCENT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define VINCENT_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * VINCENT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define PANASONIC_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PANASONIC_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PANASONIC_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PANASONIC_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PANASONIC_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define PANASONIC_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define PANASONIC_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PANASONIC_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PANASONIC_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PANASONIC_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define RECS80_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1 // BOSE conflicts with RC5, so keep tolerance for RC5 minimal here:
+#define RC5_START_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RC5_START_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#else
+#define RC5_START_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC5_START_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#endif
+
+#define RC5_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC5_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RCII_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCII_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCII_START_BIT2_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT2_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT2_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT2_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+
+#define RCII_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCII_BIT_LEN ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME))
+#define RCII_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1 // BOSE conflicts with S100, so keep tolerance for S100 minimal here:
+#define S100_START_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define S100_START_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#else
+#define S100_START_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define S100_START_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#endif
+
+#define S100_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define S100_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define DENON_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define DENON_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+// RUWIDO (see t-home-mediareceiver-15kHz.txt) conflicts here with DENON
+#define DENON_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define DENON_AUTO_REPETITION_PAUSE_LEN ((uint_fast16_t)(F_INTERRUPTS * DENON_AUTO_REPETITION_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define THOMSON_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define THOMSON_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define THOMSON_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RC6_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_TOGGLE_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_TOGGLE_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_60 + 0.5) + 1) // pulses: 300 - 800
+#define RC6_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) // pauses: 300 - 600
+
+#define RECS80EXT_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80EXT_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80EXT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80EXT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80EXT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80EXT_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80EXT_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NUBERT_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define FAN_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define SPEAKER_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX ((PAUSE_LEN)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) // value must be below IRMP_TIMEOUT
+#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_R_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_R_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define IR60_TIMEOUT_LEN ((uint_fast8_t)(F_INTERRUPTS * IR60_TIMEOUT_TIME * 0.5))
+#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_BIT_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define GRUNDIG_NOKIA_IR60_BIT_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define FDC_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) // 5%: avoid conflict with NETBOX
+#define FDC_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#define FDC_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define FDC_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#define FDC_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define FDC_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1)
+#define FDC_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FDC_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#if 0
+#define FDC_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) // could be negative: 255
+#else
+#define FDC_0_PAUSE_LEN_MIN (1) // simply use 1
+#endif
+#define FDC_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RCCAR_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RCCAR_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RCCAR_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RCCAR_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RCCAR_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RCCAR_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RCCAR_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCCAR_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define RCCAR_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCCAR_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define JVC_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MIN_TOLERANCE_40 + 0.5) - 1) // HACK!
+#define JVC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MAX_TOLERANCE_70 + 0.5) - 1) // HACK!
+#define JVC_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+// autodetect JVC repetition frame within 50 msec:
+#define JVC_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * JVC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define NIKON_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_START_BIT_PAUSE_LEN_MIN ((uint_fast16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_START_BIT_PAUSE_LEN_MAX ((uint_fast16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * NIKON_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define KATHREIN_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NETBOX_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define NETBOX_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define NETBOX_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define NETBOX_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define NETBOX_PULSE_LEN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME))
+#define NETBOX_PAUSE_LEN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME))
+#define NETBOX_PULSE_REST_LEN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME / 4))
+#define NETBOX_PAUSE_REST_LEN ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME / 4))
+
+#define LEGO_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+
+#define IRMP16_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * IRMP16_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * IRMP16_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * IRMP16_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * IRMP16_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * IRMP16_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * IRMP16_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define GREE_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GREE_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GREE_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GREE_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GREE_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * GREE_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * GREE_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define BOSE_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BOSE_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BOSE_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BOSE_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BOSE_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * BOSE_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * BOSE_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5)
+
+#define A1TVBOX_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define A1TVBOX_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define A1TVBOX_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define A1TVBOX_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define A1TVBOX_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define A1TVBOX_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define A1TVBOX_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define A1TVBOX_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define MERLIN_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MERLIN_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MERLIN_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MERLIN_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MERLIN_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define MERLIN_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define MERLIN_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define MERLIN_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define ORTEK_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define TELEFUNKEN_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define TELEFUNKEN_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * (TELEFUNKEN_START_BIT_PAUSE_TIME) * MIN_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * (TELEFUNKEN_START_BIT_PAUSE_TIME) * MAX_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define TELEFUNKEN_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define TELEFUNKEN_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+// autodetect TELEFUNKEN repetition frame within 50 msec:
+// #define TELEFUNKEN_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * TELEFUNKEN_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define ROOMBA_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ROOMBA_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ROOMBA_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ROOMBA_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ROOMBA_1_PAUSE_LEN_EXACT ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME + 0.5))
+#define ROOMBA_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_0_PAUSE_LEN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME))
+#define ROOMBA_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define RCMM32_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_00_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_00_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_00_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_00_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_01_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_01_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_01_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_01_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_10_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_10_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_10_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_10_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_11_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RCMM32_11_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_11_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RCMM32_11_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+
+#define PENTAX_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PENTAX_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PENTAX_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PENTAX_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PENTAX_1_PAUSE_LEN_EXACT ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME + 0.5))
+#define PENTAX_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_0_PAUSE_LEN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME))
+#define PENTAX_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define ACP24_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PULSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PULSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ACP24_PULSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ACP24_PULSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ACP24_1_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ACP24_1_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * ACP24_0_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * ACP24_0_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+
+#define METZ_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define METZ_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define METZ_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define METZ_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define METZ_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * METZ_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * METZ_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * METZ_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * METZ_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * METZ_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * METZ_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_FRAME_REPEAT_PAUSE_LEN_MAX (uint_fast16_t)(F_INTERRUPTS * METZ_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define RADIO1_START_BIT_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RADIO1_START_BIT_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RADIO1_START_BIT_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RADIO1_START_BIT_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RADIO1_1_PAUSE_LEN_EXACT ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME + 0.5))
+#define RADIO1_1_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_1_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_0_PAUSE_LEN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME))
+#define RADIO1_0_PULSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_0_PULSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define AUTO_FRAME_REPETITION_LEN (uint_fast16_t)(F_INTERRUPTS * AUTO_FRAME_REPETITION_TIME + 0.5) // use uint_fast16_t!
+
+#ifdef ANALYZE
+# define ANALYZE_PUTCHAR(a) { if (! silent) { putchar (a); } }
+# define ANALYZE_ONLY_NORMAL_PUTCHAR(a) { if (! silent && !verbose) { putchar (a); } }
+# define ANALYZE_PRINTF(...) { if (verbose) { printf (__VA_ARGS__); } }
+# define ANALYZE_ONLY_NORMAL_PRINTF(...) { if (! silent && !verbose) { printf (__VA_ARGS__); } }
+# define ANALYZE_NEWLINE() { if (verbose) { putchar ('\n'); } }
+static int silent;
+static int time_counter;
+static int verbose;
+
+#elif 0 /* not every PIC compiler knows variadic macros :-( */
+# define ANALYZE_PUTCHAR(a)
+# define ANALYZE_ONLY_NORMAL_PUTCHAR(a)
+# define ANALYZE_PRINTF(...)
+# define ANALYZE_ONLY_NORMAL_PRINTF(...)
+# define ANALYZE_NEWLINE()
+
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+static void (*irmp_callback_ptr) (uint_fast8_t);
+#endif // IRMP_USE_CALLBACK == 1
+
+#define PARITY_CHECK_OK 1
+#define PARITY_CHECK_FAILED 0
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Protocol names
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined(UNIX_OR_WINDOWS) || IRMP_PROTOCOL_NAMES == 1
+static const char proto_unknown[] PROGMEM = "UNKNOWN";
+static const char proto_sircs[] PROGMEM = "SIRCS";
+static const char proto_nec[] PROGMEM = "NEC";
+static const char proto_samsung[] PROGMEM = "SAMSUNG";
+static const char proto_matsushita[] PROGMEM = "MATSUSH";
+static const char proto_kaseikyo[] PROGMEM = "KASEIKYO";
+static const char proto_recs80[] PROGMEM = "RECS80";
+static const char proto_rc5[] PROGMEM = "RC5";
+static const char proto_denon[] PROGMEM = "DENON";
+static const char proto_rc6[] PROGMEM = "RC6";
+static const char proto_samsung32[] PROGMEM = "SAMSG32";
+static const char proto_apple[] PROGMEM = "APPLE";
+static const char proto_recs80ext[] PROGMEM = "RECS80EX";
+static const char proto_nubert[] PROGMEM = "NUBERT";
+static const char proto_bang_olufsen[] PROGMEM = "BANG OLU";
+static const char proto_grundig[] PROGMEM = "GRUNDIG";
+static const char proto_nokia[] PROGMEM = "NOKIA";
+static const char proto_siemens[] PROGMEM = "SIEMENS";
+static const char proto_fdc[] PROGMEM = "FDC";
+static const char proto_rccar[] PROGMEM = "RCCAR";
+static const char proto_jvc[] PROGMEM = "JVC";
+static const char proto_rc6a[] PROGMEM = "RC6A";
+static const char proto_nikon[] PROGMEM = "NIKON";
+static const char proto_ruwido[] PROGMEM = "RUWIDO";
+static const char proto_ir60[] PROGMEM = "IR60";
+static const char proto_kathrein[] PROGMEM = "KATHREIN";
+static const char proto_netbox[] PROGMEM = "NETBOX";
+static const char proto_nec16[] PROGMEM = "NEC16";
+static const char proto_nec42[] PROGMEM = "NEC42";
+static const char proto_lego[] PROGMEM = "LEGO";
+static const char proto_thomson[] PROGMEM = "THOMSON";
+static const char proto_bose[] PROGMEM = "BOSE";
+static const char proto_a1tvbox[] PROGMEM = "A1TVBOX";
+static const char proto_ortek[] PROGMEM = "ORTEK";
+static const char proto_telefunken[] PROGMEM = "TELEFUNKEN";
+static const char proto_roomba[] PROGMEM = "ROOMBA";
+static const char proto_rcmm32[] PROGMEM = "RCMM32";
+static const char proto_rcmm24[] PROGMEM = "RCMM24";
+static const char proto_rcmm12[] PROGMEM = "RCMM12";
+static const char proto_speaker[] PROGMEM = "SPEAKER";
+static const char proto_lgair[] PROGMEM = "LGAIR";
+static const char proto_samsung48[] PROGMEM = "SAMSG48";
+static const char proto_merlin[] PROGMEM = "MERLIN";
+static const char proto_pentax[] PROGMEM = "PENTAX";
+static const char proto_fan[] PROGMEM = "FAN";
+static const char proto_s100[] PROGMEM = "S100";
+static const char proto_acp24[] PROGMEM = "ACP24";
+static const char proto_technics[] PROGMEM = "TECHNICS";
+static const char proto_panasonic[] PROGMEM = "PANASONIC";
+static const char proto_mitsu_heavy[] PROGMEM = "MITSU_HEAVY";
+static const char proto_vincent[] PROGMEM = "VINCENT";
+static const char proto_samsungah[] PROGMEM = "SAMSUNGAH";
+static const char proto_irmp16[] PROGMEM = "IRMP16";
+static const char proto_gree[] PROGMEM = "GREE";
+static const char proto_rcii[] PROGMEM = "RCII";
+static const char proto_metz[] PROGMEM = "METZ";
+static const char proto_onkyo[] PROGMEM = "ONKYO";
+
+static const char proto_radio1[] PROGMEM = "RADIO1";
+
+const char * const
+irmp_protocol_names[IRMP_N_PROTOCOLS + 1] PROGMEM =
+{
+ proto_unknown,
+ proto_sircs,
+ proto_nec,
+ proto_samsung,
+ proto_matsushita,
+ proto_kaseikyo,
+ proto_recs80,
+ proto_rc5,
+ proto_denon,
+ proto_rc6,
+ proto_samsung32,
+ proto_apple,
+ proto_recs80ext,
+ proto_nubert,
+ proto_bang_olufsen,
+ proto_grundig,
+ proto_nokia,
+ proto_siemens,
+ proto_fdc,
+ proto_rccar,
+ proto_jvc,
+ proto_rc6a,
+ proto_nikon,
+ proto_ruwido,
+ proto_ir60,
+ proto_kathrein,
+ proto_netbox,
+ proto_nec16,
+ proto_nec42,
+ proto_lego,
+ proto_thomson,
+ proto_bose,
+ proto_a1tvbox,
+ proto_ortek,
+ proto_telefunken,
+ proto_roomba,
+ proto_rcmm32,
+ proto_rcmm24,
+ proto_rcmm12,
+ proto_speaker,
+ proto_lgair,
+ proto_samsung48,
+ proto_merlin,
+ proto_pentax,
+ proto_fan,
+ proto_s100,
+ proto_acp24,
+ proto_technics,
+ proto_panasonic,
+ proto_mitsu_heavy,
+ proto_vincent,
+ proto_samsungah,
+ proto_irmp16,
+ proto_gree,
+ proto_rcii,
+ proto_metz,
+ proto_onkyo,
+
+ proto_radio1
+};
+
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Logging
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if IRMP_LOGGING == 1 // logging via UART
+
+#if defined(ARM_STM32F4XX)
+# define STM32_GPIO_CLOCK RCC_AHB1Periph_GPIOA // UART2 on PA2
+# define STM32_UART_CLOCK RCC_APB1Periph_USART2
+# define STM32_GPIO_PORT GPIOA
+# define STM32_GPIO_PIN GPIO_Pin_2
+# define STM32_GPIO_SOURCE GPIO_PinSource2
+# define STM32_UART_AF GPIO_AF_USART2
+# define STM32_UART_COM USART2
+# define STM32_UART_BAUD 115200 // 115200 Baud
+# include "stm32f4xx_usart.h"
+#elif defined(ARM_STM32F10X)
+# define STM32_UART_COM USART3 // UART3 on PB10
+#elif defined(ARDUINO) // Arduino Serial implementation
+# if defined(USB_SERIAL)
+# include "usb_serial.h"
+# else
+# error USB_SERIAL not defined in ARDUINO Environment
+# endif
+#elif defined(_CHIBIOS_HAL_) // ChibiOS HAL
+# if IRMP_EXT_LOGGING == 1
+# error IRMP_EXT_LOGGING not implemented for ChibiOS HAL, use regular logging instead
+# endif
+#else
+# if IRMP_EXT_LOGGING == 1 // use external logging
+# include "irmpextlog.h"
+# else // normal UART log (IRMP_EXT_LOGGING == 0)
+# define BAUD 9600L
+# ifndef UNIX_OR_WINDOWS
+# include <util/setbaud.h>
+# endif
+
+#ifdef UBRR0H
+
+#define UART0_UBRRH UBRR0H
+#define UART0_UBRRL UBRR0L
+#define UART0_UCSRA UCSR0A
+#define UART0_UCSRB UCSR0B
+#define UART0_UCSRC UCSR0C
+#define UART0_UDRE_BIT_VALUE (1<<UDRE0)
+#define UART0_UCSZ1_BIT_VALUE (1<<UCSZ01)
+#define UART0_UCSZ0_BIT_VALUE (1<<UCSZ00)
+#ifdef URSEL0
+#define UART0_URSEL_BIT_VALUE (1<<URSEL0)
+#else
+#define UART0_URSEL_BIT_VALUE (0)
+#endif
+#define UART0_TXEN_BIT_VALUE (1<<TXEN0)
+#define UART0_UDR UDR0
+#define UART0_U2X U2X0
+
+#else
+
+#define UART0_UBRRH UBRRH
+#define UART0_UBRRL UBRRL
+#define UART0_UCSRA UCSRA
+#define UART0_UCSRB UCSRB
+#define UART0_UCSRC UCSRC
+#define UART0_UDRE_BIT_VALUE (1<<UDRE)
+#define UART0_UCSZ1_BIT_VALUE (1<<UCSZ1)
+#define UART0_UCSZ0_BIT_VALUE (1<<UCSZ0)
+#ifdef URSEL
+#define UART0_URSEL_BIT_VALUE (1<<URSEL)
+#else
+#define UART0_URSEL_BIT_VALUE (0)
+#endif
+#define UART0_TXEN_BIT_VALUE (1<<TXEN)
+#define UART0_UDR UDR
+#define UART0_U2X U2X
+
+#endif //UBRR0H
+#endif //IRMP_EXT_LOGGING
+#endif //ARM_STM32F4XX
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Initialize UART
+ * @details Initializes UART
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+void
+irmp_uart_init (void)
+{
+#ifndef UNIX_OR_WINDOWS
+#if defined(ARM_STM32F4XX)
+ GPIO_InitTypeDef GPIO_InitStructure;
+ USART_InitTypeDef USART_InitStructure;
+
+ // Clock enable vom TX Pin
+ RCC_AHB1PeriphClockCmd(STM32_GPIO_CLOCK, ENABLE);
+
+ // Clock enable der UART
+ RCC_APB1PeriphClockCmd(STM32_UART_CLOCK, ENABLE);
+
+ // UART Alternative-Funktion mit dem IO-Pin verbinden
+ GPIO_PinAFConfig(STM32_GPIO_PORT,STM32_GPIO_SOURCE,STM32_UART_AF);
+
+ // UART als Alternative-Funktion mit PushPull
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
+
+ // TX-Pin
+ GPIO_InitStructure.GPIO_Pin = STM32_GPIO_PIN;
+ GPIO_Init(STM32_GPIO_PORT, &GPIO_InitStructure);
+
+ // Oversampling
+ USART_OverSampling8Cmd(STM32_UART_COM, ENABLE);
+
+ // init baud rate, 8 data bits, 1 stop bit, no parity, no RTS+CTS
+ USART_InitStructure.USART_BaudRate = STM32_UART_BAUD;
+ USART_InitStructure.USART_WordLength = USART_WordLength_8b;
+ USART_InitStructure.USART_StopBits = USART_StopBits_1;
+ USART_InitStructure.USART_Parity = USART_Parity_No;
+ USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+ USART_InitStructure.USART_Mode = USART_Mode_Tx;
+ USART_Init(STM32_UART_COM, &USART_InitStructure);
+
+ // UART enable
+ USART_Cmd(STM32_UART_COM, ENABLE);
+
+#elif defined(ARM_STM32F10X)
+ GPIO_InitTypeDef GPIO_InitStructure;
+ USART_InitTypeDef USART_InitStructure;
+
+ // Clock enable vom TX Pin
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // UART3 an PB10
+
+ // Clock enable der UART
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
+
+ // UART als Alternative-Funktion mit PushPull
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+
+ // TX-Pin
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+ GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+ // Oversampling
+ USART_OverSampling8Cmd(STM32_UART_COM, ENABLE);
+
+ // init baud rate, 8 data bits, 1 stop bit, no parity, no RTS+CTS
+ USART_InitStructure.USART_BaudRate = 115200;
+ USART_InitStructure.USART_WordLength = USART_WordLength_8b;
+ USART_InitStructure.USART_StopBits = USART_StopBits_1;
+ USART_InitStructure.USART_Parity = USART_Parity_No;
+ USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+ USART_InitStructure.USART_Mode = USART_Mode_Tx;
+ USART_Init(STM32_UART_COM, &USART_InitStructure);
+
+ // UART enable
+ USART_Cmd(STM32_UART_COM, ENABLE);
+
+#elif defined(ARDUINO)
+ // we use the Arduino Serial Imlementation
+ // you have to call Serial.begin(SER_BAUD); in Arduino setup() function
+
+#elif defined (__AVR_XMEGA__)
+
+ PMIC.CTRL |= PMIC_HILVLEN_bm;
+
+ USARTC1.BAUDCTRLB = 0;
+ USARTC1.BAUDCTRLA = F_CPU / 153600 - 1;
+ USARTC1.CTRLA = USART_RXCINTLVL_HI_gc; // high INT level (receive)
+ USARTC1.CTRLB = USART_TXEN_bm | USART_RXEN_bm; // activated RX and TX
+ USARTC1.CTRLC = USART_CHSIZE_8BIT_gc; // 8 Bit
+ PORTC.DIR |= (1<<7); // TXD is output
+ PORTC.DIR &= ~(1<<6);
+
+#elif defined (_CHIBIOS_HAL_)
+ // we use the SD interface for logging, no need to init that here
+
+#else
+
+#if (IRMP_EXT_LOGGING == 0) // use UART
+ UART0_UBRRH = UBRRH_VALUE; // set baud rate
+ UART0_UBRRL = UBRRL_VALUE;
+
+#if USE_2X
+ UART0_UCSRA |= (1<<UART0_U2X);
+#else
+ UART0_UCSRA &= ~(1<<UART0_U2X);
+#endif
+
+ UART0_UCSRC = UART0_UCSZ1_BIT_VALUE | UART0_UCSZ0_BIT_VALUE | UART0_URSEL_BIT_VALUE;
+ UART0_UCSRB |= UART0_TXEN_BIT_VALUE; // enable UART TX
+#else // other log method
+ initextlog();
+#endif //IRMP_EXT_LOGGING
+#endif //ARM_STM32F4XX
+#endif // UNIX_OR_WINDOWS
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Send character
+ * @details Sends character
+ * @param ch character to be transmitted
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+void
+irmp_uart_putc (unsigned char ch)
+{
+#ifndef UNIX_OR_WINDOWS
+#if defined(ARM_STM32F4XX) || defined(ARM_STM32F10X)
+ // warten bis altes Byte gesendet wurde
+ while (USART_GetFlagStatus(STM32_UART_COM, USART_FLAG_TXE) == RESET)
+ {
+ ;
+ }
+
+ USART_SendData(STM32_UART_COM, ch);
+
+ if (ch == '\n')
+ {
+ while (USART_GetFlagStatus(STM32_UART_COM, USART_FLAG_TXE) == RESET);
+ USART_SendData(STM32_UART_COM, '\r');
+ }
+
+#elif defined(ARDUINO)
+ // we use the Arduino Serial Imlementation
+ usb_serial_putchar(ch);
+
+#elif defined(_CHIBIOS_HAL_)
+ // use the SD interface from HAL, log to IRMP_LOGGING_SD which is defined in irmpconfig.h
+ sdWriteI(&IRMP_LOGGING_SD,&ch,1); // we are called from interrupt context, so use the ...I version of the function
+
+#else
+#if (IRMP_EXT_LOGGING == 0)
+
+# if defined (__AVR_XMEGA__)
+ while (!(USARTC1.STATUS & USART_DREIF_bm))
+ {
+ ;
+ }
+
+ USARTC1.DATA = ch;
+
+# else // AVR_MEGA
+ while (!(UART0_UCSRA & UART0_UDRE_BIT_VALUE))
+ {
+ ;
+ }
+
+ UART0_UDR = ch;
+
+# endif // __AVR_XMEGA__
+
+#else
+
+ sendextlog(ch); // use external log
+
+#endif // IRMP_EXT_LOGGING
+#endif // ARM_STM32F4XX
+#else
+ fputc (ch, stderr);
+#endif // UNIX_OR_WINDOWS
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Log IR signal
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#define STARTCYCLES 2 // min count of zeros before start of logging
+#define ENDBITS 1000 // number of sequenced highbits to detect end
+#define DATALEN 700 // log buffer size
+
+static void
+irmp_log (uint_fast8_t val)
+{
+ static uint8_t buf[DATALEN]; // logging buffer
+ static uint_fast16_t buf_idx; // index
+ static uint_fast8_t startcycles; // current number of start-zeros
+ static uint_fast16_t cnt; // counts sequenced highbits - to detect end
+ static uint_fast8_t last_val = 1;
+
+ if (! val && (startcycles < STARTCYCLES) && !buf_idx) // prevent that single random zeros init logging
+ {
+ startcycles++;
+ }
+ else
+ {
+ startcycles = 0;
+
+ if (! val || buf_idx != 0) // start or continue logging on "0", "1" cannot init logging
+ {
+ if (last_val == val)
+ {
+ cnt++;
+
+ if (val && cnt > ENDBITS) // if high received then look at log-stop condition
+ { // if stop condition is true, output on uart
+ uint_fast8_t i8;
+ uint_fast16_t i;
+ uint_fast16_t j;
+ uint_fast8_t v = '1';
+ uint_fast16_t d;
+
+ for (i8 = 0; i8 < STARTCYCLES; i8++)
+ {
+ irmp_uart_putc ('0'); // the ignored starting zeros
+ }
+
+ for (i = 0; i < buf_idx; i++)
+ {
+ d = buf[i];
+
+ if (d == 0xff)
+ {
+ i++;
+ d = buf[i];
+ i++;
+ d |= ((uint_fast16_t) buf[i] << 8);
+ }
+
+ for (j = 0; j < d; j++)
+ {
+ irmp_uart_putc (v);
+ }
+
+ v = (v == '1') ? '0' : '1';
+ }
+
+ for (i8 = 0; i8 < 20; i8++)
+ {
+ irmp_uart_putc ('1');
+ }
+
+ irmp_uart_putc ('\n');
+ buf_idx = 0;
+ last_val = 1;
+ cnt = 0;
+ }
+ }
+ else if (buf_idx < DATALEN - 3)
+ {
+ if (cnt >= 0xff)
+ {
+ buf[buf_idx++] = 0xff;
+ buf[buf_idx++] = (cnt & 0xff);
+ buf[buf_idx] = (cnt >> 8);
+ }
+ else
+ {
+ buf[buf_idx] = cnt;
+ }
+
+ buf_idx++;
+ cnt = 1;
+ last_val = val;
+ }
+ }
+ }
+}
+
+#else
+#define irmp_log(val)
+#endif //IRMP_LOGGING
+
+typedef struct
+{
+ uint_fast8_t protocol; // ir protocol
+ uint_fast8_t pulse_1_len_min; // minimum length of pulse with bit value 1
+ uint_fast8_t pulse_1_len_max; // maximum length of pulse with bit value 1
+ uint_fast8_t pause_1_len_min; // minimum length of pause with bit value 1
+ uint_fast8_t pause_1_len_max; // maximum length of pause with bit value 1
+ uint_fast8_t pulse_0_len_min; // minimum length of pulse with bit value 0
+ uint_fast8_t pulse_0_len_max; // maximum length of pulse with bit value 0
+ uint_fast8_t pause_0_len_min; // minimum length of pause with bit value 0
+ uint_fast8_t pause_0_len_max; // maximum length of pause with bit value 0
+ uint_fast8_t address_offset; // address offset
+ uint_fast8_t address_end; // end of address
+ uint_fast8_t command_offset; // command offset
+ uint_fast8_t command_end; // end of command
+ uint_fast8_t complete_len; // complete length of frame
+ uint_fast8_t stop_bit; // flag: frame has stop bit
+ uint_fast8_t lsb_first; // flag: LSB first
+ uint_fast8_t flags; // some flags
+} IRMP_PARAMETER;
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER sircs_param =
+{
+ IRMP_SIRCS_PROTOCOL, // protocol: ir protocol
+ SIRCS_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ SIRCS_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ SIRCS_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ SIRCS_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ SIRCS_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ SIRCS_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ SIRCS_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ SIRCS_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ SIRCS_ADDRESS_OFFSET, // address_offset: address offset
+ SIRCS_ADDRESS_OFFSET + SIRCS_ADDRESS_LEN, // address_end: end of address
+ SIRCS_COMMAND_OFFSET, // command_offset: command offset
+ SIRCS_COMMAND_OFFSET + SIRCS_COMMAND_LEN, // command_end: end of command
+ SIRCS_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ SIRCS_STOP_BIT, // stop_bit: flag: frame has stop bit
+ SIRCS_LSB, // lsb_first: flag: LSB first
+ SIRCS_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nec_param =
+{
+ IRMP_NEC_PROTOCOL, // protocol: ir protocol
+ NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ NEC_ADDRESS_OFFSET, // address_offset: address offset
+ NEC_ADDRESS_OFFSET + NEC_ADDRESS_LEN, // address_end: end of address
+ NEC_COMMAND_OFFSET, // command_offset: command offset
+ NEC_COMMAND_OFFSET + NEC_COMMAND_LEN, // command_end: end of command
+ NEC_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NEC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NEC_LSB, // lsb_first: flag: LSB first
+ NEC_FLAGS // flags: some flags
+};
+
+static const PROGMEM IRMP_PARAMETER nec_rep_param =
+{
+ IRMP_NEC_PROTOCOL, // protocol: ir protocol
+ NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ 0, // address_offset: address offset
+ 0, // address_end: end of address
+ 0, // command_offset: command offset
+ 0, // command_end: end of command
+ 0, // complete_len: complete length of frame
+ NEC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NEC_LSB, // lsb_first: flag: LSB first
+ NEC_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nec42_param =
+{
+ IRMP_NEC42_PROTOCOL, // protocol: ir protocol
+ NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ NEC42_ADDRESS_OFFSET, // address_offset: address offset
+ NEC42_ADDRESS_OFFSET + NEC42_ADDRESS_LEN, // address_end: end of address
+ NEC42_COMMAND_OFFSET, // command_offset: command offset
+ NEC42_COMMAND_OFFSET + NEC42_COMMAND_LEN, // command_end: end of command
+ NEC42_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NEC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NEC_LSB, // lsb_first: flag: LSB first
+ NEC_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER lgair_param =
+{
+ IRMP_LGAIR_PROTOCOL, // protocol: ir protocol
+ NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ LGAIR_ADDRESS_OFFSET, // address_offset: address offset
+ LGAIR_ADDRESS_OFFSET + LGAIR_ADDRESS_LEN, // address_end: end of address
+ LGAIR_COMMAND_OFFSET, // command_offset: command offset
+ LGAIR_COMMAND_OFFSET + LGAIR_COMMAND_LEN, // command_end: end of command
+ LGAIR_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NEC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NEC_LSB, // lsb_first: flag: LSB first
+ NEC_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER samsung_param =
+{
+ IRMP_SAMSUNG_PROTOCOL, // protocol: ir protocol
+ SAMSUNG_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ SAMSUNG_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ SAMSUNG_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ SAMSUNG_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ SAMSUNG_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ SAMSUNG_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ SAMSUNG_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ SAMSUNG_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ SAMSUNG_ADDRESS_OFFSET, // address_offset: address offset
+ SAMSUNG_ADDRESS_OFFSET + SAMSUNG_ADDRESS_LEN, // address_end: end of address
+ SAMSUNG_COMMAND_OFFSET, // command_offset: command offset
+ SAMSUNG_COMMAND_OFFSET + SAMSUNG_COMMAND_LEN, // command_end: end of command
+ SAMSUNG_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ SAMSUNG_STOP_BIT, // stop_bit: flag: frame has stop bit
+ SAMSUNG_LSB, // lsb_first: flag: LSB first
+ SAMSUNG_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER samsungah_param =
+{
+ IRMP_SAMSUNGAH_PROTOCOL, // protocol: ir protocol
+ SAMSUNGAH_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ SAMSUNGAH_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ SAMSUNGAH_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ SAMSUNGAH_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ SAMSUNGAH_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ SAMSUNGAH_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ SAMSUNGAH_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ SAMSUNGAH_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ SAMSUNGAH_ADDRESS_OFFSET, // address_offset: address offset
+ SAMSUNGAH_ADDRESS_OFFSET + SAMSUNGAH_ADDRESS_LEN, // address_end: end of address
+ SAMSUNGAH_COMMAND_OFFSET, // command_offset: command offset
+ SAMSUNGAH_COMMAND_OFFSET + SAMSUNGAH_COMMAND_LEN, // command_end: end of command
+ SAMSUNGAH_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ SAMSUNGAH_STOP_BIT, // stop_bit: flag: frame has stop bit
+ SAMSUNGAH_LSB, // lsb_first: flag: LSB first
+ SAMSUNGAH_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER telefunken_param =
+{
+ IRMP_TELEFUNKEN_PROTOCOL, // protocol: ir protocol
+ TELEFUNKEN_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ TELEFUNKEN_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ TELEFUNKEN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ TELEFUNKEN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ TELEFUNKEN_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ TELEFUNKEN_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ TELEFUNKEN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ TELEFUNKEN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ TELEFUNKEN_ADDRESS_OFFSET, // address_offset: address offset
+ TELEFUNKEN_ADDRESS_OFFSET + TELEFUNKEN_ADDRESS_LEN, // address_end: end of address
+ TELEFUNKEN_COMMAND_OFFSET, // command_offset: command offset
+ TELEFUNKEN_COMMAND_OFFSET + TELEFUNKEN_COMMAND_LEN, // command_end: end of command
+ TELEFUNKEN_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ TELEFUNKEN_STOP_BIT, // stop_bit: flag: frame has stop bit
+ TELEFUNKEN_LSB, // lsb_first: flag: LSB first
+ TELEFUNKEN_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER matsushita_param =
+{
+ IRMP_MATSUSHITA_PROTOCOL, // protocol: ir protocol
+ MATSUSHITA_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ MATSUSHITA_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ MATSUSHITA_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ MATSUSHITA_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ MATSUSHITA_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ MATSUSHITA_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ MATSUSHITA_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ MATSUSHITA_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ MATSUSHITA_ADDRESS_OFFSET, // address_offset: address offset
+ MATSUSHITA_ADDRESS_OFFSET + MATSUSHITA_ADDRESS_LEN, // address_end: end of address
+ MATSUSHITA_COMMAND_OFFSET, // command_offset: command offset
+ MATSUSHITA_COMMAND_OFFSET + MATSUSHITA_COMMAND_LEN, // command_end: end of command
+ MATSUSHITA_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ MATSUSHITA_STOP_BIT, // stop_bit: flag: frame has stop bit
+ MATSUSHITA_LSB, // lsb_first: flag: LSB first
+ MATSUSHITA_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER kaseikyo_param =
+{
+ IRMP_KASEIKYO_PROTOCOL, // protocol: ir protocol
+ KASEIKYO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ KASEIKYO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ KASEIKYO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ KASEIKYO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ KASEIKYO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ KASEIKYO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ KASEIKYO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ KASEIKYO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ KASEIKYO_ADDRESS_OFFSET, // address_offset: address offset
+ KASEIKYO_ADDRESS_OFFSET + KASEIKYO_ADDRESS_LEN, // address_end: end of address
+ KASEIKYO_COMMAND_OFFSET, // command_offset: command offset
+ KASEIKYO_COMMAND_OFFSET + KASEIKYO_COMMAND_LEN, // command_end: end of command
+ KASEIKYO_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ KASEIKYO_STOP_BIT, // stop_bit: flag: frame has stop bit
+ KASEIKYO_LSB, // lsb_first: flag: LSB first
+ KASEIKYO_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER panasonic_param =
+{
+ IRMP_PANASONIC_PROTOCOL, // protocol: ir protocol
+ PANASONIC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ PANASONIC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ PANASONIC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ PANASONIC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ PANASONIC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ PANASONIC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ PANASONIC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ PANASONIC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ PANASONIC_ADDRESS_OFFSET, // address_offset: address offset
+ PANASONIC_ADDRESS_OFFSET + PANASONIC_ADDRESS_LEN, // address_end: end of address
+ PANASONIC_COMMAND_OFFSET, // command_offset: command offset
+ PANASONIC_COMMAND_OFFSET + PANASONIC_COMMAND_LEN, // command_end: end of command
+ PANASONIC_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ PANASONIC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ PANASONIC_LSB, // lsb_first: flag: LSB first
+ PANASONIC_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER mitsu_heavy_param =
+{
+ IRMP_MITSU_HEAVY_PROTOCOL, // protocol: ir protocol
+ MITSU_HEAVY_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ MITSU_HEAVY_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ MITSU_HEAVY_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ MITSU_HEAVY_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ MITSU_HEAVY_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ MITSU_HEAVY_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ MITSU_HEAVY_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ MITSU_HEAVY_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ MITSU_HEAVY_ADDRESS_OFFSET, // address_offset: address offset
+ MITSU_HEAVY_ADDRESS_OFFSET + MITSU_HEAVY_ADDRESS_LEN, // address_end: end of address
+ MITSU_HEAVY_COMMAND_OFFSET, // command_offset: command offset
+ MITSU_HEAVY_COMMAND_OFFSET + MITSU_HEAVY_COMMAND_LEN, // command_end: end of command
+ MITSU_HEAVY_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ MITSU_HEAVY_STOP_BIT, // stop_bit: flag: frame has stop bit
+ MITSU_HEAVY_LSB, // lsb_first: flag: LSB first
+ MITSU_HEAVY_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER vincent_param =
+{
+ IRMP_VINCENT_PROTOCOL, // protocol: ir protocol
+ VINCENT_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ VINCENT_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ VINCENT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ VINCENT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ VINCENT_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ VINCENT_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ VINCENT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ VINCENT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ VINCENT_ADDRESS_OFFSET, // address_offset: address offset
+ VINCENT_ADDRESS_OFFSET + VINCENT_ADDRESS_LEN, // address_end: end of address
+ VINCENT_COMMAND_OFFSET, // command_offset: command offset
+ VINCENT_COMMAND_OFFSET + VINCENT_COMMAND_LEN, // command_end: end of command
+ VINCENT_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ VINCENT_STOP_BIT, // stop_bit: flag: frame has stop bit
+ VINCENT_LSB, // lsb_first: flag: LSB first
+ VINCENT_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER recs80_param =
+{
+ IRMP_RECS80_PROTOCOL, // protocol: ir protocol
+ RECS80_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ RECS80_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ RECS80_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ RECS80_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ RECS80_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ RECS80_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ RECS80_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ RECS80_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ RECS80_ADDRESS_OFFSET, // address_offset: address offset
+ RECS80_ADDRESS_OFFSET + RECS80_ADDRESS_LEN, // address_end: end of address
+ RECS80_COMMAND_OFFSET, // command_offset: command offset
+ RECS80_COMMAND_OFFSET + RECS80_COMMAND_LEN, // command_end: end of command
+ RECS80_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RECS80_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RECS80_LSB, // lsb_first: flag: LSB first
+ RECS80_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rc5_param =
+{
+ IRMP_RC5_PROTOCOL, // protocol: ir protocol
+ RC5_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ RC5_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ RC5_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ RC5_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ RC5_ADDRESS_OFFSET, // address_offset: address offset
+ RC5_ADDRESS_OFFSET + RC5_ADDRESS_LEN, // address_end: end of address
+ RC5_COMMAND_OFFSET, // command_offset: command offset
+ RC5_COMMAND_OFFSET + RC5_COMMAND_LEN, // command_end: end of command
+ RC5_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RC5_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RC5_LSB, // lsb_first: flag: LSB first
+ RC5_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rcii_param =
+{
+ IRMP_RCII_PROTOCOL, // protocol: ir protocol
+ RCII_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ RCII_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ RCII_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ RCII_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ RCII_BIT_LEN_MIN, // pulse_0_len_min: here: not used
+ RCII_BIT_LEN_MAX, // pulse_0_len_max: here: not used
+ RCII_BIT_LEN_MIN, // pause_0_len_min: here: not used
+ RCII_BIT_LEN_MAX, // pause_0_len_max: here: not used
+ RCII_ADDRESS_OFFSET, // address_offset: address offset
+ RCII_ADDRESS_OFFSET + RCII_ADDRESS_LEN, // address_end: end of address
+ RCII_COMMAND_OFFSET, // command_offset: command offset
+ RCII_COMMAND_OFFSET + RCII_COMMAND_LEN, // command_end: end of command
+ RCII_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RCII_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RCII_LSB, // lsb_first: flag: LSB first
+ RCII_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER s100_param =
+{
+ IRMP_S100_PROTOCOL, // protocol: ir protocol
+ S100_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ S100_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ S100_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ S100_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ S100_ADDRESS_OFFSET, // address_offset: address offset
+ S100_ADDRESS_OFFSET + S100_ADDRESS_LEN, // address_end: end of address
+ S100_COMMAND_OFFSET, // command_offset: command offset
+ S100_COMMAND_OFFSET + S100_COMMAND_LEN, // command_end: end of command
+ S100_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ S100_STOP_BIT, // stop_bit: flag: frame has stop bit
+ S100_LSB, // lsb_first: flag: LSB first
+ S100_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER denon_param =
+{
+ IRMP_DENON_PROTOCOL, // protocol: ir protocol
+ DENON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ DENON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ DENON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ DENON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ DENON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ DENON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ DENON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ DENON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ DENON_ADDRESS_OFFSET, // address_offset: address offset
+ DENON_ADDRESS_OFFSET + DENON_ADDRESS_LEN, // address_end: end of address
+ DENON_COMMAND_OFFSET, // command_offset: command offset
+ DENON_COMMAND_OFFSET + DENON_COMMAND_LEN, // command_end: end of command
+ DENON_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ DENON_STOP_BIT, // stop_bit: flag: frame has stop bit
+ DENON_LSB, // lsb_first: flag: LSB first
+ DENON_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rc6_param =
+{
+ IRMP_RC6_PROTOCOL, // protocol: ir protocol
+
+ RC6_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ RC6_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ RC6_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ RC6_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ RC6_ADDRESS_OFFSET, // address_offset: address offset
+ RC6_ADDRESS_OFFSET + RC6_ADDRESS_LEN, // address_end: end of address
+ RC6_COMMAND_OFFSET, // command_offset: command offset
+ RC6_COMMAND_OFFSET + RC6_COMMAND_LEN, // command_end: end of command
+ RC6_COMPLETE_DATA_LEN_SHORT, // complete_len: complete length of frame
+ RC6_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RC6_LSB, // lsb_first: flag: LSB first
+ RC6_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER recs80ext_param =
+{
+ IRMP_RECS80EXT_PROTOCOL, // protocol: ir protocol
+ RECS80EXT_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ RECS80EXT_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ RECS80EXT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ RECS80EXT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ RECS80EXT_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ RECS80EXT_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ RECS80EXT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ RECS80EXT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ RECS80EXT_ADDRESS_OFFSET, // address_offset: address offset
+ RECS80EXT_ADDRESS_OFFSET + RECS80EXT_ADDRESS_LEN, // address_end: end of address
+ RECS80EXT_COMMAND_OFFSET, // command_offset: command offset
+ RECS80EXT_COMMAND_OFFSET + RECS80EXT_COMMAND_LEN, // command_end: end of command
+ RECS80EXT_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RECS80EXT_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RECS80EXT_LSB, // lsb_first: flag: LSB first
+ RECS80EXT_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nubert_param =
+{
+ IRMP_NUBERT_PROTOCOL, // protocol: ir protocol
+ NUBERT_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NUBERT_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NUBERT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NUBERT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NUBERT_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NUBERT_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NUBERT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NUBERT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ NUBERT_ADDRESS_OFFSET, // address_offset: address offset
+ NUBERT_ADDRESS_OFFSET + NUBERT_ADDRESS_LEN, // address_end: end of address
+ NUBERT_COMMAND_OFFSET, // command_offset: command offset
+ NUBERT_COMMAND_OFFSET + NUBERT_COMMAND_LEN, // command_end: end of command
+ NUBERT_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NUBERT_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NUBERT_LSB, // lsb_first: flag: LSB first
+ NUBERT_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER fan_param =
+{
+ IRMP_FAN_PROTOCOL, // protocol: ir protocol
+ FAN_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ FAN_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ FAN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ FAN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ FAN_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ FAN_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ FAN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ FAN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ FAN_ADDRESS_OFFSET, // address_offset: address offset
+ FAN_ADDRESS_OFFSET + FAN_ADDRESS_LEN, // address_end: end of address
+ FAN_COMMAND_OFFSET, // command_offset: command offset
+ FAN_COMMAND_OFFSET + FAN_COMMAND_LEN, // command_end: end of command
+ FAN_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ FAN_STOP_BIT, // stop_bit: flag: frame has NO stop bit
+ FAN_LSB, // lsb_first: flag: LSB first
+ FAN_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER speaker_param =
+{
+ IRMP_SPEAKER_PROTOCOL, // protocol: ir protocol
+ SPEAKER_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ SPEAKER_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ SPEAKER_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ SPEAKER_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ SPEAKER_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ SPEAKER_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ SPEAKER_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ SPEAKER_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ SPEAKER_ADDRESS_OFFSET, // address_offset: address offset
+ SPEAKER_ADDRESS_OFFSET + SPEAKER_ADDRESS_LEN, // address_end: end of address
+ SPEAKER_COMMAND_OFFSET, // command_offset: command offset
+ SPEAKER_COMMAND_OFFSET + SPEAKER_COMMAND_LEN, // command_end: end of command
+ SPEAKER_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ SPEAKER_STOP_BIT, // stop_bit: flag: frame has stop bit
+ SPEAKER_LSB, // lsb_first: flag: LSB first
+ SPEAKER_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER bang_olufsen_param =
+{
+ IRMP_BANG_OLUFSEN_PROTOCOL, // protocol: ir protocol
+ BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ BANG_OLUFSEN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ BANG_OLUFSEN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ BANG_OLUFSEN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ BANG_OLUFSEN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ BANG_OLUFSEN_ADDRESS_OFFSET, // address_offset: address offset
+ BANG_OLUFSEN_ADDRESS_OFFSET + BANG_OLUFSEN_ADDRESS_LEN, // address_end: end of address
+ BANG_OLUFSEN_COMMAND_OFFSET, // command_offset: command offset
+ BANG_OLUFSEN_COMMAND_OFFSET + BANG_OLUFSEN_COMMAND_LEN, // command_end: end of command
+ BANG_OLUFSEN_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ BANG_OLUFSEN_STOP_BIT, // stop_bit: flag: frame has stop bit
+ BANG_OLUFSEN_LSB, // lsb_first: flag: LSB first
+ BANG_OLUFSEN_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+
+static uint_fast8_t first_bit;
+
+static const PROGMEM IRMP_PARAMETER grundig_param =
+{
+ IRMP_GRUNDIG_PROTOCOL, // protocol: ir protocol
+
+ GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ GRUNDIG_ADDRESS_OFFSET, // address_offset: address offset
+ GRUNDIG_ADDRESS_OFFSET + GRUNDIG_ADDRESS_LEN, // address_end: end of address
+ GRUNDIG_COMMAND_OFFSET, // command_offset: command offset
+ GRUNDIG_COMMAND_OFFSET + GRUNDIG_COMMAND_LEN + 1, // command_end: end of command (USE 1 bit MORE to STORE NOKIA DATA!)
+ NOKIA_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: NOKIA instead of GRUNDIG!
+ GRUNDIG_NOKIA_IR60_STOP_BIT, // stop_bit: flag: frame has stop bit
+ GRUNDIG_NOKIA_IR60_LSB, // lsb_first: flag: LSB first
+ GRUNDIG_NOKIA_IR60_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER ruwido_param =
+{
+ IRMP_RUWIDO_PROTOCOL, // protocol: ir protocol
+ SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ RUWIDO_ADDRESS_OFFSET, // address_offset: address offset
+ RUWIDO_ADDRESS_OFFSET + RUWIDO_ADDRESS_LEN, // address_end: end of address
+ RUWIDO_COMMAND_OFFSET, // command_offset: command offset
+ RUWIDO_COMMAND_OFFSET + RUWIDO_COMMAND_LEN, // command_end: end of command
+ SIEMENS_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: SIEMENS instead of RUWIDO!
+ SIEMENS_OR_RUWIDO_STOP_BIT, // stop_bit: flag: frame has stop bit
+ SIEMENS_OR_RUWIDO_LSB, // lsb_first: flag: LSB first
+ SIEMENS_OR_RUWIDO_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER fdc_param =
+{
+ IRMP_FDC_PROTOCOL, // protocol: ir protocol
+ FDC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ FDC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ FDC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ FDC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ FDC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ FDC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ FDC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ FDC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ FDC_ADDRESS_OFFSET, // address_offset: address offset
+ FDC_ADDRESS_OFFSET + FDC_ADDRESS_LEN, // address_end: end of address
+ FDC_COMMAND_OFFSET, // command_offset: command offset
+ FDC_COMMAND_OFFSET + FDC_COMMAND_LEN, // command_end: end of command
+ FDC_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ FDC_STOP_BIT, // stop_bit: flag: frame has stop bit
+ FDC_LSB, // lsb_first: flag: LSB first
+ FDC_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rccar_param =
+{
+ IRMP_RCCAR_PROTOCOL, // protocol: ir protocol
+ RCCAR_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ RCCAR_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ RCCAR_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ RCCAR_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ RCCAR_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ RCCAR_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ RCCAR_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ RCCAR_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ RCCAR_ADDRESS_OFFSET, // address_offset: address offset
+ RCCAR_ADDRESS_OFFSET + RCCAR_ADDRESS_LEN, // address_end: end of address
+ RCCAR_COMMAND_OFFSET, // command_offset: command offset
+ RCCAR_COMMAND_OFFSET + RCCAR_COMMAND_LEN, // command_end: end of command
+ RCCAR_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RCCAR_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RCCAR_LSB, // lsb_first: flag: LSB first
+ RCCAR_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nikon_param =
+{
+ IRMP_NIKON_PROTOCOL, // protocol: ir protocol
+ NIKON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ NIKON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ NIKON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ NIKON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ NIKON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ NIKON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ NIKON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ NIKON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ NIKON_ADDRESS_OFFSET, // address_offset: address offset
+ NIKON_ADDRESS_OFFSET + NIKON_ADDRESS_LEN, // address_end: end of address
+ NIKON_COMMAND_OFFSET, // command_offset: command offset
+ NIKON_COMMAND_OFFSET + NIKON_COMMAND_LEN, // command_end: end of command
+ NIKON_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NIKON_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NIKON_LSB, // lsb_first: flag: LSB first
+ NIKON_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER kathrein_param =
+{
+ IRMP_KATHREIN_PROTOCOL, // protocol: ir protocol
+ KATHREIN_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ KATHREIN_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ KATHREIN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ KATHREIN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ KATHREIN_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ KATHREIN_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ KATHREIN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ KATHREIN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ KATHREIN_ADDRESS_OFFSET, // address_offset: address offset
+ KATHREIN_ADDRESS_OFFSET + KATHREIN_ADDRESS_LEN, // address_end: end of address
+ KATHREIN_COMMAND_OFFSET, // command_offset: command offset
+ KATHREIN_COMMAND_OFFSET + KATHREIN_COMMAND_LEN, // command_end: end of command
+ KATHREIN_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ KATHREIN_STOP_BIT, // stop_bit: flag: frame has stop bit
+ KATHREIN_LSB, // lsb_first: flag: LSB first
+ KATHREIN_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER netbox_param =
+{
+ IRMP_NETBOX_PROTOCOL, // protocol: ir protocol
+ NETBOX_PULSE_LEN, // pulse_1_len_min: minimum length of pulse with bit value 1, here: exact value
+ NETBOX_PULSE_REST_LEN, // pulse_1_len_max: maximum length of pulse with bit value 1, here: rest value
+ NETBOX_PAUSE_LEN, // pause_1_len_min: minimum length of pause with bit value 1, here: exact value
+ NETBOX_PAUSE_REST_LEN, // pause_1_len_max: maximum length of pause with bit value 1, here: rest value
+ NETBOX_PULSE_LEN, // pulse_0_len_min: minimum length of pulse with bit value 0, here: exact value
+ NETBOX_PULSE_REST_LEN, // pulse_0_len_max: maximum length of pulse with bit value 0, here: rest value
+ NETBOX_PAUSE_LEN, // pause_0_len_min: minimum length of pause with bit value 0, here: exact value
+ NETBOX_PAUSE_REST_LEN, // pause_0_len_max: maximum length of pause with bit value 0, here: rest value
+ NETBOX_ADDRESS_OFFSET, // address_offset: address offset
+ NETBOX_ADDRESS_OFFSET + NETBOX_ADDRESS_LEN, // address_end: end of address
+ NETBOX_COMMAND_OFFSET, // command_offset: command offset
+ NETBOX_COMMAND_OFFSET + NETBOX_COMMAND_LEN, // command_end: end of command
+ NETBOX_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ NETBOX_STOP_BIT, // stop_bit: flag: frame has stop bit
+ NETBOX_LSB, // lsb_first: flag: LSB first
+ NETBOX_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER lego_param =
+{
+ IRMP_LEGO_PROTOCOL, // protocol: ir protocol
+ LEGO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ LEGO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ LEGO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ LEGO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ LEGO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ LEGO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ LEGO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ LEGO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ LEGO_ADDRESS_OFFSET, // address_offset: address offset
+ LEGO_ADDRESS_OFFSET + LEGO_ADDRESS_LEN, // address_end: end of address
+ LEGO_COMMAND_OFFSET, // command_offset: command offset
+ LEGO_COMMAND_OFFSET + LEGO_COMMAND_LEN, // command_end: end of command
+ LEGO_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ LEGO_STOP_BIT, // stop_bit: flag: frame has stop bit
+ LEGO_LSB, // lsb_first: flag: LSB first
+ LEGO_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER irmp16_param =
+{
+ IRMP_IRMP16_PROTOCOL, // protocol: ir protocol
+ IRMP16_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ IRMP16_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ IRMP16_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ IRMP16_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ IRMP16_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ IRMP16_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ IRMP16_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ IRMP16_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ IRMP16_ADDRESS_OFFSET, // address_offset: address offset
+ IRMP16_ADDRESS_OFFSET + IRMP16_ADDRESS_LEN, // address_end: end of address
+ IRMP16_COMMAND_OFFSET, // command_offset: command offset
+ IRMP16_COMMAND_OFFSET + IRMP16_COMMAND_LEN, // command_end: end of command
+ IRMP16_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ IRMP16_STOP_BIT, // stop_bit: flag: frame has stop bit
+ IRMP16_LSB, // lsb_first: flag: LSB first
+ IRMP16_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER gree_param =
+{
+ IRMP_GREE_PROTOCOL, // protocol: ir protocol
+ GREE_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ GREE_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ GREE_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ GREE_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ GREE_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ GREE_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ GREE_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ GREE_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ GREE_ADDRESS_OFFSET, // address_offset: address offset
+ GREE_ADDRESS_OFFSET + GREE_ADDRESS_LEN, // address_end: end of address
+ GREE_COMMAND_OFFSET, // command_offset: command offset
+ GREE_COMMAND_OFFSET + GREE_COMMAND_LEN, // command_end: end of command
+ GREE_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ GREE_STOP_BIT, // stop_bit: flag: frame has stop bit
+ GREE_LSB, // lsb_first: flag: LSB first
+ GREE_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER thomson_param =
+{
+ IRMP_THOMSON_PROTOCOL, // protocol: ir protocol
+ THOMSON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ THOMSON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ THOMSON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ THOMSON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ THOMSON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ THOMSON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ THOMSON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ THOMSON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ THOMSON_ADDRESS_OFFSET, // address_offset: address offset
+ THOMSON_ADDRESS_OFFSET + THOMSON_ADDRESS_LEN, // address_end: end of address
+ THOMSON_COMMAND_OFFSET, // command_offset: command offset
+ THOMSON_COMMAND_OFFSET + THOMSON_COMMAND_LEN, // command_end: end of command
+ THOMSON_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ THOMSON_STOP_BIT, // stop_bit: flag: frame has stop bit
+ THOMSON_LSB, // lsb_first: flag: LSB first
+ THOMSON_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER bose_param =
+{
+ IRMP_BOSE_PROTOCOL, // protocol: ir protocol
+ BOSE_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ BOSE_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ BOSE_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ BOSE_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ BOSE_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ BOSE_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ BOSE_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ BOSE_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ BOSE_ADDRESS_OFFSET, // address_offset: address offset
+ BOSE_ADDRESS_OFFSET + BOSE_ADDRESS_LEN, // address_end: end of address
+ BOSE_COMMAND_OFFSET, // command_offset: command offset
+ BOSE_COMMAND_OFFSET + BOSE_COMMAND_LEN, // command_end: end of command
+ BOSE_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ BOSE_STOP_BIT, // stop_bit: flag: frame has stop bit
+ BOSE_LSB, // lsb_first: flag: LSB first
+ BOSE_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER a1tvbox_param =
+{
+ IRMP_A1TVBOX_PROTOCOL, // protocol: ir protocol
+
+ A1TVBOX_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ A1TVBOX_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ A1TVBOX_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ A1TVBOX_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ A1TVBOX_ADDRESS_OFFSET, // address_offset: address offset
+ A1TVBOX_ADDRESS_OFFSET + A1TVBOX_ADDRESS_LEN, // address_end: end of address
+ A1TVBOX_COMMAND_OFFSET, // command_offset: command offset
+ A1TVBOX_COMMAND_OFFSET + A1TVBOX_COMMAND_LEN, // command_end: end of command
+ A1TVBOX_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ A1TVBOX_STOP_BIT, // stop_bit: flag: frame has stop bit
+ A1TVBOX_LSB, // lsb_first: flag: LSB first
+ A1TVBOX_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER merlin_param =
+{
+ IRMP_MERLIN_PROTOCOL, // protocol: ir protocol
+
+ MERLIN_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ MERLIN_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ MERLIN_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ MERLIN_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ MERLIN_ADDRESS_OFFSET, // address_offset: address offset
+ MERLIN_ADDRESS_OFFSET + MERLIN_ADDRESS_LEN, // address_end: end of address
+ MERLIN_COMMAND_OFFSET, // command_offset: command offset
+ MERLIN_COMMAND_OFFSET + MERLIN_COMMAND_LEN, // command_end: end of command
+ MERLIN_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ MERLIN_STOP_BIT, // stop_bit: flag: frame has stop bit
+ MERLIN_LSB, // lsb_first: flag: LSB first
+ MERLIN_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER ortek_param =
+{
+ IRMP_ORTEK_PROTOCOL, // protocol: ir protocol
+
+ ORTEK_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ ORTEK_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ ORTEK_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause
+ ORTEK_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause
+ 0, // pulse_0_len_min: here: not used
+ 0, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ ORTEK_ADDRESS_OFFSET, // address_offset: address offset
+ ORTEK_ADDRESS_OFFSET + ORTEK_ADDRESS_LEN, // address_end: end of address
+ ORTEK_COMMAND_OFFSET, // command_offset: command offset
+ ORTEK_COMMAND_OFFSET + ORTEK_COMMAND_LEN, // command_end: end of command
+ ORTEK_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ ORTEK_STOP_BIT, // stop_bit: flag: frame has stop bit
+ ORTEK_LSB, // lsb_first: flag: LSB first
+ ORTEK_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER roomba_param =
+{
+ IRMP_ROOMBA_PROTOCOL, // protocol: ir protocol
+ ROOMBA_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ ROOMBA_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ ROOMBA_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ ROOMBA_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ ROOMBA_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ ROOMBA_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ ROOMBA_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ ROOMBA_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ ROOMBA_ADDRESS_OFFSET, // address_offset: address offset
+ ROOMBA_ADDRESS_OFFSET + ROOMBA_ADDRESS_LEN, // address_end: end of address
+ ROOMBA_COMMAND_OFFSET, // command_offset: command offset
+ ROOMBA_COMMAND_OFFSET + ROOMBA_COMMAND_LEN, // command_end: end of command
+ ROOMBA_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ ROOMBA_STOP_BIT, // stop_bit: flag: frame has stop bit
+ ROOMBA_LSB, // lsb_first: flag: LSB first
+ ROOMBA_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rcmm_param =
+{
+ IRMP_RCMM32_PROTOCOL, // protocol: ir protocol
+
+ RCMM32_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse
+ RCMM32_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse
+ 0, // pause_1_len_min: here: minimum length of short pause
+ 0, // pause_1_len_max: here: maximum length of short pause
+ RCMM32_BIT_PULSE_LEN_MIN, // pulse_0_len_min: here: not used
+ RCMM32_BIT_PULSE_LEN_MAX, // pulse_0_len_max: here: not used
+ 0, // pause_0_len_min: here: not used
+ 0, // pause_0_len_max: here: not used
+ RCMM32_ADDRESS_OFFSET, // address_offset: address offset
+ RCMM32_ADDRESS_OFFSET + RCMM32_ADDRESS_LEN, // address_end: end of address
+ RCMM32_COMMAND_OFFSET, // command_offset: command offset
+ RCMM32_COMMAND_OFFSET + RCMM32_COMMAND_LEN, // command_end: end of command
+ RCMM32_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RCMM32_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RCMM32_LSB, // lsb_first: flag: LSB first
+ RCMM32_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER pentax_param =
+{
+ IRMP_PENTAX_PROTOCOL, // protocol: ir protocol
+ PENTAX_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ PENTAX_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ PENTAX_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ PENTAX_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ PENTAX_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ PENTAX_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ PENTAX_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ PENTAX_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ PENTAX_ADDRESS_OFFSET, // address_offset: address offset
+ PENTAX_ADDRESS_OFFSET + PENTAX_ADDRESS_LEN, // address_end: end of address
+ PENTAX_COMMAND_OFFSET, // command_offset: command offset
+ PENTAX_COMMAND_OFFSET + PENTAX_COMMAND_LEN, // command_end: end of command
+ PENTAX_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ PENTAX_STOP_BIT, // stop_bit: flag: frame has stop bit
+ PENTAX_LSB, // lsb_first: flag: LSB first
+ PENTAX_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER acp24_param =
+{
+ IRMP_ACP24_PROTOCOL, // protocol: ir protocol
+ ACP24_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ ACP24_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ ACP24_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ ACP24_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ ACP24_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ ACP24_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ ACP24_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ ACP24_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ ACP24_ADDRESS_OFFSET, // address_offset: address offset
+ ACP24_ADDRESS_OFFSET + ACP24_ADDRESS_LEN, // address_end: end of address
+ ACP24_COMMAND_OFFSET, // command_offset: command offset
+ ACP24_COMMAND_OFFSET + ACP24_COMMAND_LEN, // command_end: end of command
+ ACP24_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ ACP24_STOP_BIT, // stop_bit: flag: frame has stop bit
+ ACP24_LSB, // lsb_first: flag: LSB first
+ ACP24_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER metz_param =
+{
+ IRMP_METZ_PROTOCOL, // protocol: ir protocol
+ METZ_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ METZ_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ METZ_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ METZ_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ METZ_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ METZ_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ METZ_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ METZ_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ METZ_ADDRESS_OFFSET, // address_offset: address offset
+ METZ_ADDRESS_OFFSET + METZ_ADDRESS_LEN, // address_end: end of address
+ METZ_COMMAND_OFFSET, // command_offset: command offset
+ METZ_COMMAND_OFFSET + METZ_COMMAND_LEN, // command_end: end of command
+ METZ_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ METZ_STOP_BIT, // stop_bit: flag: frame has stop bit
+ METZ_LSB, // lsb_first: flag: LSB first
+ METZ_FLAGS // flags: some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RADIO1_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER radio1_param =
+{
+ IRMP_RADIO1_PROTOCOL, // protocol: ir protocol
+
+ RADIO1_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1
+ RADIO1_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1
+ RADIO1_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1
+ RADIO1_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1
+ RADIO1_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0
+ RADIO1_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0
+ RADIO1_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0
+ RADIO1_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0
+ RADIO1_ADDRESS_OFFSET, // address_offset: address offset
+ RADIO1_ADDRESS_OFFSET + RADIO1_ADDRESS_LEN, // address_end: end of address
+ RADIO1_COMMAND_OFFSET, // command_offset: command offset
+ RADIO1_COMMAND_OFFSET + RADIO1_COMMAND_LEN, // command_end: end of command
+ RADIO1_COMPLETE_DATA_LEN, // complete_len: complete length of frame
+ RADIO1_STOP_BIT, // stop_bit: flag: frame has stop bit
+ RADIO1_LSB, // lsb_first: flag: LSB first
+ RADIO1_FLAGS // flags: some flags
+};
+
+#endif
+
+static uint_fast8_t irmp_bit; // current bit position
+static IRMP_PARAMETER irmp_param;
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+static IRMP_PARAMETER irmp_param2;
+#endif
+
+static volatile uint_fast8_t irmp_ir_detected = FALSE;
+static volatile uint_fast8_t irmp_protocol;
+static volatile uint_fast16_t irmp_address;
+#if IRMP_32_BIT == 1
+static volatile uint_fast32_t irmp_command;
+#else
+static volatile uint_fast16_t irmp_command;
+#endif
+static volatile uint_fast16_t irmp_id; // only used for SAMSUNG protocol
+static volatile uint_fast8_t irmp_flags;
+// static volatile uint_fast8_t irmp_busy_flag;
+
+#if defined(__MBED__)
+// DigitalIn inputPin(IRMP_PIN, PullUp); // this requires mbed.h and source to be compiled as cpp
+gpio_t gpioIRin; // use low level c function instead
+#endif
+
+
+#ifdef ANALYZE
+#define input(x) (x)
+static uint_fast8_t IRMP_PIN;
+static uint_fast8_t radio;
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Initialize IRMP decoder
+ * @details Configures IRMP input pin
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef ANALYZE
+void
+irmp_init (void)
+{
+#if defined(PIC_CCS) || defined(PIC_C18) // PIC: do nothing
+#elif defined (ARM_STM32_HAL) // STM32 with Hal Library: do nothing
+#elif defined (ARM_STM32) // STM32
+ GPIO_InitTypeDef GPIO_InitStructure;
+
+ /* GPIOx clock enable */
+# if defined (ARM_STM32L1XX)
+ RCC_AHBPeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+# elif defined (ARM_STM32F10X)
+ RCC_APB2PeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+# elif defined (ARM_STM32F4XX)
+ RCC_AHB1PeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+# endif
+
+ /* GPIO Configuration */
+ GPIO_InitStructure.GPIO_Pin = IRMP_BIT;
+# if defined (ARM_STM32L1XX) || defined (ARM_STM32F4XX)
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+# elif defined (ARM_STM32F10X)
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+# endif
+ GPIO_Init(IRMP_PORT, &GPIO_InitStructure);
+
+#elif defined(STELLARIS_ARM_CORTEX_M4)
+ // Enable the GPIO port
+ ROM_SysCtlPeripheralEnable(IRMP_PORT_PERIPH);
+
+ // Set as an input
+ ROM_GPIODirModeSet(IRMP_PORT_BASE, IRMP_PORT_PIN, GPIO_DIR_MODE_IN);
+ ROM_GPIOPadConfigSet(IRMP_PORT_BASE, IRMP_PORT_PIN, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
+
+#elif defined(__SDCC_stm8) // STM8
+ IRMP_GPIO_STRUCT->DDR &= ~(1<<IRMP_BIT); // pin is input
+ IRMP_GPIO_STRUCT->CR1 |= (1<<IRMP_BIT); // activate pullup
+
+#elif defined (TEENSY_ARM_CORTEX_M4) // TEENSY
+ pinMode(IRMP_PIN, INPUT);
+
+#elif defined(__xtensa__) // ESP8266
+ pinMode(IRMP_BIT_NUMBER, INPUT);
+ // select pin function
+# if (IRMP_BIT_NUMBER == 12)
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);
+// doesn't work for me:
+// # elif (IRMP_BIT_NUMBER == 13)
+// PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U , FUNC_GPIO13);
+# else
+# warning Please add PIN_FUNC_SELECT when necessary.
+# endif
+ GPIO_DIS_OUTPUT(IRMP_BIT_NUMBER);
+
+#elif defined(__MBED__)
+ gpio_init_in_ex(&gpioIRin, IRMP_PIN, IRMP_PINMODE); // initialize input for IR diode
+
+#elif defined(_CHIBIOS_HAL_)
+ // ChibiOS HAL automatically initializes all pins according to the board config file, no need to repeat here
+
+#else // AVR
+ IRMP_PORT &= ~(1<<IRMP_BIT); // deactivate pullup
+ IRMP_DDR &= ~(1<<IRMP_BIT); // set pin to input
+#endif
+
+#if IRMP_LOGGING == 1
+ irmp_uart_init ();
+#endif
+}
+#endif
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Get IRMP data
+ * @details gets decoded IRMP data
+ * @param pointer in order to store IRMP data
+ * @return TRUE: successful, FALSE: failed
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+uint_fast8_t
+irmp_get_data (IRMP_DATA * irmp_data_p)
+{
+ uint_fast8_t rtc = FALSE;
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+ uint_fast8_t cmd_len = 0;
+#endif
+
+ if (irmp_ir_detected)
+ {
+ switch (irmp_protocol)
+ {
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ case IRMP_SAMSUNG_PROTOCOL:
+ if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+ {
+ irmp_command &= 0xff;
+ irmp_command |= irmp_id << 8;
+ rtc = TRUE;
+ }
+ break;
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+ case IRMP_SAMSUNG48_PROTOCOL:
+ irmp_command = (irmp_command & 0x00FF) | ((irmp_id & 0x00FF) << 8);
+ rtc = TRUE;
+ break;
+#endif
+#endif
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ case IRMP_NEC_PROTOCOL:
+ if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+ {
+ irmp_command &= 0xff;
+ rtc = TRUE;
+ }
+ else if (irmp_address == 0x87EE)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to APPLE protocol\n");
+#endif // ANALYZE
+ irmp_protocol = IRMP_APPLE_PROTOCOL;
+ irmp_address = (irmp_command & 0xFF00) >> 8;
+ irmp_command &= 0x00FF;
+ rtc = TRUE;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to ONKYO protocol\n");
+#endif // ANALYZE
+ irmp_protocol = IRMP_ONKYO_PROTOCOL;
+ rtc = TRUE;
+ }
+ break;
+#endif
+
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ case IRMP_VINCENT_PROTOCOL:
+ if ((irmp_command >> 8) == (irmp_command & 0x00FF))
+ {
+ irmp_command &= 0xff;
+ rtc = TRUE;
+ }
+ break;
+#endif
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+ case IRMP_BOSE_PROTOCOL:
+ if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+ {
+ irmp_command &= 0xff;
+ rtc = TRUE;
+ }
+ break;
+#endif
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+ case IRMP_MERLIN_PROTOCOL:
+ if (irmp_bit == 10)
+ {
+ rtc = TRUE;
+ }
+ else if (irmp_bit >= 19 && ((irmp_bit - 3) % 8 == 0))
+ {
+ if (((irmp_command >> 1) & 1) != (irmp_command & 1))
+ {
+ irmp_command >>= 1;
+ irmp_command |= ((irmp_address & 1) << (irmp_bit - 12));
+ irmp_address >>= 1;
+ cmd_len = (irmp_bit - 11) >> 3;
+ rtc = TRUE;
+ }
+ }
+ break;
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+ case IRMP_SIEMENS_PROTOCOL:
+ case IRMP_RUWIDO_PROTOCOL:
+ if (((irmp_command >> 1) & 0x0001) == (~irmp_command & 0x0001))
+ {
+ irmp_command >>= 1;
+ rtc = TRUE;
+ }
+ break;
+#endif
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+ case IRMP_KATHREIN_PROTOCOL:
+ if (irmp_command != 0x0000)
+ {
+ rtc = TRUE;
+ }
+ break;
+#endif
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+ case IRMP_RC5_PROTOCOL:
+ irmp_address &= ~0x20; // clear toggle bit
+ rtc = TRUE;
+ break;
+#endif
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+ case IRMP_S100_PROTOCOL:
+ irmp_address &= ~0x20; // clear toggle bit
+ rtc = TRUE;
+ break;
+#endif
+#if IRMP_SUPPORT_IR60_PROTOCOL == 1
+ case IRMP_IR60_PROTOCOL:
+ if (irmp_command != 0x007d) // 0x007d (== 62<<1 + 1) is start instruction frame
+ {
+ rtc = TRUE;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF("Info IR60: got start instruction frame\n");
+#endif // ANALYZE
+ }
+ break;
+#endif
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ case IRMP_RCCAR_PROTOCOL:
+ // frame in irmp_data:
+ // Bit 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // V D7 D6 D5 D4 D3 D2 D1 D0 A1 A0 C1 C0 // 10 9 8 7 6 5 4 3 2 1 0
+ irmp_address = (irmp_command & 0x000C) >> 2; // addr: 0 0 0 0 0 0 0 0 0 A1 A0
+ irmp_command = ((irmp_command & 0x1000) >> 2) | // V-Bit: V 0 0 0 0 0 0 0 0 0 0
+ ((irmp_command & 0x0003) << 8) | // C-Bits: 0 C1 C0 0 0 0 0 0 0 0 0
+ ((irmp_command & 0x0FF0) >> 4); // D-Bits: D7 D6 D5 D4 D3 D2 D1 D0
+ rtc = TRUE; // Summe: V C1 C0 D7 D6 D5 D4 D3 D2 D1 D0
+ break;
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 // squeeze code to 8 bit, upper bit indicates release-key
+ case IRMP_NETBOX_PROTOCOL:
+ if (irmp_command & 0x1000) // last bit set?
+ {
+ if ((irmp_command & 0x1f) == 0x15) // key pressed: 101 01 (LSB)
+ {
+ irmp_command >>= 5;
+ irmp_command &= 0x7F;
+ rtc = TRUE;
+ }
+ else if ((irmp_command & 0x1f) == 0x10) // key released: 000 01 (LSB)
+ {
+ irmp_command >>= 5;
+ irmp_command |= 0x80;
+ rtc = TRUE;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF("error NETBOX: bit6/7 must be 0/1\n");
+#endif // ANALYZE
+ }
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF("error NETBOX: last bit not set\n");
+#endif // ANALYZE
+ }
+ break;
+#endif
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+ case IRMP_LEGO_PROTOCOL:
+ {
+ uint_fast8_t crc = 0x0F ^ ((irmp_command & 0xF000) >> 12) ^ ((irmp_command & 0x0F00) >> 8) ^ ((irmp_command & 0x00F0) >> 4);
+
+ if ((irmp_command & 0x000F) == crc)
+ {
+ irmp_command >>= 4;
+ rtc = TRUE;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("CRC error in LEGO protocol\n");
+#endif // ANALYZE
+ // rtc = TRUE; // don't accept codes with CRC errors
+ }
+ break;
+ }
+#endif
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+ case IRMP_METZ_PROTOCOL:
+ irmp_address &= ~0x40; // clear toggle bit
+ if (((~irmp_address) & 0x07) == (irmp_address >> 3) && ((~irmp_command) & 0x3f) == (irmp_command >> 6))
+ {
+ irmp_address >>= 3;
+ irmp_command >>= 6;
+ rtc = TRUE;
+ }
+ break;
+#endif
+ default:
+ {
+ rtc = TRUE;
+ break;
+ }
+ }
+
+ if (rtc)
+ {
+ irmp_data_p->protocol = irmp_protocol;
+ irmp_data_p->address = irmp_address;
+ irmp_data_p->command = irmp_command;
+ irmp_data_p->flags = irmp_flags;
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+ irmp_data_p->flags |= cmd_len;
+#endif
+ }
+ else
+ {
+ irmp_protocol = IRMP_UNKNOWN_PROTOCOL;
+ }
+
+ irmp_command = 0; // don't reset irmp_protocol here, needed for detection of NEC & JVC repetition frames!
+ irmp_address = 0;
+ irmp_flags = 0;
+
+ irmp_ir_detected = FALSE;
+ }
+
+ return rtc;
+}
+
+#if IRMP_USE_CALLBACK == 1
+void
+irmp_set_callback_ptr (void (*cb)(uint_fast8_t))
+{
+ irmp_callback_ptr = cb;
+}
+#endif // IRMP_USE_CALLBACK == 1
+
+// these statics must not be volatile, because they are only used by irmp_store_bit(), which is called by irmp_ISR()
+static uint_fast16_t irmp_tmp_address; // ir address
+#if IRMP_32_BIT == 1
+static uint_fast32_t irmp_tmp_command; // ir command
+#else
+static uint_fast16_t irmp_tmp_command; // ir command
+#endif
+
+#if (IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)) || IRMP_SUPPORT_NEC42_PROTOCOL == 1
+static uint_fast16_t irmp_tmp_address2; // ir address
+static uint_fast16_t irmp_tmp_command2; // ir command
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+static uint_fast16_t irmp_lgair_address; // ir address
+static uint_fast16_t irmp_lgair_command; // ir command
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+static uint_fast16_t irmp_tmp_id; // ir id (only SAMSUNG)
+#endif
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+static uint8_t xor_check[6]; // check kaseikyo "parity" bits
+static uint_fast8_t genre2; // save genre2 bits here, later copied to MSB in flags
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+static uint_fast8_t parity; // number of '1' of the first 14 bits, check if even.
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+static uint_fast8_t check; // number of '1' of the first 14 bits, check if even.
+static uint_fast8_t mitsu_parity; // number of '1' of the first 14 bits, check if even.
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * store bit
+ * @details store bit in temp address or temp command
+ * @param value to store: 0 or 1
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+// verhindert, dass irmp_store_bit() inline compiliert wird:
+// static void irmp_store_bit (uint_fast8_t) __attribute__ ((noinline));
+
+static void
+irmp_store_bit (uint_fast8_t value)
+{
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_ACP24_PROTOCOL) // squeeze 64 bits into 16 bits:
+ {
+ if (value)
+ {
+ // ACP24-Frame:
+ // 1 2 3 4 5 6
+ // 0123456789012345678901234567890123456789012345678901234567890123456789
+ // N VVMMM ? ??? t vmA x y TTTT
+ //
+ // irmp_data_p->command:
+ //
+ // 5432109876543210
+ // NAVVvMMMmtxyTTTT
+
+ switch (irmp_bit)
+ {
+ case 0: irmp_tmp_command |= (1<<15); break; // N
+ case 2: irmp_tmp_command |= (1<<13); break; // V
+ case 3: irmp_tmp_command |= (1<<12); break; // V
+ case 4: irmp_tmp_command |= (1<<10); break; // M
+ case 5: irmp_tmp_command |= (1<< 9); break; // M
+ case 6: irmp_tmp_command |= (1<< 8); break; // M
+ case 20: irmp_tmp_command |= (1<< 6); break; // t
+ case 22: irmp_tmp_command |= (1<<11); break; // v
+ case 23: irmp_tmp_command |= (1<< 7); break; // m
+ case 24: irmp_tmp_command |= (1<<14); break; // A
+ case 26: irmp_tmp_command |= (1<< 5); break; // x
+ case 44: irmp_tmp_command |= (1<< 4); break; // y
+ case 66: irmp_tmp_command |= (1<< 3); break; // T
+ case 67: irmp_tmp_command |= (1<< 2); break; // T
+ case 68: irmp_tmp_command |= (1<< 1); break; // T
+ case 69: irmp_tmp_command |= (1<< 0); break; // T
+ }
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_ACP24_PROTOCOL
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL)
+ {
+ if (irmp_bit < 14)
+ {
+ if (value)
+ {
+ parity++;
+ }
+ }
+ else if (irmp_bit == 14)
+ {
+ if (value) // value == 1: even parity
+ {
+ if (parity & 0x01)
+ {
+ parity = PARITY_CHECK_FAILED;
+ }
+ else
+ {
+ parity = PARITY_CHECK_OK;
+ }
+ }
+ else
+ {
+ if (parity & 0x01) // value == 0: odd parity
+ {
+ parity = PARITY_CHECK_OK;
+ }
+ else
+ {
+ parity = PARITY_CHECK_FAILED;
+ }
+ }
+ }
+ }
+ else
+#endif
+ {
+ ;
+ }
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+ if (irmp_bit == 0 && irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL)
+ {
+ first_bit = value;
+ }
+ else
+#endif
+
+ if (irmp_bit >= irmp_param.address_offset && irmp_bit < irmp_param.address_end)
+ {
+ if (irmp_param.lsb_first)
+ {
+ irmp_tmp_address |= (((uint_fast16_t) (value)) << (irmp_bit - irmp_param.address_offset)); // CV wants cast
+ }
+ else
+ {
+ irmp_tmp_address <<= 1;
+ irmp_tmp_address |= value;
+ }
+ }
+ else if (irmp_bit >= irmp_param.command_offset && irmp_bit < irmp_param.command_end)
+ {
+ if (irmp_param.lsb_first)
+ {
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL && irmp_bit >= 32)
+ {
+ irmp_tmp_id |= (((uint_fast16_t) (value)) << (irmp_bit - 32)); // CV wants cast
+ }
+ else
+#endif
+ {
+ irmp_tmp_command |= (((uint_fast16_t) (value)) << (irmp_bit - irmp_param.command_offset)); // CV wants cast
+ }
+ }
+ else
+ {
+ irmp_tmp_command <<= 1;
+ irmp_tmp_command |= value;
+ }
+ }
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NEC_PROTOCOL || irmp_param.protocol == IRMP_NEC42_PROTOCOL)
+ {
+ if (irmp_bit < 8)
+ {
+ irmp_lgair_address <<= 1; // LGAIR uses MSB
+ irmp_lgair_address |= value;
+ }
+ else if (irmp_bit < 24)
+ {
+ irmp_lgair_command <<= 1; // LGAIR uses MSB
+ irmp_lgair_command |= value;
+ }
+ }
+ // NO else!
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit >= 13 && irmp_bit < 26)
+ {
+ irmp_tmp_address2 |= (((uint_fast16_t) (value)) << (irmp_bit - 13)); // CV wants cast
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit >= SAMSUNG_ID_OFFSET && irmp_bit < SAMSUNG_ID_OFFSET + SAMSUNG_ID_LEN)
+ {
+ irmp_tmp_id |= (((uint_fast16_t) (value)) << (irmp_bit - SAMSUNG_ID_OFFSET)); // store with LSB first
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL)
+ {
+ if (irmp_bit >= 20 && irmp_bit < 24)
+ {
+ irmp_tmp_command |= (((uint_fast16_t) (value)) << (irmp_bit - 8)); // store 4 system bits (genre 1) in upper nibble with LSB first
+ }
+ else if (irmp_bit >= 24 && irmp_bit < 28)
+ {
+ genre2 |= (((uint_fast8_t) (value)) << (irmp_bit - 20)); // store 4 system bits (genre 2) in upper nibble with LSB first
+ }
+
+ if (irmp_bit < KASEIKYO_COMPLETE_DATA_LEN)
+ {
+ if (value)
+ {
+ xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8);
+ }
+ else
+ {
+ xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8));
+ }
+ }
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_MITSU_HEAVY_PROTOCOL) // squeeze 64 bits into 16 bits:
+ {
+ if (irmp_bit == 72 )
+ { // irmp_tmp_address, irmp_tmp_command received: check parity & compress
+ mitsu_parity = PARITY_CHECK_OK;
+
+ check = irmp_tmp_address >> 8; // inverted upper byte == lower byte?
+ check = ~ check;
+
+ if (check == (irmp_tmp_address & 0xFF))
+ { // ok:
+ irmp_tmp_address <<= 8; // throw away upper byte
+ }
+ else
+ {
+ mitsu_parity = PARITY_CHECK_FAILED;
+ }
+
+ check = irmp_tmp_command >> 8; // inverted upper byte == lower byte?
+ check = ~ check;
+ if (check == (irmp_tmp_command & 0xFF))
+ { // ok: pack together
+ irmp_tmp_address |= irmp_tmp_command & 0xFF; // byte 1, byte2 in irmp_tmp_address, irmp_tmp_command can be used for byte 3
+ }
+ else
+ {
+ mitsu_parity = PARITY_CHECK_FAILED;
+ }
+ irmp_tmp_command = 0;
+ }
+
+ if (irmp_bit >= 72 )
+ { // receive 3. word in irmp_tmp_command
+ irmp_tmp_command <<= 1;
+ irmp_tmp_command |= value;
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+ {
+ ;
+ }
+
+ irmp_bit++;
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * store bit
+ * @details store bit in temp address or temp command
+ * @param value to store: 0 or 1
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+static void
+irmp_store_bit2 (uint_fast8_t value)
+{
+ uint_fast8_t irmp_bit2;
+
+ if (irmp_param.protocol)
+ {
+ irmp_bit2 = irmp_bit - 2;
+ }
+ else
+ {
+ irmp_bit2 = irmp_bit - 1;
+ }
+
+ if (irmp_bit2 >= irmp_param2.address_offset && irmp_bit2 < irmp_param2.address_end)
+ {
+ irmp_tmp_address2 |= (((uint_fast16_t) (value)) << (irmp_bit2 - irmp_param2.address_offset)); // CV wants cast
+ }
+ else if (irmp_bit2 >= irmp_param2.command_offset && irmp_bit2 < irmp_param2.command_end)
+ {
+ irmp_tmp_command2 |= (((uint_fast16_t) (value)) << (irmp_bit2 - irmp_param2.command_offset)); // CV wants cast
+ }
+}
+#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+
+#ifdef ANALYZE
+static uint32_t s_curSample;
+static uint32_t s_startBitSample;
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ISR routine
+ * @details ISR routine, called 10000 times per second
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+uint_fast8_t
+irmp_ISR (void)
+{
+ static uint_fast8_t irmp_start_bit_detected; // flag: start bit detected
+ static uint_fast8_t wait_for_space; // flag: wait for data bit space
+ static uint_fast8_t wait_for_start_space; // flag: wait for start bit space
+ static uint_fast8_t irmp_pulse_time; // count bit time for pulse
+ static PAUSE_LEN irmp_pause_time; // count bit time for pause
+ static uint_fast16_t last_irmp_address = 0xFFFF; // save last irmp address to recognize key repetition
+#if IRMP_32_BIT == 1
+ static uint_fast32_t last_irmp_command = 0xFFFFFFFF; // save last irmp command to recognize key repetition
+#else
+ static uint_fast16_t last_irmp_command = 0xFFFF; // save last irmp command to recognize key repetition
+#endif
+ static uint_fast16_t key_repetition_len; // SIRCS repeats frame 2-5 times with 45 ms pause
+ static uint_fast8_t repetition_frame_number;
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+ static uint_fast16_t last_irmp_denon_command; // save last irmp command to recognize DENON frame repetition
+ static uint_fast16_t denon_repetition_len = 0xFFFF; // denon repetition len of 2nd auto generated frame
+#endif
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || IRMP_SUPPORT_S100_PROTOCOL == 1
+ static uint_fast8_t rc5_cmd_bit6; // bit 6 of RC5 command is the inverted 2nd start bit
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1
+ static PAUSE_LEN last_pause; // last pause value
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1 || IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+ static uint_fast8_t last_value; // last bit value
+#endif
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+ static uint_fast8_t waiting_for_2nd_pulse = 0;
+#endif
+ uint_fast8_t irmp_input; // input value
+
+#ifdef ANALYZE
+
+#if 0 // only for test
+ static uint_fast8_t last_irmp_start_bit_detected = 0xFF;
+ static uint_fast8_t last_irmp_pulse_time = 0xFF;
+
+ if (last_irmp_start_bit_detected != irmp_start_bit_detected || last_irmp_pulse_time != irmp_pulse_time)
+ {
+ last_irmp_start_bit_detected = irmp_start_bit_detected;
+ last_irmp_pulse_time = irmp_pulse_time;
+
+ printf ("%d %d %d\n", time_counter, irmp_start_bit_detected, irmp_pulse_time);
+ }
+#endif // 0
+
+ time_counter++;
+#endif // ANALYZE
+
+#if defined(__SDCC_stm8)
+ irmp_input = input(IRMP_GPIO_STRUCT->IDR)
+#elif defined(__MBED__)
+ //irmp_input = inputPin;
+ irmp_input = gpio_read (&gpioIRin);
+#else
+ irmp_input = input(IRMP_PIN);
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+ if (irmp_callback_ptr)
+ {
+ static uint_fast8_t last_inverted_input;
+
+ if (last_inverted_input != !irmp_input)
+ {
+ (*irmp_callback_ptr) (! irmp_input);
+ last_inverted_input = !irmp_input;
+ }
+ }
+#endif // IRMP_USE_CALLBACK == 1
+
+ irmp_log(irmp_input); // log ir signal, if IRMP_LOGGING defined
+
+ if (! irmp_ir_detected) // ir code already detected?
+ { // no...
+ if (! irmp_start_bit_detected) // start bit detected?
+ { // no...
+ if (! irmp_input) // receiving burst?
+ { // yes...
+// irmp_busy_flag = TRUE;
+#ifdef ANALYZE
+ if (! irmp_pulse_time)
+ {
+ s_startBitSample = s_curSample;
+ ANALYZE_PRINTF("%8.3fms [starting pulse]\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+ }
+#endif // ANALYZE
+ irmp_pulse_time++; // increment counter
+ }
+ else
+ { // no...
+ if (irmp_pulse_time) // it's dark....
+ { // set flags for counting the time of darkness...
+ irmp_start_bit_detected = 1;
+ wait_for_start_space = 1;
+ wait_for_space = 0;
+ irmp_tmp_command = 0;
+ irmp_tmp_address = 0;
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+ genre2 = 0;
+#endif
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ irmp_tmp_id = 0;
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1
+ irmp_tmp_command2 = 0;
+ irmp_tmp_address2 = 0;
+#endif
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+ irmp_lgair_command = 0;
+ irmp_lgair_address = 0;
+#endif
+ irmp_bit = 0xff;
+ irmp_pause_time = 1; // 1st pause: set to 1, not to 0!
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || IRMP_SUPPORT_S100_PROTOCOL == 1
+ rc5_cmd_bit6 = 0; // fm 2010-03-07: bugfix: reset it after incomplete RC5 frame!
+#endif
+ }
+ else
+ {
+ if (key_repetition_len < 0xFFFF) // avoid overflow of counter
+ {
+ key_repetition_len++;
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+ if (denon_repetition_len < 0xFFFF) // avoid overflow of counter
+ {
+ denon_repetition_len++;
+
+ if (denon_repetition_len >= DENON_AUTO_REPETITION_PAUSE_LEN && last_irmp_denon_command != 0)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms warning: did not receive inverted command repetition\n",
+ (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+ last_irmp_denon_command = 0;
+ denon_repetition_len = 0xFFFF;
+ }
+ }
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+ }
+ }
+ }
+ }
+ else
+ {
+ if (wait_for_start_space) // we have received start bit...
+ { // ...and are counting the time of darkness
+ if (irmp_input) // still dark?
+ { // yes
+ irmp_pause_time++; // increment counter
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+ if (((irmp_pulse_time < NIKON_START_BIT_PULSE_LEN_MIN || irmp_pulse_time > NIKON_START_BIT_PULSE_LEN_MAX) && irmp_pause_time > IRMP_TIMEOUT_LEN) ||
+ irmp_pause_time > IRMP_TIMEOUT_NIKON_LEN)
+#else
+ if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout?
+#endif
+ { // yes...
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ if (irmp_protocol == IRMP_JVC_PROTOCOL) // don't show eror if JVC protocol, irmp_pulse_time has been set below!
+ {
+ ;
+ }
+ else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms error 1: pause after start bit pulse %d too long: %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ }
+
+ irmp_start_bit_detected = 0; // reset flags, let's wait for another start bit
+ irmp_pulse_time = 0;
+ irmp_pause_time = 0;
+ }
+ }
+ else
+ { // receiving first data pulse!
+ IRMP_PARAMETER * irmp_param_p;
+ irmp_param_p = (IRMP_PARAMETER *) 0;
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+ irmp_param2.protocol = 0;
+#endif
+
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [start-bit: pulse = %2d, pause = %2d]\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+ if (irmp_pulse_time >= SIRCS_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIRCS_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= SIRCS_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIRCS_START_BIT_PAUSE_LEN_MAX)
+ { // it's SIRCS
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = SIRCS, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX,
+ SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &sircs_param;
+ }
+ else
+#endif // IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame
+ irmp_pulse_time >= JVC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= JVC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= JVC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= JVC_REPEAT_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NEC or JVC (type 1) repeat frame, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ JVC_START_BIT_PULSE_LEN_MIN, JVC_START_BIT_PULSE_LEN_MAX,
+ JVC_REPEAT_START_BIT_PAUSE_LEN_MIN, JVC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+ }
+ else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX)
+ {
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NEC42, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+ NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nec42_param;
+#else
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NEC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+ NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+#endif
+ }
+ else if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NEC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_REPEAT_START_BIT_PAUSE_LEN_MAX)
+ { // it's NEC
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ if (irmp_protocol == IRMP_JVC_PROTOCOL) // last protocol was JVC, awaiting repeat frame
+ { // some jvc remote controls use nec repetition frame for jvc repetition frame
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = JVC repeat frame type 2, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+ NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+ }
+ else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NEC (repetition frame), start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+ NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+
+ irmp_param_p = (IRMP_PARAMETER *) &nec_rep_param;
+ }
+ }
+ else
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame
+ irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NEC_0_PAUSE_LEN_MIN && irmp_pause_time <= NEC_0_PAUSE_LEN_MAX)
+ { // it's JVC repetition type 3
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = JVC repeat frame type 3, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+ NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+ }
+ else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+ if (irmp_pulse_time >= TELEFUNKEN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= TELEFUNKEN_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= TELEFUNKEN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= TELEFUNKEN_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = TELEFUNKEN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ TELEFUNKEN_START_BIT_PULSE_LEN_MIN, TELEFUNKEN_START_BIT_PULSE_LEN_MAX,
+ TELEFUNKEN_START_BIT_PAUSE_LEN_MIN, TELEFUNKEN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &telefunken_param;
+ }
+ else
+#endif // IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+ if (irmp_pulse_time >= ROOMBA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= ROOMBA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ROOMBA_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = ROOMBA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ ROOMBA_START_BIT_PULSE_LEN_MIN, ROOMBA_START_BIT_PULSE_LEN_MAX,
+ ROOMBA_START_BIT_PAUSE_LEN_MIN, ROOMBA_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &roomba_param;
+ }
+ else
+#endif // IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+ if (irmp_pulse_time >= ACP24_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ACP24_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= ACP24_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ACP24_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = ACP24, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ ACP24_START_BIT_PULSE_LEN_MIN, ACP24_START_BIT_PULSE_LEN_MAX,
+ ACP24_START_BIT_PAUSE_LEN_MIN, ACP24_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &acp24_param;
+ }
+ else
+#endif // IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+ if (irmp_pulse_time >= PENTAX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= PENTAX_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= PENTAX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= PENTAX_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = PENTAX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ PENTAX_START_BIT_PULSE_LEN_MIN, PENTAX_START_BIT_PULSE_LEN_MAX,
+ PENTAX_START_BIT_PAUSE_LEN_MIN, PENTAX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &pentax_param;
+ }
+ else
+#endif // IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+ if (irmp_pulse_time >= NIKON_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NIKON_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NIKON_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NIKON_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NIKON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX,
+ (int)NIKON_START_BIT_PAUSE_LEN_MIN, (int)NIKON_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nikon_param;
+ }
+ else
+#endif // IRMP_SUPPORT_NIKON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ if (irmp_pulse_time >= SAMSUNG_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX)
+ { // it's SAMSUNG
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = SAMSUNG, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX,
+ SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &samsung_param;
+ }
+ else
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+ if (irmp_pulse_time >= SAMSUNGAH_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNGAH_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= SAMSUNGAH_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNGAH_START_BIT_PAUSE_LEN_MAX)
+ { // it's SAMSUNGAH
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = SAMSUNGAH, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ SAMSUNGAH_START_BIT_PULSE_LEN_MIN, SAMSUNGAH_START_BIT_PULSE_LEN_MAX,
+ SAMSUNGAH_START_BIT_PAUSE_LEN_MIN, SAMSUNGAH_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &samsungah_param;
+ }
+ else
+#endif // IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+ if (irmp_pulse_time >= MATSUSHITA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MATSUSHITA_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= MATSUSHITA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MATSUSHITA_START_BIT_PAUSE_LEN_MAX)
+ { // it's MATSUSHITA
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = MATSUSHITA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX,
+ MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &matsushita_param;
+ }
+ else
+#endif // IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+ if (irmp_pulse_time >= KASEIKYO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KASEIKYO_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= KASEIKYO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KASEIKYO_START_BIT_PAUSE_LEN_MAX)
+ { // it's KASEIKYO
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = KASEIKYO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX,
+ KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &kaseikyo_param;
+ }
+ else
+#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+ if (irmp_pulse_time >= PANASONIC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= PANASONIC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= PANASONIC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= PANASONIC_START_BIT_PAUSE_LEN_MAX)
+ { // it's PANASONIC
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = PANASONIC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ PANASONIC_START_BIT_PULSE_LEN_MIN, PANASONIC_START_BIT_PULSE_LEN_MAX,
+ PANASONIC_START_BIT_PAUSE_LEN_MIN, PANASONIC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &panasonic_param;
+ }
+ else
+#endif // IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+ if (irmp_pulse_time >= MITSU_HEAVY_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MITSU_HEAVY_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX)
+ { // it's MITSU_HEAVY
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = MITSU_HEAVY, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ MITSU_HEAVY_START_BIT_PULSE_LEN_MIN, MITSU_HEAVY_START_BIT_PULSE_LEN_MAX,
+ MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN, MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &mitsu_heavy_param;
+ }
+ else
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+
+#if IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+ if (irmp_pulse_time >= VINCENT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= VINCENT_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= VINCENT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= VINCENT_START_BIT_PAUSE_LEN_MAX)
+ { // it's VINCENT
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = VINCENT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ VINCENT_START_BIT_PULSE_LEN_MIN, VINCENT_START_BIT_PULSE_LEN_MAX,
+ VINCENT_START_BIT_PAUSE_LEN_MIN, VINCENT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &vincent_param;
+ }
+ else
+#endif // IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+ if (irmp_pulse_time >= METZ_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= METZ_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= METZ_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= METZ_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = METZ, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ METZ_START_BIT_PULSE_LEN_MIN, METZ_START_BIT_PULSE_LEN_MAX,
+ METZ_START_BIT_PAUSE_LEN_MIN, METZ_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &metz_param;
+ }
+ else
+#endif // IRMP_SUPPORT_METZ_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RADIO1_PROTOCOL == 1
+ if (irmp_pulse_time >= RADIO1_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RADIO1_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RADIO1_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RADIO1_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RADIO1, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RADIO1_START_BIT_PULSE_LEN_MIN, RADIO1_START_BIT_PULSE_LEN_MAX,
+ RADIO1_START_BIT_PAUSE_LEN_MIN, RADIO1_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &radio1_param;
+ }
+ else
+#endif // IRMP_SUPPORT_RRADIO1_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1
+ if (irmp_pulse_time >= RECS80_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RECS80_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80_START_BIT_PAUSE_LEN_MAX)
+ { // it's RECS80
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RECS80, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX,
+ RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &recs80_param;
+ }
+ else
+#endif // IRMP_SUPPORT_RECS80_PROTOCOL == 1
+
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+ if (((irmp_pulse_time >= S100_START_BIT_LEN_MIN && irmp_pulse_time <= S100_START_BIT_LEN_MAX) ||
+ (irmp_pulse_time >= 2 * S100_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * S100_START_BIT_LEN_MAX)) &&
+ ((irmp_pause_time >= S100_START_BIT_LEN_MIN && irmp_pause_time <= S100_START_BIT_LEN_MAX) ||
+ (irmp_pause_time >= 2 * S100_START_BIT_LEN_MIN && irmp_pause_time <= 2 * S100_START_BIT_LEN_MAX)))
+ { // it's S100
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = S100, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n",
+ S100_START_BIT_LEN_MIN, S100_START_BIT_LEN_MAX,
+ 2 * S100_START_BIT_LEN_MIN, 2 * S100_START_BIT_LEN_MAX,
+ S100_START_BIT_LEN_MIN, S100_START_BIT_LEN_MAX,
+ 2 * S100_START_BIT_LEN_MIN, 2 * S100_START_BIT_LEN_MAX);
+#endif // ANALYZE
+
+ irmp_param_p = (IRMP_PARAMETER *) &s100_param;
+ last_pause = irmp_pause_time;
+
+ if ((irmp_pulse_time > S100_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * S100_START_BIT_LEN_MAX) ||
+ (irmp_pause_time > S100_START_BIT_LEN_MAX && irmp_pause_time <= 2 * S100_START_BIT_LEN_MAX))
+ {
+ last_value = 0;
+ rc5_cmd_bit6 = 1<<6;
+ }
+ else
+ {
+ last_value = 1;
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_S100_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+ if (((irmp_pulse_time >= RC5_START_BIT_LEN_MIN && irmp_pulse_time <= RC5_START_BIT_LEN_MAX) ||
+ (irmp_pulse_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX)) &&
+ ((irmp_pause_time >= RC5_START_BIT_LEN_MIN && irmp_pause_time <= RC5_START_BIT_LEN_MAX) ||
+ (irmp_pause_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX)))
+ { // it's RC5
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+ if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RC5 or FDC\n");
+ ANALYZE_PRINTF ("FDC start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX,
+ FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX);
+ ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+ memcpy_P (&irmp_param2, &fdc_param, sizeof (IRMP_PARAMETER));
+ }
+ else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RC5 or RCCAR\n");
+ ANALYZE_PRINTF ("RCCAR start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX,
+ RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX);
+ ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+ memcpy_P (&irmp_param2, &rccar_param, sizeof (IRMP_PARAMETER));
+ }
+ else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RC5, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n",
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+ 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX,
+ RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+ 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+ }
+
+ irmp_param_p = (IRMP_PARAMETER *) &rc5_param;
+ last_pause = irmp_pause_time;
+
+ if ((irmp_pulse_time > RC5_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX) ||
+ (irmp_pause_time > RC5_START_BIT_LEN_MAX && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX))
+ {
+ last_value = 0;
+ rc5_cmd_bit6 = 1<<6;
+ }
+ else
+ {
+ last_value = 1;
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+ if ((irmp_pulse_time >= RCII_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCII_START_BIT_PULSE_LEN_MAX) &&
+ (irmp_pause_time >= RCII_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCII_START_BIT_PAUSE_LEN_MAX))
+ { // it's RCII
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RCII, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RCII_START_BIT_PULSE_LEN_MIN, RCII_START_BIT_PULSE_LEN_MAX,
+ RCII_START_BIT_PAUSE_LEN_MIN, RCII_START_BIT_PAUSE_LEN_MAX)
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &rcii_param;
+ last_pause = irmp_pause_time;
+ waiting_for_2nd_pulse = 1;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_RCII_PROTOCOL == 1
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+ if ( (irmp_pulse_time >= DENON_PULSE_LEN_MIN && irmp_pulse_time <= DENON_PULSE_LEN_MAX) &&
+ ((irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) ||
+ (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX)))
+ { // it's DENON
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = DENON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+ DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX,
+ DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX,
+ DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &denon_param;
+ }
+ else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+ if ( (irmp_pulse_time >= THOMSON_PULSE_LEN_MIN && irmp_pulse_time <= THOMSON_PULSE_LEN_MAX) &&
+ ((irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) ||
+ (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX)))
+ { // it's THOMSON
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = THOMSON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+ THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX,
+ THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX,
+ THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &thomson_param;
+ }
+ else
+#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+ if (irmp_pulse_time >= BOSE_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= BOSE_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= BOSE_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BOSE_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = BOSE, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ BOSE_START_BIT_PULSE_LEN_MIN, BOSE_START_BIT_PULSE_LEN_MAX,
+ BOSE_START_BIT_PAUSE_LEN_MIN, BOSE_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &bose_param;
+ }
+ else
+#endif // IRMP_SUPPORT_BOSE_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_pulse_time >= RC6_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RC6_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RC6_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RC6_START_BIT_PAUSE_LEN_MAX)
+ { // it's RC6
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RC6, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX,
+ RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &rc6_param;
+ last_pause = 0;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+ if (irmp_pulse_time >= RECS80EXT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80EXT_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RECS80EXT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80EXT_START_BIT_PAUSE_LEN_MAX)
+ { // it's RECS80EXT
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RECS80EXT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX,
+ RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &recs80ext_param;
+ }
+ else
+#endif // IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+ if (irmp_pulse_time >= NUBERT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NUBERT_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NUBERT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NUBERT_START_BIT_PAUSE_LEN_MAX)
+ { // it's NUBERT
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NUBERT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX,
+ NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &nubert_param;
+ }
+ else
+#endif // IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+ if (irmp_pulse_time >= FAN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FAN_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= FAN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FAN_START_BIT_PAUSE_LEN_MAX)
+ { // it's FAN
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = FAN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ FAN_START_BIT_PULSE_LEN_MIN, FAN_START_BIT_PULSE_LEN_MAX,
+ FAN_START_BIT_PAUSE_LEN_MIN, FAN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &fan_param;
+ }
+ else
+#endif // IRMP_SUPPORT_FAN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+ if (irmp_pulse_time >= SPEAKER_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SPEAKER_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= SPEAKER_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SPEAKER_START_BIT_PAUSE_LEN_MAX)
+ { // it's SPEAKER
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = SPEAKER, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ SPEAKER_START_BIT_PULSE_LEN_MIN, SPEAKER_START_BIT_PULSE_LEN_MAX,
+ SPEAKER_START_BIT_PAUSE_LEN_MIN, SPEAKER_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &speaker_param;
+ }
+ else
+#endif // IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+ if (irmp_pulse_time >= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX &&
+ irmp_pause_time >= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX)
+ { // it's BANG_OLUFSEN
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = BANG_OLUFSEN\n");
+ ANALYZE_PRINTF ("start bit 1 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX,
+ BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX);
+ ANALYZE_PRINTF ("start bit 2 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX,
+ BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX);
+ ANALYZE_PRINTF ("start bit 3 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX,
+ BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX);
+ ANALYZE_PRINTF ("start bit 4 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX,
+ BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &bang_olufsen_param;
+ last_value = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+ if (irmp_pulse_time >= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN && irmp_pulse_time <= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX &&
+ irmp_pause_time >= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX)
+ { // it's GRUNDIG
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = GRUNDIG, pre bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX,
+ GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &grundig_param;
+ last_pause = irmp_pause_time;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1 // check MERLIN before RUWIDO!
+ if (irmp_pulse_time >= MERLIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MERLIN_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= MERLIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MERLIN_START_BIT_PAUSE_LEN_MAX)
+ { // it's MERLIN
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = MERLIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ MERLIN_START_BIT_PULSE_LEN_MIN, MERLIN_START_BIT_PULSE_LEN_MAX,
+ MERLIN_START_BIT_PAUSE_LEN_MIN, MERLIN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &merlin_param;
+ last_pause = irmp_pause_time;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+ if (((irmp_pulse_time >= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX) ||
+ (irmp_pulse_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX)) &&
+ ((irmp_pause_time >= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX) ||
+ (irmp_pause_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX)))
+ { // it's RUWIDO or SIEMENS
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RUWIDO, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+ SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX,
+ 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX,
+ SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX,
+ 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &ruwido_param;
+ last_pause = irmp_pause_time;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+ if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = FDC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX,
+ FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &fdc_param;
+ }
+ else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RCCAR, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX,
+ RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &rccar_param;
+ }
+ else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+ if (irmp_pulse_time >= KATHREIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX)
+ { // it's KATHREIN
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = KATHREIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ KATHREIN_START_BIT_PULSE_LEN_MIN, KATHREIN_START_BIT_PULSE_LEN_MAX,
+ KATHREIN_START_BIT_PAUSE_LEN_MIN, KATHREIN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &kathrein_param;
+ }
+ else
+#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+ if (irmp_pulse_time >= NETBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NETBOX_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= NETBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NETBOX_START_BIT_PAUSE_LEN_MAX)
+ { // it's NETBOX
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = NETBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ NETBOX_START_BIT_PULSE_LEN_MIN, NETBOX_START_BIT_PULSE_LEN_MAX,
+ NETBOX_START_BIT_PAUSE_LEN_MIN, NETBOX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &netbox_param;
+ }
+ else
+#endif // IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+ if (irmp_pulse_time >= LEGO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= LEGO_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= LEGO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= LEGO_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = LEGO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX,
+ LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &lego_param;
+ }
+ else
+#endif // IRMP_SUPPORT_LEGO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+ if (irmp_pulse_time >= IRMP16_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= IRMP16_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= IRMP16_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= IRMP16_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = IRMP16, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ IRMP16_START_BIT_PULSE_LEN_MIN, IRMP16_START_BIT_PULSE_LEN_MAX,
+ IRMP16_START_BIT_PAUSE_LEN_MIN, IRMP16_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &irmp16_param;
+ }
+ else
+#endif // IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1
+ if (irmp_pulse_time >= GREE_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= GREE_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= GREE_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= GREE_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = GREE, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ GREE_START_BIT_PULSE_LEN_MIN, GREE_START_BIT_PULSE_LEN_MAX,
+ GREE_START_BIT_PAUSE_LEN_MIN, GREE_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &gree_param;
+ }
+ else
+#endif // IRMP_SUPPORT_GREE_PROTOCOL == 1
+
+#if IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+ if (irmp_pulse_time >= A1TVBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= A1TVBOX_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= A1TVBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= A1TVBOX_START_BIT_PAUSE_LEN_MAX)
+ { // it's A1TVBOX
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = A1TVBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ A1TVBOX_START_BIT_PULSE_LEN_MIN, A1TVBOX_START_BIT_PULSE_LEN_MAX,
+ A1TVBOX_START_BIT_PAUSE_LEN_MIN, A1TVBOX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &a1tvbox_param;
+ last_pause = 0;
+ last_value = 1;
+ }
+ else
+#endif // IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+ if (irmp_pulse_time >= ORTEK_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ORTEK_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= ORTEK_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ORTEK_START_BIT_PAUSE_LEN_MAX)
+ { // it's ORTEK (Hama)
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = ORTEK, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ ORTEK_START_BIT_PULSE_LEN_MIN, ORTEK_START_BIT_PULSE_LEN_MAX,
+ ORTEK_START_BIT_PAUSE_LEN_MIN, ORTEK_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &ortek_param;
+ last_pause = 0;
+ last_value = 1;
+ parity = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+ if (irmp_pulse_time >= RCMM32_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCMM32_START_BIT_PULSE_LEN_MAX &&
+ irmp_pause_time >= RCMM32_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_START_BIT_PAUSE_LEN_MAX)
+ { // it's RCMM
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = RCMM, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+ RCMM32_START_BIT_PULSE_LEN_MIN, RCMM32_START_BIT_PULSE_LEN_MAX,
+ RCMM32_START_BIT_PAUSE_LEN_MIN, RCMM32_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_param_p = (IRMP_PARAMETER *) &rcmm_param;
+ }
+ else
+#endif // IRMP_SUPPORT_RCMM_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("protocol = UNKNOWN\n");
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // wait for another start bit...
+ }
+
+ if (irmp_start_bit_detected)
+ {
+ memcpy_P (&irmp_param, irmp_param_p, sizeof (IRMP_PARAMETER));
+
+ if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max);
+ ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max);
+#endif // ANALYZE
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max,
+ 2 * irmp_param.pulse_1_len_min, 2 * irmp_param.pulse_1_len_max);
+ ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max,
+ 2 * irmp_param.pause_1_len_min, 2 * irmp_param.pause_1_len_max);
+#endif // ANALYZE
+ }
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+ if (irmp_param2.protocol)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param2.pulse_0_len_min, irmp_param2.pulse_0_len_max);
+ ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param2.pause_0_len_min, irmp_param2.pause_0_len_max);
+ ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param2.pulse_1_len_min, irmp_param2.pulse_1_len_max);
+ ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param2.pause_1_len_min, irmp_param2.pause_1_len_max);
+#endif // ANALYZE
+ }
+#endif
+
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC6_PROTOCOL)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse_toggle: %3d - %3d\n", RC6_TOGGLE_BIT_LEN_MIN, RC6_TOGGLE_BIT_LEN_MAX);
+#endif // ANALYZE
+ }
+#endif
+
+ if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+ ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max);
+#endif // ANALYZE
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max,
+ 2 * irmp_param.pulse_0_len_min, 2 * irmp_param.pulse_0_len_max);
+ ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max,
+ 2 * irmp_param.pause_0_len_min, 2 * irmp_param.pause_0_len_max);
+#endif // ANALYZE
+ }
+
+#ifdef ANALYZE
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL)
+ {
+ ANALYZE_PRINTF ("pulse_r: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+ ANALYZE_PRINTF ("pause_r: %3d - %3d\n", BANG_OLUFSEN_R_PAUSE_LEN_MIN, BANG_OLUFSEN_R_PAUSE_LEN_MAX);
+ }
+#endif
+
+ ANALYZE_PRINTF ("command_offset: %2d\n", irmp_param.command_offset);
+ ANALYZE_PRINTF ("command_len: %3d\n", irmp_param.command_end - irmp_param.command_offset);
+ ANALYZE_PRINTF ("complete_len: %3d\n", irmp_param.complete_len);
+ ANALYZE_PRINTF ("stop_bit: %3d\n", irmp_param.stop_bit);
+#endif // ANALYZE
+ }
+
+ irmp_bit = 0;
+
+#if IRMP_SUPPORT_MANCHESTER == 1
+ if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) &&
+ irmp_param.protocol != IRMP_RUWIDO_PROTOCOL && // Manchester, but not RUWIDO
+ irmp_param.protocol != IRMP_RC6_PROTOCOL /*** && // Manchester, but not RC6
+ irmp_param.protocol != IRMP_RCII_PROTOCOL ****/) // Manchester, but not RCII
+ {
+ if (irmp_pause_time > irmp_param.pulse_1_len_max && irmp_pause_time <= 2 * irmp_param.pulse_1_len_max)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1);
+ }
+ else if (! last_value) // && irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0);
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+
+#if IRMP_SUPPORT_SERIAL == 1
+ if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL)
+ {
+ ; // do nothing
+ }
+ else
+#endif // IRMP_SUPPORT_SERIAL == 1
+
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_DENON_PROTOCOL)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+ if (irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX)
+ { // pause timings correct for "1"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1'); // yes, store 1
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ }
+ else // if (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX)
+ { // pause timings correct for "0"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0'); // yes, store 0
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (0);
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_THOMSON_PROTOCOL)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+ if (irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX)
+ { // pause timings correct for "1"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1'); // yes, store 1
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ }
+ else // if (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX)
+ { // pause timings correct for "0"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0'); // yes, store 0
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (0);
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+ {
+ ; // else do nothing
+ }
+
+ irmp_pulse_time = 1; // set counter to 1, not 0
+ irmp_pause_time = 0;
+ wait_for_start_space = 0;
+ }
+ }
+ else if (wait_for_space) // the data section....
+ { // counting the time of darkness....
+ uint_fast8_t got_light = FALSE;
+
+ if (irmp_input) // still dark?
+ { // yes...
+ if (irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 1)
+ {
+ if (
+#if IRMP_SUPPORT_MANCHESTER == 1
+ (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) ||
+#endif
+#if IRMP_SUPPORT_SERIAL == 1
+ (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) ||
+#endif
+ (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max))
+ {
+#ifdef ANALYZE
+ if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+ {
+ ANALYZE_PRINTF ("stop bit detected\n");
+ }
+#endif // ANALYZE
+ irmp_param.stop_bit = 0;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error: stop bit timing wrong, irmp_bit = %d, irmp_pulse_time = %d, pulse_0_len_min = %d, pulse_0_len_max = %d\n",
+ irmp_bit, irmp_pulse_time, irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // wait for another start bit...
+ irmp_pulse_time = 0;
+ irmp_pause_time = 0;
+ }
+ }
+ else
+ {
+ irmp_pause_time++; // increment counter
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && // Sony has a variable number of bits:
+ irmp_pause_time > SIRCS_PAUSE_LEN_MAX && // minimum is 12
+ irmp_bit >= 12 - 1) // pause too long?
+ { // yes, break and close this frame
+ irmp_param.complete_len = irmp_bit + 1; // set new complete length
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ irmp_tmp_address |= (irmp_bit - SIRCS_MINIMUM_DATA_LEN + 1) << 8; // new: store number of additional bits in upper byte of address!
+ irmp_param.command_end = irmp_param.command_offset + irmp_bit + 1; // correct command length
+ irmp_pause_time = SIRCS_PAUSE_LEN_MAX - 1; // correct pause length
+ }
+ else
+#endif
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_MERLIN_PROTOCOL && // Merlin has a variable number of bits:
+ irmp_pause_time > MERLIN_START_BIT_PAUSE_LEN_MAX && // minimum is 8
+ irmp_bit >= 8 - 1) // pause too long?
+ { // yes, break and close this frame
+ irmp_param.complete_len = irmp_bit; // set new complete length
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ irmp_pause_time = MERLIN_BIT_PAUSE_LEN_MAX - 1; // correct pause length
+ }
+ else
+#endif
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_FAN_PROTOCOL && // FAN has no stop bit.
+ irmp_bit >= FAN_COMPLETE_DATA_LEN - 1) // last bit in frame
+ { // yes, break and close this frame
+ if (irmp_pulse_time <= FAN_0_PULSE_LEN_MAX && irmp_pause_time >= FAN_0_PAUSE_LEN_MIN)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Generating virtual stop bit\n");
+#endif // ANALYZE
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ }
+ else if (irmp_pulse_time >= FAN_1_PULSE_LEN_MIN && irmp_pause_time >= FAN_1_PAUSE_LEN_MIN)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Generating virtual stop bit\n");
+#endif // ANALYZE
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ }
+ }
+ else
+#endif
+#if IRMP_SUPPORT_SERIAL == 1
+ // NETBOX generates no stop bit, here is the timeout condition:
+ if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) && irmp_param.protocol == IRMP_NETBOX_PROTOCOL &&
+ irmp_pause_time >= NETBOX_PULSE_LEN * (NETBOX_COMPLETE_DATA_LEN - irmp_bit))
+ {
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ }
+ else
+#endif
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && !irmp_param.stop_bit)
+ {
+ if (irmp_pause_time > IR60_TIMEOUT_LEN && (irmp_bit == 5 || irmp_bit == 6))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to IR60 protocol\n");
+#endif // ANALYZE
+ got_light = TRUE; // this is a lie, but generates a stop bit ;-)
+ irmp_param.stop_bit = TRUE; // set flag
+
+ irmp_param.protocol = IRMP_IR60_PROTOCOL; // change protocol
+ irmp_param.complete_len = IR60_COMPLETE_DATA_LEN; // correct complete len
+ irmp_param.address_offset = IR60_ADDRESS_OFFSET;
+ irmp_param.address_end = IR60_ADDRESS_OFFSET + IR60_ADDRESS_LEN;
+ irmp_param.command_offset = IR60_COMMAND_OFFSET;
+ irmp_param.command_end = IR60_COMMAND_OFFSET + IR60_COMMAND_LEN;
+
+ irmp_tmp_command <<= 1;
+ irmp_tmp_command |= first_bit;
+ }
+ else if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN - 2)
+ { // special manchester decoder
+ irmp_param.complete_len = GRUNDIG_COMPLETE_DATA_LEN; // correct complete len
+ got_light = TRUE; // this is a lie, but generates a stop bit ;-)
+ irmp_param.stop_bit = TRUE; // set flag
+ }
+ else if (irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to NOKIA protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_NOKIA_PROTOCOL; // change protocol
+ irmp_param.address_offset = NOKIA_ADDRESS_OFFSET;
+ irmp_param.address_end = NOKIA_ADDRESS_OFFSET + NOKIA_ADDRESS_LEN;
+ irmp_param.command_offset = NOKIA_COMMAND_OFFSET;
+ irmp_param.command_end = NOKIA_COMMAND_OFFSET + NOKIA_COMMAND_LEN;
+
+ if (irmp_tmp_command & 0x300)
+ {
+ irmp_tmp_address = (irmp_tmp_command >> 8);
+ irmp_tmp_command &= 0xFF;
+ }
+ }
+ }
+ else
+#endif
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RUWIDO_PROTOCOL && !irmp_param.stop_bit)
+ {
+ if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= RUWIDO_COMPLETE_DATA_LEN - 2)
+ { // special manchester decoder
+ irmp_param.complete_len = RUWIDO_COMPLETE_DATA_LEN; // correct complete len
+ got_light = TRUE; // this is a lie, but generates a stop bit ;-)
+ irmp_param.stop_bit = TRUE; // set flag
+ }
+ else if (irmp_bit >= RUWIDO_COMPLETE_DATA_LEN)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to SIEMENS protocol\n");
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_SIEMENS_PROTOCOL; // change protocol
+ irmp_param.address_offset = SIEMENS_ADDRESS_OFFSET;
+ irmp_param.address_end = SIEMENS_ADDRESS_OFFSET + SIEMENS_ADDRESS_LEN;
+ irmp_param.command_offset = SIEMENS_COMMAND_OFFSET;
+ irmp_param.command_end = SIEMENS_COMMAND_OFFSET + SIEMENS_COMMAND_LEN;
+
+ // 76543210
+ // RUWIDO: AAAAAAAAACCCCCCCp
+ // SIEMENS: AAAAAAAAAAACCCCCCCCCCp
+ irmp_tmp_address <<= 2;
+ irmp_tmp_address |= (irmp_tmp_command >> 6);
+ irmp_tmp_command &= 0x003F;
+// irmp_tmp_command <<= 4;
+ irmp_tmp_command |= last_value;
+ }
+ }
+ else
+#endif
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_ROOMBA_PROTOCOL && // Roomba has no stop bit
+ irmp_bit >= ROOMBA_COMPLETE_DATA_LEN - 1) // it's the last data bit...
+ { // break and close this frame
+ if (irmp_pulse_time >= ROOMBA_1_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_1_PULSE_LEN_MAX)
+ {
+ irmp_pause_time = ROOMBA_1_PAUSE_LEN_EXACT;
+ }
+ else if (irmp_pulse_time >= ROOMBA_0_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_0_PULSE_LEN_MAX)
+ {
+ irmp_pause_time = ROOMBA_0_PAUSE_LEN;
+ }
+
+ got_light = TRUE; // this is a lie, but helps (generates stop bit)
+ }
+ else
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1
+ if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) &&
+ irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= irmp_param.complete_len - 2 && !irmp_param.stop_bit)
+ { // special manchester decoder
+ got_light = TRUE; // this is a lie, but generates a stop bit ;-)
+ irmp_param.stop_bit = TRUE; // set flag
+ }
+ else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+ if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout?
+ { // yes...
+ if (irmp_bit == irmp_param.complete_len - 1 && irmp_param.stop_bit == 0)
+ {
+ irmp_bit++;
+ }
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ else if ((irmp_param.protocol == IRMP_NEC_PROTOCOL || irmp_param.protocol == IRMP_NEC42_PROTOCOL) && irmp_bit == 0)
+ { // it was a non-standard repetition frame
+#ifdef ANALYZE // with 4500us pause instead of 2250us
+ ANALYZE_PRINTF ("Detected non-standard repetition frame, switching to NEC repetition\n");
+#endif // ANALYZE
+ if (key_repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX)
+ {
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_NEC_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+ irmp_tmp_address = last_irmp_address; // address is last address
+ irmp_tmp_command = last_irmp_command; // command is last command
+ irmp_flags |= IRMP_FLAG_REPETITION;
+ key_repetition_len = 0;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("ignoring NEC repetition frame: timeout occured, key_repetition_len = %d > %d\n",
+ (int)key_repetition_len, (int)NEC_FRAME_REPEAT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+ irmp_tmp_command = (irmp_tmp_address >> 4); // set command: upper 12 bits are command bits
+ irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits
+ irmp_start_bit_detected = 1; // tricky: don't wait for another start bit...
+ }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 28 || irmp_bit == 29)) // it was a LGAIR stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to LGAIR protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_LGAIR_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+ irmp_tmp_command = irmp_lgair_command; // set command: upper 8 bits are command bits
+ irmp_tmp_address = irmp_lgair_address; // lower 4 bits are address bits
+ irmp_start_bit_detected = 1; // tricky: don't wait for another start bit...
+ }
+#endif // IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 32) // it was a NEC stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to NEC protocol\n");
+#endif // ANALYZE
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_NEC_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+
+ // 0123456789ABC0123456789ABC0123456701234567
+ // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc
+ // NEC: AAAAAAAAaaaaaaaaCCCCCCCCcccccccc
+ irmp_tmp_address |= (irmp_tmp_address2 & 0x0007) << 13; // fm 2012-02-13: 12 -> 13
+ irmp_tmp_command = (irmp_tmp_address2 >> 3) | (irmp_tmp_command << 10);
+ }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 28) // it was a NEC stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to LGAIR protocol\n");
+#endif // ANALYZE
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_LGAIR_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+ irmp_tmp_address = irmp_lgair_address;
+ irmp_tmp_command = irmp_lgair_command;
+ }
+#endif // IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length: 16 or 17
+
+ // 0123456789ABC0123456789ABC0123456701234567
+ // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc
+ // JVC: AAAACCCCCCCCCCCC
+ irmp_tmp_command = (irmp_tmp_address >> 4) | (irmp_tmp_address2 << 9); // set command: upper 12 bits are command bits
+ irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits
+ }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL && irmp_bit == 32) // it was a SAMSUNG32 stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol\n");
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_SAMSUNG32_PROTOCOL;
+ irmp_param.command_offset = SAMSUNG32_COMMAND_OFFSET;
+ irmp_param.command_end = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN;
+ irmp_param.complete_len = SAMSUNG32_COMPLETE_DATA_LEN;
+ }
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_RCMM32_PROTOCOL && (irmp_bit == 12 || irmp_bit == 24)) // it was a RCMM stop bit
+ {
+ if (irmp_bit == 12)
+ {
+ irmp_tmp_command = (irmp_tmp_address & 0xFF); // set command: lower 8 bits are command bits
+ irmp_tmp_address >>= 8; // upper 4 bits are address bits
+
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to RCMM12 protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_RCMM12_PROTOCOL; // switch protocol
+ }
+ else // if ((irmp_bit == 24)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to RCMM24 protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_RCMM24_PROTOCOL; // switch protocol
+ }
+ irmp_param.stop_bit = TRUE; // set flag
+ irmp_param.complete_len = irmp_bit; // patch length
+ }
+#endif // IRMP_SUPPORT_RCMM_PROTOCOL == 1
+
+#if IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+ else if (irmp_param.protocol == IRMP_MATSUSHITA_PROTOCOL && irmp_bit == 22) // it was a TECHNICS stop bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to TECHNICS protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+ // Situation:
+ // The first 12 bits have been stored in irmp_tmp_command (LSB first)
+ // The following 10 bits have been stored in irmp_tmp_address (LSB first)
+ // The code of TECHNICS is:
+ // cccccccccccCCCCCCCCCCC (11 times c and 11 times C)
+ // ccccccccccccaaaaaaaaaa
+ // where C is inverted value of c
+
+ irmp_tmp_address <<= 1;
+ if (irmp_tmp_command & (1<<11))
+ {
+ irmp_tmp_address |= 1;
+ irmp_tmp_command &= ~(1<<11);
+ }
+
+ if (irmp_tmp_command == ((~irmp_tmp_address) & 0x07FF))
+ {
+ irmp_tmp_address = 0;
+
+ irmp_param.protocol = IRMP_TECHNICS_PROTOCOL; // switch protocol
+ irmp_param.complete_len = irmp_bit; // patch length
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 8: TECHNICS frame error\n");
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // wait for another start bit...
+ irmp_pulse_time = 0;
+ irmp_pause_time = 0;
+ }
+ }
+#endif // IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 2: pause %d after data bit %d too long\n", irmp_pause_time, irmp_bit);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // wait for another start bit...
+ irmp_pulse_time = 0;
+ irmp_pause_time = 0;
+ }
+ }
+ }
+ }
+ else
+ { // got light now!
+ got_light = TRUE;
+ }
+
+ if (got_light)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_MANCHESTER == 1
+ if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) // Manchester
+ {
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+ if (irmp_param.complete_len == irmp_bit && irmp_param.protocol == IRMP_MERLIN_PROTOCOL)
+ {
+ if (last_value == 0)
+ {
+ if (irmp_pulse_time >= 2 * irmp_param.pulse_1_len_min && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max &&
+ last_pause >= irmp_param.pause_1_len_min && last_pause <= irmp_param.pulse_1_len_max)
+ {
+ irmp_param.complete_len += 2;
+ irmp_store_bit(0);
+ irmp_store_bit(1);
+ }
+ }
+ else
+ {
+ if (last_pause >= 2 * irmp_param.pause_1_len_min && last_pause <= 2 * irmp_param.pulse_1_len_max)
+ {
+ if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max)
+ {
+ irmp_param.complete_len++;
+ irmp_store_bit(0);
+ }
+ else if (irmp_pulse_time >= 2 * irmp_param.pulse_1_len_min && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max)
+ {
+ irmp_param.complete_len += 2;
+ irmp_store_bit(0);
+ irmp_store_bit(1);
+ }
+ }
+ }
+ }
+ else
+#endif
+#if 1
+ if (irmp_pulse_time > irmp_param.pulse_1_len_max /* && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max */)
+#else // better, but some IR-RCs use asymmetric timings :-/
+ if (irmp_pulse_time > irmp_param.pulse_1_len_max && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max &&
+ irmp_pause_time <= 2 * irmp_param.pause_1_len_max)
+#endif
+ {
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('T');
+#endif // ANALYZE
+ if (irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode 6A
+ {
+ irmp_store_bit (1);
+ last_value = 1;
+ }
+ else // RC6 mode 0
+ {
+ irmp_store_bit (0);
+ last_value = 0;
+ }
+#ifdef ANALYZE
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ }
+ else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1');
+#endif // ANALYZE
+ irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1 );
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('T');
+#endif // ANALYZE
+ irmp_store_bit (1);
+
+ if (irmp_pause_time > 2 * irmp_param.pause_1_len_max)
+ {
+ last_value = 0;
+ }
+ else
+ {
+ last_value = 1;
+ }
+#ifdef ANALYZE
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ }
+ else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0');
+#endif // ANALYZE
+ irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0 );
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+ if (! irmp_param2.protocol)
+#endif
+ {
+#ifdef ANALYZE
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ }
+ last_value = (irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0;
+ }
+ }
+ }
+ else if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max
+ /* && irmp_pause_time <= 2 * irmp_param.pause_1_len_max */)
+ {
+ uint_fast8_t manchester_value;
+
+ if (last_pause > irmp_param.pause_1_len_max && last_pause <= 2 * irmp_param.pause_1_len_max)
+ {
+ manchester_value = last_value ? 0 : 1;
+ last_value = manchester_value;
+ }
+ else
+ {
+ manchester_value = last_value;
+ }
+
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR (manchester_value + '0');
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+ if (! irmp_param2.protocol)
+#endif
+ {
+#ifdef ANALYZE
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ }
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 1 && manchester_value == 1) // RC6 mode != 0 ???
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to RC6A protocol\n");
+#endif // ANALYZE
+ irmp_param.complete_len = RC6_COMPLETE_DATA_LEN_LONG;
+ irmp_param.address_offset = 5;
+ irmp_param.address_end = irmp_param.address_offset + 15;
+ irmp_param.command_offset = irmp_param.address_end + 1; // skip 1 system bit, changes like a toggle bit
+ irmp_param.command_end = irmp_param.command_offset + 16 - 1;
+ irmp_tmp_address = 0;
+ }
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+ irmp_store_bit (manchester_value);
+ }
+ else
+ {
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1
+ if (irmp_param2.protocol == IRMP_FDC_PROTOCOL &&
+ irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX &&
+ ((irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) ||
+ (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX)))
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('?');
+#endif // ANALYZE
+ irmp_param.protocol = 0; // switch to FDC, see below
+ }
+ else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL &&
+ irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX &&
+ ((irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) ||
+ (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX)))
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('?');
+#endif // ANALYZE
+ irmp_param.protocol = 0; // switch to RCCAR, see below
+ }
+ else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('?');
+ ANALYZE_NEWLINE ();
+ ANALYZE_PRINTF ("error 3 manchester: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1
+ if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX)
+ {
+ if (irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF (" 1 (FDC)\n");
+#endif // ANALYZE
+ irmp_store_bit2 (1);
+ }
+ else if (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF (" 0 (FDC)\n");
+#endif // ANALYZE
+ irmp_store_bit2 (0);
+ }
+
+ if (! irmp_param.protocol)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to FDC protocol\n");
+#endif // ANALYZE
+ memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER));
+ irmp_param2.protocol = 0;
+ irmp_tmp_address = irmp_tmp_address2;
+ irmp_tmp_command = irmp_tmp_command2;
+ }
+ }
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+ if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX)
+ {
+ if (irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF (" 1 (RCCAR)\n");
+#endif // ANALYZE
+ irmp_store_bit2 (1);
+ }
+ else if (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF (" 0 (RCCAR)\n");
+#endif // ANALYZE
+ irmp_store_bit2 (0);
+ }
+
+ if (! irmp_param.protocol)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to RCCAR protocol\n");
+#endif // ANALYZE
+ memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER));
+ irmp_param2.protocol = 0;
+ irmp_tmp_address = irmp_tmp_address2;
+ irmp_tmp_command = irmp_tmp_command2;
+ }
+ }
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+ last_pause = irmp_pause_time;
+ wait_for_space = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+
+#if IRMP_SUPPORT_SERIAL == 1
+ if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL)
+ {
+ while (irmp_bit < irmp_param.complete_len && irmp_pulse_time > irmp_param.pulse_1_len_max)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+ irmp_store_bit (1);
+
+ if (irmp_pulse_time >= irmp_param.pulse_1_len_min)
+ {
+ irmp_pulse_time -= irmp_param.pulse_1_len_min;
+ }
+ else
+ {
+ irmp_pulse_time = 0;
+ }
+ }
+
+ while (irmp_bit < irmp_param.complete_len && irmp_pause_time > irmp_param.pause_1_len_max)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+ irmp_store_bit (0);
+
+ if (irmp_pause_time >= irmp_param.pause_1_len_min)
+ {
+ irmp_pause_time -= irmp_param.pause_1_len_min;
+ }
+ else
+ {
+ irmp_pause_time = 0;
+ }
+ }
+#ifdef ANALYZE
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ wait_for_space = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_SERIAL == 1
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit == 16) // Samsung: 16th bit
+ {
+ if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX &&
+ irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("SYNC\n");
+#endif // ANALYZE
+ wait_for_space = 0;
+ irmp_bit++;
+ }
+ else if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX)
+ {
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to SAMSUNG48 protocol ");
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_SAMSUNG48_PROTOCOL;
+ irmp_param.command_offset = SAMSUNG48_COMMAND_OFFSET;
+ irmp_param.command_end = SAMSUNG48_COMMAND_OFFSET + SAMSUNG48_COMMAND_LEN;
+ irmp_param.complete_len = SAMSUNG48_COMPLETE_DATA_LEN;
+#else
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol ");
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_SAMSUNG32_PROTOCOL;
+ irmp_param.command_offset = SAMSUNG32_COMMAND_OFFSET;
+ irmp_param.command_end = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN;
+ irmp_param.complete_len = SAMSUNG32_COMPLETE_DATA_LEN;
+#endif
+ if (irmp_pause_time >= SAMSUNG_1_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_1_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ wait_for_space = 0;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (0);
+ wait_for_space = 0;
+ }
+ }
+ else
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3 Samsung: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL
+
+#if IRMP_SUPPORT_NEC16_PROTOCOL
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NEC42_PROTOCOL &&
+#else // IRMP_SUPPORT_NEC_PROTOCOL instead
+ if (irmp_param.protocol == IRMP_NEC_PROTOCOL &&
+#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1
+ irmp_bit == 8 && irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Switching to NEC16 protocol\n");
+#endif // ANALYZE
+ irmp_param.protocol = IRMP_NEC16_PROTOCOL;
+ irmp_param.address_offset = NEC16_ADDRESS_OFFSET;
+ irmp_param.address_end = NEC16_ADDRESS_OFFSET + NEC16_ADDRESS_LEN;
+ irmp_param.command_offset = NEC16_COMMAND_OFFSET;
+ irmp_param.command_end = NEC16_COMMAND_OFFSET + NEC16_COMMAND_LEN;
+ irmp_param.complete_len = NEC16_COMPLETE_DATA_LEN;
+ wait_for_space = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_NEC16_PROTOCOL
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL)
+ {
+ if (irmp_pulse_time >= BANG_OLUFSEN_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_PULSE_LEN_MAX)
+ {
+ if (irmp_bit == 1) // Bang & Olufsen: 3rd bit
+ {
+ if (irmp_pause_time >= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("3rd start bit\n");
+#endif // ANALYZE
+ wait_for_space = 0;
+ irmp_bit++;
+ }
+ else
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3a B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+ else if (irmp_bit == 19) // Bang & Olufsen: trailer bit
+ {
+ if (irmp_pause_time >= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("trailer bit\n");
+#endif // ANALYZE
+ wait_for_space = 0;
+ irmp_bit++;
+ }
+ else
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3b B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+ else
+ {
+ if (irmp_pause_time >= BANG_OLUFSEN_1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_1_PAUSE_LEN_MAX)
+ { // pulse & pause timings correct for "1"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ last_value = 1;
+ wait_for_space = 0;
+ }
+ else if (irmp_pause_time >= BANG_OLUFSEN_0_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_0_PAUSE_LEN_MAX)
+ { // pulse & pause timings correct for "0"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (0);
+ last_value = 0;
+ wait_for_space = 0;
+ }
+ else if (irmp_pause_time >= BANG_OLUFSEN_R_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_R_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR (last_value + '0');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (last_value);
+ wait_for_space = 0;
+ }
+ else
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3c B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+ }
+ else
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3d B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RCMM32_PROTOCOL)
+ {
+ if (irmp_pause_time >= RCMM32_BIT_00_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_00_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+ ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+ irmp_store_bit (0);
+ irmp_store_bit (0);
+ }
+ else if (irmp_pause_time >= RCMM32_BIT_01_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_01_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+ ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+ irmp_store_bit (0);
+ irmp_store_bit (1);
+ }
+ else if (irmp_pause_time >= RCMM32_BIT_10_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_10_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+ ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+ irmp_store_bit (1);
+ irmp_store_bit (0);
+ }
+ else if (irmp_pause_time >= RCMM32_BIT_11_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_11_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+ ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+ irmp_store_bit (1);
+ irmp_store_bit (1);
+ }
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("\n");
+#endif // ANALYZE
+ wait_for_space = 0;
+ }
+ else
+#endif
+
+ if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max &&
+ irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max)
+ { // pulse & pause timings correct for "1"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('1');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ wait_for_space = 0;
+ }
+ else if (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max &&
+ irmp_pause_time >= irmp_param.pause_0_len_min && irmp_pause_time <= irmp_param.pause_0_len_max)
+ { // pulse & pause timings correct for "0"?
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('0');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (0);
+ wait_for_space = 0;
+ }
+ else
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL
+
+ if (irmp_param.protocol == IRMP_KATHREIN_PROTOCOL &&
+ irmp_pulse_time >= KATHREIN_1_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_1_PULSE_LEN_MAX &&
+ (((irmp_bit == 8 || irmp_bit == 6) &&
+ irmp_pause_time >= KATHREIN_SYNC_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_SYNC_BIT_PAUSE_LEN_MAX) ||
+ (irmp_bit == 12 &&
+ irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX)))
+
+ {
+ if (irmp_bit == 8)
+ {
+ irmp_bit++;
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('S');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_tmp_command <<= 1;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PUTCHAR ('S');
+ ANALYZE_NEWLINE ();
+#endif // ANALYZE
+ irmp_store_bit (1);
+ }
+ wait_for_space = 0;
+ }
+ else
+#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL
+ { // timing incorrect!
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 3: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ irmp_start_bit_detected = 0; // reset flags and wait for next start bit
+ irmp_pause_time = 0;
+ }
+
+ irmp_pulse_time = 1; // set counter to 1, not 0
+ }
+ }
+ else
+ { // counting the pulse length ...
+ if (! irmp_input) // still light?
+ { // yes...
+ irmp_pulse_time++; // increment counter
+ }
+ else
+ { // now it's dark!
+ wait_for_space = 1; // let's count the time (see above)
+ irmp_pause_time = 1; // set pause counter to 1, not 0
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RCII_PROTOCOL && waiting_for_2nd_pulse)
+ {
+printf ("fm: %d %d\n", irmp_pulse_time * 1000000 / F_INTERRUPTS, RCII_BIT_LEN * 1000000 / F_INTERRUPTS); // fm: Ausgabe ist "1000 466" oder "1533 466"
+#if 0
+ if (irmp_pulse_time >= RCII_BIT_LEN)
+ {
+ irmp_pulse_time -= RCII_BIT_LEN;
+ last_value = 0;
+ }
+ else
+ {
+ last_value = 1;
+ }
+#else // fm: das reicht für RCII
+ irmp_pulse_time -= RCII_BIT_LEN;
+ last_value = 0;
+#endif
+
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("RCII: got 2nd pulse, irmp_pulse_time = %d\n", irmp_pulse_time);
+#endif
+ waiting_for_2nd_pulse = 0;
+ }
+#endif
+ }
+ }
+
+ if (irmp_start_bit_detected && irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 0) // enough bits received?
+ {
+ if (last_irmp_command == irmp_tmp_command && key_repetition_len < AUTO_FRAME_REPETITION_LEN)
+ {
+ repetition_frame_number++;
+ }
+ else
+ {
+ repetition_frame_number = 0;
+ }
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+ // if SIRCS protocol and the code will be repeated within 50 ms, we will ignore 2nd and 3rd repetition frame
+ if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && (repetition_frame_number == 1 || repetition_frame_number == 2))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: SIRCS auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+ // if ORTEK protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame
+ if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL && repetition_frame_number == 1)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: ORTEK auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+#if 0 && IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 // fm 2015-12-02: don't ignore every 2nd frame
+ // if KASEIKYO protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame
+ if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && repetition_frame_number == 1)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: KASEIKYO auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+#if 0 && IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 // fm 2015-12-02: don't ignore every 2nd frame
+ // if SAMSUNG32 or SAMSUNG48 protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+ if ((irmp_param.protocol == IRMP_SAMSUNG32_PROTOCOL || irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL) && (repetition_frame_number & 0x01))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: SAMSUNG32/SAMSUNG48 auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+ // if NUBERT protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+ if (irmp_param.protocol == IRMP_NUBERT_PROTOCOL && (repetition_frame_number & 0x01))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: NUBERT auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+ // if SPEAKER protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+ if (irmp_param.protocol == IRMP_SPEAKER_PROTOCOL && (repetition_frame_number & 0x01))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: SPEAKER auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+ repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+ key_repetition_len = 0;
+ }
+ else
+#endif
+
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms code detected, length = %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit);
+#endif // ANALYZE
+ irmp_ir_detected = TRUE;
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_DENON_PROTOCOL)
+ { // check for repetition frame
+ if ((~irmp_tmp_command & 0x3FF) == last_irmp_denon_command) // command bits must be inverted
+ {
+ irmp_tmp_command = last_irmp_denon_command; // use command received before!
+ last_irmp_denon_command = 0;
+
+ irmp_protocol = irmp_param.protocol; // store protocol
+ irmp_address = irmp_tmp_address; // store address
+ irmp_command = irmp_tmp_command; // store command
+ }
+ else
+ {
+ if ((irmp_tmp_command & 0x01) == 0x00)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms info Denon: waiting for inverted command repetition\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+ last_irmp_denon_command = irmp_tmp_command;
+ denon_repetition_len = 0;
+ irmp_ir_detected = FALSE;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("%8.3fms warning Denon: got unexpected inverted command, ignoring it\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+ last_irmp_denon_command = 0;
+ irmp_ir_detected = FALSE;
+ }
+ }
+ }
+ else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && irmp_tmp_command == 0x01ff)
+ { // Grundig start frame?
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Detected GRUNDIG start frame, ignoring it\n");
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ else
+#endif // IRMP_SUPPORT_GRUNDIG_PROTOCOL
+
+#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NOKIA_PROTOCOL && irmp_tmp_address == 0x00ff && irmp_tmp_command == 0x00fe)
+ { // Nokia start frame?
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Detected NOKIA start frame, ignoring it\n");
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ else
+#endif // IRMP_SUPPORT_NOKIA_PROTOCOL
+ {
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NEC_PROTOCOL && irmp_bit == 0) // repetition frame
+ {
+ if (key_repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Detected NEC repetition frame, key_repetition_len = %d\n", (int)key_repetition_len);
+ ANALYZE_ONLY_NORMAL_PRINTF("REPETETION FRAME ");
+#endif // ANALYZE
+ irmp_tmp_address = last_irmp_address; // address is last address
+ irmp_tmp_command = last_irmp_command; // command is last command
+ irmp_flags |= IRMP_FLAG_REPETITION;
+ key_repetition_len = 0;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("Detected NEC repetition frame, ignoring it: timeout occured, key_repetition_len = %d > %d\n",
+ (int)key_repetition_len, (int)NEC_FRAME_REPEAT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL)
+ {
+ uint_fast8_t xor_value;
+
+ xor_value = (xor_check[0] & 0x0F) ^ ((xor_check[0] & 0xF0) >> 4) ^ (xor_check[1] & 0x0F) ^ ((xor_check[1] & 0xF0) >> 4);
+
+ if (xor_value != (xor_check[2] & 0x0F))
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 4: wrong XOR check for customer id: 0x%1x 0x%1x\n", xor_value, xor_check[2] & 0x0F);
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+
+ xor_value = xor_check[2] ^ xor_check[3] ^ xor_check[4];
+
+ if (xor_value != xor_check[5])
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 5: wrong XOR check for data bits: 0x%02x 0x%02x\n", xor_value, xor_check[5]);
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+
+ irmp_flags |= genre2; // write the genre2 bits into MSB of the flag byte
+ }
+#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL)
+ {
+ if (parity == PARITY_CHECK_FAILED)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 6: parity check failed\n");
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+
+ if ((irmp_tmp_address & 0x03) == 0x02)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("code skipped: ORTEK end of transmission frame (key release)\n");
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ irmp_tmp_address >>= 2;
+ }
+#endif // IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_MITSU_HEAVY_PROTOCOL)
+ {
+ check = irmp_tmp_command >> 8; // inverted upper byte == lower byte?
+ check = ~ check;
+ if (check == (irmp_tmp_command & 0xFF)) { //ok:
+ irmp_tmp_command &= 0xFF;
+ }
+ else mitsu_parity = PARITY_CHECK_FAILED;
+ if (mitsu_parity == PARITY_CHECK_FAILED)
+ {
+#ifdef ANALYZE
+ ANALYZE_PRINTF ("error 7: parity check failed\n");
+#endif // ANALYZE
+ irmp_ir_detected = FALSE;
+ }
+ }
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode = 6?
+ {
+ irmp_protocol = IRMP_RC6A_PROTOCOL;
+ }
+ else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+ {
+ irmp_protocol = irmp_param.protocol;
+ }
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_FDC_PROTOCOL)
+ {
+ if (irmp_tmp_command & 0x000F) // released key?
+ {
+ irmp_tmp_command = (irmp_tmp_command >> 4) | 0x80; // yes, set bit 7
+ }
+ else
+ {
+ irmp_tmp_command >>= 4; // no, it's a pressed key
+ }
+ irmp_tmp_command |= (irmp_tmp_address << 2) & 0x0F00; // 000000CCCCAAAAAA -> 0000CCCC00000000
+ irmp_tmp_address &= 0x003F;
+ }
+#endif
+
+ irmp_address = irmp_tmp_address; // store address
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_NEC_PROTOCOL)
+ {
+ last_irmp_address = irmp_tmp_address; // store as last address, too
+ }
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_RC5_PROTOCOL)
+ {
+ irmp_tmp_command |= rc5_cmd_bit6; // store bit 6
+ }
+#endif
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+ if (irmp_param.protocol == IRMP_S100_PROTOCOL)
+ {
+ irmp_tmp_command |= rc5_cmd_bit6; // store bit 6
+ }
+#endif
+ irmp_command = irmp_tmp_command; // store command
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+ irmp_id = irmp_tmp_id;
+#endif
+ }
+ }
+
+ if (irmp_ir_detected)
+ {
+ if (last_irmp_command == irmp_tmp_command &&
+ last_irmp_address == irmp_tmp_address &&
+ key_repetition_len < IRMP_KEY_REPETITION_LEN)
+ {
+ irmp_flags |= IRMP_FLAG_REPETITION;
+ }
+
+ last_irmp_address = irmp_tmp_address; // store as last address, too
+ last_irmp_command = irmp_tmp_command; // store as last command, too
+
+ key_repetition_len = 0;
+ }
+ else
+ {
+#ifdef ANALYZE
+ ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+ }
+
+ irmp_start_bit_detected = 0; // and wait for next start bit
+ irmp_tmp_command = 0;
+ irmp_pulse_time = 0;
+ irmp_pause_time = 0;
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+ if (irmp_protocol == IRMP_JVC_PROTOCOL) // the stop bit of JVC frame is also start bit of next frame
+ { // set pulse time here!
+ irmp_pulse_time = ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME));
+ }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+ }
+ }
+ }
+
+#if defined(STELLARIS_ARM_CORTEX_M4)
+ // Clear the timer interrupt
+ TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
+#endif
+
+#if (defined(_CHIBIOS_RT_) || defined(_CHIBIOS_NIL_)) && IRMP_USE_EVENT == 1
+ if (IRMP_EVENT_THREAD_PTR != NULL && irmp_ir_detected)
+ chEvtSignalI(IRMP_EVENT_THREAD_PTR,IRMP_EVENT_BIT);
+#endif
+
+#if IRMP_USE_IDLE_CALL == 1
+ // check if there is no ongoing transmission or repetition
+ if (!irmp_start_bit_detected && !irmp_pulse_time
+ && key_repetition_len > IRMP_KEY_REPETITION_LEN)
+ {
+ // no ongoing transmission
+ // enough time passed since last decoded signal that a repetition won't affect our output
+
+ irmp_idle();
+ }
+#endif // IRMP_USE_IDLE_CALL
+
+ return (irmp_ir_detected);
+}
+
+#ifdef ANALYZE
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * main functions - for Unix/Linux + Windows only!
+ *
+ * AVR: see main.c!
+ *
+ * Compile it under linux with:
+ * cc irmp.c -o irmp
+ *
+ * usage: ./irmp [-v|-s|-a|-l] < file
+ *
+ * options:
+ * -v verbose
+ * -s silent
+ * -a analyze
+ * -l list pulse/pauses
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+void print_spectrum (char * text, int * buf, int is_pulse);
+void
+print_spectrum (char * text, int * buf, int is_pulse)
+{
+ int i;
+ int j;
+ int min;
+ int max;
+ int max_value = 0;
+ int value;
+ int sum = 0;
+ int counter = 0;
+ double average = 0;
+ double tolerance;
+
+ puts ("-----------------------------------------------------------------------------");
+ printf ("%s:\n", text);
+
+ for (i = 0; i < 256; i++)
+ {
+ if (buf[i] > max_value)
+ {
+ max_value = buf[i];
+ }
+ }
+
+ for (i = 1; i < 200; i++)
+ {
+ if (buf[i] > 0)
+ {
+ printf ("%3d ", i);
+ value = (buf[i] * 60) / max_value;
+
+ for (j = 0; j < value; j++)
+ {
+ putchar ('o');
+ }
+ printf (" %d\n", buf[i]);
+
+ sum += i * buf[i];
+ counter += buf[i];
+ }
+ else
+ {
+ max = i - 1;
+
+ if (counter > 0)
+ {
+ average = (float) sum / (float) counter;
+
+ if (is_pulse)
+ {
+ printf ("pulse ");
+ }
+ else
+ {
+ printf ("pause ");
+ }
+
+ printf ("avg: %4.1f=%6.1f us, ", average, (1000000. * average) / (float) F_INTERRUPTS);
+ printf ("min: %2d=%6.1f us, ", min, (1000000. * min) / (float) F_INTERRUPTS);
+ printf ("max: %2d=%6.1f us, ", max, (1000000. * max) / (float) F_INTERRUPTS);
+
+ tolerance = (max - average);
+
+ if (average - min > tolerance)
+ {
+ tolerance = average - min;
+ }
+
+ tolerance = tolerance * 100 / average;
+ printf ("tol: %4.1f%%\n", tolerance);
+ }
+
+ counter = 0;
+ sum = 0;
+ min = i + 1;
+ }
+ }
+}
+
+#define STATE_LEFT_SHIFT 0x01
+#define STATE_RIGHT_SHIFT 0x02
+#define STATE_LEFT_CTRL 0x04
+#define STATE_LEFT_ALT 0x08
+#define STATE_RIGHT_ALT 0x10
+
+#define KEY_ESCAPE 0x1B // keycode = 0x006e
+#define KEY_MENUE 0x80 // keycode = 0x0070
+#define KEY_BACK 0x81 // keycode = 0x0071
+#define KEY_FORWARD 0x82 // keycode = 0x0072
+#define KEY_ADDRESS 0x83 // keycode = 0x0073
+#define KEY_WINDOW 0x84 // keycode = 0x0074
+#define KEY_1ST_PAGE 0x85 // keycode = 0x0075
+#define KEY_STOP 0x86 // keycode = 0x0076
+#define KEY_MAIL 0x87 // keycode = 0x0077
+#define KEY_FAVORITES 0x88 // keycode = 0x0078
+#define KEY_NEW_PAGE 0x89 // keycode = 0x0079
+#define KEY_SETUP 0x8A // keycode = 0x007a
+#define KEY_FONT 0x8B // keycode = 0x007b
+#define KEY_PRINT 0x8C // keycode = 0x007c
+#define KEY_ON_OFF 0x8E // keycode = 0x007c
+
+#define KEY_INSERT 0x90 // keycode = 0x004b
+#define KEY_DELETE 0x91 // keycode = 0x004c
+#define KEY_LEFT 0x92 // keycode = 0x004f
+#define KEY_HOME 0x93 // keycode = 0x0050
+#define KEY_END 0x94 // keycode = 0x0051
+#define KEY_UP 0x95 // keycode = 0x0053
+#define KEY_DOWN 0x96 // keycode = 0x0054
+#define KEY_PAGE_UP 0x97 // keycode = 0x0055
+#define KEY_PAGE_DOWN 0x98 // keycode = 0x0056
+#define KEY_RIGHT 0x99 // keycode = 0x0059
+#define KEY_MOUSE_1 0x9E // keycode = 0x0400
+#define KEY_MOUSE_2 0x9F // keycode = 0x0800
+
+static uint_fast8_t
+get_fdc_key (uint_fast16_t cmd)
+{
+ static uint8_t key_table[128] =
+ {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, '^', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0xDF, 0xB4, 0, '\b',
+ '\t', 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 0xFC, '+', 0, 0, 'a',
+ 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xF6, 0xE4, '#', '\r', 0, '<', 'y', 'x',
+ 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0, 0, 0, 0, 0, ' ', 0, 0,
+
+ 0, 0xB0, '!', '"', 0xA7, '$', '%', '&', '/', '(', ')', '=', '?', '`', 0, '\b',
+ '\t', 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P', 0xDC, '*', 0, 0, 'A',
+ 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xD6, 0xC4, '\'', '\r', 0, '>', 'Y', 'X',
+ 'C', 'V', 'B', 'N', 'M', ';', ':', '_', 0, 0, 0, 0, 0, ' ', 0, 0
+ };
+ static uint_fast8_t state;
+
+ uint_fast8_t key = 0;
+
+ switch (cmd)
+ {
+ case 0x002C: state |= STATE_LEFT_SHIFT; break; // pressed left shift
+ case 0x00AC: state &= ~STATE_LEFT_SHIFT; break; // released left shift
+ case 0x0039: state |= STATE_RIGHT_SHIFT; break; // pressed right shift
+ case 0x00B9: state &= ~STATE_RIGHT_SHIFT; break; // released right shift
+ case 0x003A: state |= STATE_LEFT_CTRL; break; // pressed left ctrl
+ case 0x00BA: state &= ~STATE_LEFT_CTRL; break; // released left ctrl
+ case 0x003C: state |= STATE_LEFT_ALT; break; // pressed left alt
+ case 0x00BC: state &= ~STATE_LEFT_ALT; break; // released left alt
+ case 0x003E: state |= STATE_RIGHT_ALT; break; // pressed left alt
+ case 0x00BE: state &= ~STATE_RIGHT_ALT; break; // released left alt
+
+ case 0x006e: key = KEY_ESCAPE; break;
+ case 0x004b: key = KEY_INSERT; break;
+ case 0x004c: key = KEY_DELETE; break;
+ case 0x004f: key = KEY_LEFT; break;
+ case 0x0050: key = KEY_HOME; break;
+ case 0x0051: key = KEY_END; break;
+ case 0x0053: key = KEY_UP; break;
+ case 0x0054: key = KEY_DOWN; break;
+ case 0x0055: key = KEY_PAGE_UP; break;
+ case 0x0056: key = KEY_PAGE_DOWN; break;
+ case 0x0059: key = KEY_RIGHT; break;
+ case 0x0400: key = KEY_MOUSE_1; break;
+ case 0x0800: key = KEY_MOUSE_2; break;
+
+ default:
+ {
+ if (!(cmd & 0x80)) // pressed key
+ {
+ if (cmd >= 0x70 && cmd <= 0x7F) // function keys
+ {
+ key = cmd + 0x10; // 7x -> 8x
+ }
+ else if (cmd < 64) // key listed in key_table
+ {
+ if (state & (STATE_LEFT_ALT | STATE_RIGHT_ALT))
+ {
+ switch (cmd)
+ {
+ case 0x0003: key = 0xB2; break; // upper 2
+ case 0x0008: key = '{'; break;
+ case 0x0009: key = '['; break;
+ case 0x000A: key = ']'; break;
+ case 0x000B: key = '}'; break;
+ case 0x000C: key = '\\'; break;
+ case 0x001C: key = '~'; break;
+ case 0x002D: key = '|'; break;
+ case 0x0034: key = 0xB5; break; // Mu
+ }
+ }
+ else if (state & (STATE_LEFT_CTRL))
+ {
+ if (key_table[cmd] >= 'a' && key_table[cmd] <= 'z')
+ {
+ key = key_table[cmd] - 'a' + 1;
+ }
+ else
+ {
+ key = key_table[cmd];
+ }
+ }
+ else
+ {
+ int idx = cmd + ((state & (STATE_LEFT_SHIFT | STATE_RIGHT_SHIFT)) ? 64 : 0);
+
+ if (key_table[idx])
+ {
+ key = key_table[idx];
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return (key);
+}
+
+static int analyze = FALSE;
+static int list = FALSE;
+static IRMP_DATA irmp_data;
+static int expected_protocol;
+static int expected_address;
+static int expected_command;
+static int do_check_expected_values;
+
+static void
+next_tick (void)
+{
+ if (! analyze && ! list)
+ {
+ (void) irmp_ISR ();
+
+ if (irmp_get_data (&irmp_data))
+ {
+ uint_fast8_t key;
+
+ ANALYZE_ONLY_NORMAL_PUTCHAR (' ');
+
+ if (verbose)
+ {
+ printf ("%8.3fms ", (double) (time_counter * 1000) / F_INTERRUPTS);
+ }
+
+ if (irmp_data.protocol == IRMP_ACP24_PROTOCOL)
+ {
+ uint16_t temp = (irmp_data.command & 0x000F) + 15;
+
+ printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, temp=%d",
+ irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, temp);
+ }
+ else if (irmp_data.protocol == IRMP_FDC_PROTOCOL && (key = get_fdc_key (irmp_data.command)) != 0)
+ {
+ if ((key >= 0x20 && key < 0x7F) || key >= 0xA0)
+ {
+ printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x, key='%c'",
+ irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key, key);
+ }
+ else if (key == '\r' || key == '\t' || key == KEY_ESCAPE || (key >= 0x80 && key <= 0x9F)) // function keys
+ {
+ char * p = (char *) NULL;
+
+ switch (key)
+ {
+ case '\t' : p = "TAB"; break;
+ case '\r' : p = "CR"; break;
+ case KEY_ESCAPE : p = "ESCAPE"; break;
+ case KEY_MENUE : p = "MENUE"; break;
+ case KEY_BACK : p = "BACK"; break;
+ case KEY_FORWARD : p = "FORWARD"; break;
+ case KEY_ADDRESS : p = "ADDRESS"; break;
+ case KEY_WINDOW : p = "WINDOW"; break;
+ case KEY_1ST_PAGE : p = "1ST_PAGE"; break;
+ case KEY_STOP : p = "STOP"; break;
+ case KEY_MAIL : p = "MAIL"; break;
+ case KEY_FAVORITES : p = "FAVORITES"; break;
+ case KEY_NEW_PAGE : p = "NEW_PAGE"; break;
+ case KEY_SETUP : p = "SETUP"; break;
+ case KEY_FONT : p = "FONT"; break;
+ case KEY_PRINT : p = "PRINT"; break;
+ case KEY_ON_OFF : p = "ON_OFF"; break;
+
+ case KEY_INSERT : p = "INSERT"; break;
+ case KEY_DELETE : p = "DELETE"; break;
+ case KEY_LEFT : p = "LEFT"; break;
+ case KEY_HOME : p = "HOME"; break;
+ case KEY_END : p = "END"; break;
+ case KEY_UP : p = "UP"; break;
+ case KEY_DOWN : p = "DOWN"; break;
+ case KEY_PAGE_UP : p = "PAGE_UP"; break;
+ case KEY_PAGE_DOWN : p = "PAGE_DOWN"; break;
+ case KEY_RIGHT : p = "RIGHT"; break;
+ case KEY_MOUSE_1 : p = "KEY_MOUSE_1"; break;
+ case KEY_MOUSE_2 : p = "KEY_MOUSE_2"; break;
+ default : p = "<UNKNWON>"; break;
+ }
+
+ printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x, key=%s",
+ irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key, p);
+ }
+ else
+ {
+ printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x",
+ irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key);
+ }
+ }
+ else
+ {
+ printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x",
+ irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags);
+ }
+
+ if (do_check_expected_values)
+ {
+ if (irmp_data.protocol != expected_protocol ||
+ irmp_data.address != expected_address ||
+ (int)irmp_data.command != expected_command)
+ {
+ printf ("\nerror 7: expected values differ: p=%2d (%s), a=0x%04x, c=0x%04x\n",
+ expected_protocol, irmp_protocol_names[expected_protocol], expected_address, expected_command);
+ }
+ else
+ {
+ printf (" checked!\n");
+ }
+ do_check_expected_values = FALSE; // only check 1st frame in a line!
+ }
+ else
+ {
+ putchar ('\n');
+ }
+ }
+ }
+}
+
+int
+main (int argc, char ** argv)
+{
+ int i;
+ int ch;
+ int last_ch = 0;
+ int pulse = 0;
+ int pause = 0;
+
+ int start_pulses[256];
+ int start_pauses[256];
+ int pulses[256];
+ int pauses[256];
+
+ int first_pulse = TRUE;
+ int first_pause = TRUE;
+
+ if (argc == 2)
+ {
+ if (! strcmp (argv[1], "-v"))
+ {
+ verbose = TRUE;
+ }
+ else if (! strcmp (argv[1], "-l"))
+ {
+ list = TRUE;
+ }
+ else if (! strcmp (argv[1], "-a"))
+ {
+ analyze = TRUE;
+ }
+ else if (! strcmp (argv[1], "-s"))
+ {
+ silent = TRUE;
+ }
+ else if (! strcmp (argv[1], "-r"))
+ {
+ radio = TRUE;
+ }
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ start_pulses[i] = 0;
+ start_pauses[i] = 0;
+ pulses[i] = 0;
+ pauses[i] = 0;
+ }
+
+ IRMP_PIN = 0xFF;
+
+ while ((ch = getchar ()) != EOF)
+ {
+ if (ch == '_' || ch == '0')
+ {
+ if (last_ch != ch)
+ {
+ if (pause > 0)
+ {
+ if (list)
+ {
+ printf ("pause: %d\n", pause);
+ }
+
+ if (analyze)
+ {
+ if (first_pause)
+ {
+ if (pause < 256)
+ {
+ start_pauses[pause]++;
+ }
+ first_pause = FALSE;
+ }
+ else
+ {
+ if (pause < 256)
+ {
+ pauses[pause]++;
+ }
+ }
+ }
+ }
+ pause = 0;
+ }
+ pulse++;
+ IRMP_PIN = 0x00;
+ }
+ else if (ch == 0xaf || ch == '-' || ch == '1')
+ {
+ if (last_ch != ch)
+ {
+ if (list)
+ {
+ printf ("pulse: %d ", pulse);
+ }
+
+ if (analyze)
+ {
+ if (first_pulse)
+ {
+ if (pulse < 256)
+ {
+ start_pulses[pulse]++;
+ }
+ first_pulse = FALSE;
+ }
+ else
+ {
+ if (pulse < 256)
+ {
+ pulses[pulse]++;
+ }
+ }
+ }
+ pulse = 0;
+ }
+
+ pause++;
+ IRMP_PIN = 0xff;
+ }
+ else if (ch == '\n')
+ {
+ IRMP_PIN = 0xff;
+ time_counter = 0;
+
+ if (list && pause > 0)
+ {
+ printf ("pause: %d\n", pause);
+ }
+ pause = 0;
+
+ if (! analyze)
+ {
+ for (i = 0; i < (int) ((10000.0 * F_INTERRUPTS) / 10000); i++) // newline: long pause of 10000 msec
+ {
+ next_tick ();
+ }
+ }
+ first_pulse = TRUE;
+ first_pause = TRUE;
+ }
+ else if (ch == '#')
+ {
+ time_counter = 0;
+
+ if (analyze)
+ {
+ while ((ch = getchar()) != '\n' && ch != EOF)
+ {
+ ;
+ }
+ }
+ else
+ {
+ char buf[1024];
+ char * p;
+ int idx = -1;
+
+ puts ("----------------------------------------------------------------------");
+ putchar (ch);
+
+
+ while ((ch = getchar()) != '\n' && ch != EOF)
+ {
+ if (ch != '\r') // ignore CR in DOS/Windows files
+ {
+ if (ch == '[' && idx == -1)
+ {
+ idx = 0;
+ }
+ else if (idx >= 0)
+ {
+ if (ch == ']')
+ {
+ do_check_expected_values = FALSE;
+ buf[idx] = '\0';
+ idx = -1;
+
+ expected_protocol = atoi (buf);
+
+ if (expected_protocol > 0)
+ {
+ p = buf;
+ while (*p)
+ {
+ if (*p == 'x')
+ {
+ p++;
+
+ if (sscanf (p, "%x", &expected_address) == 1)
+ {
+ do_check_expected_values = TRUE;
+ }
+ break;
+ }
+ p++;
+ }
+
+ if (do_check_expected_values)
+ {
+ do_check_expected_values = FALSE;
+
+ while (*p)
+ {
+ if (*p == 'x')
+ {
+ p++;
+
+ if (sscanf (p, "%x", &expected_command) == 1)
+ {
+ do_check_expected_values = TRUE;
+ }
+ break;
+ }
+ p++;
+ }
+
+ if (do_check_expected_values)
+ {
+ // printf ("!%2d %04x %04x!\n", expected_protocol, expected_address, expected_command);
+ }
+ }
+ }
+ }
+ else if (idx < 1024 - 2)
+ {
+ buf[idx++] = ch;
+ }
+ }
+ putchar (ch);
+ }
+ }
+ putchar ('\n');
+ }
+
+ }
+
+ last_ch = ch;
+
+ next_tick ();
+ }
+
+ if (analyze)
+ {
+ print_spectrum ("START PULSES", start_pulses, TRUE);
+ print_spectrum ("START PAUSES", start_pauses, FALSE);
+ print_spectrum ("PULSES", pulses, TRUE);
+ print_spectrum ("PAUSES", pauses, FALSE);
+ puts ("-----------------------------------------------------------------------------");
+ }
+ return 0;
+}
+
+#endif // ANALYZE
--- /dev/null
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmp.h
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_H_
+#define _IRMP_H_
+
+#ifndef IRMP_USE_AS_LIB
+# define IRMPCONFIG_STAGE1_H
+# include "irmpconfig.h"
+# undef IRMPCONFIG_STAGE1_H
+#endif
+
+#include "irmpsystem.h"
+
+#ifndef IRMP_USE_AS_LIB
+# define IRMPCONFIG_STAGE2_H
+# include "irmpconfig.h"
+# undef IRMPCONFIG_STAGE2_H
+#endif
+
+#if defined (__AVR_XMEGA__)
+# define _CONCAT(a,b) a##b
+# define CONCAT(a,b) _CONCAT(a,b)
+# define IRMP_PORT_PRE CONCAT(PORT, IRMP_PORT_LETTER)
+# define IRMP_DDR_PRE CONCAT(PORT, IRMP_PORT_LETTER)
+# define IRMP_PIN_PRE CONCAT(PORT, IRMP_PORT_LETTER)
+# define IRMP_PORT IRMP_PORT_PRE.OUT
+# define IRMP_DDR IRMP_DDR_PRE.DIR
+# define IRMP_PIN IRMP_PIN_PRE.IN
+# define IRMP_BIT IRMP_BIT_NUMBER
+# define input(x) ((x) & (1 << IRMP_BIT))
+
+#elif defined (ATMEL_AVR)
+# define _CONCAT(a,b) a##b
+# define CONCAT(a,b) _CONCAT(a,b)
+# define IRMP_PORT CONCAT(PORT, IRMP_PORT_LETTER)
+# define IRMP_DDR CONCAT(DDR, IRMP_PORT_LETTER)
+# define IRMP_PIN CONCAT(PIN, IRMP_PORT_LETTER)
+# define IRMP_BIT IRMP_BIT_NUMBER
+# define input(x) ((x) & (1 << IRMP_BIT))
+
+#elif defined (PIC_C18) || defined (PIC_CCS)
+# define input(x) (x)
+
+#elif defined (ARM_STM32)
+# define _CONCAT(a,b) a##b
+# define CONCAT(a,b) _CONCAT(a,b)
+# define IRMP_PORT CONCAT(GPIO, IRMP_PORT_LETTER)
+# if defined (ARM_STM32L1XX)
+# define IRMP_PORT_RCC CONCAT(RCC_AHBPeriph_GPIO, IRMP_PORT_LETTER)
+# elif defined (ARM_STM32F10X)
+# define IRMP_PORT_RCC CONCAT(RCC_APB2Periph_GPIO, IRMP_PORT_LETTER)
+# elif defined (ARM_STM32F4XX)
+# define IRMP_PORT_RCC CONCAT(RCC_AHB1Periph_GPIO, IRMP_PORT_LETTER)
+# endif
+# define IRMP_BIT CONCAT(GPIO_Pin_, IRMP_BIT_NUMBER)
+# define IRMP_PIN IRMP_PORT // for use with input(x) below
+# define input(x) (GPIO_ReadInputDataBit(x, IRMP_BIT))
+# ifndef USE_STDPERIPH_DRIVER
+# warning The STM32 port of IRMP uses the ST standard peripheral drivers which are not enabled in your build configuration.
+# endif
+
+#elif defined (ARM_STM32_HAL)
+# define IRMP_BIT IRMP_BIT_NUMBER
+# define IRMP_PIN IRMP_BIT_NUMBER // for use with input(x) below
+# define input(x) HAL_GPIO_ReadPin(IRMP_PORT_LETTER, x)
+
+#elif defined (STELLARIS_ARM_CORTEX_M4)
+# define _CONCAT(a,b) a##b
+# define CONCAT(a,b) _CONCAT(a,b)
+# define IRMP_PORT_PERIPH CONCAT(SYSCTL_PERIPH_GPIO, IRMP_PORT_LETTER)
+# define IRMP_PORT_BASE CONCAT(GPIO_PORT, CONCAT(IRMP_PORT_LETTER, _BASE))
+# define IRMP_PORT_PIN CONCAT(GPIO_PIN_, IRMP_BIT_NUMBER)
+# define IRMP_PIN IRMP_PORT_PIN
+# define input(x) ((uint8_t)(ROM_GPIOPinRead(IRMP_PORT_BASE, IRMP_PORT_PIN)))
+# define sei() IntMasterEnable()
+
+#elif defined(__SDCC_stm8)
+# define _CONCAT(a,b) a##b
+# define CONCAT(a,b) _CONCAT(a,b)
+# define IRMP_GPIO_STRUCT CONCAT(GPIO, IRMP_PORT_LETTER)
+# define IRMP_BIT IRMP_BIT_NUMBER
+# define input(x) ((x) & (1 << IRMP_BIT))
+
+#elif defined (TEENSY_ARM_CORTEX_M4)
+# define input(x) ((uint8_t)(digitalReadFast(x)))
+
+#elif defined(__xtensa__)
+# define IRMP_BIT IRMP_BIT_NUMBER
+# define input(x) GPIO_INPUT_GET(IRMP_BIT_NUMBER)
+
+#elif defined(_CHIBIOS_HAL_)
+# define input(x) palReadLine(x)
+
+#endif
+
+#if IRMP_USE_IDLE_CALL == 1
+void irmp_idle(void); // the user has to provide an implementation of the irmp_idle() function and link it
+#endif
+
+#if IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+# undef IRMP_SUPPORT_MATSUSHITA_PROTOCOL
+# define IRMP_SUPPORT_MATSUSHITA_PROTOCOL 1
+#endif
+
+#if IRMP_32_BIT == 0 && IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+# undef IRMP_SUPPORT_MERLIN_PROTOCOL
+# warning MERLIN protocol disabled, IRMP_32_BIT=1 needed
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1 && IRMP_SUPPORT_RUWIDO_PROTOCOL == 1
+# warning DENON protocol conflicts wih RUWIDO, please enable only one of both protocols
+# warning RUWIDO protocol disabled
+# undef IRMP_SUPPORT_RUWIDO_PROTOCOL
+# define IRMP_SUPPORT_RUWIDO_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 && IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+# warning KASEIKYO protocol conflicts wih PANASONIC, please enable only one of both protocols
+# warning PANASONIC protocol disabled
+# undef IRMP_SUPPORT_PANASONIC_PROTOCOL
+# define IRMP_SUPPORT_PANASONIC_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1 && IRMP_SUPPORT_ACP24_PROTOCOL == 1
+# warning DENON protocol conflicts wih ACP24, please enable only one of both protocols
+# warning ACP24 protocol disabled
+# undef IRMP_SUPPORT_ACP24_PROTOCOL
+# define IRMP_SUPPORT_ACP24_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1 && IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+# warning RC6 protocol conflicts wih ROOMBA, please enable only one of both protocols
+# warning ROOMBA protocol disabled
+# undef IRMP_SUPPORT_ROOMBA_PROTOCOL
+# define IRMP_SUPPORT_ROOMBA_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1 && IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+# warning PANASONIC protocol conflicts wih MITSU_HEAVY, please enable only one of both protocols
+# warning MITSU_HEAVY protocol disabled
+# undef IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+# define IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+# warning RC5 protocol conflicts wih ORTEK, please enable only one of both protocols
+# warning ORTEK protocol disabled
+# undef IRMP_SUPPORT_ORTEK_PROTOCOL
+# define IRMP_SUPPORT_ORTEK_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_S100_PROTOCOL == 1
+# warning RC5 protocol conflicts wih S100, please enable only one of both protocols
+# warning S100 protocol disabled
+# undef IRMP_SUPPORT_S100_PROTOCOL
+# define IRMP_SUPPORT_S100_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 && IRMP_SUPPORT_FAN_PROTOCOL == 1
+# warning NUBERT protocol conflicts wih FAN, please enable only one of both protocols
+# warning FAN protocol disabled
+# undef IRMP_SUPPORT_FAN_PROTOCOL
+# define IRMP_SUPPORT_FAN_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1 && IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+# warning FDC protocol conflicts wih ORTEK, please enable only one of both protocols
+# warning ORTEK protocol disabled
+# undef IRMP_SUPPORT_ORTEK_PROTOCOL
+# define IRMP_SUPPORT_ORTEK_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1 && IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+# warning ORTEK protocol conflicts wih NETBOX, please enable only one of both protocols
+# warning NETBOX protocol disabled
+# undef IRMP_SUPPORT_NETBOX_PROTOCOL
+# define IRMP_SUPPORT_NETBOX_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1
+# warning GRUNDIG protocol conflicts wih RCII, please enable only one of both protocols
+# warning RCII protocol disabled
+# undef IRMP_SUPPORT_RCII_PROTOCOL
+# define IRMP_SUPPORT_RCII_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1
+# warning NOKIA protocol conflicts wih RCII, please enable only one of both protocols
+# warning RCII protocol disabled
+# undef IRMP_SUPPORT_RCII_PROTOCOL
+# define IRMP_SUPPORT_RCII_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 && F_INTERRUPTS < 15000
+# warning F_INTERRUPTS too low, SIEMENS protocol disabled (should be at least 15000)
+# undef IRMP_SUPPORT_SIEMENS_PROTOCOL
+# define IRMP_SUPPORT_SIEMENS_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 && F_INTERRUPTS < 15000
+# warning F_INTERRUPTS too low, RUWIDO protocol disabled (should be at least 15000)
+# undef IRMP_SUPPORT_RUWIDO_PROTOCOL
+# define IRMP_SUPPORT_RUWIDO_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 && F_INTERRUPTS < 15000
+# warning F_INTERRUPTS too low, RECS80 protocol disabled (should be at least 15000)
+# undef IRMP_SUPPORT_RECS80_PROTOCOL
+# define IRMP_SUPPORT_RECS80_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 && F_INTERRUPTS < 15000
+# warning F_INTERRUPTS too low, RECS80EXT protocol disabled (should be at least 15000)
+# undef IRMP_SUPPORT_RECS80EXT_PROTOCOL
+# define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 && F_INTERRUPTS < 20000
+# warning F_INTERRUPTS too low, LEGO protocol disabled (should be at least 20000)
+# undef IRMP_SUPPORT_LEGO_PROTOCOL
+# define IRMP_SUPPORT_LEGO_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1 && IRMP_SUPPORT_SAMSUNG_PROTOCOL == 0
+# warning SAMSUNG48 protocol needs also SAMSUNG protocol, SAMSUNG protocol enabled
+# undef IRMP_SUPPORT_SAMSUNG_PROTOCOL
+# define IRMP_SUPPORT_SAMSUNG_PROTOCOL 1
+#endif
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+# warning JVC protocol needs also NEC protocol, NEC protocol enabled
+# undef IRMP_SUPPORT_NEC_PROTOCOL
+# define IRMP_SUPPORT_NEC_PROTOCOL 1
+#endif
+
+#if IRMP_SUPPORT_NEC16_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+# warning NEC16 protocol needs also NEC protocol, NEC protocol enabled
+# undef IRMP_SUPPORT_NEC_PROTOCOL
+# define IRMP_SUPPORT_NEC_PROTOCOL 1
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+# warning NEC42 protocol needs also NEC protocol, NEC protocol enabled
+# undef IRMP_SUPPORT_NEC_PROTOCOL
+# define IRMP_SUPPORT_NEC_PROTOCOL 1
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+# warning LGAIR protocol needs also NEC protocol, NEC protocol enabled
+# undef IRMP_SUPPORT_NEC_PROTOCOL
+# define IRMP_SUPPORT_NEC_PROTOCOL 1
+#endif
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1 && F_INTERRUPTS < 20000
+# warning F_INTERRUPTS too low, RCMM protocol disabled (should be at least 20000)
+# undef IRMP_SUPPORT_RCMM_PROTOCOL
+# define IRMP_SUPPORT_RCMM_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1 && F_INTERRUPTS > 16000
+# warning F_INTERRUPTS too high, PENTAX protocol disabled (should be max 16000)
+# undef IRMP_SUPPORT_PENTAX_PROTOCOL
+# define IRMP_SUPPORT_PENTAX_PROTOCOL 0
+#endif
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1 && F_INTERRUPTS > 16000
+# warning F_INTERRUPTS too high, GREE protocol disabled (should be max 16000)
+# undef IRMP_SUPPORT_GREE_PROTOCOL
+# define IRMP_SUPPORT_GREE_PROTOCOL 0
+#endif
+
+#if F_INTERRUPTS > 20000
+#error F_INTERRUPTS too high (should be not greater than 20000)
+#endif
+
+#include "irmpprotocols.h"
+
+#define IRMP_FLAG_REPETITION 0x01
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern void irmp_init (void);
+extern uint_fast8_t irmp_get_data (IRMP_DATA *);
+extern uint_fast8_t irmp_ISR (void);
+
+#if IRMP_PROTOCOL_NAMES == 1
+extern const char * const irmp_protocol_names[IRMP_N_PROTOCOLS + 1] PROGMEM;
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+extern void irmp_set_callback_ptr (void (*cb)(uint_fast8_t));
+#endif // IRMP_USE_CALLBACK == 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IRMP_H_ */
--- /dev/null
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpconfig.h
+ *
+ * DO NOT INCLUDE THIS FILE, WILL BE INCLUDED BY IRMP.H!
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Extensions for PIC 12F1820 W.Strobl 2014-07-20
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_H_
+# error please include only irmp.h, not irmpconfig.h
+#endif
+
+#ifdef IRMPCONFIG_STAGE1_H
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change IRMP_32_BIT here
+ *
+ * Normally, IRMP_32_BIT ist 0. Then we use irmp.command as a 16 bit value.
+ * If you set IRMP_32_BIT to 1, we will use irmp.command as a 32 bit value.
+ * A 32 bit command costs more CPU resources on 8 bit processors (e.g. AVR),
+ * but there are IR protocols which need more than 16 bits for a reasonable
+ * command value.
+ *
+ * If you want to use one of the following protocols, set IRMP_32_BIT to 1,
+ * otherwise set it to 0:
+ * - MERLIN
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#define IRMP_32_BIT 1 // use 32 bit command value, 0 or 1
+
+#endif // IRMPCONFIG_STAGE1_H
+#ifdef IRMPCONFIG_STAGE2_H
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change F_INTERRUPTS if you change the number of interrupts per second,
+ * Normally, F_INTERRUPTS should be in the range from 10000 to 15000, typical is 15000
+ * A value above 15000 costs additional program space, absolute maximum value is 20000.
+ * On PIC with XC8/C18 Compiler, use 15151 as the correct value.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef F_INTERRUPTS
+# define F_INTERRUPTS 20000 // interrupts per second, min: 10000, max: 20000, typ: 15000
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change settings from 1 to 0 if you want to disable one or more decoders.
+ * This saves program space.
+ *
+ * 1 enable decoder
+ * 0 disable decoder
+ *
+ * The standard decoders are enabled per default.
+ * Less common protocols are disabled here, you need to enable them manually.
+ *
+ * If you want to use FDC or RCCAR simultaneous with RC5 protocol, additional program space is required.
+ * If you don't need RC5 when using FDC/RCCAR, you should disable RC5.
+ * You cannot enable both DENON and RUWIDO, only enable one of them.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+// typical protocols, disable here! Enable Remarks F_INTERRUPTS Program Space
+#define IRMP_SUPPORT_SIRCS_PROTOCOL 1 // Sony SIRCS >= 10000 ~150 bytes
+#define IRMP_SUPPORT_NEC_PROTOCOL 1 // NEC + APPLE + ONKYO >= 10000 ~300 bytes
+#define IRMP_SUPPORT_SAMSUNG_PROTOCOL 1 // Samsung + Samsg32 >= 10000 ~300 bytes
+#define IRMP_SUPPORT_KASEIKYO_PROTOCOL 1 // Kaseikyo >= 10000 ~250 bytes
+
+// more protocols, enable here! Enable Remarks F_INTERRUPTS Program Space
+#define IRMP_SUPPORT_JVC_PROTOCOL 1 // JVC >= 10000 ~150 bytes
+#define IRMP_SUPPORT_NEC16_PROTOCOL 1 // NEC16 >= 10000 ~100 bytes
+#define IRMP_SUPPORT_NEC42_PROTOCOL 1 // NEC42 >= 10000 ~300 bytes
+#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL 1 // Matsushita >= 10000 ~50 bytes
+#define IRMP_SUPPORT_DENON_PROTOCOL 1 // DENON, Sharp >= 10000 ~250 bytes
+#define IRMP_SUPPORT_RC5_PROTOCOL 1 // RC5 >= 10000 ~250 bytes
+#define IRMP_SUPPORT_RC6_PROTOCOL 1 // RC6 & RC6A >= 10000 ~250 bytes
+#define IRMP_SUPPORT_IR60_PROTOCOL 1 // IR60 (SDA2008) >= 10000 ~300 bytes
+#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 1 // Grundig >= 10000 ~300 bytes
+#define IRMP_SUPPORT_SIEMENS_PROTOCOL 1 // Siemens Gigaset >= 15000 ~550 bytes
+#define IRMP_SUPPORT_NOKIA_PROTOCOL 1 // Nokia >= 10000 ~300 bytes
+
+// exotic protocols, enable here! Enable Remarks F_INTERRUPTS Program Space
+#define IRMP_SUPPORT_BOSE_PROTOCOL 1 // BOSE >= 10000 ~150 bytes
+#define IRMP_SUPPORT_KATHREIN_PROTOCOL 1 // Kathrein >= 10000 ~200 bytes
+#define IRMP_SUPPORT_NUBERT_PROTOCOL 1 // NUBERT >= 10000 ~50 bytes
+#define IRMP_SUPPORT_FAN_PROTOCOL 0 // FAN (ventilator) >= 10000 ~50 bytes
+#define IRMP_SUPPORT_SPEAKER_PROTOCOL 1 // SPEAKER (~NUBERT) >= 10000 ~50 bytes
+#define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL 1 // Bang & Olufsen >= 10000 ~200 bytes
+#define IRMP_SUPPORT_RECS80_PROTOCOL 1 // RECS80 (SAA3004) >= 15000 ~50 bytes
+#define IRMP_SUPPORT_RECS80EXT_PROTOCOL 1 // RECS80EXT (SAA3008) >= 15000 ~50 bytes
+#define IRMP_SUPPORT_THOMSON_PROTOCOL 1 // Thomson >= 10000 ~250 bytes
+#define IRMP_SUPPORT_NIKON_PROTOCOL 1 // NIKON camera >= 10000 ~250 bytes
+#define IRMP_SUPPORT_NETBOX_PROTOCOL 1 // Netbox keyboard >= 10000 ~400 bytes (PROTOTYPE!)
+#define IRMP_SUPPORT_ORTEK_PROTOCOL 0 // ORTEK (Hama) >= 10000 ~150 bytes
+#define IRMP_SUPPORT_TELEFUNKEN_PROTOCOL 1 // Telefunken 1560 >= 10000 ~150 bytes
+#define IRMP_SUPPORT_FDC_PROTOCOL 1 // FDC3402 keyboard >= 10000 (better 15000) ~150 bytes (~400 in combination with RC5)
+#define IRMP_SUPPORT_RCCAR_PROTOCOL 1 // RC Car >= 10000 (better 15000) ~150 bytes (~500 in combination with RC5)
+#define IRMP_SUPPORT_ROOMBA_PROTOCOL 0 // iRobot Roomba >= 10000 ~150 bytes
+#define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 // RUWIDO, T-Home >= 15000 ~550 bytes
+#define IRMP_SUPPORT_A1TVBOX_PROTOCOL 1 // A1 TV BOX >= 15000 (better 20000) ~300 bytes
+#define IRMP_SUPPORT_LEGO_PROTOCOL 1 // LEGO Power RC >= 20000 ~150 bytes
+#define IRMP_SUPPORT_RCMM_PROTOCOL 1 // RCMM 12,24, or 32 >= 20000 ~150 bytes
+#define IRMP_SUPPORT_LGAIR_PROTOCOL 1 // LG Air Condition >= 10000 ~300 bytes
+#define IRMP_SUPPORT_SAMSUNG48_PROTOCOL 1 // Samsung48 >= 10000 ~100 bytes (SAMSUNG must be enabled!)
+#define IRMP_SUPPORT_MERLIN_PROTOCOL 0 // Merlin >= 15000 (better 20000) ~300 bytes
+#define IRMP_SUPPORT_PENTAX_PROTOCOL 0 // Pentax >= 10000 ~150 bytes
+#define IRMP_SUPPORT_S100_PROTOCOL 0 // S100 >= 10000 ~250 bytes
+#define IRMP_SUPPORT_ACP24_PROTOCOL 0 // ACP24 >= 10000 ~250 bytes
+#define IRMP_SUPPORT_TECHNICS_PROTOCOL 1 // TECHNICS >= 10000 ~250 bytes
+#define IRMP_SUPPORT_PANASONIC_PROTOCOL 0 // PANASONIC Beamer >= 10000 ~250 bytes
+#define IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL 1 // Mitsubishi Aircond >= 10000 ~250 bytes
+#define IRMP_SUPPORT_VINCENT_PROTOCOL 1 // VINCENT >= 10000 ~250 bytes
+#define IRMP_SUPPORT_SAMSUNGAH_PROTOCOL 1 // SAMSUNG AH >= 10000 ~250 bytes
+#define IRMP_SUPPORT_IRMP16_PROTOCOL 1 // IRMP specific >= 15000 ~250 bytes
+#define IRMP_SUPPORT_GREE_PROTOCOL 0 // GREE CLIMATE >= 10000 ~250 bytes
+#define IRMP_SUPPORT_RCII_PROTOCOL 0 // RCII T+A >= 15000 ~250 bytes
+#define IRMP_SUPPORT_METZ_PROTOCOL 1 // METZ >= 15000 ~250 bytes
+
+#define IRMP_SUPPORT_RADIO1_PROTOCOL 0 // RADIO, e.g. TEVION >= 10000 ~250 bytes (experimental)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ATMEL ATMega/ATTiny/XMega
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined (ATMEL_AVR) || defined (__AVR_XMEGA__) // use PB6 as IR input on AVR
+# define IRMP_PORT_LETTER B
+# define IRMP_BIT_NUMBER 6
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for PIC C18 or XC8 compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (PIC_C18) // use RB4 as IR input on PIC (C18 or XC8 compiler)
+# if defined(__12F1840)
+# define IRMP_PIN RA5 // on 12F1840 with XC8 compiler
+# else
+# define IRMP_PIN PORTBbits.RB4 // PIC C18
+# endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for PIC CCS compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (PIC_CCS)
+# define IRMP_PIN PIN_B4 // use PB4 as IR input on PIC (CCS compiler)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ARM STM32
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (ARM_STM32) // use C13 as IR input on STM32
+# define IRMP_PORT_LETTER C
+# define IRMP_BIT_NUMBER 13
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Hardware pin for ARM STM32 (HAL) - don't change here, define IRMP_RECEIVE_GPIO_Port & IRMP_RECEIVE_PIN in STM32Cube (Main.h)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (ARM_STM32_HAL) // STM32: IRMP_RECEIVE_GPIO_Port & IRMP_RECEIVE_PIN must be defined in STM32Cube
+# define IRMP_PORT_LETTER IRMP_Receive_GPIO_Port
+# define IRMP_BIT_NUMBER IRMP_Receive_Pin
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for Stellaris ARM Cortex M4
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (STELLARIS_ARM_CORTEX_M4) // use B4 as IR input on Stellaris LM4F
+# define IRMP_PORT_LETTER B
+# define IRMP_BIT_NUMBER 4
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for STM8
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (SDCC_STM8) // use PA1 as IR input on STM8
+# define IRMP_PORT_LETTER A
+# define IRMP_BIT_NUMBER 1
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ESP8266
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (__xtensa__)
+# define IRMP_BIT_NUMBER 12 // use GPIO12 (Pin 7 UEXT) on ESP8266-EVB evaluation board
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for Teensy 3.x with teensyduino gcc compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (TEENSY_ARM_CORTEX_M4)
+# define IRMP_PIN 1 // use Digital pin 1 as IR input on Teensy
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for MBED
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined(__MBED__)
+# define IRMP_PIN P0_22 // use P1_27 on LPC1347
+# define IRMP_PINMODE PullUp // hardware dependent
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ChibiOS HAL
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined(_CHIBIOS_HAL_)
+# define IRMP_PIN LINE_IR_IN // use pin names as defined in the board config file, prefixed with "LINE_"
+# define IRMP_LOGGING_SD SD1 // the ChibiOS HAL Serial Driver instance to log to
+ // (when IRMP_LOGGING is enabled below).
+ // Make sure SERIAL_BUFFERS_SIZE is large enough when enabling logging
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Handling of unknown target system: DON'T CHANGE
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif !defined (UNIX_OR_WINDOWS)
+# error target system not defined.
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Set IRMP_LOGGING to 1 if want to log data to UART with 9600Bd
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_LOGGING
+# define IRMP_LOGGING 0 // 1: log IR signal (scan), 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use external logging routines
+ * If you enable external logging, you have also to enable IRMP_LOGGING above
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_EXT_LOGGING
+# define IRMP_EXT_LOGGING 0 // 1: use external logging, 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM!
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_PROTOCOL_NAMES
+# define IRMP_PROTOCOL_NAMES 0 // 1: access protocol names, 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use Callbacks to indicate input signal
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_USE_CALLBACK
+# define IRMP_USE_CALLBACK 0 // 1: use callbacks. 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Call the user-provided irmp_idle() function when IRMP is idle.
+ * Can be used to disable the timer irq and enter a sleep mode to save power
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_USE_IDLE_CALL
+# define IRMP_USE_IDLE_CALL 0 // 1: use idle calls. 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use ChibiOS Events to signal that valid IR data was received
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined(_CHIBIOS_RT_) || defined(_CHIBIOS_NIL_)
+
+# ifndef IRMP_USE_EVENT
+# define IRMP_USE_EVENT 0 // 1: use event. 0: do not. default is 0
+# endif
+
+# if IRMP_USE_EVENT == 1 && !defined(IRMP_EVENT_BIT)
+# define IRMP_EVENT_BIT 1 // event flag or bit to send
+# endif
+# if IRMP_USE_EVENT == 1 && !defined(IRMP_EVENT_THREAD_PTR)
+# define IRMP_EVENT_THREAD_PTR ir_receive_thread_p // pointer to the thread to send the event to
+extern thread_t *IRMP_EVENT_THREAD_PTR; // the pointer must be defined and initialized elsewhere
+# endif
+
+#endif // _CHIBIOS_RT_ || _CHIBIOS_NIL_
+
+#endif // _IRMPCONFIG_STAGE2_H_
--- /dev/null
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpprotocols.h - irmp protocols
+ *
+ * DO NOT INCLUDE THIS FILE, WILL BE INCLUDED BY IRMP.H or IRSND.H!
+ *
+ * Copyright (c) 2013-2019 Frank Meyer - frank(at)fli4l.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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_PROTOCOLS_H_
+#define _IRMP_PROTOCOLS_H_
+
+#if !defined(_IRMP_H_) && !defined(_IRSND_H_)
+# error please include only irmp.h or irsnd.h, not irmpprotocols.h
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IR protocols:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP_UNKNOWN_PROTOCOL 0 // uknown protocol
+#define IRMP_SIRCS_PROTOCOL 1 // Sony
+#define IRMP_NEC_PROTOCOL 2 // NEC, Pioneer, JVC, Toshiba, NoName etc.
+#define IRMP_SAMSUNG_PROTOCOL 3 // Samsung
+#define IRMP_MATSUSHITA_PROTOCOL 4 // Matsushita
+#define IRMP_KASEIKYO_PROTOCOL 5 // Kaseikyo (Panasonic etc)
+#define IRMP_RECS80_PROTOCOL 6 // Philips, Thomson, Nordmende, Telefunken, Saba
+#define IRMP_RC5_PROTOCOL 7 // Philips etc
+#define IRMP_DENON_PROTOCOL 8 // Denon, Sharp
+#define IRMP_RC6_PROTOCOL 9 // Philips etc
+#define IRMP_SAMSUNG32_PROTOCOL 10 // Samsung32: no sync pulse at bit 16, length 32 instead of 37
+#define IRMP_APPLE_PROTOCOL 11 // Apple, very similar to NEC
+#define IRMP_RECS80EXT_PROTOCOL 12 // Philips, Technisat, Thomson, Nordmende, Telefunken, Saba
+#define IRMP_NUBERT_PROTOCOL 13 // Nubert
+#define IRMP_BANG_OLUFSEN_PROTOCOL 14 // Bang & Olufsen
+#define IRMP_GRUNDIG_PROTOCOL 15 // Grundig
+#define IRMP_NOKIA_PROTOCOL 16 // Nokia
+#define IRMP_SIEMENS_PROTOCOL 17 // Siemens, e.g. Gigaset
+#define IRMP_FDC_PROTOCOL 18 // FDC keyboard
+#define IRMP_RCCAR_PROTOCOL 19 // RC Car
+#define IRMP_JVC_PROTOCOL 20 // JVC (NEC with 16 bits)
+#define IRMP_RC6A_PROTOCOL 21 // RC6A, e.g. Kathrein, XBOX
+#define IRMP_NIKON_PROTOCOL 22 // Nikon
+#define IRMP_RUWIDO_PROTOCOL 23 // Ruwido, e.g. T-Home Mediareceiver
+#define IRMP_IR60_PROTOCOL 24 // IR60 (SDA2008)
+#define IRMP_KATHREIN_PROTOCOL 25 // Kathrein
+#define IRMP_NETBOX_PROTOCOL 26 // Netbox keyboard (bitserial)
+#define IRMP_NEC16_PROTOCOL 27 // NEC with 16 bits (incl. sync)
+#define IRMP_NEC42_PROTOCOL 28 // NEC with 42 bits
+#define IRMP_LEGO_PROTOCOL 29 // LEGO Power Functions RC
+#define IRMP_THOMSON_PROTOCOL 30 // Thomson
+#define IRMP_BOSE_PROTOCOL 31 // BOSE
+#define IRMP_A1TVBOX_PROTOCOL 32 // A1 TV Box
+#define IRMP_ORTEK_PROTOCOL 33 // ORTEK - Hama
+#define IRMP_TELEFUNKEN_PROTOCOL 34 // Telefunken (1560)
+#define IRMP_ROOMBA_PROTOCOL 35 // iRobot Roomba vacuum cleaner
+#define IRMP_RCMM32_PROTOCOL 36 // Fujitsu-Siemens (Activy remote control)
+#define IRMP_RCMM24_PROTOCOL 37 // Fujitsu-Siemens (Activy keyboard)
+#define IRMP_RCMM12_PROTOCOL 38 // Fujitsu-Siemens (Activy keyboard)
+#define IRMP_SPEAKER_PROTOCOL 39 // Another loudspeaker protocol, similar to Nubert
+#define IRMP_LGAIR_PROTOCOL 40 // LG air conditioner
+#define IRMP_SAMSUNG48_PROTOCOL 41 // air conditioner with SAMSUNG protocol (48 bits)
+#define IRMP_MERLIN_PROTOCOL 42 // Merlin (Pollin 620 185)
+#define IRMP_PENTAX_PROTOCOL 43 // Pentax camera
+#define IRMP_FAN_PROTOCOL 44 // FAN (ventilator), very similar to NUBERT, but last bit is data bit instead of stop bit
+#define IRMP_S100_PROTOCOL 45 // very similar to RC5, but 14 instead of 13 data bits
+#define IRMP_ACP24_PROTOCOL 46 // Stiebel Eltron ACP24 air conditioner
+#define IRMP_TECHNICS_PROTOCOL 47 // Technics, similar to Matsushita, but 22 instead of 24 bits
+#define IRMP_PANASONIC_PROTOCOL 48 // Panasonic (Beamer), start bits similar to KASEIKYO
+#define IRMP_MITSU_HEAVY_PROTOCOL 49 // Mitsubishi-Heavy Aircondition, similar timing as Panasonic beamer
+#define IRMP_VINCENT_PROTOCOL 50 // Vincent
+#define IRMP_SAMSUNGAH_PROTOCOL 51 // SAMSUNG AH
+#define IRMP_IRMP16_PROTOCOL 52 // IRMP specific protocol for data transfer, e.g. between two microcontrollers via IR
+#define IRMP_GREE_PROTOCOL 53 // Gree climate
+#define IRMP_RCII_PROTOCOL 54 // RC II Infra Red Remote Control Protocol for FM8
+#define IRMP_METZ_PROTOCOL 55 // METZ
+#define IRMP_ONKYO_PROTOCOL 56
+
+#define IRMP_RADIO1_PROTOCOL 57 // Radio protocol (experimental status), do not use it yet!
+
+#define IRMP_N_PROTOCOLS 57 // number of supported protocols
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * timing constants:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+// fm 22.09.2011: may not be more than 16000L, otherwise some JVC codes will not be accepted
+#define IRMP_TIMEOUT_TIME 15500.0e-6 // timeout after 15.5 ms darkness
+#define IRMP_TIMEOUT_TIME_MS 15500L // timeout after 15.5 ms darkness
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+# define IRMP_TIMEOUT_NIKON_TIME 29500.0e-6 // 2nd timeout after 29.5 ms darkness (only for NIKON!)
+# define IRMP_TIMEOUT_NIKON_TIME_MS 29500L // 2nd timeout after 29.5 ms darkness
+typedef uint16_t PAUSE_LEN;
+# define IRMP_TIMEOUT_NIKON_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_NIKON_TIME + 0.5)
+#else
+# if (F_INTERRUPTS * IRMP_TIMEOUT_TIME_MS) / 1000000 >= 254
+typedef uint16_t PAUSE_LEN;
+# else
+typedef uint8_t PAUSE_LEN;
+# endif
+#endif
+
+#define IRMP_TIMEOUT_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_TIME + 0.5)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * flags of struct IRMP_PARAMETER:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP_PARAM_FLAG_IS_MANCHESTER 0x01
+#define IRMP_PARAM_FLAG_1ST_PULSE_IS_1 0x02
+#define IRMP_PARAM_FLAG_IS_SERIAL 0x04
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SIRCS:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SIRCS_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse
+#define SIRCS_START_BIT_PAUSE_TIME 600.0e-6 // 600 usec pause
+#define SIRCS_1_PULSE_TIME 1200.0e-6 // 1200 usec pulse
+#define SIRCS_0_PULSE_TIME 600.0e-6 // 600 usec pulse
+#define SIRCS_PAUSE_TIME 600.0e-6 // 600 usec pause
+#define SIRCS_FRAMES 3 // SIRCS sends each frame 3 times
+#define SIRCS_AUTO_REPETITION_PAUSE_TIME 25.0e-3 // auto repetition after 25ms
+#define SIRCS_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms
+#define SIRCS_ADDRESS_OFFSET 15 // skip 15 bits
+#define SIRCS_ADDRESS_LEN 5 // read up to 5 address bits
+#define SIRCS_COMMAND_OFFSET 0 // skip 0 bits
+#define SIRCS_COMMAND_LEN 15 // read 12-15 command bits
+#define SIRCS_MINIMUM_DATA_LEN 12 // minimum data length
+#define SIRCS_COMPLETE_DATA_LEN 20 // complete length - may be up to 20
+#define SIRCS_STOP_BIT 0 // has no stop bit
+#define SIRCS_LSB 1 // LSB...MSB
+#define SIRCS_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NEC & NEC42 & NEC16 & LGAIR:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NEC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse
+#define NEC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
+#define NEC_REPEAT_START_BIT_PAUSE_TIME 2250.0e-6 // 2250 usec pause
+#define NEC_PULSE_TIME 560.0e-6 // 560 usec pulse
+#define NEC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause
+#define NEC_0_PAUSE_TIME 560.0e-6 // 560 usec pause
+#define NEC_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define NEC_ADDRESS_OFFSET 0 // skip 0 bits
+#define NEC_ADDRESS_LEN 16 // read 16 address bits
+#define NEC_COMMAND_OFFSET 16 // skip 16 bits (8 address + 8 /address)
+#define NEC_COMMAND_LEN 16 // read 16 bits (8 command + 8 /command)
+#define NEC_COMPLETE_DATA_LEN 32 // complete length
+#define NEC_STOP_BIT 1 // has stop bit
+#define NEC_LSB 1 // LSB...MSB
+#define NEC_FLAGS 0 // flags
+
+#define NEC42_ADDRESS_OFFSET 0 // skip 0 bits
+#define NEC42_ADDRESS_LEN 13 // read 13 address bits
+#define NEC42_COMMAND_OFFSET 26 // skip 26 bits (2 x 13 address bits)
+#define NEC42_COMMAND_LEN 8 // read 8 command bits
+#define NEC42_COMPLETE_DATA_LEN 42 // complete length (2 x 13 + 2 x 8)
+
+#define LGAIR_ADDRESS_OFFSET 0 // skip 0 bits
+#define LGAIR_ADDRESS_LEN 8 // read 8 address bits
+#define LGAIR_COMMAND_OFFSET 8 // skip 8 bits (8 address)
+#define LGAIR_COMMAND_LEN 16 // read 16 bits (16 command)
+#define LGAIR_COMPLETE_DATA_LEN 28 // complete length (8 address + 16 command + 4 checksum)
+
+#define NEC16_ADDRESS_OFFSET 0 // skip 0 bits
+#define NEC16_ADDRESS_LEN 8 // read 8 address bits
+#define NEC16_COMMAND_OFFSET 8 // skip 8 bits (8 address)
+#define NEC16_COMMAND_LEN 8 // read 8 bits (8 command)
+#define NEC16_COMPLETE_DATA_LEN 16 // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SAMSUNG & SAMSUNG32 & SAMSUNG48:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SAMSUNG_START_BIT_PULSE_TIME 4500.0e-6 // 4500 usec pulse
+#define SAMSUNG_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
+#define SAMSUNG_PULSE_TIME 550.0e-6 // 550 usec pulse
+#define SAMSUNG_1_PAUSE_TIME 1500.0e-6 // 1550 usec pause
+#define SAMSUNG_0_PAUSE_TIME 500.0e-6 // 500 usec pause
+
+#define SAMSUNG_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms
+#define SAMSUNG_ADDRESS_OFFSET 0 // skip 0 bits
+#define SAMSUNG_ADDRESS_LEN 16 // read 16 address bits
+#define SAMSUNG_ID_OFFSET 17 // skip 16 + 1 sync bit
+#define SAMSUNG_ID_LEN 4 // read 4 id bits
+#define SAMSUNG_COMMAND_OFFSET 21 // skip 16 + 1 sync + 4 data bits
+#define SAMSUNG_COMMAND_LEN 16 // read 16 command bits
+#define SAMSUNG_COMPLETE_DATA_LEN 37 // complete length
+#define SAMSUNG_STOP_BIT 1 // has stop bit
+#define SAMSUNG_LSB 1 // LSB...MSB?
+#define SAMSUNG_FLAGS 0 // flags
+
+#define SAMSUNG32_COMMAND_OFFSET 16 // skip 16 bits
+#define SAMSUNG32_COMMAND_LEN 16 // read 16 command bits
+#define SAMSUNG32_COMPLETE_DATA_LEN 32 // complete length
+#define SAMSUNG32_FRAMES 1 // SAMSUNG32 sends one frame
+#define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME 47.0e-3 // repetition after 47 ms
+#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 47ms
+
+#define SAMSUNG48_COMMAND_OFFSET 16 // skip 16 bits
+#define SAMSUNG48_COMMAND_LEN 32 // read 32 command bits
+#define SAMSUNG48_COMPLETE_DATA_LEN 48 // complete length
+#define SAMSUNG48_FRAMES 2 // SAMSUNG48 sends each frame 2 times
+#define SAMSUNG48_AUTO_REPETITION_PAUSE_TIME 5.0e-3 // repetition after 5 ms
+#define SAMSUNG48_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 47ms
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SAMSUNGAH:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SAMSUNGAH_START_BIT_PULSE_TIME 2500.0e-6 // 2500 usec pulse
+#define SAMSUNGAH_START_BIT_PAUSE_TIME 1900.0e-6 // 1900 usec pause
+#define SAMSUNGAH_PULSE_TIME 450.0e-6 // 450 usec pulse
+#define SAMSUNGAH_1_PAUSE_TIME 1100.0e-6 // 1100 usec pause
+#define SAMSUNGAH_0_PAUSE_TIME 450.0e-6 // 450 usec pause
+#define SAMSUNGAH_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define SAMSUNGAH_ADDRESS_OFFSET 0 // skip 0 bits
+#define SAMSUNGAH_ADDRESS_LEN 16 // read 16 address bits, ignore 17..31
+#define SAMSUNGAH_COMMAND_OFFSET 32 // skip 32 bits
+#define SAMSUNGAH_COMMAND_LEN 16 // read 32 bits
+#define SAMSUNGAH_COMPLETE_DATA_LEN 48 // complete length
+#define SAMSUNGAH_STOP_BIT 1 // has stop bit
+#define SAMSUNGAH_LSB 1 // LSB...MSB?
+#define SAMSUNGAH_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MATSUSHITA:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MATSUSHITA_START_BIT_PULSE_TIME 3488.0e-6 // 3488 usec pulse
+#define MATSUSHITA_START_BIT_PAUSE_TIME 3488.0e-6 // 3488 usec pause
+#define MATSUSHITA_PULSE_TIME 872.0e-6 // 872 usec pulse
+#define MATSUSHITA_1_PAUSE_TIME 2616.0e-6 // 2616 usec pause
+#define MATSUSHITA_0_PAUSE_TIME 872.0e-6 // 872 usec pause
+#define MATSUSHITA_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define MATSUSHITA_ADDRESS_OFFSET 12 // skip 12 bits
+#define MATSUSHITA_ADDRESS_LEN 12 // read 12 address bits
+#define MATSUSHITA_COMMAND_OFFSET 0 // skip 0 bits
+#define MATSUSHITA_COMMAND_LEN 12 // read 12 bits (6 custom + 6 command)
+#define MATSUSHITA_COMPLETE_DATA_LEN 24 // complete length
+#define MATSUSHITA_STOP_BIT 1 // has stop bit
+#define MATSUSHITA_LSB 1 // LSB...MSB?
+#define MATSUSHITA_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * TECHNICS: same timings as MATSUSHITA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define TECHNICS_ADDRESS_LEN 0 // read 0 address bits
+#define TECHNICS_COMMAND_LEN 11 // read 11 bits
+#define TECHNICS_COMPLETE_DATA_LEN 22 // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * KASEIKYO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define KASEIKYO_START_BIT_PULSE_TIME 3380.0e-6 // 3380 usec pulse
+#define KASEIKYO_START_BIT_PAUSE_TIME 1690.0e-6 // 1690 usec pause
+#define KASEIKYO_PULSE_TIME 423.0e-6 // 525 usec pulse
+#define KASEIKYO_1_PAUSE_TIME 1269.0e-6 // 525 usec pause
+#define KASEIKYO_0_PAUSE_TIME 423.0e-6 // 1690 usec pause
+#define KASEIKYO_AUTO_REPETITION_PAUSE_TIME 74.0e-3 // repetition after 74 ms
+#define KASEIKYO_FRAME_REPEAT_PAUSE_TIME 74.0e-3 // frame repeat after 74 ms
+#define KASEIKYO_ADDRESS_OFFSET 0 // skip 0 bits
+#define KASEIKYO_ADDRESS_LEN 16 // read 16 address bits
+#define KASEIKYO_COMMAND_OFFSET 28 // skip 28 bits (16 manufacturer & 4 parity & 8 genre)
+#define KASEIKYO_COMMAND_LEN 12 // read 12 command bits (10 real command & 2 id)
+#define KASEIKYO_COMPLETE_DATA_LEN 48 // complete length
+#define KASEIKYO_STOP_BIT 1 // has stop bit
+#define KASEIKYO_LSB 1 // LSB...MSB?
+#define KASEIKYO_FRAMES 1 // KASEIKYO sends 1 frame
+#define KASEIKYO_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * PANASONIC (Beamer), start bit timings similar to KASEIKYO
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define PANASONIC_START_BIT_PULSE_TIME 3600.0e-6 // 3600 usec pulse
+#define PANASONIC_START_BIT_PAUSE_TIME 1600.0e-6 // 1690 usec pause
+#define PANASONIC_PULSE_TIME 565.0e-6 // 565 usec pulse
+#define PANASONIC_1_PAUSE_TIME 1140.0e-6 // 1140 usec pause
+#define PANASONIC_0_PAUSE_TIME 316.0e-6 // 316 usec pause
+#define PANASONIC_AUTO_REPETITION_PAUSE_TIME 40.0e-3 // repetition after 40 ms?
+#define PANASONIC_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40 ms
+#define PANASONIC_ADDRESS_OFFSET 24 // skip 24 bits: 010000000000010000000001
+#define PANASONIC_ADDRESS_LEN 16 // read 16 address bits
+#define PANASONIC_COMMAND_OFFSET 40 // skip 40 bits
+#define PANASONIC_COMMAND_LEN 16 // read 16 command bits
+#define PANASONIC_COMPLETE_DATA_LEN 56 // complete length
+#define PANASONIC_STOP_BIT 1 // has stop bit
+#define PANASONIC_LSB 1 // LSB...MSB?
+#define PANASONIC_FRAMES 1 // PANASONIC sends 1 frame
+#define PANASONIC_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MITSUBISHI-Heavy Aircondition, timings similar to PANASONIC beamer
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MITSU_HEAVY_START_BIT_PULSE_TIME 3200.0e-6 // 3600 usec pulse
+#define MITSU_HEAVY_START_BIT_PAUSE_TIME 1560.0e-6 // 1690 usec pause
+#define MITSU_HEAVY_PULSE_TIME 400.0e-6 // 565 usec pulse
+#define MITSU_HEAVY_1_PAUSE_TIME 1200.0e-6 // 1140 usec pause
+#define MITSU_HEAVY_0_PAUSE_TIME 430.0e-6 // 316 usec pause
+#define MITSU_HEAVY_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40 ms
+#define MITSU_HEAVY_ADDRESS_OFFSET 40 // skip 24 bits: 010000000000010000000001
+#define MITSU_HEAVY_ADDRESS_LEN 16 // read 16 address bits
+#define MITSU_HEAVY_COMMAND_OFFSET 56 // skip 40 bits
+#define MITSU_HEAVY_COMMAND_LEN 16 // read 16 command bits
+#define MITSU_HEAVY_COMPLETE_DATA_LEN 88 // complete length
+#define MITSU_HEAVY_STOP_BIT 1 // has stop bit
+#define MITSU_HEAVY_LSB 0 // LSB...MSB?
+#define MITSU_HEAVY_FRAMES 1 // PANASONIC sends 1 frame
+#define MITSU_HEAVY_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * VINCENT
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define VINCENT_START_BIT_PULSE_TIME 2500.0e-6 // 2500 usec pulse
+#define VINCENT_START_BIT_PAUSE_TIME 4600.0e-6 // 4600 usec pause
+#define VINCENT_PULSE_TIME 550.0e-6 // 550 usec pulse
+#define VINCENT_1_PAUSE_TIME 1540.0e-6 // 1540 usec pause
+#define VINCENT_0_PAUSE_TIME 550.0e-6 // 550 usec pause
+#define VINCENT_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40 ms ?
+#define VINCENT_ADDRESS_OFFSET 0 // skip 0 bits
+#define VINCENT_ADDRESS_LEN 16 // read 16 address bits
+#define VINCENT_COMMAND_OFFSET 16 // skip 16 bits
+#define VINCENT_COMMAND_LEN 16 // read 16 command bits
+#define VINCENT_COMPLETE_DATA_LEN 32 // complete length
+#define VINCENT_STOP_BIT 1 // has stop bit
+#define VINCENT_LSB 0 // LSB...MSB?
+#define VINCENT_FRAMES 1 // VINCENT sends 1 frame
+#define VINCENT_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RECS80:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RECS80_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse
+#define RECS80_START_BIT_PAUSE_TIME 7432.0e-6 // 7432 usec pause
+#define RECS80_PULSE_TIME 158.0e-6 // 158 usec pulse
+#define RECS80_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause
+#define RECS80_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause
+#define RECS80_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define RECS80_ADDRESS_OFFSET 1 // skip 1 bit (toggle bit)
+#define RECS80_ADDRESS_LEN 3 // read 3 address bits
+#define RECS80_COMMAND_OFFSET 4 // skip 4 bits (1 toggle + 3 address)
+#define RECS80_COMMAND_LEN 6 // read 6 command bits
+#define RECS80_COMPLETE_DATA_LEN 10 // complete length
+#define RECS80_STOP_BIT 1 // has stop bit
+#define RECS80_LSB 0 // MSB...LSB
+#define RECS80_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC5:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RC5_BIT_TIME 889.0e-6 // 889 usec pulse/pause
+#define RC5_FRAME_REPEAT_PAUSE_TIME 88.9e-3 // frame repeat after 88.9ms
+
+#define RC5_ADDRESS_OFFSET 1 // skip 1 bit (2nd start)
+#define RC5_ADDRESS_LEN 6 // read 1 toggle bit (for key repetition detection) + 5 address bits
+#define RC5_COMMAND_OFFSET 7 // skip 5 bits (2nd start + 1 toggle + 5 address)
+#define RC5_COMMAND_LEN 6 // read 6 command bits
+#define RC5_COMPLETE_DATA_LEN 13 // complete length
+#define RC5_STOP_BIT 0 // has no stop bit
+#define RC5_LSB 0 // MSB...LSB
+#define RC5_FLAGS IRMP_PARAM_FLAG_IS_MANCHESTER // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RCII:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCII_START_BIT_PULSE_TIME 512.0e-6 // 512 usec pulse
+#define RCII_START_BIT_PAUSE_TIME 2560.0e-6 // 2560 usec pause
+#define RCII_START_BIT2_PULSE_TIME 1024.0e-6 // 1024 usec pulse
+
+#define RCII_BIT_TIME 512.0e-6 // 512 usec pulse/pause
+#define RCII_FRAME_REPEAT_PAUSE_TIME 117.76e-3 // frame repeat after 117.76ms
+
+#define RCII_ADDRESS_OFFSET 0 // skip 1 bit (2nd start)
+#define RCII_ADDRESS_LEN 0 // no address
+#define RCII_COMMAND_OFFSET 0 // command offset is 0
+#define RCII_COMMAND_LEN 10 // read 1 + 9 command bits
+#define RCII_COMPLETE_DATA_LEN 10 // complete length
+#define RCII_STOP_BIT 0 // has no stop bit
+#define RCII_LSB 0 // MSB...LSB
+#define RCII_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * S100: very similar to RC5, but 14 insted of 13 bits
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define S100_BIT_TIME 889.0e-6 // 889 usec pulse/pause
+#define S100_FRAME_REPEAT_PAUSE_TIME 88.9e-3 // frame repeat after 88.9ms
+
+#define S100_ADDRESS_OFFSET 1 // skip 1 bit (2nd start)
+#define S100_ADDRESS_LEN 6 // read 1 toggle bit (for key repetition detection) + 5 address bits
+#define S100_COMMAND_OFFSET 7 // skip 5 bits (2nd start + 1 toggle + 5 address)
+#define S100_COMMAND_LEN 7 // read 7 command bits
+#define S100_COMPLETE_DATA_LEN 14 // complete length
+#define S100_STOP_BIT 0 // has no stop bit
+#define S100_LSB 0 // MSB...LSB
+#define S100_FLAGS IRMP_PARAM_FLAG_IS_MANCHESTER // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * DENON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define DENON_PULSE_TIME 310.0e-6 // 310 usec pulse in practice, 275 in theory
+#define DENON_1_PAUSE_TIME 1780.0e-6 // 1780 usec pause in practice, 1900 in theory
+#define DENON_0_PAUSE_TIME 745.0e-6 // 745 usec pause in practice, 775 in theory
+#define DENON_FRAMES 2 // DENON sends each frame 2 times
+#define DENON_AUTO_REPETITION_PAUSE_TIME 45.0e-3 // inverted repetition after 45ms
+#define DENON_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define DENON_ADDRESS_OFFSET 0 // skip 0 bits
+#define DENON_ADDRESS_LEN 5 // read 5 address bits
+#define DENON_COMMAND_OFFSET 5 // skip 5
+#define DENON_COMMAND_LEN 10 // read 10 command bits
+#define DENON_COMPLETE_DATA_LEN 15 // complete length
+#define DENON_STOP_BIT 1 // has stop bit
+#define DENON_LSB 0 // MSB...LSB
+#define DENON_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC6:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RC6_START_BIT_PULSE_TIME 2666.0e-6 // 2.666 msec pulse
+#define RC6_START_BIT_PAUSE_TIME 889.0e-6 // 889 usec pause
+#define RC6_TOGGLE_BIT_TIME 889.0e-6 // 889 msec pulse/pause
+#define RC6_BIT_TIME 444.0e-6 // 444 usec pulse/pause
+#define RC6_BIT_2_TIME 889.0e-6 // 889 usec pulse/pause
+#define RC6_BIT_3_TIME 1333.0e-6 // 1333 usec pulse/pause
+#define RC6_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define RC6_ADDRESS_OFFSET 5 // skip "1" + 3 mode bits + 1 toggle bit
+#define RC6_ADDRESS_LEN 8 // read 8 address bits
+#define RC6_COMMAND_OFFSET 13 // skip 12 bits ("1" + 3 mode + 1 toggle + 8 address)
+#define RC6_COMMAND_LEN 8 // read 8 command bits
+#define RC6_COMPLETE_DATA_LEN_SHORT 21 // complete length
+#define RC6_COMPLETE_DATA_LEN_LONG 36 // complete length
+#define RC6_STOP_BIT 0 // has no stop bit
+#define RC6_LSB 0 // MSB...LSB
+#define RC6_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RECS80EXT:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RECS80EXT_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse
+#define RECS80EXT_START_BIT_PAUSE_TIME 3637.0e-6 // 3637 usec pause
+#define RECS80EXT_PULSE_TIME 158.0e-6 // 158 usec pulse
+#define RECS80EXT_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause
+#define RECS80EXT_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause
+#define RECS80EXT_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define RECS80EXT_ADDRESS_OFFSET 2 // skip 2 bits (2nd start + 1 toggle)
+#define RECS80EXT_ADDRESS_LEN 4 // read 4 address bits
+#define RECS80EXT_COMMAND_OFFSET 6 // skip 6 bits (2nd start + 1 toggle + 4 address)
+#define RECS80EXT_COMMAND_LEN 6 // read 6 command bits
+#define RECS80EXT_COMPLETE_DATA_LEN 12 // complete length
+#define RECS80EXT_STOP_BIT 1 // has stop bit
+#define RECS80EXT_LSB 0 // MSB...LSB
+#define RECS80EXT_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NUBERT:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NUBERT_START_BIT_PULSE_TIME 1340.0e-6 // 1340 usec pulse
+#define NUBERT_START_BIT_PAUSE_TIME 340.0e-6 // 340 usec pause
+#define NUBERT_1_PULSE_TIME 1340.0e-6 // 1340 usec pulse
+#define NUBERT_1_PAUSE_TIME 340.0e-6 // 340 usec pause
+#define NUBERT_0_PULSE_TIME 500.0e-6 // 500 usec pulse
+#define NUBERT_0_PAUSE_TIME 1300.0e-6 // 1300 usec pause
+#define NUBERT_FRAMES 2 // Nubert sends 2 frames
+#define NUBERT_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
+#define NUBERT_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 45ms
+#define NUBERT_ADDRESS_OFFSET 0 // skip 0 bits
+#define NUBERT_ADDRESS_LEN 0 // read 0 address bits
+#define NUBERT_COMMAND_OFFSET 0 // skip 0 bits
+#define NUBERT_COMMAND_LEN 10 // read 10 bits
+#define NUBERT_COMPLETE_DATA_LEN 10 // complete length
+#define NUBERT_STOP_BIT 1 // has stop bit
+#define NUBERT_LSB 0 // MSB?
+#define NUBERT_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * FAN: (ventilator)
+ *
+ * Similar to NUBERT, but
+ * - has data bit instead of stop bit
+ * - has NO frame repetition
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define FAN_START_BIT_PULSE_TIME 1280.0e-6 // 1280 usec pulse
+#define FAN_START_BIT_PAUSE_TIME 380.0e-6 // 380 usec pause
+#define FAN_1_PULSE_TIME 1280.0e-6 // 1280 usec pulse
+#define FAN_1_PAUSE_TIME 380.0e-6 // 380 usec pause
+#define FAN_0_PULSE_TIME 380.0e-6 // 380 usec pulse
+#define FAN_0_PAUSE_TIME 1280.0e-6 // 1280 usec pause
+#define FAN_FRAMES 1 // FAN sends only 1 frame (NUBERT sends 2)
+#define FAN_AUTO_REPETITION_PAUSE_TIME 6.6e-3 // auto repetition after 6.6ms
+#define FAN_FRAME_REPEAT_PAUSE_TIME 6.6e-3 // frame repeat after 6.6ms
+#define FAN_ADDRESS_OFFSET 0 // skip 0 bits
+#define FAN_ADDRESS_LEN 0 // read 0 address bits
+#define FAN_COMMAND_OFFSET 0 // skip 0 bits
+#define FAN_COMMAND_LEN 11 // read 10 bits
+#define FAN_COMPLETE_DATA_LEN 11 // complete length
+#define FAN_STOP_BIT 0 // has NO stop bit (fm: this seems to be wrong)
+#define FAN_LSB 0 // MSB
+#define FAN_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SPEAKER:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SPEAKER_START_BIT_PULSE_TIME 440.0e-6 // 440 usec pulse
+#define SPEAKER_START_BIT_PAUSE_TIME 1250.0e-6 // 1250 usec pause
+#define SPEAKER_1_PULSE_TIME 1250.0e-6 // 1250 usec pulse
+#define SPEAKER_1_PAUSE_TIME 440.0e-6 // 440 usec pause
+#define SPEAKER_0_PULSE_TIME 440.0e-6 // 440 usec pulse
+#define SPEAKER_0_PAUSE_TIME 1250.0e-6 // 1250 usec pause
+#define SPEAKER_FRAMES 2 // SPEAKER sends 2 frames
+#define SPEAKER_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
+#define SPEAKER_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 45ms
+#define SPEAKER_ADDRESS_OFFSET 0 // skip 0 bits
+#define SPEAKER_ADDRESS_LEN 0 // read 0 address bits
+#define SPEAKER_COMMAND_OFFSET 0 // skip 0 bits
+#define SPEAKER_COMMAND_LEN 10 // read 10 bits
+#define SPEAKER_COMPLETE_DATA_LEN 10 // complete length
+#define SPEAKER_STOP_BIT 1 // has stop bit
+#define SPEAKER_LSB 0 // MSB?
+#define SPEAKER_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * BANG_OLUFSEN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define BANG_OLUFSEN_START_BIT1_PULSE_TIME 200.0e-6 // 200 usec pulse
+#define BANG_OLUFSEN_START_BIT1_PAUSE_TIME 3125.0e-6 // 3125 usec pause
+#define BANG_OLUFSEN_START_BIT2_PULSE_TIME 200.0e-6 // 200 usec pulse
+#define BANG_OLUFSEN_START_BIT2_PAUSE_TIME 3125.0e-6 // 3125 usec pause
+#define BANG_OLUFSEN_START_BIT3_PULSE_TIME 200.0e-6 // 200 usec pulse
+#define BANG_OLUFSEN_START_BIT3_PAUSE_TIME 15625.0e-6 // 15625 usec pause
+#define BANG_OLUFSEN_START_BIT4_PULSE_TIME 200.0e-6 // 200 usec pulse
+#define BANG_OLUFSEN_START_BIT4_PAUSE_TIME 3125.0e-6 // 3125 usec pause
+#define BANG_OLUFSEN_PULSE_TIME 200.0e-6 // 200 usec pulse
+#define BANG_OLUFSEN_1_PAUSE_TIME 9375.0e-6 // 9375 usec pause
+#define BANG_OLUFSEN_0_PAUSE_TIME 3125.0e-6 // 3125 usec pause
+#define BANG_OLUFSEN_R_PAUSE_TIME 6250.0e-6 // 6250 usec pause (repeat last bit)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME 12500.0e-6 // 12500 usec pause (trailer bit)
+#define BANG_OLUFSEN_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define BANG_OLUFSEN_ADDRESS_OFFSET 0 // no address bits
+#define BANG_OLUFSEN_ADDRESS_LEN 0 // no address bits
+#define BANG_OLUFSEN_COMMAND_OFFSET 3 // skip startbits 2, 3, 4
+#define BANG_OLUFSEN_COMMAND_LEN 16 // read 16 command bits
+#define BANG_OLUFSEN_COMPLETE_DATA_LEN 20 // complete length: startbits 2, 3, 4 + 16 data bits + trailer bit
+#define BANG_OLUFSEN_STOP_BIT 1 // has stop bit
+#define BANG_OLUFSEN_LSB 0 // MSB...LSB
+#define BANG_OLUFSEN_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * GRUNDIG & NOKIA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define GRUNDIG_NOKIA_IR60_BIT_TIME 528.0e-6 // 528 usec pulse/pause
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME 2639.0e-6 // 2639 usec pause after pre bit
+#define GRUNDIG_NOKIA_IR60_FRAME_REPEAT_PAUSE_TIME 117.76e-3 // info frame repeat after 117.76 ms
+#define GRUNDIG_NOKIA_IR60_STOP_BIT 0 // has no stop bit
+#define GRUNDIG_NOKIA_IR60_LSB 1 // MSB...LSB
+#define GRUNDIG_NOKIA_IR60_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
+
+#define GRUNDIG_FRAMES 2 // GRUNDIG sends each frame 1+1 times
+#define GRUNDIG_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms
+#define GRUNDIG_ADDRESS_OFFSET 0 // no address
+#define GRUNDIG_ADDRESS_LEN 0 // no address
+#define GRUNDIG_COMMAND_OFFSET 1 // skip 1 start bit
+#define GRUNDIG_COMMAND_LEN 9 // read 9 command bits
+#define GRUNDIG_COMPLETE_DATA_LEN 10 // complete length: 1 start bit + 9 data bits
+
+#define NOKIA_FRAMES 3 // NOKIA sends each frame 1 + 1 + 1 times
+#define NOKIA_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms
+#define NOKIA_ADDRESS_OFFSET 9 // skip 9 bits (1 start bit + 8 data bits)
+#define NOKIA_ADDRESS_LEN 8 // 7 address bits
+#define NOKIA_COMMAND_OFFSET 1 // skip 1 bit (1 start bit)
+#define NOKIA_COMMAND_LEN 8 // read 8 command bits
+#define NOKIA_COMPLETE_DATA_LEN 17 // complete length: 1 start bit + 8 address bits + 8 command bits
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IR60:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IR60_FRAMES 2 // IR60 sends each frame 1+1 times
+#define IR60_AUTO_REPETITION_PAUSE_TIME 22.2e-3 // repetition after 22.2ms
+#define IR60_TIMEOUT_TIME 5000.0e-6 // timeout grundig frame, switch to IR60
+#define IR60_ADDRESS_OFFSET 0 // skip 1 bits
+#define IR60_ADDRESS_LEN 0 // read 0 address bits
+#define IR60_COMMAND_OFFSET 0 // skip 1 bit (start bit after pre bit, always 1)
+#define IR60_COMMAND_LEN 7 // read 6 command bits
+#define IR60_COMPLETE_DATA_LEN 7 // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SIEMENS & RUWIDO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#if 0
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME 275.0e-6 // 275 usec pulse
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME 550.0e-6 // 550 usec pause
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME 275.0e-6 // 275 usec short pulse
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2 550.0e-6 // 550 usec long pulse
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME 275.0e-6 // 275 usec short pause
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2 550.0e-6 // 550 usec long pause
+#else
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME 370.0e-6 // 370 usec pulse
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME 550.0e-6 // 550 usec pause
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME 370.0e-6 // 370 usec short pulse
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2 680.0e-6 // 680 usec long pulse
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME 275.0e-6 // 275 usec short pause
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2 550.0e-6 // 550 usec long pause
+#endif
+
+#define SIEMENS_OR_RUWIDO_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define SIEMENS_OR_RUWIDO_STOP_BIT 0 // has no stop bit
+#define SIEMENS_OR_RUWIDO_LSB 0 // MSB...LSB
+#define SIEMENS_OR_RUWIDO_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
+
+#define RUWIDO_ADDRESS_OFFSET 0 // skip 0 bits
+#define RUWIDO_ADDRESS_LEN 9 // read 9 address bits
+#define RUWIDO_COMMAND_OFFSET 9 // skip 9 bits
+#define RUWIDO_COMMAND_LEN 8 // read 7 + 1 command bits, last bit is only check bit
+#define RUWIDO_COMPLETE_DATA_LEN 17 // complete length
+
+#define SIEMENS_ADDRESS_OFFSET 0 // skip 0 bits
+#define SIEMENS_ADDRESS_LEN 11 // read 11 bits
+#define SIEMENS_COMMAND_OFFSET 11 // skip 11 bits
+#define SIEMENS_COMMAND_LEN 11 // read 10 + 1 command bits, last bit is only check bit
+#define SIEMENS_COMPLETE_DATA_LEN 22 // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * FDC:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define FDC_START_BIT_PULSE_TIME 2085.0e-6 // 2085 usec pulse
+#define FDC_START_BIT_PAUSE_TIME 966.0e-6 // 966 usec pause
+#define FDC_PULSE_TIME 300.0e-6 // 300 usec pulse
+#define FDC_1_PAUSE_TIME 715.0e-6 // 715 usec pause
+#define FDC_0_PAUSE_TIME 220.0e-6 // 220 usec pause
+#define FDC_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms
+#define FDC_ADDRESS_OFFSET 0 // skip 0 bits
+#define FDC_ADDRESS_LEN 14 // read 14 address bits, but use only 6, shift 8 into command
+#define FDC_COMMAND_OFFSET 20 // skip 20 bits
+#define FDC_COMMAND_LEN 12 // read 12 bits
+#define FDC_COMPLETE_DATA_LEN 40 // complete length
+#define FDC_STOP_BIT 1 // has stop bit
+#define FDC_LSB 1 // LSB...MSB
+#define FDC_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RCCAR:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCCAR_START_BIT_PULSE_TIME 2000.0e-6 // 2000 usec pulse
+#define RCCAR_START_BIT_PAUSE_TIME 2000.0e-6 // 2000 usec pause
+#define RCCAR_PULSE_TIME 600.0e-6 // 360 usec pulse
+#define RCCAR_1_PAUSE_TIME 450.0e-6 // 650 usec pause
+#define RCCAR_0_PAUSE_TIME 900.0e-6 // 180 usec pause
+#define RCCAR_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define RCCAR_ADDRESS_OFFSET 0 // skip 0 bits
+#define RCCAR_ADDRESS_LEN 0 // read 0 address bits
+#define RCCAR_COMMAND_OFFSET 0 // skip 0 bits
+#define RCCAR_COMMAND_LEN 13 // read 13 bits
+#define RCCAR_COMPLETE_DATA_LEN 13 // complete length
+#define RCCAR_STOP_BIT 1 // has stop bit
+#define RCCAR_LSB 1 // LSB...MSB
+#define RCCAR_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * JVC:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define JVC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse
+#define JVC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
+#define JVC_PULSE_TIME 560.0e-6 // 560 usec pulse
+#define JVC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause
+#define JVC_0_PAUSE_TIME 560.0e-6 // 560 usec pause
+#define JVC_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after 22ms
+#define JVC_ADDRESS_OFFSET 0 // skip 0 bits
+#define JVC_ADDRESS_LEN 4 // read 4 address bits
+#define JVC_COMMAND_OFFSET 4 // skip 4 bits
+#define JVC_COMMAND_LEN 12 // read 12 bits
+#define JVC_COMPLETE_DATA_LEN 16 // complete length
+#define JVC_STOP_BIT 1 // has stop bit
+#define JVC_LSB 1 // LSB...MSB
+#define JVC_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NIKON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NIKON_START_BIT_PULSE_TIME 2200.0e-6 // 2200 usec pulse
+#define NIKON_START_BIT_PAUSE_TIME 27100.0e-6 // 27100 usec pause
+#define NIKON_PULSE_TIME 500.0e-6 // 500 usec pulse
+#define NIKON_1_PAUSE_TIME 3500.0e-6 // 3500 usec pause
+#define NIKON_0_PAUSE_TIME 1500.0e-6 // 1500 usec pause
+#define NIKON_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms
+#define NIKON_ADDRESS_OFFSET 0 // skip 0 bits
+#define NIKON_ADDRESS_LEN 0 // read 0 address bits
+#define NIKON_COMMAND_OFFSET 0 // skip 0 bits
+#define NIKON_COMMAND_LEN 2 // read 2 bits
+#define NIKON_COMPLETE_DATA_LEN 2 // complete length
+#define NIKON_STOP_BIT 1 // has stop bit
+#define NIKON_LSB 0 // LSB...MSB
+#define NIKON_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * KATHREIN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define KATHREIN_START_BIT_PULSE_TIME 210.0e-6 // 1340 usec pulse
+#define KATHREIN_START_BIT_PAUSE_TIME 6218.0e-6 // 340 usec pause
+#define KATHREIN_1_PULSE_TIME 210.0e-6 // 1340 usec pulse
+#define KATHREIN_1_PAUSE_TIME 3000.0e-6 // 340 usec pause
+#define KATHREIN_0_PULSE_TIME 210.0e-6 // 500 usec pulse
+#define KATHREIN_0_PAUSE_TIME 1400.0e-6 // 1300 usec pause
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_TIME 4600.0e-6 // 4600 usec sync (on 6th and/or 8th bit)
+#define KATHREIN_FRAMES 1 // Kathrein sends 1 frame
+#define KATHREIN_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
+#define KATHREIN_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms
+#define KATHREIN_ADDRESS_OFFSET 1 // skip 1 bits
+#define KATHREIN_ADDRESS_LEN 4 // read 4 address bits
+#define KATHREIN_COMMAND_OFFSET 5 // skip 5 bits
+#define KATHREIN_COMMAND_LEN 7 // read 7 bits
+#define KATHREIN_COMPLETE_DATA_LEN 13 // complete length
+#define KATHREIN_STOP_BIT 1 // has stop bit
+#define KATHREIN_LSB 0 // MSB
+#define KATHREIN_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NETBOX:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NETBOX_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse
+#define NETBOX_START_BIT_PAUSE_TIME 800.0e-6 // 800 usec pause
+#define NETBOX_PULSE_TIME 800.0e-6 // 800 usec pulse
+#define NETBOX_PAUSE_TIME 800.0e-6 // 800 usec pause
+#define NETBOX_FRAMES 1 // Netbox sends 1 frame
+#define NETBOX_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
+#define NETBOX_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms
+#define NETBOX_ADDRESS_OFFSET 0 // skip 0 bits
+#define NETBOX_ADDRESS_LEN 3 // read 3 address bits
+#define NETBOX_COMMAND_OFFSET 3 // skip 3 bits
+#define NETBOX_COMMAND_LEN 13 // read 13 bits
+#define NETBOX_COMPLETE_DATA_LEN 16 // complete length
+#define NETBOX_STOP_BIT 0 // has no stop bit
+#define NETBOX_LSB 1 // LSB
+#define NETBOX_FLAGS IRMP_PARAM_FLAG_IS_SERIAL // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * LEGO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define LEGO_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz)
+#define LEGO_START_BIT_PAUSE_TIME 1026.0e-6 // 1026 usec pause (39 x 1/38kHz)
+#define LEGO_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz)
+#define LEGO_1_PAUSE_TIME 553.0e-6 // 553 usec pause (21 x 1/38kHz)
+#define LEGO_0_PAUSE_TIME 263.0e-6 // 263 usec pause (10 x 1/38kHz)
+#define LEGO_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define LEGO_ADDRESS_OFFSET 0 // skip 0 bits
+#define LEGO_ADDRESS_LEN 0 // read 0 address bits
+#define LEGO_COMMAND_OFFSET 0 // skip 0 bits
+#define LEGO_COMMAND_LEN 16 // read 16 bits (12 command + 4 CRC)
+#define LEGO_COMPLETE_DATA_LEN 16 // complete length
+#define LEGO_STOP_BIT 1 // has stop bit
+#define LEGO_LSB 0 // MSB...LSB
+#define LEGO_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * THOMSON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define THOMSON_PULSE_TIME 550.0e-6 // 550 usec pulse
+#define THOMSON_1_PAUSE_TIME 4500.0e-6 // 4500 usec pause
+#define THOMSON_0_PAUSE_TIME 2000.0e-6 // 2000 usec pause
+#define THOMSON_FRAMES 1 // THOMSON sends 1 frame
+#define THOMSON_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // repetition after 35ms
+#define THOMSON_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms
+#define THOMSON_ADDRESS_OFFSET 0 // skip 0 bits
+#define THOMSON_ADDRESS_LEN 4 // read 4 address bits
+#define THOMSON_COMMAND_OFFSET 5 // skip 4 address bits + 1 toggle bit
+#define THOMSON_COMMAND_LEN 7 // read 7 command bits
+#define THOMSON_COMPLETE_DATA_LEN 12 // complete length
+#define THOMSON_STOP_BIT 1 // has stop bit
+#define THOMSON_LSB 0 // MSB...LSB
+#define THOMSON_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * BOSE:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define BOSE_START_BIT_PULSE_TIME 1060.0e-6 // 1060 usec pulse
+#define BOSE_START_BIT_PAUSE_TIME 1425.0e-6 // 1425 usec pause
+#define BOSE_PULSE_TIME 550.0e-6 // 550 usec pulse
+#define BOSE_1_PAUSE_TIME 1425.0e-6 // 1425 usec pause
+#define BOSE_0_PAUSE_TIME 437.0e-6 // 437 usec pause
+#define BOSE_FRAMES 1
+#define BOSE_AUTO_REPETITION_PAUSE_TIME 40.0e-3 // repetition after 40ms?
+#define BOSE_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms?
+#define BOSE_ADDRESS_OFFSET 0 // skip 0 bits
+#define BOSE_ADDRESS_LEN 0 // read 16 address bits
+#define BOSE_COMMAND_OFFSET 0 // skip 16 bits (8 address + 8 /address)
+#define BOSE_COMMAND_LEN 16 // read 16 bits (8 command + 8 /command)
+#define BOSE_COMPLETE_DATA_LEN 16 // complete length
+#define BOSE_STOP_BIT 1 // has stop bit
+#define BOSE_LSB 1 // LSB...MSB
+#define BOSE_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * A1TVBOX:
+ * In reality A1 TV Box has no start bit with 300/340 usec. There are 2 start bits "10" with 250us pulse + 150us pause + 150us pause + 250us pulse
+ * This is not very easy to detect, because 1st and 2nd pause of both start bits are closely spaced.
+ * So IRMP looks for pseudo start bit with 300/340 usec and ignores the second half of the 2nd bit (250us pulse)
+ * This method only works because the first data bit (which is the 3rd bit) following is always "1":
+ * IRMP treats the first "long" pulse (250us of 2nd start bit + 250us of 1st data bit) of this "1" as a first _short_ pulse.
+ * This is a bug in IRMP's manchester decoder, but a good feature here ;-)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define A1TVBOX_START_BIT_PULSE_TIME 300.0e-6 // 300 usec pulse
+#define A1TVBOX_START_BIT_PAUSE_TIME 340.0e-6 // 340 usec pause
+#define A1TVBOX_BIT_PULSE_TIME 250.0e-6 // 250 usec pulse
+#define A1TVBOX_BIT_PAUSE_TIME 150.0e-6 // 150 usec pulse
+#define A1TVBOX_STOP_BIT 0 // has no stop bit
+#define A1TVBOX_LSB 0 // MSB...LSB
+#define A1TVBOX_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1 ) // flags
+#define A1TVBOX_FRAMES 1 // A1TVBOX sends each frame 1 times
+#define A1TVBOX_ADDRESS_OFFSET 1 // skip 1 bits
+#define A1TVBOX_ADDRESS_LEN 8 // read 8 address bits
+#define A1TVBOX_COMMAND_OFFSET 9 // skip 9 bits (start bit + address)
+#define A1TVBOX_COMMAND_LEN 8 // read 8 command bits
+#define A1TVBOX_COMPLETE_DATA_LEN 17 // complete length incl. start bit
+#define A1TVBOX_FRAME_REPEAT_PAUSE_TIME 50.0e-3 // 50 msec pause between frames, don't know if it is correct
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MERLIN:
+ * See notes for A1TVBOX
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MERLIN_START_BIT_PULSE_TIME 210.0e-6 // 210 usec pulse
+#define MERLIN_START_BIT_PAUSE_TIME 420.0e-6 // 429 usec pause
+#define MERLIN_BIT_PULSE_TIME 210.0e-6 // 210 usec pulse
+#define MERLIN_BIT_PAUSE_TIME 210.0e-6 // 210 usec pulse
+#define MERLIN_STOP_BIT 0 // has no stop bit
+#define MERLIN_LSB 0 // MSB...LSB
+#define MERLIN_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1 ) // flags
+#define MERLIN_FRAMES 1 // MERLIN sends each frame 1 times
+#define MERLIN_ADDRESS_OFFSET 2 // skip 1 bits
+#define MERLIN_ADDRESS_LEN 9 // read 9 address bits
+#define MERLIN_COMMAND_OFFSET 11 // skip 11 bits (start bit + address)
+#define MERLIN_COMMAND_LEN 32 // read up to 32 command bits
+#define MERLIN_COMPLETE_DATA_LEN 45 // complete length incl. start bit
+#define MERLIN_FRAME_REPEAT_PAUSE_TIME 50.0e-3 // 50 msec pause between frames, don't know if it is correct
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ORTEK (Hama): 6 address bits + 2 frame type bits + 6 command bits + 1 parity bit + 1 unknown bit + "1" + "0"
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ORTEK_START_BIT_PULSE_TIME 2000.0e-6 // 2000 usec pulse
+#define ORTEK_START_BIT_PAUSE_TIME 1000.0e-6 // 1000 usec pause
+#define ORTEK_BIT_TIME 500.0e-6 // 500 usec pulse/pause
+#define ORTEK_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
+#define ORTEK_ADDRESS_OFFSET 0 // skip 0 bits
+#define ORTEK_ADDRESS_LEN 8 // read 6 address bits + 2 special bits
+#define ORTEK_COMMAND_OFFSET 8 // skip 6 address bits + 2 special bits
+#define ORTEK_COMMAND_LEN 6 // read 6 command bits
+#define ORTEK_COMPLETE_DATA_LEN 18 // complete length
+#define ORTEK_STOP_BIT 0 // has no stop bit
+#define ORTEK_LSB 0 // MSB...LSB
+#define ORTEK_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * TELEFUNKEN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define TELEFUNKEN_START_BIT_PULSE_TIME 600.0e-6 // 600 usec pulse
+#define TELEFUNKEN_START_BIT_PAUSE_TIME 1500.0e-6 // 1500 usec pause
+#define TELEFUNKEN_PULSE_TIME 600.0e-6 // 600 usec pulse
+#define TELEFUNKEN_1_PAUSE_TIME 1500.0e-6 // 1500 usec pause
+#define TELEFUNKEN_0_PAUSE_TIME 600.0e-6 // 600 usec pause
+#define TELEFUNKEN_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after XX ms ?????
+#define TELEFUNKEN_ADDRESS_OFFSET 0 // skip 0 bits
+#define TELEFUNKEN_ADDRESS_LEN 0 // read 0 address bits
+#define TELEFUNKEN_COMMAND_OFFSET 0 // skip 0 bits
+#define TELEFUNKEN_COMMAND_LEN 15 // read 15 bits
+#define TELEFUNKEN_COMPLETE_DATA_LEN 15 // complete length
+#define TELEFUNKEN_STOP_BIT 1 // has stop bit
+#define TELEFUNKEN_LSB 0 // LSB...MSB
+#define TELEFUNKEN_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ROOMBA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ROOMBA_START_BIT_PULSE_TIME 2790.0e-6 // 2790 usec pulse
+#define ROOMBA_START_BIT_PAUSE_TIME 930.0e-6 // 930 usec pause
+#define ROOMBA_0_PULSE_TIME 930.0e-6 // 930 usec pulse
+#define ROOMBA_1_PULSE_TIME 2790.0e-6 // 2790 usec pulse
+#define ROOMBA_0_PAUSE_TIME 2790.0e-6 // 2790 usec pause
+#define ROOMBA_1_PAUSE_TIME 930.0e-6 // 930 usec pause
+#define ROOMBA_FRAME_REPEAT_PAUSE_TIME 18.0e-3 // frame repeat after 18ms
+#define ROOMBA_ADDRESS_OFFSET 0 // skip 0 bits
+#define ROOMBA_ADDRESS_LEN 0 // read 0 address bits
+#define ROOMBA_COMMAND_OFFSET 0 // skip 0 bits
+#define ROOMBA_COMMAND_LEN 7 // read 7 bits
+#define ROOMBA_COMPLETE_DATA_LEN 7 // complete length
+#define ROOMBA_STOP_BIT 0 // has stop bit (fm: sure?)
+#define ROOMBA_LSB 0 // MSB...LSB
+#define ROOMBA_FLAGS 0 // flags
+#define ROOMBA_FRAMES 8 // ROOMBA sends 8 frames (this is a lie, but more comfortable)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC-MM (32, 24, or 12 bit)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCMM32_START_BIT_PULSE_TIME 500.0e-6 // 500 usec pulse
+#define RCMM32_START_BIT_PAUSE_TIME 220.0e-6 // 220 usec pause
+#define RCMM32_PULSE_TIME 230.0e-6 // 230 usec pulse
+#define RCMM32_00_PAUSE_TIME 220.0e-6 // 220 usec pause
+#define RCMM32_01_PAUSE_TIME 370.0e-6 // 370 usec pause
+#define RCMM32_10_PAUSE_TIME 540.0e-6 // 540 usec pause
+#define RCMM32_11_PAUSE_TIME 720.0e-6 // 720 usec pause
+
+#define RCMM32_FRAME_REPEAT_PAUSE_TIME 80.0e-3 // frame repeat after 80 ms
+#define RCMM32_ADDRESS_OFFSET 0 // skip 0 bits
+#define RCMM32_ADDRESS_LEN 16 // read 16 address bits
+#define RCMM32_COMMAND_OFFSET 17 // skip 17 bits
+#define RCMM32_COMMAND_LEN 15 // read 15 bits
+#define RCMM32_COMPLETE_DATA_LEN 32 // complete length
+#define RCMM32_STOP_BIT 1 // has stop bit
+#define RCMM32_LSB 0 // LSB...MSB
+#define RCMM32_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * PENTAX:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define PENTAX_START_BIT_PULSE_TIME 13000.0e-6 // 13 msec pulse
+#define PENTAX_START_BIT_PAUSE_TIME 3000.0e-6 // 3 msec pause
+#define PENTAX_PULSE_TIME 1000.0e-6 // 1 msec pulse
+#define PENTAX_1_PAUSE_TIME 3000.0e-6 // 3 msec pause
+#define PENTAX_0_PAUSE_TIME 1000.0e-6 // 1 msec pause
+#define PENTAX_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms
+#define PENTAX_ADDRESS_OFFSET 0 // skip 0 bits
+#define PENTAX_ADDRESS_LEN 0 // read 0 address bits
+#define PENTAX_COMMAND_OFFSET 0 // skip 0 bits
+#define PENTAX_COMMAND_LEN 6 // read 6 bits
+#define PENTAX_COMPLETE_DATA_LEN 6 // complete length
+#define PENTAX_STOP_BIT 1 // has stop bit
+#define PENTAX_LSB 0 // LSB...MSB
+#define PENTAX_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ACP24: Stiebel Eltron ACP24 air conditioner
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ACP24_START_BIT_PULSE_TIME 390.0e-6 // 390 usec pulse
+#define ACP24_START_BIT_PAUSE_TIME 950.0e-6 // 950 usec pause
+#define ACP24_PULSE_TIME 390.0e-6 // 390 usec pulse
+#define ACP24_1_PAUSE_TIME 1300.0e-6 // 1300 usec pause
+#define ACP24_0_PAUSE_TIME 950.0e-6 // 950 usec pause
+#define ACP24_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after 22ms?
+#define ACP24_ADDRESS_OFFSET 0 // skip 0 bits
+#define ACP24_ADDRESS_LEN 0 // read 6 address bits
+#define ACP24_COMMAND_OFFSET 0 // skip 6 bits
+#define ACP24_COMMAND_LEN 0 // read 0 bits (70 bits will be read and compressed by special routine)
+#define ACP24_COMPLETE_DATA_LEN 70 // complete length
+#define ACP24_STOP_BIT 1 // has stop bit
+#define ACP24_LSB 0 // LSB...MSB
+#define ACP24_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IRMP16:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP16_START_BIT_PULSE_TIME 842.0e-6 // 842 usec pulse (32 x 1/38kHz)
+#define IRMP16_START_BIT_PAUSE_TIME 1052.0e-6 // 1052 usec pause (40 x 1/38kHz)
+#define IRMP16_PULSE_TIME 421.0e-6 // 421 usec pulse (16 x 1/38kHz)
+#define IRMP16_1_PAUSE_TIME 842.0e-6 // 842 usec pause (32 x 1/38kHz)
+#define IRMP16_0_PAUSE_TIME 421.0e-6 // 421 usec pause (16 x 1/38kHz)
+#define IRMP16_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define IRMP16_ADDRESS_OFFSET 0 // skip 0 bits
+#define IRMP16_ADDRESS_LEN 0 // read 0 address bits
+#define IRMP16_COMMAND_OFFSET 0 // skip 0 bits
+#define IRMP16_COMMAND_LEN 16 // read 16 bits (12 command + 4 CRC)
+#define IRMP16_COMPLETE_DATA_LEN 16 // complete length
+#define IRMP16_STOP_BIT 1 // has stop bit
+#define IRMP16_LSB 1 // LSB...MSB
+#define IRMP16_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * GREE - climate:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define GREE_START_BIT_PULSE_TIME 12000.0e-6 // 12000 usec pulse (32 x 1/38kHz)
+#define GREE_START_BIT_PAUSE_TIME 6000.0e-6 // 6000 usec pause (40 x 1/38kHz)
+#define GREE_PULSE_TIME 900.0e-6 // 900 usec pulse (16 x 1/38kHz)
+#define GREE_1_PAUSE_TIME 700.0e-6 // 700 usec pause (32 x 1/38kHz)
+#define GREE_0_PAUSE_TIME 2100.0e-6 // 2100 usec pause (16 x 1/38kHz)
+#define GREE_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
+#define GREE_ADDRESS_OFFSET 0 // skip 0 bits
+#define GREE_ADDRESS_LEN 16 // read 16 address bits
+#define GREE_COMMAND_OFFSET 16 // skip 16 bits
+#define GREE_COMMAND_LEN 16 // read 16 bits
+#define GREE_COMPLETE_DATA_LEN 32 // complete length
+#define GREE_STOP_BIT 1 // has stop bit
+#define GREE_LSB 1 // LSB...MSB
+#define GREE_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * METZ:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define METZ_START_BIT_PULSE_TIME 870.0e-6 // 870 usec pulse
+#define METZ_START_BIT_PAUSE_TIME 2300.0e-6 // 2300 usec pause
+#define METZ_PULSE_TIME 435.0e-6 // 435 usec pulse
+#define METZ_1_PAUSE_TIME 1680.0e-6 // 1680 usec pause
+#define METZ_0_PAUSE_TIME 960.0e-6 // 960 usec pause
+#define METZ_FRAME_REPEAT_PAUSE_TIME 122.0e-3 // frame repeat after 122ms
+#define METZ_ADDRESS_OFFSET 1 // skip 1 bit (toggle bit)
+#define METZ_ADDRESS_LEN 6 // read 6 address bits
+#define METZ_COMMAND_OFFSET 7 // skip 7 bits
+#define METZ_COMMAND_LEN 13 // read 13 bits
+#define METZ_COMPLETE_DATA_LEN 20 // complete length
+#define METZ_STOP_BIT 0 // has no stop bit
+#define METZ_LSB 0 // MSB...LSB
+#define METZ_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RADIO1 - e.g. Tevion
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RADIO1_START_BIT_PULSE_TIME 3000.0e-6 // 3000 usec pulse
+#define RADIO1_START_BIT_PAUSE_TIME 7000.0e-6 // 7000 usec pulse
+#define RADIO1_0_PULSE_TIME 500.0e-6 // 500 usec pulse
+#define RADIO1_0_PAUSE_TIME 1000.0e-6 // 1000 usec pause
+#define RADIO1_1_PULSE_TIME 1000.0e-6 // 1000 usec pulse
+#define RADIO1_1_PAUSE_TIME 500.0e-6 // 500 usec pause
+
+#define RADIO1_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms
+#define RADIO1_ADDRESS_OFFSET 4 // skip 4 bits
+#define RADIO1_ADDRESS_LEN 16 // read 16 address bits
+#define RADIO1_COMMAND_OFFSET 20 // skip 4 + 16 bits
+#define RADIO1_COMMAND_LEN 3 // read 3 command bits
+#define RADIO1_COMPLETE_DATA_LEN 23 // complete length
+#define RADIO1_STOP_BIT 1 // has stop bit
+#define RADIO1_LSB 1 // LSB...MSB?
+#define RADIO1_FLAGS 0 // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Frame Repetitions:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define AUTO_FRAME_REPETITION_TIME 80.0e-3 // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms
+
+#endif // _IRMP_PROTOCOLS_H_
--- /dev/null
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpsystem.h - system specific includes and defines
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMPSYSTEM_H_
+#define _IRMPSYSTEM_H_
+
+#if !defined(_IRMP_H_) && !defined(_IRSND_H_)
+# error please include only irmp.h or irsnd.h, not irmpsystem.h
+#endif
+
+/* Workaround required when building with xtensa (cross-)toolchains. */
+#ifdef __xtensa__
+#undef __xtensa__
+#endif
+
+#if defined(__18CXX) // Microchip PIC C18 compiler
+# define PIC_C18
+#elif defined(__XC8) // PIC XC8 compiler
+# include <xc.h>
+# define PIC_C18
+#elif defined(__PCM__) || defined(__PCB__) || defined(__PCH__) // CCS PIC compiler
+# define PIC_CCS
+#elif defined(STM32L1XX_MD) || defined(STM32L1XX_MDP) || defined(STM32L1XX_HD) // ARM STM32
+# include <stm32l1xx.h>
+# define ARM_STM32
+# define ARM_STM32L1XX
+# define F_CPU (SysCtlClockGet())
+#elif defined(STM32F10X_LD) || defined(STM32F10X_LD_VL) \
+ || defined(STM32F10X_MD) || defined(STM32F10X_MD_VL) \
+ || defined(STM32F10X_HD) || defined(STM32F10X_HD_VL) \
+ || defined(STM32F10X_XL) || defined(STM32F10X_CL) // ARM STM32
+# include <stm32f10x.h>
+# define ARM_STM32
+# define ARM_STM32F10X
+# define F_CPU (SysCtlClockGet())
+#elif defined(STM32F4XX) // ARM STM32
+# include <stm32f4xx.h>
+# define ARM_STM32
+# define ARM_STM32F4XX
+#elif defined(USE_HAL_DRIVER) // ARM STM32 with HAL Library
+# include "gpio.h"
+# if defined(_IRSND_H_)
+# include"tim.h"
+# endif
+# define ARM_STM32_HAL
+# define F_CPU SystemCoreClock
+#elif defined(__SDCC_stm8) // STM8
+# define SDCC_STM8
+#elif defined(TARGET_IS_BLIZZARD_RA2) // TI Stellaris (tested on Stellaris Launchpad with Code Composer Studio)
+# define STELLARIS_ARM_CORTEX_M4
+# define F_CPU (SysCtlClockGet())
+#elif defined(__xtensa__) // ESP8266 (Arduino)
+# include "Arduino.h"
+# include "ets_sys.h"
+# include "osapi.h"
+# include "gpio.h"
+# include "os_type.h"
+# include "c_types.h"
+# define uint_fast8_t uint8_t
+# define uint_fast16_t uint16_t
+#elif defined(TEENSYDUINO) && (defined(__MK20DX256__) || defined(__MK20DX128__)) // Teensy 3.x (tested on Teensy 3.1 in Arduino 1.6.5 / Teensyduino 1.2.5)
+# include <core_pins.h>
+# define TEENSY_ARM_CORTEX_M4
+#elif defined(unix) || defined(WIN32) || defined(__APPLE__) // Unix/Linux or Windows or Apple
+# define UNIX_OR_WINDOWS
+#elif defined(__MBED__) // mbed platform
+// #include "mbed.h" // if mbed.h is used, source must be compiled as cpp
+#include "gpio_api.h"
+#elif defined(IRMP_CHIBIOS_HAL) // ChibiOS HAL
+# include "hal.h"
+#else
+# define ATMEL_AVR // ATMEL AVR
+#endif
+
+#include <string.h>
+
+#ifdef UNIX_OR_WINDOWS // Analyze on Unix/Linux or Windows
+# include <stdio.h>
+# include <stdlib.h>
+# define F_CPU 8000000L
+# define ANALYZE
+# ifdef unix
+# include <stdint.h>
+# else
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+# endif
+#endif
+
+
+#if defined(ATMEL_AVR)
+# include <stdint.h>
+# include <stdio.h>
+# include <avr/io.h>
+# include <util/delay.h>
+# include <avr/pgmspace.h>
+# include <avr/interrupt.h>
+# define IRSND_OC2 0 // OC2
+# define IRSND_OC2A 1 // OC2A
+# define IRSND_OC2B 2 // OC2B
+# define IRSND_OC0 3 // OC0
+# define IRSND_OC0A 4 // OC0A
+# define IRSND_OC0B 5 // OC0B
+
+# define IRSND_XMEGA_OC0A 0 // OC0A
+# define IRSND_XMEGA_OC0B 1 // OC0B
+# define IRSND_XMEGA_OC0C 2 // OC0C
+# define IRSND_XMEGA_OC0D 3 // OC0D
+# define IRSND_XMEGA_OC1A 4 // OC1A
+# define IRSND_XMEGA_OC1B 5 // OC1B
+
+#elif defined(STELLARIS_ARM_CORTEX_M4)
+
+# include "inc/hw_ints.h"
+# include "inc/hw_memmap.h"
+# include "inc/hw_types.h"
+# include "inc/hw_gpio.h"
+# include "driverlib/fpu.h"
+# include "driverlib/sysctl.h"
+# include "driverlib/interrupt.h"
+# include "driverlib/gpio.h"
+# include "driverlib/rom.h"
+# include "driverlib/systick.h"
+# include "driverlib/pin_map.h"
+# include "driverlib/timer.h"
+# define PROGMEM
+# define memcpy_P memcpy
+# define APP_SYSTICKS_PER_SEC 32
+
+#elif defined(ARM_STM32F10X)
+
+# include "stm32f10x_gpio.h"
+# include "stm32f10x_rcc.h"
+# include "stm32f10x_tim.h"
+# include "misc.h"
+# define PROGMEM
+# define memcpy_P memcpy
+
+#elif defined(SDCC_STM8)
+
+# include "stm8s.h"
+# define PROGMEM
+# define memcpy_P memcpy
+# define __attribute__(x)
+# define uint_fast8_t uint8_t
+# define uint_fast16_t uint16_t
+
+#elif defined(TEENSY_ARM_CORTEX_M4)
+# define PROGMEM
+# define memcpy_P memcpy
+
+#elif defined(__xtensa__)
+# define PROGMEM
+# define memcpy_P memcpy
+
+#elif defined(__MBED__)
+# define PROGMEM
+# define memcpy_P memcpy
+
+#else
+# define PROGMEM
+# define memcpy_P memcpy
+
+#endif
+
+#if defined(PIC_CCS) || defined(PIC_C18)
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned char uint_fast8_t;
+typedef unsigned short uint_fast16_t;
+#endif
+
+#if defined (PIC_C18) // PIC C18 or XC8 compiler
+# include <p18cxxx.h> // main PIC18 h file
+#ifndef __XC8
+# include <timers.h> // timer lib
+# include <pwm.h> // pwm lib
+#endif
+# define IRSND_PIC_CCP1 1 // PIC C18 RC2 = PWM1 module
+# define IRSND_PIC_CCP2 2 // PIC C18 RC1 = PWM2 module
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+#if IRMP_32_BIT == 1
+
+typedef struct
+{
+ uint8_t protocol; // protocol, e.g. NEC_PROTOCOL
+ uint16_t address; // address
+ uint32_t command; // command
+ uint8_t flags; // flags, e.g. repetition
+} IRMP_DATA;
+
+#else // not IRMP_32_BIT == 1
+
+#if defined(PIC_C18)
+#define IRMP_PACKED_STRUCT
+#else
+#define IRMP_PACKED_STRUCT __attribute__ ((__packed__))
+#endif
+
+typedef struct IRMP_PACKED_STRUCT
+{
+ uint8_t protocol; // protocol, e.g. NEC_PROTOCOL
+ uint16_t address; // address
+ uint16_t command; // command
+ uint8_t flags; // flags, e.g. repetition
+} IRMP_DATA;
+
+#endif // IRMP_32_BIT == 1
+
+#endif // _IRMPSYSTEM_H_
#include <Python.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
#include "libsigrokdecode.h"
+/*
+ * Static definition of tables ending with an all-zero sentinel entry
+ * may raise warnings when compiling with -Wmissing-field-initializers.
+ * GCC suppresses the warning only with { 0 }, clang wants { } instead.
+ */
+#ifdef __clang__
+# define ALL_ZERO { }
+#else
+# define ALL_ZERO { 0 }
+#endif
+
enum {
+ SRD_TERM_ALWAYS_FALSE,
SRD_TERM_HIGH,
SRD_TERM_LOW,
SRD_TERM_RISING_EDGE,
uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match);
+SRD_PRIV int srd_inst_flush(struct srd_decoder_inst *di);
+SRD_PRIV int srd_inst_send_eof(struct srd_decoder_inst *di);
SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di);
SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di);
SRD_PRIV void srd_inst_free_all(struct srd_session *sess);
SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr);
SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx, char **outstr);
SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, char **outstr);
-SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out);
+SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, int64_t *out);
SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr);
SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv);
SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj);
*/
/* Marks public libsigrokdecode API symbols. */
-#ifndef _WIN32
-#define SRD_API __attribute__((visibility("default")))
+#if defined _WIN32
+# if defined DLL_EXPORT
+# define SRD_API __declspec(dllexport)
+# else
+# define SRD_API extern
+# endif
#else
-#define SRD_API
+# define SRD_API __attribute__((visibility("default")))
#endif
/* Marks private, non-public libsigrokdecode symbols (not part of the API). */
-#ifndef _WIN32
-#define SRD_PRIV __attribute__((visibility("hidden")))
+#if defined _WIN32
+# define SRD_PRIV /* EMPTY */
#else
-#define SRD_PRIV
+# define SRD_PRIV __attribute__((visibility("hidden")))
#endif
/*
* When adding an output type, don't forget to...
- * - expose it to PDs in controller.c:PyInit_sigrokdecode()
- * - add a check in module_sigrokdecode.c:Decoder_put()
- * - add a debug string in type_decoder.c:OUTPUT_TYPES
+ * - expose it to PDs in module_sigrokdecode.c:PyInit_sigrokdecode()
+ * - add a check in type_decoder.c:Decoder_put()
+ * - add a debug string in type_decoder.c:output_type_name()
*/
enum srd_output_type {
SRD_OUTPUT_ANN,
SRD_OUTPUT_PYTHON,
SRD_OUTPUT_BINARY,
+ SRD_OUTPUT_LOGIC,
SRD_OUTPUT_META,
};
GSList *opt_channels;
/**
- * List of NULL-terminated char[], containing descriptions of the
- * supported annotation output.
+ * List of annotation classes. Each list item is a GSList itself, with
+ * two NUL-terminated strings: name and description.
*/
GSList *annotations;
GSList *annotation_rows;
/**
- * List of NULL-terminated char[], containing descriptions of the
- * supported binary output.
+ * List of binary classes. Each list item is a GSList itself, with
+ * two NUL-terminated strings: name and description.
*/
GSList *binary;
+ /**
+ * List of logic output channels (item: id, description).
+ */
+ GSList *logic_output_channels;
+
/** List of decoder options. */
GSList *options;
GSList *ann_classes;
};
+struct srd_decoder_logic_output_channel {
+ char *id;
+ char *desc;
+};
+
struct srd_decoder_inst {
struct srd_decoder *decoder;
struct srd_session *sess;
/** Requests termination of wait() and decode(). */
gboolean want_wait_terminate;
+ /** Requests that .wait() terminates a Python iteration. */
+ gboolean communicate_eof;
+
/** Indicates the current state of the decoder stack. */
int decoder_state;
void *data;
};
struct srd_proto_data_annotation {
- int ann_class;
+ int ann_class; /* Index into "struct srd_decoder"->annotations. */
char **ann_text;
};
struct srd_proto_data_binary {
- int bin_class;
+ int bin_class; /* Index into "struct srd_decoder"->binary. */
uint64_t size;
- const unsigned char *data;
+ const uint8_t *data;
+};
+struct srd_proto_data_logic {
+ int logic_group;
+ uint64_t repeat_count; /* Number of times the value in data was repeated. */
+ const uint8_t *data; /* Bitfield containing the states of the logic outputs */
};
typedef void (*srd_pd_output_callback)(struct srd_proto_data *pdata,
SRD_API int srd_session_send(struct srd_session *sess,
uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
+SRD_API int srd_session_send_eof(struct srd_session *sess);
SRD_API int srd_session_terminate_reset(struct srd_session *sess);
SRD_API int srd_session_destroy(struct srd_session *sess);
SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
goto err_out;
if (PyModule_AddIntConstant(mod, "OUTPUT_BINARY", SRD_OUTPUT_BINARY) < 0)
goto err_out;
+ if (PyModule_AddIntConstant(mod, "OUTPUT_LOGIC", SRD_OUTPUT_LOGIC) < 0)
+ goto err_out;
if (PyModule_AddIntConstant(mod, "OUTPUT_META", SRD_OUTPUT_META) < 0)
goto err_out;
/* Expose meta input symbols. */
return SRD_OK;
}
+/**
+ * Communicate the end of the stream of sample data to the session.
+ *
+ * @param[in] sess The session. Must not be NULL.
+ *
+ * @return SRD_OK upon success. A (negative) error code otherwise.
+ *
+ * @since 0.6.0
+ */
+SRD_API int srd_session_send_eof(struct srd_session *sess)
+{
+ GSList *d;
+ int ret;
+
+ if (!sess)
+ return SRD_ERR_ARG;
+
+ for (d = sess->di_list; d; d = d->next) {
+ ret = srd_inst_send_eof(d->data);
+ if (ret != SRD_OK)
+ return ret;
+ }
+
+ return SRD_OK;
+}
+
/**
* Terminate currently executing decoders in a session, reset internal state.
*
* @section sec_irc IRC
*
* You can find the sigrok developers in the
- * <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
- * IRC channel on Freenode.
+ * <a href="ircs://irc.libera.chat/#sigrok">\#sigrok</a>
+ * IRC channel on Libera.Chat.
*
* @section sec_website Website
*
}
/* Environment variable overrides everything, for debugging. */
+ /*
+ * TODO
+ * Is the comment still applicable and correct or up to date?
+ * This implementation adds paths which were specified by the
+ * env var. Which can shadow files in other locations, or can
+ * extend the set of available decoders. Which need not only
+ * serve for development, it is as beneficial to regular users.
+ * Without shadowing all other files still are found.
+ */
if ((env_path = g_getenv("SIGROKDECODE_DIR"))) {
if ((ret = srd_decoder_searchpath_add(env_path)) != SRD_OK) {
Py_Finalize();
return ret;
}
}
+ env_path = g_getenv("SIGROKDECODE_PATH");
+ if (env_path) {
+ char **dir_list, **dir_iter, *dir_item;
+ dir_list = g_strsplit(env_path, G_SEARCHPATH_SEPARATOR_S, 0);
+ for (dir_iter = dir_list; *dir_iter; dir_iter++) {
+ dir_item = *dir_iter;
+ if (!dir_item || !*dir_item)
+ continue;
+ ret = srd_decoder_searchpath_add(dir_item);
+ if (ret != SRD_OK) {
+ Py_Finalize();
+ return ret;
+ }
+ }
+ g_strfreev(dir_list);
+ }
- /* Initialize the Python GIL (this also happens to acquire it). */
+#if PY_VERSION_HEX < 0x03090000
+ /*
+ * Initialize and acquire the Python GIL. In Python 3.7+ this
+ * will be done implicitly as part of the Py_InitializeEx()
+ * call above. PyEval_InitThreads() was deprecated in 3.9.
+ */
PyEval_InitThreads();
+#endif
/* Release the GIL (ignore return value, we don't need it here). */
(void)PyEval_SaveThread();
* Check whether srd_decoder_doc_get() fails with NULL as argument.
* If it returns a value != NULL (or segfaults) this test will fail.
* See also: http://sigrok.org/bugzilla/show_bug.cgi?id=179
+ * Check whether srd_decoder_doc_get() fails with dec->py_mod == NULL.
+ * If it returns a value != NULL (or segfaults) this test will fail.
+ * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=180
*/
START_TEST(test_doc_get_null)
{
+ struct srd_decoder dec;
+
+ dec.py_mod = NULL;
+
srd_init(DECODERS_TESTDIR);
fail_unless(srd_decoder_doc_get(NULL) == NULL);
+ fail_unless(srd_decoder_doc_get(&dec) == NULL);
srd_exit();
}
END_TEST
/* This is only used for nicer srd_dbg() output. */
SRD_PRIV const char *output_type_name(unsigned int idx)
{
- static const char names[][16] = {
+ static const char *names[] = {
"OUTPUT_ANN",
"OUTPUT_PYTHON",
"OUTPUT_BINARY",
+ "OUTPUT_LOGIC",
"OUTPUT_META",
"(invalid)"
};
/* Should be a list of [annotation class, [string, ...]]. */
if (!PyList_Check(obj)) {
- srd_err("Protocol decoder %s submitted an annotation that"
- " is not a list", di->decoder->name);
+ srd_err("Protocol decoder %s submitted an annotation that is not a list",
+ di->decoder->name);
goto err;
}
/* Should have 2 elements. */
if (PyList_Size(obj) != 2) {
- srd_err("Protocol decoder %s submitted annotation list with "
- "%zd elements instead of 2", di->decoder->name,
- PyList_Size(obj));
+ ssize_t sz = PyList_Size(obj);
+ srd_err("Protocol decoder %s submitted annotation list with %zd elements instead of 2",
+ di->decoder->name, sz);
goto err;
}
*/
py_tmp = PyList_GetItem(obj, 0);
if (!PyLong_Check(py_tmp)) {
- srd_err("Protocol decoder %s submitted annotation list, but "
- "first element was not an integer.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted annotation list, but first element was not an integer.",
+ di->decoder->name);
goto err;
}
ann_class = PyLong_AsLong(py_tmp);
if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) {
- srd_err("Protocol decoder %s submitted data to unregistered "
- "annotation class %d.", di->decoder->name, ann_class);
+ srd_err("Protocol decoder %s submitted data to unregistered annotation class %d.",
+ di->decoder->name, ann_class);
goto err;
}
/* Second element must be a list. */
py_tmp = PyList_GetItem(obj, 1);
if (!PyList_Check(py_tmp)) {
- srd_err("Protocol decoder %s submitted annotation list, but "
- "second element was not a list.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted annotation list, but second element was not a list.",
+ di->decoder->name);
goto err;
}
if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) {
- srd_err("Protocol decoder %s submitted annotation list, but "
- "second element was malformed.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted annotation list, but second element was malformed.",
+ di->decoder->name);
goto err;
}
return SRD_ERR_PYTHON;
}
+static void release_logic(struct srd_proto_data_logic *pdl)
+{
+ if (!pdl)
+ return;
+ g_free((void *)pdl->data);
+}
+
+static int convert_logic(struct srd_decoder_inst *di, PyObject *obj,
+ struct srd_proto_data *pdata)
+{
+ struct srd_proto_data_logic *pdl;
+ PyObject *py_tmp;
+ Py_ssize_t size;
+ int logic_group;
+ char *group_name, *buf;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ /* Should be a list of [logic group, bytes]. */
+ if (!PyList_Check(obj)) {
+ srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_LOGIC.",
+ di->decoder->name);
+ goto err;
+ }
+
+ /* Should have 2 elements. */
+ if (PyList_Size(obj) != 2) {
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list "
+ "with %zd elements instead of 2", di->decoder->name,
+ PyList_Size(obj));
+ goto err;
+ }
+
+ /* The first element should be an integer. */
+ py_tmp = PyList_GetItem(obj, 0);
+ if (!PyLong_Check(py_tmp)) {
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, "
+ "but first element was not an integer.", di->decoder->name);
+ goto err;
+ }
+ logic_group = PyLong_AsLong(py_tmp);
+ if (!(group_name = g_slist_nth_data(di->decoder->logic_output_channels, logic_group))) {
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC with "
+ "unregistered logic group %d.", di->decoder->name, logic_group);
+ goto err;
+ }
+
+ /* Second element should be bytes. */
+ py_tmp = PyList_GetItem(obj, 1);
+ if (!PyBytes_Check(py_tmp)) {
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, "
+ "but second element was not bytes.", di->decoder->name);
+ goto err;
+ }
+
+ /* Consider an empty set of bytes a bug. */
+ if (PyBytes_Size(py_tmp) == 0) {
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC "
+ "with empty data set.", di->decoder->name);
+ goto err;
+ }
+
+ if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1)
+ goto err;
+
+ PyGILState_Release(gstate);
+
+ pdl = pdata->data;
+ pdl->logic_group = logic_group;
+ /* pdl->repeat_count is set by the caller as it depends on the sample range */
+ if (!(pdl->data = g_try_malloc(size)))
+ return SRD_ERR_MALLOC;
+ memcpy((void *)pdl->data, (const void *)buf, size);
+
+ return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
static void release_binary(struct srd_proto_data_binary *pdb)
{
if (!pdb)
/* Should have 2 elements. */
if (PyList_Size(obj) != 2) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list "
- "with %zd elements instead of 2", di->decoder->name,
- PyList_Size(obj));
+ ssize_t sz = PyList_Size(obj);
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list with %zd elements instead of 2",
+ di->decoder->name, sz);
goto err;
}
/* The first element should be an integer. */
py_tmp = PyList_GetItem(obj, 0);
if (!PyLong_Check(py_tmp)) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
- "but first element was not an integer.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, but first element was not an integer.",
+ di->decoder->name);
goto err;
}
bin_class = PyLong_AsLong(py_tmp);
if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
- "unregistered binary class %d.", di->decoder->name, bin_class);
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with unregistered binary class %d.",
+ di->decoder->name, bin_class);
goto err;
}
/* Second element should be bytes. */
py_tmp = PyList_GetItem(obj, 1);
if (!PyBytes_Check(py_tmp)) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
- "but second element was not bytes.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, but second element was not bytes.",
+ di->decoder->name);
goto err;
}
/* Consider an empty set of bytes a bug. */
if (PyBytes_Size(py_tmp) == 0) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY "
- "with empty data set.", di->decoder->name);
+ srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with empty data set.",
+ di->decoder->name);
goto err;
}
if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_INT64)) {
if (!PyLong_Check(obj)) {
- PyErr_Format(PyExc_TypeError, "This output was registered "
- "as 'int', but something else was passed.");
+ PyErr_Format(PyExc_TypeError,
+ "This output was registered as 'int', but something else was passed.");
goto err;
}
intvalue = PyLong_AsLongLong(obj);
pdata->data = g_variant_new_int64(intvalue);
} else if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_DOUBLE)) {
if (!PyFloat_Check(obj)) {
- PyErr_Format(PyExc_TypeError, "This output was registered "
- "as 'float', but something else was passed.");
+ PyErr_Format(PyExc_TypeError,
+ "This output was registered as 'float', but something else was passed.");
goto err;
}
dvalue = PyFloat_AsDouble(obj);
g_variant_unref(gvar);
}
+PyDoc_STRVAR(Decoder_put_doc,
+ "Put an annotation for the specified span of samples.\n"
+ "\n"
+ "Arguments: start and end sample number, stream id, annotation data.\n"
+ "Annotation data's layout depends on the output stream type."
+);
+
static PyObject *Decoder_put(PyObject *self, PyObject *args)
{
GSList *l;
struct srd_proto_data pdata;
struct srd_proto_data_annotation pda;
struct srd_proto_data_binary pdb;
+ struct srd_proto_data_logic pdl;
uint64_t start_sample, end_sample;
int output_id;
struct srd_pd_callback *cb;
start_sample,
end_sample, output_type_name(pdo->output_type),
output_id, pdo->proto_id, next_di->inst_id);
- if (!(py_res = PyObject_CallMethod(
- next_di->py_inst, "decode", "KKO", start_sample,
- end_sample, py_data))) {
+ py_res = PyObject_CallMethod(next_di->py_inst, "decode",
+ "KKO", start_sample, end_sample, py_data);
+ if (!py_res) {
srd_exception_catch("Calling %s decode() failed",
next_di->inst_id);
}
release_binary(pdata.data);
}
break;
+ case SRD_OUTPUT_LOGIC:
+ if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
+ pdata.data = &pdl;
+ /* Convert from PyDict to srd_proto_data_logic. */
+ if (convert_logic(di, py_data, &pdata) != SRD_OK) {
+ /* An error was already logged. */
+ break;
+ }
+ if (end_sample <= start_sample) {
+ srd_err("Ignored SRD_OUTPUT_LOGIC with invalid sample range.");
+ break;
+ }
+ pdl.repeat_count = (end_sample - start_sample) - 1;
+ Py_BEGIN_ALLOW_THREADS
+ cb->cb(&pdata, cb->cb_data);
+ Py_END_ALLOW_THREADS
+ release_logic(pdata.data);
+ }
+ break;
case SRD_OUTPUT_META:
if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
/* Annotations need converting from PyObject. */
return NULL;
}
-static PyObject *Decoder_register(PyObject *self, PyObject *args,
- PyObject *kwargs)
+PyDoc_STRVAR(Decoder_register_doc,
+ "Register a new output stream."
+);
+
+static PyObject *Decoder_register(PyObject *self,
+ PyObject *args, PyObject *kwargs)
{
struct srd_decoder_inst *di;
struct srd_pd_output *pdo;
/* A channelmap value of -1 means "unused optional channel". */
if (di->dec_channelmap[i] == -1) {
/* Value of unused channel is 0xff, instead of 0 or 1. */
- PyTuple_SetItem(py_pinvalues, i, PyLong_FromLong(0xff));
+ PyTuple_SetItem(py_pinvalues, i, PyLong_FromUnsignedLong(0xff));
} else {
sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
byte_offset = di->dec_channelmap[i] / 8;
bit_offset = di->dec_channelmap[i] % 8;
sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
- PyTuple_SetItem(py_pinvalues, i, PyLong_FromLong(sample));
+ PyTuple_SetItem(py_pinvalues, i, PyLong_FromUnsignedLong(sample));
}
}
*
* If there are no terms in the condition, 'term_list' will be NULL.
*
+ * @param di The decoder instance to use. Must not be NULL.
* @param py_dict A Python dict containing terms. Must not be NULL.
* @param term_list Pointer to a GSList which will be set to the newly
* created list of terms. Must not be NULL.
*
* @return SRD_OK upon success, a negative error code otherwise.
*/
-static int create_term_list(PyObject *py_dict, GSList **term_list)
+static int create_term_list(struct srd_decoder_inst *di,
+ PyObject *py_dict, GSList **term_list)
{
Py_ssize_t pos = 0;
PyObject *py_key, *py_value;
struct srd_term *term;
- uint64_t num_samples_to_skip;
+ int64_t num_samples_to_skip;
char *term_str;
PyGILState_STATE gstate;
/* Check whether the current key is a string or a number. */
if (PyLong_Check(py_key)) {
/* The key is a number. */
- /* TODO: Check if the number is a valid channel. */
/* Get the value string. */
if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) {
srd_err("Failed to get the value.");
term = g_malloc(sizeof(struct srd_term));
term->type = get_term_type(term_str);
term->channel = PyLong_AsLong(py_key);
+ if (term->channel < 0 || term->channel >= di->dec_num_channels)
+ term->type = SRD_TERM_ALWAYS_FALSE;
g_free(term_str);
} else if (PyUnicode_Check(py_key)) {
/* The key is a string. */
- /* TODO: Check if it's "skip". */
+ /* TODO: Check if the key is "skip". */
if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) {
srd_err("Failed to get number of samples to skip.");
goto err;
term->type = SRD_TERM_SKIP;
term->num_samples_to_skip = num_samples_to_skip;
term->num_samples_already_skipped = 0;
+ if (num_samples_to_skip < 0)
+ term->type = SRD_TERM_ALWAYS_FALSE;
} else {
srd_err("Term key is neither a string nor a number.");
goto err;
num_conditions = PyList_Size(py_conditionlist);
if (num_conditions == 0)
goto ret_9999; /* The PD invoked self.wait([]). */
- Py_IncRef(py_conditionlist);
+ Py_INCREF(py_conditionlist);
} else if (PyDict_Check(py_conds)) {
/* 'py_conds' is a dict. */
if (PyDict_Size(py_conds) == 0)
goto ret_9999; /* The PD invoked self.wait({}). */
/* Make a list and put the dict in there for convenience. */
py_conditionlist = PyList_New(1);
- Py_IncRef(py_conds);
+ Py_INCREF(py_conds);
PyList_SetItem(py_conditionlist, 0, py_conds);
num_conditions = 1;
} else {
}
/* Create the list of terms in this condition. */
- if ((ret = create_term_list(py_dict, &term_list)) < 0)
+ if ((ret = create_term_list(di, py_dict, &term_list)) < 0)
break;
/* Add the new condition to the PD instance's condition list. */
return SRD_OK;
}
+PyDoc_STRVAR(Decoder_wait_doc,
+ "Wait for one or more conditions to occur.\n"
+ "\n"
+ "Returns the sample data at the next position where the condition\n"
+ "is seen. When the optional condition is missing or empty, the next\n"
+ "sample number is used. The condition can be a dictionary with one\n"
+ "condition's details, or a list of dictionaries specifying multiple\n"
+ "conditions of which at least one condition must be true. Dicts can\n"
+ "contain one or more key/value pairs, all of which must be true for\n"
+ "the dict's condition to be considered true. The key either is a\n"
+ "channel index or a keyword, the value is the operation's parameter.\n"
+ "\n"
+ "Supported parameters for channel number keys: 'h', 'l', 'r', 'f',\n"
+ "or 'e' for level or edge conditions. Other supported keywords:\n"
+ "'skip' to advance over the given number of samples.\n"
+);
+
static PyObject *Decoder_wait(PyObject *self, PyObject *args)
{
int ret;
unsigned int i;
gboolean found_match;
struct srd_decoder_inst *di;
- PyObject *py_pinvalues, *py_matched;
+ PyObject *py_pinvalues, *py_matched, *py_samplenum;
PyGILState_STATE gstate;
if (!self || !args)
/* If there's a match, set self.samplenum etc. and return. */
if (found_match) {
/* Set self.samplenum to the (absolute) sample number that matched. */
- PyObject_SetAttrString(di->py_inst, "samplenum",
- PyLong_FromLong(di->abs_cur_samplenum));
+ py_samplenum = PyLong_FromUnsignedLongLong(di->abs_cur_samplenum);
+ PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+ Py_DECREF(py_samplenum);
if (di->match_array && di->match_array->len > 0) {
py_matched = PyTuple_New(di->match_array->len);
for (i = 0; i < di->match_array->len; i++)
PyTuple_SetItem(py_matched, i, PyBool_FromLong(di->match_array->data[i]));
PyObject_SetAttrString(di->py_inst, "matched", py_matched);
+ Py_DECREF(py_matched);
match_array_free(di);
} else {
PyObject_SetAttrString(di->py_inst, "matched", Py_None);
/* Signal the main thread that we handled all samples. */
g_cond_signal(&di->handled_all_samples_cond);
+ /*
+ * When EOF was provided externally, communicate the
+ * Python EOFError exception to .decode() and return
+ * from the .wait() method call. This is motivated by
+ * the use of Python context managers, so that .decode()
+ * methods can "close" incompletely accumulated data
+ * when the sample data is exhausted.
+ */
+ if (di->communicate_eof) {
+ /* Advance self.samplenum to the (absolute) last sample number. */
+ py_samplenum = PyLong_FromUnsignedLongLong(di->abs_cur_samplenum);
+ PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+ Py_DECREF(py_samplenum);
+ /* Raise an EOFError Python exception. */
+ srd_dbg("%s: %s: Raising EOF from wait().",
+ di->inst_id, __func__);
+ g_mutex_unlock(&di->data_mutex);
+ PyErr_SetString(PyExc_EOFError, "samples exhausted");
+ goto err;
+ }
+
/*
* When termination of wait() and decode() was requested,
* then exit the loop after releasing the mutex.
return NULL;
}
+PyDoc_STRVAR(Decoder_has_channel_doc,
+ "Check whether input data is supplied for a given channel.\n"
+ "\n"
+ "Argument: A channel index.\n"
+ "Returns: A boolean, True if the channel is connected,\n"
+ "False if the channel is open (won't see any input data).\n"
+);
+
/**
* Return whether the specified channel was supplied to the decoder.
*
int idx, count;
struct srd_decoder_inst *di;
PyGILState_STATE gstate;
+ PyObject *bool_ret;
if (!self || !args)
return NULL;
PyGILState_Release(gstate);
- return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+ bool_ret = (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+ Py_INCREF(bool_ret);
+ return bool_ret;
err:
PyGILState_Release(gstate);
return NULL;
}
+PyDoc_STRVAR(Decoder_doc, "sigrok Decoder base class");
+
static PyMethodDef Decoder_methods[] = {
- { "put", Decoder_put, METH_VARARGS,
- "Accepts a dictionary with the following keys: startsample, endsample, data" },
- { "register", (PyCFunction)Decoder_register, METH_VARARGS|METH_KEYWORDS,
- "Register a new output stream" },
- { "wait", Decoder_wait, METH_VARARGS,
- "Wait for one or more conditions to occur" },
- { "has_channel", Decoder_has_channel, METH_VARARGS,
- "Report whether a channel was supplied" },
- {NULL, NULL, 0, NULL}
+ { "put",
+ Decoder_put, METH_VARARGS,
+ Decoder_put_doc,
+ },
+ { "register",
+ (PyCFunction)(void(*)(void))Decoder_register, METH_VARARGS | METH_KEYWORDS,
+ Decoder_register_doc,
+ },
+ { "wait",
+ Decoder_wait, METH_VARARGS,
+ Decoder_wait_doc,
+ },
+ { "has_channel",
+ Decoder_has_channel, METH_VARARGS,
+ Decoder_has_channel_doc,
+ },
+ ALL_ZERO,
};
/**
{
PyType_Spec spec;
PyType_Slot slots[] = {
- { Py_tp_doc, "sigrok Decoder base class" },
+ { Py_tp_doc, Decoder_doc },
{ Py_tp_methods, Decoder_methods },
{ Py_tp_new, (void *)&PyType_GenericNew },
- { 0, NULL }
+ ALL_ZERO,
};
PyObject *py_obj;
PyGILState_STATE gstate;
SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **outstrlist)
{
PyObject *py_list;
- Py_ssize_t i;
+ ssize_t idx;
int ret;
char *outstr;
PyGILState_STATE gstate;
*outstrlist = NULL;
- for (i = 0; i < PyList_Size(py_list); i++) {
- ret = py_listitem_as_str(py_list, i, &outstr);
+ for (idx = 0; idx < PyList_Size(py_list); idx++) {
+ ret = py_listitem_as_str(py_list, idx, &outstr);
if (ret < 0) {
- srd_dbg("Couldn't get item %" PY_FORMAT_SIZE_T "d.", i);
+ srd_dbg("Couldn't get item %zd.", idx);
goto err;
}
*outstrlist = g_slist_append(*outstrlist, outstr);
SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx,
char **outstr)
{
- PyObject *py_value;
PyGILState_STATE gstate;
+ ssize_t item_idx;
+ PyObject *py_value;
gstate = PyGILState_Ensure();
goto err;
}
- if (!(py_value = PyList_GetItem(py_obj, idx))) {
- srd_dbg("Couldn't get list item %" PY_FORMAT_SIZE_T "d.", idx);
+ item_idx = idx;
+ if (!(py_value = PyList_GetItem(py_obj, item_idx))) {
+ srd_dbg("Couldn't get list item %zd.", item_idx);
goto err;
}
*
* @private
*/
-SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out)
+SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, int64_t *out)
{
PyObject *py_value;
PyGILState_STATE gstate;
goto err;
}
- *out = PyLong_AsUnsignedLongLong(py_value);
+ *out = PyLong_AsLongLong(py_value);
PyGILState_Release(gstate);
/**
* Convert a Python list of unicode strings to a C string vector.
- * On success, a pointer to a newly allocated NULL-terminated array of
+ * On success, a pointer to a newly allocated NUL-terminated array of
* allocated C strings is written to @a out_strv. The caller must g_free()
* each string and the array itself.
*