External Interrupt

Discussion around product based on ARM Cortex M4 core.

Moderators: nferre, ncollot

Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

External Interrupt

Tue Aug 26, 2014 4:46 pm

Hi!

I am new to the SAM4S and i'm trying to setup an external interrupt triggered by a button. The interrupt should be triggered when there is a rising edge at Pin PA6, so i connected the button to VDDIO and activated the internal pull-down resistor. If i push the button, it pulls the Pin PA6 to VDDIO, so there's a transition from LOW to HIGH.

Here's my code:

Code: Select all

uint8_t led_state;

void PIOA_Handler(void) {
	if (PIOA->PIO_ISR & (1<<6)) {
		led_state = 0;
	}
}

void io_init(void) {
     /* PA6 is a input by default, disable pull-up and enable pull-down for PA6*/
	PIOA->PIO_PUDR = PIO_PA6;
	PIOA->PIO_PPDER = PIO_PA6;

     /* Enable interrupt: rising edge on Pin PA6 */
     PIOA->PIO_IER = PIO_PA6;
     PIOA->PIO_AIMER = PIO_PA6;
     PIOA->PIO_ESR = PIO_PA6;
     PIOA->PIO_REHLSR = PIO_PA6;
}

int main(void) {
     /* Initialize the SAM system */
     SystemInit();
     io_init();
	
     led_state = 1;
	
     NVIC_EnableIRQ(PIOA_IRQn);

     while(1);
}
But if i push the button, led_state never changes to 0 (i'm using a SAM-ICE Debugger for checking the variable).

Is my approach okay or are there any mistakes?
thofis
Posts: 8
Joined: Thu Apr 10, 2014 4:20 pm

Re: External Interrupt

Tue Aug 26, 2014 11:30 pm

I would advice to look at the according ASF example:

http://asf.atmel.com/docs/latest/sam4s/ ... ase_2.html

I think you have to add enabling of the module clock. (at least)

additionally you variable has to be 'volatile'
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Wed Aug 27, 2014 10:20 am

thofis wrote: I think you have to add enabling of the module clock. (at least)

additionally you variable has to be 'volatile'
Thanks for your advices!

I already enalbed the peripheral clock for PIOA in the SystemInit()-function:

Code: Select all

PMC->PMC_PCER0 = PMC_PCER0_PID11;
Does the NVIC need a peripheral clock?

I changed the led_state variable to volatile. led_state == 0 means, that my leds on the breadboard turn off. They are enabled in the main-function by default.

If i now flash the SAM4S with the new program, the leds are always turned off, i.e. the interrupt-handler is always executed at the beginning.

Do you have any ideas? :?:

My intention was to avoid the ASF at first to learn more about the Cortex-M4 architecture.
thofis
Posts: 8
Joined: Thu Apr 10, 2014 4:20 pm

Re: External Interrupt

Wed Aug 27, 2014 12:31 pm

Fry12 wrote: If i now flash the SAM4S with the new program, the leds are always turned off, i.e. the interrupt-handler is always executed at the beginning.

Do you have any ideas? :?:

My intention was to avoid the ASF at first to learn more about the Cortex-M4 architecture.
You can do this by using the ASF, at least its examples.

try to insert

Code: Select all

p_pio->PIO_ISR;
before

Code: Select all

PIOA->PIO_IER = PIO_PA6;
this clears the status register.

I learned this from the ASF :)

Code: Select all

/**
 * \brief Enable the given interrupt source.
 * The PIO must be configured as an NVIC interrupt source as well.
 * The status register of the corresponding PIO controller is cleared
 * prior to enabling the interrupt.
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Interrupt sources bit map.
 */
void pio_enable_interrupt(Pio *p_pio, const uint32_t ul_mask)
{
	p_pio->PIO_ISR;
	p_pio->PIO_IER = ul_mask;
}

Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Wed Aug 27, 2014 1:28 pm

Thanks for your feedback! I think i'm gonna look after the ASF ;)

I now placed the

Code: Select all

PIOA->PIO_ISR;
statement right before

Code: Select all

PIOA->PIO_IER = PIO_PA6;
but i'm getting a strange behavior: If i debug through the program step by step everything works fine, but if i turn on the µC without debugger, it doesn't work as expected. :lol: What's the reason for this?
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Wed Aug 27, 2014 4:01 pm

Okay i tried it with the ASF PIO driver. My code now looks like this:

Code: Select all

volatile uint8_t led_state;

void pin_edge_handler(void) {
	led_state = (led_state == 0) ? 1 : 0;
}

void io_init(void) {
	pmc_enable_periph_clk(ID_PIOA);
	
	pio_set_output(PIOA, (RED_LEDS | GREEN_LEDS), LOW, DISABLE, DISABLE);
	pio_set_input(PIOA, (PIO_PA6), DISABLE);
	pio_pull_down(PIOA, (PIO_PA6), ENABLE);
	
	pio_handler_set(PIOA, PIO_PA6_IDX, PIO_PA6, PIO_IT_RISE_EDGE, pin_edge_handler);
	pio_enable_interrupt(PIOA, PIO_PA6);
	
	NVIC_EnableIRQ(PIOA_IRQn);
}
In the main-function i disable the watchdog and call the io_init()-function.

My Debugger says, if i press the button, the pin_edge_handler()-function is never executed. I also get some warnings:

Code: Select all

passing argument 5 of 'pio_handler_set' from incompatible pointer type [enabled by default]

Code: Select all

expected 'void (*)(uint32_t,  uint32_t)' but argument is of type 'void (*)(void)'
It seems like the handler function isn't accepted by the compiler, although i use almost the same style as in the example.
thofis
Posts: 8
Joined: Thu Apr 10, 2014 4:20 pm

Re: External Interrupt

Wed Aug 27, 2014 5:43 pm

Fry12 wrote:Okay i tried it with the ASF PIO driver. My code now looks like this:

Code: Select all

volatile uint8_t led_state;

void pin_edge_handler(void) {
	led_state = (led_state == 0) ? 1 : 0;
}

void io_init(void) {
	pmc_enable_periph_clk(ID_PIOA);
	
	pio_set_output(PIOA, (RED_LEDS | GREEN_LEDS), LOW, DISABLE, DISABLE);
	pio_set_input(PIOA, (PIO_PA6), DISABLE);
	pio_pull_down(PIOA, (PIO_PA6), ENABLE);
	
	pio_handler_set(PIOA, PIO_PA6_IDX, PIO_PA6, PIO_IT_RISE_EDGE, pin_edge_handler);
	pio_enable_interrupt(PIOA, PIO_PA6);
	
	NVIC_EnableIRQ(PIOA_IRQn);
}
In the main-function i disable the watchdog and call the io_init()-function.

My Debugger says, if i press the button, the pin_edge_handler()-function is never executed. I also get some warnings:

Code: Select all

passing argument 5 of 'pio_handler_set' from incompatible pointer type [enabled by default]

Code: Select all

expected 'void (*)(uint32_t,  uint32_t)' but argument is of type 'void (*)(void)'
It seems like the handler function isn't accepted by the compiler, although i use almost the same style as in the example.
Second parameter has to be PIO ID, not pin, like in the example:

Code: Select all

pio_handler_set(PIOA, ID_PIOA, PIO_PA16, PIO_IT_EDGE, pin_edge_handler);
the handler should call

Code: Select all

pio_get_interrupt_status(PIOA);
to clear the interupt status register (not 100% sure about his)
thofis
Posts: 8
Joined: Thu Apr 10, 2014 4:20 pm

Re: External Interrupt

Wed Aug 27, 2014 6:07 pm

Code: Select all

pio_handler_set(PIOA, ID_PIOA, PIO_PA16, PIO_IT_EDGE, pin_edge_handler);
Well, bug in ASF documentation, if you follow function to definition, the handler should be like:

Code: Select all

void pinEdgeHandler(uint32_t sourceid, uint32_t mask)
{
 ....
}
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Thu Aug 28, 2014 9:49 am

Everything works fine now, thank you! :)
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

External Interrupt delayed

Mon Oct 13, 2014 1:39 pm

Hi guys,

i have another problem related to the old one. I want to do some stuff in a function until there's a falling edge at a pin.

So here's my PIO configuration:

Code: Select all

pio_set_input(PIOA, PIO_PA10, DISABLE);
pio_handler_set(PIOA, ID_PIOA, PIO_PA10, PIO_IT_FALL_EDGE, falling_edge_handler);
pio_enable_interrupt(PIOA, PIO_PA10);
I set up my interrupt handler like this:

Code: Select all

volatile bool interrupt_flag;

void falling_edge_handler(const uint32_t sourceid, const uint32_t mask)
{
	if (sourceid == ID_PIOA && mask == PIOA_PA10) {
		interrupt_flag = true;
	}
}
Inside the function i check if interrupt_flag is true or not:

Code: Select all

while (!interrupt_flag) {
		...
}
Now, after a given time pin PA10 goes low (checked with oscilloscope), but interrupt_flag isn't set true instantly, so the program will take antoher loop pass. I want to avoid this behavior. The only way to ensure that the loop stops when the pin level goes low is to write the loop like this:

Code: Select all

while (1) {
      delay_ms(1);

      if (interrupt_flag) {
            break;
      }

      ...
}
I debugged this code (using SAM-ICE): After PA10 went low, interrupt_flag is still false before delay_ms(1); and changes to true after executing delay_ms(1);

Written like this, my code works as intended, but i have to use the delay-statement. I don't like these delays, so i want to get rid of them, but how can i do that?

I tried it with Memory Barrier Instructions, but it didn't have the desired effect:

Code: Select all

while (1) {
      __DSB();
      __ISB();

      if (interrupt_flag) {
            break;
      }

      ...
}
Any idea?

Many thanks in advance! :)
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Thu Oct 16, 2014 9:33 am

No ideas so far?
jharley
Posts: 238
Joined: Thu Dec 06, 2012 6:40 am

Re: External Interrupt

Thu Oct 16, 2014 12:22 pm

...but interrupt_flag isn't set true instantly, so the program will take antoher loop pass.
Interrupts are asynchronous. So, if the interrupt happens after your test then yes the loop will continue until the test is reached again.
e.g.

Code: Select all

while(1)
{
   if(interrupt_flag)
   {
       break;
   }
// <-- if Interrupt occurs here...
   foo();               // ...loop will continue until test above
   bar();               
   foovar = barvar;
   barvar++;
   ...
}
You may have to rethink your design or accept that the loop will always complete a full pass (until the test).
Fry12
Posts: 14
Joined: Tue Aug 26, 2014 4:29 pm

Re: External Interrupt

Thu Oct 16, 2014 2:02 pm

Thanks for your feedback!

In reality, the structure of the while-loop now looks like this:

Code: Select all

while(1)
{
   if (interrupt_flag) {
       break;
   }

   foo();

   if (cond1 == true) {
       bar1(); 
   } else if (cond2 == true) {
       bar2();
   }

   counter++;
}
After the test for interrupt_flag there are three other instructions. The level change of Pin PA10 always occurs after the if-else-clause (after bar1() or bar2() in the example, to be precise), but it still takes another loop pass, as interrupt_flag isn't instantly set to true. So i have to set a delay between the incrementation of counter and the if-check.

I wrote similar while loops, which always worked as intended.
Interestingly, if i first call this function with cond2 set to true, everything works fine. If i then call the function with cond1 set to true, this approach doesn't work, although bar1() and bar2() are very similar: They just send a short message by SPI.

Is this still a problem of asynchronism?

Edit: If i call the function a single time with cond1 == true, it also works as expected. It also works if i call it with cond1 == true at first and then antoher time with cond2 == true.
jharley
Posts: 238
Joined: Thu Dec 06, 2012 6:40 am

Re: External Interrupt

Thu Oct 16, 2014 2:57 pm

The level change of Pin PA10 always occurs after the if-else-clause (after bar1() or bar2() in the example, to be precise)
How are you ensuring this is the case?
IOW: what is controlling the level change at PA10?
...as interrupt_flag isn't instantly set to true
why do you think this?
Is this still a problem of asynchronism?
Yes. You must expect that interrupts will happen at any time, unless your software is controlling the PA10 input.

You haven't provided enough information on your design and what you are trying to do...

EDIT: Not sure if this gives you any ideas...

Code: Select all

uint8_t func( ... )
{
   uint8_t result = true;

   foo();

   if (cond1 == true) 
   {
       SPI_SendMsg1();
   } else if (cond2 == true) 
   {
       SPI_SendMsg2();
   }

    // wait for the interrupt or timeout
   counter = 0;
   while( (!interrupt_flag) && (counter < TIMEOUTVAL) ) 
   {
       counter++;
   }
    // ... Reset interrupt_flag? (i.e. interrupt_flag = 0;)

   if( counter >= TIMEOUTVAL)
   {
      result = false;
   }

   return( result );
}

Return to “SAM4 Cortex-M4 MCU”

Who is online

Users browsing this forum: No registered users and 3 guests