Receiver interface for the fischertechnik IR control set

Dependencies:   NeedfulThings

Receiver interface for the fischertechnik IR control set.

An mbed port of the code found on this ft community WIKI page The ft IR remote control uses some kind of RC-MM Protocol . When any of the controls is applied the remote control sends updates at a rate of at 1/120ms or 1/90ms depending on the senders frequency setting. Each message delivers the complete remote control status encoded into 30 bits. The structure of the message can be seen in FtControlSetMessage.h.

I assume that the ft control set works fine with standard 38kHz IR detectors. I have used a CHQ0038 from a broken DVD Player. It can be connected directly to the mbed: GND, VCC->3.3V and the signal line to any of the numbered mbed gpios.

The PDM timing of the ft IR control set isn't that exact. Thus receive errors occur quite frequently. I am observing an error rate somewhere between 3% and 5%. This driver "ignores" up to 3 consecutive receive errors, i.e. it just indicates an error but still provides the last correctly received message, before it resets the message to zero after the fourth error.

FtControlSetReceiver.cpp

Committer:
humlet
Date:
2013-03-25
Revision:
6:3cce31050c90
Parent:
5:7ce37fae13ff

File content as of revision 6:3cce31050c90:

#include "FtControlSetReceiver.h"

#include "us_ticker_api.h"

/// message timeout depends on selected sender frequency
const uint32_t FtControlSetReceiver::c_messageTimeout[2] = {89000+20*c_maxPulseTime,118000+20*c_maxPulseTime};
/// di-bit discrimination times
const uint32_t FtControlSetReceiver::c_pulseWidthLimits[5] = {740,860,940,1055,1150};

FtControlSetReceiver::FtControlSetReceiver(PinName in):
    m_pulseIRQ(in),
    m_rxBuffer(0),
    m_nPulses(0),
    m_timeOfLastEdge(0),
    m_nOnes(0),
    m_errorCount(0),
    m_nGracefullyIgnoredErrors(0),
    m_lastMessage(0)
{
    m_pulseIRQ.mode(PullNone); // the CHQ0038 does't like the default pull down
    m_pulseIRQ.fall(this, &FtControlSetReceiver::pulseISR); // we are measuring the distances of falling edges

    //  attch the callbacks 
    m_messageTimeout.attach(this,&FtControlSetReceiver::messageTimeoutISR);
    m_receiveTimeout.attach(this,&FtControlSetReceiver::handleReceiveError);
    m_recoverTimeout.attach(this,&FtControlSetReceiver::recoverISR);
}

/// called on each falling edge on the IR receivers signal line
void FtControlSetReceiver::pulseISR()
{
    // takes 1µs-5µs, 4.1µs on average
    if(m_nPulses) {
        // received a messge pulse
        uint32_t now = us_ticker_read();
        m_receiveTimeout.remove();
        uint32_t width = now - m_timeOfLastEdge;
        m_timeOfLastEdge = now;
        if(width>=c_pulseWidthLimits[0] && width<c_pulseWidthLimits[4]) {
            if(width<c_pulseWidthLimits[1]){
                // m_rxBuffer|=0x0;
            }else if(width<c_pulseWidthLimits[2]) {
                m_rxBuffer|=0x1;
                ++m_nOnes;
            } else if(width<c_pulseWidthLimits[3]) {
                m_rxBuffer|=0x2;
                ++m_nOnes;
            } else {
                m_rxBuffer|=0x3;
            }
            m_rxBuffer<<=2;
            if(++m_nPulses<=15) {
                // still receiving re-attach the pulse timeout
                m_receiveTimeout.insert(m_timeOfLastEdge+c_maxPulseTime);
            } else {
                // we have got a full message
                FtControlSetMessage::Status status;
                if(m_nOnes&1) { // message ones + parity one has to be even
                    status = FtControlSetMessage::ParityKO;
                } else if(m_rxBuffer>>28!=8 || m_rxBuffer&4) { // check header=8 and tail=0
                    status = FtControlSetMessage::FrameKO;
                } else { // happyhappyjoyjoy
                    status = FtControlSetMessage::OK;
                }
                // detach and reattach the message timeout
                m_messageTimeout.remove();
                m_messageTimeout.insert(m_timeOfLastEdge+c_messageTimeout[1]);//just take the longer timeout here, otherwise we run into problems
                if(status == FtControlSetMessage::OK) {
                    m_nGracefullyIgnoredErrors = 0;
                    m_lastMessage = m_rxBuffer;
                } else {
                    if(++m_nGracefullyIgnoredErrors <= c_errorsToBeGracefullyIgnored) {
                        m_lastMessage &= ~0x3; // just indicate the error but keep the old message
                        m_lastMessage |= status;
                    } else {
                        m_lastMessage = status; // reset the message and indicate the error
                    }
                    ++m_errorCount;
                }
                m_callback.call();
                m_rxBuffer=0;
                m_nPulses=0;
                m_nOnes=0;
            }
        } else {
            handleReceiveError();
        }
    } else {
        // the very first edge of a potential message, just memorize time stamp
        m_timeOfLastEdge = us_ticker_read();
        m_receiveTimeout.insert(m_timeOfLastEdge+c_maxPulseTime);
        ++m_nPulses;
    }
}

void FtControlSetReceiver::messageTimeoutISR()
{
    m_lastMessage = 0;
    m_callback.call();
}

// called on errors detected in pulseISR or on pulse timeouts
void FtControlSetReceiver::handleReceiveError()
{
    // receive errors are not reported via the status bits and the message is not reset here,
    // because it might have been just a disturbance between two messages.
    // On severe transmission problems the message timeout will bail us out
    ++m_errorCount;
    //m_errorCount+=10000;
    m_rxBuffer=0;
    m_nPulses=0;
    m_nOnes=0;
    // to be graceful in case of high frequency noise disconnect the ISR for some time
    m_pulseIRQ.fall(0);
    m_recoverTimeout.insert(us_ticker_read()+c_recoverTimeout);
}

void FtControlSetReceiver::recoverISR()
{
    // reattach the pulse handler
    m_pulseIRQ.fall(this, &FtControlSetReceiver::pulseISR);
}