i2cDevice.hpp:
Code: Select all
using namespace chibios_rt;
class i2cDevice : public EnhancedThread<128> {
private:
    uint8_t *outBuffer;
    I2CDriver *i2cp;
    uint16_t address;
    uint8_t tbuf[2];
    static i2cflags_t errors;
    static BinarySemaphore bsem_start;
    static BinarySemaphore bsem_finish;
    static uint8_t txbytes, rxbytes;
protected:
    virtual msg_t Main(void);
public:
    void write(uint8_t *data, size_t bytes);
    void write(uint8_t reg, uint8_t data);
    void startRead(uint8_t reg, size_t bytes);
    void read(uint8_t reg, size_t bytes);
    void waitForReadComplete(void);
    void set_outbuff(uint8_t *out);
    i2cDevice(I2CDriver *i2cp_in, uint16_t address_in, uint8_t *out);
    i2cDevice(I2CDriver *i2cp_in, uint16_t address_in, uint8_t *out, const char *tname);
};Â i2cDevice.cpp
Code: Select all
#include "ch.hpp"
#include "hal.h"
#include "i2cDevice.hpp"
/* static member variables */
i2cflags_t i2cDevice::errors;
BinarySemaphore i2cDevice::bsem_start;
BinarySemaphore i2cDevice::bsem_finish;
uint8_t i2cDevice::txbytes;
uint8_t i2cDevice::rxbytes;
/* Each I2C device has a thread to issue read commands in */
msg_t i2cDevice::Main(void) {
    while(true) {
        /* wait for the semaphore to be unlocked */
        chBSemWait(&bsem_start);
        /* get access to the bus */
        i2cAcquireBus(i2cp);
        /* make the transaction */
          i2cMasterTransmit(i2cp, address, tbuf, txbytes, outBuffer, rxbytes, &errors, TIME_INFINITE);
        /* release the bus */
          i2cReleaseBus(i2cp);
        /* signal to the issuing thread that the read completed */
        chBSemSignal(&bsem_finish);
    }
}
void i2cDevice::read(uint8_t reg, size_t bytes) {
    this->startRead(reg, bytes);
    this->waitForReadComplete();
}
void i2cDevice::startRead(uint8_t reg, size_t bytes) {
    tbuf[0] = reg;
   Â
    if(bytes == 1) {
        bytes = 2;
    }
    txbytes = 1;
    rxbytes = bytes;
   Â
    /* release the reading thread */
    chBSemSignal(&bsem_start);
}
void i2cDevice::waitForReadComplete(void) {
    /* wait for the read to complete */
    if(!chBSemGetStateI(&bsem_finish))
        chBSemWait(&bsem_finish);
}
void i2cDevice::write(uint8_t reg, uint8_t data) {
    tbuf[0] = reg;
    tbuf[1] = data;
    write(tbuf, 2);
}
 Â
void i2cDevice::write(uint8_t *data, size_t bytes) {
    i2cAcquireBus(i2cp);
      i2cMasterTransmit(i2cp, address, data, bytes, tbuf, 0, &errors, TIME_INFINITE);
      i2cReleaseBus(i2cp);
}
void i2cDevice::set_outbuff(uint8_t *out) {
    this->outBuffer = out;
}
i2cDevice::i2cDevice(I2CDriver *i2cp_in, uint16_t address_in, uint8_t *out) :Â
    EnhancedThread<128>("I2CDevice"), i2cp(i2cp_in), address(address_in), outBuffer(out) {
    chBSemInit(&bsem_start, TRUE);
    chBSemInit(&bsem_finish, TRUE);
}
i2cDevice::i2cDevice(I2CDriver *i2cp_in, uint16_t address_in, uint8_t *out, const char *tname) :Â
    EnhancedThread<128>(tname), i2cp(i2cp_in), address(address_in), outBuffer(out) {
    chBSemInit(&bsem_start, TRUE);
    chBSemInit(&bsem_finish, TRUE);
}
 An example usage of the class for an ITG3200:
Code: Select all
#include "i2c/i2cDevice.hpp"
#include "i2c/itg3200.hpp"
int16_t *ITG3200::read() {
    i2cDevice::read(ITG3200_GYRO_XOUT_H, 6);
    return getData();
}
void ITG3200::startRead() {
    i2cDevice::startRead(ITG3200_GYRO_XOUT_H, 6);
}
int16_t *ITG3200::getData(void) {
    waitForReadComplete();
    uint8_t *u8mTmp = (uint8_t *)data;
    u8mTmp[0] =  rx[1]; u8mTmp[1] =  rx[0];
    u8mTmp[2] =  rx[3]; u8mTmp[3] =  rx[2];
    u8mTmp[4] =  rx[5]; u8mTmp[5] =  rx[4];
    return data;
}
void ITG3200::reset(void) {
    write(ITG3200_PWR_MGM, ITG3200_RESET);
}
void ITG3200::init(void) {
    reset();
       Â
    tx[0] = ITG3200_SMPLRT_DIV;
    // Sampling frequency: 8khz / (7+1) => 1khz
    tx[1] = 7;
    /*
     * DLPF register:
     *     - FS_SEL = 0x03 (2000deg/s)
     *     - DLPF_CFG = 0 (8khz internal sampling)
     */
    tx[2] = (0x03 << 3);
    /*
     * Interrupt Configuration
     * - off for now
     */
    tx[3] = 0;
   Â
    write(tx, 4);
    /* select the PLL source */
    tx[0] = ITG3200_PWR_MGM;
    tx[1] = 0x01;
   Â
    write(tx, 2);
}
ITG3200::ITG3200(I2CDriver *i2cp) : i2cDevice(i2cp, ITG3200_ADDR, rx, "ITG3200") {
    //init();
}Â The advantage of this class is that it makes it very easy to issue read commands for several different devices on different I2C buses simultaneously, and not worry about the order they are issued in or waiting for one to complete before doing the next etc:
Code: Select all
    /* issue read commands */
        gyro.startRead();
        acc.startRead();
        mag.startRead();
       Â
        g = gyro.getData();
        data.gyro[0] = g[0]; data.gyro[1] = g[1]; data.gyro[2] = g[2];
        a = acc.getData();
        data.acc[0] = a[0];  data.acc[1] = a[1];  data.acc[2] = a[2];
        m = mag.read();
        data.mag[0] = m[0];  data.mag[1] = m[1];  data.mag[2] = m[2]; Is using binary semaphores in the way I have a good idea? Basically the idea is to have a thread for each sensor which is sleeping until the main thread decides to wake it. The thread then goes off and does some stuff and notifies the main thread that the I2C process is complete, at which point the main thread may or may not be sleeping waiting for a binary semaphore signal.