Serwis Edukacyjny
Nauczycieli
w I-LO w Tarnowie

Do strony głównej I LO w Tarnowie

Materiały dla uczniów liceum

  Wyjście       Spis treści       Poprzedni       Następny  

©2019 mgr Jerzy Wałaszek
I LO w Tarnowie

Autor artykułu: mgr Jerzy Wałaszek
Konsultacje: Wojciech Grodowski, mgr inż. Janusz Wałaszek

 

 

Warsztat elektronika

Kurs Arduino Digispark

Płytka ćwiczeniowa DS001

 

Płytka DS001

Do wykonania kilku ćwiczeń zaprojektujemy prostą płytkę (ang. shield) dla Digisparka. Jeśli nie chcesz tej płytki wykonywać, to możesz zamiast niej używać płytki stykowej ze zmontowanym na niej układem, ale jest to rozwiązanie mniej wygodne. Dla płytki stykowej goldpiny na płytce Digispark powinny być przylutowane tak:

Dla naszej płytki DS001 (i następnych) należy przylutować goldpiny żeńskie:

Goldpiny łatwo wylutujesz z płytki za pomocą elektrycznego odsysacza cyny:

Warto coś takiego mieć w swoim warsztacie, koszt około 25zł.

Schemat ideowy płytki DS001

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.

Wersja na płytce stykowej

  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 większym od 5,5V.
  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.

Wersja THT

 

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

Pliki do pobrania

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

Wersja SMT

 

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  

Pliki do pobrania

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

 

 

Programowanie płytki DS001

Płytka DS001 pozwoli wykonać kilka ćwiczeń w programowaniu. Prześledź przedstawione tutaj programy. Musisz dokładnie zrozumieć zasady ich działania.

Mrugacz 1

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.

Mrugacz 2

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.

Ruchomy punkt 1

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;  
}

Ruchomy punkt 2

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ę.

Ping pong

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;  
}

Wąż

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);
}

Licznik dwójkowy

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

// 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.

Efekt pseudolosowy 2

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.

Efekt pseudolosowy 3

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:

Tworzymy 5-cio elementową tablicę globalną T[ ] i inicjujemy jej komórki wartościami 0.

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.

Efekt pseudolosowy 4

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.

Cejlon

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ą.

Laser

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
©2019 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: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.