How I could have 2 interrupts with one handler by this code below:
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PB | SYSCFG_EXTICR1_EXTI1_PC;
EXTI->IMR = EXTI_IMR_MR0 | EXTI_IMR_MR1;
EXTI->RTSR = EXTI_RTSR_TR0| EXTI_RTSR_TR1;
/* Configure NVIC for External Interrupt */
/* (6) Enable Interrupt on EXTI0_1 */
/* (7) Set priority for EXTI0_1 */
NVIC_EnableIRQ(EXTI0_1_IRQn); /* (6) */
NVIC_SetPriority(EXTI0_1_IRQn,0); /* (7) */
This is the code that the handler excecute:
void EXTI0_1_IRQHandler(void)
{
if ((EXTI->PR & EXTI_PR_PR1) == EXTI_PR_PR1) /* Check line 1 has triggered the IT */
{
EXTI->PR = EXTI_PR_PR1; /* Clear the pending bit */
GPIOC->ODR |= 1<<0;
}
if ((EXTI->PR & EXTI_PR_PR0) == EXTI_PR_PR0) /* Check line 0 has triggered the IT */
{
EXTI->PR = EXTI_PR_PR0; /* Clear the pending bit */
GPIOC->ODR &= ~(1<<0);
}
}
The code works fine when I click on the button that is connected to PC1, the LED turns on and when I click on the button that is connected to PB0 the LED turns off.
In my if structures I check which line is active but I also want the LED only turns on by clicking on PC1 and not with a click on another pin on line 1, the same for line 0 but I don't know how I can change the conditions for the if structures.
The micro-controller is a STM32F091.
First: you can't connect more than one pin (A..Fx) per EXTIx line (see RM0091 page 177). So EXTI line 0 IRQ is strictly correspond to one pin: C0 in your code.
Second: don't use IRQs for serve buttons. You must implement bounce filter and best idea for this is check button's pin state by timer. Human reaction is about 200ms, really pushed button will produce pulse with duration 100-200ms. So you need timer with 12-24ms and two bytes in RAM for each button... See code example bellow:
uint8_t btn_state = (uint8_t)0x0, btn_mask = (uint8_t)0x1;
void some_tim_irq_handler(void)
{
if (GPIOC->IDR & (uint16_t)0x1) { // PC0 up
btn_state |= btn_mask;
} else { // PC0 down
btn_state &= (uint8_t)~btn_mask;
}
btn_mask <<= (uint8_t)0x1; // mask cycle
if (btn_state == (uint8_t)0x0) {
// One state
return;
}
if (btn_state == (uint8_t)0xFF) {
// Second state
}
}
Related
I'm currently working on a SAME70 board made by Atmel. I plugged on it an extention OLED1 board (Atmel too) on EXT1 port. My goal is to recover the information about interrupt type (falling or raising) when I'm pressing the button 3 on OLED1 board. I have a function which allows me to set the different registers related to an interrupts. But unfortunately the register which indicates the polarity (FLHSR) stay always at 0 whatever the state of the button.
Below my code.
#include <asf.h>
void set_Interupt_Pin()
{
uint32_t mask = 1 << 19; /*bitmasking*/
PIOA->PIO_IER = mask; /*enable interruptions*/
PIOA->PIO_AIMER = mask; /*add new interruption*/
PIOA->PIO_ESR = mask; /*set interrupt source on edge*/
PIOA->PIO_REHLSR = mask; /* set interrupt to rising edge*/
PIOA->PIO_FELLSR= mask; /* set interrupt to falling edge*/
NVIC_DisableIRQ(ID_PIOA);
NVIC_ClearPendingIRQ(ID_PIOA);
NVIC_EnableIRQ(ID_PIOA); /*set NVIC to get interruptions from PIOA*/
NVIC_SetPriority(ID_PIOA, 4); /*priority 4*/
}
void PIOA_Handler(void)
{
printf("FRLHSR: %x\n",PIOA->PIO_FRLHSR); /*state of polarity event */
printf("ISR: %x\n",PIOA->PIO_ISR);
printf("ELSR: %x\n",PIOA->PIO_ELSR);
printf("AIMMR: %x\n",PIOA->PIO_AIMMR);
}
int main(void)
{
const usart_serial_options_t usart_serial_options = {
.baudrate = CONF_UART_BAUDRATE,
.charlength = CONF_UART_CHAR_LENGTH,
.paritytype = CONF_UART_PARITY,
.stopbits = CONF_UART_STOP_BITS
};
sysclk_init();
board_init();
stdio_serial_init(CONF_UART, &usart_serial_options);
set_Interupt_Pin();
while(1){}
}
You can see here the result of print when I press button (first part) and I release button (second part).
Best regards
Every time I enable the timer it instantly activates the Interrupt. No matter how I try to prescale it. only ARR seems to work but 16 bit with 0,5MHz clock gives me ~160ms maneuver.
#define SYSCLK_FREQ 524288
void timer_init(uint16_t detonation_delay_ms);
int main(void){
RCC->APB2ENR = RCC_APB2ENR_TIM22EN;
TIM22->PSC = (SYSCLK_FREQ/1000)-1;
NVIC_EnableIRQ(TIM22_IRQn);
NVIC_SetPriority(TIM22_IRQn,4);
}
/* calling function */
timer_init(65535);
/* calling function */
void timer_init(uint16_t detonation_delay_ms){
TIM22->CR1 &= ~TIM_CR1_CEN;
TIM22->SR=0;
TIM22->ARR = detonation_delay_ms;
TIM22->CR1 |= TIM_CR1_CEN;
TIM22->SR = 0;
}
void TIM22_IRQHandler(void){
TIM22->CR1 &= ~TIM_CR1_CEN;
TIM22->SR=0;
GPIOB->BSRR = GPIO_BSRR_BS_7;
}
I wish that calling function makes the timer tick till the called value in milisec. But no matter how I set it up it ends up with no scaled timer and instant interrupt after calling it.
Correct way to do it?
TIM22->DIER = TIM_DIER_UIE;
TIM22->ARR = 65535-detonation_delay_ms;
TIM22->EGR = TIM_EGR_UG;
TIM22->CR1 |= TIM_CR1_OPM | TIM_CR1_CEN;
TIM22->SR=0;
Do not delay in interrupts
you enable the timer then set the ARR which is wrong - first set ARR and prescaller, then generate the UG event using the EGR register, then enable the timer.
Works like a charm. Just because I got help here will describe for future interested people.
The way to get the interrupt working for the timers is to generate interrupt 'by hand' once. It's okay to do it because you can control what's happening during the interrupt by a single 'if'.
/* TIMER Enable */
RCC->APB2ENR = RCC_APB2ENR_TIM22EN;
I had a problem with the above declaration, dunno why but it wasn't working after declaring some more modules before it. Had to put it on higher on the list. Manual does not say why it happened.
/* Configure TIM22 interrupt */
TIM22->PSC = (SYSCLK_FREQ/1000)-1; /* Prescaler TIMERA 22 */
TIM22->DIER = TIM_DIER_UIE;
TIM22->CNT = 0;
TIM22->EGR = TIM_EGR_UG;
NVIC_EnableIRQ(TIM22_IRQn); /* Zalaczenie przerwania od TIMER'a */
NVIC_SetPriority(TIM22_IRQn,4); /* Ustawienie priorytetu przerwania od TIMER'a */
The prescaler meant to be 1ms so I divided 524288 of my core speed. Then enabling the interrupt, resetting the counter to make sure it starts from 0 and then manually generating the interrupt. And it does the interrupt 'loop' once but with a single 'if' and variable, I can control what it does.
So what I do, I am calling a function that set the clock and enables the count inside another function enable = 1; timer_init(ms);
Then comes the function call
void timer_init(uint16_t ms)
{
TIM22->CNT = 65535-ms;
TIM22->CR1 |= TIM_CR1_CEN;
}
void TIM22_IRQHandler(void)
/* Up-Counter milisec */
{
if(TIM22->CR1 & TIM_CR1_CEN) {TIM22->CR1 &= ~TIM_CR1_CEN;}
if(TIM22->SR & TIM_SR_UIF) {
if(enable == 1){
GPIOB->BSRR = GPIO_BSRR_BS_7;
}
}
TIM22->SR=0;
}
And the interrupt.
Thanks a lot!Have fun with registers!
I am using a Kinetis KEA64 microcontroller from NXP.
The actual frequency of clock is 20 MHz, and a timer interrupt is generated at every 2.5ms. I have an interrupt handler that toggles an LED when this timer interrupt is generated. LED is toggling on this timer interrupt but i dont know exactly the frequency of LED. Does my LED toggles at 5kHz? Is it correct?
void interrupt_application_timer_FTM0()
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK; /* Enable Clock for FTM0 */
FTM0_SC |= FTM_SC_PS(7); /* Select Preescaler in this case 128. 20 Mhz /128 =156.25 Khz. */
/* Counter increase by one every 6.4 us */
/* Enable Channle 0*/
FTM0_C0SC |= FTM_CnSC_CHIE_MASK; /* Enable channel 0 interrupt */
FTM0_C0SC |= FTM_CnSC_MSA_MASK; /* Channel as Output compare mode */
/*Select interrupt frequency*/
FTM0_C0V = FTM_CnV_VAL(391) ; /* Interrupt every 2.5ms */
FTM0_SC |= FTM_SC_CLKS(1); /*FTM0 use system clock*/
/* Set the ICPR and ISER registers accordingly */
NVIC_ICPR |= 1 << ((INT_FTM0-16)%32);
NVIC_ISER |= 1 << ((INT_FTM0-16)%32);
}
Here is my interrupt handler
void FTM0_IRQHandler()
{
if (1==((FTM0_C0SC & FTM_CnSC_CHF_MASK)>>FTM_CnSC_CHF_SHIFT) ) /* If the CHF of the channel is equal to 0 */
{
(void)FTM0_C0SC; /* Read to clear flag */
FTM0_C0SC ^= FTM_CnSC_CHF_MASK; /* Clear flag */
FTM0_C0V = FTM0_C0V + 391 ; /* Refresh interrupt period */
if (LED_counter>=50){
/* Toggle LED */
/* Reset counter */
LED0_TOGGLE;
LED_counter = 0;
}
LED_counter++;
}
}
Use scope for frequency measuring
In the MCU, you can enable bus clock to one pin (Busclockout), enable that and check the bus clock so that you can confirm your calculations
Then use scope at LED to confirm your frequency
Is it possible to get multiple interrupts from te same EXTI line for par example for PA1 and PC1 they are both on EXTI1.
So that by clicking on a button on PA1 a LED go on at PB6, And by clicking on PC1 that a LED toggle on PC0.
Microcontroller ==> STM32F091
That is the code that i use for interrupts from 2 different lines:
//PC1
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PA;
EXTI->IMR = EXTI_IMR_MR1;
EXTI->RTSR = EXTI_RTSR_TR1;
EXTI->FTSR = EXTI_FTSR_TR1;
//PB0
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC;
EXTI->IMR |= EXTI_IMR_MR1;
EXTI->RTSR |= EXTI_RTSR_TR1;
EXTI->FTSR |= EXTI_FTSR_TR1;
NVIC_EnableIRQ(EXTI0_1_IRQn);
NVIC_SetPriority(EXTI0_1_IRQn,0);
The interrupt Handler:
void EXTI0_1_IRQHandler(void)
{
// Check line 1 has triggered the IT.
if ((EXTI->PR & EXTI_PR_PR1) == EXTI_PR_PR1)
{
EXTI->PR = EXTI_PR_PR1; // Clear the pending bit.
GPIOC->ODR ^= 1 << 0;
}
// Check line 0 has triggered the IT.
if ((EXTI->PR & EXTI_PR_PR0) == EXTI_PR_PR0)
{
EXTI->PR = EXTI_PR_PR0; // Clear the pending bit.
GPIOB->ODR ^= 1 << 6;
}
}
No, you can't. Sorry.
Each of the 16 GPIO-driven EXTIs can only be connected to one of the six corresponding pins. For instance, EXTI0 can be connected to PA0, PB0, PC0, PD0, PE0, or PF0, etc. Values like SYSCFG_EXTICR1_EXTI1_PA are not pure bitmasks, and cannot be combined.
For additional details, see section 12.2.5 of the STM32F0 reference manual.
Friends , I have to sample the input , every 14 microsecond in a 61 micro second slot with the timer input (Project Requirement).
I have to do it 8 times to make a byte. More like UART , but I am using it for One Wire Bus communication for my Masters Project.
I have written the code as below , which gives expected result, tested it executing one instruction at a time in debugger.
Below is the Code.
/*****************************************************************************
COMPARE MODE SAMPLING:
MCLK and SCLK #8MZ
The code configures P2.1 as TA1.CCI1A input.
It samples the input at P2.1 Whenever the TA1R reaches the TA1CCR1 value.
It samples input on P2.1 every 14us once in a duration of 61 us.
It then reads 8 bits one by one to read a byte.
******************************************************************************/
#include "io430g2553.h"
#define MSP_DQ BIT5
unsigned char word=0x00;
unsigned char i=0;
unsigned char temp;
void Read(void)
{
TA1CCR0 = 0x1E8; // 61 micro secs
TA1CCR1 = 0x70; // 14 micro secs
//TA0CCTL1 = CM_2 | CCIS_0 | SCS | CAP | OUTMOD_0 | CCIE;
//Keep in mind that It should not be configured as campture mode
TA1CCTL1 |= CM_2 | CCIS_0 | SCS | OUTMOD_0 | CCIE;
TA1CTL = TASSEL_2 + MC_1 + ID_0; // Register TA0CTL -> SMCLK/1, Up mode
do{
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
**TA1CCTL0 &= ~CCIFG; // Clear the flag** (%1%)
//TA1CTL &= ~TAIFG; // Clear the flag
i++;
} while( i<8) ;
TA1CTL = TACLR; // Stop the Timer
TA1CCTL1 = 0x00;
}
void Configure_CCI1A(void)
{
// Configuring P2.1 as TA1.CCI1A
P2OUT &= 0x00; // Clearing P1OUT
P2DIR &= ~BIT1 ; // Configuring P1.2 as input
P2SEL |= BIT1 ; // P2.1 Timer1_A, capture: CCI1A input, compare: Out1 output
P2SEL2 &= ~BIT1 ;
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
P1OUT &= 0x00; // Clearing P1OUT
P1DIR |= BIT0 ; // Configuring P1.0 as Output
__enable_interrupt();
Configure_CCI1A();
Read();
**P1OUT ^= BIT0;** //(%2%)
while(1) {
}
}
// Timer A1 interrupt service routine
#pragma vector=TIMER1_A1_VECTOR
__interrupt void Timer1_A1 (void)
{
P1OUT ^= BIT0; // To show Read occured
word <<=1; // If x = 00000010 (binary) => x <<= 2; => x=00001000
temp=0x00;
temp=((TA1CCTL1 & SCCI)>>10);
**word = word + temp ;** //(%3%)
}
But the issue is that when I call the function it some how appears stuck . I guess it is not coming out of the ISR cleanly though it completed all its execution when I run in debugger one instruction at a time. To make my question clear , This is how I tested :
Suppose if I put toggle break point at highlighted expression (%3%) in ISR , then it enters the ISR hits the togglebreak 8 times capturing correct values and comes out of Read Function neatly ( and also
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
and
{
....
....
i++;
} while( i<8) ;
out of above loop)
, to reach while(1) expression in the mains.
But instead if i put toggle point at the highlighted expression (%1%) it appears stuck.
OR if I put the toggle break point directly at (%2%) in main what i expect , it to complete the Read function , store the value in word variable to reach the toggle break point but the code appears stuck and does not hit the togglebreak.
I do not know what is wrong, can any one please help ?
The interrupt flag is automatically cleared when you read TAIV or if you manually clear it in TACCTL1. However, you do neither of these things in your ISR so the interrupt remains pending and your CPU continually executes your ISR and no other code so it never gets a chance to exit Read().
My guess is that by putting a breakpoint in the ISR your development environment causes a read from TAIV and clears the pending interrupt. I've experienced that before, but not sure how common that behaviour is as it is undesirable.