Manchester
Diff: Manchester.cpp
- Revision:
- 0:d5c75b0e5708
- Child:
- 1:11292d238e50
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Manchester.cpp Wed May 17 07:52:15 2017 +0000 @@ -0,0 +1,272 @@ +/* + ****************************************************************************** + * @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 the rxTicker + * @param + * @retval + */ +void Manchester::transmission(void) { + static uint8_t encodeByte; + static uint32_t byteIndex; + static uint8_t bitIndex; + + 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 hight 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; // transition is complete, bring line high + _state = IDLE; + break; + + case IDLE: + default: + return; + } +} + +/** + * @brief Receives message + * @note + * @param msg A 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 to handle reception + * @note Called on 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 == 0) { + _state = SYNCH_START; + _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2); + } + else + _state = ERROR; + break; + + case SYNCH_START: + pulseWidth = now - begin; + if((pulseWidth > _maxPulseWidth) | (_rx == 0)) + _state = ERROR; + else { + begin = now; + decodeByte = 0; + _len = 0; + bitIndex = 0; + _state = DECODE; + _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2); + } + 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; + } + else + _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2); + + break; + + case IDLE: + case ERROR: + default: + break; + } +} + +/** + * @brief ISR to handle receive timeout + * @note 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) { + _rxTimeout.detach(); + + if(_state == DECODE) + _state = IDLE; // End of transmission + else + _state = ERROR; // Incomplete transmission +} +