Manchester
Manchester.cpp
- Committer:
- hudakz
- Date:
- 2018-10-14
- Revision:
- 8:c1b5893191fe
- Parent:
- 7:afd0ee36dcd1
- Child:
- 9:7a23184aa9ef
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
}
}