From b7c4ae8ae91def1e214f78323bbe05d79407701c Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 5 Nov 2018 07:37:31 +1100 Subject: [PATCH] AP_RCProtocol: added separate soft serial decoder this will replace the version in each protocol --- libraries/AP_RCProtocol/SoftSerial.cpp | 114 +++++++++++++++++++++++++ libraries/AP_RCProtocol/SoftSerial.h | 50 +++++++++++ 2 files changed, 164 insertions(+) create mode 100644 libraries/AP_RCProtocol/SoftSerial.cpp create mode 100644 libraries/AP_RCProtocol/SoftSerial.h diff --git a/libraries/AP_RCProtocol/SoftSerial.cpp b/libraries/AP_RCProtocol/SoftSerial.cpp new file mode 100644 index 0000000000..7b9baf606f --- /dev/null +++ b/libraries/AP_RCProtocol/SoftSerial.cpp @@ -0,0 +1,114 @@ +/* + * This file 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 file 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 . + */ +/* + soft serial receive implementation, based on pulse width inputs + */ + +#include "SoftSerial.h" +#include + +SoftSerial::SoftSerial(uint32_t _baudrate, serial_config _config) : + baudrate(_baudrate), + config(_config), + half_bit((1000000U / baudrate)/2) +{ + switch (config) { + case SERIAL_CONFIG_8N1: + data_width = 8; + byte_width = 10; + stop_mask = 0x200; + break; + case SERIAL_CONFIG_8E2I: + data_width = 9; + byte_width = 12; + stop_mask = 0xC00; + break; + } +} + +/* + process a pulse made up of a width of values at high voltage + followed by a width at low voltage + */ +bool SoftSerial::process_pulse(uint32_t width_high, uint32_t width_low, uint8_t &byte) +{ + // convert to bit widths, allowing for a half bit error + uint16_t bits_high = ((width_high+half_bit)*baudrate) / 1000000; + uint16_t bits_low = ((width_low+half_bit)*baudrate) / 1000000; + + byte_timestamp_us = timestamp_us; + timestamp_us += (width_high + width_low); + + if (bits_high == 0 || bits_low == 0) { + // invalid data + goto reset; + } + + if (bits_high >= byte_width) { + // if we have a start bit and a stop bit then we can have at + // most 9 bits in high state for data. The rest must be idle + // bits + bits_high = byte_width-1; + } + + if (state.bit_ofs == 0) { + // we are in idle state, waiting for first low bit. swallow + // the high bits + bits_high = 0; + } + + state.byte |= ((1U<= byte_width) { + // check start bit + if ((state.byte & 1) != 0) { + goto reset; + } + // check stop bits + if ((state.byte & stop_mask) != stop_mask) { + goto reset; + } + if (config == SERIAL_CONFIG_8E2I) { + // check parity + if (__builtin_parity((state.byte>>1)&0xFF) != (state.byte&0x200)>>9) { + goto reset; + } + } + + byte = ((state.byte>>1) & 0xFF); + state.byte >>= byte_width; + state.bit_ofs -= byte_width; + if (state.bit_ofs > byte_width) { + state.byte = 0; + state.bit_ofs = bits_low; + } + // swallow idle bits + while (state.bit_ofs > 0 && (state.byte & 1)) { + state.bit_ofs--; + state.byte >>= 1; + } + return true; + } + return false; + +reset: + state.byte = 0; + state.bit_ofs = 0; + + return false; +} + diff --git a/libraries/AP_RCProtocol/SoftSerial.h b/libraries/AP_RCProtocol/SoftSerial.h new file mode 100644 index 0000000000..3521654347 --- /dev/null +++ b/libraries/AP_RCProtocol/SoftSerial.h @@ -0,0 +1,50 @@ +/* + * This file 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 file 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 . + */ + +#pragma once + +#include "AP_RCProtocol.h" + +class SoftSerial { +public: + enum serial_config { + SERIAL_CONFIG_8N1, // DSM, SRXL etc, 8 bit, no parity, 1 stop bit + SERIAL_CONFIG_8E2I, // SBUS, 8 bit, even parity, 2 stop bits, inverted + }; + + SoftSerial(uint32_t baudrate, enum serial_config config); + bool process_pulse(uint32_t width_s0, uint32_t width_s1, uint8_t &b); + + // get timestamp of the last byte + uint32_t get_byte_timestamp_us(void) const { + return byte_timestamp_us; + } + +private: + const uint32_t baudrate; + const uint8_t half_bit; // width of half a bit in microseconds + const enum serial_config config; + + uint8_t data_width; + uint8_t byte_width; + uint16_t stop_mask; + uint32_t timestamp_us; + uint32_t byte_timestamp_us; + + struct { + uint32_t byte; + uint16_t bit_ofs; + } state; +};