Frédéric Praca
Frédéric Praca

Reputation: 1641

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 :)

Upvotes: 3

Views: 6497

Answers (2)

Fr&#233;d&#233;ric Praca
Fr&#233;d&#233;ric Praca

Reputation: 1641

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.

Upvotes: 2

ralf htp
ralf htp

Reputation: 9422

No, between HAL_I2C_Mem_Read and the HAL_I2C_Master_Transmit, wait, HAL_I2C_Master_Receive procedure is only a nuance cf How do I use the STM32CUBEF4 HAL library to read out the sensor data with i2c? . If you know what size of data you want to receive you can use the HAL_I2C_Master_Transmit, wait, HAL_I2C_Master_Receive procedure.

A C++ HAL I2C example is in https://letanphuc.net/2017/05/stm32f0-i2c-tutorial-7/

//Trigger Temperature measurement 
buffer[0]=0x00;  
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);  
HAL_Delay(20);  
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);  
//buffer[0] : MSB data 
//buffer[1] : LSB data 
rawT = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit  
Temperature = ((float)rawT/65536)*165.0 -40.0;  
//Trigger Humidity measurement buffer[0]=0x01; 
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);  
HAL_Delay(20);  
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);  
//buffer[0] : MSB data 
//buffer[1] : LSB data 
rawH = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit  
Humidity = ((float)rawH/65536)*100.0; HAL_Delay(100); }

Note that it uses HAL_I2C_Master_Transmit, waits 20 ms until the slave puts the data on the bus and then receives it with HAL_I2C_Master_Receive. This code is working, i tested it myself.

Possibly the problem is that the BME280 supports single byte reads and multi-byte reads (until it sends a NOACK and stop). HAL_I2C_Mem_Read waits for the ACK or stop but for some reasons it does not get it what causes the Busy and then Timeout behavior, cf page 33 of the datasheet http://www.embeddedadventures.com/datasheets/BME280.pdf for the multibyte read. You specified timeout to 15 sec and you get the timeout after 15 secs. So it appears that the BME280 simply does not stop sending or it sends nothing including not a NOACK and Stop condition ...

HAL_I2C_Mem_Read sometimes causes problems, this depends on the slave https://community.arm.com/developer/ip-products/system/f/embedded-forum/7989/trouble-getting-values-with-i2c-using-hal_library

By the way with the

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);

you try to read 1 byte the chip identification number from register D0 cf http://www.embeddedadventures.com/datasheets/BME280.pdf page 26

Upvotes: 1

Related Questions