Sebagai bagian dari bekerja dengan beberapa peralatan yang kompatibel dengan Arduino (tentang hal itu pada akhirnya), saya membutuhkan layar dengan tombol untuk mengontrol dan menampilkan informasi saat ini. Artinya, panel operator diperlukan, itu juga HMI.
Diputuskan untuk membuat HMI secara independen, dan menggunakan bus i2c "persegi" sebagai antarmuka.

Jika Anda tertarik dalam proses pengembangan dan pemrograman perangkat tersebut, selamat datang di cat.
Karakteristik
- Tampilan 1602, karakter monokrom 16x2
- 5 tombol: atas, bawah, batalkan, masuk, edit
- Antarmuka I2c
- Konektor DB9F
- Dimensi 155x90x44 mm
Ada beberapa pertanyaan yang jelas:
Mengapa tidak membeli perisai siap pakai?Tentu saja, itu mungkin untuk membeli perisai siap pakai dari Cina yang sama dengan layar dan keyboard, dan seperti ini:

Anda dapat menyolder 2 selendang FC-113 ke perisai ini dan akan berfungsi secara fungsional seperti milik saya: sebuah tampilan dengan keyboard yang bekerja pada i2c. Harga set akan mulai dari $ 4.
Tapi di papan ini, ukuran tombol tidak cocok untuk saya, tapi saya ingin yang besar dengan kemampuan untuk menginstal topi multi-warna. Saya tidak ingin menghubungkan Arduino ke HMI menggunakan konektor DB9F normal, tetapi perlu membuat papan koneksi. Dan dalam hal ini, apa bedanya, membuat satu atau dua papan? Selain itu, saya sudah memiliki beberapa 1602 display dalam stok, jadi saya hanya perlu menghabiskan $ 1,02 untuk membeli FC-113 ($ 0,55) dan expander port PCF8574P ($ 0,47) di Aliexpress.
Nah, dan yang paling penting, jika Anda berurusan dengan Arduino, maka pembuatan perisai independen untuknya tentu saja, kan?
Mengapa bus i2c, bukankah lebih mudah untuk menghubungkan tombol secara langsung?Di bidang sistem kontrol industri, tempat saya bekerja, HMI menggunakan antarmuka transmisi data digital RS-232, RS-485, CAN, dll. Untuk berkomunikasi dengan perangkat. Oleh karena itu, logis bagi saya bahwa HMI buatan saya semua akan bekerja pada antarmuka data, dalam hal ini, pada i2c.
Jika saya membuat perangkat di mana tampilan bekerja pada bus persegi, dan tombol langsung ke input Arduino, itu akan membuat saya merasa sangat tidak puas. Saat saya akan menyajikan gambar ini: kabel terpisah menjulur dari panel ke antarmuka, secara terpisah kabel ke input, brrrr ...
Selain itu, perbedaan antara papan tombol yang langsung ke input Arduino dan papan tombol dengan antarmuka i2c hanya dalam chip PCF8574P ($ 0,47), sebuah kapasitor dan dua resistor.
Mengapa tombol disusun seperti ini dan bukan sebaliknya?Tombol-tombol dari kiri ke kanan memiliki fungsi-fungsi berikut: atas, bawah, batalkan, masuk, edit.
Tombol "edit" diambil dari yang lain sedikit ke samping untuk menekankan fungsinya - mengubah nilai-nilai parameter logis (on / off) atau beralih ke mode edit jika ada parameter numerik.
Ada 5 tombol secara total, meskipun chip pada papan keyboard memungkinkan Anda untuk menghubungkan hingga 8 buah.
Ini akan cukup untuk dilakukan dengan empat tombol dan fungsi tidak akan terpengaruh - "input" dan "pengeditan" dapat dikombinasikan dalam satu tombol. Tapi saya hanya merasa menyesal bahwa setengah dari 8 kaki port expander microcircuit tidak akan terlibat.
Tombol "edit" lain yang terpisah dapat berguna jika saya memutuskan untuk menampilkan beberapa parameter dalam satu baris. Kemudian dengan tombol ini Anda dapat beralih di antara parameter, yang menunjukkan mana yang perlu diubah. Inilah cara tombol "SET" bekerja di HMI OP320 Cina yang populer.
Jika dua tombol pertama berarti naik dan turun, lalu mengapa tidak menempatkannya secara vertikal, seperti, misalnya, dilakukan di perisai Cina di atas?
Bagi saya pribadi, akan lebih nyaman ketika semua tombol horisontal, maka selama operasi jari hanya bergerak dalam satu bidang.
Besi


1. Backplane buatan sendiri dengan konektor DB9F. Jadi, saat kami mengambil catu daya + 5V untuk port dan menampilkan ekspander dari Arduino, saya meletakkan sekering 0,1 A di papan tulis
2. Kita semua tahu layar 1602 yang terkenal dengan papan solder FC-113 yang menghubungkan layar ke bus i2c.
3. Papan keyboard buatan sendiri dengan chip PCF8574P, yang akan membaca status tombol dan mengirimkannya melalui bus i2c. Omong-omong, papan "display" FC-113 juga didasarkan pada chip PCF8574, hanya dengan indeks T, mis. planar, bukan DIP, seperti PCF8574P.
Tombol I menempatkan 12h12mm dengan pendorong persegi, pada mereka Anda bisa mengenakan topi multi-warna besar.
Foto dan skema papan sirkuit buatan sendiri Perlu mengatakan beberapa kata tentang chip PCF8574P, atas dasar yang saya buat papan keyboard.
PCF8574P adalah extender port i2c. Ada total 8 port, yang masing-masing dapat dikonfigurasi untuk berfungsi sebagai input atau output. Untuk chip ini dan pengikat seperti itu tidak diperlukan (ingat, misalnya, max232), kalau-kalau saya meletakkan kapasitor untuk daya.
Alamat chip PCF8574P diatur menggunakan kaki alamat A0, A1, A2, yang ditarik ke tanah atau ke catu daya melalui resistor 10 kΩ.
Di papan keyboard, saya meletakkan semua kaki alamat pada PCF8574P di tanah, sehingga alamat tersebut di-hardcode sebagai 0x20 dan Anda tidak dapat mengubahnya.
Seperti yang sudah saya tulis, saya memilih DB9F sebagai konektor untuk HMI. Arduino menerima sinyal darinya +5 V, GND, SDA, SCL.

Kabel untuk komunikasi via i2c Arduino dan HMI dibuat sepanjang 1,4 m, ia bekerja tanpa gangguan.
Saya menggambar papan di Sprint Layout 6, memindahkannya ke textolite menggunakan metode LUT dan menatanya dalam larutan peroksida dan asam sitrat.
Sedikit tentang acarAda banyak resep untuk etsa papan asam sitrat pada fiberglass foil.
Saya membuat solusi ini: 100 ml hidrogen peroksida 3%, 50 g asam sitrat, 3 sendok teh garam. Dia menghangatkan tabung peroksida dalam panci dengan air hingga suhu sekitar 70 derajat.
Kami merendam papan dalam larutan dengan pola turun, seperti yang direkomendasikan ketika dietsa dengan peroksida.
Setelah beberapa detik, proses badai dimulai. Banyak uap yang dikeluarkan, yang tidak direkomendasikan untuk dihirup. Mungkin

Kemudian prosesnya reda. Balikkan papan.

Selesai

Kasing dibuat dari seorang teman dari Plexiglas 4 mm pada mesin pemotong laser.
Penyimpangan liris tentang korpsBeli kasing yang sudah jadi atau lakukan sendiri? Setelah berpikir sebentar, saya memutuskan untuk melakukannya sendiri. Barang-barang yang saya lihat dijual tidak cocok untuk saya karena harga atau karena alasan estetika, atau berada di rel DIN, yang juga tidak cocok untuk saya.
Awalnya, tubuh ingin dipotong dari kayu lapis. Tetapi kemudian saya ingat bahwa saya memiliki seorang teman yang luar biasa dan, dengan penuh kegembiraan saya, direktur sebuah perusahaan penghargaan olahraga. Dia memiliki semua jenis mesin di sana, termasuk untuk memotong laser.
Dia meminta bantuan dan seorang teman tidak menolak, selama beberapa menit bagian-bagian dipotong dengan laser.
Saya mengambil kesempatan ini untuk mengucapkan terima kasih, Kolya! Kalau tidak, saya harus memotong dan menggiling kayu lapis sepanjang hari, dan hasilnya tidak akan terlalu cemerlang.
Pemrograman
Dari sudut pandang Arduino, HMI ini terdiri dari 2 perangkat yang beroperasi pada bus i2c: layar (LCD) dengan alamat 0x27 dan keyboard dengan alamat 0x20. Karenanya, Arduino akan bekerja secara terpisah dengan keyboard dan secara terpisah dengan LCD.
Bekerja dengan LCD dilakukan melalui perpustakaan khusus "LiquidCrystal_I2C.h", itu harus dipasang di Aduino IDE.
Bekerja dengan keyboard dilakukan melalui pustaka Wire.h standar, yang awalnya tersedia di Aduino IDE.
Kami menghubungkan HMI ke Ardiuno.

1. Pertama, periksa apakah Arduino melihat HMI kami. Untuk melakukan ini, muat program ke dalamnya, yang akan memindai bus i2c untuk perangkat di dalamnya.
Sketsa 1, pemindaian 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); }
Selama pelaksanaan program ini, Arduino akan menulis hasil pemindaian bus i2c ke port serial. Untuk melihat data ini, buka Arduino IDE Tools-> Port Monitor.

Kita melihat bahwa Arduino pada bus i2c mengidentifikasi dua perangkat dengan alamat 0x20 dan 0x27, masing-masing adalah keyboard dan LCD.
2. Sekarang mari kita lihat cara kerja keyboard kita. Buat program yang akan memilih status tombol dan menampilkannya pada LCD.
Sketsa 2, tampilkan status tombol #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); }

Keyboard berfungsi.
3. Akhirnya, Anda dapat beralih ke apa yang semuanya lakukan, membuat menu multi-level di Arduino. Melalui menu kami tidak hanya akan menonton informasi, tetapi juga mengontrol output Arduino sendiri.

Di nete ada banyak informasi tentang cara membuat menu multi-level di C ++, tetapi untuk Arduino saya bahkan melihat beberapa perpustakaan. Tetapi saya memutuskan dalam program saya untuk menulis menu sendiri. Pertama, semakin sedikit perpustakaan yang tersisa di proyek, lebih tenang. Dan kedua, itu sederhana.
Saya mendapat variasi lain dari menu pohon. Menu ini memungkinkan Anda untuk menampilkan di setiap baris pada saat yang bersamaan teks statis dan nilai variabel. Misalnya, Anda dapat menampilkan nama parameter dan nilainya.
Untuk menampilkan variabel di layar, saya menerapkan prinsip tag - dengan cara tertentu mendesain label teks dalam teks, alih-alih, ketika teks ditampilkan di layar, nilai ditampilkan.
Parameter dapat diubah dengan menekan tombol "Edit". Selain itu, tag setiap parameter menunjukkan apakah tersedia untuk diedit atau hanya-baca. Jika parameter saat ini hanya-baca, pada awal baris pointer akan menjadi '*'; jika pengeditan diaktifkan, pointer akan menjadi '+'.
Sketsa 3, menu multi-level /* , 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 dan masalah bahasa
Secara terpisah, perlu untuk mengangkat masalah Russification.
Dalam generator karakter beberapa LCD 1602 tidak ada huruf Rusia, dan alih-alih itu krakozyabry Jepang dijahit. Tidak mungkin untuk merombak generator karakter. Oleh karena itu, Anda harus menulis kata-kata di layar dalam huruf Latin, atau dalam program untuk membentuk huruf Rusia sendiri, karena LCD 1602 memiliki kemampuan untuk membuat dan menyimpan karakter Anda sendiri dalam RAM RAM. Namun, dalam kasus yang terakhir, Anda dapat menampilkan di layar tidak lebih dari delapan karakter "buatan sendiri" sekaligus.
Pada prinsipnya, tidak apa-apa menulis kata-kata Rusia dalam bahasa Inggris di LCD. Di sana, bahkan perusahaan Prancis terhormat Shneider Electric (yang menjual howitzer ke tsar bahkan sebelum revolusi) selama satu setengah dekade tidak dapat memperkenalkan Rusia ke relay Zelio yang terkenal dan dapat diprogram. Tapi ini tidak menghalangi mereka untuk aktif berdagang di luasnya CIS. Apalagi kanal, Spanyol dan Portugis diperkenalkan.
Di banyak pabrik kami, Zelio ini berkomunikasi dengan staf dengan frasa seperti "NASOS 1 VKL".
Ketika tidak jelas apakah ada huruf Rusia di LCD tertentu, Anda harus menampilkan semua karakter generator karakternya di layar. Jika ada Cyrillic, itu dimulai dari posisi 160.
Sketsa 4, menampilkan semua karakter dari tabel generator karakter LCD 1602 #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); } } }
Tetapi bahkan jika LCD 1602 Anda adalah Russified, menampilkan kata-kata Rusia tidak begitu sederhana. Setidaknya, menggunakan perpustakaan LiquidCrystal_I2C.h saat bekerja dengan LCD pada bus i2c.
Jika Anda hanya menampilkan teks Rusia, misalnya, dengan instruksi lcd.print ("Halo !!!"), maka alih-alih "Halo !!!" beberapa sampah akan muncul di layar.
Ini karena Arduino IDE menerjemahkan huruf-huruf Rusia ke dalam kode UTF-8 dua-byte, dan dalam LCD semua karakter adalah satu-byte.
Masalah yang sama, omong-omong, diamati ketika mentransfer teks-teks Rusia dari Arduino ke monitor port Arduino IDE. Arduino mengirimkan huruf Rusia dalam pengkodean byte ganda UTF-8 ke port serial, dan monitor port IDE Arduino mencoba membacanya dalam pengkodean Windows-1251 bita tunggal (cp1251). Meskipun cp1251 juga 8-bit, seperti pengkodean LCD 1602, itu tidak cocok dengannya.
Anda dapat membentuk teks-teks Rusia melalui kode karakter. Sebagai contoh, garis 'layar LCD' pada LCD yang teridentifikasi akan ditampilkan seperti ini:
lcd.print("\243K \343\270c\276\273e\271");
Tapi saya tidak suka pendekatan ini.
Untuk menampilkan teks Rusia dengan benar pada LCD 1602 Russified, beberapa perpustakaan diciptakan untuk Arduino. Tapi setelah membaca ulasan, saya melihat banyak yang mengeluh tentang gangguan saat menggunakannya.
Oleh karena itu, dalam program menu multi-level saya, saya sendiri menulis fungsi sederhana untuk mengubah kode UTF-8 menjadi LCD. Benar, saya melakukannya hanya untuk huruf besar Rusia, yang sangat cocok untuk saya.
Fungsi untuk mengubah huruf Rusia UTF-8 huruf besar ke kode 160-byte LCD 1602 tunggal /* * 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; }
Itu semua untuk HMI buatan sendiri dengan bus i2c.
Oh ya, di awal artikel saya menulis bahwa saya melakukan HMI tidak cukup untuk Arduino, tetapi untuk peralatan yang kompatibel dengan Arduino. Inilah saya tentang PLC
CONTROLLINO MAXI , yang diprogram dari Arduino IDE (dan banyak lainnya).

CONTROLLINO MAXI sebenarnya adalah Arduino + banyak perisai dan semuanya dirancang sebagai PLC industri. Tapi tentang dia lain kali.
Referensi→
Arsipkan dengan diagram, sketsa dan papan sirkuit tercetak dalam format lay6
→
PLC KONTROLLINO yang kompatibel dengan Arduino , karya yang mengilhami penciptaan HMI i2c
→
PCF8574 port expander dan menghubungkannya ke Arduino
→
papan FC-113 untuk operasi LCD 1602 melalui bus i2c dan koneksinya ke Arduino
→
Menu pohon bertingkat , prinsip-prinsip umum penciptaan di C
→
Pengkodean UTF-8→
Pengkodean Windows-1251