I2CRTOS Driver by Helmut Schmücker. Removed included mbed-rtos library to prevent multiple definition. Make sure to include mbed-rtos library in your program!

Fork of I2cRtosDriver by Helmut Schmücker

I2CDriver.cpp

Committer:
humlet
Date:
2013-05-09
Revision:
11:8c1d44595620
Parent:
10:e3d6c92ff222
Child:
13:530968937ccb

File content as of revision 11:8c1d44595620:

#include "I2CDriver.h"
#include "i2cRtos_api.h"
#include "error.h"

using namespace mbed;
using namespace rtos;

#define DRV_USR_SIG (1<<6)

const PinName I2CDriver::c_sdas[] = {p9,p28};
const PinName I2CDriver::c_scls[] = {p10,p27};

I2CDriver::Channel* I2CDriver::s_channels[2] = {0,0};

I2CDriver::I2CDriver(PinName sda, PinName scl, int hz, int slaveAdr):m_freq(hz),m_slaveAdr(slaveAdr)
{
    // check pins and determine i2c channel
    int channel=0;
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    if(sda==c_sdas[0] && scl==c_scls[0]) channel=0; // I2C_1
    else
#endif
        if (sda==c_sdas[1] && scl==c_scls[1]) channel=1; //I2C_2 or I2C
        else error("I2CDriver: Invalid I2C pins selected\n");

    if(s_channels[channel]==0) {
        new Thread(threadFun,(void *)channel,osPriorityRealtime,1024);  // evillive
    }
    m_channel = s_channels[channel];
}


void I2CDriver::threadFun(void const *args)
{
    int channelIdx = (int)args;
    Channel channel;
    s_channels[channelIdx] = &channel;
    channel.driver = Thread::gettid();

    int freq = 0;
    int adrSlave = 0;
    int modeSlave = 0;
    i2c_t i2c;
    i2cRtos_init(&i2c, c_sdas[channelIdx], c_scls[channelIdx]);

    volatile Transfer& tr = channel.transfer;
    while(1) {
        // wait for requests
        osSignalWait(DRV_USR_SIG,osWaitForever);

        // check and adapt frequency
        if(freq != tr.freq) {
            freq = tr.freq;
            i2c_frequency(&i2c, tr.freq);
        }

        // check and adapt slave/master mode
        if(modeSlave != tr.slv) {
            modeSlave = tr.slv;
            i2c_slave_mode(&i2c, tr.slv);
        }

        // check and adapt slave address
        int adr = (tr.adr & 0xFF) | 1;
        if(tr.slv && adrSlave != adr) {
            adrSlave = adr;
            i2c_slave_address(&i2c, 0, adr, 0);
        }

        // just doit
        switch(tr.cmd) {
            case START:
                i2c_start(&i2c);
                break;
            case STOP:
                i2c_stop(&i2c);
                break;
            case READ_MST:
                tr.ret = i2cRtos_read(&i2c, tr.adr, tr.dta, tr.len, (tr.rep?0:1));
                break;
            case READ_MST_REG:
                tr.ret = i2cRtos_write(&i2c, tr.adr,(const char*)&(tr.reg), 1, 0);
                if(tr.ret)break; // error => bail out
                tr.ret = i2cRtos_read(&i2c, tr.adr, tr.dta, tr.len, (tr.rep?0:1));
                break;
            case READ_SLV:
                tr.ret = i2cRtos_slave_read(&i2c, tr.dta, tr.len);
                break;
            case READ_BYTE:
                tr.ret = i2cRtos_byte_read(&i2c, (tr.ack?0:1));
                break;
            case WRITE_MST:
                tr.ret = i2cRtos_write(&i2c, tr.adr, tr.wdta, tr.len, (tr.rep?0:1));
                break;
            case WRITE_SLV:
                tr.ret = i2cRtos_slave_write(&i2c, tr.wdta, tr.len);
                break;
            case WRITE_BYTE:
                tr.ret = i2cRtos_byte_write(&i2c, tr.ack);
                break;
            case RECEIVE:
                tr.ret = i2cRtos_slave_receive(&i2c, tr.tmout);
                break;
            default:
                error("call 911\n");
        }
        // inform the caller
        osSignalSet( channel.transfer.caller, DRV_USR_SIG);
    }
}

void I2CDriver::lock()
{
    // One and the same thread can lock twice, but then it needs also to unlock twice.
    // exactly what we need here
    m_callerID = osThreadGetId();
    m_callerPrio = osThreadGetPriority(m_callerID);
    m_channel->mutex.lock(osWaitForever);
    osThreadSetPriority(m_callerID, c_drvPrio); // evillive hopefully not interrupted since the lock
}

void I2CDriver::unlock()
{
    // free the mtex and restore original prio
    m_channel->mutex.unlock();
    osThreadSetPriority(m_callerID, m_callerPrio);
}

int I2CDriver::sendNwait()
{
    m_channel->transfer.freq = m_freq;
    m_channel->transfer.caller = Thread::gettid();
    osSignalSet( m_channel->driver, DRV_USR_SIG);
    osSignalWait(DRV_USR_SIG,osWaitForever);
    int ret = m_channel->transfer.ret;
    unlock();
    return ret;
}

int I2CDriver::readMaster(int address, char *data, int length, bool repeated)
{
    lock();
    m_channel->transfer.cmd = READ_MST;
    m_channel->transfer.slv = false;
    m_channel->transfer.adr = address;
    m_channel->transfer.dta = data;
    m_channel->transfer.len = length;
    m_channel->transfer.rep = repeated;
    return sendNwait();
}

int I2CDriver::readMaster(int address, uint8_t _register, char *data, int length, bool repeated)
{
    lock();
    m_channel->transfer.cmd = READ_MST_REG;
    m_channel->transfer.slv = false;
    m_channel->transfer.adr = address;
    m_channel->transfer.reg = _register;
    m_channel->transfer.dta = data;
    m_channel->transfer.len = length;
    m_channel->transfer.rep = repeated;
    return sendNwait();
}

int I2CDriver::readMaster(int ack)
{
    lock();
    m_channel->transfer.cmd = READ_BYTE;
    m_channel->transfer.slv = false;
    m_channel->transfer.ack = ack;
    return sendNwait();
}

int I2CDriver::writeMaster(int address, const char *data, int length, bool repeated)
{
    lock();
    m_channel->transfer.cmd = WRITE_MST;
    m_channel->transfer.slv = false;
    m_channel->transfer.adr = address;
    m_channel->transfer.wdta = data;
    m_channel->transfer.len = length;
    m_channel->transfer.rep = repeated;
    return sendNwait();
}

int I2CDriver::writeMaster(int data)
{
    lock();
    m_channel->transfer.cmd = WRITE_BYTE;
    m_channel->transfer.slv = false;
    m_channel->transfer.ack = data;
    return sendNwait();
}

void I2CDriver::startMaster(void)
{
    lock();
    m_channel->transfer.cmd = START;
    m_channel->transfer.slv = false;
    sendNwait();
}

void I2CDriver::stopMaster(void)
{
    lock();
    m_channel->transfer.cmd = STOP;
    m_channel->transfer.slv = false;
    sendNwait();
}

void I2CDriver::stopSlave(void)
{
    lock();
    m_channel->transfer.cmd = STOP;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    sendNwait();
}

int I2CDriver::receiveSlave(uint32_t timeout_ms)
{
    lock();
    m_channel->transfer.cmd = RECEIVE;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    m_channel->transfer.tmout = timeout_ms;
    return sendNwait();
}

int I2CDriver::readSlave(char* data, int length)
{
    lock();
    m_channel->transfer.cmd = READ_SLV;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    m_channel->transfer.dta = data;
    m_channel->transfer.len = length;
    return sendNwait();
}

int I2CDriver::readSlave(void)
{
    lock();
    m_channel->transfer.cmd = READ_BYTE;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    m_channel->transfer.ack = 1;
    return sendNwait();
}

int I2CDriver::writeSlave(const char *data, int length)
{
    lock();
    m_channel->transfer.cmd = WRITE_SLV;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    m_channel->transfer.wdta = data;
    m_channel->transfer.len = length;
    return sendNwait();
}

int I2CDriver::writeSlave(int data)
{
    lock();
    m_channel->transfer.cmd = WRITE_BYTE;
    m_channel->transfer.slv = true;
    m_channel->transfer.adr = m_slaveAdr;
    m_channel->transfer.ack = data;
    return sendNwait();
}