cancel
Showing results for 
Search instead for 
Did you mean: 

Spurious EXTIO ISR (I cant Understand Why)

bluewaters213
Associate III
Posted on February 10, 2013 at 14:07

Hello,

I am relatively New to ARM Cortex Microcontroller but with experience in Microchip 8Bit to 32bits Microcontrollers. I am having a serous issue with configuring the external Interrupt of STM32F103RB. I want to Toggle two LED connected to PB8-PB9  in EXTI0 ISR [PA0]. I connect PA0 to TIM1 PWM CH4 PA11 (20KHz) Pin to trigger the External Interrupt on the Falling Edge of PA0.

After going through RM0008 Reference manual (Page 200 hardware interrupt selection), it has just 3 lines  explaining how to configure EXT Interrupt.I thought It will be easy but how wrong I was.Probing with Logic Analyzer, I noticed that the LED Toggle 435KHz in the EXTI0 ISR instead of the 20KHz that is triggering it.

I disconnect the wire that is connecting PA0 and PWM CH4 PA11 and view  the PWM and 2 LED in Logic Analyzer. The LED are in active High, the default state I set in my code (Please see attached Logic Analyzer Screen shot '' No_Interrupt.jpg'').I connect it back and the LED are toggling at 435KHz instead of the 20KHz (please see attached Logic Analyzer Screen shot '' Interrupt_Connected.jpg).I do not want to use ST Peripherals library.

I have read the Datasheet so many times, perform all voodoo dance known to me but I still can't get it to work. I have the feeling I did not configure the Interrupt properly. I just can't figure out the missing piece.

Please I need help.

----Code---

#include ''stm32f10x.h''

void PWM_init(void);

void Init_System(void);

void Init_ExtIO(void);

int main(void)

{

  Init_System();              //Initialize GPIOA-D & Peripherals

  PWM_init();                 //Initialize 20KHz PWM on CH 1-3

  Init_ExtIO();

    while(1)

      {

       }

}

void Init_System()

{

  RCC-> CFGR |= RCC_CFGR_ADCPRE_DIV4;     //ADC divided by 4

  /*Enable Alternate Function,GPIOA,GPIOB,GPIOC, GPIOD & TIMER1, ADC1 & ADC2 Clock Clock*/

  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |

                  RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_TIM1EN |

                  RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN;

  /*Enable TIMER2, TIMER3,I2C1  & USART2 Clock*/

  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN |RCC_APB1ENR_I2C1EN |

                  RCC_APB1ENR_TIM4EN | RCC_APB1ENR_USART2EN;

  RCC->AHBENR = RCC_AHBENR_DMA1EN ;   //DMA1 clock enable

/*Partial Remap TIM1: CH1/PA8,CH2/PA9,CH3/PA10,CH4/PA11,CH1N/PA7,CH2N/PB0,CH3N/PB1

Remap USART1: TX/PB6, RXPB7*/

  AFIO->MAPR |= AFIO_MAPR_TIM1_REMAP_PARTIALREMAP | AFIO_MAPR_USART1_REMAP;

 //PA2, PA7 Alternate Function Push Pull Output | PA0 Input Floating

  GPIOA->CRL = 0xA4444B44;

  GPIOA->CRH = 0x4444AAAA;  //PA8 - PA11 Alternate Function Push Pull Output

  //PB0 & PB1 =>Alt Function Push Pull O/P | PB6-PB7 Alt Function Open Drain

  GPIOB->CRL = 0xFF4444AA;

  GPIOB->CRH = 0x44444422;    //Configure PB8 & PB9 as Output (Push Pull)

  GPIOB->ODR |= 0x00000300;        //Set GPIOB8 & GPIOB9

}

void PWM_init(void)

{

  TIM1->PSC = 0;            //Pre-Scale Value=>0

  TIM1->ARR = (2800 - 1);  // Auto reload value 2800 (Period = 50us)

  TIM1->CCR1 = 1000;       // Start PWM duty for channel 1

  TIM1->CCR2 = 800;        // Start PWM duty for channel 2

  TIM1->CCR3 = 600;       // Start PWM duty for channel 3

  TIM1->CCR4 = 1400;      //CH4 Compare Value

  TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 |  //CH1-3 PWM mode 1

                 TIM_CCMR1_OC2M_2 |TIM_CCMR1_OC2M_1;   //CH3-4 PWM mode 1

  TIM1->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | //CH3 & CH4 is configured as output

                 TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1;

  /* Channel 1-4 active high | CH1-4 O/P Compare Enable | CH1-3N O/P Enable*/

  TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E  | TIM_CCER_CC4E|

                TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE;

  TIM1->BDTR |= TIM_BDTR_MOE | TIM_BDTR_OSSR | TIM_BDTR_DTG_7; //Enable Main O/P & Enable OSSR

  TIM1->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE; //Enable Counter,Center Align Mode 1,Automatic Reload Enable

}

void Init_ExtIO(void)

{

  AFIO->EXTICR[0]|= AFIO_EXTICR1_EXTI0_PA; //Enable Ext Interrupt on PA0 pin

  EXTI->IMR |= EXTI_IMR_MR0;    //Interrupt Mask on line 0

  EXTI->FTSR |=EXTI_FTSR_TR0;   //Falling trigger Interrupt enabled

  NVIC_EnableIRQ(EXTI0_IRQn);     //Enable EXTIO Interrupt in NVIC

}

void EXTI0_IRQHandler(void)

{

  if(EXTI->PR & EXTI_PR_PR0)          // if CC4IF flag is set

    {

      EXTI->PR &= ~(EXTI_PR_PR0);     //Clear CC4IF Flag

      GPIOB->ODR ^= 0x00000300;        //Toggle GPIOB8 & GPIOB9

     }

}

-------Code---------------

Note: I use CooCox IDE and GNU 4.7 C Compiler

Regards

Slim
5 REPLIES 5
Posted on February 10, 2013 at 14:24

EXTI->PR = EXTI_PR_PR0;     //Clear CC4IF Flag

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
bluewaters213
Associate III
Posted on February 10, 2013 at 15:13

Hello,

Thanks a million times..it working perfectly. I will like to get something clear from you. The EXTI_PR_PR0 is set (1) when interrupt occur else it remain reset (0). I thought you have to clear EXTI_PR_PR0 by using the procedure below.

Clearing bits

The code to clear bit 2 uses the & and ~ operators as follows:

*pTimerStatus &= ~(0x04);

For this operation, the inverse of 0x04 equals 0xFB. The &= operator sets bit 2 of the timer status register to 0, while leaving all other bits unchanged. The operation looks like this:

0 1 0 1 1 1 0 0 (0x5C) AND (&) NOT (~) 1 1 1 1 1 0 1 1 (0xFB) ================= 0 1 0 1 1 0 0 0 (0x58)

Note that all bits in the register remain the same except for the bit we want to clear.

why EXTI->PR = EXTI_PR_PR0;  instead of EXTI->PR &= ~EXTI_PR_PR0;?

Thanks

slim

gbulmer
Associate II
Posted on February 10, 2013 at 15:56

The manual (10.3.6 Pending register (EXTI_PR)) says:

''...  This bit is cleared by writing a

1

into the bit ...'' so it is not cleared by writing 0, which is what

EXTI->PR &= ~EXTI_PR_PR0;

was trying to do. This is a bit confusing (Edit: ooops - sorry about awful pun:-).

Writing 0 doesn't effect EXTI->PR bits, so there is no need to read the EXTI->PR register and create a pattern which preserves other EXTI->PR bits. Instead write a 1 into the bit that needs to be cleared.

Posted on February 10, 2013 at 16:16

The critical problem here is in assuming the peripheral register are implemented like memory. They often are not, and reads and writes can go to different logic. USART->DR being a profound example.

Your RMW of the register does totally the wrong thing when contrasted to the documented behaviour of the register. This is a problem when programming at the register level, where the side-effects are nuanced.

Why is RMW a bad plan on ARM? It's hugely inefficient, taking at least 8 cycles in many cases, which is particularly dangerous where the content is volatile. This creates a hazard, a race condition if you will. In a synchronous design this is addressed by reducing the hazard to a clock edge, and inputs meeting setup/hold requirements.

The write to the PR is not writing to the register you read, but rather doing a masked AND with the inverted content of the write. This has the effect to leaving other bits alone, whilst clearing the ones you want gone.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
bluewaters213
Associate III
Posted on February 10, 2013 at 16:39

Hello,

Thanks for your explanation.

Regards