» Advertenties

Zo 5 Februari 2012, 12:58

I²C is een multimaster seriële bus, uitgevonden door Philips. Toch is er meetstal maar 1 master op de bus en meerdere slaves. Deze slaves kunnen enkel data sturen als de master dat gevraagd heeft.

Deze bus is bedoelt voor communicatie tussen microprocessors en andere IC’s op 1 zelfde printplaat. De bedrading voor dit bussysteem is zeer eenvoudig, er zijn maar 2 draden nodig, SCL en SDA, om tot 127 IC te verbinden. Beide signaaldraden hebben een pullup nodig, want de I²C IC’s hebben allemaal een opendrain uitgang.

Bus instructies
Voordat I²C communicatie kan plaats vinden moet er een start conditie worden gegeven. Aan het einde van de communicatie moet ook een stop conditie worden gegeven.

Bus idle:
- SCL: hoog
- SDA: hoog

Start: (repeated start)
- SCL: dalende flank
- SDA: hoog

Stop:
- SCL: stijgende flank
- SDA: hoog

C code -
  1. //! Send an I2C start condition in Master mode
  2. void i2cSendStart(void);
  3.  
  4. //! Send an I2C stop condition in Master mode
  5. void i2cSendStop(void);
  6.  
  7.  
  8.  
  9.  
  10. /**
  11.  *   @param     void
  12.  *   @return     void
  13.  */
  14. void i2cSendStart(void){
  15.   outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK) | ((1<<TWINT) | (1<<TWSTA)));
  16. }
  17.  
  18. /**
  19.  *   @param     void
  20.  *   @return     void
  21.  */
  22. void i2cSendStop(void){
  23.   // leave with TWEA on for slave receiving
  24.   outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK) | ((1<<TWINT) | (1<<TWEA) | (1<<TWSTO)));
  25. }


Het register TWCR is het controle register van de hardware TWI (Two Wiire Interface oftewel I²C). De functie van de TWINT, TWSTA, ... bits kun je terug vinden in de datsheets onder het kopje TWI.


Voordat het mogelijk is om Start en Stop condities te sturen met de AVR’s hardware TWI moet deze geïnitialiseerd worden.
C code -
  1. //! Initialize I2C (TWI) interface
  2. void i2cInit(u16 bitrateKHz);
  3.  
  4.  
  5.  
  6.  
  7. /**
  8.  *   @param     bitrateKHz   I2C SCL speed
  9.  *   @return     void
  10.  */
  11. void i2cInit(u16 bitrateKHz){
  12.   // set pull-up resistors on I2C bus pins
  13. #if   defined(__AVR_ATmega16__)   ||
  14.     defined(__AVR_ATmega32__)
  15.   sbi(PORTC, 0);
  16.   sbi(PORTC, 1);
  17.   cbi(PORTC, 7);
  18. #endif
  19. #if   defined(__AVR_ATmega64__)   ||
  20.     defined(__AVR_ATmega64__)
  21.   sbi(PORTD, 0);
  22.   sbi(PORTD, 1);
  23. #endif
  24. #if   defined(__AVR_ATmega8__)   ||
  25.     defined(__AVR_ATmega88__)   ||
  26.     defined(__AVR_ATmega168__)
  27.   sbi(PORTC, 4);
  28.   sbi(PORTC, 5);
  29. #endif
  30.  
  31.   // clear SlaveReceive and SlaveTransmit handler to null
  32.   i2cSlaveReceive = 0;
  33.   i2cSlaveTransmit = 0;
  34.  
  35.   // enable TWI (two-wire interface)
  36.   sbi(TWCR, TWEN);
  37.   // set state
  38.   I2cState = I2C_IDLE;
  39.   sbi(TWCR, TWEA);
  40.  
  41.   u08 bitrate_div;
  42.   // set i2c bitrate
  43.   // SCL freq = F_CPU/(16+2*TWBR))
  44.   #ifdef TWPS0
  45.     // for processors with additional bitrate division (mega128)
  46.     // SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
  47.     // set TWPS to zero
  48.     cbi(TWSR, TWPS0);
  49.     cbi(TWSR, TWPS1);
  50.   #endif
  51.   // calculate bitrate division  
  52.   bitrate_div = ((F_CPU/1000l)/bitrateKHz);
  53.   if(bitrate_div >= 16) bitrate_div = (bitrate_div-16)/2;
  54.   outb(TWBR, bitrate_div);
  55.  
  56.   // enable interrupts
  57.   sei();
  58. }


De snelheid van de I²C communicatie moet worden ingesteld tijdens het initialisatie proces. De normale snelheid is 100Khz, tegenwoordig is fast I²C, 400Khz, ook veel gebruikt of zelf nog hogere snelheden.


C code -
  1. //! send I2C data to a device on the bus (non-interrupt based)
  2. u08 i2cSend(u08 deviceAddr, u08 length, u08* data);
  3.  
  4.  
  5.  
  6. /**
  7.  *   @param     deviceAddr     target slave address
  8.  *   @param     length       data length
  9.  *   @param     data       pointer to data
  10.  *   @return     TWI bus status
  11.  */
  12. u08 i2cSend(u08 deviceAddr, u08 length, u08* data){
  13.   u08 retval = I2C_OK;
  14.  
  15.   i2cSendStart();
  16.   i2cWaitForComplete();
  17.   i2cSendByte( deviceAddr & 0xFE );
  18.   i2cWaitForComplete();
  19.  
  20.   // check if device is present and live
  21.   if( inb(TWSR) == TW_MT_SLA_ACK){
  22.     while(length)     {
  23.       i2cSendByte( *data++ );
  24.       i2cWaitForComplete();
  25.       length--;
  26.     }
  27.   }
  28.   else{
  29.     // device did not ACK it's address,
  30.     // data will not be transferred
  31.     // return error
  32.     retval = I2C_ERROR_NODEV;
  33.   }
  34.  
  35.   // transmit stop condition
  36.   i2cSendStop();
  37.   while( !(inb(TWCR) & BV(TWSTO)) );
  38.  
  39.   return retval;
  40. }


Bovenstaande functie is een high-level functie voor het verzenden van I²C data. Eerst wordt het start commando gestuurd, vervolgens wachten we totdat de TWI bus klaar is. Dan wordt het slave-adres verzonden. Als we data willen lezen van een slave, moet de 0e adresbit 0 zijn. Vervolgens wachten we weer, totdat het adres verzonden is.

Nu kan de data gestuurd worden, deze wordt byte per byte verzonden, na elke byte wordt er gewacht totdat de TWI bus klaar is met verzenden van de vorige byte. Ten slotte wordt het stop commando doorgestuurd.


C code -
  1. //! receive I2C data from a device on the bus (non-interrupt based)
  2. u08 i2cReceive(u08 deviceAddr, u08 length, u08 *data);
  3.  
  4.  
  5.  
  6. /**
  7.  *   @param     deviceAddr     target slave address
  8.  *   @param     length       data length
  9.  *   @param     data       pointer where need to be stored at
  10.  *   @return     TWI bus status
  11.  */
  12. u08 i2cReceive(u08 deviceAddr, u08 length, u08 *data){
  13.   u08 retval = I2C_OK;
  14.  
  15.   i2cSendStart();
  16.   i2cWaitForComplete();
  17.   i2cSendByte( deviceAddr | 0x01 );
  18.   i2cWaitForComplete();
  19.  
  20.   // check if device is present and live
  21.   if( inb(TWSR) == TW_MR_SLA_ACK){
  22.     // accept receive data and ack it
  23.     while(length > 1){
  24.       i2cReceiveByte(TRUE);
  25.       i2cWaitForComplete();
  26.       *data++ = i2cGetReceivedByte();
  27.       // decrement length
  28.       length--;
  29.     }
  30.  
  31.     // accept receive data and nack it (last-byte signal)
  32.     i2cReceiveByte(FALSE);
  33.     i2cWaitForComplete();
  34.     *data++ = i2cGetReceivedByte();
  35.   }
  36.   else{
  37.     // device did not ACK it's address,
  38.     // data will not be transferred
  39.     // return error
  40.     retval = I2C_ERROR_NODEV;
  41.   }
  42.  
  43.   // transmit stop condition
  44.   i2cSendStop();
  45.  
  46.   return retval;
  47. }


Het principe voor het ontvangen van I²C is vrij gelijkend op het verzenden. Eerst het start commando verzenden, wachten, slave-adres. Bij het opvragen van slavedata is de 0e adresbit 1. Na het verzenden van het slave-adres wachten we weer.

Ook het ontvangen gebeurt byte per byte, enkel de laatste byte is speciaal om te ontvangen, omdat het een nack – not ack – moet worden gegeven. Aan het einde van de datatransitie wordt het stop commande verzonden.
C code -
  1. //! init ds1631
  2. void ds1631Init(void);
  3.  
  4. //! start continue reading
  5. void ds1631StartReading(void);
  6.  
  7. //! read data from ds1631
  8. void ds1631Read(u08* d);
  9.  
  10.  
  11.  
  12.  
  13. /**
  14.  *   @param     void
  15.  *   @return   void
  16.  **/
  17. void ds1631Init(void){
  18.   // set control register
  19.   u08 buff[2];
  20.   buff[0] = 0xAC;
  21.   buff[1] = 0x0A;
  22.   i2cSend(ds1631addr, 2, buff);
  23. }
  24.  
  25. /**
  26.  *   @param     void
  27.  *   @return   void
  28.  **/
  29. void ds1631StartReading(void){
  30.   u08 buff[1];
  31.   buff[0] = 0xEE;
  32.   i2cSend(ds1631addr, 1, buff);
  33. }
  34.  
  35. /**
  36.  *   @param     *d     Pointer voor data opslag [3]
  37.  *   @return   void
  38.  **/
  39. void ds1631Read(u08* d){
  40.   *d = 0xAA;
  41.   i2cSend(ds1631addr, 1, d);
  42.   i2cReceive(ds1631addr, 2, d);
  43. }
C code -
  1. #define F_CPU 8000000
  2.  
  3. #define ds1631addr   0b10011110     ///< hex: 0xC7
  4.  
  5. #include "s/g.h"
  6. #include "s/rprintf.c"
  7. #include "s/i2c.c"
  8. #include "s/ds1631.c"
  9. #include "util/delay.h"
  10.  
  11.  
  12. //! main functie, start programma, moet altijd aanwezich zijn
  13. int main(void){
  14.   uartInit(UART_FULL, 19200);
  15.   rprintfInit(uartSendByte);
  16.   rprintf("UART PRESENTn");
  17.  
  18.   i2cInit(100);
  19.   ds1631Init();
  20.   ds1631StartReading();
  21.  
  22.   u08 temp[2];
  23.  
  24.   while(1){
  25.     ds1631Read(temp);
  26.     rprintf("DS1631: %d,%dn", temp[0], ((temp[1]==128) ? 5 : 0));
  27.  
  28.     _delay_ms(1000);  
  29.   }
  30.  
  31.   return 0;
  32. }
Nog geen reacties.
Naam
a-z A-Z 0-9 _

 
E-mail
Wordt niet getoont.
  (niet verplicht)
 
Mail sturen als
(optie)
Smoerijf reageerd
Eender wie reageaard
Nooit
 
Mail sturen
(optie)
Enkel mailen bij eerste reactie
Bij elke reactie mailen
 
Reactie
Laatste wijziging: Wo 15 April 2009, 23:14