Painel do operador (IHM) com barramento I2C para Arduino

Como parte do trabalho com alguns equipamentos compatíveis com arduino (sobre isso no final), eu precisava de uma tela com botões para controlar e exibir informações atuais. Ou seja, o painel do operador era necessário, também é HMI.

Foi decidido fazer a HMI de forma independente e usar o barramento i2c "quadrado" como uma interface.



Se você estiver interessado no processo de desenvolvimento e programação de tais dispositivos, bem-vindo ao gato.

Características

  • Exibir 1602, caracteres monocromáticos de 16x2
  • 5 botões: cima, baixo, cancelar, entrar, editar
  • Interface I2c
  • Conector DB9F
  • Dimensões 155x90x44 mm

Existem perguntas óbvias:

Por que não comprar um escudo pronto?
Obviamente, era possível comprar um escudo pronto do mesmo chinês com uma tela e teclado, e assim:

Você pode soldar 2 lenços FC-113 nesse escudo e ele funcionará da mesma forma que o meu: um monitor com um teclado trabalhando no i2c. O preço do conjunto será de US $ 4.

Mas nesta placa, o tamanho dos botões não combina comigo, mas eu queria grandes, com a capacidade de instalar bonés multicoloridos. Eu não queria conectar o Arduino à HMI usando o conector DB9F normal, mas era necessário fazer uma placa de conexão. E, neste caso, qual é a diferença, criando um tabuleiro ou dois? Além disso, eu já tinha vários monitores de 1602 em estoque, então só precisei gastar US $ 1,02 para comprar o FC-113 (US $ 0,55) e o expansor de porta PCF8574P (US $ 0,47) no Aliexpress.

Bem, e mais importante, se você está lidando com o Arduino, a fabricação independente de escudos para ele é uma questão de curso, certo?

Por que o barramento i2c não é mais fácil conectar os botões diretamente?
No campo dos sistemas de controle industrial, onde trabalho, as IHMs usam interfaces de transmissão de dados digitais RS-232, RS-485, CAN etc. para se comunicar com os dispositivos. Portanto, é lógico para mim que minha IHM caseira funcione em uma interface de dados, neste caso, no i2c.

Se eu fizesse um dispositivo em que o monitor funcionasse em um barramento quadrado e os botões fossem diretamente para a entrada do Arduino, isso me faria sentir profundamente insatisfeito. Como apresentarei esta figura: um cabo separado se destaca do painel para a interface, fios separados para a entrada, brrrr ...

Além disso, a diferença entre a placa do botão que vai diretamente para as entradas do Arduino e a placa do botão com a interface i2c está apenas no chip PCF8574P (US $ 0,47), um capacitor e dois resistores.

Por que os botões estão organizados assim e não de outra forma?
Os botões da esquerda para a direita têm as seguintes funções: cima, baixo, cancelar, entrar, editar.
O botão “editar” é retirado um pouco dos outros para enfatizar sua função - alterando os valores dos parâmetros lógicos (ligado / desligado) ou alternando para o modo de edição no caso de parâmetros numéricos.

Existem 5 botões no total, embora o chip na placa do teclado permita conectar até 8 peças.
Seria o suficiente para quatro botões e a funcionalidade não seria afetada - “entrada” e “edição” podem ser combinadas em um botão. Mas fiquei com pena de que metade das 8 pernas do microcircuito expansor de portas não estivesse envolvida.
Outro botão "editar" separado pode ser útil se eu decidir exibir vários parâmetros em uma linha. Então, com este botão, você pode alternar entre os parâmetros, indicando qual deles precisa ser alterado. É assim que o botão "SET" funciona na popular HMI OP320 chinesa.

Se os dois primeiros botões significam para cima e para baixo, por que não colocá-los na vertical, como, por exemplo, é feito no escudo chinês acima?

Para mim, pessoalmente, é mais conveniente quando todos os botões estão na horizontal; depois, durante a operação, os dedos se movem apenas em um plano.

Ferro






1. Backplane caseiro com conector DB9F. Então, quando pegamos uma fonte de alimentação de + 5V para os expansores de porta e display do Arduino, coloquei um fusível de 0,1 A na placa

2. Todos conhecemos o conhecido monitor 1602 com uma placa soldada FC-113 que conecta o monitor ao barramento i2c.

3. Uma placa de teclado caseira com um chip PCF8574P, que lerá o status dos botões e os transmitirá pelo barramento i2c. A propósito, a placa “display” do FC-113 também é baseada no chip PCF8574, apenas com o índice T, ou seja, planar, não DIP, como PCF8574P.

Botões Coloquei 12h12mm com um empurrador quadrado; neles você pode usar grandes capas multicoloridas.

Fotos e esquemas de placas de circuito caseiras






Vale a pena dizer algumas palavras sobre o chip PCF8574P, com base no qual fiz uma placa de teclado.
O PCF8574P é um extensor de porta i2c. Existem 8 portas no total, cada uma das quais pode ser configurada para funcionar como entrada ou saída. Para este chip e a cinta, como tal, não é necessário (lembre-se, por exemplo, max232), apenas no caso de eu colocar um capacitor de energia.

O endereço do chip PCF8574P é definido usando as pernas de endereço A0, A1, A2, que são puxadas para o chão ou para a fonte de alimentação através de um resistor de 10 kΩ.

Na placa do teclado, coloquei todas as pernas de endereço do PCF8574P no chão, para que o endereço seja codificado como 0x20 e você não possa alterá-lo.

Como já escrevi, escolhi o DB9F como o conector para a HMI. O Arduino recebe sinais dele +5 V, GND, SDA, SCL.



O fio para comunicação via i2c Arduino e HMI tem 1,4 m de comprimento e funciona sem falhas.

Eu desenhei as placas no Sprint Layout 6, as transferi para o textolite usando o método LUT e as gravei em uma solução de peróxido e ácido cítrico.

Um pouco sobre decapagem
Existem muitas receitas para gravar placas de ácido cítrico em fibra de vidro.

Eu fiz esta solução: 100 ml de peróxido de hidrogênio a 3%, 50 g de ácido cítrico, 3 colheres de chá de sal. Ele aqueceu o frasco de peróxido em uma panela com água a uma temperatura de cerca de 70 graus.

Mergulhamos o cartão na solução com o padrão para baixo, conforme recomendado quando gravado com peróxido.
Depois de algumas dezenas de segundos, um processo tempestuoso começa. É liberado muito vapor, o que não é recomendado para inalação. Provavelmente.



Então o processo desaparece. Vire o quadro.



Feito.



O estojo foi fabricado com um amigo da Plexiglas de 4 mm em uma máquina de corte a laser.

Digressão lírica sobre o corpo
Compre um estojo acabado ou faça você mesmo? Depois de um pouco de reflexão, decidi fazer isso sozinho. Aqueles que vi à venda não eram adequados para mim nem por preço ou por razões estéticas, ou estavam em um trilho DIN, o que também não era adequado para mim.

Inicialmente, o corpo queria cortar madeira compensada. Mas então lembrei que tinha um amigo maravilhoso e, pela minha grande alegria, o diretor de uma empresa de prêmios esportivos. Ele tem todos os tipos de máquinas, inclusive para corte a laser.

Ele pediu ajuda e um amigo não recusou, por alguns minutos as peças foram cortadas com um laser.

Aproveito a oportunidade para dizer obrigado, Kolya! Caso contrário, eu teria que cortar e moer madeira compensada o dia todo, e o resultado dificilmente seria tão brilhante.

Programação


Do ponto de vista do Arduino, esta HMI consiste em 2 dispositivos que operam no barramento i2c: um display (LCD) com o endereço 0x27 e um teclado com o endereço 0x20. Consequentemente, o Arduino funcionará separadamente com o teclado e separadamente com o LCD.

O trabalho com o LCD é realizado através de uma biblioteca especial "LiquidCrystal_I2C.h", deve ser instalado no Aduino IDE.

O trabalho com o teclado é realizado através da biblioteca Wire.h padrão, que está inicialmente disponível no Aduino IDE.

Conectamos a HMI a Ardiuno.



1. Primeiro, verifique se o Arduino vê nossa IHM. Para fazer isso, carregue o programa nele, que verificará o barramento i2c quanto a dispositivos nele.

Esboço 1, verificação de barramento 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); } 


Durante a execução deste programa, o Arduino gravará os resultados da varredura do barramento i2c na porta serial. Para visualizar esses dados, vá para Arduino IDE Tools-> Port Monitor.



Vimos que o Arduino no barramento i2c identificou dois dispositivos com os endereços 0x20 e 0x27, esses são o teclado e o LCD, respectivamente.

2. Agora vamos ver como o nosso teclado funciona. Crie um programa que irá pesquisar o estado dos botões e exibi-lo no LCD.

Esboço 2, exibe o status dos botões
 /*   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); } 




O teclado está funcionando.

3. Finalmente, você pode prosseguir para o que tudo estava fazendo, criando um menu de vários níveis no Arduino. Através do menu, não apenas assistiremos às informações, mas também controlaremos as saídas do próprio Arduino.





No nete, há muitas informações sobre a criação de um menu de vários níveis em C ++, mas para o Arduino, eu até vi algumas bibliotecas. Mas eu decidi no meu programa escrever o menu pessoalmente. Em primeiro lugar, quanto menos bibliotecas sobrarem no projeto, mais calmo. E segundo, é simples.

Eu tenho outra variação do menu da árvore. O menu permite exibir em cada linha ao mesmo tempo o texto estático e o valor da variável. Por exemplo, você pode exibir o nome do parâmetro e seu valor.

Para exibir variáveis ​​na tela, aplico o princípio das tags - de certa maneira, as etiquetas de texto projetadas no texto, em vez das quais, quando o texto é exibido na tela, um valor é exibido.

Os parâmetros podem ser alterados pressionando o botão "Editar". Além disso, a tag de cada parâmetro indica se está disponível para edição ou somente leitura. Se o parâmetro atual for somente leitura, no início da linha o ponteiro será '*'; se a edição estiver ativada, o ponteiro se tornará '+'.

Esboço 3, menu de vários níveis
 /*  ,    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 e problema de idioma


Separadamente, é necessário levantar a questão da russificação.

No gerador de caracteres de alguns LCD 1602 não há letras russas e, em vez delas, krakozyabry japonês é costurado. É impossível atualizar o gerador de caracteres. Portanto, você precisará escrever palavras na tela em letras latinas ou no programa para formar as letras russas, porque O LCD 1602 tem a capacidade de criar e armazenar seus próprios caracteres na RAM RAM. Mas, neste último caso, você pode exibir na tela não mais do que oito caracteres "caseiros" por vez.

Gráfico de caracteres LCD 1602




Em princípio, não há problema em escrever palavras russas em inglês no LCD. Lá, mesmo a venerável empresa francesa Shneider Electric (a que vendeu obuses até o czar antes da revolução) por uma década e meia não conseguiu introduzir o russo em seus famosos relés programáveis ​​Zelio. Mas isso não os impede de negociar ativamente a vastidão de toda a CEI. Além disso, canais, espanhol e português introduzidos.

Em muitas de nossas fábricas, esses Zelio se comunicam com a equipe com frases como "NASOS 1 VKL".

Quando não está claro se há letras russas em um LCD específico, é necessário exibir todos os caracteres de seu gerador de caracteres na tela. Se houver cirílico, ele começa na posição 160.

Desenho 4, exibindo todos os caracteres da tabela do gerador de caracteres 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); } } } 


Mas mesmo que seu LCD 1602 seja russo, exibir palavras em russo não é tão simples. Pelo menos, usando a biblioteca LiquidCrystal_I2C.h ao trabalhar com LCD no barramento i2c.

Se você simplesmente imprimir o texto em russo, por exemplo, com a instrução lcd.print ("Olá !!!"), em vez de "Olá !!!" algum lixo aparecerá na tela.

Isso ocorre porque o IDE do Arduino converte letras russas em código UTF-8 de dois bytes, e no LCD todos os caracteres são de byte único.

A propósito, o mesmo problema é observado ao transferir textos em russo do Arduino para o monitor de porta IDE do Arduino. O Arduino envia letras russas na codificação UTF-8 de dois bytes para a porta serial, e o monitor de porta IDE do Arduino tenta lê-las na codificação Windows-1251 de byte único (cp1251). Embora o cp1251 também seja de 8 bits, como a codificação LCD 1602, ele não corresponde a ele.

Você pode formar textos em russo através de códigos de caracteres. Por exemplo, a linha 'display LCD' em um LCD russificado será exibida assim:

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

Mas eu não gosto dessa abordagem.

Para exibir corretamente o texto em russo no LCD Russified 1602, várias bibliotecas foram inventadas para o Arduino. Mas depois de ler os comentários, vi que muitos reclamam de falhas ao usá-los.

Portanto, no meu programa de menu multinível, eu mesmo escrevi uma função simples para converter códigos UTF-8 em LCD. É verdade que fiz isso apenas com letras maiúsculas russas, o que combina perfeitamente comigo.

Função para converter letras russas maiúsculas UTF-8 em código LCD 1602 de byte único
 /* *  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; } 


Isso é tudo para a IHM caseira com o barramento i2c.

Ah, sim, no começo do artigo, escrevi que fazia HMI não exatamente para o Arduino, mas para equipamentos compatíveis com o arduino. Este sou eu sobre o PLC CONTROLLINO MAXI , que é programado a partir do Arduino IDE (e muitos outros).



CONTROLLINO MAXI é na verdade Arduino + um monte de escudos e tudo é projetado como um PLC industrial. Mas sobre ele na próxima vez.

Referências

Arquivar com diagramas, esboços e uma placa de circuito impresso no formato lay6
PLC CONTROLLINO compatível com Arduino , o trabalho com o qual inspirou a criação da HMI i2c
Expansor de porta PCF8574 e conectá-lo ao Arduino
Placa FC-113 para operação do LCD 1602 via barramento i2c e sua conexão com o Arduino
Menu em árvore multinível , princípios gerais de criação em C
Codificação UTF-8
Codificação do Windows-1251

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


All Articles