Łączenie rejestrów przesuwnych oraz sterowanie serwomechanizmami.

Na ostatnim spotkaniu przed przerwą świąteczną rozwinęliśmy projekt rozpoczęty na poprzednich zajęciach tj. wyświetlanie cyfr przy pomocy wyświetlacza siedmiosegmentowego oraz rejestru przesuwnego. Główną korzyścią tej metody jest użycie jedynie trzech pinów cyfrowych Arduino. Okazuje się, że dołączenie drugiego wyświetlacza siedmiosegmentowego wymaga podłączenia drugiego rejestru przesuwnego, ale liczba pinów cyfrowych pozostaje taka sama !

Logika stosowania wielu rejestrów przesuwnych może być porównana do tworzenia jednego rejestru, który przedłużamy kolejnymi chipami 74HC595.

Podłączenie drugiego układu scalonego 74HC595 ogranicza się do podłączenia pinów RCLK oraz SRCLK do tych samych pinów arduino co odpowiadające RCLK oraz SRCLK piny pierwszego rejestru. Będzie to oznaczało, że nowy chip będzie on otrzymywał te same sygnały sterujące co pierwszy. Różnicą jest podłączenie pinu SER. Zamiast bezpośrednio do Arduino, podłączamy ten pin do wyjścia 9 naszego pierwszego rejestru przesuwnego. Dzięki temu całość będzie funkcjonowała jak jeden, przedłużony rejestr przesuwny.

 

Nasuwa się pytanie: jak zmodyfikować ostatnio napisany przez nas program tak, by poprawnie działał?
Odpowiedź jest prosta. Przygotowaliśmy się na to już na poprzednim spotkaniu i wystarczy wprowadzić nową wartość dla zmiennej ileScalakow (widocznej od razu w pierwszej linijce kodu), tak by równała się liczbie zastosowanych rejestrów, czyli 2:

Cały kod pozwalający sprawdzić działanie wyświetlaczy

#define ileScalakow 2
#define ilePinow ileScalakow * 8
int SER=8; 
int RCLK=9;
int SRCLK=10; 
int rejestr[ilePinow];

void setup(){
  pinMode(SER, OUTPUT);
  pinMode(RCLK, OUTPUT);
  pinMode(SRCLK, OUTPUT);
  czyscRejestr();
  zapiszRejestr();
}
void czyscRejestr(){
  for(int i=0; i<ilePinow; i++)
    rejestr[i]=LOW;
}
void zapiszRejestr(){
  digitalWrite(RCLK, LOW); 
  for(int i=ilePinow-1; i>=0; i--){
    digitalWrite(SRCLK, LOW);
    digitalWrite(SER, rejestr[i]);
    digitalWrite(SRCLK, HIGH); 
  }
  digitalWrite(RCLK, HIGH); 
}
void ustawPin(int ktory, int wartosc){
  rejestr[ktory]=wartosc;
}

void loop(){
  int i;
  for(i=0;i<ilePinow;i++)
    ustawPin(i, LOW);
    zapiszRejestr();
    delay(500);
   for(i=0;i<ilePinow;i++){
    ustawPin(i, HIGH);
    zapiszRejestr();
    delay(500);
}
}

Po tej prostej zmianie kod działa poprawnie. Jest to przykład jak odpowiednie wprowadzanie zmiennych pozwala na skalowalność i rozwijanie kreowanych programów.

Sterowanie serwomechanizmami

Serwomechanizm, lub potocznie serwo, to nic innego jak silniczek prądu stałego sterowany PWM (modulator szerokości impulsu) z podłączonym odpowiednim układem zębatek. To niewielkie i niedrogie urządzenie pozwala wygenerować niespodziewanie dużą siłę i moment obrotowy, szczególnie zważywszy na jego rozmiary i nieduży pobór mocy (przy mini serwach np. używanych przez nas SG92r produkcji TowerPro rzędu kilku watów).

Przed rozpoczęciem jakichkolwiek doświadczeń z serwami warto pamiętać właśnie o poborze mocy. Rekomendowane jest zasilanie ich z zewnętrznego źródła mocy np. baterii lub zasilacza prądu stałego. Przyczyną jest niebezpieczeństwo, jakie niesie za sobą skok natężenia prądu podczas ruchu serw – podłączenie kilku na raz do Arduino może niechybnie doprowadzić do spalenia sterownika.

Znalezione obrazy dla zapytania arduino servo batteries

Tworzenie programu sterującego serwami

Arduino posiada dedykowaną bibliotekę do sterowania serwami, co niezwykle ułatwia sterowanie i zarządzanie.

#include <Servo.h>

Nasz program pozwala na wpisywanie wartości do monitora szeregowego i dyktowanie ich serwomechanizmom.

Servo serwomechanizm;
int pozycja = 90;

void setup() 
{ 
 serwomechanizm.attach(9);
 Serial.begin(9600);
}

„Servo” jest formą deklaracji, że dany obiekt o nazwie „Serwmechanizm” ma być traktowany przez interpreter kodu jako serwomechanizm. Deklarujemy je w tej samej konwencji, co np. zmienne typu integer. Traktowanie go jak serwomechanizm oznacza, że możemy przypisać mu jakiś pin oraz pozycję (będą to odpowiednie zmienne w obiekcie Servo).

W naszym kodzie w etapie setup() przypisaliśmy pozycję wyjściową 90 stopni (2. linijka), pin 9 (7. linijka) oraz uruchomiliśmy monitor szeregowy dla częstotliwości 9600 baud. Następnie w loop() przy pomocy metody „parseInt” każemy „nasłuchiwać” wartości, które zostaną przypisane zmiennej „pozycja”.

void loop() 
{ 
 if (Serial.available() > 0) {
pozycja = Serial.parseInt();
 }
 if (pozycja > 0 && pozycja < 180) { 
 serwomechanizm.write(pozycja);
 } else { 
 pozycja = pozycja%160+10;
 serwomechanizm.write(pozycja);
 } 
 
}

Tym sposobem możemy sterować serwomechanizmem z klawiatury przypisując mu kąt od 0 do 180 stopni. Warto jednak pamiętać, by unikać skrajnych pozycji takich jak 0 i 180 stopni, gdyż w niektórych przypadkach może to doprowadzić do uszkodzenia serwomechanizmu.

Adnotacja: W bloku „else” umieściłem zabezpieczenie, które nie pozwoli również na przypisanie wartości większych niż 180. Była to nadprogramowa metoda, która pozwala ograniczyć przypisywane na serwo wartości tak, by zamykały się pomiędzy bezpiecznymi kątami 10 – 170 stopni. Sposoby zabezpieczania zależą od nas, jednak nie warto ich zaniedbywać. Szkoda naszego sprzętu i nerwów!

 

Pomiar prądu płynącego przez serwo.

Na bardziej ciekawych czekała również możliwość pomiaru prądu płynącego przez serwo. Warto pamiętać, że pomiar natężenia prądu należy przeprowadzać szeregowo względem badanego układu. Oznacza to, że multimetr w trybie pomiaru prądu musi być „po drodze” prądu między źródłem zasilania, a serwem. Dlatego zrobiliśmy to w następujący sposób:

Źródło napięcia (bez obaw, 5V i prąd zdecydowanie poniżej 1 A można swobodnie „izolować” suchymi dłońmi) dołączyliśmy do czerwonej sondy multimetru, a czarną do wejścia 5V serwa – jest to właśnie połączenie szeregowe.

Przy okazji mogliśmy zbadać wartość prądu pobieraną przez pracujące serwo. Wartości te na ogół wynosiły kilkadziesiąt mA, ale czasami (w skarajnym położeniu a także podczas zmiany położenia) wynosiły 100-200 mA. Oznacza to, że podłączenie kilku serw do Arduino jako źródła napięcia (a nie tylko do zadawania sygnału) skończyłoby się spaleniem płytki. Dlatego budując bardziej złożone układy z pewnością użyjemy zewnętrznych źródeł napięcia.

Do zobaczenia na kolejnym spotkaniu!
Maciej (c)

Sterowanie wyświetlaczem siedmiosegmentowym. Programowanie strukturalne.

Wyświetlacze siedmiosegmentowe (sevseg).

Na dzisiejszym spotkaniu poznaliśmy jedno z wielu praktycznych zastosowań LEDów. Siedmiosegmentowe wyświetlacze są układami diod emitujących światło w odpowiednim ustawieniu umożliwiającym wyświetlanie cyfr. Wyświetlacze te, nazywane sevseg (ang. seven sevseg

segment display) są wszechobecne w świecie elektroniki i służą reprezentowaniu danych w przystępny (i niedrogi) sposób. Tego typu wyświetlacz może zastąpić droższe ekrany (np. LCD) w przypadku gdy chcemy zobaczyć tylko wartości liczbowe.

Anoda czy katoda?

Najprostsze układy wyświetlaczy siedmiosegmentowych opierają się na wspólnej katodzie lub wspólnej anodzie, co oznacza (w przypadku wspólnej anody), że z aż 10 pinów dwa środkowe będą otrzymywały napięcie, a 8 pozostałych – uziemienia lub na odwrót (w przypadku wspólnej katody).

My pracowaliśmy na wyświetlaczu ze wspólną anodą. Każdy pin pozwala na przepływ prądu przez inny segment (LED) na obszarze wyświetlania. Segmenty mają swoje oznaczenia od a do g (oraz kropka dziesiętna DP – decimal point).
 

Sterowanie wyświetlaczem przy pomocy arduino

Po podłączeniu 8 pinów wyświetlacza do pinów cyfrowych Arduino oraz doprowadzeniu z Arduino zasilania (dodając po drodze opornik) przyszedł czas na programowanie.

Przed dowolnym programem należało przypisać odpowiednie piny w programie.

int start=6;
int end=13;
int i;

„Start” oznacza pierwszy z użytych pinów Arduino, „end” – ostatni. Zmienna „i” jest pomocniczym licznikiem do tablic.

Na próbę stworzyliśmy prosty program pozwalający zapalić wszystkie segmenty wyświetlacza, co pozwoliło przetestować prawidłowe podłączenie wyświetlacza.

void setup(){
  for(i=start;i<=end;i++){
      pinMode(i,OUTPUT);
      digitalWrite(i,1); //0=LOW, 1=HIGH
  }
}

void loop(){
  for(i=start;i<=end;i++)
      digitalWrite(i,1);
  delay(500);
  for(i=start;i<=end;i++)
      digitalWrite(i,0);
  delay(500);
}

W pętlach for widocznych w bloku funkcji void loop() możemy zobaczyć przypisanie stanów wysokich i niskich każdemu z pinów podłączonych do wyświetlacza. Ze względu na to, że jest to wyświetlacz o wspólnej anodzie, to stan niski powoduje zapalenie się lampek, gdyż następuje wtedy przepływ prądu.

Wyświetlanie cyfr

Następnie rozpoczęliśmy tworzenie programu wyświetlającego konkretne cyfry. Stworzyliśmy prosty program w którym wprowadziliśmy własne funkcje – wyświetlające jedynkę (funkcja void jedynka()) , dwójkę (void dwojka()) oraz „czyszczące” wyświetlacz (funkcja void nic()). Budowanie takich funkcji ma ogromny sens – będą to nasz „cegiełki”, które wielokrotnie można później używać – patrz główna funkcja loop() w Arduino. 

int i;
int ledA =8;
int ledB = 9;
int ledC = 11;
int ledD = 12;
int ledE = 13;
int ledF = 7;
int ledG = 6;
int ledDP = 10;
// Kolejność podpięcia diod a - g,DP to właśnie 8,9,11,12,13,7,6,10
void setup(){
  pinMode(ledA,OUTPUT);
  pinMode(ledB,OUTPUT);
  pinMode(ledC,OUTPUT);
  pinMode(ledD,OUTPUT);
  pinMode(ledE,OUTPUT);
  pinMode(ledF,OUTPUT);
  pinMode(ledG,OUTPUT);
  pinMode(ledDP,OUTPUT);
  digitalWrite(ledDP,1);
  nic();
}

void loop(){
  jedynka();
  delay (500);
  dwojka();
  delay (500);
  dwojka();
  delay (500);
  jedynka();
  delay (500);
  nic();
  delay (500);
}

void nic(){
  digitalWrite(ledA,1);
  digitalWrite(ledB,1);
  digitalWrite(ledC,1);
  digitalWrite(ledD,1);
  digitalWrite(ledE,1);
  digitalWrite(ledF,1);
  digitalWrite(ledG,1);
}//nic

void jedynka(){
  digitalWrite(ledA,1);
  digitalWrite(ledB,0);
  digitalWrite(ledC,0);
  digitalWrite(ledD,1);
  digitalWrite(ledE,1);
  digitalWrite(ledF,1);
  digitalWrite(ledG,1);
}//jedynka

void dwojka(){
  digitalWrite(ledA,0);
  digitalWrite(ledB,0);
  digitalWrite(ledC,1);
  digitalWrite(ledD,0);
  digitalWrite(ledE,0);
  digitalWrite(ledF,1);
  digitalWrite(ledG,0);
}//dwojka

W naszym kodzie, są również widoczne zmienne „ledA”, „ledB” itd (linijki 2 – 9), które były tworzone właśnie w nawiązaniu do nazewnictwa widocznym na rysunkach pokazującym budowę wyświetlacza (widocznym na samym początku wpisu). Warto zauważyć, że opłaca się używać nazw zmiennych mających jakieś konkretne znaczenie – tutaj wybraliśmy nazewnictwo zaczerpnięte z budowy sevseg-a. 

Łatwo zauważyć, że powyższy kod jest bardzo powtarzalny i wizualnie zajmuje sporo miejsca, przez co jest też mniej czytelny i nieprofesjonalny. W/w program jest przykładem programowania strukturalnego, czyli hierarchicznego podziału programu na odpowiednie procedury i bloki kodu. Widoczne są utworzone funkcje „nic()”, „jedynka()” i „dwojka()”. Analogicznie należy tworzyć kolejne funkcje generujące kolejne cyfry i wywoływać je w void loop().

Oczywiście na tym nie poprzestaliśmy.

Program z tablicami

Jak widać każda funkcja kolejno zapala odpowiednie segmenty wyświetlacza. Jest to robione „ręcznie”, tj za każdym razem musieliśmy przypisywać każdemu pinowi stan wysoki i niski. Znacznie bardziej optymalną metodą było stworzenie dwuwymiarowej tablicy. Każdy 8-elementowy wiersz tablicy odpowiadał przypisaniu odpowiedniego stanu logicznego danemu segmentowi (jest ich osiem, gdyż poza 7 segmentami jest również 'kropka dziesiętna’ – decimal point). Ten proces pozwolił nam porzucić metodę nazywania każdej diody po kolei (tj. „ledA”, „ledB”…). Na potrzeby tej metody należało dodać tablicę kontrolną z numerami pinów do których podłączone są piny wyświetlacza (linijka 2).

int i;
int led[8]={8,9,11,12,13,7,6,10};
int digits[5][8]={ //na razie definiuje tylko 5 cyfr
// A B C D E F G DP
  {0,0,0,0,0,0,1,0}, //zero
  {1,0,0,1,1,1,1,0}, //wyswietla 1
  {0,0,1,0,0,1,0,0}, //wyswietla 2
  {0,0,0,0,1,1,0,0}, //wyswietla 3
  {1,0,0,1,1,0,0,0}
  }; //...
   //macierz, 4 tablice, kazda z osmioma elementami

void loop(){
  jedynka();
  delay (500);
  dwojka();
  delay (500);
  trojka();
  delay (500);
  czworka();
  delay (500);
  nic();
  delay (500);
}

  void nic(){
    for(i=0;i<8;i++)
      digitalWrite(led[i],digits[0][i]);
  }//nic

  void jedynka(){
    for(i=0;i<8;i++)
      digitalWrite(led[i],digits[1][i]);
  }//jedynka
  
  void dwojka(){
    for(i=0;i<8;i++)
      digitalWrite(led[i],digits[2][i]);
  }//dwojka

Teraz pozostało jedynie przewidzieć który segment ma dostać stan wysoki albo niski, by całość reprezentowała daną liczbę. Kolejne liczby były reprezentowane przez kolejne wiersze w tablicy dwuwymiarowej.

By zrozumieć działanie tej tablicy należy przyjrzeć się działaniu funkcji wywołujących kolejne liczby. Dla przykładu: „jedynka()” zamiast odwoływać się bezpośrednio do każdego pinu przez zmienne ledA, ledB, ledC… (poprzednio), zastąpiliśmy to teraz pętlą „for” przypisującą kolejne stany logiczne (zera lub jedynki) z wiersza tablicy. Każda kolumna tej tablicy odpowiada kolejnej diodzie w wyświetlaczu, czyli w pierwsza kolumna to (poprzednio) ledA (0=świeci, 1=nie świeci), druga to (poprzednio) ledB (0=świeci, 1=nie swieci) itd.

Jeśli wywołujemy jedynkę, pętla „bierze” drugi wiersz w tablicy „digits[]” (uwaga: indeksowanie od zera, więc indeks wiersza=1) i przypisuje diodzie „A” stan wysoki (obszar A nie zaświeci się), diodzie „B” stan niski (dioda zaświeci się). Rysowanie cyfry dwa to branie wiersza numer trzy (indeks wiersza tablicy=2) itd.

Ta operacja zarówno skraca kod, jak i zwiększa jego czytelność i wygodę dodawania kolejnych wyświetlanych liczb lub znaków.

Program wciąż można było ulepszyć.

Ostateczna wersja programu

Optymalizacja kodu dotyczy wywoływania liczb – mamy wiele podobnych funkcji (jedynka(), dwojka() itd). Zamiast tworzyć kolejne funkcje stworzyliśmy jedną funkcję void cyfra(int n), które wywoływały z tablicy dwuwymiarowej kolejne wiersze, czyli kolejne cyfry.

int i;
int led[8]={8,9,11,12,13,7,6,10};
int digits[5][8]={
// A B C D E F G DP
  {0,0,0,0,0,0,1,0}, //zero
  {1,0,0,1,1,1,1,0}, //swieci 1
  {0,0,1,0,0,1,0,0}, //swieci 2
  {0,0,0,0,1,1,0,0}, //swieci 3
  {1,0,0,1,1,0,0,0}
  }; //...
   //macierz, 4 tablice, kazda z osmioma elementami

void setup(){
    for(i=0;i<8;i++)
      pinMode(led[i],OUTPUT);
}

void cyfra(int nr){
  for(i=0;i<8;i++)
    digitalWrite(led[i],digits[nr][i]);
}

void loop(){
  cyfra(1);
  delay (500);
  cyfra(2);
  delay (500);
  cyfra(3);
  delay (500);
  cyfra(4);
  delay (500);
 }

Dodawanie kolejnych liczb stało się niezwykle proste i przejrzyste. Program sprawia wrażenie bardziej profesjonalnego i czytelnego. Uproszczone jest także wyświetlanie. Wystarczy rozszerzyć tablicę o kolejny wiersz i dopisać odpowiednie stany logiczne kolejnym segmentom wyświetlacza.

Podsumowanie

W tym tygodniu poznaliśmy czym jest wyświetlacz 7-segmentowy i w jaki sposób funkcjonuje, odkryliśmy kolejne zastosowania tablic oraz że tablica może być również macierzą, tj. mieć więcej niż jeden wymiar. Ostatecznie zastosowaliśmy metody programowania strukturalnego, które jest obok programowania obiektowego, jedną z najszerzej stosowanych metod w świecie programowania.

Praca domowa

Wykorzystać powyższy kod (np. w wirtualnym arduino) i rozbudować o wczytywanie cyfr z klawiatury (komunikacja szeregowa), czym bawiliśmy się poprzednio.

Do zobaczenia w przyszłym tygodniu!
(c) Maciej 2017

 

Komunikacja z naszym Arduino – monitor szeregowy oraz funkcje.

Kolejne zajęcia Fibotu za nami!

Tym razem poruszyliśmy tematy, które pozwoliły nam poznać nieco bardziej techniki komunikacji użytkownik – komputer – kontroler.

Po utworzeniu znanego z początkowych zajęć układu równolegle podłączonych i adresowanych LEDów chcieliśmy móc nimi sterować ręcznie, a nie jedynie z pomocą gotowych algorytmów w pętli.

LEDy połączone płytką stykową

By spróbować czegoś zaawansowanego wróciliśmy do podstaw – każdy uczestnik stworzył znany już układ pięciu LEDów z czego każdy był podłączony do innego pinu cyfrowego w płytce Arduino. Ten układ pozwala niezależnie kontrolować każdą diodę.

#define MAX 5
int piny[5]={2,3,4,5,6}; // Tablica z numerami wyjść cyfrowych do których podłączone zostały diody

void setup(){
  for(i=0;i<MAX;i++) // pętla pozwalająca zdefiniować wyjście każdego z pinów cyfrowych
  pinMode(piny[i],OUTPUT);

}

Monitor szeregowy i komunikacja

Wzbogaciliśmy nasz program o funkcje pozwalające na komunikację przez port szeregowy, a następnie dodaliśmy możliwość wysyłania komend, które będą zapalać i gasić nasze diody.

Sterownik również na bieżąco informuje nas o tym, czy wczytał nasz input – wyświetlał wszystkie znaki które wprowadzimy do monitora szeregowego. By mieć możliwość wczytania więcej niż znaku (char – 1 bajt) zastosowaliśmy funkcję parseInt() pozwalającą na wczytanie ciągu znaków, który zostanie zamienony na liczbę całkowitą. Zmienna „ile” była wprowadzana przez użytkownika i definiowała ile razy lampki mają zamrugać.

#define MAX 5 // liczba diod
int piny[MAX]={2,3,4,5,6};
int i,j;
//char znak;
byte znak;

void setup(){
  for(i=0;i<MAX;i++)
     pinMode(piny[i],OUTPUT);
  Serial.begin(9600);
}
void loop(){
  if(Serial.available()>0){
    int ile=Serial.parseInt();
    Serial.print("Wczytalam ");
    Serial.println(ile);
    mig(ile);
  }

Warto zwrócić uwagę na linijkę trzynastą Serial.available() zwraca liczbę bajtów czekających na odczytanie (a aktualnie przechowywanych w buforze portu szeregowego), gdy zostanie wprowadzona przez użytkownika jakaś dana wejściowa. Czytając jeden bajt (np. Serial.read()) zabieramy z tego bufora jeden bajt a tym samym zmniejszamy licznik danych (bajtów) czekających na odczytanie.

Adnotacja: podczas zajęć modyfikowaliśmy nasz program na bieżąco. W kodzie możecie znaleźć 'przestarzałe’ metody które wprowadziliśmy w ramach zapoznania się z ideologią zadania. Najczęściej będą one zakomentowane w pełnym kodzie, który znajdziecie na samym dole tego wpisu. Warto zwrócić uwagę, że przy wczytywaniu zmiennej char będącej jednym znakiem musimy stosować tłumaczenie na tablicę ASCII, gdyż właśnie w tym formacie zapisane są zmienne char.

Funkcje: szkoda życia na robienie w kółko tego samego !

Stworzywszy program pozwalający na dwukierunkową komunikację z naszym sterownikiem utworzyliśmy funkcję o wdzięcznej nazwie „mig()”. Tworzenie takich funkcji jest podstawą programowania strukturalnego – chodzi o „zamykanie” logicznych części programu (tu: włączanie/wyłączanie wszystkich LEDów) w pewną całość, którą następnie będziemy wielokrotnie używać.

Funkcja „mig()” przechodziła kolejne stadia swojego rozwoju, od najprostrzej – bezargumentowej:

void mig(){
  Serial.println("ON");
  for(i=0;i<MAX;i++)
     digitalWrite(piny[i],HIGH);
   delay(400);
   Serial.println("OFF");
   for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
   delay(400);
}//mig

Zadaniem powyższej jest jednokrotne włączenie/wyłączenie wszystkich LED-ów. Aby zrobić to kilkukrotnie należy wielokrotnie wykonać stworzoną funkcję mig() – lub wykonać ją w pętli n-razy. Dlatego kolejna modyfikacja polegała na dodaniu argumentu do funkcji:

void mig(int ile){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(400);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(400);
  }//jj
}//mig

jednoargumentową, która umożliwia nam wielokrotne włączenie/wyłączenie LED-ów (dodatkow pętla po zmiennej jj). Mając takią funkcję możemy kazać migać, np. czterokrotnie przez wywołanie mig(4) – wówczas następuję przekazanie liczby 4 dla parametru ile w definicji funkcji mig(int ile). Kolejna modyfikacja polegała na dodaniu dodatkowego, drugiego parametru czas określającego ile ms mają być włączone/wyłączone LED-y.

void mig(int ile, int czas){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(czas);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

Ten dodatkowy parametr umożliwia nam szybkie miganie (np. czterorkotne) przez wywołanie mig(4, 100) lub wolne przez wywołanie mig(4,2000). Podobnie jak poprzednio wywołując naszą funkcję przypisujemy wartości 4 do zmiennej ile, oraz 100 (lub 2000 w drugim przykładzie) do zmiennej czas. Ostatnia modyfikacja to parametry domyślne w języku C++ (nie ma tego w „czystym” C), czyli zamiana prototypu funkcji (=nagłówka) na następujący: 

void mig(int ile, int czas=400){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(czas);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

Powyższa zmiana umożliwia wywołanie dwuargumentowej funkcji mig(int, int) nie wtylko w postaci mig(4,100) ale także mig(4) – wówczas parametr czas przyjmie domyślną wartość 400.   

Funkcja ta robi dokładnie to, o czym wspomniałem przy zmiennej „ile”. Wartość ukryta pod tą zmienną była kierowana do funkcji. Pętla zapalająca (zaznaczona linijka 2) zapala i gasi (linijki 5 i 10) lampki za pomocą znanej już nam pętli wewnętrznej (zapalającej każdą diodę jedną po drugiej w odstępie czasu rzędu milisekund – linijka 4).

Funkcja miała też dodatkową, opcjonalną zmienną wejściową „czas” regulującą odstępy między zapaleniem i zgaszeniem diod za pomocą wbudowanej funkcji „delay()”.

Podsumowanie:

Na tych zajęciach zamiast poznać nowe elementy elektroniczne jak np. znana z poprzednich zajęć czujka szczelinowa, poznaliśmy niezwykle kluczowe możliwości sterownika Arduino – komunikację dwukierunkową przez monitor szeregowy. Możliwość bezpośredniego wysyłania sterownikowi danych wejściowych pozwala na ręczne sterowanie i otwiera nas na nowe możliwości.

Stworzyliśmy swoją własną funkcję istniejącą poza pętlą główną, co zwiększa przejrzystość kodu i daje wygodę stosowania gotowych funkcji.

To nie koniec przygód z komunikacją za pomocą monitora szeregowego. Możliwości implementacji tak kluczowej metody są niezwykle szerokie.

Do zobaczenia na następnych zajęciach!
Maciej (c) 2017 & KG

 

 

Pełny kod:

#define MAX 5
int piny[5]={2,3,4,5,6};
int i,j;
//char znak;
byte znak;

void setup(){
  for(i=0;i<MAX;i++)
  pinMode(piny[i],OUTPUT);
  Serial.begin(9600);
}

void mig(int ile, int czas=400){
  for (int jj=0;jj<ile;jj++){
  Serial.println("ON");
  for(i=0;i<MAX;i++)
    digitalWrite(piny[i],HIGH);
    delay(czas);
   Serial.print("OFF x");
   Serial.println(jj+1);
   for(i=0;i<MAX;i++)
    digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

void loop(){
  if(Serial.available()>0){
    int ile=Serial.parseInt();
    //znak=Serial.read(); //przechowuje 1 bajt
    Serial.print("Wczytalam ");
    Serial.println(ile);
 //   mig(znak-48); //0 w tabeli ASCII to 48
   mig(ile);
 //   if (znak=='3')mig(3);
 //  if (znak=='5')mig(5,500);
    }