[PROJEKT] KA-NUCLEO-F411CE i Waveshare Accessory Shield – obsługa diody RGB i sterownika P9813
Programowanie funkcji obsługi sterownika
Włączenie zasilania diody LED RGB
Pierwszym etapem prac nad oprogramowaniem będzie ustawienie stanu wysokiego wyjścia oznaczonego jako ON_Pin (PA6). Spowoduje to włączenie zasilania +5 V, które zasili diodę LED. W tym celu dodajemy instrukcję:
HAL_GPIO_WritePin(ON_GPIO_Port, ON_Pin, GPIO_PIN_SET);
Wprowadzamy ją w bloku:
/* USER CODE BEGIN 2 */ /* USER CODE END 2 */
Blok ten znajduje się w funkcji main przed pętlą nieskończoną while.
Modyfikacja częstotliwości występowania przerwania SysTick
Aby mieć możliwość generowania sygnału zegarowego o okresie 70 us, musimy wprowadzić modyfikację występowania przerwania timera systemowego SysTick. Dokonujemy tego dopisując funkcję konfiguracyjną w postaci:
void SysTick_Init(void) { /**************************************** *SystemFrequency/1000 1ms * *SystemFrequency/100000 10us * *SystemFrequency/1000000 1us * *****************************************/ while (SysTick_Config(SystemCoreClock / 1000000) != 0) { } // One SysTick interrupt now equals 1us }
Dodajemy ją w bloku:
/* USER CODE BEGIN 4 */ /* USER CODE END 4 */
Dodatkowo powinniśmy dodać jej prototyp w sekcji:
/* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ void SysTick_Init(void); /* USER CODE END PFP */
W tej sekcji będziemy dodawali również prototypy do kolejnych pisanych przez nas funkcji.
Aby wszystko działało poprawnie, wywołujemy funkcję przed pętlą główną funkcji main:
/* USER CODE BEGIN 2 */ SysTick_Init(); HAL_GPIO_WritePin(GPIOA, ON_Pin, GPIO_PIN_SET); /* USER CODE END 2 */
Generowanie sygnału zegarowego dla sterownika P9813
Zgodnie z interfejsem opisanym w dokumentacji sterownika, jak i wcześniejszymi założeniami, wygenerujemy sygnał prostokątny będący sygnałem zegarowym. Zostanie on wygenerowany na pinie oznaczonym przez nas jako CIN (PB4). W tym celu zdefiniujemy funkcję DelayClockTime. Jej argumentem jest czas w mikrosekundach. Aby czas oczekiwania wynosił dokładnie 35 us, należy od podanego czasu odjąć 3 us. Następnie program przechodzi do pętli while, w której pozostaje do momentu kiedy zmienna delayUsCounter osiągnie zadaną wartość.
void DelayClockTime(int _TIME_TO_DELAY) { delayUsCounter = 0; if(_TIME_TO_DELAY > 3) { _TIME_TO_DELAY -= 3; } while(delayUsCounter < _TIME_TO_DELAY) { //Wait for correct value of delayUsCounter. } }
Zmienna delayUsCounter została zdefiniowana na samym początku pliku stm32f4xx_it.c w bloku:
/* USER CODE BEGIN 0 */ uint8_t delayUsCounter = 0; /* USER CODE END 0 */
oraz w pliku main.c:
/* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ extern uint8_t delayUsCounter; /* USER CODE END PV */
Parametr extern oznacza, że zmienna została zdefiniowana w innym pliku, ale definicja umożliwia korzystanie z niej w pliku main.c.
Inkrementacja wartości zmiennej odbywa się w funkcji przerwania SysTick:
void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ delayUsCounter++; /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ }
Aby wygodnie generować sygnał zegarowy, zdefiniowana została funkcja P9813_CLK():
void P9813_CLK() { HAL_GPIO_WritePin(GPIOB, CIN_Pin, GPIO_PIN_SET); DelayClockTime(35); HAL_GPIO_WritePin(GPIOB, CIN_Pin, GPIO_PIN_RESET); DelayClockTime(35); }
Umożliwia ona generowanie jednego okresu sygnału prostokątnego o czasie trwania 70 us.
Wysyłanie bajtu danych do sterownika P9813
Kolejną funkcją, którą zdefiniujemy i która jest niezbędna do pracy z układem P9813, jest funkcja przesyłająca jeden bajt danych. Będzie ona korzystać z wcześniej zdefiniowanej funkcji P9813_CLK(). Jej kod przedstawia się następująco:
void P9813_SendByte(uint8_t _BYTE_TO_SEND) { int i = 0; for(i = 0; i < 8; i++) { if((_BYTE_TO_SEND & 0x80) != 0) { HAL_GPIO_WritePin(GPIOB, DIN_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(GPIOB, DIN_Pin, GPIO_PIN_RESET); } P9813_CLK(); _BYTE_TO_SEND <<= 1; } }
Jej argumentem jest bajt, który zostanie wysłany do układu sterownika. Cała procedura ma za zadanie ustawić stan na wyjściu opisanym jako DIN, a następnie wygenerować pełen okres sygnału zegarowego dla sterownika P9813. Dane przesyłane są w kolejności od najstarszego do najmłodszego bitu. W tym celu została wykorzystana pętla for, która wykona się 8 razy (8 bitów w bajcie). Na początku pętli, wykonywana jest instrukcja sprawdzająca najstarszy bit zmiennej _BYTE_TO_SEND, a następnie w zależności od tego czy jest to 1 czy 0, ustawiany jest stan wysoki na wyjściu DIN. Po każdorazowym ustawieniu bitu następuje wykonanie funkcji P9813_CLK(). Jako ostatnia instrukcja w pętli wykonywana jest operacja przesunięcia bitowego z nadpisaniem o jeden bit w lewo. Dzięki takiemu zabiegowi sprawdzanie kolejnych bitów odbywa się za pomocą jednej i tej samej maski 0x80 (0b10000000).
Wysyłanie prefiksu do sterownika P9813
Zgodnie z protokołem komunikacyjnym, konieczne jest wysłanie do sterownika prefiksu będącego pierwszą częścią ramki danych. W tym celu została utworzona funkcja P9813_SendPrefix. Została ona przeciążona trzema argumentami o kolorze, jaki zostaje wysłany do sterownika diody LED RGB.
void P9813_SendPrefix(uint8_t _RED, uint8_t _GREEN, uint8_t _BLUE) { uint8_t prefixToSend = 0; prefixToSend = 0xC0; if((_BLUE & 0x80) == 0) prefixToSend |= 0x20; if((_BLUE & 0x40) == 0) prefixToSend |= 0x10; if((_GREEN & 0x80) == 0) prefixToSend |= 0x8; if((_GREEN & 0x40) == 0) prefixToSend |= 0x4; if((_RED & 0x80) == 0) prefixToSend |= 0x2; if((_RED & 0x40) == 0) prefixToSend |= 0x1; P9813_SendByte(prefixToSend); }
Zmienna wewnętrzna o nazwie prefixToSend zawiera kompletny prefix gotowy do wysłania. Na początku zmienna ta jest zerowana oraz zostają w niej zapisane dwa najstarsze bity o wartości „1” – 0xC0 (0b11000000). Następnie sprawdzane są dwa najstarsze bity każdego z podanych kolorów i ich negacja jest zapisana do zmiennej. Kolory zapisywane są w notacji BGR, zgodnie z opisanym w dokumentacji protokołem. Na końcu tak przygotowany prefix wysyłany jest do układu za pomocą wcześniej przygotowanej funkcji do przesyłania bajtu danych.
Wysyłanie ramki danych o kolorze do sterownika P9813
Ostatnia zdefiniowana funkcja przesyła kompletną ramkę danych do sterownika P9813 i umożliwia wyświetlenie danego koloru na diodzie LED RGB.
void P9813_SetColor(uint8_t _RED, uint8_t _GREEN, uint8_t _BLUE) { P9813_SendByte(0x00); P9813_SendByte(0x00); P9813_SendByte(0x00); P9813_SendByte(0x00); P9813_SendPrefix(_RED, _GREEN, _BLUE); P9813_SendByte(_BLUE); P9813_SendByte(_GREEN); P9813_SendByte(_RED); }
Zgodnie z protokołem wysyłane są 32 bity o wartości „0” oznaczające początek transmisji, następnie wysyłany jest prefix, a po nim trzy bajty opisujące natężenie koloru o wartości od 0…255 dla każdej z diod. Kolory również są wysyłane w notacji BGR.