Operacje logiczne w C
W kodzie funkcji wysyłających do wyświetlacza polecenia sterujące, wykorzystana została operacja logicznej sumy – „LICZBA_NR_1 | LICZBA_NR_2”. Co powoduje wykonanie takiego kodu? Po „zamianie” obu liczb na postać binarną, operacja ta wykonywana jest osobno na każdej parze bitów, pochodzących z obu liczb i będących na tych samych pozycjach, licząc od prawej. Jeśli choć jeden z nich jest jedynką, w wyniku, na tej samej pozycji, zapiana zostanie jedynka. Istnieje również podobna operacja logicznej koniunkcji – jeśli oba bity będą takie same (zero i zero lub jedynka i jedynka), wynik przyjmie wartość jedynki. Definiujemy ją symbolem „LICZBA_NR_1 & LICZBA_NR_1”. Możemy także zanegować wszystkie bity w danej licznie tj. zamienić jedynki na zera i zera na jedynki, poprzedzając liczbę symbolem „~”.
Drugi projekt – wyświetlanie tekstu
Oprócz wyświetlania bitmap, bardzo często potrzebujemy prezentować na wyświetlaczu litery i cyfry. Rozbudujmy więc nasz projekt, aby to umożliwić. Musimy dodać czcionkę i funkcję wypisującą na wyświetlaczu kolejne znaki. Dodatkowo, wyposażymy mikrokontroler w bufor znaków i to do niego zapisywać będziemy tekst przed wyświetleniem.
Do plików „display.h” i „display.c” dopisujemy kod umieszczony w listingach 4 i 5. Plik „main.c” modyfikujemy zgodnie z listingiem 6.
List. 4. Zawartość pliku display.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#ifndef display_header #define display_header [...] static const uint8_t display_font[][5] = { { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x5f, 0x00, 0x00 }, { 0x00, 0x07, 0x00, 0x07, 0x00 }, [...] // Pełny kod dostępny do pobrania z serwera FTP { 0x00, 0x41, 0x36, 0x08, 0x00 }, { 0x10, 0x08, 0x08, 0x10, 0x08 }, { 0x78, 0x46, 0x41, 0x46, 0x78 } }; // Powyższa czcionka, autorstwa Pascala Stanga, pochodzi // z biblioteki GLCD, dostępnej na licencji BSD. Źródło: // https://github.com/andygock/glcd/blob/master/fonts/font5x7.h [...] void display_write_char(struct display_config * cfg, char chr); void display_rewrite_buffer(struct display_config * cfg); void display_clear_buffer(struct display_config * cfg); [...] #endif |
List. 5. Zawartość pliku display.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[...] void display_write_char(struct display_config * cfg, char chr) { for (uint8_t i = 0; i < 5; i++) display_write_data(cfg, display_font[chr - 0x20][i]); display_write_data(cfg, 0x00); } void display_rewrite_buffer(struct display_config * cfg) { display_set_dxy(cfg, horizontal, 0, 0); for(uint8_t i=0; i<5; i++) for(uint8_t j=0; j<14; j++) display_write_char(cfg, cfg->buffer[i][j]); } void display_clear_buffer(struct display_config * cfg) { for(uint8_t i=0; i<6; i++) for(uint8_t j=0; j<14; j++) cfg->buffer[i][j] = ' '; } |
List. 6. Zawartość pliku main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
[...] /* USER CODE BEGIN Includes */ #include "string.h" #include "display.h" /* USER CODE END Includes */ [...] int main(void) { [...] /* USER CODE BEGIN 2 */ struct display_config cfg; cfg.spi = &hspi1; cfg.reset_port = RST_GPIO_Port; cfg.reset_pin = RST_Pin; cfg.bl_port = BL_GPIO_Port; cfg.bl_pin = BL_Pin; cfg.dc_port = DC_GPIO_Port; cfg.dc_pin = DC_Pin; cfg.ce_port = CE_GPIO_Port; cfg.ce_pin = CE_Pin; display_setup(&cfg); display_clear_buffer(&cfg); char hw[] = "HELLO WORLD!"; memcpy(&(cfg.buffer[2][1]), hw, strlen(hw)); display_rewrite_buffer(&cfg); /* USER CODE END 2 */ [...] } [...] |
W pliku „display.h”, do definicji struktury display_config, dodajemy dwuwymiarową tablicę – bufor znaków. Umieszczamy tam także: tablicę map bitowych poszczególnych znaków – czcionkę oraz prototypy dodatkowych funkcji rysujących zawartość bufora znaków oraz poszczególne znaki. Do pliku „display.c”, dodajemy definicje tych funkcji. Modyfikujemy także sekcję „USER CODE 2”, dodając do niej wywołanie nowych funkcji. Od teraz, tekst który chcemy wyświetlić na wyświetlaczu umieszczamy w dwuwymiarowej tablicy „cfg.buffer[y][x]” – gdzie y i x to współrzędne, odpowiednio pionowe i poziome, znaku na wyświetlaczu. Najłatwiej zrobić to przy pomocy funkcji memcpy() – kopiującej fragmenty pamięci z podanego adresu, pod podany adres. Kolejne jej parametry to docelowy adres miejsca w pamięci, źródłowy adres oraz długość, w bajtach, kopiowanych danych. Aby wydobyć adres danej zmiennej lub komórki tablicy, z pamięci, poprzedzamy jej nazwę znakiem „&”.
Powyższy przykład możemy rozbudować i np., korzystając z funkcji sprintf() i snprintf(), zapisywać do bufora i wyświetlać na wyświetlaczu, odczytywane parametry stało i zmiennoprzecinkowe z różnych czujników, czy też zbudować proste menu obsługiwane przyciskami. Przydatnym może być umieszczenie wywoływania funkcji przerysowującej zawartość wyświetlacza, na podstawie bufora znaków, w funkcji obsługi przerwania zegara.
Aleksander Kurczyk