cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_GPIO_EXTI_Callback() Only called half the time.

JustSomeGuy
Senior

The code I wrote for the B-L072Z-LRWAN1 is supposed to count pulses on a falling-edge external interrupt pin. The pulse source is an Arduino which toggles its GPIO on and off every 5ms, for a 10ms pulse period. after 6 seconds, the B-L072Z-LRWAN1 transmits the accumulated pulses to a receiving B-L072Z-LRWAN1 device (using proprietary peer-to-peer communication [confirmed to work properly]), which after 6 seconds should receive a count of 600 pulses. The 6 second timer has been confirmed to trigger a callback every 6 seconds.

My issue is that only 300 pulses are being send, which implies that the interrupt is only being called or incrementing every other time. There is an IoT bridge which takes the pulse input from both the arduino and the receiving STM32 device, and displays the pulse count on an IoT platform. the graph shows that the pulse rate for the STM32 is exactly half of what is coming from the Arduino. After debugging, I confirmed that the sending device is sending the wrong number of 300 pulses. Can anyone tell me the reason for this? 

Here is a screenshot of the gpio configuration:

Screenshot 2024-10-30 113050.png

here is the cubeMX configuration function:

 

 

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, PA15_RESERVED_Pin|PA12_RESERVED_Pin|PA1_RESERVED_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GREEN_LED_Pin|BLUE_LED_Pin|RED_LED_Pin|TCXO_PWR_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, PC1_RESERVED_Pin|PC0_RESERVED_Pin|PC2_RESERVED_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PA15_RESERVED_Pin PA12_RESERVED_Pin PA1_RESERVED_Pin */
  GPIO_InitStruct.Pin = PA15_RESERVED_Pin|PA12_RESERVED_Pin|PA1_RESERVED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : GREEN_LED_Pin BLUE_LED_Pin RED_LED_Pin TCXO_PWR_Pin */
  GPIO_InitStruct.Pin = GREEN_LED_Pin|BLUE_LED_Pin|RED_LED_Pin|TCXO_PWR_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PB9 PB8 PB15 PB11
                           PB12 PB10 */
  GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_8|GPIO_PIN_15|GPIO_PIN_11
                          |GPIO_PIN_12|GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PA14 PA10 PA13 PA8
                           PA11 PA9 PA0 PA5 */
  GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_10|GPIO_PIN_13|GPIO_PIN_8
                          |GPIO_PIN_11|GPIO_PIN_9|GPIO_PIN_0|GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB4_RESERVED_Pin PB1_RESERVED_Pin TEST_RANGE_Pin PB0_RESERVED_Pin */
  GPIO_InitStruct.Pin = PB4_RESERVED_Pin|PB1_RESERVED_Pin|TEST_RANGE_Pin|PB0_RESERVED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PC13 PC14 PC15 */
  GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : PC1_RESERVED_Pin PC0_RESERVED_Pin PC2_RESERVED_Pin */
  GPIO_InitStruct.Pin = PC1_RESERVED_Pin|PC0_RESERVED_Pin|PC2_RESERVED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : PH0 PH1 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

  /*Configure GPIO pin : PULSE_INPUT_Pin */
  GPIO_InitStruct.Pin = PULSE_INPUT_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(PULSE_INPUT_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : ENTER_SETUP_Pin */
  GPIO_InitStruct.Pin = ENTER_SETUP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(ENTER_SETUP_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);

  HAL_NVIC_SetPriority(EXTI2_3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);

  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

 

 

here is the interrupt callback function:

 

 

//this function is called when an external interrupt is triggered
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	/*if the interrupt is triggered from the pulse input pin, increment the pulse 
        counter*/
  if(GPIO_Pin == PULSE_INPUT_Pin)
	pulseCount++;
}

 

 

for good measure, here is the code for the arduino which sends the pulses to the STM32 (sorry in advance; arduino sucks, I know):

 

 

#define ledPin 13
#define pulse_1 4
#define pulse_2 5
#define PULSE_WIDTH 5000 //5 mS

uint32_t timestamp = 0;
bool state = LOW;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin, OUTPUT);
  pinMode(pulse_1, OUTPUT);
  pinMode(pulse_2, OUTPUT);
  digitalWrite(pulse_1, LOW);
  digitalWrite(pulse_2, LOW);
  digitalWrite(ledPin, LOW);
  timestamp = micros();
}

void loop() {
  if((micros() - timestamp) >= PULSE_WIDTH)
  {
    digitalWrite(pulse_1, state);
    digitalWrite(pulse_2, state);
    timestamp = micros();
    digitalWrite(ledPin, state);
    (state == HIGH) ? state = LOW : state = HIGH;
  }
}

 

 

And here is what is showing up on the IoT platform. Sometimes the controller sends the correct 600 pulses, and sometimes it sends 300. The units are not important, as this is only to show if the number of pulses sent match what is sent from the arduino or not. the dip at 11:00 is just from me disconnecting the receiving device for a few minutes.

Screenshot 2024-10-30 142615.png

23 REPLIES 23
PGump.1
Senior III

Hi,

Have you got an independent pulse count?

Kind regards
Pedro

AI = Artificial Intelligence, NI = No Intelligence, RI = Real Intelligence.
Sarra.S
ST Employee

Hello @JustSomeGuy,

This is looking like debounce effect, based on the provided GPIO initialization code, the pin is set to trigger on the falling edge, but if the external interrupt pin is not properly debounced, it might be missing some of the pulses! 

Make sure that the signal from the Arduino is clean and stable, if not, consider adding a debounce mechanism either in hardware (using capacitors) or in software (by adding a small delay in the ISR)

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Yes, the independent pulse count is the blue line in the last screenshot

So I measured the pulse input on an oscilloscope, and it's about as clean of a square wave as one can get. It's coming from a digital output device, anyway. I thought switch bouncing was the result of physical switches.

> on and off every 5ms, for a 10ms pulse period

What happens if you change these times to e.g. 2x or 4x longer?

JW

I get the exact same behaviour.

At 2x, the period is 20 ms, and should send 300 pulses after 6 seconds. It only sends 150. 

At 4x, the period is 40 ms, and should send 150 pulses after 6 seconds. It only sends 75. 

Is there any other interrupt enabled? If yes, what are their priorities? Try to set the EXTI to be the highest priority interrupt of them all.

Also, do you zero pulseCount? If yes, don't do that, send its current value and perform subtraction from previous value in the target.

JW

"Is there any other interrupt enabled? If yes, what are their priorities? Try to set the EXTI to be the highest priority interrupt of them all."

I have a couple, but none of them should be firing anywhere near as frequently as the pulse input. here are my callback functions:

 

 

//this function is called when the LP timer's CNT register = ARR
//in human terms, when the timer has counted up to its set point.
void HAL_LPTIM_AutoReloadMatchCallback(LPTIM_HandleTypeDef *hlptim)
{
	LPTimRollOver++;
	if(LPTimRollOver >= sTxDelay)
		{
			TxTimerDone = true;
			LPTimRollOver = 0;
		}
}

//this function is called when an external interrupt is triggered
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	///if the interrupt is triggered from the pulse input pin, increment the pulse counter
  if(GPIO_Pin == PULSE_INPUT_Pin)
	pulseCount++;

  //if the interrupt is triggered by the user push button, toggle a flag to indicate a range test must be done
  if(GPIO_Pin == TEST_RANGE_Pin)
	  testRange = 1;

  //the DIO0 pin is an internal connection between the STM32L0 controller, and the SX1276 LoRa controller.
  //when DIO0 is high, that means that a transmission has been received.
  if(GPIO_Pin == DIO0_Pin)
	  RxReady = 1;
}

//callback for timer which controls the Rx indicator LED
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM6)
	{
		HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, RESET);
		__HAL_TIM_DISABLE(&htim6);
	}
	if(htim->Instance == TIM7)
	{
		HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, RESET);
		__HAL_TIM_DISABLE(&htim7);
	}
	if(htim->Instance == TIM21)
	{
		HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, RESET);
		__HAL_TIM_DISABLE(&htim21);
	}
}

 

 

and the interrupt handlers:

 

 

void EXTI0_1_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(PB0_RESERVED_Pin);
  HAL_GPIO_EXTI_IRQHandler(PB1_RESERVED_Pin);
}
void EXTI2_3_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(TEST_RANGE_Pin);
}
void EXTI4_15_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(PB4_RESERVED_Pin);
  HAL_GPIO_EXTI_IRQHandler(PULSE_INPUT_Pin);
}
void LPTIM1_IRQHandler(void)
{
  HAL_LPTIM_IRQHandler(&hlptim1);
}
void TIM6_DAC_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim6);
}

void TIM7_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim7);
}
void TIM21_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim21);
}
void USART2_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart2);
}

 

 

lptim has a 1 second period where sTxDelay = 6, the TEST_RANGE_Pin [PB2] is never triggered in this case, and DIO0 [PB4] only goes high if a transmission has been received, which is every 6 seconds, which is a response from the receiving device to verify the sending device's transmission. But to test your hypothesis, I set the interrupt lines to the following:

Screenshot 2024-10-31 145637.png

Unfortunately, I am still only getting half the amount of pulses.

"Also, do you zero pulseCount? If yes, don't do that, send its current value and perform subtraction from previous value in the target."

I have accounted for that exact issue when I first wrote the code. The sending device is operating at 131.072 kHz, so processes like snprintf() and lora transmission takes a long time, and many pulse inputs may be received in that time.

The code is strange.

Whenever EXTI 0 or 1 is detected, you call the handlers for both EXTI 0 and EXTI 1. Same for 4..15.

No idea why you use HAL as intermediate layer - the processor invokes the handler, the handler invokes HAL (incorrectly - see above), HAL invokes your callback mostly to slow down the response.

Anyway, I believe that all this EXTI stuff is not needed at all.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice