9
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 DS002

 

Płytka DS002

Płytka DS001 powinna cię nauczyć sterowania portami mikrokontrolera skonfigurowanymi jako wyjścia (ang. OUTPUT). Taki sposób pracy mikrokontrolera pozwala mu sterować urządzeniami zewnętrznymi. Jednak często musi on również reagować na zdarzenia ze świata zewnętrznego, zatem musi mieć możliwość odczytu danych z portów. Do tego celu została zaprojektowana płytka ćwiczeniowa DS002.

Schemat ideowy płytki DS002

Na płytce do portu P0 podłączony jest przycisk, który w momencie naciśnięcia go przez użytkownika zwiera port P0 do masy, czyli wymusza niski stan logiczny 0.

Wersja na płytce stykowej

Wersja THT

 

Elementy Ilość Uwagi
Dioda LED 4 3mm czerwona
Opornik 470Ω 4 0,25W
Przycisk TACT 1  
Goldpiny męskie długie 1x3 1  
Goldpiny męskie 1x5 1  

Pliki do pobrania

ds002_tht.sch : schemat ideowy w Eagle
ds002_tht.brd : projekt płytki drukowanej w Eagle
ds002_tht_a.png : obrazek z widokiem elementów na płytce
ds002_tht_b.png : obrazek spodu płytki
ds002.svg : plik Inkscape do wydruku na drukarce laserowej

Wersja SMT – płytka jednostronna

 

Elementy Ilość Uwagi
Dioda LED 4 SMD czerwona, 0805
Opornik 470Ω 4 SMD 0805
Przycisk TACT 1 SMD
Goldpiny męskie długie 1x3 1  
Goldpiny męskie 1x5 1  

Pliki do pobrania

ds002_smd.sch : schemat ideowy w Eagle
ds002_smd.brd : projekt płytki drukowanej w Eagle
ds002_smd_a.png : obrazek z widokiem elementów na płytce
ds002_smd_t.png : obrazek góry płytki
ds002.svg : plik Inkscape do wydruku na drukarce laserowej

 

 

Programowanie DS002

W poprzednim rozdziale zajmowaliśmy się programowaniem wyjść portów P0...P4. W tym rozdziale zajmiemy się programowaniem wejść, a ściślej jednego wejścia P0.

Każdy port Digisparka może pracować jako wyjście lub wejście. Praca portu jako wejście umożliwia odczyt danych. Aby port działał jako wejście lub wyjście, należy go odpowiednio skonfigurować na początku programu. Najczęściej wykonuje się to w funkcji setup() przez wywołanie pinMode(nr_portu, tryb).

Płytka DS002 jest tak skonstruowana, iż do portu P0 podłączony jest przycisk, a do portów P1, P2, P3 i P4 podłączone są diody LED. Mikrokontroler Digisparka należy tak skonfigurować, aby port P0 pracował w trybie wejścia, czyli pozwalał odczytywać stan logiczny panujący na P0, a pozostałe porty pracowały w trybie wyjścia.

Podłącz płytkę DS002 do modułu Digispark.

Uruchom środowisko Arduino i utwórz szkic dla Digisparka.

Przekopiuj do edytora poniższy program, a następnie prześlij go do Digisparka (pamiętaj o procedurze programowania modułu Digispark, którą opisaliśmy w poprzednich rozdziałach).
// Odbiornik pola bioenergetycznego
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście
  pinMode(0,INPUT);
  // Ustawiamy porty P1...P4 jako wyjścia
  for(char i = 1; i <= 4; i++) pinMode(i,OUTPUT);  
}

// Pętla główna programu
//----------------------
void loop()
{
  char v;
  v = digitalRead(0); // Czytamy stan portu P0
  // Zapisujemy ten stan do portów P1...P4, które
  // sterują diodami LED.
  for(char i = 1; i <= 4; i++) digitalWrite(i,v);
}

Program ten działa dziwnie. Przeanalizujmy go:

W funkcji setup() ustawiamy tryb pracy wszystkich portów. Port P0 zostaje ustawiony w tryb wejścia (ang. INPUT), tzn. mikrokontroler będzie mógł odczytywać stan logiczny linii P0, do której podpięty jest przycisk. Przycisk normalnie jest rozwarty, zatem linia P0 wisi sobie i nie jest do niczego podłączona, jeśli nie naciskasz przycisku. Więcej o tym piszemy dalej.

Pozostałe porty P1...P4 zostają ustawione w znany nam z poprzedniego rozdziału tryb wyjścia (ang. OUTPUT).

W pętli głównej odczytujemy stan portu P0 za pomocą funkcji bibliotecznej digitalRead(0). Odczytany stan portu P0 (niski 0 lub wysoki 1) zapamiętujemy sobie w zmiennej v. Następnie stan ten zapisujemy do portów P1...P4. W efekcie na wyjściach P1...P4 pojawia się ten sam stan logiczny, jaki panuje na wejściu portu P0. I tutaj zaczynają się dziać dziwne rzeczy. Jeśli zbliżysz palec do płytki, to diody LED się zapalają, jeśli palec oddalisz, to gasną. Dlaczego tak jest? Współczesne mikrokontrolery budowane są na bazie tranzystorów polowych. Tranzystory polowe są sterowane napięciem i posiadają olbrzymie oporności wejściowe (idące w megaomy). Linia P0 zachowuje się tutaj jak antena i zbiera zakłócenia z otoczenia. Gdy zbliżasz palec, tworzysz takie zakłócenie i na linii P0 indukuje się napięcie, które trafia na wejście portu P0. A wtedy napięcie na wejściu P0 może wzrosnąć z uwagi na jego dużą oporność wejściową. Taki wzrost napięcia mikrokontroler odczyta jako stan logiczny 1 i diody LED zapalą się, ponieważ stan ten zostanie przekazany na wyjścia portów P1...P4. Gdy wciśniesz przycisk na płytce DS002, to zewrze on linię P0 do masy układu, a więc wymusi na niej stan niski 0. Diody LED zgasną. Gdy zwolnisz przycisk, diody LED znów się zapalą aż nie odsuniesz palca od płytki.

W rezultacie zbudowaliśmy czujnik zbliżeniowy palucha.

Specjalnie umieściłem tutaj ten program, ponieważ musisz takie zjawiska rozumieć, aby odpowiednio projektować swoje urządzenia elektroniczne. Zapamiętaj: linia wejścia nie powinna nigdy "wisieć" niepodłączona, ponieważ zbiera wtedy zakłócenia z otoczenia. Co zatem należy zrobić? Rozwiązanie jest bardzo proste i wbudowano je w każdy mikrokontroler.

Wystarczy linię wejścia podpiąć przez opornik do plusa zasilania:

Gdy przycisk jest rozwarty, to opornik wymusza na wejściu stan wysoki. Gdy przycisk zostanie zwarty przez naciśnięcie, to na wejściu pojawi się stan niski. Opornik powinien być tak dobrany, aby niwelował zakłócenia a jednocześnie nie obciążał zbytnio zasilania przy zwarciu go do masy poprzez przycisk. Oporność równa 10kΩ jest w sam raz. Opornik taki nazywamy opornikiem podciągającym (ang. pull-up resistor), ponieważ "podciąga" napięcie wejścia w górę, gdy przycisk jest rozwarty.

Ponieważ oporniki podciągające stosuje się bardzo często w praktyce, wszystkie mikrokontrolery pozwalają wewnętrznie dołączyć taki opornik do portu pracującego w trybie wejścia.

Użyte tu rozwiązanie pozwala w prosty sposób dołączać przyciski do portów wejścia mikrokontrolera.

Aby otrzymać wejście z opornikiem podciągającym, należy użyć wywołania pinMode(nr_portu, INPUT_PULLUP).

Poniższy program wykorzystuje ten tryb pracy wejścia. Naciśnięcie przycisku powoduje zwarcie wejścia do masy, czyli wymusza stan niski na wejściu portu P0. Ponieważ mikrokontroler kopiuje ten stan na pozostałe porty wyjścia P1...P4, wszystkie diody LED zgasną. Gdy zwolnisz przycisk, opornik podciągający wymusi stan wysoki na wejściu portu P0. Stan zostanie skopiowany na pozostałe porty i diody LED się zaświecą:

// Odczyt stanu przycisku 1
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia
  for(char i = 1; i <= 4; i++) pinMode(i,OUTPUT);  
}

// Pętla główna programu
//----------------------
void loop()
{
  char v;
  v = digitalRead(0); // Czytamy stan portu P0
  // Zapisujemy ten stan do portów P1...P4, które
  // sterują diodami LED.
  for(char i = 1; i <= 4; i++) digitalWrite(i,v);
}

Gdy uruchomisz ten program, wszystkie diody LED będą świecić. Zbliżanie palców do płytki nie powoduje żadnych zakłóceń. Zapamiętaj: odczyt przycisków należy wykonywać z opornikami podciągającymi.

Jeśli chcemy mieć zgaszone diody przy rozwartym przycisku i zaświecone przy zwartym, należy odwrócić odczytany stan przycisku na przeciwny. Uzyskasz to za pomocą operatora zaprzeczenia !.
// Odczyt stanu przycisku 2
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia
  for(char i = 1; i <= 4; i++) pinMode(i,OUTPUT);  
}

// Pętla główna programu
//----------------------
void loop()
{
  char v;
  v = !digitalRead(0); // Czytamy stan portu P0, zaprzeczając go
  // Zapisujemy ten stan do portów P1...P4, które
  // sterują diodami LED.
  for(char i = 1; i <= 4; i++) digitalWrite(i,v);
}

Mrugacz 3

Program mruga diodami LED. Jeśli przycisk jest zwolniony, to diody mrugają wolno. Gdy przycisk jest wciśnięty, diody mrugają szybko.
// Mrugacz 3
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  // Zmieniamy stan portów P1...P4 na przeciwny
  for(char i = 1; i <= 4; i++)
    digitalWrite(i,1^digitalRead(i));

  // Opóźnienie uzależniamy od stanu przycisku
  if(digitalRead(0)) delay(200); // Przycisk rozwarty
  else               delay(50);  // Przycisk zwarty
}

Możesz się zastanawiać, dlaczego w tym programie używamy funkcji digitalRead() do odczytu portów P1...P4, skoro porty te są skonfigurowane jako wyjścia. Funkcja ta czyta stan, jaki panuje na linii portu. Jeśli port pracuje jako wyjście, to stan ten ustala mikrokontroler przez zapis do portu za pomocą funkcji digitalWrite(). Zatem w tym przypadku funkcja digitalRead() zwraca nam to, co w poprzednim wywołaniu funkcji loop() zostało zapisane do portu. Odczytany stan portu negujemy poprzez operację 1^digitalRead(...). Operator ^ jest operatorem tzw. różnicy symetrycznej, czyli funkcji logicznej o następujących własnościach:

a b a ^ b
0 0 0
0 1 1
1 0 1
1 1 0

Zwróć uwagę, że jeśli jeden z argumentów operatora ^ ma wartość 1, to wynik jest zaprzeczeniem logicznym drugiego argumentu. To właśnie wykorzystujemy w tym programie do mrugania diodami LED. Program odczytuje stan portu, neguje go za pomocą operatora ^ i wynik zapisuje z powrotem do portu. W efekcie diody LED cyklicznie zapalają się i gasną. Częstotliwość tego mrugania zależy od opóźnienia, które wstawimy pomiędzy zmianami stanu portów. Opóźnienie to zależy od stanu przycisku, który badamy instrukcją warunkową if.

Zamiast operacji 1^... możesz użyć operatora negacji !, efekt będzie taki sam:

// Mrugacz 3
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  // Zmieniamy stan portów P1...P4 na przeciwny
  for(char i = 1; i <= 4; i++)
    digitalWrite(i,!digitalRead(i));

  // Opóźnienie uzależniamy od stanu przycisku
  if(digitalRead(0)) delay(200); // Przycisk rozwarty
  else               delay(50);  // Przycisk zwarty
}

Włączanie/wyłączanie

Kolejnym problemem jest wykorzystanie przycisku do włączania/wyłączania. Działa to w ten sposób, iż pierwsze naciśnięcie przycisku włącza, a drugie naciśnięcie wyłącza.

// Włączanie/wyłączanie 1
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  // Czekamy na naciśnięcie przycisku
  while(digitalRead(PRZYCISK));

  // Czekamy na zwolnienie przycisku
  while(!digitalRead(PRZYCISK));

  // Włączamy/wyłączamy świecenie diod
  for(char i = 1; i <= 4; i++) digitalWrite(i,!digitalRead(i));
}

Dwie pierwsze pętle while służą do obsługi przycisku. Obie odczytują stan portu przycisku, czyli P0. Pierwsza wykonuje się dotąd, aż użytkownik wciśnie przycisk, czyli stan portu zmieni się z 1 (przycisk niewciśnięty) na 0 (przycisk wciśnięty). Zadaniem drugiej petli jest odczekanie aż użytkownik przestanie naciskać przycisk, czyli stan portu zmieni się z 0 na 1. Gdy obie pętle zakończą się, wystąpi cykl: przyciśnięcie-zwolnienie przycisku. Wtedy przystępujemy do działania, czyli zmieniamy stan świecenia diod LED na przeciwny.

Gdy uruchomisz program, to po chwili zabawy płytką DS002 stwierdzisz, że coś jest nie tak. Czasem przycisk włącza/wyłącza diody, a czasem tego nie robi. Dlaczego?

Wyjaśnieniem jest zjawisko drgania styków (ang. bouncing). Przycisk jest urządzeniem mechanicznym. Wewnątrz zawiera parę styków, które w momencie naciśnięcia główki przycisku zwierają się ze sobą. Czasem styki odbijają się od siebie i wykonują serię krótkich drgań, zanim nastąpi trwałe złączenie/rozłączenie. Jest to zjawisko powszechnie znane. Wykres czasowy załączania/wyłączania przycisku wygląda następująco:

Wynika z niego, że tuż po włączeniu lub wyłączeniu przycisku jego styki mogą wykonywać przez około 5 ms drgania powodujące zwieranie i rozwieranie obwodu. Mikrokontrolery są bardzo szybkie. Gdy mikrokontroler wykryje spadek napięcia na linii portu wejściowego (zwarcie przyciskiem do masy), to wychodzi z pierwszej pętli while:

while(digitalRead(PRZYCISK));

i wchodzi w drugą pętlę while:

while(!digitalRead(PRZYCISK));

która wykonuje się dotąd, aż napięcie na porcie wróci do stanu wysokiego. Jeśli teraz styki drgają, to pętla odkryje wzrost napięcia i zakończy swoje działanie. Gdy to się stanie mikrokontroler zapali lub zgasi diody LED i wróci do pierwszej pętli while. Jeśli styki wciąż drgają, to pętla ta może się ponownie zakończyć i nastąpi przejście do drugiej pętli while. A ta napotkawszy wysokie napięcie na linii portu znów się zakończy i mikrokontroler ponownie zmieni stan diod LED. W efekcie jedno naciśnięcie przycisku może wywołać kilkakrotne, szybkie zaświecenie/zgaszenie  diod LED, a zatem czasem zostaną one zaświecone, a czasem zgaszone. Nie jest to poprawne działanie programu, ponieważ powinien on być wykonywany wg sekwencji:

naciśnięcie przycisku zwolnienie przycisku zapalenie/zgaszenie diod LED

Jak sobie z tym poradzić? Otóż problem wywołują niekontrolowane drgania styków przycisku. Najprostszym rozwiązaniem jest po prostu odczekanie tych 5 ms, aż drgania styków wygasną. 5 ms jest bardzo krótkim okresem czasu i użytkownik go nie zauważy.

Poprawiony program wygląda następująco:

// Włączanie/wyłączanie 2
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  // Czekamy na naciśnięcie przycisku
  while(digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Czekamy na zwolnienie przycisku
  while(!digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Włączamy/wyłączamy świecenie diod
  for(char i = 1; i <= 4; i++) digitalWrite(i,!digitalRead(i));
}

Teraz program działa prawidłowo, jednak aby włączyć lub wyłączyć diody LED, musisz nacisnąć przycisk i zwolnić go. Dopiero wtedy diody LED zmieniają swój stan. Jak uzyskać zmianę stanu diod w momencie naciśnięcia przycisku, co jest bardziej intuicyjne?

Odpowiedź jest prosta: wystarczy przenieś drugą pętlę while na koniec lub na początek funkcji loop():
// Włączanie/wyłączanie 3
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  // Czekamy na zwolnienie przycisku
  while(!digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Czekamy na naciśnięcie przycisku
  while(digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Włączamy/wyłączamy świecenie diod
  for(char i = 1; i <= 4; i++) digitalWrite(i,!digitalRead(i));
}

Dzięki temu wciśnięcie przycisku spowoduje przejście do zmiany stanu diod LED, a dopiero w następnej kolejności program poczeka na zwolnienie przycisku:

naciśnięcie przyciskuzapalenie/zgaszenie diod LEDzwolnienie przycisku

Jeśli druga pętla while znajdzie się na początku funkcji loop(), to działanie będzie identyczne, ponieważ funkcja loop() wykonywana jest cyklicznie w pętli.

Przesuwanie punktu świetlnego

Wykorzystajmy nabyte umiejętności do napisania programu, który przesuwa na diodach LED punkt świetlny po każdym naciśnięciu przycisku.
// Przesuwanie punktu świetlnego
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  // Diodę 1 zaświecamy
  digitalWrite(1,HIGH);
}

// Pętla główna programu
//----------------------
void loop()
{
  // Czekamy na zwolnienie przycisku
  while(!digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Czekamy na naciśnięcie przycisku
  while(digitalRead(PRZYCISK));

  // Czekamy na wygaśnięcie drgań styków
  delay(5);

  // Przesuwamy punkt o 1 pozycję
  char LED4 = digitalRead(4); // Zapamiętujemy stan LED 4
  for(char i = 4; i > 1 ; i--) digitalWrite(i,digitalRead(i - 1));
  digitalWrite(1,LED4);
}

Działanie programu jest następujące:

Ustawiamy porty:

P0 jako wejście z opornikiem podciągającym
P1...P4 jako wyjścia sterujące diodami LED

Diody LED 2..4 gasimy. Diodę LED 1 zaświecamy:

W pętli głównej czekamy na wciśnięcie przycisku. Gdy wykryjemy wciśnięcie, przesuwamy punkt świetlny następująco:

Zapamiętujemy stan diody LED 4 w zmiennej pomocniczej LED4 (na rysunku stany diod oznaczyłem różnymi kolorami, aby pokazać, jak będą się przemieszczać):

Idąc od końca, kopiujemy stan diody i-1 do i:

 

 

 

Na koniec kopiujemy zapamiętany stan LED4 do 1:

Zwróć uwagę, że stany diod przed i po operacji są przesunięte o 1 pozycję, a pierwsza dioda ma stan diody ostatniej:

PRZED:
PO:

Wykorzystajmy przesuwanie stanu diod w następnym programie, który po naciśnięciu przycisku generuje efekt strzału:
// Strzał 1
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Pętla główna programu
//----------------------
void loop()
{
  delay(20);
  digitalWrite(1,!digitalRead(PRZYCISK));
   
  // Przesuwamy punkt o 1 pozycję
  for(char i = 4; i > 1 ; i--) digitalWrite(i,digitalRead(i - 1));
}

W pętli głównej stany diod LED są cyklicznie przesuwane. Przed przesunięciem pierwsza dioda LED otrzymuje zaprzeczony stan przycisku. Jeśli przycisk jest zwolniony, to dioda będzie zgaszona. Jeśli przycisk jest wciśnięty, to dioda zostanie zaświecona. W następnych obiegach stan ten zostanie przeniesiony na kolejne diody LED.

Program ma pewną "wadę". Jeśli przytrzymasz przycisk, to wszystkie diody zostaną zaświecone. Zróbmy tak, aby przy naciśnięciu przycisku był oddawany "pojedynczy" strzał, a nie cała seria. W tym przypadku nie wystarczy kopiowanie stanu przycisku do portów wyjściowych. Należy zapamiętywać stan przycisku i reagować tylko w momencie, gdy się on zmienia. Do tego celu będziemy potrzebować zmiennej globalnej, która jest tworzona poza funkcjami i zachowuje swoją zawartość pomiędzy wywołaniami funkcji.
// Strzał 2
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Stan przycisku, HIGH = nienaciśnięty
char przycisk = HIGH;

// Pętla główna programu
//----------------------
void loop()
{
  // Czytamy stan przycisku
  char p = digitalRead(PRZYCISK);

  // Sprawdzamy, czy różni się od stanu poprzedniego
  if(p != przycisk)
  {
    // Różni się, sprawdzamy, czy jest wciśnięty,
    // a jeśli tak, to zapalamy diodę 1
    if(!p) digitalWrite(1,HIGH);

    // Zapamiętujemy nowy stan przycisku
    przycisk = p;
  }
  delay(25);
   
  // Przesuwamy punkt o 1 pozycję
  for(char i = 4; i > 1 ; i--) digitalWrite(i,digitalRead(i - 1));
   
  // Gasimy diodę 1
  digitalWrite(1,LOW);
}

Przesuwanie w prawo i w lewo

W kolejnym programie wykorzystamy metodę wykrywania naciśnięcia przycisku w locie. Program przesuwa cyklicznie punkt świetlny. Gdy naciśniesz przycisk, to nastąpi zmiana kierunku przesuwania punktu.
// Przesuwanie w prawo/lewo
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  // Zaświecamy diodę 1
  digitalWrite(1,HIGH);
}

// Stan przycisku, HIGH = nienaciśnięty
char przycisk = HIGH;

// Kierunek przesuwu
char kierunek = 1;

// Pętla główna programu
//----------------------
void loop()
{
  // Czytamy stan przycisku
  char p = digitalRead(PRZYCISK);

  // Sprawdzamy, czy różni się od stanu poprzedniego
  if(p != przycisk)
  {
    // Różni się, sprawdzamy, czy jest wciśnięty,
    // a jeśli tak, to zmieniamy kierunek przesuwu
    if(!p) kierunek ^= 1;
    przycisk = p; // Zapamiętujemy nowy stan przycisku
  }
  delay(75);
   
  // W zależności od kierunku przesuwamy odpowiednio punkt
  if(kierunek)
  {
    char LED4 = digitalRead(4);
    for(char i = 4; i > 1 ; i--)
    digitalWrite(i,digitalRead(i - 1));
    digitalWrite(1,LED4);
  }
  else
  {
    char LED1 = digitalRead(1);
    for(char i = 1; i < 4 ; i++)
    digitalWrite(i,digitalRead(i + 1));
    digitalWrite(4,LED1);
  }
}

Detonator

Ostatni program jest symulacją detonatora ładunku wybuchowego (spokojnie, nic tutaj nie wybucha).

Działanie będzie następujące. Przycisk uruchamia odliczanie. Co 2 sekundy będzie się zaświecać kolejna dioda LED. Gdy zaświecą się wszystkie cztery diody i minie kolejne 2 sekundy, nastąpi "eksplozja" i detonator powróci do stanu nieaktywnego. Jesli w trakcie odliczania naciśniesz ponownie przycisk, to odliczanie zostanie przerwane.

Sprawdzanie stanu przycisku jest tutaj wykonywane co 1/20 sekundy. Dzięki temu odliczanie jest przerywane prawie natychmiast i program nie gubi naciśnięć.
// Detonator
// (C)2018 mgr Jerzy Wałaszek
//---------------------------

// Port przycisku
#define PRZYCISK 0

// Konfigurujemy mikrokontroler
//-----------------------------
void setup()
{
  // Ustawiamy port P0 jako wejście z opornikiem podciągającym
  pinMode(0,INPUT_PULLUP);
  
  // Ustawiamy porty P1...P4 jako wyjścia i gasimy wszystkie diody
  for(char i = 1; i <= 4; i++)
  {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }
}

// Funkcja zapala diody od 1 do n, a gasi diody od n+1 do 4.
// Czeka 1/20 sekundy.
//----------------------------------------------------------
void zapalDiody(char n)
{
  for(char i = 1; i <= 4; i++) digitalWrite(i,(i <= n));
  delay(50);
}

// Stan przycisku
char przycisk = HIGH;

// Tryb pracy detonatora
char zliczanie = 0;

// Licznik detonatora
char licznik = 0;

// Licznik obiegów pętli
char obieg = 0;

// Pętla główna programu
//----------------------
void loop()
{
  // Czytamy stan przycisku
  char p = digitalRead(PRZYCISK);

  // Sprawdzamy, czy różni się od stanu poprzedniego
  if(p != przycisk)
  {
    // Różni się, sprawdzamy, czy jest wciśnięty,
    // a jeśli tak, to zmieniamy tryb pracy detonatora
    if(p == LOW)
    {
       zliczanie ^= 1;

       // Detonator włączony?
       if(zliczanie)
       {
         // Tak, inicjujemy  licznik i numer obiegu
         licznik = 1;
         obieg = 0;
       }
       else licznik = 0; // Nie, zerujemy licznik
    }

    // Zapamiętujemy nowy stan przycisku
    przycisk = p;
  }
      
  // Wyświetlamy aktualny stan licznika
  zapalDiody(licznik);

  // Jeśli zliczanie, to uaktualniamy licznik po upływie 2 sekund:
  if(zliczanie)
  {
    if(obieg < 40) obieg++; // 40 obiegów = 2 sekundy
    else
    {
      obieg = 0; // Po 40 obiegach zerujemy licznik obiegów
      licznik++; // Zwiększamy licznik detonatora
      if(licznik > 4) // Jeśli zliczył ponad 4
      {
        licznik = 0;  // Zerujemy licznik
        for(char i = 0; i < 8; i++) // Eksplozja
        {
          zapalDiody(0);
          zapalDiody(4);
        }
        zliczanie = 0; // Detonator w stan nieaktywny
      }
    }
  }
}

 

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.