[2] Kurs: pierwsze kroki z STM32F0DISCOVERY. Zabawy z LED-ami
Sterowanie świeceniem diod przy użyciu przycisku
Ponieważ na płytce DISCOVERY jest umieszczony przycisk (koloru niebieskiego), którego stan może być testowany przez mikrokontroler, wypada użyć go w kolejnym projekcie. Tym razem diody nie będą zapalały się i gasły samoczynnie, lecz będą one sterowane przez naciśnięcie przycisku. Każde naciśnięcie spowoduje zmianę stanu świecenia obu diod. Projekt ten jest dobrym pretekstem do zaprezentowania techniki programowego ignorowania drgań styków przycisku. W celu uniknięcia reakcji na drgania styków, stan przycisku będzie testowany ze stałą częstotliwością (100 razy na sekundę), a reakcja na naciśnięcie nastąpi, gdy po trzech kolejnych okresach zwolnienia przycisku zostanie zarejestrowane jego naciśnięcie. Dzięki temu program nie będzie reagował na naprzemienne odczyty naciśnięcia i zwolnienia, które mogą mieć miejsce w przypadku drgań styków. Taka technika obsługi przycisku może być stosowana, gdy nie występują zmiany stanów wejścia przycisku spowodowane czynnikami innymi niż samym drżeniem styków, np. zakłóceniami elektrycznymi na liniach łączących przyciski z wejściami mikrokontrolera.
Program różni się od poprzedniego sekwencją inicjującą oraz procedurą obsługi przerwania timera SysTick. Sekwencję inicjującą uzupełniono o włączenie portu A, do którego podłączony jest przycisk. W procedurze obsługi przerwania SysTick zdefiniowano zmienną statyczną bstate, przechowującą cztery ostatnio zarejestrowane stany przycisku. Zmienna ta zachowuje się jak 4-bitowy rejestr przesuwający. Na początku obsługi przerwania jest ona przesuwana o jeden bit w lewo, a na najmniej znaczący bit wsuwana jest informacja o nowym stanie przycisku. Stany wejść portu GPIO są dostępne dla oprogramowania w rejestrze wejściowym portu GPIO – IDR. Ponieważ przycisk został podłączony na płytce STM32F0DISCOVERY dość nietypowo – pomiędzy dodatnim biegunem zasilania i wejściem, przy naciśnięciu przycisku wejście portu przyjmie stan 1. Procedura obsługi przerwania zmieni stan diod, gdy zmienna bstate osiągnie wartość 1 (binarnie – 0001), co odpowiada trzem okresom zwolnienia i następującym po nim naciśnięciu przycisku.
/* STM32F0DISCOVERY tutorial SysTick-based LED toggle by button press gbm, 12'2012 */ #include "stm32f0xx.h" //======================================================================== // defs for STM32F05x chips #define GPIO_MODER_OUT 1 //======================================================================== // defs for STM32F0DISCOVERY board #define LED_PORT GPIOC #define BLUE_LED_BIT 8 #define GREEN_LED_BIT 9 #define BUTTON_PORT GPIOA #define BUTTON_BIT 0 //======================================================================== // Timings #define SYSCLK_FREQ HSI_VALUE #define SYSTICK_FREQ 100 // 100 Hz ->10 ms //======================================================================== struct init_entry_ { volatile uint32_t *loc; uint32_t value; }; static __INLINE void writeregs(const struct init_entry_ *p) { for (; p->loc; p ++) *p->loc = p->value; } //======================================================================== void SystemInit(void) { FLASH->ACR = FLASH_ACR_PRFTBE; // enable prefetch } //======================================================================== static const struct init_entry_ init_table[] = { // port setup {&RCC->AHBENR, RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIOAEN}, // GPIOC, GPIOA { &LED_PORT->MODER, GPIO_MODER_OUT << (GREEN_LED_BIT <<1) | GPIO_MODER_OUT <<(BLUE_LED_BIT <<1) }, // set LED pins as outputs {(__IO uint32_t *)&LED_PORT->ODR, 1 <LOAD, SYSCLK_FREQ / SYSTICK_FREQ - 1}, {&SysTick->VAL, 0}, { &SysTick->CTRL, SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk }, // sleep {&SCB->SCR, SCB_SCR_SLEEPONEXIT_Msk}, // sleep while not in handler {0, 0} }; //======================================================================== int main(void) { writeregs(init_table); __WFI(); // go to sleep } //======================================================================== void SysTick_Handler(void) { static uint8_t bstate = 0; // button history - last 4 samples if ((bstate = (bstate <<1 &0xf) | (BUTTON_PORT->IDR >>BUTTON_BIT &1)) == 1) // button was released, now it is pressed - change LED state LED_PORT->ODR ^= 1 << GREEN_LED_BIT | 1 <
Przełączanie trybu migotania diody przy użyciu przycisku
Kolejny projekt, BtnBlink, jest niemal dokładnym funkcjonalnym odpowiednikiem przykładowego programu dostarczanego wraz z płytką STM32F0DISCOVERY, dzięki rezygnacji z użycia bibliotek do obsługi peryferiali jest jednak od niego krótszy i przejrzystszy w zapisie i zajmuje mniej miejsca w pamięci. Nowy program stworzymy na bazie poprzedniego, uzupełniając go o obsługę zmiany trybu świecenia diod.
Procedura obsługi przerwania timera SysTick zostanie rozbudowana o reakcję na naciśnięcie przycisku. Każde naciśnięcie przycisku ma powodować zaświecenie niebieskiej diody na określony czas (1 sekunda) oraz zmieniać tryb sterowania diody zielonej. Trzy tryby odpowiadają kolejno: wolnemu migotaniu, szybkiemu migotaniu i wygaszeniu. Program nie reaguje na naciśnięcie przycisku podczas świecenia diody niebieskiej.
W procedurze obsługi przerwania zadeklarujemy następujące zmienne statyczne (czyli takie, których wartości są zachowywane pomiędzy wywołaniami procedury):
- blink_mode – określa tryb sterowania zielonej diody,
- blink_timer – do odmierzania okresu świecenia zielonej diody,
- blue_led_timer – do odmierzania czasu świecenia diody niebieskiej,
- green_on_time – przechowuje czas zaświecenia diody zielonej,
- bstate – przechowuje stan przycisku rejestrowany po każdym przerwaniu timera.
Tablica stałych blink_period[] zawiera okresy świecenia zielonej diody dla poszczególnych trybów pracy. Na końcu każdego okresu świecenia następuje ustawienie czasu kolejnego okresu świecenia i czasu zapalenia diody równego połowie okresu świecenia, zaokrąglonej w dół. Dla trybu wygaszenia (okres równy 1) czas zapalenia jest równy 0.
Do zaświecania i gaszenia diod użyto rejestrów BSRR i BRR modułu GPIO. Umożliwiają one zmianę stanu dowolnych bitów portu z zachowaniem stanu pozostałych bitów, bez wcześniejszego odczytu stanu portu i konieczności wykonywania operacji logicznych Zapis jedynki do dolnej połowy rejestru BSRR powoduje ustawienie wyjścia w stan 1, a zapis jedynki do BRR – ustawienie wyjścia w stan 0. Ustawienie wyjścia w stan 0 jest również możliwe poprzez zapis jedynki do górnej połowy rejestru BSRR.
Naciśnięcie nie jest wykrywane w czasie świecenia niebieskiej diody, czyli w ciągu sekundy od poprzedniego naciśnięcia przycisku – wtedy, gdy zmienna blue_led_timer ma wartość różną od zera.
/* STM32F0DISCOVERY tutorial SysTick-based blinker with table-driven init and blink mode switching gbm, 12'2012 */ #include "stm32f0xx.h" //======================================================================== // defs for STM32F0DISCOVERY board #define LED_PORT GPIOC #define BLUE_LED_BIT 8 #define GREEN_LED_BIT 9 #define BUTTON_PORT GPIOA #define BUTTON_BIT 0 #define GPIO_MODER_OUT 1 #define SYSCLK_FREQ HSI_VALUE #define SYSTICK_FREQ 100 // 100 Hz ->10 ms #define BLINK_PERIOD 50 // * 10 ms #define BLUE_ACK_TIME 100 //======================================================================== struct init_entry_ { volatile uint32_t *loc; uint32_t value; }; static __INLINE void writeregs(const struct init_entry_ *p) { for (; p->loc; p ++) *p->loc = p->value; } //======================================================================== void SystemInit(void) { FLASH->ACR = FLASH_ACR_PRFTBE; // enable prefetch } //======================================================================== static const struct init_entry_ init_table[] = { // port setup {&RCC->AHBENR, RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIOAEN}, // GPIOC, GPIOA { &LED_PORT->MODER, GPIO_MODER_OUT <<(GREEN_LED_BIT <<1) | GPIO_MODER_OUT <<(BLUE_LED_BIT <<1) }, // set LED pins as outputs //SysTick setup {&SysTick->LOAD, SYSCLK_FREQ / SYSTICK_FREQ - 1}, {&SysTick->VAL, 0}, { &SysTick->CTRL, SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk }, {&SCB->SCR, SCB_SCR_SLEEPONEXIT_Msk}, // sleep while not in handler {0, 0} }; //======================================================================== int main(void) { writeregs(init_table); __WFI(); // go to sleep } //======================================================================== void SysTick_Handler(void) { static enum {BM_SLOW, BM_FAST, BM_OFF} blink_mode = BM_SLOW; static uint8_t blink_timer = BLINK_PERIOD; static uint8_t blue_led_timer = 0, green_on_time = 0; static uint8_t bstate = 0; static const uint8_t blink_period[] = {100, 50, 1}; bstate = (bstate <<1 &0xf) | (BUTTON_PORT->IDR >>BUTTON_BIT &1); if (blue_led_timer) { if (-- blue_led_timer == 0) LED_PORT->BRR = 1 <BM_OFF) blink_mode = BM_SLOW; blue_led_timer = BLUE_ACK_TIME; LED_PORT->BSRR = 1 < BRR = 1 < >1; LED_PORT->BRR = 1 < BSRR = 1 <
Sterowanie LED przebiegiem PWM
W kolejnym projekcie, PWMblink1, zamiast włączać i wyłączać diody będziemy zmieniać ich jasność poprzez sterowanie wypełnieniem impulsów powodujących świecenie diod. Użyjemy do tego celu timera TIM3, gdyż może on sterować sprzętowo liniami portów, do których są dołączone diody. Nie będzie w tym projekcie potrzebny timer SysTick – do zgłaszania periodycznych przerwań użyjemy timera TIM3.