Digispark: słowo o debuggowaniu po UART

Do czego właściwie przydaje się komunikacja szeregowa między Digispark’iem a hostem (komputerem, do którego jest podłączony)? Jednym z podstawowych przypadków zastosowania takiej wymiany danych jest tzw. debuggowanie (brzydko brzmi ale polskie „odpluskwianie” jeszcze paskudniej:)). Oznacza to po prostu szukanie błędów w Waszym programie.

digisparkSerial_NoBug


Poniższe instrukcje ćwiczyłem na Digispark’u. Z powodzeniem możecie je jednak zastosować dla Arduino!

Gdy napiszecie kod źródłowy programu jest on kompilowany do postaci zrozumiałej dla AtTiny – z tekstowej do binarnej. Następnie jest łączony z bibliotekami (tzw. linkowanie). Wreszcie w postaci binarnej (plik hex) zapisuje się go do pamięci programu samego Digisparka.


Digispark, podobnie jak np. Arduino (i wiele innych) zawiera kilka rodzajów pamięci. Jednym z nich jest pamięć typu flash (podobna jak w pendrivach), gdzie zapisywane są stworzone przez Was programy. Digispark – a własciwie napędzający go AtTiny85 – ma jej 8kB (czyli 8192 bajty). Dla porównania – Arduino UNO (dokładniej kontroler AtMega328p) ma 32kB flasha. Oczywiście wielkość flasha ogranicza rozmiar programu, jaki możecie na niego wgrać. Pamiętajcie, że obok Waszego programu, mogą tam być jeszcze inne rzeczy – na przykład bootloader (w Digispark: micronucleus).

Pierwszym etapem weryfikacji poprawności Waszego programu jest kompilacja. Jeżeli kod nie jest zgodny z konwencją języka (np. źle ustawione nawiasy, średniki, składania instrukcji) albo używa nieznanych odwołań (np. błędy w nazwach komend czy zmiennych, niedołączona biblioteki) – kompilator (avr_gcc) przekaże błąd do środowiska Arduino IDE a to wyświetli Wam na czym polega problem i linię, w której wystąpił.

Poprawna kompilacja nie gwarantuje poprawnego wykonania. Wiele razy słyszałem: „przecież się zbudowało i wgrało, czemu nie działa?!”. No właśnie: to, że program się skompilował mówi nam tylko o tym, że jest napisany zgodnie z konwencją języka. Nic nie mówi to o tym, czy zrobi to, co powinien! Tester podzieliłby to na weryfikację i walidację (verify & validate – v&v). Weryfikacja to sprawdzenie, czy program działa prawidłowo (np. czy się nie zawiesza), walidacja – czy robi to, co powinien.

Komunikację po porcie szeregowym możecie użyć na obudwu etapach v&v. Do Waszego kodu wystarczy dodać instrukcje wysyłające na hosta informacje o stanie zmiennych, np:

  int p5A = analogRead(0);
  sserial.print("p5A:");
  sserial.println(p5A);

Debugowanie z softserial

O instalacji i podłączeniu do portu szeregowego UART dowiecie się ze wpisu: Digispark: port szeregowy UART.

Spójrzmy na taki kod (nie przejmując się co robi):

#include <SimpleServo.h>

SimpleServo s2;
SimpleServo s5;

#define DELAY_ME 20

void setup() {
  s2.attach(0);
  s5.attach(4);
  pinMode(1, OUTPUT);
}

void loop() {
  int p2A = analogRead(1);
  int p5A = analogRead(0);
  delay(DELAY_ME);
}

Teraz go skompilujemy i zobaczymy ile zajmuje miejsca; Arduino IDE (1.6.7 pod linuks) pokaże:

Sketch uses 878 bytes (14%) of program storage space. Maximum is 6,012 bytes.
Global variables use 46 bytes of dynamic memory.

Stąd widać, że program w obecnej formie zajął 878 bajtów.

Zauważcie: IDE podało, że maksymalna wielkość dostępnej dla programów pamięci to 6012 bajtów – mimo że „fizycznie” AtTiny ma flash o rozmiarze 8192 bajtów. Różnica to tzw. bootloader. Bootloader (w tej roli domyślnie micronucleus) to niewielki programik, dzieki któremu Wasze programy mogą być ładowane z Arduino IDE przez USB. Niewielki… właściwie to zajmuje prawie 1/4 pamięci dostępnej dla programów!

W następnym kroku dodajmy bibliotekę DigisparkSoftSerial i skompilujmy:

Sketch uses 1,022 bytes (16%) of program storage space. Maximum is 6,012 bytes.
Global variables use 62 bytes of dynamic memory.

Złapaliśmy dodatkowe 144 bajty. Teraz użyjemy SoftSerial w kodzie:

#include <SoftSerial.h>

#include <SimpleServo.h>

SimpleServo s2;
SimpleServo s5;

#define DELAY_ME 20

//Create SoftSerial instance
SoftSerial sserial(0,1);

void setup() {
  s2.attach(0);
  s5.attach(4);
  pinMode(1, OUTPUT);
  sserial.begin(9600);
}

void loop() {
  int p2A = analogRead(1);
  int p5A = analogRead(0);
  delay(DELAY_ME);
}

Efekt kompilacji:

Sketch uses 2,780 bytes (46%) of program storage space. Maximum is 6,012 bytes.
Global variables use 166 bytes of dynamic memory.

Zauważcie że, dopiero użycie funkcji biblioteki SoftSerial sprawiło, że nasz kod znacząco „urósł” – z 878 do prawie 2800 bajtów. Oczywiście wywołania kolejnych funkcji tej biblioteki (np. sserial.println()) też będą powodowały wzrost ilości kodu – ale nie już tak spektakularny.

Oszczędzamy zasoby

W przypadku takich urządzonek jak AtTiny, każdy bajt pamięci jest na wagę złota. Spójrzcie więc na taką konstrukcję:

#include <SoftSerial.h>

#include <SimpleServo.h>

SimpleServo s2;
SimpleServo s5;

#define DELAY_ME 20

#define DEBUG
#ifdef DEBUG
  //Create SoftSerial instance
  SoftSerial sserial(0,1);
#endif

void debugMe(char* nameV, int valueV){
#ifdef DEBUG
  sserial.print("[ ");
  sserial.print(millis());
  sserial.print(" ] DEBUG: ");
  sserial.print(nameV);
  sserial.print(":");
  sserial.println(valueV);
#endif
}

void setup() {
  s2.attach(0);
  s5.attach(4);
  pinMode(1, OUTPUT);
#ifdef DEBUG
  sserial.begin(9600);
#endif
}

void loop() {
  int p2A = analogRead(1);
  int p5A = analogRead(0);
  debugMe("p2A", p2A);
  debugMe("p5A", p5A);
  delay(DELAY_ME);
}

Rezultat kompilacji:

Sketch uses 3,528 bytes (58%) of program storage space. Maximum is 6,012 bytes.
Global variables use 190 bytes of dynamic memory.

Kod „urósł” ponieważ dodaliśmy do niego kilka linijek. W zamian uzyskaliśmy całkiem elegancki sposób na debuggowanie.

Gdy upewnimy się, że nasz kod działa tak jak powinien, wystarczy że:

#define DEBUG

Zamienicie na:

#undef DEBUG

Rezultat:

Sketch uses 1,022 bytes (16%) of program storage space. Maximum is 6,012 bytes.
Global variables use 62 bytes of dynamic memory.

W ten sposób możecie właczać tryb debugowania kiedy tylko potrzebujecie – a wyłączać w kodzie ostatecznym.

Źródła

  • Rysunek „The Bug”: Rudy

Dodaj komentarz