I2c-master

From wikipost
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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