Manchester code (phase encoding) library.
Dependents: Manchester_Transmitter Manchester_Receiver
Manchester code (phase encoding) library
It 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 are synchronization pulses (the number can be set) at the begin of transmission
Select a convention to be used by commenting or uncommenting the line below in theManchester.h
header file.
Manchester.h
#define G_E_THOMAS 1
The IEEE 802.3 convention is used by default.
A Manchester encoded message (using G.E. Thomas' convention), with one sync pulse in the preamble, carrying four bytes:
ACKNOWLEDGEMENT: The code in this library was based on this article published by Robert Guastella.
Import programManchester_Transmitter
Manchester transmitter demo.
Import programManchester_Receiver
Manchester receiver demo.
NOTE: To perform a simple test (without radio modules) connect the txPin on transmitter board to the rxPin on the receiver board and make sure that grounds are also connected one another.
Manchester.cpp
- Committer:
- hudakz
- Date:
- 2018-10-14
- Revision:
- 8:c1b5893191fe
- Parent:
- 7:afd0ee36dcd1
File content as of revision 8:c1b5893191fe:
/* ****************************************************************************** * @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 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 = 8; // number of synch_start patterns _error = "no error"; // printf("_midBitTime = %d\r\n", _midBitTime); // printf("_minPulseWidth = %d\r\n", _minPulseWidth); // printf("_maxPulseWidth = %d\r\n", _maxPulseWidth); #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; 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; } #ifdef G_E_THOMAS _tx = 0; // pull line low to start synch pulse #else _tx = 1; // bring line high to start synch pulse #endif counter = 0; _state = SYNCH_NEXT; break; case SYNCH_NEXT: counter++; if ((counter % 4) == 0) _tx = !_tx; if (counter < (_preamble * 8)) break; else { 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 { counter = 0; _state = STOP; } } break; case STOP: counter++; if (counter == 1) { #ifdef G_E_THOMAS _tx = 1; #else _tx = 0; #endif } else if (counter == 5) { #ifdef G_E_THOMAS _tx = 0; #else _tx = 1; #endif } else if (counter == 8) _state = COMPLETE; break; case COMPLETE: #ifdef G_E_THOMAS _tx = 1; #else _tx = 0; #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::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(ManchesterMsg& msg) { bool rxFinished; _data = msg.data; _maxLen = msg.maxLen(); _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) { 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; switch (_state) { case LISTEN: begin = now; if ( #ifdef G_E_THOMAS _rx == 0 #else _rx == 1 #endif ) { _state = SYNCH_START; } else { _error = "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 { _error = "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; #ifdef G_E_THOMAS decodeByte |= (((_rx == 0) & 0x01) << bitIndex++); #else decodeByte |= (((_rx == 1) & 0x01) << bitIndex++); #endif _state = DECODE; } 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; } } break; } if (pulseWidth > _maxPulseWidth) { if ( #ifdef G_E_THOMAS _rx == 0 #else _rx == 1 #endif ) { begin = now; _state = COMPLETE; // End of reception } } break; case COMPLETE: pulseWidth = now - begin; if (pulseWidth > _maxPulseWidth) { if ( #ifdef G_E_THOMAS _rx == 1 #else _rx == 0 #endif ) { _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(); #ifdef G_E_THOMAS if ((_state == DECODE) && (_rx == 1)) #else if ((_state == DECODE) && (_rx == 0)) #endif { _error = "rx timeout"; _state = IDLE; // Reception successful } else { _error = "reception error"; _state = ERROR; // Reception incomplete } }