Wiki copymirror

This is a mirror of the wiki at pascal.chat. Editing, history and sign-in are there.

AVR Embedded Tutorial - I²C, TWI

Deutsch · English

I²C / TWI

This code is for an Atmega328/Arduino with 16MHz clock.

How to control the UART can be found here: UARTInit and UARTSendString (...

Description

This example demonstrates the communication with the I²C/TWI interface.

Two AD1115 A/D converters are used as slave devices. These are very easy to use and are also very easy to obtain. What is very important is that with TWIStart you shift the address one bit to the left and then combine it with or using Write/Read. There are many tutorials on this.

PulUp resistors are not necessary because they are already installed on the ADS1115 board (Caution: check your board for pullup resistors).

I²C functions

Constants

pascal
  const
    CPU_Clock = 16000000; // Arduino clock frequency, default 16MHz.
    ADSaddr0  = $48;      // Address converter 0
    ADSaddr1  = $49;      // Address converter 1
    I2C_clock = 400000;   // I²C clock (400KHz)
    TWI_Write = 0;
    TWI_Read  = 1;

Initialize I²C

pascal
  procedure TWIInit;
    const
      TWBR_val = byte((CPU_Clock div I2C_clock) - 16) div 2;
    begin
      TWSR := 0;
      TWBR := byte(TWBR_val);
    end;

Start I²C send/receive

pascal
  procedure TWIStart(addr : byte);
   begin
     // Initiate sending / receiving
     TWCR := 0;
     TWCR := (1 shl TWINT) or (1 shl TWSTA) or (1 shl TWEN);

     while ((TWCR and (1 shl TWINT)) = 0) do 
       begin
       end;

     // Send the address of the end device
     TWDR := addr;
     TWCR := (1 shl TWINT) or (1 shl TWEN);

     while((TWCR and (1 shl TWINT)) = 0) do 
       begin
       end;
   end;

I²C transmission / reception ended

pascal
  procedure TWIStop ;
    begin
      TWCR := (1 shl TWINT) or (1 shl TWSTO) or (1 shl TWEN);
    end;

Send characters

pascal
  procedure TWIWrite(u8data : byte);
    begin
      TWDR := u8data;
      TWCR := (1 shl TWINT) or (1 shl TWEN);

      while((TWCR and (1 shl TWINT)) = 0) do 
        begin
        end;
    end;

Receive characters

Except for the last character, characters must be read with TWIReadACK (.... The last character must be read with TWIReadNACK(...

pascal
  // Read to the penultimate character.
  function TWIReadACK : byte;
  begin
    TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA);
     
    while (TWCR and (1 shl TWINT)) = 0 do 
      begin
      end;

   Result := TWDR;
  end;

  // read last character.
  function TWIReadNACK : byte ;
  begin
    TWCR := (1 shl TWINT) or (1 shl TWEN);

    while(TWCR and (1 shl TWINT)) = 0 do 
      begin
      end;
   
    Result := TWDR;
  end;

Note: These functions get stuck in the event of a hardware error or the slave address is wrong!

Character received with error query

So that these functions do not get stuck, you have to install a counter in the while loop.

When the counter reaches 255, the loop is interrupted and an error is output.

255 is reached if no device is found at the I²C address.

pascal
  // Read to the penultimate character.
 function TWIReadACK_Error : byte;
 var
   err : byte;
 begin
   err := 0;
   TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWA);

   while ((TWCR and (1 shl TWINT)) = 0) and (err < 255) do 
     begin
        Inc(err);
     end;

   if err = 255 then 
     begin
       UARTSendString('I²C-Timeout');
       Result := 0;
     end 
   else 
     begin
       Result := TWDR;
     end;
 end;

 // read last character.
 function TWIReadNACK_Error : byte;
 var
   err : byte;
 begin
   err  := 0;
   TWCR := (1 shl TWINT) or (1 shl TWEN);

   while((TWCR and (1 shl TWINT)) = 0) and (err < 255) do 
     begin
       Inc(err);
     end;

   if err = 255 then 
     begin
       UARTSendString('I²C-Timeout');
       Result := 0;
     end 
   else 
     begin
       Result := TWDR;
     end;
 end;

ADS1115 specific code

This code is ADS1115 specific, for other I²C devices you will have to check their data sheets.

pascal
  // Send parameters to ADS1115 and initiate measurement
   procedure WriteADS1115(addr : UInt16);
   begin
     TWIStart((addr shl 1) or TWI_Write);
     TWIWrite(1);
     TWIWrite(%11000011);  // High
     TWIWrite(%11100011);  // low
     TWIStop;
   end;

   // Read out measurement from ADS1115.
   function ReadADS1115(addr : UInt16) : UInt16;
   begin
     TWIStart((addr shl 1) or TWI_Write);
     TWIWrite(0);
     TWIStop;

     TWIStart((addr shl 1) or TWI_Read);
     Result := TWIReadACK * $100 + TWIReadNACK;
     TWIStop;
   end;

Example

pascal
  var
   Data : Integer;
   s    : ShortString;

 begin
   asm cli END ;  // block interrupt
   UARTInit;
   TWIInit;
   asm be END;    // release interrupt

   repeat
     WriteADS1115(ADSaddr0);
     Data := ReadADS1115(ADSaddr0);

     str(Data : 6, s);
     UARTSendString('Channel 0:');
     UARTSendString(s);

     WriteADS1115(ADSaddr1);
     Data := ReadADS1115(ADSaddr1);

     str(Data : 6, s);
     UARTSendString('Channel 1:');
     UARTSendString(s);
     UARTSendString(#13#10);
   until 1 = 2;
 end.

ADS1115 parameters

A list of the parameters for the ADS1115. More information is in the data sheet.

Bit assignment of the ADS1115:

Low: 11100011 High: 11000011

<thead>

</thead> <tbody>

<strong>1</strong> : Start a single conversion (when in power-down state)</td>

<strong>001</strong> : AINP = AIN0 and AINN = AIN3<br /> <strong>010</strong> : AINP = AIN1 and AINN = AIN3<br /> <strong>011</strong> : AINP = AIN2 and AINN = AIN3<br /> <strong>100</strong> : AINP = AIN0 and AINN = GND<br /> <strong>101</strong> : AINP = AIN1 and AINN = GND<br /> <strong>110</strong> : AINP = AIN2 and AINN = GND<br /> <strong>111</strong> : AINP = AIN3 and AINN = GND</td>

<strong>001</strong> : FSR = ±4.096 V<br /> <strong>010</strong> : FSR = ±2.048 V (default)<br /> <strong>011</strong> : FSR = ±1.024 V<br /> <strong>100</strong> : FSR = ±0.512 V<br /> <strong>101</strong> : FSR = ±0.256 V<br /> <strong>110</strong> : FSR = ±0.256 V<br /> <strong>111</strong> : FSR = ±0.256 V<br />

<strong>1</strong> : Single-shot mode or power-down state (default)</td>

<strong>001</strong> : 16 SPS<br /> <strong>010</strong> : 32 SPS<br /> <strong>011</strong> : 64 SPS<br /> <strong>100</strong> : 128 SPS (default)<br /> <strong>101</strong> : 250 SPS<br /> <strong>110</strong> : 475 SPS<br /> <strong>111</strong> : 860 SPS<br />

<strong>1</strong> : Window comparator</td>

<strong>1</strong> : Active high</td>

<strong>1</strong> : Latching comparator</td>

<strong>01</strong> : Assert after two conversions<br /> <strong>10</strong> : Assert after four conversions<br /> <strong>11</strong> : Disable comparator and set ALERT/RDY pin to high-impedance (default)</td>

</tbody>

See also


From the Free Pascal wiki, page "AVR Embedded Tutorial - I²C, TWI", as it stood on 2020-03-06 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 2, last changed by WikiArchive .