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.