Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy
Wałaszek |
©2024 mgr Jerzy Wałaszek
|
Dla naszej płytki DS001 (i następnych) należy przylutować goldpiny żeńskie, co ułatwi budowę płytek ćwiczeniowych:
Goldpiny łatwo wylutujesz z płytki za pomocą elektrycznego odsysacza cyny:
Warto coś takiego mieć w swoim warsztacie, koszt około 25zł.
Płytka zawiera 5 diod LED podłączonych do poszczególnych portów P0...P4 Digisparka poprzez oporniki 270...470Ω. Port P5 pozostawiamy niepodłączony, ponieważ w klonach pełni on zwykle funkcję RESET i bez przeprogramowania mikrokontrolera ATtiny 85 nie nadaje się do operacji wejścia/wyjścia (jeśli brakuje ci portów, to zmień Digisparka na Arduino Nano/Uno/Mega... Digispark jest przeznaczony do bardzo małych projektów).
Do działania płytka DS001 potrzebuje z Digisparka sygnałów P0...P4 oraz GND.
Moduł Digispark można podłączyć bezpośrednio do gniazd w standardowej płytce stykowej. Pierwszy sposób polega na tym, iż na płytce Digispark nie montujemy goldpina VIN, tylko goldpiny GND i +5V. Sposób podłączenia pokazany jest obok. Układ można wtedy zasilać poprzez gniazdo USB. Dodatkowo będzie dostarczane napięcie zasilające na linie czerwoną (+4,4V) i niebieską (masa). Układ może również być zasilany napięciem z płytki stykowej, jednak nie wyższym niż 5,5...6V. | ||
Drugi sposób polega na niemontowaniu goldpina +5V. Płytka Digispark może wtedy być zasilana z portu USB, lub z linii czerwonej i niebieskiej na płytce stykowej. W przypadku zasilania z USB, moduł Digispark nie będzie umieszczał napięcia 5V na liniach czerwonej i niebieskiej płytki stykowej. |
Trzeci sposób polega na połączeniu goldpinów płytki Digisparka przewodami z płytką stykową. Jest to rozwiązanie akceptowalne w prototypach. Jednak zachęcam do skonstruowania odpowiednich płytek ćwiczeniowych, ponieważ ich połączenie z Digisparkiem jest błyskawiczne i nie będziesz tracił czasu na budowę układu na płytce stykowej.
Samą płytkę DS001 można wykonać w technologii THT lub SMT. Poniżej są oba warianty.
Elementy | Ilość | Uwagi |
Dioda LED | 5 | 3mm czerwona |
Opornik 470Ω | 5 | 0,25W |
Goldpiny męskie długie 1x3 | 1 | lutowane do ścieżek |
Goldpiny męskie 1x5 | 1 | lutowane do ścieżek |
ds001_tht.sch | : | schemat ideowy w Eagle |
ds001_tht.brd | : | projekt płytki drukowanej w Eagle |
ds001_tht_a.png | : | obrazek z widokiem elementów na płytce |
ds001_tht_b.png | : | obrazek spodu płytki |
ds001.svg | : | plik Inkscape do wydruku na drukarce laserowej |
Elementy | Ilość | Uwagi |
Dioda LED | 5 | SMD 0805 czerwona |
Opornik 470Ω | 5 | SMD 0805 |
Goldpiny męskie długie 1x3 | 1 | |
Goldpiny męskie 1x5 | 1 |
ds001_smd.sch | : | schemat ideowy w Eagle |
ds001_smd.brd | : | projekt płytki drukowanej w Eagle |
ds001_smd_a.png | : | obrazek z widokiem elementów na płytce |
ds001_smd_t.png | : | obrazek góry płytki |
ds001.svg | : | plik Inkscape do wydruku na drukarce laserowej |
Ten program jest odpowiednikiem programu, który mruga jedną diodą. Tutaj mrugają wszystkie.
// Mrugacz 1 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia for(char i = LED_START; i <= LED_END; i++) pinMode(i,OUTPUT); } // Kontroluje świecenie (1) lub zgaszenie (0) diod LED char t = 1; // Pętla główna programu //---------------------- void loop() { // Zaświecamy lub gasimy diody LED na płytce for(char i = LED_START; i <= LED_END; i++) digitalWrite(i,t); delay(1000); t ^= 1; // Zmiana stanu t na przeciwny 0->1 lub 1->0 } |
Jeśli zasilasz Digisarka z portów USB komputera PC, to możesz zaobserwować dodatkowe mrugnięcia diod 3 i 4. Spowodowane jest to interferencją z portem USB (komunikacja przez USB wykonywana jest przez porty P3 i P4). Przy zasilaniu z innego źródła efekt ten nie występuje (np. podłącz do Digisparka ładowarkę 5V z wtyczką USB micro-B).
Program ustawia porty od numeru LED_START do LED_END jako wyjścia. W pętli głównej zapisuje do tych portów stan zmiennej t, która przyjmuje wartości 0 i 1.
Operator logiczny ^ oznacza funkcję logiczną XOR. Powoduje ona, że zawartość t zmienia się z 0 na 1 lub z 1 na 0. Dzięki temu jeden obieg pętli zapala diody LED, a następny je gasi, i tak w kółko.
Drobna modyfikacja Mrugacza 1 pozwala gasić i zapalać naprzemiennie diody LED.
// Mrugacz 2 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia for(char i = LED_START; i <= LED_END; i++) pinMode(i,OUTPUT); } // Kontroluje świecenie (1) lub zgaszenie (0) diod LED char t = 1; // Pętla główna programu //---------------------- void loop() { // Zaświecamy lub gasimy diody LED na płytce for(char i = LED_START; i <= LED_END; i++) { digitalWrite(i,t); t ^= 1; // Zmiana stanu t na przeciwny 0->1 lub 1->0 } delay(300); } |
Uwaga, liczba diod musi być nieparzysta, inaczej program nie zadziała.
Kolejny program wyświetla na 5 diodach LED przesuwający się punkt.
// Ruchomy punkt 1 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = LED_START; i <= LED_END; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja diody LED char p = LED_START; // Pętla główna //------------- void loop() { digitalWrite(p,HIGH); // Zapalamy diodę na pozycji p delay(100); digitalWrite(p,LOW); // Gasimy diodę na pozycji p p++; // Nowa pozycja if(p > LED_END) p = LED_START; } |
Ten program wyświetla na 5 diodach LED przesuwającą się przerwę.
// Ruchomy punkt 2 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i zapalamy diody for(char i = LED_START; i <= LED_END; i++) { pinMode(i,OUTPUT); digitalWrite(i,HIGH); } } // Pozycja diody LED char p = LED_START; // Pętla główna //------------- void loop() { digitalWrite(p,LOW); // Gasimy diodę na pozycji p delay(100); digitalWrite(p,HIGH); // Zapalamy diodę na pozycji p p++; // Nowa pozycja if(p > LED_END) p = LED_START; } |
Jako ćwiczenie zmodyfikuj dwa poprzednie programy, tak aby punkt lub przerwa przesuwały się w drugą stronę.
Ten program wyświetla odbijający się punkt świetlny
// Ping pong // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = LED_START; i <= LED_END; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja diody LED char p = LED_START; // Kierunek ruchu punktu char d = 1; // Pętla główna //------------- void loop() { digitalWrite(p,HIGH); // Zapalamy diodę na pozycji p delay(150); digitalWrite(p,LOW); // Gasimy diodę na pozycji p p += d; // Nowa pozycja // Jeśli punkt osiągnął krańcową pozycję, // to zmieniamy kierunek ruchu na przeciwny if((p == LED_START) || (p == LED_END)) d = -d; } |
Program najpierw zapala kolejno diody LED, a gdy zapali wszystkie, to je kolejno gasi. Otrzymujemy w ten sposób efekt węża.
// Wąż // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = LED_START; i <= LED_END; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja diody LED char p = LED_START; // Stan diody LED char t = 1; // Pętla główna //------------- void loop() { digitalWrite(p,t); // Zapalamy/gasimy diodę na pozycji p delay(100); p++; // Nowa pozycja // Jeśli punkt osiągnął krańcową pozycję, // to zmieniamy stan t i wracamy z pozycją na początek if((p > LED_END)) { t ^= 1; p = LED_START; } // Dodatkowe opóźnienie przed pojawieniem się węża if((p == LED_START) && t) delay(500); } |
Ten program tworzy licznik, który zlicza w systemie dwójkowym.
// Licznik dwójkowy // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Pierwsza i ostatnia dioda LED w szeregu #define LED_START 0 #define LED_END 4 // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = LED_START; i <= LED_END; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pętla główna //------------- void loop() { for(char i = LED_START; i <= LED_END; i++) { digitalWrite(i,digitalRead(i) ^ 1); if(digitalRead(i)) break; } delay(200); } |
W programie wprowadzamy nową funkcję:
digitalRead(n) – zwraca stan logiczny 0 lub 1, który panuje na porcie n: krótko: czyta cyfrowy stan portu.
Możesz się początkowo dziwić, dlaczego używamy funkcji odczytu, skoro ustawiliśmy porty na zapis. Otóż zapisując za pomocą funkcji digitalWrite() w określonym porcie stan logiczny 0 (LOW) lub 1 (HIGH) ustawiamy ten port. Funkcja digitalRead() po prostu odczytuje ten ustawiony w porcie stan. Dlatego nie musimy nigdzie zapisywać stanu portu mikrokontrolera. Zawsze możemy go odczytać. Tak działają mikrokontrolery AVR.
Aby zrozumieć działanie programu, musisz rozumieć zasady pracy liczników dwójkowych. Licznik dwójkowy zbudowany jest z bitów, które zmieniają przy zliczaniu swoje stany tak, iż otrzymujemy kolejne liczby dwójkowe. Załóżmy, że mamy licznik 4 bitowy (dla prostoty). Bity w tym liczniku mają wartości kolejnych potęg liczby 2. Na początku wszystkie bity mają stan 0, co odpowiada liczbie 0:
8 | 4 | 2 | 1 | wartości bitów |
0 | 0 | 0 | 0 | stan licznika = 0 |
Pierwsze zliczenie. Najmłodszy bit o wadze 1 zmienia stan na przeciwny:
8 | 4 | 2 | 1 | wartości bitów |
0 | 0 | 0 | 1 | stan licznika = 1 |
Drugie zliczenie. Najmłodszy bit zmienia swój stan na przeciwny. Jeśli po zmianie ma on wartość 0, to następny bit zmienia stan na przeciwny, czyli bit o wadze 2:
8 | 4 | 2 | 1 | wartości bitów |
0 | 0 | 1 | 0 | stan licznika = 2 |
Trzecie zliczenie. Najmłodszy bit znów zmienia swój stan na przeciwny, czyli 1. Ponieważ po zmianie nie mamy stanu 0, następny bit nie jest zmieniany:
8 | 4 | 2 | 1 | wartości bitów |
0 | 0 | 1 | 1 | stan licznika = 3 |
Czwarte zliczenie. Najmłodszy bit zmienia stan na 0. Ponieważ po zmianie ma stan 0, to kolejny bit licznika zmienia stan. Znów mamy 0, zatem następny bit zmienia stan na przeciwny. Na bicie o wadze 4 zmiany się kończą, ponieważ po zmianie ma wartość 1:
8 | 4 | 2 | 1 | wartości bitów |
0 | 1 | 0 | 0 | stan licznika = 4 |
Piąte zliczenie. Najmłodszy bit zmienia stan na 1.
8 | 4 | 2 | 1 | wartości bitów |
0 | 1 | 0 | 1 | stan licznika = 5 |
Szóste zliczenie. najmłodszy bit zmienia stan 0, co powoduje zmianę stanu na 1 następnego bitu:
8 | 4 | 2 | 1 | wartości bitów |
0 | 1 | 1 | 0 | stan licznika = 6 |
Siódme zliczenie: najmłodszy bit zmienia stan na 1:
8 | 4 | 2 | 1 | wartości bitów |
0 | 1 | 1 | 1 | stan licznika = 7 |
Ósme zliczenie. Najmłodszy bit zmienia stan na 0. To powoduje zmianę na 0 stanu bitu o wadze 2. To powoduje zmianę stanu na 0 bitu o wadze 4. To powoduje zmianę stanu na 1 bitu o wadze 8:
8 | 4 | 2 | 1 | wartości bitów |
1 | 0 | 0 | 0 | stan licznika = 8 |
Czy widzisz już prawidłowość?
Zaczynamy od najmłodszego bitu. Zmieniamy jego stan na przeciwny do tego, który obecnie ma (tzn. z 0 na 1 lub z 1 na 0). Jeśli po zmianie bit ma wartość 0, to to samo robimy z kolejnym bitem licznika. I tak dalej aż przejdziemy przez wszystkie bity, które mają być zmienione. To właśnie robi pętla for w programie.
W języku C wyrażenie x ^ 1 neguje (zmienia na przeciwny) najmłodszy bit x.
digitalRead(i) ^ 1 oznacza: odczytaj stan portu o numerze i, a następnie zwróć zanegowaną wartość.
digitalWrite(i,digitalRead(i) ^ 1) oznacza: wpisz do portu o numerze i jego zanegowany stan. Jeśli w porcie tym był stan 0, to będzie stan 1. Jeśli był stan 1, to będzie 0.
Następnie sprawdzamy stan portu po tej modyfikacji. Jeśli jest w nim teraz 1, to pętle przerywamy. Jeśli jest w nim stan 0, to w identyczny sposób postępujemy z kolejnym portem w pętli for.
W ten sposób w portach P powstaje licznik dwójkowy.
Jeśli Digisparka zasilasz z portu USB swojego komputera, to w trakcie pracy program ten może oddziaływać na port USB. Wszystko przez oszczędności – porty P3 i P4 są podłączone do linii danych USB i zmiany ich stanu mogą powodować interferencje. Przy zasilaniu z innego źródła wszystko jest w porządku.
// Efekt pseudolosowy 1 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja char p = 0; // Pętla główna //------------- void loop() { // Zapalamy na 0,05 sekundy diodę na pozycji p digitalWrite(p,HIGH); delay(50); digitalWrite(p,LOW); delay(150); // Czekamy 0,15 sekundy // wyliczamy nową pozycję p p = (p + 7) % 5; } |
Liczba losowa to taka, której wartości nie jesteś w stanie określić, zanim ją nie otrzymasz. Np. liczbami losowymi są wyniki lotto, liczba oczek wyrzuconych kostką do gry, itp.
Prawdziwe liczby losowe trudno jest uzyskać za pomocą obliczeń, ponieważ obliczenia zawsze dają określony wynik, a liczba losowa nie może przecież być określona, bo wtedy przestanie być losowa. Dlatego często zadowalamy się liczbami pseudolosowymi. Łacińskie słówko pseudo znaczy jakby. Czyli liczba pseudolosowa to jakby liczba losowa, taki oszust. No, ale czasem nam to wystarcza.
Liczby pseudolosowe można tworzyć na różne sposoby. Na przykład za pomocą wyrażenia mieszającego (ang. hashing expression). Wyrażenie takie daje wartości z pewnego przedziału liczb, lecz nie kolejne, tylko w pewien sposób pomieszane. Najczęściej nowe wartości tworzy się z wartości poprzednich. W naszym programie wykorzystałem takie proste wyrażenie mieszające ((p + 7) % 5), które wykorzystuje dodawanie i operację reszty z dzielenia (%). Ponieważ wartości jest niewiele, prześledźmy ich uzyskiwanie. Na początku p ma wartość 0. To tzw. ziarno pseudolosowe (ang. random seed). W przyrodzie z ziarna wyrasta roślina. Tutaj z tego ziarna będą powstawały kolejne wartości pseudolosowe. Operacja reszty z dzielenia przez 5 redukuje wyniki dodawania do przedziału od 0 do 4 (dlaczego?). Liczby pseudolosowe będą wyliczane w kolejnych wywołaniach funkcji loop(). Zapiszmy to w tabelce:
Obieg | p przed | obliczenia | p po |
1 | 0 | (0 + 7) % 5 = 2 | 2 |
2 | 2 | (2 + 7) % 5 = 4 | 4 |
3 | 4 | (4 + 7) % 5 = 1 | 1 |
4 | 1 | (1 + 7) % 5 = 3 | 3 |
5 | 3 | (3 + 7) % 5 = 0 | 0 |
6 | 0 | (0 + 7) % 5 = 2 | 2 |
... | ... | ... | ... |
W pierwszej kolumnie jest numer wywołania funkcji loop(), czyli numer kolejnego obiegu pętli.
W drugiej kolumnie jest zawartość zmiennej p przed wykonaniem obliczeń.
W trzeciej kolumnie są obliczenia z tą wartością p. Jest to wyrażenie mieszające.
W czwartej kolumnie mamy wynik wyrażenia mieszającego, który trafia do p, a ta wartość posłuży w następnym obiegu do wygenerowania kolejnej liczby pseudolosowej.
Wyniki wyrażenia mieszającego dają ciąg liczb: 2 4 1 3 0. Ciąg ten się powtarza, ponieważ w 5-tym obiegu otrzymujemy wartość 0, czyli taką samą jaka była na początku. To powtarzanie wartości jest cechą charakterystyczną liczb pseudolosowych. Nazywamy to cyklem. Im jest on dłuższy, tym bardziej wyniki przypominają ciąg prawdziwych liczb losowych.
Biblioteka Arduino zawiera funkcję random(), która generuje kolejne liczby pseudolosowe:
random(n) – liczba pseudolosowa w zakresie od 0 do n - 1.
random(m,n) – liczba pseudolosowa w zakresie od m do n - 1.
Funkcja ta wykorzystuje wewnętrzny generator pseudolosowy o dużym okresie powtarzania, dzięki czemu otrzymane wartości bardzo przypominają rzeczywiste liczby losowe.
Kolejny program wykorzystuje tę funkcję do generowania pozycji zapalonej diody LED. Tym razem diody zapalają się losowo, bez widocznego wzoru.
// Efekt pseudolosowy 2 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja char p = 0; // Pętla główna //------------- void loop() { // Zapalamy na 0,05 sekundy diodę na pozycji p digitalWrite(p,HIGH); delay(50); digitalWrite(p,LOW); delay(150); // Czekamy 0,15 sekundy // wyliczamy nową pozycję p p = random(5); } |
Mankamentem jest wzrost rozmiaru programu, ponieważ musi on teraz zawierać generator pseudolosowy.
Pobawimy się teraz funkcją random(), skoro ją już mamy.
Ten program również zapala diody LED na wylosowanych pozycjach. Jednak nie gasi ich od razu, tylko po określonym czasie (również losowanym). Będzie nam potrzebna tablica P[], w której będą przechowywane informacje o tym, które diody LED świecą oraz ile jeszcze im zostało czasu.
// Efekt pseudolosowy 3 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Tablica czasu świecenia diod LED char T[] = {0,0,0,0,0}; // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pętla główna //------------- void loop() { // Przeglądamy tablicę T. // Jeśli T[i] jest większe od 0, zapalamy diodę i-tą, // po czym P[i] zmniejszamy. // Jeśli T[i] jest równe 0, gasimy diodę i-tą. for(char i = 0; i < 5; i++) if(T[i]) { digitalWrite(i,HIGH); T[i]--; } else digitalWrite(i,LOW); // Losujemy liczbę pseudolosową od 0 do 4. // Jeśli otrzymamy 0, to wpisujemy na losowym // miejscu w tablicy losowy czas od 1 do 9 if(!random(5)) T[random(5)] = random(1,10); delay(25); } |
Program działa następująco:
W funkcji setup() przeglądamy w pętli for kolejne komórki tablicy T[ ].
Jeśli przeglądana komórka zawiera wartość różną od zera, to znaczy, że skojarzona z nią dioda LED świeci i jej czas świecenia jeszcze nie upłynął. Przesyłamy zatem na port diody stan wysoki i zmniejszamy o 1 zawartość komórki tablicy. W ten sposób odmierzamy jej czas świecenia.
Jeśli przeglądana komórka ma wartość 0, to dioda powinna być zgaszona, bo jej czas świecenia upłynął. Przesyłamy więc na port diody stan niski.
Gdy przeglądniemy całą tablicę losujemy liczbę z zakresu od 0 do 4. Jeśli wypadnie 0, to losujemy pozycję diody oraz jej nowy czas świecenia i zapisujemy to w tablicy T[ ]. Spowoduje to w następnym wywołaniu funkcji setup() zapalenie tej diody i odmierzanie jej czasu.
Ten program jest tylko modyfikacją poprzedniego. Wykorzystuje on ideę regulowania jasności świecenia diody LED przez zmianę wypełnienia impulsów PWM (ang. Pulse Width Modulation – Modulacja Szerokości Impulsu), które ją włączają i wyłączają. Podobny efekt otrzymaliśmy w programie Migacz 4 z poprzedniego rozdziału. Tutaj tablica T[ ] przechowuje aktualne wypełnienia impulsów sterujących diodami, które tworzone są w pętli for. Przeanalizuj ten program, ponieważ z takich technik sterowania będziemy jeszcze korzystać w dalszej części kursu.
// Efekt pseudolosowy 4 // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Tablica wypełnień impulsów sterujących diodami LED char PWM[] = {0,0,0,0,0}; // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pętla główna //------------- void loop() { // Pętla generująca impuls for(char j = 0; j < 64; j++) { // Pętla przeglądająca tablicę PWM[ ] for(char i = 0; i < 5; i++) if(j < PWM[i]) digitalWrite(i,HIGH); else digitalWrite(i,LOW); delayMicroseconds(100); } // Pętla zmniejszająca szerokości impulsów w PWM[ ] for(char i = 0; i < 5; i++) if(PWM[i]) PWM[i]--; // Losujemy diodę do zaświecenia if(!random(30)) PWM[random(5)] = 80; } |
W programie użyliśmy nowej funkcji:
delayMicroseconds(n) – czeka przez n mikrosekund.
1 sekunda = 1000 milisekund = 1000000 mikrosekund.
Opóźnienie wnoszone przez tę funkcję nie jest specjalnie dokładne, ale ma większą rozdzielczość czasową od funkcji delay(), która operuje na milisekundach. Dzięki temu nasze PWM działa płynniej.
W latach 80 popularny był serial TV pt. "Battlestar Galactica". W sieci znajdziesz opis oraz nawet odcinki. W serialu rasa ludzka walczy z bezwzględnymi robotami. Cechą charakterystyczną tych robotów było przesuwające się tam i z powrotem czerwone światło w miejscu oczu:
Nasz program, wykorzystując PWM próbuje odtworzyć ten efekt. Sprawdź, czy będziesz w stanie sam wyjaśnić zasadę jego działania:
// Battlestar Galactica Cylon // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Tablica wypełnień impulsów sterujących diodami LED char PWM[] = {0,0,0,0,0,0,0,0,0}; // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pozycja światła char p = 0; // Kierunek ruchu char d = 1; // Pętla główna //------------- void loop() { // Pętla generująca impulsy PWM for(char k = 0; k < 5; k++) for(char j = 0; j < 16; j++) { // Pętla przeglądająca tablicę PWM[ ] for(char i = 2; i < 9; i++) if(j < PWM[i]) digitalWrite(i-2,HIGH); else digitalWrite(i-2,LOW); delay(1); } // Pętla zmniejszająca szerokości impulsów w PWM[ ] for(char i = 0; i < 7; i++) PWM[i] >>= 1; // Zapalamy diodę na pozycji p w PWM[ ] PWM[p] = 24; // Modyfikujemy pozycję p += d; if((p == 0) || (p == 8)) d = - d; } |
Podpowiedzi: tablica PWM[ ] zawiera więcej pozycji niż jest wyświetlane na diodach LED. Dzięki temu punkt świetlny może się przesuwać o 2 pozycje dalej w prawo i w lewo. Operacja >>= odpowiada podzieleniu przez 2, dzięki temu diody szybciej przygasają.
Ostatni program wykorzystuje ideę ostatniego programu do tworzenia efektu strzałów laserowych:
// Laser // (C)2018 mgr Jerzy Wałaszek //--------------------------- // Tablice wypełnień impulsów sterujących diodami LED char PWM[] = {0,0,0,0,0}; char P[] = {0,0,0,0,0}; // Konfigurujemy mikrokontroler //----------------------------- void setup() { // Ustawiamy porty P jako wyjścia i gasimy diody for(char i = 0; i <= 4; i++) { pinMode(i,OUTPUT); digitalWrite(i,LOW); } } // Pętla główna //------------- void loop() { // Pętla generująca impulsy PWM for(char k = 0; k < 8; k++) for(char j = 0; j < 32; j++) { // Pętla przeglądająca tablicę PWM[ ] for(char i = 0; i < 5; i++) if(j < PWM[i]) digitalWrite(i,HIGH); else digitalWrite(i,LOW); delayMicroseconds(150); } // Pętla zmniejszająca szerokości impulsów w PWM[ ] for(char i = 0; i < 5; i++) PWM[i] >>= 2; // Kopiujemy P[] do PWM[] for(char i = 0; i < 5; i++) if(P[i]) PWM[i] = P[i]; // Przesuwamy P[] for(char i = 4; i > 0; i--) P[i] = P[i-1]; P[0] = 0; // Losujemy strzał if(!random(10)) P[0] = 32; } |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.