Ékezetes betűk írása LCD-re kényelmesen

Tartalom:

  • Ékezetes betűk helyfoglalása string-ben
  • karakteres LCD kijelzőre magyar ékezetes karakterképek feltöltése induláskor
  • Kezelőfüggvény, ami ékezetes karaktereket tartalmazó stringet küld a kijelzőnek, és kijelzőn ékezetesen jelenik meg a szöveg
  • Meg nem valósítható ékezetek konvertálása közeli karakterképhez (pl. Ő Ö)
  • Teljes forráskód, példa program

——————————————————————————————————

A napokban egy LCD kijelzőt használó programot készítettem, és rengeteg időt vacakoltam azzal, hogy a kijelzőn a szavak ékezetesen jelenjenek meg. Egy 2×16 karakteres kijelzőn nagyon kevés a hely, sokat kell rövidíteni, ezért fontosak lehetnek az ékezetek! Ezért töltöttem sok időt a problémával. Az ékezetes írás nem probléma, hiszen a neten jó sok példát lehet találni. A kényelmetlenséget az okozza, hogy az ékezetes betűket külön paranccsal lehet kiírni, mivel a kijelzőbe letöltött user definiált karakterek speciálisa a 0-7-ig terjedő karakter kódokra kerülnek. Így ha le akarsz írni a kijelzőn egy ékezetes betűket tartalmazó szót, akkor a következőt kell csinálni (saját példa, a változó értékek itt most nem számítanak):

lcd.print(„LP1 tanul”); lcd.write(oI); lcd.print(” m”); lcd.write(oI); lcd.print(„d „);

Ebben a példában az „LP1 tanuló mód” szöveget kellett kiírnom. Nem elég, hogy vacakolni kell az ékezetekkel, még a forráskódban elhelyezett szöveg is olvashatatlan lesz. Meguntam a tökölődést, és csináltam egy függvényt, ami kényelmesebbé teszi a programírást, és még olvasható is marad a kiírt szöveg a forrásban. Sajnos nem vagyok egy túlképzett C++ programozó, ezért sok fejtörést okozott a stringekkel történő munka. Minden bizonnyal sokkal szebben és optimálisabban is meg lehetett csinálni.

Első problémát az okozta, hogy a stringbe kerülő ékezetes betűk fura módon nem egy, hanem két byte helyet foglalnak a többi karakterrel szemben. Ez nem meglepő, hiszen az ékezet nélküli standard karakterek feltehetőleg ASCII kódtábla szerint kezelődnek, míg a magyar ékezetekkel nem tud mit kezdeni a rendszer, hiszen azok nem részei az ASCII kódtáblának. Nagyon intelligensen valamilyen más kódtábla szerint illeszti be ezekez. Úgy tapasztaltam, hogy ez az UTF-8. Nem igazán szoktam ilyen kérdésekkel foglalkozni, ezért számomra ez új infó volt. Nézegettem a két byte tartalmát, aztán keresgettem a neten, és így találtam rá egy táblázatra, amiben az általam tapasztalt kódokat találtam. Az általam preparált UTF-8 kódtábal így néz ki a magyar ékezetes betűkre:

Betű UTF-8 DEC Betű UTF-8 DEC
Á – C3 81 – 195,129 á – C3 A1 – 195,161
É – C3 89 – 195,137 é – C3 A9 – 195,169
Í – C3 8D – 195,141 í – C3 AD – 195,173
Ó – C3 93 – 195,147 ó – C3 B3 – 195,179
Ö – C3 96 – 195,150 ö – C3 B6 – 195,182
Ő – C5 90 – 197,144 ő – C5 91 – 197,145
Ú – C3 9A – 195,154 ú – C3 BA – 195,186
Ü – C3 9C – 195,156 ü – C3 BC – 195,188
Ű – C5 B0 – 197,176 ű – C5 B1 – 197,177

Saját munkám könnyítésre a táblázatban szereplő hexa kódpárokat decimálisan is kiírtam, mert ezekkel én könnyebben tudtam dolgozni.

Az elkészült program tartalmaz egy lcd_print_hu() nevű függvényt. Ennek egy paramétere van, a kiírásra szánt szöveg, ami tartalmazhat magyar ékezetet. A függvény letölti a kijelzőre a magyar ékezetes betűk karakterképét, ha üres string a paraméter. Egyébként meg kiírja a szöveget ékezetes karakterekkel a kijelzőre az aktuális karakter pozíciótól kezdve. Egy az egyben az lcd.print() helyett lehet használni. Belül a függvény semmi meglepőt nem tartalmaz, végig megy a string-en, az ékezetes betűk két byte-jából egy byte-ot kihagy, a második byte tartalmát pedig lecseréli az LCD kijelző letöltött karakterének kódjára. Sajnos nagybetűket nem lehet megjeleníteni, mert én úgy döntöttem, hogy csak a kisbetűk karakterképét készítem el, kicsi és nagy egyszerre nem fér el a rendelkezésre álló 8 memóriahelyen. A nagybetűk benne lehetnek a szövegben, azokat is kicsire cseréli!

Csak 7 karaktert kellett letölteni, mert az ö és ü betű a standard ASCII táblában is benne van. Az egyes letöltött ékezetes betűk karakter kódjai a kijelzőben (ez tetszőleges, nekem így sikerült letölteni):
0 – á
1 – é
2 – í
3 – ó
4 – ú
5 – ő
6 – ű

A Forrás:

/*
 * Betű  UTF-8  DEC        Betű  UTF-8  DEC
 * Á     C3 81  195,129    á     C3 A1  195,161
 * É     C3 89  195,137    é     C3 A9  195,169
 * Í     C3 8D  195,141    í     C3 AD  195,173
 * Ó     C3 93  195,147    ó     C3 B3  195,179
 * Ö     C3 96  195,150    ö     C3 B6  195,182
 * Ő     C5 90  197,144    ő     C5 91  197,145
 * Ú     C3 9A  195,154    ú     C3 BA  195,186
 * Ü     C3 9C  195,156    ü     C3 BC  195,188
 * Ű     C5 B0  197,176    ű     C5 B1  197,177
 * Az ékezetes betűk UTF-8 kódolással kerülnek a string-be. Az első byte kódja 
 * minden magyar ékezetes karakter esetén C3 (195) vagy C5 (197). Szerencsére
 * a második byte minden ékezetes karakternél különbözű, így 195 vagy 197 után
 * elegendő a fenti kódokat figyelni. Mivel nincs elegendő karakterképnek hely,
 * csak a kisbetűket jelenítem meg, így csak kisbetűk lesznek a szövegben 
 * az LCD karakteres kijelzőn. 
 * Az alkotott új függvény neve lcd_print_hu()
 */

#include <Wire.h>    //I2C library 
#include <LiquidCrystal_I2C.h>  //I2C LCD kezelő könyvtár
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  //LCD paraméterek megadása

void setup() {
  Serial.begin(9600);
  lcd.begin(16,2);                                     //LCD inicializálása
  lcd.clear();                                         //LCD törlés
  lcd.backlight();                                     //háttérvilágítás bekapcsolása
  lcd_print_hu("");                                    //üres stringgel hívtam meg a függvényt, ez jelenti, hogy töltse fel a magyar karakterképeket
  lcd.setCursor(0,0);lcd_print_hu("Nagy:ÁÉÍÓÖŐÚÜŰ");   //összes nagy ékezetes betű írása (mind kicsiként fog megjelenni)
  lcd.setCursor(0,1);lcd_print_hu("Kicsi:áéíóöőúüű");  //összes kis ékezetes betű írása
}

void loop() {
}


void lcd_print_hu(String szoveg) {
String kiir="";
byte karakter;
byte j=0;
byte hossz=szoveg.length();   //az átvett string hossza
if (hossz==0) {  //üres karakterrel hívtuk meg a függvényt, ez azt jelenti, hogy feltöltjük a kijelzőt a magyar kerekterek képével
  byte tt[8] = {B10,B100,B1110,B1,B1111,B10001,B1111};             //á betű karakterképe
  lcd.createChar(0,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B1110;tt[3]=B10001;tt[4]=B11111;tt[5]=B10000;tt[6]=B1110;        //é betű karakterképe
  lcd.createChar(1,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B0;tt[3]=B1110;tt[4]=B100;tt[5]=B100;tt[6]=B1110;                //í betű karekterképe
  lcd.createChar(2,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B0;tt[3]=B1110;tt[4]=B10001;tt[5]=B10001;tt[6]=B1110;            //ó betű karakterképe
  lcd.createChar(3,tt);
  tt[0]=B00101;tt[1]=B01010;tt[2]=B0;tt[3]=B1110;tt[4]=B10001;tt[5]=B10001;tt[6]=B1110;      //ő betű karakterképe
  lcd.createChar(4,tt);
  tt[0]=B10;tt[1]=B100;tt[2]=B10001;tt[3]=B10001;tt[4]=B10001;tt[5]=B10011;tt[6]=B1101;       //ú betű karakterképe
  lcd.createChar(5,tt);
  tt[0]=B00101;tt[1]=B01010;tt[2]=B0;tt[3]=B10001;tt[4]=B10001;tt[5]=B10011;tt[6]=B1101;     //ű betű karakterképe
  lcd.createChar(6,tt);
  return;
}
for (byte i=0;i<hossz;i++) {                             //minden karakteren sorban végig megyünk
  if ((byte) szoveg[i]==195 or (byte) szoveg[i]==197)    //ha a kerekter 195 vagy 197, akkor azt kihagyjuk az eredmény stringből és vesszük a következő karaktert
  {
    i++;
    karakter=(byte) szoveg[i];
    kiir.concat(" ");   //ez a hiányos C++ tudásom miatt van, mivel nem tudtam hozzáfűzni a 0-7 ASCII kódot, ezért hozzáfüzük egy szóközt, 
                        //amit a switch-ben lecserélek a megfelelő feltöltött karakter kódjára, illetve a karakterkészletben meglévő 
                        //ö és ü betű kódjára
    switch (karakter) {
      case 129: kiir.setCharAt(j,0);break;    //Á  //a j. pozicióban (utolsó hozzáfűzött karaktert cserélem a feltöltött karakter kódjára
      case 161: kiir.setCharAt(j,0);break;    //á
      case 137: kiir.setCharAt(j,1);break;    //É
      case 169: kiir.setCharAt(j,1);break;    //é
      case 141: kiir.setCharAt(j,2);break;    //Í
      case 173: kiir.setCharAt(j,2);break;    //í
      case 147: kiir.setCharAt(j,3);break;    //Ó
      case 179: kiir.setCharAt(j,3);break;    //ó
      case 150: kiir.setCharAt(j,239);break;  //Ö
      case 182: kiir.setCharAt(j,239);break;  //ö
      case 144: kiir.setCharAt(j,4);break;    //Ő
      case 145: kiir.setCharAt(j,4);break;    //ő
      case 154: kiir.setCharAt(j,5);break;    //Ú
      case 186: kiir.setCharAt(j,5);break;    //ú
      case 156: kiir.setCharAt(j,245);break;  //Ü
      case 188: kiir.setCharAt(j,245);break;  //ü
      case 176: kiir.setCharAt(j,6);break;    //Ű
      case 177: kiir.setCharAt(j,6);break;    //ű
    }
  }
  else {kiir.concat(szoveg[i]);}              //ez egy kijelzőn is megtalálható karakter, simán csak másoljuk
  j++;                                        //egy karakterrel több van a kijelzőre irantó stringben
}
  lcd.print(kiir);                            //..és akkor kiírjuk a kijelzőre az ékezetes kódokra cserélt szöveget
}

Meglepő volt számomra a lefordított program mérete és memória használatának mértéke. Amikor elkészültem az automata locsolórendszer programjával, még a fentebbi példa szerinti módon írtam az ékezetes betűket a kijelzőre, azaz minden ékezetes betűt külön kiírtam az lcd.write() függvénnyel. Ekkor a a program fordítását követően a következő üzenetet írta ki a fordító:

A vázlat 34604 bájt (13%)-ot használ a program tárhelyből. A maximum 253952 bájt.
A globális változók 1586 bájt (19%)-ot használnak a dinamikus memóriából, 6606 bájtot hagyva a helyi változóknak. A maximum 8192 bájt.

Ezt követően kicseréltem a feliratok kiírását a fenti lcd_print_hu() függvényre. Hozzámásoltam az új függvényt a programhoz, kiszedtem az lcd.write()-ot, valamint a felesleges memóriaváltozókat. Semmi máshoz nem nyúltam. A fordító üzenete:

A vázlat 34808 bájt (13%)-ot használ a program tárhelyből. A maximum 253952 bájt.
A globális változók 1853 bájt (22%)-ot használnak a dinamikus memóriából, 6339 bájtot hagyva a helyi változóknak. A maximum 8192 bájt.

Lényegesen nagyobb méret növekedésre számítottam. 204 byte-al lett hosszabb a program és 267 byte-al több memóriát használ. Ezt megfelelőnek ítélem, érdemes használni, ha van memória.

Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!