@@ 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.