Als Teil der Arbeit mit einigen Arduino-kompatiblen Geräten (ungefähr am Ende) brauchte ich einen Bildschirm mit Schaltflächen, um aktuelle Informationen zu steuern und anzuzeigen. Das heißt, das Bedienfeld wurde benötigt, es ist auch HMI.
Es wurde beschlossen, HMI unabhängig zu machen und den „quadratischen“ i2c-Bus als Schnittstelle zu verwenden.

Wenn Sie an der Entwicklung und Programmierung solcher Geräte interessiert sind, sind Sie bei cat willkommen.
Eigenschaften
- Anzeige 1602, monochrome 16x2 Zeichen
- 5 Tasten: hoch, runter, abbrechen, eingeben, bearbeiten
- I2c-Schnittstelle
- DB9F-Anschluss
- Abmessungen 155x90x44 mm
Es gibt offensichtliche Fragen:
Warum nicht einen fertigen Schild kaufen?Natürlich war es möglich, von einem Chinesen ein fertiges Schild mit Display und Tastatur zu kaufen, und so:

Sie können 2 FC-113-Schals an diese Abschirmung anlöten, und es stellt sich heraus, dass sie funktional mit meiner identisch ist: ein Display mit einer Tastatur, die an i2c arbeitet. Der Preis für das Set beträgt ab 4 US-Dollar.
Aber auf diesem Board passt die Größe der Tasten nicht zu mir, aber ich wollte große mit der Fähigkeit, mehrfarbige Kappen zu installieren. Ich wollte das Arduino nicht über den normalen DB9F-Anschluss mit dem HMI verbinden, aber es musste eine Verbindungsplatine hergestellt werden. Und in diesem Fall, was ist der Unterschied, ein oder zwei Boards zu machen? Außerdem hatte ich bereits mehrere 1602 Displays auf Lager, sodass ich nur 1,02 US-Dollar ausgeben musste, um FC-113 (0,55 US-Dollar) und den PCF8574P-Port-Expander (0,47 US-Dollar) für Aliexpress zu kaufen.
Nun, und vor allem, wenn Sie es mit Arduino zu tun haben, ist die unabhängige Herstellung von Schilden für ihn eine Selbstverständlichkeit, oder?
Warum ist der i2c-Bus nicht einfacher, die Tasten direkt anzuschließen?Im Bereich der industriellen Steuerungssysteme, in denen ich arbeite, verwenden HMIs digitale Datenübertragungsschnittstellen RS-232, RS-485, CAN usw., um mit Geräten zu kommunizieren. Daher ist es für mich logisch, dass mein hausgemachtes HMI alle auf einer Datenschnittstelle funktioniert, in diesem Fall auf i2c.
Wenn ich ein Gerät herstellen würde, bei dem das Display auf einem quadratischen Bus funktioniert und die Tasten direkt zum Eingang von Arduino gehen, würde ich mich zutiefst unzufrieden fühlen. Wie ich dieses Bild präsentieren werde: Ein separates Kabel ragt aus dem Panel zur Schnittstelle heraus, separate Drähte zum Eingang, brrrr ...
Darüber hinaus besteht der Unterschied zwischen der Tastenkarte, die direkt zu den Arduino-Eingängen führt, und der Tastenkarte mit der i2c-Schnittstelle nur im PCF8574P-Chip (0,47 USD), einem Kondensator und zwei Widerständen.
Warum sind die Tasten so angeordnet und nicht anders?Die Schaltflächen von links nach rechts haben folgende Funktionen: nach oben, unten, abbrechen, eingeben, bearbeiten.
Die Schaltfläche „Bearbeiten“ wird von den anderen ein wenig zur Seite entfernt, um ihre Funktion hervorzuheben - Ändern der Werte logischer Parameter (Ein / Aus) oder Umschalten in den Bearbeitungsmodus bei numerischen Parametern.
Insgesamt gibt es 5 Tasten, obwohl Sie mit dem Chip auf der Tastatur bis zu 8 Teile anschließen können.
Es würde ausreichen, mit vier Schaltflächen zu arbeiten, und die Funktionalität würde nicht beeinträchtigt - „Eingabe“ und „Bearbeitung“ können in einer Schaltfläche kombiniert werden. Aber es tat mir nur leid, dass die Hälfte der 8 Beine des Port Expander-Mikrokreises nicht betroffen sein würde.
Eine weitere separate Schaltfläche zum Bearbeiten kann hilfreich sein, wenn ich mehrere Parameter in einer Zeile anzeigen möchte. Mit dieser Schaltfläche können Sie dann zwischen den Parametern wechseln und angeben, welche geändert werden müssen. So funktioniert die Taste „SET“ im beliebten chinesischen HMI OP320.
Wenn die ersten beiden Tasten nach oben und unten bedeuten, warum sollten Sie sie dann nicht vertikal platzieren, wie dies beispielsweise im obigen chinesischen Schild der Fall ist?
Für mich persönlich ist es bequemer, wenn alle Tasten horizontal sind und sich die Finger während des Betriebs nur in einer Ebene bewegen.
Eisen


1. Selbstgemachte Rückwandplatine mit DB9F-Anschluss. Da wir also eine + 5V-Stromversorgung für Port- und Display-Expander von Arduino verwenden, habe ich eine 0,1-A-Sicherung auf die Platine gelegt
2. Wir alle kennen das bekannte 1602-Display mit einer gelöteten FC-113-Platine, die das Display mit dem i2c-Bus verbindet.
3. Eine selbstgemachte Tastaturplatine mit einem PCF8574P-Chip, die den Status der Tasten liest und über den i2c-Bus überträgt. Übrigens basiert die FC-113 "Anzeige" -Karte auch auf dem PCF8574-Chip, nur mit dem T-Index, d.h. planar, nicht DIP, wie PCF8574P.
Knöpfe Ich habe 12h12mm mit einem quadratischen Drücker angebracht, auf denen Sie große mehrfarbige Kappen tragen können.
Fotos und Schemata von hausgemachten Leiterplatten Es lohnt sich, ein paar Worte über den PCF8574P-Chip zu sagen, auf dessen Grundlage ich eine Tastaturplatine hergestellt habe.
Der PCF8574P ist ein i2c-Port-Extender. Insgesamt gibt es 8 Ports, von denen jeder als Ein- oder Ausgang konfiguriert werden kann. Für diesen Chip ist kein Umreifen als solches erforderlich (denken Sie beispielsweise an max232), nur für den Fall, dass ich einen Kondensator für die Stromversorgung einsetze.
Die Adresse des PCF8574P-Chips wird über die Adresszweige A0, A1, A2 eingestellt, die über einen 10-kΩ-Widerstand zur Erde oder zur Stromversorgung gezogen werden.
Auf der Tastatur habe ich alle Adresszweige des PCF8574P auf den Boden gelegt, sodass die Adresse als 0x20 fest codiert ist und Sie sie nicht ändern können.
Wie ich bereits geschrieben habe, habe ich DB9F als Anschluss für das HMI gewählt. Arduino empfängt Signale von ihm +5 V, GND, SDA, SCL.

Der Draht für die Kommunikation über i2c Arduino und HMI ist 1,4 m lang und funktioniert ohne Störungen.
Ich habe die Platten in Sprint Layout 6 gezeichnet, sie mit der LUT-Methode auf Textolit übertragen und sie in eine Lösung aus Peroxid und Zitronensäure geätzt.
Ein bisschen über BeizenEs gibt viele Rezepte zum Ätzen von Zitronensäureplatten auf Glasfaserfolie.
Ich habe diese Lösung hergestellt: 100 ml Wasserstoffperoxid 3%, 50 g Zitronensäure, 3 Teelöffel Salz. Er erwärmte das Peroxidglas in einer Pfanne mit Wasser auf eine Temperatur von etwa 70 Grad.
Wir tauchen die Platte mit dem Muster nach unten in die Lösung, wie beim Ätzen mit Peroxid empfohlen.
Nach einigen zehn Sekunden beginnt ein stürmischer Prozess. Es wird viel Dampf freigesetzt, dessen Einatmen nicht empfohlen wird. Wahrscheinlich.

Dann lässt der Prozess nach. Drehen Sie das Brett um.

Fertig.

Das Gehäuse wurde von einem Freund aus Plexiglas 4 mm auf einer Laserschneidmaschine hergestellt.
Lyrischer Exkurs über das KorpsKaufen Sie einen fertigen Koffer oder machen Sie es selbst? Nach einigem Nachdenken entschied ich mich, es selbst zu tun. Diejenigen, die ich zum Verkauf sah, passten weder aus Preis- noch aus ästhetischen Gründen zu mir oder befanden sich auf einer DIN-Schiene, die auch nicht zu mir passte.
Anfangs wollte der Körper aus Sperrholz herausschneiden. Aber dann erinnerte ich mich, dass ich einen wunderbaren Freund und zu meiner großen Freude den Direktor einer Sportpreisfirma hatte. Er hat dort alle Arten von Maschinen, auch zum Laserschneiden.
Er bat um Hilfe und ein Freund weigerte sich nicht, für ein paar Minuten wurden die Teile mit einem Laser geschnitten.
Ich nutze diese Gelegenheit, um mich zu bedanken, Kolya! Sonst müsste ich den ganzen Tag Sperrholz schneiden und schleifen, und das Ergebnis wäre kaum so brillant.
Programmierung
Aus Sicht von Arduino besteht dieses HMI aus 2 Geräten, die am i2c-Bus arbeiten: einem Display (LCD) mit der Adresse 0x27 und einer Tastatur mit der Adresse 0x20. Dementsprechend arbeitet Arduino separat mit der Tastatur und separat mit dem LCD.
Die Arbeit mit LCD erfolgt über eine spezielle Bibliothek "LiquidCrystal_I2C.h", die in der Aduino IDE installiert sein muss.
Die Arbeit mit der Tastatur erfolgt über die Standardbibliothek Wire.h, die zunächst in der Aduino IDE verfügbar ist.
Wir verbinden HMI mit Ardiuno.

1. Überprüfen Sie zunächst, ob Arduino unser HMI sieht. Laden Sie dazu das Programm hinein, das den i2c-Bus nach Geräten durchsucht.
Skizze 1, i2c-Bus-Scan//i2c_scaner #include <Wire.h> String stringOne; void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { String stringOne = String(address, HEX); Serial.print("0x"); Serial.print(stringOne); Serial.print(" - "); if(stringOne=="0A") Serial.println("'Motor Driver'"); if(stringOne=="0F") Serial.println("'Motor Driver'"); if(stringOne=="1D") Serial.println("'ADXL345 Input 3-Axis Digital Accelerometer'"); if(stringOne=="1E") Serial.println("'HMC5883 3-Axis Digital Compass'"); if(stringOne=="5A") Serial.println("'Touch Sensor'"); if(stringOne=="5B") Serial.println("'Touch Sensor'"); if(stringOne=="5C") Serial.println("'BH1750FVI digital Light Sensor' OR 'Touch Sensor" ); if(stringOne=="5D") Serial.println("'Touch Sensor'"); if(stringOne=="20") Serial.println("'PCF8574 8-Bit I/O Expander' OR 'LCM1602 LCD Adapter' "); if(stringOne=="21") Serial.println("'PCF8574 8-Bit I/O Expander'"); if(stringOne=="22") Serial.println("'PCF8574 8-Bit I/O Expander'"); if(stringOne=="23") Serial.println("'PCF8574 8-Bit I/O Expander' OR 'BH1750FVI digital Light Sensor'"); if(stringOne=="24") Serial.println("'PCF8574 8-Bit I/O Expander'"); if(stringOne=="25") Serial.println("'PCF8574 8-Bit I/O Expander'"); if(stringOne=="26") Serial.println("'PCF8574 8-Bit I/O Expander'"); if(stringOne=="27") Serial.println("'PCF8574 8-Bit I/O Expander' OR 'LCM1602 LCD Adapter '"); if(stringOne=="39") Serial.println("'TSL2561 Ambient Light Sensor'"); if(stringOne=="40") Serial.println("'BMP180 barometric pressure sensor'" ); if(stringOne=="48") Serial.println("'ADS1115 Module 16-Bit'"); if(stringOne=="49") Serial.println("'ADS1115 Module 16-Bit' OR 'SPI-to-UART'"); if(stringOne=="4A") Serial.println("'ADS1115 Module 16-Bit'"); if(stringOne=="4B") Serial.println("'ADS1115 Module 16-Bit'"); if(stringOne=="50") Serial.println("'AT24C32 EEPROM'"); if(stringOne=="53") Serial.println("'ADXL345 Input 3-Axis Digital Accelerometer'"); if(stringOne=="68") Serial.println("'DS3231 real-time clock' OR 'MPU-9250 Nine axis sensor module'"); if(stringOne=="7A") Serial.println("'LCD OLED 128x64'"); if(stringOne=="76") Serial.println("'BMP280 barometric pressure sensor'"); if(stringOne=="77") Serial.println("'BMP180 barometric pressure sensor' OR 'BMP280 barometric pressure sensor'"); if(stringOne=="78") Serial.println("'LCD OLED 128x64'" ); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); }
Während der Ausführung dieses Programms schreibt Arduino die Scanergebnisse des i2c-Busses auf die serielle Schnittstelle. Um diese Daten anzuzeigen, gehen Sie zu Arduino IDE Tools-> Port Monitor.

Wir sehen, dass Arduino auf dem i2c-Bus zwei Geräte mit den Adressen 0x20 und 0x27 identifiziert hat, dies sind die Tastatur bzw. das LCD.
2. Nun wollen wir sehen, wie unsere Tastatur funktioniert. Erstellen Sie ein Programm, das den Status der Tasten abfragt und auf dem LCD anzeigt.
Skizze 2, zeigt den Status der Schaltflächen an #include <LiquidCrystal_I2C.h> #include <Wire.h> #define led 13 #define ADDR_KBRD 0x20 #define ADDR_LCD 0x27 byte dio_in; bool b; bool key[5]; LiquidCrystal_I2C lcd(ADDR_LCD,16,2); // void setup() { pinMode(led, OUTPUT); // lcd.init(); lcd.backlight();// // Wire.begin(); Wire.beginTransmission(ADDR_KBRD); Wire.write(B11111111); // PCF8574P Wire.endTransmission(); } void loop() { Wire.requestFrom(ADDR_KBRD,1); while (!Wire.available()); byte dio_in = Wire.read(); // PCF8574P() // byte mask=1; for(int i=0; i<5;i++) { key[i]=!(dio_in & mask); mask=mask<<1; } b=!b; digitalWrite(led, b); // // LCD lcd.setCursor(0, 0); lcd.print(String(key[0])+" "+ String(key[1])+" "+ String(key[2])+" "+ String(key[3])+" "+ String(key[4])+" "); delay(100); }

Die Tastatur funktioniert.
3. Schließlich können Sie mit dem, was alles vorhatte, fortfahren und in Arduino ein mehrstufiges Menü erstellen. Über das Menü sehen wir nicht nur Informationen, sondern steuern auch die Ausgänge von Arduino selbst.

In nete gibt es viele Informationen zum Erstellen eines mehrstufigen Menüs in C ++, aber für Arduino habe ich sogar einige Bibliotheken gesehen. Aber ich habe mich in meinem Programm entschieden, das Menü selbst zu schreiben. Erstens ist es umso ruhiger, je weniger Bibliotheken im Projekt übrig sind. Und zweitens ist es einfach.
Ich habe eine andere Variante des Baummenüs. Über das Menü können Sie in jeder Zeile gleichzeitig statischen Text und den Wert der Variablen anzeigen. Sie können beispielsweise den Namen des Parameters und seinen Wert anzeigen.
Um Variablen auf dem Bildschirm anzuzeigen, wende ich das Prinzip der Tags an - auf bestimmte Weise gestaltete Textbeschriftungen im Text, anstelle dessen, wenn der Text auf dem Bildschirm angezeigt wird, ein Wert angezeigt wird.
Die Parameter können durch Drücken der Schaltfläche „Bearbeiten“ geändert werden. Darüber hinaus gibt das Tag jedes Parameters an, ob es zum Bearbeiten oder schreibgeschützt verfügbar ist. Wenn der aktuelle Parameter schreibgeschützt ist, lautet der Zeiger am Zeilenanfang '*'. Wenn die Bearbeitung aktiviert ist, wird der Zeiger zu '+'.
Skizze 3, mehrstufiges Menü /* , LCD i2c LCD FC-113, 0x27 PCF8574P, 0x20 */ #include <LiquidCrystal_I2C.h> #include <Wire.h> #define led 13 // ; , , #define ADDR_KBRD 0x20 #define ADDR_LCD 0x27 #define PORT_D2 2 #define PORT_D3 3 #define PORT_D4 4 #define POINT_ON_ROOT_MENU_ITEM 0 // 0/1= / (* +) byte dio_in; bool b; byte i; //bool , bool BoolVal[9]={0,0,0, 0,0,0, 0,0,0}; #define ValSvet1 BoolVal[0] #define ValSvet2 BoolVal[1] #define ValSvet3 BoolVal[2] #define ValRozetka1 BoolVal[3] #define ValRozetka2 BoolVal[4] #define ValRozetka3 BoolVal[5] #define ValClapan1 BoolVal[6] #define ValClapan2 BoolVal[7] #define ValClapan3 BoolVal[8] // struct STRUCT_KEY{ bool StateCur; // bool StateOld; // bool Imp; // ( 0 1) }; // STRUCT_KEY Key[5]={0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0 }; //--- /* * , : * '#A1' bool , * '#'- bool, * 'A'- (HEX) BoolVal, * '1'- * , */ String StrNull=" "; // String StrRoot1="COMP-MAN.INFO"; String StrRoot2="PLC-BLOG.COM.UA"; String StrSvet= ""; // String StrSvet1=" 1 #01"; String StrSvet2=" 2 #10"; String StrSvet3=" 3 #21"; String StrRozetka=""; // String StrRozetka1=" 1 #30"; String StrRozetka2=" 2 #40"; String StrRozetka3=" 3 #50"; String StrClapan=""; // String StrClapan1=" 1 #60"; // String StrClapan2=" 2 #70"; String StrClapan3=" 3 #80"; struct MENU_ITEM // (), 2 { byte KeyUp; //№ , "" byte KeyDwn; //№ , "" byte KeyCancel; //№ , ""(cancel) byte KeyEnter; //№ , ""(enter) byte KeyEdit; // "edit", String *pstr1; // () String *pstr2; // () }; // MENU_ITEM Menu[]={0,0,0,1,0, &StrRoot1,&StrRoot2, //0 1,8,0,2,0, &StrSvet,&StrRozetka, //1 2,3,1,2,0, &StrSvet1,&StrSvet2, //2 2,4,1,3,0, &StrSvet2,&StrSvet3, //3 3,4,1,4,0, &StrSvet3,&StrNull, //4 0,0,0,0,0, &StrNull,&StrNull, //5 0,0,0,0,0, &StrNull,&StrNull, //6 0,0,0,0,0, &StrNull,&StrNull, //7 1,15,0,9,0, &StrRozetka,&StrClapan, //8 9,10,8,9,0, &StrRozetka1, &StrRozetka2, //9 9,11,8,10,0, &StrRozetka2, &StrRozetka3, //10 10,11,8,11,0, &StrRozetka3, &StrNull, //11 0,0,0,0,0, &StrNull,&StrNull, //12 0,0,0,0,0, &StrNull,&StrNull, //13 0,0,0,0,0, &StrNull,&StrNull, //14 8,15,0,16,0, &StrClapan, &StrNull, //15 16,17,15,0,0, &StrClapan1,&StrClapan2, //16 16,18,15,0,0, &StrClapan2,&StrClapan3, //17 17,18,15,0,0, &StrClapan3,&StrNull, //18 0,0,0,0,0, &StrNull,&StrNull, //19 0,0,0,0,0, &StrNull,&StrNull, //20 0,0,0,0,0, &StrNull,&StrNull, //21 }; byte PosMenu=0; // LiquidCrystal_I2C lcd(ADDR_LCD,16,2); // // void ReadKey(byte dio_in) { // byte mask=1; for(i=0; i<5; i++) { Key[i].StateCur=!(dio_in & mask); mask=mask<<1; Key[i].Imp=!Key[i].StateOld & Key[i].StateCur; // ( 0 1) Key[i].StateOld=Key[i].StateCur; } } /* * UTF-8 ( ) LCD * */ byte MasRus[33][2]= { 144, 0x41, // 145, 0xa0, 146, 0x42, 147, 0xa1, 148, 0xe0, 149, 0x45, 129, 0xa2, 150, 0xa3, 151, 0xa4, 152, 0xa5, 153, 0xa6, 154, 0x4b, 155, 0xa7, 156, 0x4d, 157, 0x48, 158, 0x4f, 159, 0xa8, 160, 0x50, 161, 0x43, 162, 0x54, 163, 0xa9, 164, 0xaa, 165, 0x58, 166, 0xe1, 167, 0xab, 168, 0xac, 169, 0xe2, 170, 0xad, 171, 0xae, 172, 0xc4, 173, 0xaf, 174, 0xb0, 175, 0xb1 // }; String RusStrLCD(String StrIn) { String StrOut=""; byte b1; byte y; byte l=StrIn.length(); for(byte i=0; i<l; i++) { b1=StrIn.charAt(i); if (b1<128) StrOut=StrOut+char(b1); else { if (b1==208) //==208, 2- . { b1=StrIn.charAt(i+1); for(y=0; y<33; y++) if(MasRus[y][0]==b1) { StrOut=StrOut+char(MasRus[y][1]); break; } } i++; } } return StrOut; } //--------------------------- //ASCII HEX ---> dec byte StrHexToByte(char val) { byte dec=0; switch (val) { case '0': dec=0; break; case '1': dec=1; break; case '2': dec=2; break; case '3': dec=3; break; case '4': dec=4; break; case '5': dec=5; break; case '6': dec=6; break; case '7': dec=7; break; case '8': dec=8; break; case '9': dec=9; break; case 'A': dec=10; break; case 'B': dec=11; break; case 'C': dec=12; break; case 'D': dec=13; break; case 'E': dec=14; break; case 'F': dec=15; break; default: dec=0; break; } return dec; } // void WriteLCD(byte num) { String str[]={"*"+*Menu[num].pstr1,*Menu[num].pstr2}; if (num==0 && POINT_ON_ROOT_MENU_ITEM==0) // ? str[0].setCharAt(0,' '); // , // byte NumVal; byte l; for(byte y=0; y<2; y++) { l=str[y].length(); for(i=0; i<l; i++) { if (str[y].charAt(i)=='#') //# bool, off/ON { if(StrHexToByte(str[y].charAt(i+2))==1 && y==0) // ? str[y].setCharAt(0,'+'); NumVal=StrHexToByte(str[y].charAt(i+1)); str[y]=str[y].substring(0,i)+String(NumVal) ; if(BoolVal[NumVal]==0) str[y]=str[y].substring(0,i)+"off" ; if(BoolVal[NumVal]==1) str[y]=str[y].substring(0,i)+"ON" ; } if (str[y].charAt(i)=='$') //$ int, , { ; } if (str[y].charAt(i)=='~') //~ , , { ; } } } //--- lcd.clear(); lcd.setCursor(0, 0); lcd.print(str[0]); lcd.setCursor(1, 1); lcd.print(str[1]); } //, byte GoMenu(byte key) { byte PosMenuNew=PosMenu; switch (key) { case 0: PosMenuNew=Menu[PosMenu].KeyUp; break; case 1: PosMenuNew=Menu[PosMenu].KeyDwn; break; case 2: PosMenuNew=Menu[PosMenu].KeyCancel; break; case 3: PosMenuNew=Menu[PosMenu].KeyEnter; break; case 4: ; break; default: break; } return PosMenuNew; } // "Edit" void Edit(byte posmenu) { byte NumVal; bool *pval; String str=*Menu[posmenu].pstr1; byte l=str.length(); for(i=0; i<l; i++) if (str.charAt(i)=='#') //#- bool, off/ON { if(StrHexToByte(str.charAt(i+2))==1) // ? { pval= &(BoolVal[StrHexToByte(str.charAt(i+1))]); // , . *pval=!(*pval); // } } } // void ValToPort() { digitalWrite(PORT_D2,ValSvet1); digitalWrite(PORT_D3,ValSvet2); digitalWrite(PORT_D4,ValSvet3); } void setup() { pinMode(led, OUTPUT); // pinMode(PORT_D2, OUTPUT); pinMode(PORT_D3, OUTPUT); pinMode(PORT_D4, OUTPUT); // LCD StrSvet=RusStrLCD(StrSvet); StrSvet1=RusStrLCD(StrSvet1); StrSvet2=RusStrLCD(StrSvet2); StrSvet3=RusStrLCD(StrSvet3); StrRozetka=RusStrLCD(StrRozetka); StrRozetka1=RusStrLCD(StrRozetka1); StrRozetka2=RusStrLCD(StrRozetka2); StrRozetka3=RusStrLCD(StrRozetka3); StrClapan=RusStrLCD(StrClapan); StrClapan1=RusStrLCD(StrClapan1); StrClapan2=RusStrLCD(StrClapan2); StrClapan3=RusStrLCD(StrClapan3); // lcd.init(); lcd.backlight();// WriteLCD(PosMenu); Wire.begin(); Wire.beginTransmission(ADDR_KBRD); Wire.write(B11111111); // PCF8574P Wire.endTransmission(); } void loop() { Wire.requestFrom(ADDR_KBRD,1); while (!Wire.available()); byte dio_in = Wire.read(); // PCF8574P() ReadKey(dio_in); // //, ; , int KeyImp=-1; for (i=0; i<5; i++) if(Key[i].Imp==1) { KeyImp=i; Key[i].Imp==0; } if (KeyImp>-1) // ? { if (KeyImp==4) // "Edit" Edit(PosMenu); PosMenu=GoMenu((KeyImp)); WriteLCD(PosMenu); } b=!b; digitalWrite(led, b); // ValToPort(); // delay(50); }
LCD 1602 und Sprachproblem
Unabhängig davon muss das Problem der Russifizierung angesprochen werden.
Im Zeichengenerator einiger LCD 1602 gibt es keine russischen Buchstaben, und stattdessen werden japanische Krakozyabry gestickt. Es ist unmöglich, den Zeichengenerator erneut zu starten. Daher müssen Sie entweder Wörter in lateinischen Buchstaben auf den Bildschirm schreiben oder im Programm, um die russischen Buchstaben selbst zu bilden, weil Das LCD 1602 bietet die Möglichkeit, eigene Zeichen im RAM-RAM zu erstellen und zu speichern. Im letzteren Fall können Sie jedoch nicht mehr als acht "hausgemachte" Zeichen gleichzeitig auf dem Bildschirm anzeigen.
Grundsätzlich ist es in Ordnung, russische Wörter auf Englisch auf dem LCD zu schreiben. Dort war selbst die ehrwürdige französische Firma Shneider Electric (die bereits vor der Revolution Haubitzen an den Zaren verkaufte) anderthalb Jahrzehnte lang nicht in der Lage, Russisch in ihre berühmten programmierbaren Relais Zelio einzuführen. Dies hindert sie jedoch nicht daran, aktiv mit der Weite der gesamten GUS zu handeln. Darüber hinaus wurden Kanäle, Spanisch und Portugiesisch eingeführt.
In vielen unserer Fabriken kommunizieren diese Zelio mit Mitarbeitern mit Redewendungen wie „NASOS 1 VKL“.
Wenn nicht klar ist, ob ein bestimmtes LCD russische Buchstaben enthält, müssen Sie alle Zeichen des Zeichengenerators auf dem Bildschirm anzeigen. Wenn es Kyrillisch gibt, beginnt es an Position 160.
Skizze 4, in der alle Zeichen aus der Tabelle des LCD 1602-Zeichengenerators angezeigt werden #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,16,2); // void setup() { // put your setup code here, to run once: lcd.init(); lcd.clear(); } void loop() { int i,y; while(1) { for (i=0; i < 16; i++) { lcd.clear(); lcd.setCursor(0,0); lcd.print(String(i*16)+" - "+String(i*16+15)); lcd.setCursor(0,1); for(y=0;y<16;y++) lcd.print(char(i*16+y)); delay(3000); } } }
Aber selbst wenn Ihr LCD 1602 russifiziert ist, ist die Anzeige russischer Wörter nicht so einfach. Verwenden Sie zumindest die LiquidCrystal_I2C.h-Bibliothek, wenn Sie mit LCD am i2c-Bus arbeiten.
Wenn Sie den russischen Text beispielsweise einfach mit der Anweisung lcd.print („Hallo !!!“) ausgeben, dann anstelle von „Hallo !!!“ Auf dem Bildschirm erscheint etwas Müll.
Dies liegt daran, dass die Arduino-IDE russische Buchstaben in Zwei-Byte-UTF-8-Code übersetzt und im LCD alle Zeichen Einzelbyte sind.
Das gleiche Problem tritt übrigens beim Übertragen russischer Texte von Arduino auf den Arduino IDE-Portmonitor auf. Arduino sendet russische Buchstaben in der UTF-8-Doppelbyte-Codierung an die serielle Schnittstelle, und der Arduino IDE-Port-Monitor versucht, sie in der Einzelbyte-Windows-1251-Codierung (cp1251) zu lesen. Obwohl cp1251 wie die LCD 1602-Codierung ebenfalls 8-Bit ist, stimmt es nicht mit dieser überein.
Sie können russische Texte durch Zeichencodes bilden. Beispielsweise wird die Zeile "LCD-Anzeige" auf einem russifizierten LCD wie folgt angezeigt:
lcd.print("\243K \343\270c\276\273e\271");
Aber ich mag diesen Ansatz nicht.
Um russischen Text korrekt auf dem russifizierten LCD 1602 anzuzeigen, wurden mehrere Bibliotheken für Arduino erfunden. Aber nachdem ich die Bewertungen gelesen hatte, sah ich, dass sich viele über Störungen bei der Verwendung beschwerten.
Daher habe ich in meinem mehrstufigen Menüprogramm selbst eine einfache Funktion zum Konvertieren von UTF-8 in LCD-Codes geschrieben. Es stimmt, ich habe es nur für russische Großbuchstaben gemacht, was perfekt zu mir passt.
Funktion zum Konvertieren von russischen UTF-8-Großbuchstaben in Einzelbyte-LCD 1602-Code /* * UTF-8 ( ) LCD * */ byte MasRus[33][2]= { 144, 0x41, // 145, 0xa0, 146, 0x42, 147, 0xa1, 148, 0xe0, 149, 0x45, 129, 0xa2, 150, 0xa3, 151, 0xa4, 152, 0xa5, 153, 0xa6, 154, 0x4b, 155, 0xa7, 156, 0x4d, 157, 0x48, 158, 0x4f, 159, 0xa8, 160, 0x50, 161, 0x43, 162, 0x54, 163, 0xa9, 164, 0xaa, 165, 0x58, 166, 0xe1, 167, 0xab, 168, 0xac, 169, 0xe2, 170, 0xad, 171, 0xae, 172, 0xc4, 173, 0xaf, 174, 0xb0, 175, 0xb1 // }; String RusStrLCD(String StrIn) { String StrOut=""; byte b1; byte y; byte l=StrIn.length(); for(byte i=0; i<l; i++) { b1=StrIn.charAt(i); if (b1<128) StrOut=StrOut+char(b1); else { if (b1==208) //==208, 2- . { b1=StrIn.charAt(i+1); for(y=0; y<33; y++) if(MasRus[y][0]==b1) { StrOut=StrOut+char(MasRus[y][1]); break; } } i++; } } return StrOut; }
Das ist alles für das hausgemachte HMI mit dem i2c-Bus.
Oh ja, am Anfang des Artikels schrieb ich, dass ich HMI nicht ganz für Arduino mache, sondern für Arduino-kompatible Geräte. Hier geht es um die SPS
CONTROLLINO MAXI , die von der Arduino IDE (und vielen anderen) programmiert wird.

CONTROLLINO MAXI ist eigentlich Arduino + eine Reihe von Abschirmungen und alles ist als industrielle SPS konzipiert. Aber über ihn beim nächsten Mal.
Referenzen→
Archiv mit Diagrammen, Skizzen und einer Leiterplatte im Lay6-Format
→
Arduino-kompatible SPS CONTROLLINO , deren Arbeit die Entwicklung des HMI i2c inspirierte
→
PCF8574 Port Expander und Anschluss an Arduino
→
FC-113- Karte für den Betrieb des LCD 1602 über den i2c-Bus und dessen Verbindung zu Arduino
→
Mehrstufiges Baummenü , allgemeine Prinzipien der Erstellung in C.
→
UTF-8 codieren→
Windows-1251 codieren