Author Topic: STM32F446 weird interrupt routine  (Read 1757 times)

0 Members and 1 Guest are viewing this topic.

Offline RobotecTopic starter

  • Contributor
  • Posts: 44
  • Country: es
STM32F446 weird interrupt routine
« on: February 16, 2022, 07:07:58 pm »
Hello everyone,

I'm trying to read a AS5600 magnetic encoder through I2C at the maximum possible speed , I´ve achieved a stable 350 Khz clock in SCL(chip is connected with a small cable) but what bother me is that with the ISR routine. The peripheric should only make use of the CPU at the end of the data transmission but instead  I´ve got this ,

yellow is the i2c mem request, everything normal there.

light blue is the ISR being activated

purple is SCL and dark blue SDA.

I was hoping for the ISR to be activated after the transfer was completed no randomly until the end like something similar to polling and DMA is even worse.

any toughs?. In some places they say that's due some STM workaround for I2C problems in the peripheric.

 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1193
  • Country: ca
    • VE7XEN Blog
Re: STM32F446 weird interrupt routine
« Reply #1 on: February 16, 2022, 08:40:31 pm »
Code......?
73 de VE7XEN
He/Him
 
The following users thanked this post: Robotec

Offline RobotecTopic starter

  • Contributor
  • Posts: 44
  • Country: es
Re: STM32F446 weird interrupt routine
« Reply #2 on: February 17, 2022, 07:13:19 am »
This is the code based on CubeMx libraries:

The request i2C sentence :

void TIM6_DAC_IRQHandler(void) //TIM6 being called at 1 Khz
{
  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */
   HAL_GPIO_WritePin(GPIOB, Green_Led_Pin, GPIO_PIN_SET);
   if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
   {
     HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t) I2C_ADDRESS,(uint8_t) AS5600_REGISTER_RAW_ANGLE_HIGH,I2C_MEMADD_SIZE_8BIT,data,2);
   }
   HAL_GPIO_WritePin(GPIOB, Green_Led_Pin, GPIO_PIN_RESET);
  /* USER CODE END TIM6_DAC_IRQn 0 */
  HAL_DAC_IRQHandler(&hdac);
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */

  /* USER CODE END TIM6_DAC_IRQn 1 */
}


The ISR code:

void I2C1_EV_IRQHandler(void)
{
  /* USER CODE BEGIN I2C1_EV_IRQn 0 */
   HAL_GPIO_WritePin(GPIOB, Red_led_Pin, GPIO_PIN_SET);
  angle = ((data[0] << 8) | data[1]);

  /* USER CODE END I2C1_EV_IRQn 0 */
  HAL_I2C_EV_IRQHandler(&hi2c1);
  /* USER CODE BEGIN I2C1_EV_IRQn 1 */
   HAL_GPIO_WritePin(GPIOB, Red_led_Pin, GPIO_PIN_RESET);

  /* USER CODE END I2C1_EV_IRQn 1 */
}

as you can see is as basic as you can get, but still I have this weird behaviour.

 
 

Offline lucazader

  • Regular Contributor
  • *
  • Posts: 221
  • Country: au
Re: STM32F446 weird interrupt routine
« Reply #3 on: February 17, 2022, 07:15:06 am »
The best mode for this sort of operation on the F446 would likely be DMA.
This should give you an interupt only on completion (maybe one on half complete).
Since you are using the CubeMX you could look at this example in the HAL with how to implement a DMA transfer:
https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32446E-Nucleo/Examples/I2C/I2C_TwoBoards_ComDMA

Alternately you could look into a different magnetic encoder that uses an SPI interface, which should perform much better at these sorts of speeds, especially over distances since the SPI interface is Push pull rather than open drain.

Something like the TLE5012B might be suitable.
« Last Edit: February 17, 2022, 07:28:11 am by lucazader »
 
The following users thanked this post: Robotec

Offline RobotecTopic starter

  • Contributor
  • Posts: 44
  • Country: es
Re: STM32F446 weird interrupt routine
« Reply #4 on: February 17, 2022, 07:32:04 am »
Thanks, Problem is that is an already routed board and getting an SPI port would require a redesigning, I'm trying to get a PWM output and read it thought the PWM input in another timer.

as for the DMA mode I would say that is even worse... way more execution time being expended at the beginning.

 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1193
  • Country: ca
    • VE7XEN Blog
Re: STM32F446 weird interrupt routine
« Reply #5 on: February 17, 2022, 07:37:14 am »
There are quite a few I2C events that trigger ITEVFEN, and it doesn't look like a mask register is available. Some of them might also re-fire immediately if the state isn't cleared in the ISR, but I'm not too sure. So I'm not an expert on this chip, but I think you are stuck with filtering for the event you're actually interested in with software in the ISR. DMA should work fine, and using a DMA transfer complete interrupt instead would be a way to solve this.

Shouldn't be much to starting a DMA transfer once you've set up the DMA channel, which you only have to do once. Especially if the buffer address remains the same.

This is a 180MHz MCU, a few instructions to set up DMA or to check the I2C flags in the ISR shouldn't be a big deal or you're really running close to the margins.
« Last Edit: February 17, 2022, 07:39:10 am by ve7xen »
73 de VE7XEN
He/Him
 
The following users thanked this post: lucazader, Robotec

Offline lucazader

  • Regular Contributor
  • *
  • Posts: 221
  • Country: au
Re: STM32F446 weird interrupt routine
« Reply #6 on: February 17, 2022, 07:41:03 am »
Can you show us the code you have used for the DMA transfer you describe in the scope trace above?

As based on the timing in the scope trace. it is taking 110us to setup the transfer (which seems excessive). that would equate to ~19800 instructions at 180MHz. Which seems excessive..?
« Last Edit: February 17, 2022, 07:43:17 am by lucazader »
 
The following users thanked this post: Robotec

Offline ace1903

  • Regular Contributor
  • *
  • Posts: 237
  • Country: mk
Re: STM32F446 weird interrupt routine
« Reply #7 on: February 17, 2022, 08:23:30 am »
Be careful when using functions from CubeMX from interrupt handler. 
HAL_I2C_GetState and HAL_I2C_Mem_Read_IT are not optimized to be short and used from interrupt contexts.
You can easily make locks and corrupted data when using same data from interrupt and normal context without declaring it volatile.
Re check interrupt priorities and interrupt nesting settings in CubeMX.
 
The following users thanked this post: Robotec

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32F446 weird interrupt routine
« Reply #8 on: February 17, 2022, 08:46:43 am »
Which seems excessive..?
Even for the HAL!
In fact that long time is due to the HAL using blocking operations to send out the I2C address and the memory/register address, issue a read operation on the I2C address, and only then starting the DMA for the read operation.

I'm not sure that using DMA is warranted in this case (and in general for I2C, given the low data rate interrupt is quite OK IMO), unless one wants to rewrite the whole thing: you would need to have two DMA channels (one for reading, the other to write I2C and memory address) or use, say, IRQ for writing and DMA for reading...

As for the spurious interrupts  :-//
The same IRQ is also used for the DAC, is something going on that side, by chance (though the bursts seems too synchronised with the I2C data)?
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: Robotec

Offline wek

  • Frequent Contributor
  • **
  • Posts: 495
  • Country: sk
Re: STM32F446 weird interrupt routine
« Reply #9 on: February 18, 2022, 07:44:20 am »
Quote
There are quite a few I2C events that trigger ITEVFEN, and it doesn't look like a mask register is available. Some of them might also re-fire immediately if the state isn't cleared in the ISR,

This is most probably the case. The events are mostly mutually exclusive or they can be made so by proper sequencing. It's not trivial to get it right. But it works, after all, doesn't it?

OTOH, Cube is open source so you can debug it yourself.

Quote
as for the DMA mode I would say that is even worse... way more execution time being expended at the beginning.

Obviously the address-write stage is polled, only the data read is through DMA. You can rewrite that initial stage to interrupt-driven. Probably not something you can simply click in CubeMX.

JW
 
The following users thanked this post: Robotec

Offline RobotecTopic starter

  • Contributor
  • Posts: 44
  • Country: es
Re: STM32F446 weird interrupt routine
« Reply #10 on: February 18, 2022, 08:19:15 am »
Thanks everyone, one by one...


There are quite a few I2C events that trigger ITEVFEN, and it doesn't look like a mask register is available. Some of them might also re-fire immediately if the state isn't cleared in the ISR, but I'm not too sure. So I'm not an expert on this chip, but I think you are stuck with filtering for the event you're actually interested in with software in the ISR. DMA should work fine, and using a DMA transfer complete interrupt instead would be a way to solve this.

Shouldn't be much to starting a DMA transfer once you've set up the DMA channel, which you only have to do once. Especially if the buffer address remains the same.

This is a 180MHz MCU, a few instructions to set up DMA or to check the I2C flags in the ISR shouldn't be a big deal or you're really running close to the margins.

HAL functions clear the different flags in IT in HAL_I2C_EV_IRQHandler(&hi2c1); so no problem there.

DMA already tried and shown before, didn't change much:       HAL_I2C_Mem_Read_DMA(&hi2c1,(uint16_t) I2C_ADDRESS,(uint8_t) AS5600_REGISTER_RAW_ANGLE_HIGH,I2C_MEMADD_SIZE_8BIT,data,2);

Also changed the code to receive the data from the i2C interrupt to the DMA one.

And yeah what troubles me is that in a 180 MHz  wouldn't mind if the chip was doing other tasks , the I2C being slow would only limit he maximum sampling rate of the AS5600, but for some reason in IT mode setting interrupt is as fast as expected but has many ISR interrupts but in DMA takes forever to setting but when is done it works perfectly. Ideally a setting like the IT and working like the DMA would be the perfect match (and how it should work..).


Can you show us the code you have used for the DMA transfer you describe in the scope trace above?

As based on the timing in the scope trace. it is taking 110us to setup the transfer (which seems excessive). that would equate to ~19800 instructions at 180MHz. Which seems excessive..?

Complete code:

Code: [Select]

static void MX_DMA_Init(void)
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}

void DMA1_Stream0_IRQHandler(void)
{
  HAL_GPIO_WritePin(GPIOB, LED_Rojo_Pin, GPIO_PIN_SET);
  HAL_DMA_IRQHandler(&hdma_i2c1_rx);
  angle = ((data[0] << 8) | data[1]);
  HAL_GPIO_WritePin(GPIOB, LED_Rojo_Pin, GPIO_PIN_RESET);
}

void TIM6_DAC_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */
HAL_GPIO_WritePin(GPIOB, LED_Verde_Pin, GPIO_PIN_SET);
if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
{
  HAL_I2C_Mem_Read_DMA(&hi2c1,(uint16_t) I2C_ADDRESS,(uint8_t) AS5600_REGISTER_RAW_ANGLE_HIGH,I2C_MEMADD_SIZE_8BIT,data,2);
}
HAL_GPIO_WritePin(GPIOB, LED_Verde_Pin, GPIO_PIN_RESET);
  /* USER CODE END TIM6_DAC_IRQn 0 */
  HAL_DAC_IRQHandler(&hdac);
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */

  /* USER CODE END TIM6_DAC_IRQn 1 */
}


As for the DAC might be firing, DAC is not activated in this application.

And I completely agree,  110us is crazy , so it may be a bug(HAL style...) or some patch for I2C peripheric problems, all said in both cases I read the data completely fine, its just that it takes forever...

Be careful when using functions from CubeMX from interrupt handler. 
HAL_I2C_GetState and HAL_I2C_Mem_Read_IT are not optimized to be short and used from interrupt contexts.
You can easily make locks and corrupted data when using same data from interrupt and normal context without declaring it volatile.
Re check interrupt priorities and interrupt nesting settings in CubeMX.


Completely agreed and didn't check it, thank for the tip.


Which seems excessive..?
Even for the HAL!
In fact that long time is due to the HAL using blocking operations to send out the I2C address and the memory/register address, issue a read operation on the I2C address, and only then starting the DMA for the read operation.

I'm not sure that using DMA is warranted in this case (and in general for I2C, given the low data rate interrupt is quite OK IMO), unless one wants to rewrite the whole thing: you would need to have two DMA channels (one for reading, the other to write I2C and memory address) or use, say, IRQ for writing and DMA for reading...

As for the spurious interrupts  :-//
The same IRQ is also used for the DAC, is something going on that side, by chance (though the bursts seems too synchronised with the I2C data)?

Seriously, what a advantage to use DMA if it blocks the system until the read command is issued, but why doesn't has it the same behaviour in IT mode then? in IT the setting of the ISR is very short in contrast to the DMA one (which was what I was expecting, a short setting up and only a short time reading the data received).

DAC is not used, so nothing there, it only shared ISR routine in F446.

Quote
There are quite a few I2C events that trigger ITEVFEN, and it doesn't look like a mask register is available. Some of them might also re-fire immediately if the state isn't cleared in the ISR,

This is most probably the case. The events are mostly mutually exclusive or they can be made so by proper sequencing. It's not trivial to get it right. But it works, after all, doesn't it?

OTOH, Cube is open source so you can debug it yourself.

Quote
as for the DMA mode I would say that is even worse... way more execution time being expended at the beginning.

Obviously the address-write stage is polled, only the data read is through DMA. You can rewrite that initial stage to interrupt-driven. Probably not something you can simply click in CubeMX.

JW

Yeah, if works without a problem and cant complain there, that's for sure.

As you said I already said I already tried to debug it but without success.

And I was fearing that, in fact it shouldn't be much of a problem, just a IT driven write followed by a DMA read with HAL_I2C_Master_Transmit_IT and in the finishing interrupt send a flag to activate HAL_I2C_Master_Receive_DMA().

I´m starting to see why this has not implemented this way, It would need of both DMA and I2C IT being synchronized and is very application specific so  it a more of a trouble to implement in a general way than doing it the way they have done. As you said, hey it works.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf