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++; } }