0

I've just started with the I2C protocol. I read about it in the datasheet, other documents, blogs and videos.

So, I started to develop my code and I think I did my first success, I succeeded to send start and check TWST for sending start signal.

I get started OK, but I can't proceed to send data. What I'm missing? I'm thinking I didn't send the correct address.

void I2C_init(void);
void I2C_start(void);
void I2C_stop(void);
void I2C_tx(uint8_t data);
void I2C_TWSR_Check(void);

void setup() {
  Serial.begin(9600);
  I2C_init();
  //  I2C_TWSR_Check();
  I2C_start();
  I2C_TWSR_Check();
  I2C_tx(0x40);           // sending the address
  I2C_TWSR_Check();       // waiting for response
}

void loop() {
  uint8_t i;
  // simple loop to blink an LED on the I/O expander pin
  for (i=0;i<5;i++) {
    I2C_tx(0xff);
    I2C_TWSR_Check();
    _delay_ms(500);
    I2C_tx(0x00);
    I2C_TWSR_Check();
    _delay_ms(500);
  }
}

void I2C_init(void) {
  //set SCL to 100kHz
  TWSR = 0x00;
  TWBR = 0x48;
  //enable TWI
  TWCR = (1<<TWEN);
}

void I2C_start(void) {
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  while (!(TWCR & (1<<TWINT)));
}

void I2C_stop(void) {
  TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void I2C_tx(uint8_t u8data) {
  TWDR = u8data;
  while (!(TWCR & (1<<TWINT)));
}

void I2C_TWSR_Check(void) {
  uint8_t Status;
  Status = TWSR & 0xF8;
  switch(Status) {
    case 0x08: // start OK
    Serial.println("Start is OK");
     break; 
    case 0x10: // Re start
    Serial.println("Re start");    
     break;    
    case 0x18: // SLA+W OK ACK
    Serial.println("SLA+W OK ACK");    
     break;    
    case 0x20: // SLA+W no ACK
    Serial.println("SLA+W no ACK");    
     break;      
    case 0x28: // data OK ACK
    Serial.println("data OK ACK");    
     break;    
    case 0x30: // data OK no ACK
    Serial.println("data OK no ACK");   
     break;       
    case 0x38: // arbitration lost in SLA+W or data TX
    Serial.println("arbitration lost in SLA+W or data TX");    
     break;         
     default:
     Serial.println("Error");
    break;
  }
}
  • Can you tell us exactly what I/O expander device you are using? If you could provide a link to the documentation that would be great! – Mazaryk May 4 '17 at 15:32
  • picclickimg.com/d/l400/pict/181805816595_/… Thank you so much for reply, – Perch Eagle May 4 '17 at 16:09
  • I solved the problem :) – Perch Eagle May 5 '17 at 8:28
  • Can you post the solution as an answer and accept the answer then, it might help people that have a similar issue in the future. – Code Gorilla May 5 '17 at 9:20
  • Of course, but can I post it as an answer? – Perch Eagle May 9 '17 at 14:44
0

OK the answer is that I didn't send the correct address!

Because if the I2C starting command with the slave is successful, then you should get successful sending ACK according reading the value of transmission status from the status register TWSR and ANDing this value with 0xF8. Then by reading the datasheet you know the value of transmission status. I developed a function to determine the status of transmission with Arduino Serial library which is I2C_TWSR_Check();.

I did this code for Master transmitter and receiver only.

So the trick is that you have to fix the address according to the AVR's TW address command format, which is the first bit is for read/write command and the remaining 7-bits are for the address, so you have to shift the real address which I got by "I2C address scanner" I got from one of AVRfreaks members.

For example, if the address is 0x20, then after shifting it 1-bit to the left, the new address is 0x40 for write operation and 0x41 for read operation.

void I2C_init(void);
void I2C_start(uint8_t address);
void I2C_stop(void);
void I2C_tx(uint8_t data);
uint8_t I2C_rx(void);
void I2C_TWSR_Check(void);

uint8_t bitmask;

/////////// I2C modules addresses ////////////
// 8-bit I/O expander 0x20 
#define IOEXP_WR  0x40    // I/O 8-bits expander address
#define IOEXP_RD  0x41

// LCD1602 adapter
#define LCD1602_WR  0x4E  // LCD1602 I2C adapter address
#define LCD1602_RD  0x4F

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  I2C_init();
}

void loop() {


  I2C_start(LCD1602_WR);
  I2C_TWSR_Check();
  I2C_tx(0xff);
  I2C_TWSR_Check();
  I2C_stop();
  _delay_ms(500);

  I2C_start(LCD1602_WR);
  I2C_tx(0x00);
  I2C_stop();
  _delay_ms(500);
}

void I2C_init(void)
{
  //set SCL to 100kHz
  TWSR = 0x00;
  TWBR = 0x48;
  //enable TWI
  TWCR = (1<<TWEN);
}

void I2C_start(uint8_t address)
{
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  while (!(TWCR & (1<<TWINT)));
  //I2C_TWSR_Check();
  TWDR = address;
  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  while (!(TWCR & (1<<TWINT)));
  //I2C_TWSR_Check();  
}

void I2C_stop(void)
{
  TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void I2C_tx(uint8_t data)
{
  TWDR = data;
  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  while (!(TWCR & (1<<TWINT)));
  //I2C_TWSR_Check();
}

uint8_t I2C_rx(void)
{
  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  while (!(TWCR & (1<<TWINT))); 
  return TWDR;
}

void I2C_TWSR_Check(void)
{
  uint8_t Status;
  Status = TWSR & 0xF8;
  switch(Status)
  {
    /////////////// MASTER TRANSMITTER //////////////
    case 0x08: 
    Serial.println("Start is OK");
     break; 
    case 0x10:
    Serial.println("Re start");    
     break;    
    case 0x18:    
    Serial.println("SLA+W OK ACK");    
     break;    
    case 0x20:  
    Serial.println("SLA+W no ACK");    
     break;      
    case 0x28:  
    Serial.println("data TX OK ACK");    
     break;    
    case 0x30:       
    Serial.println("data TX OK no ACK");   
     break;       
    case 0x38:        // arbitration lost in SLA+W or data TX
    Serial.println("arbitration lost in SLA+W or data TX");    
     break;

    /////////////// MASTER RECEIVER //////////////  
    case 0x40:
    Serial.println("SLA+R OK ACK");    
     break;    
    case 0x48:    
    Serial.println("SLA+R OK NO ACK");   
     break;       
    case 0x50:  
    Serial.println("data RX OK ACK");   
     break;  
    case 0x58: 
    Serial.println("data RX OK NO ACK");   
     break;  

     default:
     Serial.println("Error");
    break;
  }
}
  • Question titles on SE sites should not be edited to indicate that a problem has been solved, rather the answer providing the solution should be marked accepted - even when as in this case it is your own. – Chris Stratton Jun 9 '17 at 15:39
  • I thought it's better to declare to the viewer that the problem for this topic is solved and so anyone who is browsing for a certain point on Google would see that it's solved. – Perch Eagle Jun 11 '17 at 1:18
  • The procedure for that on SE sites is accepting an answer, not editing the title. So accept your own answer - only that will cause the system to consider it resolved and stop churning it back up as if it needs further attention. – Chris Stratton Jun 11 '17 at 2:11
  • "and stop churning it back up as if it needs further attention", what does this mean? Not so clear to me :) sorry. – Perch Eagle Jun 11 '17 at 2:17
  • Just click the hollow check mark beside your answer so we can be done with this. – Chris Stratton Jun 11 '17 at 2:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.