Manchester
Manchester.cpp
- Committer:
- hudakz
- Date:
- 2017-05-21
- Revision:
- 5:3b2c7e9fda3f
- Parent:
- 4:f2c392191c74
- Child:
- 6:7454ad91f714
File content as of revision 5:3b2c7e9fda3f:
/*
******************************************************************************
* @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/>.
*/
/*
This library implements Manchester code according to both IEEE 802.3
and G.E. Thomas' convention.
• 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 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:
#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 '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;
#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 '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
* @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; // End of transmission
else
_state = ERROR; // Incomplete transmission
}