1

I am using the Atmel SAM3x8E micro-controller and trying to do a simple LED toggle when I press a button. I am using pull-up configuration button to trigger an interrupt routine.

This is the initialization for the interrupt:

// Set button pins as pull-up inputs 
pio_set_input(PIOC, BUTTON_1, PIO_PULLUP);
pio_set_input(PIOC, BUTTON_2, PIO_PULLUP); 

// Configure button input pin interrupt mode and handler (Rising Edge)
pio_handler_set(PIOC, ID_PIOC, BUTTON_1,  PIO_IT_RISE_EDGE, button_press_handler);
pio_handler_set(PIOC, ID_PIOC, BUTTON_2,  PIO_IT_RISE_EDGE, button_press_handler);

// Enable the interrupts
pio_enable_interrupt(PIOC, BUTTON_1); 
pio_enable_interrupt(PIOC, BUTTON_2); 
NVIC_EnableIRQ(PIOC_IRQn); 
NVIC_EnableIRQ(PIOC_IRQn); 

Then this is the interrupt routine:

// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)
{
   pio_toggle_pin_group(PIOC, BLUE_LED4); // NOT TOGGLING LED (ONLY TURNS IT ON)
}

Yet when I run it, I cannot get the LED to toggle. It simply turns on and stays on. The function that pio_toggle_pin_group calls is the following:

 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Bitmask of one or more pin(s) to configure.
 */
void pio_toggle_pin_group(Pio *p_pio, uint32_t ul_mask)
{
    if (p_pio->PIO_ODSR & ul_mask) {
        /* Value to be driven on the I/O line: 0. */
        p_pio->PIO_CODR = ul_mask;
    } else {
        /* Value to be driven on the I/O line: 1. */
        p_pio->PIO_SODR = ul_mask;
    }
}

Any ideas as to why my LED is not toggling the way I want? I've refereed to the Atmel ASF documentation but I still cannot figure this out.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Hooplator15
  • 1,540
  • 7
  • 31
  • 58
  • Have you checked that the pin actually changes state? That the interrupt is really called? – Some programmer dude Apr 25 '15 at 23:44
  • Yes, the LED is set to low initially, but when I press my tactile button, it turns on, so it must be calling the interrupt. I can turn on any number of the LED's I have connected (BLUE_LED1-4) by changing that in the interrupt routine. – Hooplator15 Apr 25 '15 at 23:46

3 Answers3

1

I cannot help you with the actual function calls, but suppose you use a edge interrupt. As far as I see, you call an interrupt handler for each rising edge. However, after the first rising edge, you need to trigger on button release, which would be a falling edge, so you need to change the edge within the interrupt handler.

But you must take into account that mechanical buttons do not generate a clean, single edge when pressed or released. It does instead bounce. For normal momentary contact buttons with pullup (or down) resistor, this results in multiple pulses for each event, so the LED may turn on/off multiple times and stay in an arbitrary state which might - by chance - be "on" far by most times. If available, check with an oscilloscope.

This can be circumvented in hardware by a capacitor or in software using a timer with a dead time after the relevant edge before reacting to any other button event. The dead time depends on the type of button, but typical values are 5 to 20ms and should be mentioned in the datasheet of the button. If in doubt, use the highest acceptable value.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • Thanks for the help, I ended up getting it to work by ditching pio_toggle_pin_group and using pio_set instead... the only issue now, weirdly, is that the LED's "reset" every 18seconds.... So if I turn them on, 18 seconds later, they will turn off, whether I hit the button or not. – Hooplator15 Apr 26 '15 at 01:02
  • That might be a race condition with another interrupt accessing the same port register. ARM CPUs do not have atomic read-modify-write instructions like AVR or MSP430, so to set a bit in a word, it has to read it into a register, modify that register and write back. If there is an interrupt right in-between, old data may be written back to the register, annihilating the effect of the nested interrupt. To aviod this, use stdatomic (C11, gcc from 4.7.2 with an external header, 4.9.? with header included in the installation). Or diable IRQs. – too honest for this site Apr 26 '15 at 01:11
  • There might be also a hardware-issue (all seen already) which generates random spikes detected as edges. – too honest for this site Apr 26 '15 at 01:16
0

This is what ended up working for me:

// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)
{
    // Turn the LED's on or off
        if (pio_get(PIOC, PIO_TYPE_PIO_OUTPUT_0, BLUE_LED4))
        pio_clear(PIOC, BLUE_LED4);
        else
        pio_set(PIOC, BLUE_LED4);
}

and here is the called "set" function which worked to turn the LED on:

void pio_set(Pio *p_pio, const uint32_t ul_mask)
{
    p_pio->PIO_SODR = ul_mask;
}
Hooplator15
  • 1,540
  • 7
  • 31
  • 58
  • That does not look different, except the called function which are not given here (it is alwas a good idea here to show a stripped down, but complete version of the code in question - don't expect ppl want to read toolkit documentation first). – too honest for this site Apr 26 '15 at 01:15
  • Sorry about that, I added in the pio_set function. I agree, that it looks nearly the same, but if I use pio_toggle_pin_group, it will not work. – Hooplator15 Apr 26 '15 at 01:22
  • Well, I would try to figure out, what actually makes the difference. Did you check the bouncing problem? This is actually really a major problem with buttons, expecially when using edge-detection hardware. If you have not taken any measures agains, it will kick you in the b??? eventually;-} That's why I would not accept code from one of my students without proper countermeasures (or good reasons why that is not be necessary. – too honest for this site Apr 26 '15 at 02:51
  • FINALLY figured this one out... I missed the all important "board_init();" Without initializing the board, things do not work correctly. I do appreciate your help, it was very insightful, especially when you mentioned that ARM CPUs do not have atomic read-modify-write instructions and the thing about nested interrupts. You got me thinking, but it turned out to be a simple omission. – Hooplator15 Apr 26 '15 at 05:30
  • That's why I prefere build-integration with a dynamic module initialization framework. You simply cannot forget to initialize a module as the build system cares about that automatically;-) – too honest for this site Apr 26 '15 at 14:07
0

To avoid those random spikes generated by the bouncing button try to use a debouncing input filter. On the sam3x8e you can enable this by setting the register PIO?->PIO_DIFSR to BUTTON_1 or BUTTON_2 in your case.

Also clear the status register of the PIO interrupt by reading PIO?->PIO_ISR. This clears all input changes and enables the interrupt to be entered multiple times.