لوحة التشغيل (HMI) مع ناقل I2C لـ Arduino

كجزء من العمل مع بعض المعدات المتوافقة مع اردوينو (حولها في النهاية) ، كنت بحاجة إلى شاشة بأزرار للتحكم وعرض المعلومات الحالية. أي أن لوحة التشغيل كانت مطلوبة ، وهي أيضًا HMI.

تقرر جعل HMI بشكل مستقل ، واستخدام ناقل i2c "المربع" كواجهة.



إذا كنت مهتمًا بعملية تطوير وبرمجة مثل هذه الأجهزة ، فمرحباً بك في القط.

الخصائص

  • عرض 1602 ، أحادية اللون 16 × 2 حرفًا
  • 5 أزرار: لأعلى ولأسفل وإلغاء وإدخال وتعديل
  • واجهة I2c
  • موصل DB9F
  • الأبعاد 155x90x44 ملم

هناك أسئلة واضحة:

لماذا لا تشتري درعا جاهزا؟
, c :

2 FC-113 , : , i2c. 4$.

, , . Arduino HMI , DB9F, . , ? , 1602, 1.02$ FC-113 (0.55$) PCF8574P (0.47$).

- , , ?

لماذا هو ناقل i2c ، أليس من السهل توصيل الأزرار مباشرة؟
, , HMI RS-232,RS-485, CAN .. , HMI , i2c.

, , , . : , , …

, , , i2c, PCF8574P(0.47$), .

لماذا يتم ترتيب الأزرار على هذا النحو وليس بطريقة أخرى؟
: , , , , .
«» - (/) .

5, 8 .
- «» «» . , 8 .
«» , . , , . «SET» HMI OP320.

, , , , ?

, , .

حديد






1. لوحة معززة محلية الصنع مع موصل DB9F. نظرًا لأننا نأخذ طاقة + 5 فولت لموسعات المنافذ والشاشات من Arduino ، أضع فيوز 0.1 أمبير على اللوحة

. جميعنا نعرف شاشة 1602 بلوحة ملحومة FC-113 تربط الشاشة بحافلة i2c.

3. لوحة مفاتيح منزلية الصنع بشريحة PCF8574P ، والتي ستقرأ حالة الأزرار وتنقلها عبر ناقل i2c. بالمناسبة ، تستند لوحة "الشاشة" FC-113 أيضًا إلى رقاقة PCF8574 ، فقط مع مؤشر T ، أي مستو ، وليس DIP ، مثل PCF8574P.

الأزرار أضع 12h12mm مع دافع مربع ؛ يمكنك ارتداء أغطية كبيرة متعددة الألوان عليها.

صور ومخططات لوحات الدوائر المنزلية






يجدر بنا أن نقول بضع كلمات حول رقاقة PCF8574P ، والتي على أساسها صنعت لوحة مفاتيح.
يعد PCF8574P موسع منفذ i2c. هناك 8 منافذ في المجموع ، يمكن تكوين كل منها للعمل كمدخل أو إخراج. بالنسبة إلى هذه الشريحة والشريط غير مطلوب على هذا النحو (تذكر ، على سبيل المثال ، max232) ، فقط في حالة وضع مكثف للطاقة.

يتم تعيين عنوان رقاقة PCF8574P باستخدام أرجل العنوان A0 ، A1 ، A2 ، والتي يتم سحبها إلى الأرض أو إلى مصدر الطاقة من خلال المقاوم 10 كيلو أوم.

على لوحة المفاتيح ، أضع كل أرجل العنوان PCF8574P على الأرض ، بحيث يكون العنوان مشفرًا بقوة 0x20 ولا يمكنك تغييره.

كما كتبت بالفعل ، اخترت DB9F كموصل لـ HMI. يستقبل Arduino إشارات منه +5 V ، GND ، SDA ، SCL.



صنع سلك الاتصال عبر i2c Arduino و HMI بطول 1.4 متر ، ويعمل بدون أعطال.

لقد رسمت اللوحات في Sprint Layout 6 ، وقمت بنقلها إلى textolite باستخدام طريقة LUT وحفرتها في محلول من بيروكسيد وحامض الستريك.

قليلا عن التخليل
.

: 100 3%, 50 , 3 . - 70 .

, .
. , . .



. .



.



تم صنع العلبة من صديق من Plexiglas 4 مم على آلة القطع بالليزر.

الاستطراد الغنائي حول السلك
? , . , , , , DIN-, .

. , , , . , .

- .

, , , ! , .

برمجة


من وجهة نظر Arduino ، يتكون HMI هذا من جهازين يعملان على ناقل i2c: شاشة (LCD) بعنوان 0x27 ولوحة مفاتيح بعنوان 0x20. وفقًا لذلك ، سيعمل Arduino بشكل منفصل مع لوحة المفاتيح وبشكل منفصل مع شاشة LCD.

يتم العمل مع شاشة LCD من خلال مكتبة خاصة "LiquidCrystal_I2C.h" ، يجب تثبيتها في Aduino IDE.

يتم العمل باستخدام لوحة المفاتيح من خلال مكتبة Wire.h القياسية ، والتي تتوفر في البداية في Aduino IDE.

نربط HMI مع Ardiuno.



1. أولاً ، تحقق مما إذا كان Arduino يرى HMI الخاص بنا. للقيام بذلك ، قم بتحميل البرنامج فيه ، والذي سيقوم بفحص ناقل i2c بحثًا عن الأجهزة الموجودة عليه.

رسم 1 ، فحص حافلة 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);          
}


أثناء تنفيذ هذا البرنامج ، سيكتب Arduino نتائج المسح الضوئي لحافلة i2c إلى المنفذ التسلسلي. لعرض هذه البيانات ، انتقل إلى Arduino IDE Tools-> Port Monitor.



نرى أن Arduino على ناقل i2c حدد جهازين بعناوين 0x20 و 0x27 ، وهما لوحة المفاتيح و LCD ، على التوالي.

2. الآن دعنا نرى كيف تعمل لوحة المفاتيح لدينا. قم بإنشاء برنامج يقوم باستطلاع حالة الأزرار وعرضه على شاشة LCD.

رسم 2 ، عرض حالة الأزرار
/*
  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);          
}




لوحة المفاتيح تعمل.

3. أخيرًا ، يمكنك الانتقال إلى ما كان كل شيء يصل إليه ؛ إنشاء قائمة متعددة المستويات في Arduino. من خلال القائمة ، لن نشاهد المعلومات فحسب ، بل نتحكم أيضًا في مخرجات Arduino نفسه.





في nete ، هناك الكثير من المعلومات حول إنشاء قائمة متعددة المستويات في C ++ ، ولكن بالنسبة إلى Arduino ، رأيت بعض المكتبات. لكنني قررت في برنامجي أن أكتب القائمة بنفسي. أولاً ، كلما قل عدد المكتبات اليسرى في المشروع ، أصبحت أكثر هدوءًا. وثانيًا ، الأمر بسيط.

حصلت على شكل آخر لقائمة الشجرة. تسمح لك القائمة بعرض نص ثابت في كل سطر وفي نفس الوقت وقيمة المتغير. على سبيل المثال ، يمكنك عرض اسم المعلمة وقيمتها.

لعرض المتغيرات على الشاشة ، أقوم بتطبيق مبدأ العلامات - بطريقة معينة تسميات نصية مصممة في النص ، بدلاً من ذلك ، عند عرض النص على الشاشة ، يتم عرض قيمة.

يمكن تغيير المعلمات بالضغط على زر "تحرير". علاوة على ذلك ، تشير علامة كل معلمة إلى ما إذا كانت متاحة للتحرير أو للقراءة فقط. إذا كانت المعلمة الحالية للقراءة فقط ، فإن المؤشر في بداية السطر سيكون "*" ؛ إذا تم تمكين التحرير ، فسيصبح المؤشر "+".

رسم 3 ، قائمة متعددة المستويات
/*
 ,    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 ومشكلة اللغة


بشكل منفصل ، من الضروري إثارة قضية الترويس.

في مولد الأحرف لبعض شاشات الكريستال السائل 1602 لا توجد أحرف روسية ، وبدلاً من ذلك يتم خياطة krakozyabry اليابانية. من المستحيل إعادة تحميل ملف مولد الأحرف. لذلك ، سيكون عليك إما كتابة الكلمات على الشاشة بأحرف لاتينية ، أو في البرنامج لتكوين الحروف الروسية بنفسك ، لأن تتمتع شاشة LCD 1602 بالقدرة على إنشاء وتخزين الشخصيات الخاصة بك في ذاكرة الوصول العشوائي. ولكن ، في الحالة الأخيرة ، يمكنك عرض ما لا يزيد عن ثمانية أحرف "محلية الصنع" على الشاشة في المرة الواحدة.

مخططات الأحرف LCD 1602




من حيث المبدأ ، لا بأس بكتابة الكلمات الروسية باللغة الإنجليزية على شاشة LCD. هناك ، حتى الشركة الفرنسية الجليلة Shneider Electric (التي باعت مدافع الهاوتزر للقيصر حتى قبل الثورة) لمدة عقد ونصف لم تكن قادرة على إدخال الروسية في مرحلات Zelio الشهيرة القابلة للبرمجة. ولكن هذا لا يمنعهم من التداول بنشاط في اتساع رابطة الدول المستقلة بأكملها. علاوة على ذلك ، أدخلت القنوات ، الإسبانية والبرتغالية.

في العديد من مصانعنا ، تتواصل Zelio مع الموظفين بعبارات مثل "NASOS 1 VKL".

عندما لا يكون من الواضح ما إذا كانت هناك أحرف روسية في شاشة LCD معينة ، فأنت بحاجة إلى عرض جميع أحرف منشئ الأحرف على الشاشة. إذا كان هناك السيريلية ، يبدأ من الموضع 160.

رسم 4 ، يعرض جميع الشخصيات من جدول مولد 1602 حرف LCD
/*   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); 
   }
}
}


ولكن حتى لو كان جهاز LCD 1602 الخاص بك ينال الجنسية الروسية ، فإن عرض الكلمات الروسية ليس بهذه البساطة. على الأقل ، استخدام مكتبة LiquidCrystal_I2C.h عند العمل مع شاشة LCD على ناقل i2c.

إذا قمت ببساطة بإخراج النص الروسي ، على سبيل المثال ، باستخدام التعليمات lcd.print ("Hello !!!") ، فبدلاً من "Hello !!!" ستظهر بعض القمامة على الشاشة.

وذلك لأن Arduino IDE يترجم الحروف الروسية إلى رمز UTF-8 ثنائي البايت ، وفي شاشة LCD تكون جميع الأحرف أحادية البايت.

بالمناسبة ، يتم ملاحظة نفس المشكلة عند نقل النصوص الروسية من Arduino إلى شاشة منفذ Arduino IDE. يرسل Arduino الحروف الروسية في ترميز UTF-8 مزدوج البايت إلى المنفذ التسلسلي ، ويحاول مراقب منفذ Arduino IDE قراءتها في ترميز Windows-1251 بايت واحد (cp1251). على الرغم من أن cp1251 هو أيضًا 8 بت ، مثل ترميز LCD 1602 ، إلا أنه لا يتطابق معه.

يمكنك تكوين نصوص روسية من خلال رموز الأحرف. على سبيل المثال ، سيتم عرض خط "شاشة LCD" على شاشة LCD سكانها ينالون الجنسية الروسية على النحو التالي:

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

لكني لا أحب هذا النهج.

لعرض النص الروسي بشكل صحيح على Russified LCD 1602 ، تم اختراع العديد من المكتبات من أجل Arduino. ولكن بعد قراءة المراجعات ، رأيت أن الكثير يشكون من الأخطاء عند استخدامها.

لذلك ، في برنامج القائمة متعدد المستويات ، قمت بنفسي بكتابة وظيفة بسيطة لتحويل UTF-8 إلى رموز LCD. صحيح ، لقد فعلت ذلك فقط للأحرف الروسية الكبيرة ، الأمر الذي يناسبني تمامًا.

وظيفة لتحويل الحروف الروسية UTF-8 إلى رمز LCD 1602 أحادي البايت
/*  
 *   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;
}


هذا كل شيء لـ HMI محلي الصنع مع ناقل i2c.

أوه نعم ، في بداية المقال كتبت أنني كنت أقوم بعمل HMI ليس تمامًا ل Arduino ، ولكن للمعدات المتوافقة مع Arduino. هذا أنا عن PLC CONTROLLINO MAXI ، المبرمجة من Arduino IDE (وغيرها الكثير).



كونترولينو ماكسي هو في الواقع اردوينو + مجموعة من الدروع وكل شيء مصمم ليكون PLC صناعي. ولكن عنه في المرة القادمة.

الروابط

الأرشيف باستخدام المخططات والرسومات ولوحة الدوائر المطبوعة بتنسيق lay6
PLC CONTROLLINO المتوافق مع Arduino ، وهو ما ألهمني لإنشاء موسع HMI i2c
PCF8574 موسع وتوصيله بلوحة Arduino
FC-113لـ LCD 1602 للعمل على ناقل i2c وربطه بـ Arduino
قائمة شجرة متعددة المستويات ، المبادئ العامة للإنشاء في ترميز C
UTF-8
ترميز Windows-1251

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


All Articles