Manchester
Manchester.cpp
- Committer:
- hudakz
- Date:
- 2017-09-03
- Revision:
- 7:afd0ee36dcd1
- Parent:
- 6:7454ad91f714
- Child:
- 8:c1b5893191fe
File content as of revision 7:afd0ee36dcd1:
/* ****************************************************************************** * @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 "ManchesterMsg.h" /** * @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 speed, /* = 1200 bps */ uint8_t tol /* = (+/-)25% */ ) : _tx(txPin), _rx(rxPin) { _state = IDLE; _midBitTime = 1000000 / speed / 2; // mid-bit time [us] _minPulseWidth = (_midBitTime * 2 * (100 - tol)) / 100; // [us] _maxPulseWidth = (_midBitTime * 2 * (100 + tol)) / 100; // [us] _rx.disable_irq(); _rx.rise(callback(this, &Manchester::reception)); _rx.fall(callback(this, &Manchester::reception)); #ifdef G_E_THOMAS _tx = 1; #else _tx = 0; #endif } /** * @brief Transmits message * @note * @param msg Message to transmit * @retval */ void Manchester::transmit(ManchesterMsg& msg) { bool txFinished; _data = msg.data; _len = msg.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; _timeout.attach_us(callback(this, &Manchester::txTimeout), _maxPulseWidth * 4); switch(_state) { case SYNCH_START: #ifdef G_E_THOMAS _tx = 0; // pull line low to start synch pulse #else _tx = 1; // bring line high to start synch pulse #endif _state = SYNCH_NEXT; break; case SYNCH_NEXT: _state = SYNCH_END; // synch pulse needs to be twice the interrupt rate break; case SYNCH_END: #ifdef G_E_THOMAS _tx = 1; // bring line high for end of sych pulse #else _tx = 0; // pull line low for end of sych pulse #endif byteIndex = 0; encodeByte = _data[byteIndex]; bitIndex = 0; _state = SETUP; break; case SETUP: #ifdef G_E_THOMAS _tx = encodeByte & 0x01; // setup for next bit to transmit #else _tx = !(encodeByte & 0x01); // setup for next bit to transmit #endif _state = TRANSITION; break; case TRANSITION: #ifdef G_E_THOMAS _tx = !(encodeByte & 0x01); // set line appropriately for transition #else _tx = encodeByte & 0x01; // set line appropriately for transition #endif if(++bitIndex < 8) { encodeByte = (encodeByte >> 1); _state = SETUP; } else { if(++byteIndex < _len) { encodeByte = _data[byteIndex]; _state = SETUP; bitIndex = 0; } else _state = COMPLETE; } break; case COMPLETE: #ifdef G_E_THOMAS _tx = 1; // transmission is complete, bring line high #else _tx = 0; // transmission is complete, pull line low #endif _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::txTimeout(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(ManchesterMsg& msg) { bool rxFinished; _data = msg.data; _maxLen = msg.maxLen(); _state = LISTEN; _rx.enable_irq(); do { core_util_critical_section_enter(); rxFinished = ((_state == IDLE) || (_state == ERROR)); core_util_critical_section_exit(); } while(!rxFinished); _rx.disable_irq(); if(_state == ERROR) { msg.len = 0; _state = IDLE; return false; } else { msg.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; _timeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 4); switch(_state) { case LISTEN: begin = now; #ifdef G_E_THOMAS if(_rx == 0) #else if(_rx == 1) #endif _state = SYNCH_START; else _state = ERROR; // It isn't a synch pulse => error break; case SYNCH_START: pulseWidth = now - begin; #ifdef G_E_THOMAS if((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth) && (_rx == 1)) { #else if((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth) && (_rx == 0)) { #endif begin = now; decodeByte = 0; bitIndex = 0; _len = 0; _state = DECODE; } else _state = ERROR; // It isn't a synch pulse => error break; case DECODE: pulseWidth = now - begin; if((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth)) { begin = now; #ifdef G_E_THOMAS decodeByte |= (((_rx == 0) & 0x01) << bitIndex++); #else decodeByte |= (((_rx == 1) & 0x01) << bitIndex++); #endif if(bitIndex > 7) { _data[_len++] = decodeByte; if(_len > _maxLen - 1) _state = ERROR; else { decodeByte = 0; bitIndex = 0; } } } if(pulseWidth > _maxPulseWidth) _state = ERROR; // Pulse width out of limit => error 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::rxTimeout(void) { _timeout.detach(); #ifdef G_E_THOMAS if((_state == DECODE) && (_rx == 1)) #else if((_state == DECODE) && (_rx == 0)) #endif _state = IDLE; // Reception successful else _state = ERROR; // Reception incomplete }