I2c-master
Jump to navigation
Jump to search
These code snippets and functions are intended for an I2C master device.
Functions:
- address an I2C slave and write data to it
- address an I2C slave and read data from it
initialisation
// I2C definitions int LCD_I2C_DEVICE_ID=58; #define SCL PA4 #define SDA PA5 #define I2C_SCL_T_HIGH_US 50 #define I2C_SCL_T_LOW_US 50 // Testing i2c reads with 24LC256 EEPROM module #define I2C_EEPROM 0x50 // ID# of eeprom int got_ack; int I2C_STRETCH_TIMEOUT_MS=10; // how long to wait for a slave that's stretching the clock unsigned int i2c_timeout=65535; // when listening for read replies
functions
// --- I2C Functions ---
// --- I2C Functions ---
// --- I2C Functions ---
void setPin(int line, int state)
{
// set the I2C SDA or SCL PIN
if (state == LOW) // if state = 0
{
// set the pin low
DDRA |= (1 << line); // set line to output
PORTA &= ~(1 << line); // set it to zero
} else {
// set the pin high (let external pull-up resistor bring it high)
DDRA &= ~(1 << line); // set line to input
}
}
void i2c_tx(char d)
{
// SDA is LOW, SCL is high (inherited from START sequence)
// 1010 0000 0 0000 00000 0 0001 0101 0 00010
// 80 << 1 W A
// bring clock low to start data transfer
setPin(SCL,LOW);
_delay_us(I2C_SCL_T_LOW_US);
char x;
//static int b;
for(x=8; x; x--)
{
if(d&0x80)
{
setPin(SDA,HIGH);
} else {
setPin(SDA,LOW);
}
setPin(SCL,HIGH); // strobe clock
// allow for I2C clock stretching (SCL can be held low by slave)
// see I2C_STRETCH_TIMEOUT_MS
int stretch_counter;
for (stretch_counter=0; stretch_counter < I2C_STRETCH_TIMEOUT_MS; stretch_counter++)
{
// allow for some cycles for SCL to go high
if (bit_is_clear(PINA,SCL))
{
// SCL is held low by slave
// we'll just wait a little
_delay_us(5);
} else {
// SCL has risen, ready to move on
stretch_counter = I2C_STRETCH_TIMEOUT_MS;
}
}
_delay_us(I2C_SCL_T_HIGH_US); // if everything went fine we still need to make sure
setPin(SCL,LOW); // we're waiting at least 4us until we can bring SCL LOW
_delay_us(I2C_SCL_T_LOW_US);
d <<= 1; // shift byte
}
// read acknowledge bit if any
_delay_us(I2C_SCL_T_LOW_US);
setPin(SDA,HIGH); // first bring SDA into listening mode
// An addressed i2c slave that wants to set an ACK will have already set SDA low.
// If the slave doesn't respond to the ACK we need to make sure not to see a false
// positive from our own SDA pin
_delay_us(I2C_SCL_T_HIGH_US); // give the bus some time to possibly bring the pin high
setPin(SCL,HIGH); // start the clock pulse
// check if slave set an ack on the SDA line or not
if (bit_is_set(PINA,SDA))
{
got_ack=0;
} else {
got_ack=1;
}
//setPin(SDA,LOW); // we now bring SDA low
//_delay_us(10); // give the bus some time to prevent slaves from detecting this as a false stop? ----- may go
_delay_us(I2C_SCL_T_HIGH_US);
setPin(SCL,LOW); // end the clock pulse
// show ack result here..
// slave detects SCL is low and will release SDA
// need an extra bit of delay here for our I2C LCD Module
// _delay_us(100);
_delay_us(I2C_SCL_T_LOW_US); // this should be 10us or so
_delay_us(I2C_SCL_T_LOW_US); // this should be 10us or so
// -- end of i2c_tx function --
// function ends with:
//
// SDA: HIGH
// SCL: LOW
}
void i2c_start()
{
// SDA goes from high to low while SCL is high
// we expect both SDA and SCL to be high
//_delay_us(500);
//setPin(SDA,HIGH);
//_delay_us(500); // give bus some time to bring SDA high
//setPin(SCL,HIGH);
//_delay_us(100);
setPin(SDA,LOW);
_delay_us(I2C_SCL_T_LOW_US);
}
void i2c_write(char i2c_id)
{
i2c_start();
// send the I2C Device ID
// send as Write (last bit is 0)
i2c_tx(i2c_id<<1);
}
void i2c_stop()
{
// SDA goes from low to high while SCL is high
// we expect SCL to be low but don't know the state of SDA
// therefore we force SCL to be low so we don't accidentally
// generate a .. and then bring down SDA so we begin with
// a known start condition.
setPin(SCL,LOW);
_delay_us(I2C_SCL_T_LOW_US); // give bus some time to bring SCL LOW
setPin(SDA,LOW);
_delay_us(I2C_SCL_T_LOW_US); // give bus some time to bring SDA LOW
// initiate STOP sequence
setPin(SCL,HIGH);
_delay_us(I2C_SCL_T_HIGH_US);
setPin(SDA,HIGH);
_delay_us(I2C_SCL_T_HIGH_US);
}
void i2c_lcd_text(char *StrData)
{
int p = 0;
int q = strlen(StrData);
int temp = 0;
for (p = 0; p < q; p++)
{
temp = StrData[p];
i2c_tx(temp);
}
}
void i2c_lcd_number(int number)
{
int digits;
if (number == 0)
{
digits = 0;
int array[0];
array[0]=0;
i2c_tx(array[0]+48);
} else {
// determine the number of digits
digits = (int) (log(number)/log(10))+1;
// split up the number's digits into an array
int i = digits -1;
int array[digits];
while (number > 0)
{
array[i--] = number % 10;
number /= 10;
}
// send array over i2c
for (i =0; i <= digits-1; i++)
{
i2c_tx(array[i]+48);
}
}
}
void i2c_cls(uint8_t device)
{
// send out Clear LCD command to I2C_LCD device
i2c_write(device);
i2c_tx(12); // blank lcd command
i2c_stop();
}
void i2c_pos (uint8_t device, uint8_t row, uint8_t col)
{
// positions the cursor on an I2C_LCD device
i2c_write(device);
i2c_tx(10); // i2c position command
i2c_tx(row); // row position
i2c_tx(col); // col position
i2c_stop();
}
void i2c_write_number(uint8_t device, int16_t number)
{
// write a number to an I2C_LCD device
i2c_write(device);
i2c_lcd_number(number);
i2c_stop();
}
unsigned char i2c_rxbyte()
{
// read a byte from an i2c slave we just addressed with its ID and the register we wish to read
unsigned char rxbyte=0;
int bitcount=8;
// i2c_tx ends with:
// SDA: HIGH
// SCL: LOW
// the ic2_tx command lastly sent the ack pulse and now has SDA and SCL set to LOW
// we now control the read from the i2c slave device by pulsing SCL and reading SDA
while (bitcount > 0)
{
rxbyte = rxbyte << 1; // shift all bits in rxbyte one position to the left
setPin(SCL,HIGH); // start the clock pulse
_delay_us(I2C_SCL_T_HIGH_US); // give the bus some time to possibly bring the pin high
if (bit_is_set(PINA,SDA))
{
// -- detected bit '1' --
rxbyte |= 1 << 0 ; // sets bit 0 of rxbyte to 1
} else {
// -- detected bit '0' --
}
setPin(SCL,LOW); // stop the clock pulse
_delay_us(I2C_SCL_T_LOW_US); // give the bus some time to possibly bring the pin high
bitcount--; // decrement bitcounter
}
// send ack clock pulse
setPin(SCL,HIGH); // start the clock pulse
_delay_us(I2C_SCL_T_HIGH_US);
setPin(SCL,LOW); // stop the clock pulse
_delay_us(I2C_SCL_T_HIGH_US);
return rxbyte;
}
unsigned char i2c_read(uint8_t i2c_id, uint16_t DEV_REG)
{
// reading a register from an eeprom device
unsigned char read_result=0;
i2c_id = i2c_id<<1; // shift all bits one position to the left (i.e. multiply by 2)
// send start sequence
i2c_start();
// send the I2C Device ID with the R/W bit low (send as Write, last bit is 0)
i2c_tx(i2c_id);
//_delay_us(10);
// send the register we want to query to the device
// for the 74LC256 we need to do this in two goes. First the MSB then the LSB
i2c_tx(DEV_REG >> 8); // MSB
i2c_tx(DEV_REG & 0xFF); // LSB
// i2c_tx alread has a delay at the end
// i2c_tx ends with:
// SDA: HIGH
// SCL: LOW
// -- send repeated start --
// in order to generate a valid repeated-start we must make sure SCL is high
//setPin(SCL,HIGH);
//_delay_us(10);
// -- or --
// send stop
i2c_stop();
_delay_us(I2C_SCL_T_LOW_US);
// send start sequence again
i2c_start();
// i2c_start ends with:
// SDA: LOW
// SCL: HIGH
// i2c_tx will bring SDA low again in order to start a valid transmission
// send the I2C Device ID with the R/W bit high (send as Read, last bit is 1)
i2c_id |= 1 << 0; // set bit 0 to 1
i2c_tx(i2c_id);
// i2c_tx ends with:
// SDA: HIGH
// SCL: LOW
// read data from device, one byte long
read_result=i2c_rxbyte();
i2c_stop();
return read_result;
}
// -- end I2C functions --
// -- end I2C functions --
// -- end I2C functions --
usage
int main()
{
setPin(SDA,HIGH);
setPin(SCL,HIGH);
uint8_t eeadr=0;
int getvalue=0;
while (eeadr < 25)
{
getvalue=i2c_read(I2C_EEPROM,eeadr);
Write_Serial_Number(eeadr);
Write_Serial_Data(',');
Write_Serial_Number(getvalue);
Write_Serial_Text(crlf); // crlf
eeadr++;
}
}