2021-02-02 09:00 PM
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.
}
2021-02-03 12:23 PM
> 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
2021-02-03 03:01 PM
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.
2021-02-03 04:52 PM
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();
}
2021-02-03 04:58 PM
2021-02-04 08:34 AM
> Still no reaction
You mean, there's no interrupt?
Try to
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?
2021-02-18 04:33 PM
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!