From: atoomnetmarc Date: Mon, 4 Mar 2024 19:36:46 +0000 (+0100) Subject: avr_isp: Add more parts X-Git-Url: https://sigrok.org/gitweb/?a=commitdiff_plain;h=HEAD;hp=b1c5d4db634f00e0f37b02d0a2982d22146d3827;p=libsigrokdecode.git avr_isp: Add more parts --- diff --git a/Makefile.am b/Makefile.am index 97e5228..32c6dd6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,21 @@ pkginclude_HEADERS = libsigrokdecode.h 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 diff --git a/README b/README index 784be27..301f235 100644 --- a/README +++ b/README @@ -91,7 +91,7 @@ Mailing list 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 diff --git a/configure.ac b/configure.ac index d59e0be..dc04166 100644 --- a/configure.ac +++ b/configure.ac @@ -40,9 +40,16 @@ AC_PROG_CC 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 @@ -93,7 +100,10 @@ SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary]) # 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], - [python-3.8-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]) + [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.])]) @@ -132,6 +142,17 @@ AC_SYS_LARGEFILE 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 ## ############################## @@ -141,6 +162,7 @@ AC_SUBST([SRD_PKGLIBS]) # 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` @@ -175,4 +197,6 @@ Detected libraries (required): $srd_pkglibs_summary Detected libraries (optional): $srd_pkglibs_opt_summary +Optional features: + - IRMP support library .......... $enable_irmp_so _EOF diff --git a/decoder.c b/decoder.c index fc6ae07..dd3bd5a 100644 --- a/decoder.c +++ b/decoder.c @@ -136,6 +136,18 @@ static void annotation_row_free(void *data) 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; @@ -187,7 +199,7 @@ static int get_channels(const struct srd_decoder *d, const char *attr, PyObject *py_channellist, *py_entry; struct srd_channel *pdch; GSList *pdchl; - ssize_t i; + ssize_t ch_idx; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -210,8 +222,9 @@ static int get_channels(const struct srd_decoder *d, const char *attr, 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; @@ -231,7 +244,7 @@ static int get_channels(const struct srd_decoder *d, const char *attr, 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); @@ -259,7 +272,7 @@ static int get_options(struct srd_decoder *d) GSList *options; struct srd_decoder_option *o; GVariant *gvar; - ssize_t opt, i; + ssize_t opt, val_idx; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -339,8 +352,9 @@ static int get_options(struct srd_decoder *d) 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; @@ -379,14 +393,17 @@ err_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")) { @@ -401,20 +418,22 @@ static int get_annotations(struct srd_decoder *dec) 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) @@ -440,43 +459,45 @@ err_out: } /* 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)); @@ -500,26 +521,30 @@ static int get_annotation_rows(struct srd_decoder *dec) 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)); @@ -549,7 +574,7 @@ static int get_binary_classes(struct srd_decoder *dec) 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(); @@ -571,8 +596,9 @@ static int get_binary_classes(struct srd_decoder *dec) 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; @@ -606,6 +632,79 @@ err_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) @@ -732,6 +831,7 @@ SRD_API int srd_decoder_load(const char *module_name) int is_subclass; const char *fail_txt; PyGILState_STATE gstate; + size_t ann_cls_count; if (!srd_check_init()) return SRD_ERR; @@ -874,12 +974,12 @@ SRD_API int srd_decoder_load(const char *module_name) 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; } @@ -944,6 +1044,11 @@ SRD_API int srd_decoder_load(const char *module_name) 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. */ diff --git a/decoders/ad5626/__init__.py b/decoders/ad5626/__init__.py new file mode 100644 index 0000000..5f67799 --- /dev/null +++ b/decoders/ad5626/__init__.py @@ -0,0 +1,25 @@ +## +## 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 . +## + +''' +This decoder stacks on top of the 'spi' PD and decodes the +Analog Devices AD5626 protocol. +''' + +from .pd import Decoder diff --git a/decoders/ad5626/pd.py b/decoders/ad5626/pd.py new file mode 100644 index 0000000..cffee83 --- /dev/null +++ b/decoders/ad5626/pd.py @@ -0,0 +1,62 @@ +## +## 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 . +## + +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 diff --git a/decoders/ad79x0/__init__.py b/decoders/ad79x0/__init__.py new file mode 100644 index 0000000..0e81f17 --- /dev/null +++ b/decoders/ad79x0/__init__.py @@ -0,0 +1,25 @@ +## +## 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 . +## + +''' +This decoder stacks on top of the 'spi' PD and decodes the +Analog Devices AD7910/AD7920 protocol. +''' + +from .pd import Decoder diff --git a/decoders/ad79x0/pd.py b/decoders/ad79x0/pd.py new file mode 100644 index 0000000..3d7ab73 --- /dev/null +++ b/decoders/ad79x0/pd.py @@ -0,0 +1,137 @@ +## +## 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 . +## + +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 diff --git a/decoders/adf435x/pd.py b/decoders/adf435x/pd.py index c60ed4e..ca61fbc 100644 --- a/decoders/adf435x/pd.py +++ b/decoders/adf435x/pd.py @@ -18,74 +18,102 @@ ## 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 @@ -100,9 +128,11 @@ class Decoder(srd.Decoder): annotations = ( # Sent from the host to the chip. ('write', 'Register write'), + ('warning', "Warnings"), ) annotation_rows = ( ('writes', 'Register writes', (ANN_REG,)), + ('warnings', 'Warnings', (ANN_WARN,)), ) def __init__(self): @@ -114,31 +144,87 @@ class Decoder(srd.Decoder): 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) diff --git a/decoders/adxl345/__init__.py b/decoders/adxl345/__init__.py new file mode 100644 index 0000000..e46bce9 --- /dev/null +++ b/decoders/adxl345/__init__.py @@ -0,0 +1,26 @@ +## +## 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 diff --git a/decoders/adxl345/lists.py b/decoders/adxl345/lists.py new file mode 100644 index 0000000..c22ef3e --- /dev/null +++ b/decoders/adxl345/lists.py @@ -0,0 +1,96 @@ +## +## 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'], +} diff --git a/decoders/adxl345/pd.py b/decoders/adxl345/pd.py new file mode 100644 index 0000000..2d53e4c --- /dev/null +++ b/decoders/adxl345/pd.py @@ -0,0 +1,453 @@ +## +## 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 . +## + +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 diff --git a/decoders/avr_isp/parts.py b/decoders/avr_isp/parts.py index 0767789..fee4d9b 100644 --- a/decoders/avr_isp/parts.py +++ b/decoders/avr_isp/parts.py @@ -22,20 +22,76 @@ # 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. } diff --git a/decoders/avr_isp/pd.py b/decoders/avr_isp/pd.py index bb52bb6..9e3c5df 100644 --- a/decoders/avr_isp/pd.py +++ b/decoders/avr_isp/pd.py @@ -20,6 +20,10 @@ 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): @@ -41,14 +45,20 @@ class Decoder(srd.Decoder): ('rfb', 'Read fuse bits'), ('rhfb', 'Read high fuse bits'), ('refb', 'Read extended fuse bits'), + ('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,)), - ('devs', 'Devices', (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): @@ -70,17 +80,17 @@ class Decoder(srd.Decoder): 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]. @@ -89,16 +99,16 @@ class Decoder(srd.Decoder): # 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] @@ -106,20 +116,23 @@ class Decoder(srd.Decoder): # 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 @@ -127,36 +140,78 @@ class Decoder(srd.Decoder): # 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) @@ -174,10 +229,20 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/caliper/__init__.py b/decoders/caliper/__init__.py new file mode 100644 index 0000000..44dab08 --- /dev/null +++ b/decoders/caliper/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Tomas Mudrunka +## +## 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 . +## + +''' +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 diff --git a/decoders/caliper/pd.py b/decoders/caliper/pd.py new file mode 100644 index 0000000..20a2a55 --- /dev/null +++ b/decoders/caliper/pd.py @@ -0,0 +1,146 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Tomas Mudrunka +## +## 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() diff --git a/decoders/can/pd.py b/decoders/can/pd.py index 3dbadc0..fcd13e6 100644 --- a/decoders/can/pd.py +++ b/decoders/can/pd.py @@ -18,6 +18,7 @@ ## along with this program; if not, see . ## +from common.srdhelper import bitpack_msb import sigrokdecode as srd class SamplerateError(Exception): @@ -34,7 +35,7 @@ class Decoder(srd.Decoder): 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'}, @@ -79,6 +80,7 @@ class Decoder(srd.Decoder): 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) @@ -117,6 +119,9 @@ class Decoder(srd.Decoder): 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 @@ -128,6 +133,8 @@ class Decoder(srd.Decoder): self.ss_bit12 = None self.ss_bit32 = None self.ss_databytebits = [] + self.frame_bytes = [] + self.rtr_type = None self.fd = False self.rtr = None @@ -198,7 +205,7 @@ class Decoder(srd.Decoder): 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): @@ -235,6 +242,10 @@ class Decoder(srd.Decoder): 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 @@ -266,6 +277,7 @@ class Decoder(srd.Decoder): 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: @@ -283,7 +295,7 @@ class Decoder(srd.Decoder): # 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 + (dlc2len(self.dlc) * 8) @@ -300,7 +312,8 @@ class Decoder(srd.Decoder): self.ss_databytebits.append(self.samplenum) # Last databyte bit. 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), @@ -323,12 +336,12 @@ class Decoder(srd.Decoder): # 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']]) @@ -347,9 +360,10 @@ class Decoder(srd.Decoder): 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: @@ -383,7 +397,7 @@ class Decoder(srd.Decoder): # 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 + (dlc2len(self.dlc) * 8) @@ -398,7 +412,8 @@ class Decoder(srd.Decoder): self.ss_databytebits.append(self.samplenum) # Last databyte bit. 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), @@ -433,6 +448,7 @@ class Decoder(srd.Decoder): # 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']]) @@ -444,10 +460,13 @@ class Decoder(srd.Decoder): # 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). diff --git a/decoders/common/srdhelper/mod.py b/decoders/common/srdhelper/mod.py index 6c45af9..b56cce6 100644 --- a/decoders/common/srdhelper/mod.py +++ b/decoders/common/srdhelper/mod.py @@ -31,6 +31,20 @@ def bin2int(s: str): 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: diff --git a/decoders/eeprom24xx/lists.py b/decoders/eeprom24xx/lists.py index c6ee63d..a66cb19 100644 --- a/decoders/eeprom24xx/lists.py +++ b/decoders/eeprom24xx/lists.py @@ -189,6 +189,16 @@ chips = { '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': { diff --git a/decoders/eeprom24xx/pd.py b/decoders/eeprom24xx/pd.py index 549ee2d..7491f58 100644 --- a/decoders/eeprom24xx/pd.py +++ b/decoders/eeprom24xx/pd.py @@ -17,6 +17,7 @@ ## along with this program; if not, see . ## +import copy import sigrokdecode as srd from .lists import * @@ -416,16 +417,25 @@ class Decoder(srd.Decoder): 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(' ', '_') diff --git a/decoders/i2c/pd.py b/decoders/i2c/pd.py index 8297662..2259b45 100644 --- a/decoders/i2c/pd.py +++ b/decoders/i2c/pd.py @@ -21,6 +21,7 @@ # 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 ''' @@ -45,20 +46,24 @@ Packet: 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' is None. +For 'BITS' 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): @@ -109,15 +114,15 @@ class Decoder(srd.Decoder): 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: @@ -130,136 +135,210 @@ class Decoder(srd.Decoder): 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 @@ -268,11 +347,18 @@ class Decoder(srd.Decoder): # 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) diff --git a/decoders/i2cfilter/pd.py b/decoders/i2cfilter/pd.py index a54baab..877c467 100644 --- a/decoders/i2cfilter/pd.py +++ b/decoders/i2cfilter/pd.py @@ -18,8 +18,12 @@ ## along with this program; if not, see . ## -# 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): @@ -43,51 +47,60 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/ieee488/pd.py b/decoders/ieee488/pd.py index 4531cb3..b0948a6 100644 --- a/decoders/ieee488/pd.py +++ b/decoders/ieee488/pd.py @@ -69,6 +69,8 @@ GPIB level byte fields (commands, addresses, pieces of data): when addressing channels within the device. - 'DATA_BYTE': is the talker address (when available), is the raw data byte (transport layer, ATN inactive). + - 'PPOLL': is not applicable, 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': is the current talker, is the list of @@ -239,11 +241,12 @@ 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(11) +) = range(12) ( BIN_RAW, @@ -284,10 +287,12 @@ class Decoder(srd.Decoder): {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'}, ) options = ( - {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details', + {'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'), @@ -298,6 +303,7 @@ class Decoder(srd.Decoder): ('saddr', 'Secondary address'), ('data', 'Data byte'), ('eoi', 'EOI'), + ('pp', 'Parallel poll'), ('text', 'Talker text'), ('periph', 'IEC bus peripherals'), ('warning', 'Warning'), @@ -307,6 +313,7 @@ class Decoder(srd.Decoder): ('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,)), @@ -333,6 +340,7 @@ class Decoder(srd.Decoder): 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 @@ -401,6 +409,63 @@ class Decoder(srd.Decoder): 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. @@ -486,6 +551,13 @@ class Decoder(srd.Decoder): 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) @@ -683,6 +755,11 @@ class Decoder(srd.Decoder): # 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. @@ -699,6 +776,14 @@ class Decoder(srd.Decoder): 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) @@ -707,18 +792,34 @@ class Decoder(srd.Decoder): # 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]) diff --git a/decoders/ir_irmp/__init__.py b/decoders/ir_irmp/__init__.py new file mode 100644 index 0000000..b6bbff6 --- /dev/null +++ b/decoders/ir_irmp/__init__.py @@ -0,0 +1,25 @@ +## +## 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 . +## + +''' +IRMP is a multi protocol infrared remote protocol decoder. See +https://www.mikrocontroller.net/articles/IRMP for details. +''' + +from .pd import Decoder diff --git a/decoders/ir_irmp/irmp_library.py b/decoders/ir_irmp/irmp_library.py new file mode 100644 index 0000000..a1bc258 --- /dev/null +++ b/decoders/ir_irmp/irmp_library.py @@ -0,0 +1,168 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Rene Staffen +## Copyright (C) 2020-2021 Gerhard Sittig +## +## 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 . +## + +''' +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, + } diff --git a/decoders/ir_irmp/pd.py b/decoders/ir_irmp/pd.py new file mode 100644 index 0000000..b8df819 --- /dev/null +++ b/decoders/ir_irmp/pd.py @@ -0,0 +1,139 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2014 Gump Yang +## Copyright (C) 2019 Rene Staffen +## Copyright (C) 2020-2021 Gerhard Sittig +## +## 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 . +## + +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}]) diff --git a/decoders/ir_nec/__init__.py b/decoders/ir_nec/__init__.py index c361c3d..c3ab293 100644 --- a/decoders/ir_nec/__init__.py +++ b/decoders/ir_nec/__init__.py @@ -19,6 +19,7 @@ ''' 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 diff --git a/decoders/ir_nec/lists.py b/decoders/ir_nec/lists.py index 7d47a46..dbb9288 100644 --- a/decoders/ir_nec/lists.py +++ b/decoders/ir_nec/lists.py @@ -19,7 +19,9 @@ # Addresses/devices. Items that are not listed are reserved/unknown. address = { + 0x00: 'Joy-it SBC-IRC01', 0x40: 'Matsui TV', + 0xEA41: 'Unknown LED Panel', } digits = { @@ -47,4 +49,45 @@ command = { 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'], + } } diff --git a/decoders/ir_nec/pd.py b/decoders/ir_nec/pd.py index 3ee3716..dffe23e 100644 --- a/decoders/ir_nec/pd.py +++ b/decoders/ir_nec/pd.py @@ -17,12 +17,34 @@ ## along with this program; if not, see . ## -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' @@ -38,8 +60,10 @@ class Decoder(srd.Decoder): ) 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'), @@ -57,10 +81,10 @@ class Decoder(srd.Decoder): ('warning', 'Warning'), ) annotation_rows = ( - ('bits', 'Bits', (0, 1, 2, 3, 4)), - ('fields', 'Fields', (5, 6, 7, 8, 9, 10)), - ('remote-vals', '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): @@ -69,36 +93,44 @@ class Decoder(srd.Decoder): 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() @@ -106,7 +138,7 @@ class Decoder(srd.Decoder): 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): @@ -115,12 +147,15 @@ class Decoder(srd.Decoder): 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) @@ -133,39 +168,57 @@ class Decoder(srd.Decoder): 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 - self.active = 0 if self.options['polarity'] == 'active-low' else 1 + 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. @@ -182,54 +235,64 @@ class Decoder(srd.Decoder): # 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() diff --git a/decoders/ir_sirc/__init__.py b/decoders/ir_sirc/__init__.py new file mode 100644 index 0000000..4061ed7 --- /dev/null +++ b/decoders/ir_sirc/__init__.py @@ -0,0 +1,26 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Tom Flanagan +## +## 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 . +## + +''' +Decoder for the Sony IR remote control protocol (SIRC). + +https://www.sbprojects.net/knowledge/ir/sirc.php +''' + +from .pd import Decoder diff --git a/decoders/ir_sirc/lists.py b/decoders/ir_sirc/lists.py new file mode 100644 index 0000000..5c6d8fa --- /dev/null +++ b/decoders/ir_sirc/lists.py @@ -0,0 +1,201 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Tom Flanagan +## +## 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 . +## + +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) diff --git a/decoders/ir_sirc/pd.py b/decoders/ir_sirc/pd.py new file mode 100644 index 0000000..14ba63f --- /dev/null +++ b/decoders/ir_sirc/pd.py @@ -0,0 +1,215 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Tom Flanagan +## +## 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 . +## + +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', + ]) diff --git a/decoders/jtag/pd.py b/decoders/jtag/pd.py index 00602bb..5fa63ab 100644 --- a/decoders/jtag/pd.py +++ b/decoders/jtag/pd.py @@ -183,18 +183,18 @@ class Decoder(srd.Decoder): 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.value.startswith('UPDATE-'): @@ -202,6 +202,8 @@ class Decoder(srd.Decoder): self.es_bitstring = self.samplenum 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' @@ -211,6 +213,8 @@ class Decoder(srd.Decoder): self.bits_samplenums_tdi = [] 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' diff --git a/decoders/lfast/__init__.py b/decoders/lfast/__init__.py new file mode 100644 index 0000000..681f4f9 --- /dev/null +++ b/decoders/lfast/__init__.py @@ -0,0 +1,35 @@ +## +## This file is part of the libsigrokdecode project. +## +## 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 . +## + +''' +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 diff --git a/decoders/lfast/pd.py b/decoders/lfast/pd.py new file mode 100644 index 0000000..7476e59 --- /dev/null +++ b/decoders/lfast/pd.py @@ -0,0 +1,335 @@ +## +## This file is part of the libsigrokdecode project. +## +## 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 . +## + +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() diff --git a/decoders/lpc/pd.py b/decoders/lpc/pd.py index cf1707f..2a88e30 100644 --- a/decoders/lpc/pd.py +++ b/decoders/lpc/pd.py @@ -315,13 +315,9 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/ltc242x/__init__.py b/decoders/ltc242x/__init__.py new file mode 100644 index 0000000..43d9d34 --- /dev/null +++ b/decoders/ltc242x/__init__.py @@ -0,0 +1,25 @@ +## +## 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 . +## + +''' +This decoder stacks on top of the 'spi' PD and decodes the +Linear Technology LTC2421/LTC2422 protocol. +''' + +from .pd import Decoder diff --git a/decoders/ltc242x/pd.py b/decoders/ltc242x/pd.py new file mode 100644 index 0000000..27ae5b9 --- /dev/null +++ b/decoders/ltc242x/pd.py @@ -0,0 +1,86 @@ +## +## 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 . +## + +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 diff --git a/decoders/ltc26x7/__init__.py b/decoders/ltc26x7/__init__.py new file mode 100644 index 0000000..efedd4d --- /dev/null +++ b/decoders/ltc26x7/__init__.py @@ -0,0 +1,25 @@ +## +## 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 . +## + +''' +This decoder stacks on top of the 'i2c' PD and decodes the +Linear Technology LTC2607/LTC2617/LTC2627 protocol. +''' + +from .pd import Decoder diff --git a/decoders/ltc26x7/pd.py b/decoders/ltc26x7/pd.py new file mode 100644 index 0000000..3255d5f --- /dev/null +++ b/decoders/ltc26x7/pd.py @@ -0,0 +1,187 @@ +## +## 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 . +## + +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 diff --git a/decoders/nes_gamepad/pd.py b/decoders/nes_gamepad/pd.py index b276e5d..a393abf 100644 --- a/decoders/nes_gamepad/pd.py +++ b/decoders/nes_gamepad/pd.py @@ -51,24 +51,22 @@ class Decoder(srd.Decoder): def reset(self): self.variant = None - self.ss_block = None - self.es_block = None def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.variant = self.options['variant'] - def putx(self, data): - self.put(self.ss_block, self.es_block, self.out_ann, data) + def putg(self, ss, es, cls, text): + self.put(ss, es, self.out_ann, [cls, [text]]) - def handle_data(self, value): - if value == 0xFF: - self.putx([1, ['No button is pressed']]) - return + def handle_data(self, ss, es, value): + if value == 0xff: + self.putg(ss, es, 1, 'No button is pressed') + return if value == 0x00: - self.putx([2, ['Gamepad is not connected']]) - return + self.putg(ss, es, 2, 'Gamepad is not connected') + return buttons = [ 'A', @@ -78,28 +76,17 @@ class Decoder(srd.Decoder): 'North', 'South', 'West', - 'East' + 'East', ] - bits = format(value, '08b') - button_str = '' - - for b in enumerate(bits): - button_index = b[0] - button_is_pressed = b[1] == '0' - - if button_is_pressed: - if button_str != '': - button_str += ' + ' - button_str += buttons[button_index] - - self.putx([0, ['%s' % button_str]]) + 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, mosi, miso = data - self.ss_block, self.es_block = ss, es - - if ptype != 'DATA': - return - - self.handle_data(miso) + ptype, _, _ = data + if ptype == 'DATA': + _, _, miso = data + self.handle_data(ss, es, miso) + return diff --git a/decoders/nrf905/__init__.py b/decoders/nrf905/__init__.py new file mode 100644 index 0000000..c6d35ab --- /dev/null +++ b/decoders/nrf905/__init__.py @@ -0,0 +1,26 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Jorge Solla Rubiales +## +## 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 diff --git a/decoders/nrf905/pd.py b/decoders/nrf905/pd.py new file mode 100644 index 0000000..12949fe --- /dev/null +++ b/decoders/nrf905/pd.py @@ -0,0 +1,301 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Jorge Solla Rubiales +## +## 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)) diff --git a/decoders/numbers_and_state/__init__.py b/decoders/numbers_and_state/__init__.py new file mode 100644 index 0000000..4fe42a3 --- /dev/null +++ b/decoders/numbers_and_state/__init__.py @@ -0,0 +1,41 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Comlab AG +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +''' +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 diff --git a/decoders/numbers_and_state/pd.py b/decoders/numbers_and_state/pd.py new file mode 100644 index 0000000..b8ac87e --- /dev/null +++ b/decoders/numbers_and_state/pd.py @@ -0,0 +1,377 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Comlab AG +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +# 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: +[, ] + +This is a list of s and their respective 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 diff --git a/decoders/parallel/__init__.py b/decoders/parallel/__init__.py index 100523e..e7ed36c 100644 --- a/decoders/parallel/__init__.py +++ b/decoders/parallel/__init__.py @@ -19,16 +19,27 @@ ''' 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 diff --git a/decoders/parallel/pd.py b/decoders/parallel/pd.py index 8f23aa8..1e31208 100644 --- a/decoders/parallel/pd.py +++ b/decoders/parallel/pd.py @@ -54,18 +54,22 @@ Packet: word 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' @@ -76,10 +80,19 @@ class Decoder(srd.Decoder): 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', @@ -88,125 +101,185 @@ class Decoder(srd.Decoder): annotations = ( ('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. diff --git a/decoders/pca9571/pd.py b/decoders/pca9571/pd.py index e27f215..ea8715d 100644 --- a/decoders/pca9571/pd.py +++ b/decoders/pca9571/pd.py @@ -23,6 +23,12 @@ NUM_OUTPUT_CHANNELS = 8 # 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' @@ -38,6 +44,7 @@ class Decoder(srd.Decoder): ('value', 'Register value'), ('warning', 'Warning'), ) + logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS) annotation_rows = ( ('regs', 'Registers', (0, 1)), ('warnings', 'Warnings', (2,)), @@ -49,13 +56,24 @@ class Decoder(srd.Decoder): 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'] @@ -64,7 +82,9 @@ class Decoder(srd.Decoder): '(%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]]) diff --git a/decoders/pjdl/__init__.py b/decoders/pjdl/__init__.py new file mode 100644 index 0000000..c3cc855 --- /dev/null +++ b/decoders/pjdl/__init__.py @@ -0,0 +1,27 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +''' +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 diff --git a/decoders/pjdl/pd.py b/decoders/pjdl/pd.py new file mode 100644 index 0000000..d5cc39f --- /dev/null +++ b/decoders/pjdl/pd.py @@ -0,0 +1,723 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +# 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: +[, ] + +This is the list of s and their respective values: + +Carrier sense: +- 'IDLE': is the pin level (always 0). +- 'BUSY': is always True. + +Raw bit slots: +- 'PAD_BIT': is the pin level (always 1). +- 'DATA_BIT': is the pin level (0, or 1). +- 'SHORT_BIT': is the pin level (always 1). +- 'SYNC_LOSS': is an arbitrary text (internal use only). + +Date bytes and frames: +- 'SYNC_PAD': is True. Spans the high pad bit as well as the + low data bit. +- 'DATA_BYTE': is the byte value (0..255). +- 'FRAME_INIT': is True. Spans three sync pads. +- 'FRAME_DATA': 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': 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() diff --git a/decoders/pjon/__init__.py b/decoders/pjon/__init__.py new file mode 100644 index 0000000..579fb59 --- /dev/null +++ b/decoders/pjon/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +''' +This protocol decoder interprets the PJON protocol on top of the PJDL +link layer (and potentially other link layers). +''' + +from .pd import Decoder diff --git a/decoders/pjon/pd.py b/decoders/pjon/pd.py new file mode 100644 index 0000000..b23cfb8 --- /dev/null +++ b/decoders/pjon/pd.py @@ -0,0 +1,603 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Gerhard Sittig +## +## 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 . +## + +# 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(' 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 diff --git a/decoders/rc_encode/__init__.py b/decoders/rc_encode/__init__.py index db78dc1..7cc7ab5 100644 --- a/decoders/rc_encode/__init__.py +++ b/decoders/rc_encode/__init__.py @@ -26,11 +26,15 @@ pressed and address settings into a series of pulses which is then 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 diff --git a/decoders/rc_encode/pd.py b/decoders/rc_encode/pd.py index 0d7cc8c..31727bd 100644 --- a/decoders/rc_encode/pd.py +++ b/decoders/rc_encode/pd.py @@ -21,43 +21,63 @@ import sigrokdecode as srd 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': @@ -68,15 +88,36 @@ def decode_model(model, bits): 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 = [] @@ -100,8 +141,8 @@ class Decoder(srd.Decoder): ('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): @@ -120,6 +161,13 @@ class Decoder(srd.Decoder): 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) @@ -134,20 +182,20 @@ class Decoder(srd.Decoder): 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: diff --git a/decoders/rgb_led_spi/pd.py b/decoders/rgb_led_spi/pd.py index 82877b3..899a64a 100644 --- a/decoders/rgb_led_spi/pd.py +++ b/decoders/rgb_led_spi/pd.py @@ -19,6 +19,8 @@ import sigrokdecode as srd +( ANN_RGB, ) = range(1) + class Decoder(srd.Decoder): api_version = 3 id = 'rgb_led_spi' @@ -37,34 +39,34 @@ class Decoder(srd.Decoder): 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)]) diff --git a/decoders/rgb_led_ws281x/pd.py b/decoders/rgb_led_ws281x/pd.py index bf181b6..099a2ce 100644 --- a/decoders/rgb_led_ws281x/pd.py +++ b/decoders/rgb_led_ws281x/pd.py @@ -17,12 +17,48 @@ ## along with this program; if not, see . ## +# 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' @@ -40,10 +76,22 @@ class Decoder(srd.Decoder): ('bit', 'Bit'), ('reset', 'RESET'), ('rgb', 'RGB'), + ('r', 'R'), + ('g', 'G'), + ('b', 'B'), + ('w', 'W'), ) annotation_rows = ( - ('bits', 'Bits', (0, 1)), - ('rgb-vals', 'RGB values', (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): @@ -51,12 +99,7 @@ class Decoder(srd.Decoder): 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) @@ -65,71 +108,160 @@ class Decoder(srd.Decoder): 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.ss 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 diff --git a/decoders/sae_j1850_vpw/__init__.py b/decoders/sae_j1850_vpw/__init__.py new file mode 100644 index 0000000..6894bfd --- /dev/null +++ b/decoders/sae_j1850_vpw/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Anthony Symons +## +## 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 . +## + +''' +SAE J1850 Variable Pulse Width decoder. Decode GM VPW 1X and 4X Vehicle Bus. +''' + +from .pd import Decoder diff --git a/decoders/sae_j1850_vpw/pd.py b/decoders/sae_j1850_vpw/pd.py new file mode 100644 index 0000000..3655f96 --- /dev/null +++ b/decoders/sae_j1850_vpw/pd.py @@ -0,0 +1,296 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Anthony Symons +## Copyright (C) 2023 Gerhard Sittig +## +## 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 . +## + +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 diff --git a/decoders/sbus_futaba/__init__.py b/decoders/sbus_futaba/__init__.py new file mode 100644 index 0000000..9404f4f --- /dev/null +++ b/decoders/sbus_futaba/__init__.py @@ -0,0 +1,35 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2022 Gerhard Sittig +## +## 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 . +## + +''' +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 diff --git a/decoders/sbus_futaba/pd.py b/decoders/sbus_futaba/pd.py new file mode 100644 index 0000000..75c2cfb --- /dev/null +++ b/decoders/sbus_futaba/pd.py @@ -0,0 +1,273 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2022 Gerhard Sittig +## +## 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 . +## + +""" +OUTPUT_PYTHON format: + +Packet: +(, ) + +This is the list of codes and their respective 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) diff --git a/decoders/sdcard_sd/pd.py b/decoders/sdcard_sd/pd.py index ecbfb08..a6e9834 100644 --- a/decoders/sdcard_sd/pd.py +++ b/decoders/sdcard_sd/pd.py @@ -22,6 +22,7 @@ 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 \ @@ -44,7 +45,7 @@ a = ['CMD%d' % i for i in range(64)] + ['ACMD%d' % i for i in range(64)] + \ ['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 'START TRANSM CMD ARG CRC END'.split()] + \ + ['F_' + f for f in token_fields] + \ ['DECODED_BIT', 'DECODED_F'] Ann = SrdIntEnum.from_list('Ann', a) @@ -86,13 +87,8 @@ class Decoder(srd.Decoder): 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) + \ ( \ - ('field-start', 'Start bit'), - ('field-transmission', 'Transmission bit'), - ('field-cmd', 'Command'), - ('field-arg', 'Argument'), - ('field-crc', 'CRC'), - ('field-end', 'End bit'), ('decoded-bit', 'Decoded bit'), ('decoded-field', 'Decoded field'), ) @@ -163,7 +159,7 @@ class Decoder(srd.Decoder): # CMD[46:46]: Transmission bit (1 == host) t = 'host' if s[1].bit == 1 else 'card' - self.putf(1, 1, [Ann.F_TRANSM, ['Transmission: ' + t, 'T: ' + t, 'T']]) + 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].bit) for i in range(2, 8)]), 2) @@ -440,7 +436,7 @@ class Decoder(srd.Decoder): 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_TRANSM, ['Transmission: ' + t, 'T: ' + t, 'T']]) + 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']]) @@ -471,7 +467,7 @@ class Decoder(srd.Decoder): 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_TRANSM, ['Transmission: ' + t, 'T: ' + t, 'T']]) + 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']]) diff --git a/decoders/sdq/__init__.py b/decoders/sdq/__init__.py new file mode 100644 index 0000000..3fc1043 --- /dev/null +++ b/decoders/sdq/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019-2020 Philip Åkesson +## +## 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 . +## + +''' +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 diff --git a/decoders/sdq/pd.py b/decoders/sdq/pd.py new file mode 100644 index 0000000..66df420 --- /dev/null +++ b/decoders/sdq/pd.py @@ -0,0 +1,131 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019-2020 Philip Åkesson +## +## 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 . +## + +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) diff --git a/decoders/seven_segment/pd.py b/decoders/seven_segment/pd.py index 87714bb..2172269 100644 --- a/decoders/seven_segment/pd.py +++ b/decoders/seven_segment/pd.py @@ -22,8 +22,27 @@ 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', @@ -31,15 +50,53 @@ digits = { (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', - (0, 0, 1, 1, 1, 1, 1): 'B', + (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, 1, 1, 1, 1, 0, 1): 'D', + (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): @@ -67,6 +124,8 @@ class Decoder(srd.Decoder): 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'), @@ -92,43 +151,36 @@ class Decoder(srd.Decoder): def decode(self): oldpins = self.wait() - - # Check if at least the 7 signals are present. - if False in [p in (0, 1) for p in oldpins[:7]]: - raise ChannelError('7 or 8 pins have to be present.') - 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 = [{0: 'e'}, {1: 'e'}, {2: 'e'}, {3: 'e'}, {4: 'e'}, {5: 'e'}, {6: 'e'}] - - if self.have_dp: - conditions.append({7: 'e'}) - + 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': - # Invert all data lines if a common anode display is used. - if self.have_dp: - oldpins = tuple((1 - state for state in oldpins)) - else: - oldpins = tuple((1 - state for state in oldpins[:7])) + 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: - dp = oldpins[7] - - # Check if decimal point is present and active. - if self.have_dp and dp == 1: - digit += '.' - + if self.have_dp: + dp = oldpins[7] + if dp == 1: + digit += '.' self.putb(lastpos, self.samplenum, [0, [digit]]) - lastpos = self.samplenum - oldpins = pins + lastpos = self.samplenum diff --git a/decoders/sipi/__init__.py b/decoders/sipi/__init__.py new file mode 100644 index 0000000..d62e3c1 --- /dev/null +++ b/decoders/sipi/__init__.py @@ -0,0 +1,30 @@ +## +## This file is part of the libsigrokdecode project. +## +## 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 . +## + +''' +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 diff --git a/decoders/sipi/pd.py b/decoders/sipi/pd.py new file mode 100644 index 0000000..5bd58fb --- /dev/null +++ b/decoders/sipi/pd.py @@ -0,0 +1,181 @@ +## +## This file is part of the libsigrokdecode project. +## +## 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 . +## + +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']) diff --git a/decoders/sle44xx/__init__.py b/decoders/sle44xx/__init__.py new file mode 100644 index 0000000..0eb0285 --- /dev/null +++ b/decoders/sle44xx/__init__.py @@ -0,0 +1,26 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Federico Cerutti +## +## 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 . +## + +''' +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 diff --git a/decoders/sle44xx/pd.py b/decoders/sle44xx/pd.py new file mode 100644 index 0000000..9f33207 --- /dev/null +++ b/decoders/sle44xx/pd.py @@ -0,0 +1,541 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Federico Cerutti +## +## 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 . +## + +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 diff --git a/decoders/spdif/pd.py b/decoders/spdif/pd.py index 126a027..1b7f6e9 100644 --- a/decoders/spdif/pd.py +++ b/decoders/spdif/pd.py @@ -77,6 +77,14 @@ class Decoder(srd.Decoder): 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 = [] @@ -88,8 +96,6 @@ class Decoder(srd.Decoder): 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: @@ -101,32 +107,54 @@ class Decoder(srd.Decoder): 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. @@ -140,24 +168,39 @@ class Decoder(srd.Decoder): 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: @@ -222,16 +265,22 @@ class Decoder(srd.Decoder): 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': diff --git a/decoders/spiflash/lists.py b/decoders/spiflash/lists.py index 80ca27d..e31daf7 100644 --- a/decoders/spiflash/lists.py +++ b/decoders/spiflash/lists.py @@ -60,6 +60,7 @@ device_name = { 0x15: 'FM25Q32', }, 'macronix': { + 0x13: 'MX25L8006', 0x14: 'MX25L1605D', 0x15: 'MX25L3205D', 0x16: 'MX25L6405D', @@ -151,6 +152,17 @@ chips = { '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', diff --git a/decoders/st25r39xx_spi/__init__.py b/decoders/st25r39xx_spi/__init__.py new file mode 100644 index 0000000..3803789 --- /dev/null +++ b/decoders/st25r39xx_spi/__init__.py @@ -0,0 +1,31 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019-2020 Benjamin Vernoux +## +## 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 . +## + +''' +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 diff --git a/decoders/st25r39xx_spi/lists.py b/decoders/st25r39xx_spi/lists.py new file mode 100644 index 0000000..1429765 --- /dev/null +++ b/decoders/st25r39xx_spi/lists.py @@ -0,0 +1,231 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019-2020 Benjamin Vernoux +## +## 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 . +## +## 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 +## ... + diff --git a/decoders/st25r39xx_spi/pd.py b/decoders/st25r39xx_spi/pd.py new file mode 100644 index 0000000..a6f55b9 --- /dev/null +++ b/decoders/st25r39xx_spi/pd.py @@ -0,0 +1,360 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019-2021 Benjamin Vernoux +## +## 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 . +## +## 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)) diff --git a/decoders/st7735/pd.py b/decoders/st7735/pd.py index 626d2eb..d938a2c 100644 --- a/decoders/st7735/pd.py +++ b/decoders/st7735/pd.py @@ -108,7 +108,7 @@ class Decoder(srd.Decoder): 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: diff --git a/decoders/tca6408a/pd.py b/decoders/tca6408a/pd.py index 3a46fac..4ca1082 100644 --- a/decoders/tca6408a/pd.py +++ b/decoders/tca6408a/pd.py @@ -21,6 +21,14 @@ 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' @@ -36,6 +44,7 @@ class Decoder(srd.Decoder): ('value', 'Register value'), ('warning', 'Warning'), ) + logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS) annotation_rows = ( ('regs', 'Registers', (0, 1)), ('warnings', 'Warnings', (2,)), @@ -48,17 +57,33 @@ class Decoder(srd.Decoder): 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]]) diff --git a/decoders/timing/pd.py b/decoders/timing/pd.py index 3c70eca..95c5332 100644 --- a/decoders/timing/pd.py +++ b/decoders/timing/pd.py @@ -45,6 +45,56 @@ def normalize_time(t): 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' @@ -60,18 +110,25 @@ class Decoder(srd.Decoder): ) annotations = ( ('time', 'Time'), + ('terse', 'Terse'), ('average', 'Average'), ('delta', 'Delta'), ) annotation_rows = ( - ('times', 'Times', (0,)), - ('averages', 'Averages', (1,)), - ('deltas', 'Deltas', (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): @@ -79,11 +136,6 @@ class Decoder(srd.Decoder): 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: @@ -91,38 +143,52 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/uart/pd.py b/decoders/uart/pd.py index 0c501b0..038a2f8 100644 --- a/decoders/uart/pd.py +++ b/decoders/uart/pd.py @@ -114,7 +114,7 @@ class Decoder(srd.Decoder): {'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)}, + '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', @@ -207,11 +207,12 @@ class Decoder(srd.Decoder): self.samplerate = None 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 = [[], []] @@ -251,11 +252,13 @@ class Decoder(srd.Decoder): # 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). @@ -266,17 +269,21 @@ class Decoder(srd.Decoder): 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([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' @@ -312,6 +319,7 @@ class Decoder(srd.Decoder): # 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 @@ -339,11 +347,7 @@ class Decoder(srd.Decoder): 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'] == 'none': - self.state[rxtx] = 'GET STOP BITS' + self.advance_state(rxtx, signal) def format_value(self, v): # Format value 'v' according to configured options. @@ -389,6 +393,7 @@ class Decoder(srd.Decoder): def get_parity_bit(self, rxtx, signal): self.paritybit[rxtx] = signal + self.cur_frame_bit[rxtx] += 1 if parity_ok(self.options['parity'], self.paritybit[rxtx], self.datavalue[rxtx], self.options['data_bits']): @@ -400,34 +405,93 @@ class Decoder(srd.Decoder): 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]]) + 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.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' - self.idle_start[rxtx] = self.frame_start[rxtx] + self.frame_len_sample_count + 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, - [Ann.RX_BREAK + rxtx, ['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): @@ -437,17 +501,12 @@ class Decoder(srd.Decoder): 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['data_bits'] - elif state == 'GET STOP BITS': - bitnum = 1 + self.options['data_bits'] - bitnum += 0 if self.options['parity'] == '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 @@ -490,7 +549,8 @@ class Decoder(srd.Decoder): 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): @@ -509,8 +569,8 @@ class Decoder(srd.Decoder): if diff < self.frame_len_sample_count: return ss, es = self.idle_start[rxtx], self.samplenum - self.putpse(ss, es, ['IDLE', rxtx, 0]) - self.idle_start[rxtx] = self.samplenum + self.handle_idle(rxtx, ss, es) + self.idle_start[rxtx] = es def decode(self): if not self.samplerate: diff --git a/decoders/xy2-100/__init__.py b/decoders/xy2-100/__init__.py new file mode 100644 index 0000000..676e1af --- /dev/null +++ b/decoders/xy2-100/__init__.py @@ -0,0 +1,28 @@ +## +## 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 . +## + +''' +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 diff --git a/decoders/xy2-100/pd.py b/decoders/xy2-100/pd.py new file mode 100644 index 0000000..47c4182 --- /dev/null +++ b/decoders/xy2-100/pd.py @@ -0,0 +1,242 @@ +## +## 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 . +## + +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 diff --git a/instance.c b/instance.c index 5264bd0..067e98d 100644 --- a/instance.c +++ b/instance.c @@ -167,7 +167,7 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, } } 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); @@ -426,6 +426,7 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, di->got_new_samples = FALSE; di->handled_all_samples = FALSE; di->want_wait_terminate = FALSE; + di->communicate_eof = FALSE; di->decoder_state = SRD_OK; /* @@ -500,6 +501,7 @@ static void srd_inst_reset_state(struct srd_decoder_inst *di) 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. */ } @@ -699,7 +701,7 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) Py_DECREF(py_res); /* Set self.samplenum to 0. */ - py_samplenum = PyLong_FromLong(0); + py_samplenum = PyLong_FromUnsignedLongLong(0); PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum); Py_DECREF(py_samplenum); @@ -1058,6 +1060,17 @@ static gpointer di_thread(gpointer data) 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; @@ -1234,12 +1247,111 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, 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. * diff --git a/irmp/README-sigrok.txt b/irmp/README-sigrok.txt new file mode 100644 index 0000000..03ff472 --- /dev/null +++ b/irmp/README-sigrok.txt @@ -0,0 +1,3 @@ +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. diff --git a/irmp/README.txt b/irmp/README.txt new file mode 100644 index 0000000..52d8c73 --- /dev/null +++ b/irmp/README.txt @@ -0,0 +1,13 @@ +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 diff --git a/irmp/irmp-main-sharedlib.c b/irmp/irmp-main-sharedlib.c new file mode 100644 index 0000000..4d02460 --- /dev/null +++ b/irmp/irmp-main-sharedlib.c @@ -0,0 +1,309 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include + +/* + * 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(); +} diff --git a/irmp/irmp-main-sharedlib.h b/irmp/irmp-main-sharedlib.h new file mode 100644 index 0000000..67e7997 --- /dev/null +++ b/irmp/irmp-main-sharedlib.h @@ -0,0 +1,160 @@ +/* + * 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 + * + * 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 +#include + +#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 diff --git a/irmp/irmp.c b/irmp/irmp.c new file mode 100644 index 0000000..42b04c8 --- /dev/null +++ b/irmp/irmp.c @@ -0,0 +1,6151 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * 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 +# 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< 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<CR1 |= (1<> 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 = ""; 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 diff --git a/irmp/irmp.h b/irmp/irmp.h new file mode 100644 index 0000000..2ed0b7d --- /dev/null +++ b/irmp/irmp.h @@ -0,0 +1,311 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * 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_ */ diff --git a/irmp/irmpconfig.h b/irmp/irmpconfig.h new file mode 100644 index 0000000..6db651c --- /dev/null +++ b/irmp/irmpconfig.h @@ -0,0 +1,287 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * 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_ diff --git a/irmp/irmpprotocols.h b/irmp/irmpprotocols.h new file mode 100644 index 0000000..6e8bdfb --- /dev/null +++ b/irmp/irmpprotocols.h @@ -0,0 +1,1072 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * 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_ diff --git a/irmp/irmpsystem.h b/irmp/irmpsystem.h new file mode 100644 index 0000000..4b14952 --- /dev/null +++ b/irmp/irmpsystem.h @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * 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 +# 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 +# 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 +# define ARM_STM32 +# define ARM_STM32F10X +# define F_CPU (SysCtlClockGet()) +#elif defined(STM32F4XX) // ARM STM32 +# include +# 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 +# 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 + +#ifdef UNIX_OR_WINDOWS // Analyze on Unix/Linux or Windows +# include +# include +# define F_CPU 8000000L +# define ANALYZE +# ifdef unix +# include +# else +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +# endif +#endif + + +#if defined(ATMEL_AVR) +# include +# include +# include +# include +# include +# include +# 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 // main PIC18 h file +#ifndef __XC8 +# include // timer lib +# include // 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_ diff --git a/libsigrokdecode-internal.h b/libsigrokdecode-internal.h index af245f6..0e3cb64 100644 --- a/libsigrokdecode-internal.h +++ b/libsigrokdecode-internal.h @@ -27,6 +27,17 @@ #include /* 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, @@ -82,6 +93,8 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, 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); diff --git a/libsigrokdecode.h b/libsigrokdecode.h index b9cc921..ea17cb8 100644 --- a/libsigrokdecode.h +++ b/libsigrokdecode.h @@ -106,29 +106,34 @@ enum srd_loglevel { */ /* 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, }; @@ -188,6 +193,11 @@ struct srd_decoder { */ GSList *binary; + /** + * List of logic output channels (item: id, description). + */ + GSList *logic_output_channels; + /** List of decoder options. */ GSList *options; @@ -232,6 +242,11 @@ struct srd_decoder_annotation_row { 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; @@ -280,6 +295,9 @@ struct srd_decoder_inst { /** 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; @@ -312,7 +330,12 @@ struct srd_proto_data_annotation { struct srd_proto_data_binary { 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, @@ -337,6 +360,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, 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, diff --git a/module_sigrokdecode.c b/module_sigrokdecode.c index 39b5b43..1fc0c77 100644 --- a/module_sigrokdecode.c +++ b/module_sigrokdecode.c @@ -63,6 +63,8 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) 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. */ diff --git a/session.c b/session.c index 386fb71..ad08407 100644 --- a/session.c +++ b/session.c @@ -278,6 +278,32 @@ SRD_API int srd_session_send(struct srd_session *sess, 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. * diff --git a/srd.c b/srd.c index 22f47fc..10dfaf6 100644 --- a/srd.c +++ b/srd.c @@ -67,8 +67,8 @@ extern SRD_PRIV int max_session_id; * @section sec_irc IRC * * You can find the sigrok developers in the - * \#sigrok - * IRC channel on Freenode. + * \#sigrok + * IRC channel on Libera.Chat. * * @section sec_website Website * @@ -270,15 +270,46 @@ SRD_API int srd_init(const char *path) } /* 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(); diff --git a/type_decoder.c b/type_decoder.c index c097c7f..6932cde 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -33,10 +33,11 @@ typedef struct { /* 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)" }; @@ -66,16 +67,16 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, /* 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; } @@ -85,27 +86,27 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, */ 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; } @@ -123,6 +124,89 @@ 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) @@ -151,38 +235,38 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, /* 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; } @@ -275,8 +359,8 @@ static int convert_meta(struct srd_proto_data *pdata, PyObject *obj) 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); @@ -285,8 +369,8 @@ static int convert_meta(struct srd_proto_data *pdata, PyObject *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); @@ -312,6 +396,13 @@ static void release_meta(GVariant *gvar) 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; @@ -321,6 +412,7 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) 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; @@ -390,9 +482,9 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) 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); } @@ -421,6 +513,25 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) 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. */ @@ -450,8 +561,12 @@ err: 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; @@ -605,13 +720,13 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di) /* 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)); } } @@ -848,6 +963,23 @@ static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count) 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; @@ -922,7 +1054,7 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *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. */ - py_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); @@ -957,6 +1089,27 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) /* 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. @@ -981,6 +1134,14 @@ err: 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. * @@ -996,6 +1157,7 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) int idx, count; struct srd_decoder_inst *di; PyGILState_STATE gstate; + PyObject *bool_ret; if (!self || !args) return NULL; @@ -1026,7 +1188,9 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) 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); @@ -1034,16 +1198,26 @@ err: 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, }; /** @@ -1057,10 +1231,10 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void) { 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; diff --git a/util.c b/util.c index 1e914e3..3a5e336 100644 --- a/util.c +++ b/util.c @@ -115,7 +115,7 @@ err: 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; @@ -139,10 +139,10 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out *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); @@ -217,8 +217,9 @@ err: 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(); @@ -227,8 +228,9 @@ SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx, 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; }