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.