Added I2C Slave functions

Added support for I2C Slave block read, block write and byte read and write. The slave address can be set and the general call address is automatically enabled. Note that the lpc812, lpc824 and lpc1549 have the same I2C engine. This new engine is very different from the lpc1768 and other NXP mbeds. The newer engine has separate controls for Master and Slave functions and they can be enable at the same time. Note that currently the lib does not support multi-master (arbitration lost is not handled).
pull/881/head
Wim 2015-02-03 21:39:04 +01:00
parent d03329fb3f
commit 7045d12363
1 changed files with 249 additions and 3 deletions

View File

@ -17,6 +17,8 @@
#include "cmsis.h"
#include "pinmap.h"
#if DEVICE_I2C
static const SWM_Map SWM_I2C_SDA[] = {
{7, 24},
};
@ -75,6 +77,10 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl) {
i2c_interface_enable(obj);
}
//Actually Wrong. Spec says: First store Address in DAT before setting STA !
//Undefined state when using single byte I2C operations and too much delay
//between i2c_start and do_i2c_write(Address).
//Also note that lpc812 will immediately continue reading a byte when Address b0 == 1
inline int i2c_start(i2c_t *obj) {
int status = 0;
if (repeated_start) {
@ -86,8 +92,6 @@ inline int i2c_start(i2c_t *obj) {
return status;
}
//Generate Stop condition and wait until bus is Idle
//Will also send NAK for previous RD
inline int i2c_stop(i2c_t *obj) {
@ -95,7 +99,8 @@ inline int i2c_stop(i2c_t *obj) {
obj->i2c->MSTCTL = (1 << 2) | (1 << 0); // STP bit and Continue bit. Sends NAK to complete previous RD
while ((obj->i2c->STAT & ((7 << 1) | (1 << 0))) != ((0 << 1) | (1 << 0))) { //Spin until Ready (b0 == 1)and Status is Idle (b3..b1 == 000)
//Spin until Ready (b0 == 1)and Status is Idle (b3..b1 == 000)
while ((obj->i2c->STAT & ((7 << 1) | (1 << 0))) != ((0 << 1) | (1 << 0))) {
timeout ++;
if (timeout > 100000) return 1;
}
@ -265,3 +270,244 @@ int i2c_byte_write(i2c_t *obj, int data) {
return ack;
}
#if DEVICE_I2CSLAVE
#define I2C_SLVDAT(x) (x->i2c->SLVDAT)
#define I2C_SLVSTAT(x) ((x->i2c->STAT >> 9) & (0x03))
#define I2C_SLVSI(x) ((x->i2c->STAT >> 8) & (0x01))
//#define I2C_SLVCNT(x) (x->i2c->SLVCTL = (1 << 0))
//#define I2C_SLVNAK(x) (x->i2c->SLVCTL = (1 << 1))
#if(0)
// Wait until the Slave Serial Interrupt (SI) is set
// Timeout when it takes too long.
static int i2c_wait_slave_SI(i2c_t *obj) {
int timeout = 0;
while (!(obj->i2c->STAT & (1 << 8))) {
timeout++;
if (timeout > 100000) return -1;
}
return 0;
}
#endif
void i2c_slave_mode(i2c_t *obj, int enable_slave) {
if (enable_slave) {
// obj->i2c->CFG &= ~(1 << 0); //Disable Master mode
obj->i2c->CFG |= (1 << 1); //Enable Slave mode
}
else {
// obj->i2c->CFG |= (1 << 0); //Enable Master mode
obj->i2c->CFG &= ~(1 << 1); //Disable Slave mode
}
}
// Wait for next I2C event and find out what is going on
//
int i2c_slave_receive(i2c_t *obj) {
int addr;
// Check if there is any data pending
if (! I2C_SLVSI(obj)) {
return 0; //NoData
};
// Check State
switch(I2C_SLVSTAT(obj)) {
case 0x0: // Slave address plus R/W received
// At least one of the four slave addresses has been matched by hardware.
// You can figure out which address by checking Slave address match Index in STAT register.
// Get the received address
addr = I2C_SLVDAT(obj) & 0xFF;
// Send ACK on address and Continue
obj->i2c->SLVCTL = (1 << 0);
if (addr == 0x00) {
return 2; //WriteGeneral
}
//check the RW bit
if ((addr & 0x01) == 0x01) {
return 1; //ReadAddressed
}
else {
return 3; //WriteAddressed
}
//break;
case 0x1: // Slave receive. Received data is available (Slave Receiver mode).
// Oops, should never get here...
obj->i2c->SLVCTL = (1 << 1); // Send NACK on received data, try to recover...
return 0; //NoData
case 0x2: // Slave transmit. Data can be transmitted (Slave Transmitter mode).
// Oops, should never get here...
I2C_SLVDAT(obj) = 0xFF; // Send dummy data for transmission
obj->i2c->SLVCTL = (1 << 0); // Continue and try to recover...
return 0; //NoData
case 0x3: // Reserved.
default: // Oops, should never get here...
obj->i2c->SLVCTL = (1 << 0); // Continue and try to recover...
return 0; //NoData
//break;
} //switch status
}
// The dedicated I2C Slave byte read and byte write functions need to be called
// from 'common' mbed I2CSlave API for devices that have separate Master and
// Slave engines such as the lpc812 and lpc1549.
//Called when Slave is addressed for Write, Slave will receive Data in polling mode
//Parameter last=1 means received byte will be NACKed.
int i2c_slave_byte_read(i2c_t *obj, int last) {
int data;
// Wait for data
while (!I2C_SLVSI(obj)); // Wait forever
//if (i2c_wait_slave_SI(obj) != 0) {return -2;} // Wait with timeout
// Dont bother to check State, were not returning it anyhow..
//if (I2C_SLVSTAT(obj)) == 0x01) {
// Slave receive. Received data is available (Slave Receiver mode).
//};
data = I2C_SLVDAT(obj) & 0xFF; // Get and store the received data
if (last) {
obj->i2c->SLVCTL = (1 << 1); // Send NACK on received data and Continue
}
else {
obj->i2c->SLVCTL = (1 << 0); // Send ACK on data and Continue to read
}
return data;
}
//Called when Slave is addressed for Read, Slave will send Data in polling mode
//
int i2c_slave_byte_write(i2c_t *obj, int data) {
// Wait until Ready
while (!I2C_SLVSI(obj)); // Wait forever
// if (i2c_wait_slave_SI(obj) != 0) {return -2;} // Wait with timeout
// Check State
switch(I2C_SLVSTAT(obj)) {
case 0x0: // Slave address plus R/W received
// At least one of the four slave addresses has been matched by hardware.
// You can figure out which address by checking Slave address match Index in STAT register.
// I2C Restart occurred
return -1;
//break;
case 0x1: // Slave receive. Received data is available (Slave Receiver mode).
// Should not get here...
return -2;
//break;
case 0x2: // Slave transmit. Data can be transmitted (Slave Transmitter mode).
I2C_SLVDAT(obj) = data & 0xFF; // Store the data for transmission
obj->i2c->SLVCTL = (1 << 0); // Continue to send
return 1;
//break;
case 0x3: // Reserved.
default:
// Should not get here...
return -3;
//break;
} // switch status
}
//Called when Slave is addressed for Write, Slave will receive Data in polling mode
//Parameter length (>=1) is the maximum allowable number of bytes. All bytes will be ACKed.
int i2c_slave_read(i2c_t *obj, char *data, int length) {
int count=0;
// Read and ACK all expected bytes
while (count < length) {
// Wait for data
while (!I2C_SLVSI(obj)); // Wait forever
// if (i2c_wait_slave_SI(obj) != 0) {return -2;} // Wait with timeout
// Check State
switch(I2C_SLVSTAT(obj)) {
case 0x0: // Slave address plus R/W received
// At least one of the four slave addresses has been matched by hardware.
// You can figure out which address by checking Slave address match Index in STAT register.
// I2C Restart occurred
return -1;
//break;
case 0x1: // Slave receive. Received data is available (Slave Receiver mode).
data[count] = I2C_SLVDAT(obj) & 0xFF; // Get and store the received data
obj->i2c->SLVCTL = (1 << 0); // Send ACK on data and Continue to read
break;
case 0x2: // Slave transmit. Data can be transmitted (Slave Transmitter mode).
case 0x3: // Reserved.
default: // Should never get here...
return -2;
//break;
} // switch status
count++;
} // for all bytes
return count; // Received the expected number of bytes
}
//Called when Slave is addressed for Read, Slave will send Data in polling mode
//Parameter length (>=1) is the maximum number of bytes. Exit when Slave byte is NACKed.
int i2c_slave_write(i2c_t *obj, const char *data, int length) {
int count;
// Send and all bytes or Exit on NAK
for (count=0; count < length; count++) {
// Wait until Ready for data
while (!I2C_SLVSI(obj)); // Wait forever
// if (i2c_wait_slave_SI(obj) != 0) {return -2;} // Wait with timeout
// Check State
switch(I2C_SLVSTAT(obj)) {
case 0x0: // Slave address plus R/W received
// At least one of the four slave addresses has been matched by hardware.
// You can figure out which address by checking Slave address match Index in STAT register.
// I2C Restart occurred
return -1;
//break;
case 0x1: // Slave receive. Received data is available (Slave Receiver mode).
// Should not get here...
return -2;
//break;
case 0x2: // Slave transmit. Data can be transmitted (Slave Transmitter mode).
I2C_SLVDAT(obj) = data[count] & 0xFF; // Store the data for transmission
obj->i2c->SLVCTL = (1 << 0); // Continue to send
break;
case 0x3: // Reserved.
default:
// Should not get here...
return -3;
//break;
} // switch status
} // for all bytes
return length; // Transmitted the max number of bytes
}
// Set the four slave addresses.
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) {
obj->i2c->SLVADR0 = (address & 0xFE); // Store address in address 0 register
obj->i2c->SLVADR1 = (0x00 & 0xFE); // Store general call write address in address 1 register
obj->i2c->SLVADR2 = (0x01); // Disable address 2 register
obj->i2c->SLVADR3 = (0x01); // Disable address 3 register
obj->i2c->SLVQUAL0 = (mask & 0xFE); // Qualifier mask for address 0 register. Any maskbit that is 1 will always be a match
}
#endif
#endif