W pewnym momencie jeden z moich Digispark’ów przestał odpowiadać. Nie – nie tak jak jak zwykle, kiedy trzeba go było wkładać do portu USB kilka razy. Całkiem. udevadm monitor
milczał jak zaklęty, nie wykrywał włożenia ani wyciągnięcia urządzenia USB.
Co gorsza: Digispark zaczął się zachowywać tak jakby… zniknął mu bootloader. Zwyczajowe 5 sekund zwłoki przy starcie, skurczyło się do góra 1/2 sekundy…
Bootloader
Bootloader to niewielki programik znajdujący się w pamięci flash czipu (czyli tej, w której zapisuje się programy). Jego zadaniem jest między innymi sprawdzenie, czy przypadkiem „ktoś” (np. Arduino IDE) nie chce załadować nowego programu użytkownika. Jeżeli tak – bootloader odbiera nowy program i zapisuje go w odpowiednim miejscu w pamięci. Jeżeli nie – bootloader kontynuuje start urządzenia ładując program wcześniej zapisany w pamięci czipu.
Digispark używa bootloadera micronucleus.
Micronucleus na Digisparku, po włączeniu zasilania, czeka 5 sekund zanim uruchomi załadowany do flasha program użytkownika. Podczas tych 5 sekund zwłoki oczekuje na instrukcje co do załadowania nowego programu.
Kłopoty
Mojego Digisparka nie udało się już przeprogramować – system go po prostu nie widział. Pod linuksem możecie użyć polecenia:
$ udevadm monitor
Narzędzie raportuje każde włożenie i usunięcie urządzenia USB. U mnie włożenie (usunięcie) Digisparka z portu USB nie powodowało żadnych zdarzeń.
Kolejną oznaką kłopotów było to, że mój program uruchamiał się dosłownie po pół-sekundzie. Nie było standardowego 5s oczekiwania. Tak jakby bootloader… zniknął?
Zawsze staram się dodać jakąś oznakę, że mój program wystartował – migam diodą trzy razy (pin P1) albo dodaję jednoznaczne logi na serialu w funkcji setup(). W ten sposób wiem, że program prawidłowo się zainicjował. Co więcej – wiem, kiedy czip się „spontanicznie” zrestartował!
Wbrew pozorom, np. dla Arduino, bootloader znika (ulega uszkodzeniu lub nadpisaniu) stosunkowo rzadko… Dla Arduino IDE, żeby wypalić bootloader, trzeba użyć programatora ISP (podłączanego do osobnego złącza na płytce Arduino). Najczęstszą przyczyną braku komunikacji z kontrolerem są inne problemy na hoście (jednostce, do której kontroler jest podłączony). Może to być np. nieodpowiedni sterownik czy (jeszcze częściej) zły port wybrany w Arduino IDE.
Tutaj jednak objawy wydawały się jednoznacznie wskazywać na problemy z bootloaderem. Mój program generował sygnał PWM na pinie P1. Efektem ubocznym tego było zapalanie LED podłączonej do tego pinu. W ten sposób dokładnie wiedziałem, kiedy mój program startuje – a startował dużo za wcześnie.
Wymiana bootloadera…
…na oryginalnym Digispark jest znacznie utrudniona. Ciekawy tekst na ten temat: Beginners Guide to Extended Programming Attinys with Digispark. Stwierdza wprost:
The digispark cannot be programmed via ISP (at least not without prior modification)
Problemem jest pin resetu czipa – wyprowadzony na P5. W oryginalnym Digisparku (jak wyczytałem, bo oryginalnego nie posiadam:)) wyłączono możliwość zewnętrznego resetu czipu. W ten sposób udało się uzyskać dodatkowy pin do innych zastosowań. Przy okazji – uniemożliwiono też programowanie przez ISP (np. wymianę bootloadera).
Jak to zrobiono? Poprzez zmianę ustawienia tzw. high fuse, a konkretnie zaprogramowanie jego bitu RSTDISBL (7). Bit ten wyłącza reset na PB5 (podłączonym do wyprowadzenia P5 Digispark). W ten sposób sprowadzając go do stanu niskiego nie zrestartujemy kontrolera – co jest konieczne przy programowaniu przez ISP.
Na oryginalnych Digisparkach można zresetować to ustawienie za pomocą HVSP – High Voltage Serial Programming. Wymaga to impulsu 12v i trochę innego sposobu programowania kontrolera.
Nie porzucajcie nadziei!
Postanowiłem jednak spróbować. Budowanie HVSP mi się nie uśmiechało, ale najpierw postanowiłem sprawdzić, czy da się odczytać fusy. W tym celu użyłem programatora usbasp2:
Programatory tego typu podłączamy do portu USB komputera.
Dla linux będziecie musieli uzupełnić reguły w /etc/udev/rules.d. Stwórzcie tam plik np. 47-usbasp.rules:
1 2 |
ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", ENV{ID_MM_DEVICE_IGNORE}="1" SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", MODE:="0666" |
Teraz wystarczy odświeżyć reguły (z poziomu roota, po „su”):
1 |
udevadm control --reload-rules |
Z drugiej strony, programowany układ podłączamy do gniazda IDC6. Jak to zrobić dla Digispark’a? Ano według tego schematu:
Na programatorze ustawiłem zasilanie 5V (żółta zworka).
Na linuxie musicie dodać kilka narzędzi:
1 |
sudo apt-get install avrdude gcc-avr binutils-avr avr-libc |
Teraz wystarczyło użyć avrdude
:
$ sudo avrdude -c usbasp -v -p t85
avrdude: Version 6.0.1, compiled on Oct 21 2013 at 15:55:32
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2009 Joerg Wunsch
System wide configuration file is "/etc/avrdude.conf"
User configuration file is "/home/arek/.avrduderc"
User configuration file does not exist or is not a regular file, skipping
Using Port : usb
Using Programmer : usbasp
AVR Part : ATtiny85
...
Programmer Type : usbasp
Description : USBasp, http://www.fischl.de/usbasp/
avrdude: auto set sck period (because given equals null)
avrdude: AVR device initialized and ready to accept instructions
Reading
...
avrdude: Device signature = 0x1e930b
avrdude: safemode: lfuse reads as F1
avrdude: safemode: hfuse reads as DF
avrdude: safemode: efuse reads as FE
...
avrdude done. Thank you.
Super! Programator skomunikował się z czipem! Sygnatura czipu AVR się zgadza. Sprawdziłem jeszcze wysoki-fuse (hfuse). Programator odczytał go jako 0xDF. Użyjecie kalkulatora fusów., żeby zdekodować tą wartość na ustawienia poszczególnych bitów. W rezultacie, wartość ta oznacza, że zewnętrzny reset jest włączony!

Nowy bootloader
Teraz pobrałem nowy bootloader:
$ git clone https://github.com/micronucleus/micronucleus.git
...
$ cd ./micronucleus/firmware
Upewnijcie się, że Makefile jest ustawiony na AtTiny85:
$ nano Makefile
Początek pliku powinien wyglądać jak:
# Name: Makefile
# Project: Micronucleus
# Author: Jenna Fox; portions by Christian Starkjohann, Louis Beaudoin
# Creation Date: 2007-12-10
# Tabsize: 4
# Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
# License: GNU GPL v2 (see License.txt)
CONFIG ?= t85_default
#CONFIG ?= t85_aggressive
#CONFIG ?= t841_default
#CONFIG ?= t167_default
Widć stąd, że make jest ustawiony na AtTiny85 (CONFIG ?= t85 default
). Wyjdźcie z pliku, i skompilujcie:
$ make
W ten sposób powinniście uzyskać plik binarny main.hex
, który zawiera bootloader. Pozostaje go przenieść na Digisparka. Wpiszcie:
$ sudo make flash
avrdude -c USBasp -p attiny85 -U flash:w:main.hex:i -B 20
avrdude: set SCK frequency to 32000 Hz
avrdude: AVR device initialized and ready to accept instructions
Reading
avrdude: Device signature = 0x1e930b
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: set SCK frequency to 32000 Hz
avrdude: reading input file "main.hex"
avrdude: writing flash (8120 bytes):
Writing ...
avrdude: 8120 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex contains 8120 bytes
avrdude: reading on-chip flash data:
Reading ...
avrdude: verifying ...
avrdude: 8120 bytes of flash verified
avrdude: safemode: Fuses OK (H:FE, E:DF, L:F1)
avrdude done. Thank you.
Voila! Teraz już nie powinniście mieć problemów z wgraniem nowego kodu na Digisparka. Ja nie miałem.
Problemy
Może się zdarzyć, że po przeflashowaniu Digisparka, przy próbie programowania w Arduino IDE pojawi się komunikat:
1 2 3 |
arning: device with unknown new version of Micronucleus detected. This tool doesn't know how to upload to this new device. Updates may be available. Device reports version as: 2.3 |
Problem polega na tym, że ściągnęliście z git-a najnowszą wersję bootloadera, która może nie być zgodna z bibliotekami Arduino IDE.
W związku z tym (opisane: tutaj):
1 2 3 4 |
sudo apt-get install libusb-dev cd micronucleus/commandline make cp ./micronucleus ~/.arduino15/packages/digistump/tools/micronucleus/2.0a4 |
W ten sposób „podmienicie” micronucleus’a w narzędziach arduino.
Podsumowanie
Oczywiście miałem trochę szczęścia. Mój Digispark to klon, w którym producent postanowił nie blokować zewnętrznego resetu. Z tego co czytałem, w oryginalnym produkcie funkcja ta została zablokowana w fabryce (przez wypalenie odpowiednich fusów). Zresztą zrobiono to celowo. Dzięki temu, pin P5 (podłączony do PB5) może być wykorzystywany jako port ogólnego przeznaczenia. W moim klonie – zbytnie obniżenie na nim napięcia spowoduje zresetowanie czipu. Już się o tym przekonałem – w ramach doświadczeń z przetwornikiem analog/cyfra. Ale o tym będzie już w kolejnym poście.