Programování ATtiny85 pomocí Arduina Uno

Z nějakého důvodu mi kdysi přišlo jako opravdu cool nápad koupit si několik miniaturních čipů ATtiny85. Ne že bych věděl co s nimi, spíš pro sichr. Jak se říká, co je doma, to se počítá. Teď, možná po roce či dvou, jsem je vyhrabal z krabice. Stále jsem přesně netušil, co s nimi, ale v hlavě se mi pár nápadů rýsovalo.

Z mých posledních pokusů jsem si pamatoval, že standardní USBisp, které jsem si koupil na programování čipů ATmega328 nejde použít. Nejjednodušší náhradou se mi jevilo využít Arduino Uno. Na internetu jsem našel velké množství tutoriálů, navíc jde o celkem jednoduchou záležitost.

Prvně je třeba přidat podporu pro čipy rodiny ATtiny do Arduino IDE. Od posledně se situace radikálně zlepšila a není třeba ručně cokoliv stahovat, rozbalovat a kopírovat na správné místo.

V Arduino IDE otevřete Nastavení (File -> Preferences) a do kolonky Additional Boards Manager URLs vložte jednu z následujících adres:

  1. https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json
  2. http://drazzy.com/package_drazzy.com_index.json

První z nich přidává podporu jen pro ATtiny 44/45/84/85. Druhá je mnohem rozsáhlejší. Pro mé potřeby až zbytečně.

Postup na přidání podpory pro Attiny, jak je popsán výše
Přidání podpory pro Attiny

Nyní v Tools->Board->Boards Manager vybereme a nainstalujeme ATtiny.

Výše popsaný postup pro přidání podpory Attiny do Arduino IDE
Načtení podpory pro Attiny do Arduino IDE

Tím jsme naučili Arduino IDE hovořit jazykem ATtiny kmene. Už stačí jen udělat z Arduina programátor. Po připojení Arduina k počítači v IDE stačí vybrat vzorový příklad ArduinoISP (File->Examples->ArduinoISP->ArduinoISP) a nahrát ho.

Tím se z Arduina stal programátor, který propojíme s ATtiny a můžeme začít.

Počítáno z levé horní strany po řádcích: (1) Reset (8) VCC, (2) A3 (7) Pin D2, (3) Pin A2 (6) Pin D1, (4) GND (5) Pin D0
Popis pinů čipu Attiny85

Propojení je následující:

Zapojení Arduina UNO a Attiny85 pro programování
Zapojení Arduina UNO a Attiny85 pro programování. Vypůjčeno z http://highlowtech.org/?p=1706.

  • ATtiny pin 2 do Arduino pinu 13
  • ATtiny pin 1 do Arduino pinu 12
  • ATtiny pin 0 do Arduino pinu 11
  • ATtiny Reset pin do Arduino pinu 10

Teď už jen stačí otevřít Sketch, vybrat čip, který budete programovat a nastavit jako programátor "Arduino as ISP" a vše nahrát.

A ještě krátká poznámka na konec. Pokud stejně jako já, budete mít trable s nastavením exsterního oscilátoru, doporučuji před nahráním Sketche vypálit Bootloader (Tools->Burn Bootloader).

SDL2: Pořízení snímku obrazovky

SDL s sebou ve druhé verzi přinesl i změnu vykreslování na obrazovku. Místo v RAM uložených SDL_Surface je v SDL2 použita SDL_Texture uložené na video RAM, kde je plně v jurisdikci GPU. Krom citelných změn na rychlost vykreslování se také změnil způsob, jakým lze zachytit snímek obrazovky.

Při použití textur se SDL_Surface okna neaktualizuje, pokud bychom použili dřívější způsob, dostali bychom prázdný snímek.

Bohužel standardní funkce SDL_SaveBMP (ani její nedokumentovaná obdoba v rozšíření SDL_image - IMG_SavePNG) nepodporuje uložení SDL_Texture. Je proto nejprve nutná konverze z SDL_Texture na SDL_Surface. Teprve pak je možné snímek obrazovky uložit.

// Uloží snímek obrazovky do souboru
// file: jméno souboru pro uložení snímku obrazovky
// renderer: ukazatel na SDL_Renderer, který používáte pro vykreslení do okna aplikace
bool saveScreenshot( const std::string &file, SDL_Renderer *renderer ) {

    // Použité proměnné
    SDL_Rect _viewport;
    SDL_Surface *_surface = NULL;

    // Získáme velikost obrazovky
    SDL_RenderGetViewport( renderer, &_viewport);

    // Vytvoří SDL_Surface s hloubkou 32 a dle toho vybranou maskou pro barvy
    _surface = SDL_CreateRGBSurface( 0, _viewport.w, _viewport.h, 32, 0, 0, 0, 0 );

    // Pokud se vytvoření SDL_Surface nezdařilo
    if ( _surface == NULL ) {
        std::cout "Nelze vytvořit SDL_Surface. Důvod: " << SDL_GetError() << std::endl;
        return false;
    }

    // Získá data z SDL_Renderer a zapíše je do vytvořené SDL_Surface
    if ( SDL_RenderReadPixels( renderer, NULL, _surface->format->format, _surface->pixels, _surface->pitch ) != 0 ) {
         std::cout << "Nelze přečíst data z SDL_Renderer. Důvod: " << SDL_GetError() << std::endl;

        // Nutné vyprázdnit paměť
        SDL_FreeSurface(_surface);
        return false;
    }

    // Uloží snímek jako soubor PNG
    if ( IMG_SavePNG( _surface, file.c_str() ) != 0 ) {
        std::cout << "Snímek nebyl uložen. Důvod: " << SDL_GetError() << std::endl;

        // Vyprázdní SDL_Surface
        SDL_FreeSurface(_surface);
        return false;
    }

    // Vyprázdnění SDL_Surface
    SDL_FreeSurface(_surface);
    return true;

}
Použité funkce:

Jak v C++ zjistit, zda soubor existuje?

Každý jistě při programování "Úžasné megalomanské aplikace/hry, která mu zajistí milióny" narazil na problém. Jak zjistit, zda soubor, který chci načíst existuje? V případě, že chceme ze souboru jen přečíst data, jde o problém celkem triviální. Stačí se pokusit tento soubor otevřít. Cest je opět několik:
// Funkce bere za parametr jméno souboru
bool fileExists(const std::string &file_name) {
    // Pokusí se soubor otevřít
    ifstream file(file_name.c_str());

    // Pokud se otevření zdařilo
    if (file.good()) {
        // Uzavře soubor, uvolní paměť
        file.close();
        // a vrátí true
        return true;

    // Pokud se otevření nezdařilo
    } else {
        // Uzavře soubor, uvolní paměť
        file.close();
        // a vrátí false
        return false;
    }
} // bool fileExists()
Takový postup bude ve většině případů plně dostačující. Pro úplnost uvedu ještě příklad s využitím funkce access:
// Funkce bere za parametr jméno souboru
bool fileExists(const std::string &file_name) {
    // Pokud soubor existuje
    if ( access( file_namename.c_str(), F_OK ) != -1 ) {
        // Vrátí true
        return true;
    // V opačném případě
    } else {
        // Vrátí false
        return false;
    }
}
Co ale dělat v případě, že chceme o souboru zjistit více informací? Je soubor spustitelný? Jaká je jeho velikost? Jaké je ID vlastníka? V takovém případě se můžeme opřít o funkci stat
// Funkce bere jako parametr jméno souboru a ukazatel na strukturu do které má doplnit data
int stat(const char *filename, struct stat *buf);
Obsah struktury stat (EN):
  • st_dev - ID zařízení, na kterém je soubor uložen
  • st_ino - Číslo inode
  • st_mode - Obsahuje práva k souboru
  • st_nlink - Počet pevných odkazů
  • st_uid - ID vlastníka
  • st_gid - ID skupiny ve, které je vlastník
  • st_rdev
  • st_size - Celková velikost v bytech
  • st_atime - Čas posledního přístupu k souboru
  • st_mtime - Čas poslední změny
  • st_ctime - Poslední změna statusu (= změna čísla inode)
  • st_blksize - Velikost bloků
  • st_blocks - Počet alokovaných bloků
Pro nás bude nejdůležitější obsah st_mode. Pro testování jeho obsahu jsou zavedeny mimo jiné (EN) tyto masky:
  • S_IFREG - Jde o soubor
  • S_IFLNK - Jde o symbolický odkaz
  • S_IFDIR - Jde o adresář
  • S_IRUSR - Práva pro čtení vlastníkem
  • S_IWUSR - Práva pro zápis vlastníkem
  • S_IXUSR - Práva pro spuštění vlastníkem
Krom masek existuje ještě několik maker. Jako jediný parametr m se jim předává st_mode. Návratovou hodnotou maker je buď 0, v případě že test neuspěl nebo nenulová hodnota pokud test uspěl.
  • S_ISBLK(m)
  • S_ISCHR(m)
  • S_ISDIR(m) - Jde o adresář
  • S_ISFIFO(m) - Jde o rouru nebo speciální soubor FIFO
  • S_ISREG(m) - Jde o soubor
  • S_ISLNK(m) - Jde o symbolický adresář
Nyní si ukážeme použití funkce stat v akci:
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>

int main() {
    // Struktura s informacemi o souboru
    struct stat info;

    // Pokud se načtení informací povedlo
    if ( stat( "test.txt", &info ) == 0 ) {
        std::cout << "Jde o soubor: " << S_ISREG(info.st_mode) << std::endl;
        std::cout << "Jde o adresář: " << S_ISDIR(info.st_mode) << std::endl;
        std::cout << "Velikost: " << info.st_size << "b" << std::endl;
        std::cout << "Má vlastník právo na čtení: " << info.st_mode&S_IRUSR << std::endl;
        std::cout << "Má vlastník právo ke spuštění: " << info.st_mode&S_IXUSR << std::endl;
    
    } else {
        std::cout << "Soubor se nezdařilo otevřít" << std::endl;
        std::cout << "Zkontrolujte, zda existuje, nebo zda není v adresáři, ke kterému nemáte přístup" << std::endl;
    }
} // main()