Manchester code (phase encoding) library.

Dependents:   Manchester_Transmitter Manchester_Receiver

Manchester code (phase encoding) library

It 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 are synchronization pulses (the number can be set) at the begin of transmission

    Select a convention to be used by commenting or uncommenting the line below in the Manchester.h header file.

Manchester.h

#define G_E_THOMAS 1

The IEEE 802.3 convention is used by default.

A Manchester encoded message (using G.E. Thomas' convention), with one sync pulse in the preamble, carrying four bytes:

/media/uploads/hudakz/manchester01.png

ACKNOWLEDGEMENT: The code in this library was based on this article published by Robert Guastella.

Import programManchester_Transmitter

Manchester transmitter demo.


Import programManchester_Receiver

Manchester receiver demo.

NOTE: To perform a simple test (without radio modules) connect the txPin on transmitter board to the rxPin on the receiver board and make sure that grounds are also connected one another.

Manchester.cpp

Committer:
hudakz
Date:
2017-05-17
Revision:
2:de778df5892c
Parent:
1:11292d238e50
Child:
3:03109c995123

File content as of revision 2:de778df5892c:

/*
 ******************************************************************************
 * @file    Manchester.cpp
 * @author  Zoltan Hudak
 * @version
 * @date    16-May-2017
 * @brief   Manchester code for mbed
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; 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 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;
        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 rxPin 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
}