Search code examples
stm32adagnat

I2C returning Busy or Error on memory reading


I started the following code to handle a Bosch BME280 sensor with a Nucleo-F446ZE and a Nucleo-F411RE boards.

with STM32.Device; use STM32.Device;
with STM32.GPIO; use STM32.GPIO;
with STM32; use STM32;
with STM32.I2C;

with HAL.I2C; use HAL.I2C;
use HAL;

procedure Simple_I2C_Demo is

   --  I2C Bus selected
   Selected_I2C_Port      : constant access STM32.I2C.I2C_Port := I2C_1'Access;
   Selected_I2C_Port_AF   : constant GPIO_Alternate_Function := GPIO_AF_I2C1_4;
   Selected_I2C_Clock_Pin : GPIO_Point renames PB8;
   Selected_I2C_Data_Pin  : GPIO_Point renames PB9;

   Port : constant HAL.I2C.Any_I2C_Port := Selected_I2C_Port;

   --  Shift one because of 7-bit addressing
   I2C_Address : constant HAL.I2C.I2C_Address := 16#76# * 2;


   procedure SetupHardware is
      GPIO_Conf_AF : GPIO_Port_Configuration (Mode_AF);
      Selected_Clock_Speed : constant := 10_000;
   begin
      Enable_Clock (Selected_I2C_Clock_Pin);
      Enable_Clock (Selected_I2C_Data_Pin);

      Enable_Clock (Selected_I2C_Port.all);

      STM32.Device.Reset (Selected_I2C_Port.all);

      Configure_Alternate_Function (Selected_I2C_Clock_Pin, Selected_I2C_Port_AF);
      Configure_Alternate_Function (Selected_I2C_Data_Pin, Selected_I2C_Port_AF);

      GPIO_Conf_AF.AF_Speed       := Speed_100MHz;
      GPIO_Conf_AF.AF_Output_Type := Open_Drain;
      GPIO_Conf_AF.Resistors   := Pull_Up;
      Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
      Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);

      STM32.I2C.Configure
        (Selected_I2C_Port.all,
         (Clock_Speed          => Selected_Clock_Speed,
          Addressing_Mode      => STM32.I2C.Addressing_Mode_7bit,
          Own_Address          => 16#00#, others => <>));

      STM32.I2C.Set_State (Selected_I2C_Port.all, Enabled => True);
   end SetupHardware;

   ID : HAL.I2C.I2C_Data (1 .. 1);
   Status : HAL.I2C.I2C_Status;

begin

   SetupHardware;

   HAL.I2C.Mem_Read (This          => Port.all,
                 Addr          => I2C_Address,
                 Mem_Addr      => 16#D0#,
                 Mem_Addr_Size => HAL.I2C.Memory_Size_8b,
                 Data          => ID,
                 Status        => Status,
                 Timeout => 15000);

   if Status /= Ok then
      raise Program_Error with "I2C read error:" & Status'Img;
   end if;
end Simple_I2C_Demo;

In this simple example, I always get an error status at the end of reading. In the context of a more complete code, I always get a Busy status after waiting 15secs.

I really don't see what is going on as my code is largely inspired from the code I found on Github for a I2C sensor.

Maybe I forgot a specific code for I2C init but as I'm not an expert, I prefer to ask to experts :)


Solution

  • Finally found what was wrong. After testing with C using STM HAL and investigating the Ada configuration code, I found that a line was missing:

          GPIO_Conf_AF.AF_Speed       := Speed_100MHz;
          GPIO_Conf_AF.AF_Output_Type := Open_Drain;
          GPIO_Conf_AF.Resistors   := Pull_Up;
    
          -- Missing configuration part of the record
          GPIO_Conf_AF.AF := Selected_I2C_Port_AF;
          -- That should be present even though there was a call to configure
          -- each pin few lines above
    
          Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
          Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);
    

    Using Configure_IO after Configure_Alternate_Function crushes the configuration and, as there was a part of the record which was left uninitialized, the GPIO were incorrectly configured.

    To be more precise, after looking at the code inside the GPIO handling, Configure_IO calls Configure_Alternate_Function using the AF part of the GPIO_Port_Configuration record. In my case, it was resetting it.

    With the missing line, the code now runs correctly with Mem_Read and Master_Transmit/Master_Receive.

    A big thanks to ralf htp for advising me to dive into the generated C code.