I2c-master

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