Manchester
Manchester.cpp
- Committer:
- hudakz
- Date:
- 2017-05-18
- Revision:
- 3:03109c995123
- Parent:
- 2:de778df5892c
- Child:
- 4:f2c392191c74
File content as of revision 3:03109c995123:
/* ****************************************************************************** * @file Manchester.cpp * @author Zoltan Hudak * @version * @date 16-May-2017 * @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/>. */ #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 /* = 20% */ ) : _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)); } /** * @brief Transmits message * @note * @param msg Message to transmit * @retval */ void Manchester::transmit(ManchesterMsg& msg) { bool txComplete; _data = msg.data; _len = msg.len; _state = SYNCH_START; _txTicker.attach_us(callback(this, &Manchester::transmission), _midBitTime); do { core_util_critical_section_enter(); txComplete = (_state == IDLE); core_util_critical_section_exit(); } while(!txComplete); _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: _tx = 0; // pull the line low to start synch pulse _state = SYNCH_NEXT; break; case SYNCH_NEXT: _state = SYNCH_END; // synch pulse needs to be twice the interrupt rate break; case SYNCH_END: _tx = 1; // bring line high for end of sych pulse byteIndex = 0; encodeByte = _data[byteIndex]; bitIndex = 0; _state = SETUP; break; case SETUP: if(encodeByte & 0x01) _tx = (encodeByte & 0x01) | _tx; // next bit to transmit is a "1" else _tx = (encodeByte & 0x01) & _tx; // next bit to transmit is a "0" _state = TRANSITION; break; case TRANSITION: _tx = (encodeByte & 0x01) ^ 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 _state = COMPLETE; } break; case COMPLETE: _tx = 1; // transmission is complete, bring line high _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 None * @retval None */ void Manchester::txTimeout(void) { _timeout.detach(); _state = IDLE; } /** * @brief Receives message * @note Waits until a message is received * or receive timeout occured * @param msg Container to store the received message * @retval true On success * false Otherwise */ bool Manchester::receive(ManchesterMsg& msg) { bool rxFinished; uint32_t now = us_ticker_read(); _data = msg.data; _maxLen = msg.maxLen(); _state = LISTEN; core_util_critical_section_enter(); _rx.enable_irq(); core_util_critical_section_exit(); do { core_util_critical_section_enter(); rxFinished = ((_state == IDLE) || (_state == ERROR)); core_util_critical_section_exit(); } while(!rxFinished); core_util_critical_section_enter(); _rx.disable_irq(); core_util_critical_section_exit(); 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; if(_rx == 0) _state = SYNCH_START; else _state = ERROR; // It isn't a synch pulse => error break; case SYNCH_START: pulseWidth = now - begin; if((_minPulseWidth <= pulseWidth) && (pulseWidth <= _maxPulseWidth) && (_rx == 1)) { 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; decodeByte |= (((_rx == 0) & 0x01) << bitIndex++); 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 'receive timeout' * @note Called when receiver line is idle longer than limit. * Signals 'end of transmission' by setting state to IDLE * or 'timeout error' by setting state to ERROR. * @param None * @retval None */ void Manchester::rxTimeout(void) { _timeout.detach(); if((_state == DECODE) && (_rx == 1)) _state = IDLE; // End of transmission else _state = ERROR; // Incomplete transmission }