1

I have a Nucleo-F446RE, and I'm trying to get the I2C working with an IMU I have (LSM6DS33). I am using STM32CubeMX and checked out all the example code for my board which is related to I2C. Specifically I'll be talking about their 'I2C_TwoBoards_ComIT' example, but all their examples which use the interrupt method have this same quirk. Here is a snipped of their code from main.c:

  /* The board sends the message and expects to receive it back */
  do
  {
    /*##-2- Start the transmission process #####################################*/  
    /* While the I2C in reception process, user can transmit data through 
    "aTxBuffer" buffer */
    if(HAL_I2C_Master_Transmit_IT(&I2cHandle, (uint16_t)I2C_ADDRESS, (uint8_t*)aTxBuffer, TXBUFFERSIZE)!= HAL_OK)
    {
      /* Error_Handler() function is called in case of error. */
      Error_Handler();
    }

    /*##-3- Wait for the end of the transfer ###################################*/  
    /*  Before starting a new communication transfer, you need to check the current   
    state of the peripheral; if it’s busy you need to wait for the end of current
    transfer before starting a new one.
    For simplicity reasons, this example is just waiting till the end of the 
    transfer, but application may perform other tasks while transfer operation
    is ongoing. */ 
    while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY)
    {
    }

    /* When Acknowledge failure occurs (Slave don't acknowledge its address)
    Master restarts communication */
  }
  while(HAL_I2C_GetError(&I2cHandle) == HAL_I2C_ERROR_AF); 

Under comment ##-3- they explain that unless we wait for the I2C state to be ready again, after sending a command, the next command will overwrite the previous one, so they use a while loop which waits for the I2C state to be 'ready' before continuing.

Isn't this a very inefficient way to use an interrupt, and no different from using the standard polling method? Both block the main code, so what's the purpose of the interrupt?

In my personal example, I want to collect the accelerometer/gyroscope data at the 1.66 kHz rate which the IMU is capable of. I use a 2kHz timer to send an I2C command to read the acc/gyr data-ready register, and if the data is ready for either sensor I read their 6 bytes to get the x/y/z plane information. Using the polling method is too slow as blocking the code at a rate of 2kHz is not inefficient, but the interrupt method doesn't seem to be any faster as I still need to hang the system during the aforementioned while loop to check if I2C is ready for another command. What am I missing here?

ritchie888
  • 583
  • 3
  • 12
  • 27
  • Your question is not very clear but I suggest you to use a 2kHz timer to periodically transmit your i2c request and then use a i2c reception interrupt to process incoming datas. I would remove the while(getState) part – GabrielT Apr 22 '22 at 14:34
  • This piece of code waits a lot because it checks for acknowledge errors and tries again, you can do it differently but it depends a lot on what else your code aims to accomplish – GabrielT Apr 22 '22 at 14:42
  • The while(getState) is necessary for the interrupt/DMA methods to work, as far as I can tell. If I have two i2c transmit commands one after an other, the first will be overwritten by the second if the while(getState) is not present. – ritchie888 Apr 22 '22 at 14:43
  • If you start a new Tx before the previous one ended it will indeed overwrite the current transaction, but I would rather check if the previous Tx is ended when I need to start a new one than always stuck waiting on transaction end. The two while in the code totally negates the benefits of the interrupt use – GabrielT Apr 22 '22 at 14:52
  • > The two while in the code totally negates the benefits of the interrupt use That's entirely my question. The code snippet is from the STM32 examples, so figure they know what they're doing. I'm asking why they have used this blocking code in an example about using interrupts, which should be a non-blocking method of achieving the goal. – ritchie888 Apr 22 '22 at 14:54
  • 1
    It doesn't negate it if you do something else useful in between starting one transfer and waiting for it to finish - but this is an example project with a single purpose, it doesn't have anything else useful to do. – Tom V Apr 22 '22 at 15:03
  • ST provide working examples but rarely well built ones. I also work on stm32s (H7 atm) and I never use HAL libs/CubeMX/examples for this exact reason – GabrielT Apr 22 '22 at 15:03

1 Answers1

0

Is this (the example you provided) an efficient way of doing things? No. Can blocking part be avoided? Yes. It's only a small example, a proof of concept, so there is some blocking in there. You should look deeper at why it is there and how can you implement what it does without blocking.

The point of that blocking part is to not start an I2C communication while another I2C communication is in progress. The problem is that while your line of code to send something over I2C has already been executed, the data is still being physically sent over the line, just because your MCU is much faster than I2C. You need to wait until I2C line is idle and available for transmission.

How to achieve that with interrupts and not waste cycles and processing time? Given in your case you can easily estimate the amount of data per each transmission, there is no probem to estimate how much time every transmission will take given your I2C speed. Since you're smartly and correctly using timer to schedule regular transmissions, you should be able to set the timer in such a way that by the next timer interrupt, which will send data, your previous communication has already ended.

For example, if you set the timer to 1Hz to start transmission, you can obviously be sure that by the next interrupt all the communication has happened. You don't need to poll anything at all.

I don't see much point in I2C-polling the IC at 2kHz if it produces data at 1.6kHz. You will have uneven time periods between samples, some data will be very fresh, while some data will come with little delay, plus there will be communication without data ready. It would be better to poll it at something like 1.5-1.6kHz and just expect data to always be there. Of course, given the communication fits into 1.5kHz period, which requires some napkin math.

Ilya
  • 992
  • 7
  • 14