cancel
Showing results for 
Search instead for 
Did you mean: 

Use of interrupt, STM32F407, with PE4, falling edge trigger

JCorl.1
Senior

I would like to respond to the falling edge of a square wave that is of 50% duty cycle, running at 1us pulses (500kHz). This signal comes from my LA2016 logic analyzer (has PWM outputs) and is fed into PE4 of my STM32 discovery board (STM32F407VG MCU). This signal is also connected to my logic analyzer so that I am able to inspect it. PE4 is configured as an input with no internal pullup set.

I have also placed PE2 under inspection in my logic analyzer. Idea is that every time I sense a falling edge of the 1us pulse, I will quickly toggle PE2 on and off. PE2 is configured as an output with open drain drive, pulled up externally with a 1k resistor.

I did manage to sense a falling edge with polling. Though I have other code I would like to run and decided an interrupt would be the best way!

Spent hours understanding how to do this and i think I got it down (shows in my comments). Though my implementation does not seem to be working properly. I know that my input and output pins work correctly since I was able to use polling so it has to be in my understanding of the interrupt mechanism.

Any hints?

void ConfigureInterrupt()
 
{
 
   /* Configure dataline2 (PE4) as interrupt */
 
   // Disable the IRQ number, for EXTI4, this is 10 (from vector table)
 
   __disable_irq();
 
   // Note: I used this function here. I could have used the
 
   // NVIC_ICER0 register instead and write a 1 to bit10
 
 
   // Enable the clock for SYSCFG and EXTI engine
 
   RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
 
 
   // Select PE for EXTI engine
 
   SYSCFG->EXTICR[1] = SYSCFG_EXTICR2_EXTI4_PE;
 
 
   // Unmask EXTI4 to be used for interrupt mechanism
 
   EXTI->IMR |= EXTI_IMR_IM4;
 
 
   // Select the external trigger to be of falling edge
 
   EXTI->FTSR |= EXTI_FTSR_TR4;
 
 
   // Make sure that there is no rising edge trigger
 
   EXTI->RTSR &= ~EXTI_RTSR_TR4;
 
 
   // Enable the IRQ number, for EXTI4, this is 10 (from vector table)
 
   NVIC_EnableIRQ(EXTI4_IRQn);
 
}
 
 
void PulseSense()
 
{
 
   // Code before this line configures input and output pins,
 
   // omitted for forum question.
 
 
   // Setup the input pin interrupt
 
   ConfigureInterrupt();
 
 
   while(1)
 
   {
 
      // Polling code - disabled for now
 
      // DKO... are macro names for my code
 
      //while(DkoGetDataline2() == LOW);
 
      //while(DkoGetDataline2() == HIGH);
 
      //DKO_DATALINE1_GPIO->ODR |= (1 << DKO_DATALINE1_PIN);
 
     // DKO_DATALINE1_GPIO->ODR &= ~(1 << DKO_DATALINE1_PIN);
 
   }
 
}
 
 
void EXTI4_IRQHandler(void)
 
{
 
   // Pulse the output quickly
 
   DKO_DATALINE1_GPIO->ODR &= ~(1 << DKO_DATALINE1_PIN);
 
   DKO_DATALINE1_GPIO->ODR |= (1 << DKO_DATALINE1_PIN);
 
 
   // Clear the EXTI engine Pending Register
 
   EXTI->PR |= EXTI_PR_PR4;
 
   // Note: Even though we write a 1 to this register
 
   // the manual says this is how you go about
 
   // clearing it, which is kinda weird.
 
}

6 REPLIES 6

> Though my implementation does not seem to be working properly.

Be specific.

Also, what's the system clock?

Clear the interrupt source early, i.e. move the EXTI_PR bit clear to the very beginning of the ISR.

Following are probably not source of your specific problem at the moment:

> DKO_DATALINE1_GPIO->ODR &= ~(1 << DKO_DATALINE1_PIN);

No. Would there be interrupt of higher priority using the same ODR register, you'd run into atomicity problems.

Use GPIO->BSRR, it is perfectly atomic thus does the same thing better.

> EXTI->PR |= EXTI_PR_PR4;

No, don't RMW, just write:

EXTI->PR = EXTI_PR_PR4;

JW

JCorl.1
Senior

Thanks for pointing out the BSRR register. Neat trick and seems a lot safer. I was mentioned this before but I was didn't know what they were referring to and i understand better now. That means for the PR register, same idea, only write with a write only register don't RMW.

The system clock is 168MHz. I think to isolate the issues, I will create a separate project just for this interrupt learning. Will post try and post this code soon.

JCorl.1
Senior

Ok I have reacted the project into one file and separate from my main project. Still no reaction but trying to debug. Here is what I have now:

#include <stdint.h>
#include "system_stm32f4xx.h"
#include "stm32f4xx.h"
 
#define GPIO_OTYPER_OT2_0		(0x1UL << GPIO_OTYPER_OT2_Pos)
 
static void configure_clock()
{
	/* Clock Settings for 168MHz
	   HCLK = 168
	   PLL: M = 8, N = 336, P = 2, Q = 7
	   AHB prescaler = 1
	   APB prescaler1 = 4, APB prescaler2 = 2
	   MCO1 prescaler = 2 */
 
	// Configures flash latency.
	// LATENCY: bits 2-0
	MODIFY_REG(FLASH->ACR,
		FLASH_ACR_LATENCY,
		_VAL2FLD(FLASH_ACR_LATENCY,
		FLASH_ACR_LATENCY_5WS) //FLASH_ACR_LATENCY_5WS << FLASH_ACR_LATENCY_Pos
	);
 
	// Enables HSE.
	// HSE_ON: bit 16
	SET_BIT(RCC->CR, RCC_CR_HSEON);
 
	// Waits until HSE is stable.
	// HSERDY: bit 17
	while (!READ_BIT(RCC->CR, RCC_CR_HSERDY));
 
	// Configures PLL: source = HSE, PLLCLK = 168MHz.
	// M: bits 5-0, N: bits 14-6, P: bits 17-16, PLLSRC: bit 22, Q: bits 27-24
	MODIFY_REG(RCC->PLLCFGR,RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLQ | RCC_PLLCFGR_PLLSRC
			| RCC_PLLCFGR_PLLP,_VAL2FLD(RCC_PLLCFGR_PLLM, 8) | _VAL2FLD(RCC_PLLCFGR_PLLN, 336)
			| _VAL2FLD(RCC_PLLCFGR_PLLQ, 7) | RCC_PLLCFGR_PLLSRC_HSE);
 
	// Enables PLL module.
	// PLLON: bit 24
	SET_BIT(RCC->CR, RCC_CR_PLLON);
 
	// Waits until PLL is stable.
	// PLLRDY: bit 25
	while (!READ_BIT(RCC->CR, RCC_CR_PLLRDY));
 
	// Switches system clock to PLL.
	// SW: bits 1-0
	MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, _VAL2FLD(RCC_CFGR_SW, RCC_CFGR_SW_PLL));
 
	// Configures PPRE1 = 4
	MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, _VAL2FLD(RCC_CFGR_PPRE1, 5));
 
	// Configures PPRE2 = 2
	MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, _VAL2FLD(RCC_CFGR_PPRE1, 4));
 
	// Configures HPRE = 1
	MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, _VAL2FLD(RCC_CFGR_PPRE1, 1));
 
	// Waits until PLL is used.
	while(READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
 
	// Disables HSI.
	CLEAR_BIT(RCC->CR, RCC_CR_HSION);
}
 
void configure_mco1()
{
	// Configures MCO1: source = PLLCLK, MCO1PRE = 2.
	MODIFY_REG(RCC->CFGR,
		RCC_CFGR_MCO1 | RCC_CFGR_MCO1PRE,
		_VAL2FLD(RCC_CFGR_MCO1, 3) | _VAL2FLD(RCC_CFGR_MCO1PRE, 7)
	);
 
	// Enables GPIOA (MCO1 is connected to PA8).
	SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);
 
	// Configures PA8 as medium speed.
	MODIFY_REG(GPIOA->OSPEEDR,
		GPIO_OSPEEDR_OSPEED8,
		_VAL2FLD(GPIO_OSPEEDR_OSPEED8, 1)
	);
 
	// Configures PA8 to work in alternate function mode.
	MODIFY_REG(GPIOA->MODER,
		GPIO_MODER_MODER8,
		_VAL2FLD(GPIO_MODER_MODER8, 2)
	);
}
 
void ConfigureInterrupt()
{
	/* Configure dataline2 (PE4) as interrupt */
	// Disable the IRQ number, for EXTI4, this is 10 (from vector table)
	__disable_irq();
	// Note: I used this function here. I could have used the
	// NVIC_ICER0 register instead and write a 1 to bit10
 
	// Enable the clock for SYSCFG and EXTI engine
	RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
 
	// Select PE for EXTI engine
	SYSCFG->EXTICR[1] = SYSCFG_EXTICR2_EXTI4_PE;
 
	// Unmask EXTI4 to be used for interrupt mechanism
	EXTI->IMR |= EXTI_IMR_IM4;
 
	// Select the external trigger to be of falling edge
	EXTI->FTSR |= EXTI_FTSR_TR4;
 
	// Make sure that there is no rising edge trigger
	EXTI->RTSR &= ~EXTI_RTSR_TR4;
 
	// Enable the IRQ number, for EXTI4, this is 10 (from vector table)
	NVIC_EnableIRQ(EXTI4_IRQn);
}
 
void ConfigureTimer()
{
	/* Configure TIM6 (basic timer) */
	// Enable TIMER6 (on the APB1 bus)
	RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
 
	// One pulse mode (counter stops counting after event)
	TIM6->CR1 |= (TIM_CR1_OPM);
}
 
void Delay()
{
	// Clear update event flag
	TIM6->SR = 0;
 
	// Prescaler
	TIM6->PSC = 0;
 
	// Preload
	TIM6->ARR = 68;
 
	// Start timer
	TIM6->CR1 |= TIM_CR1_CEN; // CEN bit
 
	// Wait for delay to elapsed
	while(!(TIM6->SR & TIM_SR_UIF));
 
	// Clear timer flag
	TIM6->SR = 0;
}
 
int main(void)
{
	// Clock config to 168MHz
	configure_clock();
 
	// Clock check
	configure_mco1();
 
	// Enable clock for GPIOE
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
 
	// Set PE2 as output
	GPIOE->MODER &= ~(GPIO_MODER_MODE2);		// Clear pin mode field
	GPIOE->MODER |= GPIO_MODER_MODER2_0;		// Set to output
 
	GPIOE->OTYPER &= ~(GPIO_OTYPER_OT2);		// Clear output mode field
	GPIOE->OTYPER |= GPIO_OTYPER_OT2_0;			// Set to open drain
	// Note: pin externally pulled up with 1k resistor to 3V.
 
	GPIOE->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2);	// Clear speed mode field
	GPIOE->OSPEEDR |= GPIO_OSPEEDR_OSPEED2_0;	// Set to medium speed
 
	// Set PE4 as input
	GPIOE->MODER &= ~(GPIO_MODER_MODE4);	// Clear pin mode field, leave as input mode
	GPIOE->PUPDR &= ~(GPIO_PUPDR_PUPD4);	// Clear pullup/ pulldown mode field, leave as none
	// Note: pin is recieving a 500kHz square wave, 50% duty cycle
 
	// Configure the timer
	ConfigureTimer();
 
	// Configure the interrupt
	ConfigureInterrupt();
 
	while(1)
	{
//		GPIOE->BSRR = GPIO_BSRR_BS2;
//		Delay();
//		GPIOE->BSRR = GPIO_BSRR_BR2;
//		Delay();
	}
}
 
void EXTI4_IRQHandler(void)
{
	// Clear the EXTI engine Pending Register
	EXTI->PR = EXTI_PR_PR4;
 
	// Pulse the output quickly
	GPIOE->BSRR = GPIO_BSRR_BS2;
	Delay();
	GPIOE->BSRR = GPIO_BSRR_BR2;
	Delay();
}

JCorl.1
Senior

Here is the project attached as well for convenience.

> Still no reaction

You mean, there's no interrupt?

Try to

  • call EXTI4_IRQHandler, as a normal function, from the main loop
  • forcibly trigger EXTI4 through EXTI_SWIER

Or try to debug as interrupts usually.

JW

PS. Any specific reason why you refuse to change the AHB/APB prescalers before swtiching to PLL as system clock?

Totally forgot to answer this post. I never did get the interrupt working correctly. I wound up performing some polling, cherry picking bits to sample, which eventually solved my problem. I guess I will try to understand interrupt another time.

PS: Not specific reason I am refusing. I actually haven't done it because I forgot!