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:
2018-10-14
Revision:
8:c1b5893191fe
Parent:
7:afd0ee36dcd1

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>&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/>.
 */
/*
   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
        }
}