This library implements RC6 (protocol used by some philips remotes). It has a receiver class and a transmitter class. Currently, only mode 0 is supported. It is tested with my DVD/HDD recorder.

Rc6.cpp

Committer:
hilgo
Date:
2011-02-27
Revision:
0:f8e2ed766064

File content as of revision 0:f8e2ed766064:

/* mbed RC6 (Philips remote control protocol) library
 * Copyright (c) 2011 Jeroen Hilgers
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "Rc6.h"

Rc6Transmitter::Rc6Transmitter(PinName irPin) :
    mOut(irPin)
{

    mBusy = false;
}

void Rc6Transmitter::Send(uint32_t code)
{
    if(mBusy)
    {
        printf("Busy");
        return;
    }
    mBusy = true;        
        
    uint8_t index = 0;
    uint8_t level;
    uint16_t time;
    uint8_t bit;
    // Leader
    mPattern[index++] = 6*16; // Mark
    mPattern[index++] = 2*16; // Pause
    // Start bit 3.
    mPattern[index++] = 16;
    mPattern[index++] = 2*16;
    // Start bit 2.
    mPattern[index++] = 16;
    // Start bit 1.
    mPattern[index++] = 16;
    mPattern[index++] = 16;
    // Start bit 0.
    mPattern[index++] = 16;
    // Toggle bit.
    if(code & 0x10000) 
    {
        mPattern[index++] = 3*16;
        level = 0;
        time = 2*16;
    }
    else
    {
        mPattern[index++] = 16;
        mPattern[index++] = 2*16;
        level = 1;
        time = 2*16;
    }
    for(bit = 0; bit < 16; bit ++)
    {
        if(code & 0x8000)
        {   // Send '1'
            if(level) 
            {
               mPattern[index++] = 16+time;
            }   
            else
            {
                mPattern[index++] = time;
                mPattern[index++] = 16;
            }
            level = 0;
            time = 16;
        }
        else
        {   // Send '0'
            if(level) 
            {
               mPattern[index++] = time;
               mPattern[index++] = 16;
            }   
            else
            {
               mPattern[index++] = 16+time;
            }
            level = 1;
            time = 16;
        }
        code <<= 1;
    }
    if(level == 1)
    {
        mPattern[index++] = time;
    }
    mPattern[index++] = 16; // Teminate with half symbol (not long enough?)
    
    mPattern[index++] = 0; // Mark duration of 0 means end-of-pattern.
    mPattern[index++] = 0;
 
    // Setup transmitter variables...
    mMark = 0;  
    mPause = 0;
    mPatPtr = mPattern;  
    
    // ... and attach ticker.
    // mTicker.attach(this, &Rc6Sender::Tick, 1.0/(2.0*36000.0)); // This rounds down to:
    // mTicker.attach_us(this, &Rc6Sender::Tick, 13); // Both are noticeable too slow compared to the real remote!
    mTicker.attach_us(this, &Rc6Transmitter::Tick, 14); // This does work.
}

bool Rc6Transmitter::IsBusy()
{
    return mBusy;
}

void Rc6Transmitter::Tick()
{
    if(mMark)
    { // Busy sending a mark. Do next part.
      mMark--;
      if(mMark&1)
      {
        mOut = 1;
      }
      else
      {
        mOut = 0;
      }
    }
    else
    { 
        if(mPause)
        {  // Need to do some more pause.
           mPause--;
        }
        else
        { // Last half cycle of pause. Check next activity.
          mMark = (*mPatPtr++)<<1; // Half-cycles of marker.
          mPause = (*mPatPtr++)<<1; // half-cycles of pause.
          mPause--; // The last half-cycle of puase is spend here setting up next cycle.
          if(mMark == 0)
          {
            // We are done. Stop ticker.
            mTicker.detach();
            mBusy = false;
          }
        }
    }
}

Rc6Receiver::Rc6Receiver(PinName irIn) :
    irq(irIn)
{
    // NOTE: Ir sensor inversion is handled here!
    irq.rise(this, &Rc6Receiver::OnFall);
    irq.fall(this, &Rc6Receiver::OnRise);
    mBusy = false;
    mLastCode = 0xFFFFFFFF;
}

uint32_t Rc6Receiver::Receive()
{
    // TODO: Disable interrupts to prevent one in a million chance that
    // mLastCode gets updated...
    uint32_t code = mLastCode;
    // ... at exactly this moment, so it will be discarded here:
    mLastCode = 0xFFFFFFFF;
    return code;
}

void Rc6Receiver::OnRise()
{
    if(mBusy) 
    {
        // Rising edges are not interesting; they correspond to '0'.
    }
    else
    {
        mBusy = true;
        mCode = 0;
        mTimer.start();
        mTimeout.attach_us(this, &Rc6Receiver::OnTimeout, 30000);  // 52T of 16/36000 sec = 23ms
    }
}

void Rc6Receiver::OnFall()
{
    if(mBusy)
    {
        // Express edge location in 'T' (444.444 us)
        int32_t edgeId = (mTimer.read_us()*1000 + 222222)/444444;
        // printf("%d %d\r\n", mTimer.read_us(), edgeId); // Debug print. Doesn't mess up timing as hard as expected :-)
        
        // Toggle bit is at edge 18. Store it!
        if(edgeId == 18)
            mCode |= 0x10000;
        // Address and data bits.
        if((edgeId & 0x01) && (edgeId > 21) && (edgeId < 52))
        {
            uint8_t bit = 15 - (edgeId-21)/2;
            mCode |= 1<<bit;
        }
    }
}

void Rc6Receiver::OnTimeout()
{
    mTimer.stop();
    mTimer.reset();
    mBusy = false;
    mLastCode = mCode;
}