Produkty Novinky Články Návody Kontakty

SAM9260 a znakový LCD - 4. ovladač, část druhá

V předchozím dílu jsme probrali inicializaci I/O portu mikrokontroléru AT91SAM9260 a inicializační rutinu samotného LCD displeje. Dnes si dokončíme rozbor zdrojového kódu ukázkového ovladače.
Už víme, že LCD displej umí automaticky posouvat kurzor na další volnou pozici, takže při zobrazovaní souvislého textu se musíme v ovladači postarat pouze o to, aby byl text zalomen na další řádek. Co když ale někdy chceme, aby text začínal uprostřed řádku? Nebo až na druhém řádku? Jak to ovladači přikázat z uživatelského prostoru, tj. z příkazové řádky Linuxu nebo z naší aplikace? Máme několik možností:
  1. Zobrazovaný text doplnit mezerami. Tzn, že text ”Dobry den, mistre!”, který zapisujeme do ovladače doplníme mezerami, tak aby byl na LCD displeji vycentrován.
  2. Do zobrazovaného textu vložíme tzv. řídící znaky. To jsou speciální znaky, které se nezobrazují, ale které ovladač displeje interpretuje a my mu tak z uživatelského prostoru můžeme určit, kam má posunout začátek zobrazovaného textu. Tyto znaky se zapisují za zpětným lomítkem, např.: "\t\t\tDobry den,\r\t\t\t\tmistre!". Znak '\t' znamená posun o jeden znak doleva, zatímco znak '\r' skok na začátek dalšího řádku.
  3. Nadefinovat příkazy pro změnu začátku zobrazovaného textu a implementovat do ovladače funkci ioctl(), přes kterou by se tyto příkazy předávaly z uživatelského prostoru do ovladače.
Varianta 1) není příliš pohodlná, uživatelská aplikace musí stále dopočítávat správné množství mezer. Ovladač samotný je ale úplně jednoduchý - co se mu předá k zobrazení, to zobrazí. Varianta 2) situaci uživatelské aplikaci ulehčuje - stačí doplnit do textu potřebné řídící znaky a tak určit počáteční pozici textu. To ale ovšem také znamená, že ovladač musí umět projít zobrazovaný text, vyhledat řídící znaky a podle nich nastavit správně počáteční pozici. Čím více řídících znaků, tím komplikovanější bude interpretr v ovladači. Varianta 3) se zdá být nejlepší volbou. Ovladač zobrazí text, který mu pošleme, od aktuální pozice dále. Pokud chceme změnit počáteční pozici textu, tak před zapísem textu požádáme ovladač pomocí operace ioctl() o změnu aktuální pozice. Ve funkci write_buf() jsme implementovali variantu 2). Ovšem s pomocí našeho úvodu do vývoje ovladačů si můžete ovladač předělat na variantu 3) a porovnat si oba způsoby nastavení počáteční pozice textu z pohledu aplikace, která běží v uživatelském prostoru.
static void write_buf(void * data_ptr, size_t data_size)
{
  int i, charcnt = 0, set_addr = 0;
  unsigned char * buffer = (unsigned char *) data_ptr;

  // zacneme vzdy na prvnim znaku prvniho radku
  // pri zapisu vice znaku nez co se vejde na LCD, zajisti reset na pozici
  // prvni radek prvni znak
  write_cmd(LCD_CMD_SET_ADDR_1R);
  
  for ( i = 0; i < data_size; i++ )
  {
    // posun se o jeden znak doleva
    if ( buffer[i] == '\t' )
    {
      ++charcnt;
      set_addr = 1;
    }
    // skoc na novy radek - dopln chybejici pocet znaku do konce radku
    else if ( buffer[i] == '\r' )
    {
      charcnt += MAX_CHARS_PER_ROW - (charcnt % MAX_CHARS_PER_ROW);
      set_addr = 1;
    }     
    // vytiskni znak, nejprve nastav spravnou adresu v LCD
    else
    {
      // posun pozici znaku
      if ( set_addr )
      {
        unsigned int rows = charcnt / MAX_CHARS_PER_ROW;
        unsigned int cols = charcnt % MAX_CHARS_PER_ROW;
        unsigned int base = ( ((rows % MAX_ROWS) == 0) ?
                        LCD_CMD_SET_ADDR_1R : 
                        LCD_CMD_SET_ADDR_2R );
        
        // zapis novou adresu do LCD
        write_cmd(base + cols);
        set_addr = 0;
      }
      
      // tisk znaku na LCD displeji
      write_data(buffer[i]);
      ++charcnt;
      
      // po kazdem 16. znaku zmen adresu pro novy znak
      if ( !(charcnt % MAX_CHARS_PER_ROW) )
      {
        set_addr = 1;
      }
    }
  }
}

Po krátkém přehledu možných řešení, jak nastavit novou počáteční pozici zobrazovaného textu, se vrátíme zpět ke zdrojovému kódu funkce witer_buf(). Vstupem funkce je ukazatel na bufer s textem (včetně řídích znaků) - parametr data_ptr a dále velikost buferu v bytech - parametr data_size. Jako úplně první krok je nastavení aktuální pozice v displeji na první znak prvního řádku. Pak postupně v cyklu procházíme obsah buferu. Když je nalezen znak '\t', počet zobrazených znaků (proměnná charcnt) se navýší o jeden znak. Zároveň se nastaví proměnná set_addr, která zajistí, že před zobrazením prvního tisknutelného znaku se do displeje pošle příkaz pro změnu aktuální počáteční pozice. Řídící znak '\r' způsobí, že se obsah proměnné charcnt zvětší o počet znaků, které chybí do konce řádku. Tím se vlastně zajistí, že další znak bude zobrazen na začátku nového řádku. A konečně, když je v buferu nalezen tisknutelný znak, tak se nejprve vypočte nová počáteční pozice pro zobrazovaný znak a tato se nastaví do displeje. Následně se vlastní tisknutelný znak odešle k zobrazení do LCD displeje.
Nová počáteční pozice pro zobrazení textu se určuje z počtu zobrazených znaků. Ať už těch skutečně zobrazených (tisknutelných) nebo těch prázdných přidaných pomocí řídích znaků. Proměnná charcnt se nejprve celočíselně podělí počtem znaků na řádek. Tím si vypočteme počet plně obsazených řádků - proměnná rows. Pak si vypočítame počet znaků, které zbyly na posledním ne zcela obsazeném řádku - proměnná cols. Počet plně obsazených řádků rozpočítáme do počtu řádků LCD displeje. Takže pokud náš text zabral tři řádky, i když má displej jen dva řádky, bude třetí řádek zobrazen na prvním řádku a přepíše obsah prvního řádku. Čvrtý na druhém atd. Podle počtů řádků vybereme správnou adresu v paměti DDRAM řadiče displeje (proměnná base) a přičteme k ní počet zbývajících znaků - proměnou cols. Výslednou adresu zapíšeme do displeje.

Seznam dílů