Programowanie ATtiny2313 dla opornych

Arduino jest fantastyczną platformą dla początkujących. Łatwa w użyciu, prosta w programowaniu. Jednak gdy już nabierzemy pewnej wprawy dostrzegamy projekty w których użycie Arduino wydaje się pewną przesadą (głównie chodzi o finanse, ale czasem również o miejsce). Co zrobić jeżeli potrzebujemy sterować tylko jednym, czasem dwoma wyjściami?

Wówczas możemy sięgnąć po mikrokontroler w rodzaju ATtiny2313. Nie uruchomimy na nim środowiska Arduino. Nie ma bootloadera, do programowania potrzebujemy jakiś programator. Ponadto nie ma biblioteki Arduino, tak więc nie ma wygodnych funkcji jak digitalWrite, analogRead itp. Trzeba się nieco ‘ubrudzić’ pisząc program, ale chyba już czas na to?

Sam doświadczenie poza Arduino posiadam niewielkie, ale właśnie mam okazję by zmierzyć się z małym projektem, w którym chcę użyć ATtiny2313. Ten wpis jest właśnie pokłosiem moich eksperymentów.

Pierwsza przeszkoda – programator. Ponieważ nie ma bootloadera, więc do wgrania naszego programu potrzebujemy programatora tzw ICSP (In Circuit  Serial Programming). W świecie układów ATMELa spotkać można programatory o dwóch rodzajach złącz 6-cio lub 10-cio pinowych. Są to gniazda 2 x 3 lub 2 x 5 w odstępach 2.54 mm, więc złącze programatora można zrobić np z goldpina. Ja już od dłuższego czasu korzystam z programatora USBtinyISP, którego trzeba przed pierwszym użyciem zlutować (jest też dostępny gotowy do pracy programator ICSP typu USBAP), który ma złącza zarówno 6 jak i 10 pinowe.

Złącze ICSP
Złącze ICSP

Opis wyprowadzeń programatorów. Uwaga! Jest to opis wyprowadzeń widzianych od góry – od strony kabla. Trzymając wtyczkę programatora i zaglądając w głąb wtyczki należy rysunki odbić w pionie.

USBtinyISP ma zworkę, która gdy założona podaje napięcie zasilające na Vcc (jest to 5V z USB). Można je wykorzystać do zasilania programowanego ATtiny2313, tak więc pozostaje podłączyć sygnały do wyprowadzeń ATtiny2313 korzystając z tej ściągi:

Jak podłączyć ICSP do ATtiny/ATmega
Jak podłączyć ICSP do ATtiny/ATmega

Cały nasz układ, gotowy do programowania wygląda tak:

ATtiny2313 gotowy do programowania
ATtiny2313 gotowy do programowania

Jak widać, korzystam z małej płytki stykowej (BB-170), w którą wsadzamy ATtiny2313 (wycięcie na obudowie jest słabo widoczne ale jest w górnej części zdjęcia). Wyjście z 6-cio pinowego złącza USBtinyISP jest rozprowadzone przewodami montażowymi (JW-75). I tak:

  • czarny – masa
  • czerwony – +5V
  • biały – SCK
  • zielony – MISO
  • żółty – MOSI
  • niebieski – RESET

Czemu nie potrzeba żadnych dodatkowych elementów? Większość mikrokontrolerów AVR ma wbudowany prosty oscylator, który zapewnia zegar niezbędny do pracy. Jest on słabej jakości – nieprecyzyjny, częstotliwość pływa z czasem i temperaturą otoczenia, ale jeżli nasz projekt nie wymaga specjalnie mierzenia czasu i dokładności w tej mierze – wewnętrzny oscylator jest good enough.

Częstotliwość tego wewnętrznego zegara to 8 MHz, jednak możliwe jest włączenie dzielnika przez 2, 8 lub 64 uzyskując odpowiednio 4 MHz, 1 MHz lub 128 kHz. Po co zmniejszać prędkość działania? Głównie dla oszczędności prądu – im niższe taktowanie tym mniej prądu pobiera układ. Jak zmieniać częstotliwość – o tym następnym razem (dla chcących szukać samemu – szukajcie informacji o fuse bits).

OK, teraz pozostaje – co tam wgrać? Zacznijmy od klasycznego blink czyli migania diodą. Ponieważ moim celem było przetestowanie procesu wgrywania – znalazłem gotowca. Zwłaszcza mam na myśli Makefile. Dzięki temu plikowi wystarczy napisać make w linii poleceń i projekt się skompiluje i wgra na ATtiny2313 (przynajmniej u mnie na Ubuntu – Makefile wydaje się być użytecznym pod Windowsem, ale nie mogę powiedzieć co dokładnie trzeba zainstalować aby działało).

Nasz cały program wygląda tak:

#include <avr/interrupt.h>	// Definicje nazw pinow układu

#define F_CPU 1000000UL		// Zapisanie predkosci procesora w Hz dla biblioteki delay.h
#include <util/delay.h>

// Deklaracja funkcji mrugajacej dioda
void blinkEm(uint8_t count);

// Glowna funkcja programu
int main()
{
  DDRD = _BV(PD4); // Ustawienie PORT D 4 (noga 8)jako wyjscie

  while (1) // Petla, ktora nigdy sie nie konczy
  {
    blinkEm(1); // Wywolanie funkcji mrugajacej dioda
  }
}

// Funkcja mrugajaca dioda LED ustalona ilosc razy
void blinkEm(uint8_t count)
{
  // petla ilosci mrugniec
  // dziala az zmienna count osiagnie wartosc 0
  while (count > 0)
  {
    // Wlaczenie diody led
    PORTD = _BV(PD4);
    // oczekiwanie 1 s
    _delay_ms(1000);

    // wylaczenie diody led
    PORTD = ~_BV(PD4);	
    _delay_ms(1000);

    // odliczanie ilosci mrugniec
    count--;
  }
}

Wystarczy podłączyć diodę z rezystorem do pinu 8, +5V oraz masę i działa:

To zdjęcie pokazuje, czemu naprawdę warto zainteresować się tego typu rozwiązaniami – nic poza ATtiny nie jest nam potrzebne, żadnych dodatkowych elementów niezbędnych do działania (tak długo jak wewnętrzny zegar jest dla nas wystarczająco dobry).

W stosunku do równoważnego szkicu w Arduino IDE widać następujące różnice:

  • nie ma setup i loop. Jest tylko main, które w C jest domyślnie uruchamianą pierwszą funkcją programu. loop jest zastąpione przez pętlę zrobioną za pomocą while (1)
  • definicja funkcji main wygląda tak jak by wyglądała w Arduino IDE. Natomiast blinkEm jest zdefiniowane tak jakby dwa razy. W linii 5 oraz 19. W linii 5 jest tak zwany nagłówek – określamy nazwę funkcji, jakie argumenty przyjmuje oraz jaką wartość zwraca. Sama funkcja wraz z kodem ją realizującym jest określona w linii 19 i następnych. Po co to? Bo bez nagłówka funkcji kompilator w linii 13 nie mógłby określić czy wywołanie funkcji jest poprawne. Dlatego w C jeżeli chcemy korzystać z funkcji  w programie w miejscu, do którego kompilator dociera zanim znalazł tą funkcję, musimy określić parametry niezbędne dla kompilatora w tym momencie – czyli właśnie jakie argumenty przyjmuje funkcja i jaką wartość zwraca. W Arduino IDE przed kompilacją naszego szkicu IDE dodaje nagłówki funkcji automatycznie.
  • Nie mamy do dyspozycji funkcji znanych z biblioteki Arduino – czyli właśnie analogWrite, digitalRead itp. Dlatego operacje na portach dokonuję się za pomocą magicznych akronimów jak _BV, PD4. O tym co dokładnie znaczą i jak z nich korzystać postaram się napisać następnym razem.

Kod jest udostępniony na githubie:

https://github.com/netmaniac/AVRtest

Ponieważ planuję na potrzeby następnych przykładów ten kod przerobić i rozbudować, to link do wersji dokładnie z tej której korzystałem w tym przykładzie jest taki:

https://github.com/netmaniac/AVRtest/tree/fb828f9edcccfc5945897b9e546b858af37022fd

LinkLove – na podstawie tych materiałów udało mi się ruszyć z programowaniem ATtiny, tam możecie szukać dalszych informacji, jeżeli nie macie cierpliwości aż powstaną następne przykłady :

http://www.evilmadscientist.com/article.php/avrtargetboards
http://www.instructables.com/id/Reading-Switches-with-ATtiny2313/step2/Blinkenlights-for-a-Signal/
http://www.engbedded.com/fusecalc/
http://www.ladyada.net/learn/avr/programming.html

Udział wzięli:

  • ATtiny2313. Ewentualnie 2313V, jego brat bliźniak, który może pracować już od napięcia zasilającego 1.8V (vs 2.7 w wersji podstawowej), kosztem maksymalnej częstotliwości pracy 10 MHz
  • USBtinyISP – sugeruję użycie gotowego programatora USBASP, który już jest w ofercie Nettigo
  • BB-170 mała płytka stykowa, niezastąpiona do takich niewielkich, prostych prototypów
  • JW-75 – uniwersalne kable do łączenia nie tylko płytek stykowych
  • Coś do migania, jeżeli jeszcze jakimś cudem nie masz w szufladzie baterii diod LED i rezystorów

Ten wpis jest początkiem nieco dłuższej serii (mam nadzieję), w której postaram się przybliżyć nieco to co można zrobić bez Arduino…