forked RemoteIR

Fork of RemoteIR by Shinichiro Nakamura

Files at this revision

API Documentation at this revision

Comitter:
shintamainjp
Date:
Tue Aug 17 08:50:54 2010 +0000
Child:
1:4adf16017a0f
Commit message:

Changed in this revision

ReceiverIR.cpp Show annotated file Show diff for this revision Revisions of this file
ReceiverIR.h Show annotated file Show diff for this revision Revisions of this file
RemoteIR.h Show annotated file Show diff for this revision Revisions of this file
Semaphore.h Show annotated file Show diff for this revision Revisions of this file
TransmitterIR.cpp Show annotated file Show diff for this revision Revisions of this file
TransmitterIR.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ReceiverIR.cpp	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,292 @@
+/**
+ * IR receiver (Version 0.0.3)
+ *
+ * Copyright (C) 2010 Shinichiro Nakamura (CuBeatSystems)
+ * http://shinta.main.jp/
+ */
+
+#include "ReceiverIR.h"
+
+#define IRQ_ENABLE()    sem.release(); __enable_irq()
+#define IRQ_DISABLE()   sem.take(); __disable_irq()
+
+#define InRange(x,y)   ((((y) * 0.7) < (x)) && ((x) < ((y) * 1.3)))
+
+ReceiverIR::ReceiverIR(PinName rxpin) : evt(rxpin) {
+}
+
+ReceiverIR::~ReceiverIR() {}
+
+void ReceiverIR::init(void) {
+    IRQ_DISABLE();
+    evt.fall(this, &ReceiverIR::isr_fall);
+    evt.rise(this, &ReceiverIR::isr_rise);
+    evt.mode(PullUp);
+    ticker.attach_us(this, &ReceiverIR::isr_wdt, 10 * 1000);
+    init_state();
+    IRQ_ENABLE();
+}
+
+ReceiverIR::State ReceiverIR::getState() {
+    IRQ_DISABLE();
+    State s = data.state;
+    IRQ_ENABLE();
+    wait_ms(100);
+    return s;
+}
+
+int ReceiverIR::getData(RemoteIR::Format *format, uint8_t *buf, int bufsiz) {
+    IRQ_DISABLE();
+
+    const int bitcount = data.bitcount;
+    const int n = bitcount / 8 + (((bitcount % 8) != 0) ? 1 : 0);
+    if (bufsiz < n) {
+        IRQ_ENABLE();
+        return -1;
+    }
+
+    *format = data.format;
+    for (int i = 0; i < n; i++) {
+        buf[i] = data.buffer[i];
+    }
+
+    init_state();
+
+    IRQ_ENABLE();
+    return bitcount;
+}
+
+void ReceiverIR::init_state(void) {
+    work.c1 = -1;
+    work.c2 = -1;
+    work.c3 = -1;
+    work.d1 = -1;
+    work.d2 = -1;
+    data.state = Idle;
+    data.format = RemoteIR::UNKNOWN;
+    data.bitcount = 0;
+    timer.stop();
+    timer.reset();
+    for (int i = 0; i < sizeof(data.buffer); i++) {
+        data.buffer[i] = 0;
+    }
+}
+
+void ReceiverIR::isr_wdt(void) {
+    IRQ_DISABLE();
+    static int cnt = 0;
+    if ((Idle != data.state) || ((0 <= work.c1) || (0 <= work.c2) || (0 <= work.c3) || (0 <= work.d1) || (0 <= work.d2))) {
+        cnt++;
+        if (cnt > 50) {
+#if 0
+            printf("# WDT [c1=%d, c2=%d, c3=%d, d1=%d, d2=%d, state=%d, format=%d, bitcount=%d]\n",
+                   work.c1,
+                   work.c2,
+                   work.c3,
+                   work.d1,
+                   work.d2,
+                   data.state,
+                   data.format,
+                   data.bitcount);
+#endif
+            init_state();
+            cnt = 0;
+        }
+    } else {
+        cnt = 0;
+    }
+    IRQ_ENABLE();
+}
+
+void ReceiverIR::isr_fall(void) {
+    IRQ_DISABLE();
+    switch (data.state) {
+        case Idle:
+            if (work.c1 < 0) {
+                timer.start();
+                work.c1 = timer.read_us();
+            } else {
+                work.c3 = timer.read_us();
+                int a = work.c2 - work.c1;
+                int b = work.c3 - work.c2;
+                if (InRange(a, TUS_NEC * 16) && InRange(b, TUS_NEC * 8)) {
+                    /*
+                     * NEC.
+                     */
+                    data.format = RemoteIR::NEC;
+                    data.state = Receiving;
+                    data.bitcount = 0;
+                } else if (InRange(a, TUS_NEC * 16) && InRange(b, TUS_NEC * 4)) {
+                    /*
+                     * NEC Repeat.
+                     */
+                    data.format = RemoteIR::NEC;
+                    data.state = Received;
+                    data.bitcount = 0;
+                } else if (InRange(a, TUS_AEHA * 8) && InRange(b, TUS_AEHA * 4)) {
+                    /*
+                     * AEHA.
+                     */
+                    data.format = RemoteIR::AEHA;
+                    data.state = Receiving;
+                    data.bitcount = 0;
+                } else if (InRange(a, TUS_AEHA * 8) && InRange(b, TUS_AEHA * 8)) {
+                    /*
+                     * AEHA Repeat.
+                     */
+                    data.format = RemoteIR::AEHA;
+                    data.state = Received;
+                    data.bitcount = 0;
+                } else {
+                    init_state();
+                }
+            }
+            break;
+        case Receiving:
+            if (RemoteIR::NEC == data.format) {
+                work.d2 = timer.read_us();
+                int a = work.d2 - work.d1;
+                if (InRange(a, TUS_NEC * 3)) {
+                    data.buffer[data.bitcount / 8] |= (1 << (data.bitcount % 8));
+                } else if (InRange(a, TUS_NEC * 1)) {
+                    data.buffer[data.bitcount / 8] &= ~(1 << (data.bitcount % 8));
+                }
+                data.bitcount++;
+#if 1
+                /*
+                 * Length of NEC is always 32 bits.
+                 */
+                if (32 <= data.bitcount) {
+                    data.state = Received;
+                    work.c1 = -1;
+                    work.c2 = -1;
+                    work.c3 = -1;
+                    work.d1 = -1;
+                    work.d2 = -1;
+                }
+#else
+                /*
+                 * Set timeout for tail detection automatically.
+                 */
+                timeout.detach();
+                timeout.attach_us(this, &ReceiverIR::isr_timeout, TUS_NEC * 12);
+#endif
+            } else if (RemoteIR::AEHA == data.format) {
+                work.d2 = timer.read_us();
+                int a = work.d2 - work.d1;
+                if (InRange(a, TUS_AEHA * 3)) {
+                    data.buffer[data.bitcount / 8] |= (1 << (data.bitcount % 8));
+                } else if (InRange(a, TUS_AEHA * 1)) {
+                    data.buffer[data.bitcount / 8] &= ~(1 << (data.bitcount % 8));
+                }
+                data.bitcount++;
+#if 1
+                /*
+                 * Typical length of AEHA is 48 bits.
+                 * Please check a specification of your remote controller if you find a problem.
+                 */
+                if (48 <= data.bitcount) {
+                    data.state = Received;
+                    work.c1 = -1;
+                    work.c2 = -1;
+                    work.c3 = -1;
+                    work.d1 = -1;
+                    work.d2 = -1;
+                }
+#else
+                /*
+                 * Set timeout for tail detection automatically.
+                 */
+                timeout.detach();
+                timeout.attach_us(this, &ReceiverIR::isr_timeout, TUS_AEHA * 12);
+#endif
+            } else if (RemoteIR::SONY == data.format) {
+                work.d1 = timer.read_us();
+            }
+            break;
+        case Received:
+            break;
+        default:
+            break;
+    }
+    IRQ_ENABLE();
+}
+
+void ReceiverIR::isr_rise(void) {
+    IRQ_DISABLE();
+    switch (data.state) {
+        case Idle:
+            if (0 <= work.c1) {
+                work.c2 = timer.read_us();
+                int a = work.c2 - work.c1;
+                if (InRange(a, TUS_SONY * 4)) {
+                    data.format = RemoteIR::SONY;
+                    data.state = Receiving;
+                    data.bitcount = 0;
+                } else {
+                    static const int MINIMUM_LEADER_WIDTH = 150;
+                    if (a < MINIMUM_LEADER_WIDTH) {
+                        init_state();
+                    }
+                }
+            } else {
+                init_state();
+            }
+            break;
+        case Receiving:
+            if (RemoteIR::NEC == data.format) {
+                work.d1 = timer.read_us();
+            } else if (RemoteIR::AEHA == data.format) {
+                work.d1 = timer.read_us();
+            } else if (RemoteIR::SONY == data.format) {
+                work.d2 = timer.read_us();
+                int a = work.d2 - work.d1;
+                if (InRange(a, TUS_SONY * 2)) {
+                    data.buffer[data.bitcount / 8] |= (1 << (data.bitcount % 8));
+                } else if (InRange(a, TUS_SONY * 1)) {
+                    data.buffer[data.bitcount / 8] &= ~(1 << (data.bitcount % 8));
+                }
+                data.bitcount++;
+#if 1
+                /*
+                 * How do we get the correct length? (6bits, 12bits, 15bits, 20bits...)
+                 * By a model only?
+                 * Please check a specification of your remote controller if you find a problem.
+                 */
+                if (12 <= data.bitcount) {
+                    data.state = Received;
+                    work.c1 = -1;
+                    work.c2 = -1;
+                    work.c3 = -1;
+                    work.d1 = -1;
+                    work.d2 = -1;
+                }
+#else
+                /*
+                 * Set timeout for tail detection automatically.
+                 */
+                timeout.detach();
+                timeout.attach_us(this, &ReceiverIR::isr_timeout, TUS_SONY * 10);
+#endif
+            }
+            break;
+        case Received:
+            break;
+        default:
+            break;
+    }
+    IRQ_ENABLE();
+}
+
+void ReceiverIR::isr_timeout(void) {
+    IRQ_DISABLE();
+    if (data.state == Receiving) {
+        data.state = Received;
+        work.c1 = -1;
+        work.c2 = -1;
+        work.c3 = -1;
+        work.d1 = -1;
+        work.d2 = -1;
+    }
+    IRQ_ENABLE();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ReceiverIR.h	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,106 @@
+/**
+ * IR receiver (Version 0.0.3)
+ *
+ * Copyright (C) 2010 Shinichiro Nakamura (CuBeatSystems)
+ * http://shinta.main.jp/
+ */
+
+#ifndef _RECEIVER_IR_H_
+#define _RECEIVER_IR_H_
+
+#include <mbed.h>
+
+#include "Semaphore.h"
+#include "RemoteIR.h"
+
+/**
+ * IR receiver class.
+ */
+class ReceiverIR {
+public:
+
+    /**
+     * Constructor.
+     *
+     * @param rxpin Pin for receive IR signal.
+     */
+    explicit ReceiverIR(PinName rxpin);
+    
+    /**
+     * Destructor.
+     */
+    ~ReceiverIR();
+
+    /**
+     * State.
+     */
+    typedef enum {
+        Idle,
+        Receiving,
+        Received
+    } State;
+    
+    void init(void);
+
+    /**
+     * Get state.
+     *
+     * @return Current state.
+     */
+    State getState();
+    
+    /**
+     * Get data.
+     *
+     * @param format Pointer to format.
+     * @param buf Buffer of data.
+     * @param bufsiz Buffer byte size.
+     *
+     * @return Data bit length.
+     */
+    int getData(RemoteIR::Format *format, uint8_t *buf, int bufsiz);
+    
+private:
+    
+    typedef struct {
+        State state;
+        RemoteIR::Format format;
+        int bitcount;
+        uint8_t buffer[64];
+    } data_t;
+    
+    typedef struct {
+        int c1;
+        int c2;
+        int c3;
+        int d1;
+        int d2;
+    } work_t;
+
+    static const int TUS_NEC = 562;
+    static const int TUS_AEHA = 425;
+    static const int TUS_SONY = 600;
+
+    InterruptIn evt;    /**< Interrupt based input for input. */
+    Timer timer;        /**< Timer for WDT. */
+    Ticker ticker;      /**< Tciker for tick. */
+    Semaphore sem;      /**< Semaphore for data. */
+    Timeout timeout;    /**< Timeout for tail. */
+
+    data_t data;
+    work_t work;
+    
+    void init_state(void);
+
+    void isr_wdt(void);
+    void isr_fall(void);
+    void isr_rise(void);
+    
+    /**
+     * ISR timeout for tail detection.
+     */
+    void isr_timeout(void);
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RemoteIR.h	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,25 @@
+/**
+ * IR remote common class (Version 0.0.3)
+ *
+ * Copyright (C) 2010 Shinichiro Nakamura (CuBeatSystems)
+ * http://shinta.main.jp/
+ */
+
+#ifndef _REMOTE_IR_H_
+#define _REMOTE_IR_H_
+
+class RemoteIR {
+public:
+
+    typedef enum {
+        UNKNOWN,
+        NEC,
+        AEHA,
+        SONY
+    } Format;
+
+private:
+    RemoteIR();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Semaphore.h	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,32 @@
+#ifndef _SEMAPHORE_H_
+#define _SEMAPHORE_H_
+
+/*
+ * http://mbed.org/forum/mbed/topic/181/#comment-799
+ */
+
+class Semaphore {
+public:
+    Semaphore(): s(SemFree) {}
+
+    bool take(bool block = true) {
+        int oldval;
+        do {
+            oldval = __ldrex(&s);
+        } while ((block && oldval == SemTaken) || __strex(SemTaken, &s) != 0);
+        if (!block) {
+            __clrex();
+        }
+        return (oldval == SemFree);
+    }
+
+    void release() {
+        s = SemFree;
+    }
+
+private:
+    enum { SemFree, SemTaken };
+    int s;
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TransmitterIR.cpp	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,281 @@
+/**
+ * IR transmitter (Version 0.0.3)
+ *
+ * Copyright (C) 2010 Shinichiro Nakamura (CuBeatSystems)
+ * http://shinta.main.jp/
+ */
+
+#include "TransmitterIR.h"
+
+#define IRQ_ENABLE()    sem.release(); __enable_irq()
+#define IRQ_DISABLE()   sem.take(); __disable_irq()
+
+TransmitterIR::TransmitterIR(PinName txpin) : tx(txpin) {
+}
+
+TransmitterIR::~TransmitterIR() {}
+
+void TransmitterIR::init(void) {
+    tx.write(0.0);
+    tx.period_us(26.3);
+
+    work.state = Idle;
+    work.bitcount = 0;
+    work.leader = 0;
+    work.data = 0;
+    work.trailer = 0;
+
+    data.format = RemoteIR::UNKNOWN;
+    data.bitlength = 0;
+}
+
+TransmitterIR::State TransmitterIR::getState(void) {
+    IRQ_DISABLE();
+    State s = work.state;
+    IRQ_ENABLE();
+    wait_ms(100);
+    return s;
+}
+
+int TransmitterIR::setData(RemoteIR::Format format, uint8_t *buf, int bitlength) {
+    IRQ_DISABLE();
+    if (work.state != Idle) {
+        IRQ_ENABLE();
+        return -1;
+    }
+
+    work.state = Leader;
+    work.bitcount = 0;
+    work.leader = 0;
+    work.data = 0;
+    work.trailer = 0;
+
+    data.format = format;
+    data.bitlength = bitlength;
+    const int n = bitlength / 8 + (((bitlength % 8) != 0) ? 1 : 0);
+    for (int i = 0; i < n; i++) {
+        data.buffer[i] = buf[i];
+    }
+
+    switch (format) {
+        case RemoteIR::NEC:
+            ticker.detach();
+            ticker.attach_us(this, &TransmitterIR::tick, TUS_NEC);
+            break;
+        case RemoteIR::AEHA:
+            ticker.detach();
+            ticker.attach_us(this, &TransmitterIR::tick, TUS_AEHA);
+            break;
+        case RemoteIR::SONY:
+            ticker.detach();
+            ticker.attach_us(this, &TransmitterIR::tick, TUS_SONY);
+            break;
+    }
+
+    IRQ_ENABLE();
+    return bitlength;
+}
+
+void TransmitterIR::tick(void) {
+    IRQ_DISABLE();
+    switch (work.state) {
+        case Idle:
+            work.bitcount = 0;
+            work.leader = 0;
+            work.data = 0;
+            work.trailer = 0;
+            break;
+        case Leader:
+            if (data.format == RemoteIR::NEC) {
+                /*
+                 * NEC.
+                 */
+                static const int LEADER_NEC_HEAD = 16;
+                static const int LEADER_NEC_TAIL = 8;
+                if (work.leader < LEADER_NEC_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.leader++;
+                if ((LEADER_NEC_HEAD + LEADER_NEC_TAIL) <= work.leader) {
+                    work.state = Data;
+                }
+            } else if (data.format == RemoteIR::AEHA) {
+                /*
+                 * AEHA.
+                 */
+                static const int LEADER_AEHA_HEAD = 8;
+                static const int LEADER_AEHA_TAIL = 4;
+                if (work.leader < LEADER_AEHA_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.leader++;
+                if ((LEADER_AEHA_HEAD + LEADER_AEHA_TAIL) <= work.leader) {
+                    work.state = Data;
+                }
+            } else if (data.format == RemoteIR::SONY) {
+                /*
+                 * SONY.
+                 */
+                static const int LEADER_SONY_HEAD = 4;
+                static const int LEADER_SONY_TAIL = 0;
+                if (work.leader < LEADER_SONY_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.leader++;
+                if ((LEADER_SONY_HEAD + LEADER_SONY_TAIL) <= work.leader) {
+                    work.state = Data;
+                }
+            } else {
+            }
+            break;
+        case Data:
+            if (data.format == RemoteIR::NEC) {
+                /*
+                 * NEC.
+                 */
+                if (work.data == 0) {
+                    tx.write(0.5);
+                    work.data++;
+                } else {
+                    tx.write(0.0);
+                    if (0 != (data.buffer[work.bitcount / 8] & (1 << work.bitcount % 8))) {
+                        if (3 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    } else {
+                        if (1 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    }
+                }
+                if (data.bitlength <= work.bitcount) {
+                    work.state = Trailer;
+                }
+            } else if (data.format == RemoteIR::AEHA) {
+                /*
+                 * AEHA.
+                 */
+                if (work.data == 0) {
+                    tx.write(0.5);
+                    work.data++;
+                } else {
+                    tx.write(0.0);
+                    if (0 != (data.buffer[work.bitcount / 8] & (1 << work.bitcount % 8))) {
+                        if (3 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    } else {
+                        if (1 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    }
+                }
+                if (data.bitlength <= work.bitcount) {
+                    work.state = Trailer;
+                }
+            } else if (data.format == RemoteIR::SONY) {
+                /*
+                 * SONY.
+                 */
+                if (work.data == 0) {
+                    tx.write(0.0);
+                    work.data++;
+                } else {
+                    tx.write(0.5);
+                    if (0 != (data.buffer[work.bitcount / 8] & (1 << work.bitcount % 8))) {
+                        if (2 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    } else {
+                        if (1 <= work.data) {
+                            work.bitcount++;
+                            work.data = 0;
+                        } else {
+                            work.data++;
+                        }
+                    }
+                }
+                if (data.bitlength <= work.bitcount) {
+                    work.state = Trailer;
+                }
+            } else {
+            }
+            break;
+        case Trailer:
+            if (data.format == RemoteIR::NEC) {
+                /*
+                 * NEC.
+                 */
+                static const int TRAILER_NEC_HEAD = 1;
+                static const int TRAILER_NEC_TAIL = 2;
+                if (work.trailer < TRAILER_NEC_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.trailer++;
+                if ((TRAILER_NEC_HEAD + TRAILER_NEC_TAIL) <= work.trailer) {
+                    work.state = Idle;
+                    //ticker.detach();
+                }
+            } else if (data.format == RemoteIR::AEHA) {
+                /*
+                 * AEHA.
+                 */
+                static const int TRAILER_AEHA_HEAD = 1;
+                static const int TRAILER_AEHA_TAIL = 8000 / TUS_AEHA;
+                if (work.trailer < TRAILER_AEHA_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.trailer++;
+                if ((TRAILER_AEHA_HEAD + TRAILER_AEHA_TAIL) <= work.trailer) {
+                    work.state = Idle;
+                    //ticker.detach();
+                }
+            } else if (data.format == RemoteIR::SONY) {
+                /*
+                 * SONY.
+                 */
+                static const int TRAILER_SONY_HEAD = 0;
+                static const int TRAILER_SONY_TAIL = 0;
+                if (work.trailer < TRAILER_SONY_HEAD) {
+                    tx.write(0.5);
+                } else {
+                    tx.write(0.0);
+                }
+                work.trailer++;
+                if ((TRAILER_SONY_HEAD + TRAILER_SONY_TAIL) <= work.trailer) {
+                    work.state = Idle;
+                    //ticker.detach();
+                }
+            } else {
+            }
+            break;
+        default:
+            break;
+    }
+    IRQ_ENABLE();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TransmitterIR.h	Tue Aug 17 08:50:54 2010 +0000
@@ -0,0 +1,62 @@
+/**
+ * IR transmitter (Version 0.0.3)
+ *
+ * Copyright (C) 2010 Shinichiro Nakamura (CuBeatSystems)
+ * http://shinta.main.jp/
+ */
+
+#ifndef _TRANSMITTER_IR_H_
+#define _TRANSMITTER_IR_H_
+
+#include <mbed.h>
+
+#include "Semaphore.h"
+#include "RemoteIR.h"
+
+class TransmitterIR {
+public:
+    explicit TransmitterIR(PinName txpin);
+    ~TransmitterIR();
+
+    typedef enum {
+        Idle,
+        Leader,
+        Data,
+        Trailer
+    } State;
+
+    void init(void);
+    State getState(void);
+    int setData(RemoteIR::Format format, uint8_t *buf, int bitlength);
+
+private:
+
+    typedef struct {
+        State state;
+        int bitcount;
+        int leader;
+        int data;
+        int trailer;
+    } work_t;
+
+    typedef struct {
+        RemoteIR::Format format;
+        int bitlength;
+        uint8_t buffer[64];
+    } data_t;
+
+    PwmOut tx;
+    Ticker ticker;
+    data_t data;
+    work_t work;
+    Semaphore sem;
+
+    static const int TUS_NEC = 562;
+    static const int TUS_AEHA = 425;
+    static const int TUS_SONY = 600;
+
+    void tick();
+
+};
+
+#endif
\ No newline at end of file