• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

How do I read a specific register with I2C?

Pommie

Well-Known Member
Most Helpful Member
I've read through the various help pages but can't seem to find how to read a specific register in an I2C device.
E.G. if I want to read the hours register of a DS1307 RTC I'd do the following,
Send start,
Send device address (0xd0)
Send register address (0x02)
Send a restart with device address and read set (0xd1)
Read the hours register and any more after that.
Send stop.

How is this done with an Arduino? There doesn't seem to be anyway to send a restart.

Mike.
 

DrG

Active Member
Easier than you think.

Reading hours only
Wire.beginTransmission(0xd0); << into a buffer
Wire.write(0x02); << into a buffer
Wire.endTransmission(); <-- THIS is when it gets sent
Wire.requestFrom(I2C_address, 0x1); << get 1 byte into a buffer
byte hours=Wire.read(); << move it to a var

Read all 13 regs on 3231 (I have not used a 1307 in ages, but you get the idea and I'm sure you have a BCD2Dec routine
Code:
// read the RTC memory
void ReadRTC(){
// update the time vars from the RTC
  byte hold;
  A1Reg=0;
  A2Reg=0;
  Wire.beginTransmission(I2C_address);
  Wire.write(0x00);
  Wire.endTransmission();
  // read all 19 bytes that have the data
  Wire.requestFrom(I2C_address, 0x13);
  second = BCD2Dec(Wire.read());
  minute = BCD2Dec(Wire.read());
  // if bit 6 of hour is set we have a 12 hour clock
  hold=Wire.read();
  if (hold & 0b01000000) {
    // 12 hour clock 
    if(bitSet(hold,5)){
      // it's PM
      AM_PM=1;
    }
    else{
      AM_PM=0;
    }
    hour = BCD2Dec(hold & 0b00011111);
  }
  else{
    // 24 h clock
    AM_PM=2;
    hour = BCD2Dec(hold & 0b00111111); 
  }
  // bit 7-3=0
  DoW = BCD2Dec(Wire.read());
  // bit 7-6=0
  DoM = BCD2Dec(Wire.read());
  // bit 7=century
  month = BCD2Dec(Wire.read() & 0b01111111);
  year = BCD2Dec(Wire.read()); 
  // Alarm #1
  hold=Wire.read();  // A1 seconds
  if(bitRead(hold,7)){
    bitSet(A1Reg,0);
  }
  hold=hold&0b01111111;
  A1second=BCD2Dec(hold);         
  hold=Wire.read();  // A1 minutes
  if(bitRead(hold,7)){
    bitSet(A1Reg,1);
  }
  hold=hold&0b01111111;  
  A1minute=BCD2Dec(hold);        
  hold=Wire.read();  // A1 hours
  if(bitRead(hold,7)){
    bitSet(A1Reg,2);
  }
  if(bitRead(hold,6)){
    // 12 hour clock - convert the time
    // get AM/PM
    if(bitRead(hold,5)){
      // it's PM
      A1AM_PM=1;
    }
    else{
      A1AM_PM=0;
    }
    A1hour = BCD2Dec(hold & 0b00011111); 
  }
    else{
    // 24 h clock
    A1AM_PM=2;
    A1hour = BCD2Dec(hold & 0b00111111); 
  }
  // DoM or Day depending on bit 6
  hold=Wire.read();  // DY/DT
  if(bitRead(hold,7)){
    bitSet(A1Reg,3);
  }
  if(bitRead(hold,6)){
    // it's day (DoW)
    A1DY_DT=1;
    A1DoM=0;
    A1DoW = BCD2Dec(hold & 0b00001111);
  }
    else{
    // it's date (DoM)
    A1DY_DT=0;
    A1DoW=0;
    A1DoM = BCD2Dec(hold & 0b00111111); 
  }
  //
  // Alarm #2
  A2minute=BCD2Dec(Wire.read());  // A2 minutes
  hold=Wire.read();  // A2 hours
  if(hold&0b01000000){
    // 12 hour clock
    A2AM_PM==hold & 0b00100000;
    A2hour = BCD2Dec(hold & 0b00011111);
  }
    else{
    // 24 h clock
    A2AM_PM=2;
    A2hour = BCD2Dec(hold & 0b00111111); 
  }
  // DoM or Day depending on bit 6
  hold=Wire.read();
  if(hold&0b01000000){
    // it's day (DoW)
    A2DoM=0;
    A2DoW = BCD2Dec(hold & 0b00001111);
  }
    else{
    // it's date (DoM)
    A2DoW=0;
    A2DoM = BCD2Dec(hold & 0b00111111); 
  } 
  // control byte
  cntb=Wire.read();  
  // control/status byte
  statusb=Wire.read();
  ageb=Wire.read();  // aging offset msb is sign
  aging=ageb&0b01111111;
  if(ageb&0b10000000){
    aging=aging*-1;
  }
  // note that we are not explicitly requesting a conversion
  temphi=Wire.read();  // Temp MSB
  templo=Wire.read();  // Temp LSB
  /* calculate the chip temperature */
  degrees = temphi<< 8;
  degrees |= templo;
  degrees/=256;
}
 

Pommie

Well-Known Member
Most Helpful Member
I suspected that was how it was done but thought there must be a way using restart so it will work with multi master systems. The point of restart is to keep possession of the bus so another master can't change the device's address.

I'm not actually using a DS1307 chip - that was just a convenient question. The slave is actually a pic chip writing to an LCD. I used an I2C LCD (4x20) with the standard library and it took 80mS to refresh the screen!!

Mike.
 

DrG

Active Member
Yes, I understand what restart is in I2C. Go back and re-look at the documentation and note that Wire.endTransmission() can be used with a Boolean argument. When false, it will issue a restart without releasing the bus and when true (default) will issue a stop and release the bus.

Still, I don’t know how much you will gain performance-wise, please let me know.

Can you up the speed to 300 (if you have not already done so)? The Arduino IDE defaults to 100, as I recall, but I could be mistaken
Edit: Sorry, I got carried away with shorthand.

What I should have said was, I am pretty sure that default I2C speed in Arduino is 100000 Hz. Can you up the speed to 400000 Hz (the higher "standard) meaning, does your LCD support that?

If so, the old school way is to change timer values directly (http://www.gammon.com.au/i2c) but newer versions of wire.h with IDE allow the statement Wire.setClock(clockFrequency) , e.g., Wire.setClock(4000000).
 
Last edited:

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
If it were me... The Arduino, set up as a slave, would have a simple character buffer.. The I2C would pass the buffer items back and forth then the Arduino would process the data.. You would use the incoming address to relate to one of the characters in said buffer...
 

Pommie

Well-Known Member
Most Helpful Member
DrG, missed the boolean argument, that will indeed allow multi master systems to exist. No noticeable difference in speed holding the bus. I'm running at 400k without any problems.
Ian, the Pic is the slave writing to a 20x4 LCD. The Arduino I²C library takes 80mS to update the whole screen. Using the Pic I can refresh the full display in 5mS. I'm also planning to use the Pic to keep track of the encoder.

Long story short, this was originally using a rotary encoder to scroll through a menu system. However, the 80mS refresh time made it completely unusable. So now I've got my own I²C LCD I'll get back to writing the scrolling menu system.

Mike.
 

DrG

Active Member
DrG, missed the boolean argument, that will indeed allow multi master systems to exist. No noticeable difference in speed holding the bus. I'm running at 400k without any problems.
Ian, the Pic is the slave writing to a 20x4 LCD. The Arduino I²C library takes 80mS to update the whole screen. Using the Pic I can refresh the full display in 5mS. I'm also planning to use the Pic to keep track of the encoder.

Long story short, this was originally using a rotary encoder to scroll through a menu system. However, the 80mS refresh time made it completely unusable. So now I've got my own I²C LCD I'll get back to writing the scrolling menu system.

Mike.
It sure seems like 80ms is way too slow and I wonder if there isn't something going on there - but, no matter, you got it solved.
 

Latest threads

EE World Online Articles

Loading

 
Top