Manchester
Manchester.cpp
- Committer:
- junhong
- Date:
- 2019-04-11
- Revision:
- 9:7a23184aa9ef
- Parent:
- 8:c1b5893191fe
File content as of revision 9:7a23184aa9ef:
/* ****************************************************************************** * @file Manchester.cpp * @author Zoltan Hudak * @version * @date 2017-May-16 * @brief Manchester code for mbed ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2017 Zoltan Hudak <hudakz@outlook.com> * * All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* This library implements Manchester code according to both IEEE 802.3 and G.E. Thomas' conventions. • A '0' is expressed by a high-to-low transition, a '1' by low-to-high transition in the IEEE 802.3 convention. The reverse is true in the G.E. Thomas' convention. • The transitions which signify '0' or '1' occur at the midpoint of a period. • Transitions at the start of a period are overhead and don't signify data. • Least significant bit is sent first • There is one synchronization pulse at the begin of transmission The IEEE 802.3 convention is used by default. Select a convention to be used by commenting or uncommenting the line "#define G_E_THOMAS 1" in the Manchester.h header file. */ #include "Manchester.h" #include "mbed-trace/mbed_trace.h" #undef TRACE_GROUP #define TRACE_GROUP "Manchester" /** * @brief Creates a Manchester object * @note * @param txPin Pin name of transmitter line * rxPin Pin name of receiver line * speed Communication bit rate in bits per second * tol Pulse width tolerance in % * @retval */ Manchester::Manchester ( PinName txPin, PinName rxPin, uint32_t baudrate, /* = 1200 bps */ uint8_t tol, /* = (+/-)25% */ float rxTimeout /* = 5s */ ) : _tx(txPin), _rx(rxPin) { _state = IDLE; _midBitTime = 1000000 / baudrate / 2; // mid-bit time [us] _minPulseWidth = (_midBitTime * 2 * (100 - tol)) / 100; // [us] _maxPulseWidth = (_midBitTime * 2 * (100 + tol)) / 100; // [us] _rxTimeout = rxTimeout; _rx.disable_irq(); _rx.rise(callback(this, &Manchester::reception)); _rx.fall(callback(this, &Manchester::reception)); _preamble = 1; // number of synch_start patterns tr_debug("_midBitTime = %d", _midBitTime); tr_debug("_minPulseWidth = %d", _minPulseWidth); tr_debug("_maxPulseWidth = %d", _maxPulseWidth); _tx = 0; } /** * @brief Transmits message * @note * @param msg Message to transmit * @retval */ void Manchester::transmit(uint8_t* msg, uint8_t len) { bool txFinished; _data = (char*)msg; _len = len; _state = SYNCH_START; _txTicker.attach_us(callback(this, &Manchester::transmission), _midBitTime); do { core_util_critical_section_enter(); txFinished = (_state == IDLE); core_util_critical_section_exit(); } while (!txFinished); _txTicker.detach(); } /** * @brief ISR handling transmission * @note Called by _txTicker * @param * @retval */ void Manchester::transmission(void) { static uint8_t encodeByte; static size_t byteIndex; static uint8_t bitIndex; static uint8_t counter; _timeout.attach_us(callback(this, &Manchester::onTxTimeout), _maxPulseWidth * 4); switch (_state) { case SYNCH_START: if (_preamble == 0) { byteIndex = 0; encodeByte = _data[byteIndex]; bitIndex = 0; _state = SETUP; break; } _tx = 1; // bring line high to start synch pulse counter = 0; _state = SYNCH_NEXT; break; case SYNCH_NEXT: counter++; _tx = !_tx; byteIndex = 0; encodeByte = _data[byteIndex]; bitIndex = 0; _state = SETUP; break; case SETUP: _tx = ((encodeByte >> 7) & 0x01); // setup for next bit to transmit _state = TRANSITION; break; case TRANSITION: _tx = !((encodeByte >> 7) & 0x01); // set line appropriately for transition if (++bitIndex < 8) { encodeByte = (encodeByte << 1); _state = SETUP; } else { if (++byteIndex < _len) { encodeByte = _data[byteIndex]; _state = SETUP; bitIndex = 0; } else { counter = 0; _state = STOP; } } break; case STOP: counter++; _tx = 0; if (counter == 4) _state = COMPLETE; break; case COMPLETE: _tx = 0; _state = IDLE; break; case IDLE: default: _timeout.detach(); return; } } /** * @brief ISR handling 'transmission timeout' * @note Called when transmitter is stuck. * Signals 'end of transmission' by setting state to IDLE * @param * @retval */ void Manchester::onTxTimeout(void) { _timeout.detach(); _state = IDLE; } /** * @brief Receives message * @note Waits until a message is received or 'reception timeout' occured * @param msg Container to store the received message * @retval true On success * false Otherwise */ bool Manchester::receive(uint8_t *msg, uint8_t *len) { bool rxFinished; _data = (char*)msg; _maxLen = *len; _state = LISTEN; _timeout.attach(callback(this, &Manchester::onRxTimeout), _rxTimeout); _rx.enable_irq(); do { core_util_critical_section_enter(); rxFinished = ((_state == IDLE) || (_state == ERROR)); core_util_critical_section_exit(); } while (!rxFinished); _rx.disable_irq(); _timer.stop(); _timeout.detach(); if (_state == ERROR) { len = 0; _state = IDLE; return false; } else { *len = _len; return true; } } /** * @brief ISR handling reception * @note Called on signal change (rise or fall) on receiver line * @param * @retval */ void Manchester::reception(void) { uint32_t now = us_ticker_read(); static uint32_t begin; uint32_t pulseWidth; static uint8_t decodeByte; static uint8_t bitIndex; switch (_state) { case LISTEN: begin = now; if (_rx == 1) { _state = SYNCH_START; } else { tr_err("SYNCH_START: Isn't a SYNCH pulse"); _state = ERROR; } break; case SYNCH_START: pulseWidth = now - begin; //printf("%d <= %d <= %d\r\n", (_midBitTime * 2) + _minPulseWidth, pulseWidth, (_midBitTime * 2) + _maxPulseWidth); if (((_midBitTime * 2) + _minPulseWidth <= pulseWidth) && (pulseWidth <= (_midBitTime * 2) + _maxPulseWidth)) { begin = now; _state = SYNCH_NEXT; } else { tr_err("SYNCH_START: Isn't a SYNCH pulse"); _state = ERROR; } break; case SYNCH_NEXT: pulseWidth = now - begin; //printf("%d <= %d <= %d\r\n", (_midBitTime * 2) + _minPulseWidth, pulseWidth, (_midBitTime * 2) + _maxPulseWidth); if (((_midBitTime * 2) + _minPulseWidth <= pulseWidth) && (pulseWidth <= (_midBitTime * 2) + _maxPulseWidth)) { begin = now; _state = SYNCH_NEXT; } else if ((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth)) { begin = now; decodeByte = 0; bitIndex = 0; _len = 0; decodeByte |= (((_rx == 1) & 0x01) << bitIndex++); _state = DECODE; } break; case DECODE: pulseWidth = now - begin; if ((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth)) { begin = now; decodeByte |= (((_rx == 1) & 0x01) << bitIndex++); if (bitIndex > 7) { _data[_len++] = decodeByte; if (_len > _maxLen - 1) _state = ERROR; else { decodeByte = 0; bitIndex = 0; } } break; } if (pulseWidth > _maxPulseWidth) { if (_rx == 1) { begin = now; _state = COMPLETE; // End of reception } } break; case COMPLETE: pulseWidth = now - begin; if (pulseWidth > _maxPulseWidth) { if (_rx == 0) { _state = IDLE; // End of reception } } break; case IDLE: case ERROR: default: _timeout.detach(); break; } } /** * @brief ISR handling 'reception timeout' * @note Called when receiver line is idle longer than limit. * Signals 'end of reception' by setting state to IDLE * or 'timeout error' by setting state to ERROR. * @param * @retval */ void Manchester::onRxTimeout(void) { _timeout.detach(); if ((_state == DECODE) && (_rx == 0)) { tr_err("rx timeout"); _state = IDLE; // Reception successful } else { tr_err("reception error"); _state = ERROR; // Reception incomplete } }