Manchester

Files at this revision

API Documentation at this revision

Comitter:
hudakz
Date:
Wed May 17 07:52:15 2017 +0000
Child:
1:11292d238e50
Commit message:
Initial issue.

Changed in this revision

Manchester.cpp Show annotated file Show diff for this revision Revisions of this file
Manchester.h Show annotated file Show diff for this revision Revisions of this file
ManchesterMsg.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Manchester.cpp	Wed May 17 07:52:15 2017 +0000
@@ -0,0 +1,272 @@
+/*
+ ******************************************************************************
+ * @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 uint32_t byteIndex;
+    static uint8_t  bitIndex;
+
+    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 hight 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;    // transition is complete, bring line high
+        _state = IDLE;
+        break;
+
+    case IDLE:
+    default:
+        return;
+    }
+}
+
+/**
+ * @brief   Receives message
+ * @note
+ * @param   msg A 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 to handle reception
+ * @note    Called on 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(_rx == 0) {
+            _state = SYNCH_START;
+            _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2);
+        }
+        else
+            _state = ERROR;
+        break;
+
+    case SYNCH_START:
+        pulseWidth = now - begin;
+        if((pulseWidth > _maxPulseWidth) | (_rx == 0))
+            _state = ERROR;
+        else {
+            begin = now;
+            decodeByte = 0;
+            _len = 0;
+            bitIndex = 0;
+            _state = DECODE;
+            _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2);
+        }
+        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;
+        }
+        else
+            _rxTimeout.attach_us(callback(this, &Manchester::rxTimeout), _maxPulseWidth * 2);
+
+        break;
+
+    case IDLE:
+    case ERROR:
+    default:
+        break;
+    }
+}
+
+/**
+ * @brief   ISR to handle receive timeout
+ * @note    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) {
+    _rxTimeout.detach();
+
+    if(_state == DECODE)
+        _state = IDLE;  // End of transmission
+    else
+        _state = ERROR; // Incomplete transmission
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Manchester.h	Wed May 17 07:52:15 2017 +0000
@@ -0,0 +1,81 @@
+/*
+ ******************************************************************************
+ * @file    Manchester.h
+ * @author  Zoltan Hudak
+ * @version 
+ * @date    16-May-2017
+ * @brief   Manchester code for ARMmbed
+ ******************************************************************************
+ * @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/>.
+ */
+ 
+#ifndef MANCHESTER_H
+#define MANCHESTER_H
+
+#include "mbed.h"
+#include "ManchesterMsg.h"
+
+class   Manchester
+{
+    enum State
+    {
+        IDLE,
+        SYNCH_START,
+        SYNCH_NEXT,
+        SYNCH_END,
+        SETUP,
+        TRANSITION,
+        COMPLETE,
+        LISTEN,
+        DECODE,
+        ERROR
+    };
+    
+public:
+    Manchester
+    (
+        PinName txPin,          /* transmitter pin name */
+        PinName rxPin,          /* receiver pin name */
+        uint32_t speed = 1500   /* speed in bits per second */, 
+        uint8_t tol = 20        /* pulse width tolerance (+/-) in % */ 
+    );
+    ~Manchester(void) { };
+    void    transmit(ManchesterMsg& msg);
+    bool    receive(ManchesterMsg& msg);
+    
+private:
+    DigitalOut  _tx;            // transmitter line
+    InterruptIn _rx;            // receiver line
+    Ticker      _txTicker;      // transmitter ticker
+    Timeout     _rxTimeout;     // receiver timeout
+    uint32_t    _midBitTime;    // mid-bit time [us]
+    uint32_t    _minPulseWidth; // minimum pulse width
+    uint32_t    _maxPulseWidth; // maximum pulse width
+    State       _state;         // state
+    char*       _data;          // data array
+    uint8_t     _len;           // data length in bytes
+    uint8_t     _maxLen;        // maximum length
+    
+    void        transmission(void);
+    void        reception(void);
+    void        rxTimeout(void);
+};
+#endif // MANCHESTER_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ManchesterMsg.h	Wed May 17 07:52:15 2017 +0000
@@ -0,0 +1,138 @@
+/*
+ ******************************************************************************
+ * @file    ManchesterMsg.h
+ * @author  Zoltan Hudak
+ * @version 
+ * @date    16-May-2017
+ * @brief   Message container
+ ******************************************************************************
+ * @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/>.
+ */
+  
+#ifndef MANCHESTERMSG_H
+#define MANCHESTERMSG_H
+
+//#define DEBUG 1
+
+/** ManchesterMsg class
+ */
+class   ManchesterMsg
+{
+    unsigned char   _max;   // Max length (capacity) of data field in bytes
+
+public:
+    char*           data;   // Data field
+    unsigned char   len;    // Length of data in bytes
+
+    /** Creates empty Manchester message of specified capacity.
+     */
+    ManchesterMsg(unsigned char max) :
+    _max(max),
+    data(new char[max])    { len = 0; }
+
+    ~ManchesterMsg(void)   { delete[] data; }
+
+     /** Copy constructor.
+     */
+    ManchesterMsg(const ManchesterMsg& msg) {
+        len     = msg.len;
+        memcpy(data, msg.data, msg.len);
+    }
+   
+    
+    /** Returns message maximum length (capacity)
+     */
+    unsigned char   maxLen(void)    { return _max; }
+
+    /** Clears message content
+     */
+    void            clear(void)     { len = 0; memset(data, 0, _max); }
+
+    /** Inserter operator: Appends data (value) to Manchester message
+     */
+    template<class T>
+    ManchesterMsg &operator<<(const T val) {
+        if(len + sizeof(T) <= _max) {
+            *reinterpret_cast <T*> (&data[len]) = val;
+            len += sizeof(T);
+        }
+
+#if DEBUG
+        else {
+            printf("Error: Cannot append data. Exceeding max data length!\r\n");
+        }
+#endif
+        return *this;
+    }
+
+    /** Inserter operator: Appends string of char to Manchester message
+     */
+    ManchesterMsg &operator<<(const char* str) {
+        unsigned char strLen = strlen(str);
+        if(len + strLen + 1 <= _max) {
+            memcpy(data + len, (char*)str, strLen);
+            len += strLen; 
+            data[len++] = '\0';
+        }
+
+#if DEBUG
+        else {
+            printf("Error: Cannot append data. Exceeding max data length!\r\n");
+        }
+#endif
+        return *this;
+    }
+
+    /** Extractor operator: Extracts data (value) from CAN message
+     */
+    template<class T>
+    ManchesterMsg &operator>>(T& val) {
+        if(sizeof(T) <= len) {
+            val = *reinterpret_cast <T*> (&data[0]);
+            len -= sizeof(T);
+            memcpy(data, data + sizeof(T), len);
+        }
+
+#if DEBUG
+        else {
+            printf("Error: Cannot extract data. Exceeding data length!\r\n");
+        }
+#endif
+        return *this;
+    }
+    /** Extractor operator: Extracts string of char from CAN message
+     */
+    ManchesterMsg &operator>>(char* str) {
+        unsigned char strLen = strlen(data);
+        if(strLen <= len) {
+            memcpy(str, data, strLen + 1);
+            len -= (strLen + 1);
+            memcpy(data, data + strLen + 1, len);
+        }
+
+#if DEBUG
+        else {
+            printf("Error: Cannot extract data. Exceeding data length!\r\n");
+        }
+#endif
+        return *this;
+    }
+};
+#endif // MANCHESTERMSG_H