É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?

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet!