Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of I2cRtosDriver by
i2cRtos_api.c
- Committer:
- humlet
- Date:
- 2013-05-19
- Revision:
- 14:352609d395c1
- Parent:
- 13:530968937ccb
File content as of revision 14:352609d395c1:
#include "i2cRtos_api.h"
#if DEVICE_I2C
#include "us_ticker_api.h"
#include "cmsis_os.h"
#include "error.h"
// little helpers cloned from official i2c api
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
#define I2C_CONSET(x) (x->i2c->I2CONSET)
#define I2C_CONCLR(x) (x->i2c->I2CONCLR)
#define I2C_STAT(x) (x->i2c->I2STAT)
#define I2C_DAT(x) (x->i2c->I2DAT)
#elif defined(TARGET_LPC11U24)
#define I2C_CONSET(x) (x->i2c->CONSET)
#define I2C_CONCLR(x) (x->i2c->CONCLR)
#define I2C_STAT(x) (x->i2c->STAT)
#define I2C_DAT(x) (x->i2c->DAT)
#endif
//#include "gpio_api.h"
//static gpio_t gpio[2]; // evillive
// isr/thread data transfer struct
enum I2cIsrCmd {
readMst,
writeMst,
readSlv,
writeSlv,
waitSI
};
struct I2cIsrTransfer {
i2c_t* obj;
enum I2cIsrCmd cmd;
int len;
int cnt;
int stat;
char* rData;
const char* wData;
};
// one for each channel
// "volatile" has been omitted since ISR and thread do not run simultaneously, hopefully ok
static struct I2cIsrTransfer i2c_transfer[2];
// struct holding IRQ and semaphore ID
// needed for thread<->isr communication/activation
struct IsrIrqSem {
IRQn_Type irq;
osSemaphoreId sem;
};
static struct IsrIrqSem isrIrqSem[2]; // one for each channel
// little helpers cloned from official i2c api
static inline void i2c_conclr(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
{
I2C_CONCLR(obj) = (start << 5)
| (stop << 4)
| (interrupt << 3)
| (acknowledge << 2);
}
static inline void i2c_conset(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
{
I2C_CONSET(obj) = (start << 5)
| (stop << 4)
| (interrupt << 3)
| (acknowledge << 2);
}
static inline void i2c_clear_SI(i2c_t *obj)
{
i2c_conclr(obj, 0, 0, 1, 0);
}
static inline int i2c_status(i2c_t *obj)
{
return I2C_STAT(obj);
}
// ISR routines
// implements the same read/write sequences as the official i2c lib
static void i2cRtos_isr(uint32_t ch)
{
struct I2cIsrTransfer* tr=&(i2c_transfer[ch]);
if(tr->cmd==waitSI) {
// just waiting for an interrupt after a byte read/write or slave receive call
osSemaphoreRelease(isrIrqSem[ch].sem);
NVIC_DisableIRQ(isrIrqSem[ch].irq);
return;
}
int stat=i2c_status(tr->obj);
int stay = 0;
switch(tr->cmd) {
case readMst:
switch(stat) {
case 0x50: // Data byte has been received; ACK has been returned.
(tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
case 0x40: // SLA+R has been transmitted; ACK has been received.
++(tr->cnt);
if(tr->cnt != tr->len-1)
i2c_conset(tr->obj, 0, 0, 0, 1);
else
i2c_conclr(tr->obj, 0, 0, 0, 1); // do not ack the last byte read
stay = 1;
break;
case 0x58: // Data byte has been received; NOT ACK has been returned.
(tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
stat=0;
break;
}
break;
case writeMst:
switch(stat) {
case 0x18: // SLA+W has been transmitted; ACK has been received.
case 0x28: // SLA+W has been transmitted; NOT ACK has been received.
if(++(tr->cnt)!=tr->len) {
I2C_DAT(tr->obj) = (tr->wData)[tr->cnt];
stay=1;
} else {
stat=0;
}
}
break;
case readSlv:
++(tr->cnt);
if(stat==0x80 || stat==0x90) // Previously addressed with own SLA address(0x80) or geberal call (0x90); DATA has been received; ACK has been returned.
(tr->rData)[tr->cnt] = I2C_DAT(tr->obj) & 0xFF;
stay = (stat==0x80 || stat==0x90 || stat==0x060 || stat==0x70) && (tr->cnt < tr->len-1);
// 60: Own SLA+W has been received; ACK has been returned. ... SLV+W???
// 70: General Call address (0x00) has been received; ACK has been returned.
break;
case writeSlv:
++(tr->cnt);
stay = tr->cnt<tr->len && stat==0xb8; // Data byte in I2DAT has been transmitted; ACK has been received.
if(stay) I2C_DAT(tr->obj) = tr->wData[tr->cnt];
break;
}
if(stay) {
// sequence not finished => stay in ISR mode and trigger next i2c by clearing he SI bit
i2c_clear_SI(tr->obj);
} else {
// sequence finished or unexpected state has been reported
// => bail out of isr mode and return last status received
tr->stat = stat;
osSemaphoreRelease(isrIrqSem[ch].sem);
NVIC_DisableIRQ(isrIrqSem[ch].irq);
}
}
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
static void i2cRtos_isr_ch0()
{
//gpio_write(&gpio[0], 1);
i2cRtos_isr(0);
//gpio_write(&gpio[0], 0);
}
#endif
static void i2cRtos_isr_ch1()
{
//gpio_write(&gpio[1], 1);
i2cRtos_isr(1);
//gpio_write(&gpio[1], 0);
}
// determine channel
static inline uint32_t i2c_get_channel(const i2c_t *obj)
{
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
switch((I2CName)(obj->i2c)) {
case I2C_1:
return 0;
case I2C_2:
return 1;
default:
error("Dial911 i2c_get_channel: Invaid I2CName \n");
}
#endif
return 1;
}
// Enable IRQ and wait for ISR sequence to be finished
static inline void i2cRtos_wait_and_see(i2c_t *obj, int ch, uint32_t tmOut) //evillive
{
struct IsrIrqSem* iis = &(isrIrqSem[ch]);
__disable_irq(); // evil, but don't want the next three lines to be interrupted
i2c_clear_SI(obj);
NVIC_ClearPendingIRQ(iis->irq);
NVIC_EnableIRQ(iis->irq);
__enable_irq();
if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq); // time out => diable the IRQ
}
// just wait for a generic i2c interrupt
static inline void i2cRtos_waitSI(i2c_t *obj, uint32_t tmOut)
{
int ch = i2c_get_channel(obj);
i2c_transfer[ch].cmd = waitSI;
i2cRtos_wait_and_see(obj, ch, tmOut);
}
// master mode read sequence
int i2cRtos_read(i2c_t *obj, int address, char *data, int length, int stop)
{
//gpio_write(&gpio[1], 1);
int stat = i2c_start(obj);
if ((stat != 0x10) && (stat != 0x08)) {
i2cRtos_stop(obj); // use freeze free stop if the start has failed
return stat;
}
//gpio_write(&gpio[1], 0);
int ch = i2c_get_channel(obj);
struct I2cIsrTransfer* tr = &(i2c_transfer[ch]);
tr->obj=obj;
tr->cmd=readMst;
tr->len=length;
tr->cnt=-1;
tr->rData=data;
I2C_DAT(obj) = address | 0x01; // initiate address+R write and enter isr mode
i2cRtos_wait_and_see(obj, ch,2+(length>>2)); // timeout (2+len/4)ms
stat = tr->stat;
//gpio_write(&gpio[1], 1);
if(stat || stop) i2cRtos_stop(obj);
//gpio_write(&gpio[1], 0);
return stat;
}
// master mode write sequence
int i2cRtos_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
//gpio_write(&gpio[1], 1);
int status = i2c_start(obj);
if ((status != 0x10) && (status != 0x08)) {
i2cRtos_stop(obj); // use freeze free stop if the start has failed
return status;
}
//gpio_write(&gpio[1], 0);
int ch = i2c_get_channel(obj);
struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
tr->obj = obj;
tr->cmd = writeMst;
tr->len = length;
tr->cnt = -1;
tr->wData = data;
I2C_DAT(obj) = address & 0xfe; // initiate address+W write and enter isr mode
i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (2+len/4)ms
//i2c_clear_SI(obj); // ... why? Also in official lib ... I guess this is the "write instead of start" bug
status = tr->stat;
//gpio_write(&gpio[1], 1);
if(status || stop) i2cRtos_stop(obj);
//gpio_write(&gpio[1], 0);
return status;
}
// read single byte from bus (master/slave)
int i2cRtos_byte_read(i2c_t *obj, int last)
{
if(last) {
i2c_conclr(obj, 0, 0, 0, 1); // send a NOT ACK
} else {
i2c_conset(obj, 0, 0, 0, 1); // send a ACK
}
i2cRtos_waitSI(obj, 2);
return (I2C_DAT(obj) & 0xff);
}
// write single byte to bus (master/slave)
int i2cRtos_byte_write(i2c_t *obj, int data)
{
I2C_DAT(obj) = (data & 0xff);
i2cRtos_waitSI(obj, 2);
int stat=i2c_status(obj);
return (stat==0x18 || stat==0x28 || stat==0x40 || stat==0xb8);
}
// freeze free i2c stop
// the busy wait without timeout of the official i2c lib
// might freeze the mbed if someone on the bus keeps the clock down
// and prevents LPC's i2c controller to create a stop condition
int i2cRtos_stop(i2c_t *obj)
{
i2c_conset(obj, 0, 1, 0, 0);
i2c_clear_SI(obj);
uint32_t t0=us_ticker_read();
uint32_t dt=0;
while((I2C_CONSET(obj) & (1 << 4)) && dt<23) {
dt = us_ticker_read() - t0;
}
return dt<23;
}
#if DEVICE_I2CSLAVE
// determine slave's receive mode. Blocking with timeout
int i2cRtos_slave_receive(i2c_t *obj, uint32_t tmOut)
{
int retval = i2c_slave_receive(obj);
//check for pending requests
if(retval)return retval; // there is one => bail out
// No request? Wait for it! ... with time out
i2cRtos_waitSI(obj, tmOut);
// check again for pending requests
return i2c_slave_receive(obj);
}
// slave mode read sequence
int i2cRtos_slave_read(i2c_t *obj, char *data, int length)
{
int ch = i2c_get_channel(obj);
struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
tr->obj=obj;
tr->cmd=readSlv;
tr->len=length;
tr->cnt=-1;
tr->rData=data;
i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms
if(tr->stat != 0xa0) {
i2cRtos_stop(obj);
}
i2c_clear_SI(obj); // stop keeping scl low
return tr->cnt; // same weird return as in official lib
}
// slave mode write sequence
int i2cRtos_slave_write(i2c_t *obj, const char *data, int length)
{
if(length <= 0) {
return(0);
}
int ch = i2c_get_channel(obj);
struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
tr->obj=obj;
tr->cmd=writeSlv;
tr->len=length;
tr->cnt=0;
tr->wData=data;
I2C_DAT(obj) = data[0];
i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms
int status = tr->stat;
if(status!=0xC0 && status!=0xC8) {
i2cRtos_stop(obj);
}
i2c_clear_SI(obj); // stops keeping scl low
return tr->cnt;
}
#endif
// setup semaphores and hook in ISRs
void i2cRtos_init(i2c_t *obj, PinName sda, PinName scl)
{
/*static int called=0;
if(!called) {
gpio_init(&gpio[0], p15, PIN_OUTPUT);
gpio_init(&gpio[1], p16, PIN_OUTPUT);
}
called = 1;
gpio_write(&gpio[0], 0);
gpio_write(&gpio[1], 0);*/
i2c_init(obj,sda,scl);
uint32_t ch = i2c_get_channel(obj);
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
static osSemaphoreDef(i2cIsrDrvSem_ch0);
static osSemaphoreDef(i2cIsrDrvSem_ch1);
switch(ch) {
case 0:
isrIrqSem[ch].irq = I2C1_IRQn;
NVIC_SetVector(I2C1_IRQn, (uint32_t)i2cRtos_isr_ch0);
isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch0), 1);
break;
case 1:
isrIrqSem[ch].irq = I2C2_IRQn;
NVIC_SetVector(I2C2_IRQn, (uint32_t)i2cRtos_isr_ch1);
isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
break;
}
osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
#elif defined(TARGET_LPC11U24)
static osSemaphoreDef(i2cIsrDrvSem_ch1);
isrIrqSem[ch].irq = I2C_IRQn;
NVIC_SetVector(I2C_IRQn, (uint32_t)i2cRtos_isr_ch1);
isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
#else
#error "Dial911 i2cRtos_init: Unsupported HW"
#endif
}
#endif
