Почему эта функция не включает и не выключает светодиод?

Я использую микроконтроллер Atmel SAM3x8E и пытаюсь сделать простое переключение светодиодов при нажатии кнопки. Я использую кнопку настройки подтягивания для запуска процедуры прерывания.

Это инициализация для прерывания:

// 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); 

Тогда это процедура прерывания:

// 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)
}

Тем не менее, когда я запускаю его, я не могу заставить светодиод переключаться. Он просто включается и остается включенным. Функция, которую вызывает pio_toggle_pin_group, следующая:

 * \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;
    }
}

Есть идеи, почему мой светодиод не переключается так, как я хочу? Я ссылался на документацию Atmel ASF, но до сих пор не могу понять это.

3 ответа

Решение

Я не могу помочь вам с фактическими вызовами функций, но предположим, что вы используете прерывание по краю. Насколько я вижу, вы вызываете обработчик прерываний для каждого нарастающего фронта. Однако после первого нарастающего фронта вам нужно активировать при отпускании кнопки, что будет падающим фронтом, поэтому вам нужно изменить ребро в обработчике прерываний.

Но вы должны принять во внимание, что механические кнопки не генерируют чистый, единственный край при нажатии или отпускании. Это вместо того, чтобы подпрыгнуть. Для обычных кнопок с кратковременным контактом с подтягивающим (или понижающим) резистором это приводит к появлению нескольких импульсов для каждого события, поэтому светодиод может включаться / выключаться несколько раз и оставаться в произвольном состоянии, которое может, случайно, быть "включено" далеко Большую часть времени. Если возможно, проверьте с помощью осциллографа.

Это можно обойти в аппаратном обеспечении с помощью конденсатора или в программном обеспечении, используя таймер с мертвым временем после соответствующего фронта перед реакцией на любое другое событие кнопки. Время простоя зависит от типа кнопки, но типичные значения составляют от 5 до 20 мс и должны быть указаны в таблице данных кнопки. В случае сомнений используйте максимально допустимое значение.

Вот что у меня получилось:

// 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);
}

и вот так называемая функция "set", которая работала, чтобы включить светодиод:

void pio_set(Pio *p_pio, const uint32_t ul_mask)
{
    p_pio->PIO_SODR = ul_mask;
}

Чтобы избежать случайных всплесков, генерируемых прыгающей кнопкой, попробуйте использовать входной фильтр. На sam3x8e вы можете включить это, установив регистр PIO?->PIO_DIFSR на BUTTON_1 или BUTTON_2 в вашем случае.

Также очистите регистр состояния прерывания PIO, прочитав PIO?->PIO_ISR, Это очищает все входные изменения и позволяет вводить прерывание несколько раз.

Другие вопросы по тегам