01 kwietnia 2017

Odczyt stanu wejścia za pomocą przerwania

Poprzednio zobaczyliśmy jak odczytać stan wejścia. Program działał bardzo ładnie, jednak cały czas mikrokontrolera był wykorzystywany na aktywną pętlę, w której sprawdzaliśmy czy aby nie został wciśnięty przycisk. Teraz spróbujemy do tego celu wykorzystać przerwania.

Początek programu wymaga niewielkiej zmiany - musimy uruchomić zegar dla alternatywnych funkcji portów:


/* Enable the GPIO Clock */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;

Teraz musimy skonfigurować przerwanie na pinie do którego podłączony jest przełącznik (czyli PC13).
Najpierw wyłączamy maskowanie przerwania na linii 13:

    /* Interrupt mode */
    EXTI->IMR |= EXTI_IMR_MR13;

Przerwanie może być aktywowane opadającym zboczem sygnału (wciśnięcie przycisku), narastającym zoboczem (zwolnienie przycisku), albo obydwoma. Napiszemy program, który będzie wykrywał tylko wciskanie przycisku - czyli reagował na opadające zbocze. W tym celu ustawiamy odpowiednią flagę w rejestrze FTSR:

    /* Falling edge */
    EXTI->FTSR |= EXTI_FTSR_TR13;

Gdybyśmy chcieli otrzymywać przerwanie po zwolnieniu przycisku, powinniśmy ustawić odpowiedni bit w rejestrze RTSR.
Teraz musimy skonfigurować, który port będzie generował przerwanie. W przypadku STM32 przerwania od wejść GPIO są nieco dziwnie rozwiązane - nie można obsługiwać przerwania od pinu o tym samym numerze na więcej niż jednym porcie. Oznacza to, że jeśli chcemy otrzymywać przerwanie po zmianie na pinie PC13, nie możemy jednocześnie obsługiwać PB13. Możemy za to np. PB12 lub PA11.
Wybieramy port C do obsługi przerwania:

    /* Selects the GPIO pin used as EXTI Line. */
    AFIO->EXTICR[13 / 4] |= AFIO_EXTICR4_EXTI13_PC;

Liczba 13 nadal oznacza numer pinu, PC to właśnie wybór portu C. Gdybyśmy chcieli mieć przerwanie na linii PA13, moglibyśmy użyć stałej AFIO_EXTICR4_EXTI13_PA.
Konfiguracja jest już prawie gotowa, pozostało tylko uruchomić samo przerwanie:

    /* Enable IRQ channel */
    NVIC->ISER[EXTI15_10_IRQn / 32] = 1 << (EXTI15_10_IRQn % 32);

Zapomnieliśmy o najważniejszym, czyli procedurze obsługi przerwania. Nasz pin należy do grupy pinów 10-15, więc do jego obsługi musimy napisać funkcję EXTI15_10_IRQHandler.
Będziemy w niej naprzemiennie zapalać i gasić diodę LED:

void EXTI15_10_IRQHandler(void)
{
static int led_state = 0;

if (EXTI->PR & EXTI_PR_PR13) {

if (led_state) {
GPIOA->BRR = GPIO_BRR_BR5; // turn off
led_state = 0;
} else {
GPIOA->BSRR = GPIO_BRR_BR5; // turn on
led_state = 1;
}

EXTI->PR = EXTI_PR_PR13;
}
}

Właściwie jedyne co ciekawe to rejestr PR. Najpierw odczytujemy jego wartość, żeby sprawdzić od którego wejścia pochodzi przerwanie. Następnie zapisujemy do niego EXTI_PR_PR13 co powoduje wyzerowanie flagi przerwania. 
Jak widać uruchomienie przerwania nie jest wcale takie trudne. Kod programu (bez definicji stałych) wygląda następująco:


void EXTI15_10_IRQHandler(void)
{
static int led_state = 0;

if (EXTI->PR & EXTI_PR_PR13) {

if (led_state) {
GPIOA->BRR = GPIO_BRR_BR5; // turn off
led_state = 0;
} else {
GPIOA->BSRR = GPIO_BRR_BR5; // turn on
led_state = 1;
}

EXTI->PR = EXTI_PR_PR13;
}
}

int main(void)
{
int i;

/* Enable the GPIO Clock */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;

// PA5 - LED
GPIOA->CRL &= ~GPIO_CRL(GPIO_MASK, 5);
GPIOA->CRL |= GPIO_CRL(GPIO_OUT_PP_50MHZ, 5);

// PC13 - button
GPIOC->CRH &= ~GPIO_CRH(GPIO_MASK, 13);
GPIOC->CRH |= GPIO_CRH(GPIO_IN_FLOAT, 13);

/* Interrupt mode */
    EXTI->IMR |= EXTI_IMR_MR13;

    /* Falling edge */
    EXTI->FTSR |= EXTI_FTSR_TR13;

    /* Selects the GPIO pin used as EXTI Line. */
    AFIO->EXTICR[13 / 4] |= AFIO_EXTICR4_EXTI13_PC;

    /* Enable IRQ channel */
    NVIC->ISER[EXTI15_10_IRQn / 32] = 1 << (EXTI15_10_IRQn % 32);

while (1)
{
}
}

Na koniec wypada wspomnieć o jednej ważnej rzeczy - drganiu styków. Na płytce Nucleo przycisk wyposażony jest w prosty filtr RC, więc w programie nie zajmowaliśmy się tym problemem. Niestety nawet ten filtr nie jest idealny - jeśli odpowiednio długo pobawimy się programem, zobaczymy że czasem generowane jest więcej niż jedno przerwanie. Najlepiej byłoby dodać cyfrową filtrację, jednak to nieco trudniejszy temat i nie związany z samym STM32F103, więc nie będziemy tego teraz rozwijać.

Brak komentarzy:

Prześlij komentarz