V minulé části jsme postavili jednoduchý Stack pro obecný komunikační protokol. Teď do něj začleníme třídu, která umožní vypsat string a číslo třeba na sériový port. A nakonec popíšeme a opravíme známé chyby z minulých částí.

3. Process z předešlé části - zde začleníme třídu Print.

V main pak přibude jen definice instance této třídy

   static  Print             print;                // obrázek - 3


a ukázka, jak se s tím pracuje:

 // Top += ... += Bottom;          // Postav "Stack"
  command += print += serial;       // V řetězci přibyl print
  // Testy výpisů
  print << "\r\n\tFile : \"" << __FILE__ << "\" Line : " << __LINE__ << EOL;
  int count = 10;
  do {
    print << "  Count Down : " << BIN << count << " = " << HEX << count << " = " << DEC << count << EOL;
  } while (10 + count--);


Nic na tom není - operátor << má přetížení pro string, číslo a volbu formátu čísla (enum PrintBases). Výstup je pak do bufferu a aby nám to "neutíkalo", tedy aby se vypsalo vše, zavedeme blokování, vycházející z toho, že spodní třída vrátí jen počet bytů, které skutečně odeslala. Při čekání spí, takže nepoužívat v přerušení.

// Výstup blokujeme podle toho, co se vrací ze spodní vrstvy
uint32_t Print::BlockDown (const char* buf, uint32_t len) {
  uint32_t n, ofs = 0, req = len;
  for (;;) {
    // spodní vrstva může vrátit i nulu, pokud je FIFO plné
    n = BaseLayer::Down (buf + ofs, req);
    ofs += n;   // Posuneme ukazatel
    req -= n;   // Zmenšíme další požadavek
    if (!req) break;
    sleep();    // A klidně můžeme spát
  }
  return ofs;
}


Známé chyby.

V minulých částech bylo několik nesrovnalostí, které mohou (ale nemusí) působit problémy. Zkusím to zde uvést trochu na pravou míru.

1. Přetypování const char* na char*.

To opravdu není dobrý nápad. Zde to bylo použito dokonce na více místech a víceméně se nic strašného nestalo. Co by se však stát mohlo je, že const char* pochopí překladač (zcela správně) jako ukazatel na data ve flash a pokud pak ukazatel už jako nekonstantní předá funkci, která se bude snažit na toto místo zapsat, protože už netuší, že je to konstanta ve flash, program zhavaruje na hard fault. Takže takto opravdu ne.

2. Zarovnání sekce .init_array.

To mi nějak uniklo a je to dost podstatná chyba, vyskytující se v obou předchozích částech. Prostě ne vše se projeví ihned. Takže správně je:

       . = ALIGN(4); /*  <---  Tohle je ten problém. */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP (*(.init_array*))
        PROVIDE_HIDDEN (__init_array_end   = .);


Finále.

Nakonec jsem si ještě trochu pohrál s GpioClass a trochu změnil strukturu adresářů. Všechny ty funkce Up a Down mají první parametr const char* místo char*. Je to tak lepší a vlastně to ničemu nevadí. Při uřezávání hlaviček se stejně jen posune ukazatel, a pokud budu chtít hlavičky přidávat nebo jinak manipulovat s daty, stejně musím vytvořit lokální kopii dat, protože uvnitř funkce netuším, jak velký bufer mi byl předán. Dokonce to umožnilo zkrátit kód o pár bytů.

Pro ladění v RAM byl přidán linker skript, i když zapnout příslušnou option, tak aby program běžel z RAM byl fakt porod. OpenOcd skutečně není dokonalé. Text minulého článku opravovat nebudu, nechť je vidět, že chybami se člověk učí, opravená verze se dá vygenerovat z tohoto doxygenem. A stejně i v tomhle asi nějaké chyby zbyly.

A nakonec zase zdrojáky k této části.