Panneau opérateur (IHM) avec bus I2C pour Arduino

Dans le cadre de l'utilisation de certains équipements compatibles Arduino (à ce sujet à la fin), j'avais besoin d'un écran avec des boutons pour contrôler et afficher les informations actuelles. Autrement dit, le panneau de commande était nécessaire, il est également HMI.

Il a été décidé de faire de l'IHM indépendamment et d'utiliser le bus i2c «carré» comme interface.



Si vous êtes intéressé par le processus de développement et de programmation de tels appareils, bienvenue à cat.

CARACTÉRISTIQUES

  • Affichage 1602, caractères monochromes 16x2
  • 5 boutons: haut, bas, annuler, entrer, éditer
  • Interface I2c
  • Connecteur DB9F
  • Dimensions 155x90x44 mm

Il y a des questions évidentes:

Pourquoi ne pas acheter un bouclier prêt à l'emploi?
Bien sûr, il était possible d'acheter un bouclier prêt à l'emploi du même chinois avec un écran et un clavier, et comme celui-ci:

Vous pouvez souder 2 écharpes FC-113 à ce bouclier et il se révélera être le même que le mien: un écran avec un clavier fonctionnant sur i2c. Le prix de l'ensemble sera de 4 $.

Mais sur cette planche, la taille des boutons ne me convient pas, mais je voulais des gros avec la possibilité d'installer des bouchons multicolores. Je ne voulais pas connecter l'Arduino à l'IHM en utilisant le connecteur DB9F normal, mais il fallait faire une carte de connexion. Et dans ce cas, quelle est la différence, faire une ou deux planches? De plus, j'avais déjà plusieurs écrans 1602 en stock, donc je n'avais qu'à dépenser 1,02 $ pour acheter le FC-113 (0,55 $) et l'extension de port PCF8574P (0,47 $) sur Aliexpress.

Eh bien, et surtout, si vous avez affaire à Arduino, la fabrication indépendante de boucliers pour lui est une évidence, non?

Pourquoi le bus i2c, n'est-il pas plus facile de connecter directement les boutons?
Dans le domaine des systèmes de contrôle industriels, où je travaille, les IHM utilisent des interfaces de transmission de données numériques RS-232, RS-485, CAN, etc. pour communiquer avec les appareils. Par conséquent, il est logique pour moi que mon IHM maison fonctionne toutes sur une interface de données, dans ce cas, sur i2c.

Si je fabriquais un appareil où l'affichage fonctionne sur un bus carré et que les boutons vont directement à l'entrée d'Arduino, cela me ferait me sentir profondément insatisfait. Comme je vais vous présenter cette photo: un cordon séparé sort du panneau de l'interface, des fils séparés à l'entrée, brrrr ...

De plus, la différence entre la carte de boutons qui va directement aux entrées Arduino et la carte de boutons avec l'interface i2c se trouve uniquement dans la puce PCF8574P (0,47 $), un condensateur et deux résistances.

Pourquoi les boutons sont-ils disposés ainsi et pas autrement?
Les boutons de gauche à droite ont les fonctions suivantes: haut, bas, annuler, entrer, éditer.
Le bouton «éditer» est légèrement éloigné des autres pour souligner sa fonction - changer les valeurs des paramètres logiques (marche / arrêt) ou passer en mode édition en cas de paramètres numériques.

Il y a 5 boutons au total, bien que la puce sur la carte du clavier vous permette de connecter jusqu'à 8 pièces.
Il suffirait de faire avec quatre boutons et la fonctionnalité ne serait pas affectée - "entrée" et "édition" peuvent être combinés en un seul bouton. Mais je me suis simplement senti désolé que la moitié des 8 jambes du microcircuit d'extension de port ne soient pas impliquées.
Un autre bouton «modifier» séparé peut être utile si je décide d'afficher plusieurs paramètres sur une seule ligne. Ensuite, avec ce bouton, vous pouvez basculer entre les paramètres, indiquant lequel doit être modifié. C'est ainsi que le bouton «SET» fonctionne dans le populaire HMI chinois OP320.

Si les deux premiers boutons signifient haut et bas, alors pourquoi ne pas les placer verticalement, comme, par exemple, cela se fait dans le bouclier chinois ci-dessus?

Pour moi personnellement, c'est plus pratique lorsque tous les boutons sont horizontaux, puis pendant le fonctionnement, les doigts ne se déplacent que dans un seul plan.

Le fer






1. Fond de panier maison avec connecteur DB9F. Donc, comme nous prenons une alimentation + 5V pour les extensions de port et d'affichage d'Arduino, j'ai mis un fusible de 0,1 A sur la carte

2. Nous connaissons tous le célèbre écran 1602 avec une carte soudée FC-113 qui relie l'écran au bus i2c.

3. Une carte de clavier faite maison avec une puce PCF8574P qui lira l'état des boutons et les transmettra via le bus i2c. Soit dit en passant, la carte «d'affichage» FC-113 est également basée sur la puce PCF8574, uniquement avec l'indice T, c'est-à-dire plan, pas DIP, comme PCF8574P.

Boutons je mets 12h12mm avec un poussoir carré; sur eux on peut porter de gros bonnets multicolores.

Photos et schémas de circuits imprimés faits maison






Il vaut la peine de dire quelques mots sur la puce PCF8574P, sur la base de laquelle j'ai fait une carte de clavier.
Le PCF8574P est un prolongateur de port i2c. Il y a 8 ports au total, chacun pouvant être configuré pour fonctionner comme entrée ou sortie. Pour cette puce et le cerclage en tant que tel n'est pas nécessaire (rappelez-vous, par exemple, max232), juste au cas où je mettrais un condensateur pour l'alimentation.

L'adresse de la puce PCF8574P est définie à l'aide des pattes d'adresse A0, A1, A2, qui sont tirées à la masse ou à l'alimentation via une résistance de 10 kΩ.

Sur la carte du clavier, j'ai mis toutes les jambes d'adresse du PCF8574P sur le sol, donc l'adresse est codée en dur comme 0x20 et vous ne pouvez pas la changer.

Comme je l'ai déjà écrit, j'ai choisi DB9F comme connecteur pour l'IHM. Arduino en reçoit les signaux +5 V, GND, SDA, SCL.



Le fil de communication via i2c Arduino et HMI fait 1,4 m de long, il fonctionne sans pépins.

J'ai dessiné les planches dans Sprint Layout 6, les ai transférées sur textolite en utilisant la méthode LUT et les ai gravées dans une solution de peroxyde et d'acide citrique.

Un peu de décapage
Il existe de nombreuses recettes pour graver des panneaux d'acide citrique sur une feuille de fibre de verre.

J'ai fait cette solution: 100 ml de peroxyde d'hydrogène à 3%, 50 g d'acide citrique, 3 cuillères à café de sel. Il a chauffé le pot de peroxyde dans une casserole avec de l'eau à une température d'environ 70 degrés.

Nous immergons la planche dans la solution avec le motif vers le bas, comme recommandé lors de la gravure avec du peroxyde.
Après quelques dizaines de secondes, un processus orageux commence. Beaucoup de vapeur est libérée, ce qui n'est pas recommandé d'être inhalé. Probablement.



Ensuite, le processus s'apaise. Retournez la planche.



C'est fait.



Le boîtier a été fabriqué par un ami en Plexiglas 4 mm sur une machine de découpe laser.

Digression lyrique sur le corps
Achetez un étui fini ou faites-le vous-même? Après un peu de réflexion, j'ai décidé de le faire moi-même. Ceux que j'ai vus en vente ne me convenaient ni pour le prix ni pour des raisons esthétiques, ou étaient sur un rail DIN, ce qui ne me convenait pas non plus.

Au départ, le corps voulait découper du contreplaqué. Mais je me suis alors souvenu que j'avais un merveilleux ami et, par ma grande joie, le directeur d'une entreprise de récompenses sportives. Il y a toutes sortes de machines, y compris pour la découpe laser.

Il a demandé de l'aide et un ami n'a pas refusé, pendant quelques minutes, les pièces ont été coupées au laser.

J'en profite pour vous dire merci, Kolya! Sinon, je devrais couper et moudre du contreplaqué toute la journée, et le résultat ne serait pas si brillant.

Programmation


Du point de vue d'Arduino, cette IHM se compose de 2 appareils qui fonctionnent sur le bus i2c: un écran (LCD) avec l'adresse 0x27 et un clavier avec l'adresse 0x20. Par conséquent, Arduino fonctionnera séparément avec le clavier et séparément avec l'écran LCD.

Le travail avec LCD est effectué via une bibliothèque spéciale "LiquidCrystal_I2C.h", il doit être installé dans l'IDE Aduino.

Le travail avec le clavier s'effectue via la bibliothèque Wire.h standard, initialement disponible dans l'IDE Aduino.

Nous connectons l'IHM à Ardiuno.



1. Tout d'abord, vérifiez si Arduino voit notre IHM. Pour ce faire, chargez-y le programme, qui recherchera des périphériques sur le bus i2c.

Croquis 1, balayage de bus i2c
//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); } 


Pendant l'exécution de ce programme, Arduino écrira les résultats de l'analyse du bus i2c sur le port série. Pour afficher ces données, allez dans Arduino IDE Tools-> Port Monitor.



Nous voyons qu'Arduino sur le bus i2c a identifié deux appareils avec les adresses 0x20 et 0x27, ce sont le clavier et l'écran LCD, respectivement.

2. Voyons maintenant comment fonctionne notre clavier. Créez un programme qui interroge l'état des boutons et l'affiche sur l'écran LCD.

Croquis 2, affichez l'état des boutons
 /*   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 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); } 




Le clavier fonctionne.

3. Enfin, vous pouvez passer à tout ce qui se passait: créer un menu à plusieurs niveaux dans Arduino. Grâce au menu, nous allons non seulement regarder les informations, mais également contrôler les sorties d'Arduino lui-même.





En nete, il y a beaucoup d'informations sur la création d'un menu à plusieurs niveaux en C ++, mais pour Arduino j'ai même vu quelques bibliothèques. Mais j'ai décidé dans mon programme d'écrire le menu moi-même. Premièrement, moins il y a de bibliothèques dans le projet, plus le calme est grand. Et deuxièmement, c'est simple.

J'ai obtenu une autre variante du menu arborescent. Le menu vous permet d'afficher dans chaque ligne à la fois du texte statique et la valeur de la variable. Par exemple, vous pouvez afficher le nom du paramètre et sa valeur.

Pour afficher des variables à l'écran, j'applique le principe des balises - d'une certaine manière, des étiquettes de texte conçues dans le texte, au lieu desquelles lorsque le texte est affiché à l'écran, une valeur est affichée.

Les paramètres peuvent être modifiés en appuyant sur le bouton «Modifier». De plus, la balise de chaque paramètre indique s'il est disponible en édition ou en lecture seule. Si le paramètre actuel est en lecture seule, au début de la ligne, le pointeur sera '*'; si l'édition est activée, le pointeur deviendra '+'.

Croquis 3, menu à plusieurs niveaux
 /*  ,    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 et problème de langue


Séparément, il est nécessaire de soulever la question de la russification.

Dans le générateur de caractères de certains LCD 1602, il n'y a pas de lettres russes, et au lieu de cela, les krakozyabry japonais sont cousus. Il est impossible de reflasher le générateur de caractères. Par conséquent, vous devrez soit écrire des mots à l'écran en lettres latines, soit dans le programme pour former vous-même les lettres russes, car LCD 1602 a la possibilité de créer et de stocker vos propres personnages dans la RAM RAM. Mais, dans ce dernier cas, vous ne pouvez afficher à l'écran que huit caractères "faits maison" à la fois.

Graphiques LCD 1602




En principe, il est normal d'écrire des mots russes en anglais sur l'écran LCD. Là-bas, même la vénérable société française Shneider Electric (celle qui a vendu des obusiers au tsar avant même la révolution) n'a pas été en mesure d'introduire le russe dans ses célèbres relais programmables Zelio pendant une décennie et demie. Mais cela ne les empêche pas de négocier activement l'immensité de l'ensemble de la CEI. De plus, des canaux, espagnol et portugais ont été introduits.

Dans beaucoup de nos usines, ces Zelio communiquent avec le personnel avec des phrases telles que «NASOS 1 VKL».

Lorsqu'il n'est pas clair s'il y a des lettres russes dans un LCD particulier, vous devez afficher tous les caractères de son générateur de caractères sur l'écran. S'il y a du cyrillique, il part de la position 160.

Croquis 4, affichant tous les caractères du tableau du générateur de caractères LCD 1602
 /*   LCD     * LCD    i2c */ #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); } } } 


Mais même si votre LCD 1602 est russifié, afficher des mots russes n'est pas si simple. Au moins, utiliser la bibliothèque LiquidCrystal_I2C.h lorsque vous travaillez avec l'écran LCD sur le bus i2c.

Si vous sortez simplement le texte russe, par exemple, avec l'instruction lcd.print ("Bonjour !!!"), alors au lieu de "Bonjour !!!" des déchets apparaîtront à l'écran.

En effet, l'IDE Arduino traduit les lettres russes en code UTF-8 à deux octets, et dans l'écran LCD, tous les caractères sont à un octet.

Soit dit en passant, le même problème est observé lors du transfert de textes russes d'Arduino vers le moniteur de port Arduino IDE. Arduino envoie des lettres russes dans le codage à deux octets UTF-8 au port série, et le moniteur de port Arduino IDE essaie de les lire dans le codage à un octet Windows-1251 (cp1251). Bien que le CP1251 soit également 8 bits, comme l'encodage LCD 1602, il ne correspond pas avec lui.

Vous pouvez former des textes russes à l'aide de codes de caractères. Par exemple, la ligne «écran LCD» sur un écran LCD russifié sera affichée comme ceci:

 lcd.print("\243K \343\270c\276\273e\271"); 

Mais je n'aime pas cette approche.

Pour afficher correctement le texte russe sur l'écran LCD russifié 1602, plusieurs bibliothèques ont été inventées pour Arduino. Mais après avoir lu les critiques, j'ai vu que beaucoup se plaignent de problèmes lors de leur utilisation.

Par conséquent, dans mon programme de menu à plusieurs niveaux, j'ai moi-même écrit une fonction simple pour convertir les codes UTF-8 en LCD. Certes, je ne l'ai fait que pour les majuscules russes, ce qui me convient parfaitement.

Fonction de conversion des lettres russes majuscules UTF-8 en code LCD 1602 à un octet
 /* *  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; } 


C'est tout pour l'IHM maison avec le bus i2c.

Oh oui, au début de l'article, j'ai écrit que je faisais de l'IHM pas tout à fait pour Arduino, mais pour un équipement compatible Arduino. Il s'agit de l'automate CONTROLLINO MAXI , qui est programmé à partir de l'IDE Arduino (et bien d'autres).



CONTROLLINO MAXI est en fait Arduino + un tas de boucliers et tout est conçu comme un PLC industriel. Mais à propos de lui la prochaine fois.

Les références

Archive avec schémas, croquis et une carte de circuit imprimé au format lay6
PLC CONTROLLINO compatible Arduino , dont le travail a inspiré la création de l'IHM i2c
Extension de port PCF8574 et connexion à Arduino
→ Carte FC-113 pour fonctionnement LCD 1602 via bus i2c et sa connexion à Arduino
Menu arborescent à plusieurs niveaux , principes généraux de création en C
Encodage UTF-8
Encodage Windows-1251

Source: https://habr.com/ru/post/fr401587/


All Articles