Add software I2C article.
1 files changed, 127 insertions(+), 0 deletions(-)

A => contents/expert/software-i2c.rst
A => contents/expert/software-i2c.rst +127 -0
@@ 0,0 1,127 @@ 
+Software I2C master implementation
+==================================
+
+:date: 2017-06-20 23:30
+:tags: i2c, tmp102
+
+Some time ago I was spending evening with a friend and it came up that
+I had not ever written software I2C master routines, even though I have
+used I2C quite much in various projects.
+
+So, to correct this omission, we armed ourselves with `the I2C specification <http://www.nxp.com/documents/user_manual/UM10204.pdf>`_,
+sit down for few hours, and composed together a simple C implementation for I2C master
+using GPIO pins.
+
+The code was for Atmel SAMD21 and as such could not be used with AVR-Ada.
+This led me to rewrite the code in Ada.
+
+I abstracted the `core logic <https://bitbucket.org/tkoskine/arduino-blog/src/8c55f8b849222c23639bacbe8ddbabcc64d8c08c/examples/soft-i2c/soft_i2c.adb?fileviewer=file-view-default>`_
+into a `generic package <https://bitbucket.org/tkoskine/arduino-blog/src/8c55f8b849222c23639bacbe8ddbabcc64d8c08c/examples/soft-i2c/soft_i2c.ads?fileviewer=file-view-default>`_,
+which can be used on any platform as long as few procedures are provided by the user:
+
+::
+
+   generic
+      with procedure Pull_SDA_Down;
+      with procedure Release_SDA_Up;
+      with procedure Pull_SCL_Down;
+      with procedure Release_SCL_Up;
+      with procedure Delay_T_HD_STA; -- 4.0
+      with procedure Delay_T_SU_STO; -- 4.0
+      with procedure Delay_T_Buf; -- 4.7..5
+      with procedure Delay_T_Low_Half; -- 2.4
+      with procedure Delay_T_High; -- 5
+      with function SDA_State return Boolean;
+   package Soft_I2C is
+
+      type Byte_Array is
+        array (Interfaces.Unsigned_8 range <>) of Interfaces.Unsigned_8;
+
+      type Error_Status is
+        (SOFT_I2C_OK,
+         SOFT_I2C_NACK,
+         SOFT_I2C_FAILED);
+
+      procedure Start;
+      procedure Stop;
+      procedure Write_Byte (Byte : Interfaces.Unsigned_8;
+                            Status : out Error_Status);
+      procedure Read_Byte (Byte : out Interfaces.Unsigned_8; Ack : Boolean);
+
+      procedure Write (Address : Interfaces.Unsigned_8; Bytes : Byte_Array;
+                       Status  : out Error_Status);
+
+      procedure Read (Address : Interfaces.Unsigned_8; Bytes : in out Byte_Array);
+   end Soft_I2C;
+
+
+The user need to provide GPIO pin manipulation procedures/functions::
+
+   with procedure Pull_SDA_Down;
+   with procedure Release_SDA_Up;
+   with procedure Pull_SCL_Down;
+   with procedure Release_SCL_Up;
+   ...
+   with function SDA_State return Boolean;
+
+
+And procedures which implement the required delays between pin changes::
+
+   with procedure Delay_T_HD_STA; -- 4.0 usecs
+   with procedure Delay_T_SU_STO; -- 4.0 usecs
+   with procedure Delay_T_Buf; -- 4.7..5 usecs
+   with procedure Delay_T_Low_Half; -- 2.4 usecs
+   with procedure Delay_T_High; -- 5 usecs
+
+
+An implementation for AVR-Ada and Arduino UNO (atmega328p) is provided
+in `uno_i2c.ads <https://bitbucket.org/tkoskine/arduino-blog/src/8c55f8b849222c23639bacbe8ddbabcc64d8c08c/examples/soft-i2c/uno_i2c.ads?fileviewer=file-view-default>`_
+and `uno_i2c.adb <https://bitbucket.org/tkoskine/arduino-blog/src/8c55f8b849222c23639bacbe8ddbabcc64d8c08c/examples/soft-i2c/uno_i2c.adb?fileviewer=file-view-default>`_.
+
+.. image:: http://arduino.tkoskine.fastmail.com.user.fm/tmp102_temperature_sensor.jpg?asatt=1&variant=small
+   :alt: TMP102 temperature sensor
+
+Following example code shows how to read TMP102 temperature sensor value using the package:
+
+::
+
+   procedure Test_I2C is
+      use type Interfaces.Unsigned_8;
+
+      TMP102_Address : constant := 16#90#;
+      Data : Uno_I2C.I2C.Byte_Array (1..2) := (0, 0);
+      Cmd : Uno_I2C.I2C.Byte_Array (1..1) := (1 => 16#00#);
+      Status : Uno_I2C.I2C.Error_Status;
+      Temp_Value : Integer;
+   begin
+      AVR.UART.Init (103);
+      loop
+         Data := (0, 0);
+         Uno_I2C.Write (Address => TMP102_Address, Bytes => Cmd, Status => Status);
+         Uno_I2C.Read (Address => TMP102_Address, Bytes => Data);
+         Temp_Value := Integer (Data (1)) * 256;
+         Temp_Value := Temp_Value + Integer (Data (2));
+         Temp_Value := Temp_Value / 256; -- Basically we ignore the second byte
+
+         AVR.UART.Put ("T:");
+         
+         if Temp_Value > 0 then
+            Data (1) := Interfaces.Unsigned_8 (Temp_Value);
+         else
+            AVR.UART.Put ("-");
+            Data (1) := Interfaces.Unsigned_8 (-Temp_Value);
+         end if;
+         AVR.UART.Put (Data (1), Base => 10);
+         AVR.UART.Put (" C");
+         AVR.UART.CRLF;
+         delay 2.0;
+      end loop;
+   end Test_I2C;
+
+Full code is available under ISC license at `my arduino-blog Bitbucket repository <https://bitbucket.org/tkoskine/arduino-blog/src/default/examples/soft-i2c/>`_.
+
+As usual, some caveats:
+
+* Code doesn't implement all I2C master features, like clock stretching.
+* The example code for Arduino UNO uses 80kHz I2C bus speed. Faster is not possible easily.
+* Read and Write procedures expect 8-bit I2C addresses.