04 lipca 2018

Parę słów wyjaśnienia

Pisząc pierwszy wpis chciałem skoncentrować się na uruchomieniu kompletnego programu. Okazało się, że tekstu wyszło całkiem sporo, więc niewiele zostało miejsca na wyjaśnienie kodu samego programu.
Teraz spróbuję nadrobić te zaległości, opisać jego działanie oraz samych linii wejscia-wyjścia.

Zacznijmy od taktowania procesora. Wspomniałem o zakomentowaniu wywołania SystemInit(). Standardowo ta funkcja konfiguruje pętlę PLL jako źródło taktowania, jej pominięcie pozostawia domyślne źródło - czyli wbudowany generator RC o częstotliwości 16 MHz.

Kolejna sprawa to porty wejścia-wyjścia, czyli GPIO. Opisywany mikrokontroler ma całkiem sporo wyprowadzeń. Porty grupują piny po maksymalnie 16 sztuk, numerowane są od 0 do 15. Same porty oznaczane literami - od A, aż do K.

Na płytce ewaluacyjnej znajdziemy dwie diody, których stanem możemy sterować - są podłączone do wyprowadzeń PG13 i PG14.

Domyślnie większość modułów peryferyjnych mikrokontrolera jest wyłączona - dzięki temu układ pobiera mniej prądu. Oznacza to, że port G do którego podłączone są diody, należy najpierw uruchomić. W tym celu trzeba najpierw sprawdzić do jakiej magistrali jest podłączony. Wszystkie porty są wpięte w AHB1, więc kod:

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;

Sprawia, że port G zaczyna działać (tzn. jest podłączany do źródła sygnału zegarowego).
Domyślnie (prawie) wszystkie piny są ustawione w tryb wejścia. Wcześniej opisywałem mikrokontroler STM32F103, w nim ustawianie trybów było nieco skomplikowane. Nowsze układy z rodziny STM32 są pod tym względem znacznie łatwiejsze do użycia.
Rejestr MODER przechowuje konfigurację wszystkich pinów danego portu. Działanie pinu opisują dwa bity (stąd 32 bitowy rejestr bez problemu obsługuje cały port).


Jak widać jest to prawie tak proste jak użycie rejestru DDRx w układach AVR. Po uruchomieniu lub resecie rejestr MODER dla portu G zawiera wartość 0 więc linie podłączone do diod są wejściami.
Chcąc sterować ich świeceniem trzeba przestawić tryb na "01", czyli zapalić zerowy bit dla pinu 13 i 14. Plik nagłówkowy stm32f429xx.h definiuje odpowiednie stałe, więc wystarczy napisać:

GPIOG->MODER |= GPIO_MODER_MODE13_0|GPIO_MODER_MODE14_0;

Teraz można przejść do samego sterowania stanem pinów. Rejestr wyjściowy to ODR, teoretycznie więc można byłoby napisać coś takiego:

GPIOG->ODR |= 1u << 13;

Jest to jednak bardzo złe rozwiązanie - kod może źle działać jeśli w czasie jego wykonywania pojawi się przerwanie. Najlepiej zapamiętać, żeby nigdy tak nie robić (albo chociaż prawie nigdy).
Są jeszcze co najmniej dwa, znacznie lepsze rozwiązania. Pierwsze to zastosowanie bit-bandingu. Dzięki tej technice można odwoływać się do pojedynczych bitów. Taka metoda była dość popularna w układach rodziny F1, jednak producent układów czyli firma ST powoli wydaje się odchodzić o tej techniki.
Pozostaje jeszcze jedna możliwość, czyli użycie rejestru BSRR (bit set/reset register).
Jest to rejestr tylko do zapisu, a znaczenie mają jedynie bity zapalone (logiczne 1). Zapis do dolnych 16 bitów powoduje wystawienie stanu wysokiego na odpowiednim pinie, a do górnych - niskiego.


 Czyli aby zapalić diodę możemy napisać:
 
GPIOG->BSRR = 1u << 13;

Natomiat aby zgasić:
 
GPIOG->BSRR = 1u << 29;

Nie ma tutaj odczytu wartości, więc opracja jest atomowa i przerwania nie są już groźne. Plik nagłówkowy stm32f429xx.h dostarcza odpowiednie definicje, więc ładniejsza wersja kodu zapalającego diodę to:

GPIOG->BSRR = GPIO_BSRR_BS13;

Natomiast do jej zgaszenia używamy:

GPIOG->BSRR = GPIO_BSRR_BR13;

Teraz można już bez problemu przeanalizować cały program, który na wszelki wypadek jeszcze raz umieszczę:

#include <stdint.h>
#include "stm32f429xx.h"

int main(int argc, char *argv[])
{
        volatile uint32_t dly;

        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;

        GPIOG->MODER |= GPIO_MODER_MODE13_0|GPIO_MODER_MODE14_0;

        while (1) {
                GPIOG->BSRR = GPIO_BSRR_BS13;
                GPIOG->BSRR = GPIO_BSRR_BR14;
                for (dly = 0; dly < 500000; dly++)
                        ;
                GPIOG->BSRR = GPIO_BSRR_BS14;
                GPIOG->BSRR = GPIO_BSRR_BR13;
                for (dly = 0; dly < 500000; dly++)
                        ;
        }

        return 0;
}

   

Brak komentarzy:

Prześlij komentarz