1

I am using a STM32 board to send I2C commands to a I2C slave using interrupt mode. I have initialized I2C module as below,

  hi2c2.Instance = I2C2;
  hi2c2.Init.Timing = 0x00303D5B;
  hi2c2.Init.OwnAddress1 = 0;
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;
  hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
  {
    Error_Handler();
  }

And for transfer I use the HAL API,

HAL_I2C_Master_Transmit_IT(&hi2c2, 0x60, buffer, 2);

But this code doesn't seem to work for me.

In buffer, first byte is the register address on the I2C slave and second byte is the data. When I use the blocking mode API,

HAL_I2C_Master_Transmit(&hi2c2, 0x60, buffer, 2,HAL_MAX_DELAY);

It works fine. Any suggestions what could be wrong here?

AR26
  • 29
  • 6

3 Answers3

0

Have you actually enabled the I2C Interrupt? That might be the Problem.

0

Take a look at your HAL_I2C_MspInit function and if HAL_NVIC_SetPriority(I2C2_IRQn, 0, 0) and HAL_NVIC_EnableIRQ(I2C2_IRQn) are called.

0

The reason your interrupt HAL call doesn't work while your polling one does, is because when using the interrupt, you need to make sure that the bus is ready first. You have to manually do the "timeout" that is embedded in the HAL_MAX_DELAY of the polling HAL call.

Thus:

  1. Polling implementation (that you called correctly):
HAL_I2C_Master_Transmit(&hi2c2, 0x60, buffer, 2, HAL_MAX_DELAY);
  1. Interrupt implementation:
while (HAL_I2C_GetState(&hi2c2) != HAL_I2C_STATE_READY)
{
}
HAL_I2C_Master_Transmit_IT(&hi2c2, 0x60, buffer, 2);

will do the trick.

It also means that without the ready check, your code could work as intended if you call your interrupt HAL at a low enough frequency, because then, the bus is always ready when you need it. This is a pernicious bug: in appearance, your code could function without the check... but only by accident (or luck, depending on how you call it).

MacroController
  • 322
  • 2
  • 6
  • 19
  • But doesn't the while loop completely render the idea of using an interrupt redundant? You can either poll which is a blocking method, or use the interrupt method which also requires a blocking while loop. I'm having the same issue with interrupts, but even the official STM32 examples are using a similar while loop to block until i2c is ready. What am I missing? This seems like a very inefficient way to work. – ritchie888 Apr 22 '22 at 14:01
  • Interruptions are not a magical tool, they cannot guess your bus state. Low-level implementation means doing a lot things yourself (like making sure that you solicit the I2C only when it can be), even when using an API. It can feel heavy, or inefficient, or inelegant. But if you think of it that way, at some point or another, when your bus is notified of a packet to transmit... it's gonna have to wait 'till it's ready for the transmission to start. You WILL have to have a waiting loop, even if in some cases it can be for a wait of 0. – MacroController Apr 25 '22 at 10:19
  • If you're concerned with good practices, being critical of while loops is good, but does not mean you have eliminate them, just that you have to identify the "bad" ones. And "bad" really just means "unnecessary". This while loop might look bad to you, but bottom line is, it is necessary. Otherwise, you'll solicit the I2C when it's not ready. Also, it doesn't trump the interruption: the while loop is out of the interruption, so you absolutely can be interrupted in the while loop. – MacroController Apr 25 '22 at 10:19
  • If you're looking for good practices, you can focus on making interruptions one line instructions. Using state-machines to dictate your firmware, and ONLY changing the states in the interruptions, while data treatment happens outside of the interruptions. Of course, some process require not to be interrupted, and you'll use locks. But yeah, broke - using loops (and not interrupts); woke - using interrupts and not loops 'cause they're evil; bespoke - using while loops where necessary when you'll have to wait for resources; forbidden - using while loops inside an interrupt (NO!) – MacroController Apr 25 '22 at 10:19
  • I partially get what you're saying, but I can't understand why as the user we need to invoke these blocking methods for functions which are supposed to remove the need for blocking methods. Take DMA for example, which I've today learnt also needs to determine the i2c state in a while loop before moving onto the next transmission. As far as I'm aware, DMA is used to take the load off the main process, so why are we required to wait until the i2c state is clear before sending another command in the main process? Isn't that what DMA is supposed to mitigate? – ritchie888 Apr 25 '22 at 14:35
  • Isn't DMA used as a sort of 'stacking tool', whereby we can stack commands (e.g., transmit over i2c) in the background, and DMA sends them when available? The need to wait until the i2c bus is free seems like a fundamental function of DMA to me, I don't understand why the user has to check this state themselves. Is there not a way to simply call these commands, have them stacked in the background, and cleared once they're performed? The idea of only being able to send one command at a time negates a lot of the benefits of interrupts and DMA to me. – ritchie888 Apr 25 '22 at 14:44