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
}