【Arduino UNO R3】以序列埠(Com Port)通訊控制 I2C LCD (最多支援 8 個 LCD)

  • 1611
  • 0
  • 2017-01-08

Arduino UNO R3 以序列埠(Com Port)通訊控制 I2C LCD (最多支援 8 個 LCD)

承上篇文章「Arduino UNO R3 以I2C介面並聯兩個 1602A LCD」內容,增加了序列埠通訊(Com Port)功能,此範例採用預設的鮑率9600bps,資料位元 8 bits,不做同位元檢查,停止位元 1 bit,因此寫測試用的通訊程式(不是指 Arduino 的韌體)時,要注意裝置管理員中的序列埠編號,與內容設定是否與此一致。

韌體程式碼如下:

/* YourDuino.com Example Software Sketch
 16 character 2 line I2C Display
 Backpack Interface labelled "A0 A1 A2" at lower right.
 ..and
 Backpack Interface labelled "YwRobot Arduino LCM1602 IIC V1"
 MOST use address 0x27, a FEW use 0x3F
 terry@yourduino.com */

/*-----( Import needed libraries )-----*/
#include <Wire.h>  // Comes with Arduino IDE
// Get the LCD I2C Library here: 
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
// Move any other LCD libraries to another folder or delete them
// See Library "Docs" folder for possible commands etc.
#include <LiquidCrystal_I2C.h>

/*-----( Declare Constants )-----*/
/*-----( Declare objects )-----*/
// set the LCD address to 0x27 for a 16 chars 2 line display
// A FEW use address 0x3F
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
//LiquidCrystal_I2C lcd1(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD#1 I2C address
//LiquidCrystal_I2C lcd2(0x25, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD#2 I2C address

/*
 * 功能說明:透過 RS-232 介面傳送訊息封包,以顯示在 LCD 上,LCD 以 I2C 介面並聯,最多支援 8 組。
 * 韌體版次:v1.1
 * 更新日期:2016/12/14
 * 程式撰寫:Jay Tsao
 * 使用說明:
 * 1. RS-232 封包格式:每組字串皆需要以逗號(",")結尾,最多 8 組,多餘的則會被忽略,整個封包內容需以"\n"做結尾。
 *    封包範例 1:"1,2,3,4,5,6,7,8,\n"  (注意!!最後一組字串結尾也需要逗號",")
 *    封包範例 2:"ABCDEDCBA-1234567890,,abcdedcba-0987654321,\n"  (只設定 LCD#1 與 LCD#3 的內容)
 *    封包範例 3:"@CLS,,@CLS,\n"  (清除 LCD#1 與 LCD#3 的內容)
 * 2. 封包內容解析後,對應 LCD 的順序為固定的,所以要注意 LCD 的位址,LCD#1 位址為 0x27 - 對應解析後的第一組字串,依此類推。
 */
const String VERSION = "v1.1_2016.12.24";
const int LCD_COUNT = 8;
const int COLS = 16;
const int ROWS = 2;
LiquidCrystal_I2C lcd[LCD_COUNT] = 
{
  LiquidCrystal_I2C(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#1
  LiquidCrystal_I2C(0x26, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#2
  LiquidCrystal_I2C(0x25, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#3
  LiquidCrystal_I2C(0x24, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#4
  LiquidCrystal_I2C(0x23, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#5
  LiquidCrystal_I2C(0x22, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#6
  LiquidCrystal_I2C(0x21, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE),    //LCD#7
  LiquidCrystal_I2C(0x20, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE)     //LCD#8
};

String lcdMsg[LCD_COUNT] = { "", "", "", "", "", "", "", "" };

String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

void setup()
{
  
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);

  InitialLCD();
  
}

void loop()
{ 
  
  // print the string when a newline arrives:
  if (stringComplete) 
  {
    DisplayMsgOnLCD(inputString);
    Serial.println(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
    
}


/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() 
{
  while (Serial.available()) 
  {
    // get the new byte:
    char inChar = (char)Serial.read();
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') 
    {
      stringComplete = true;
    }
    else
    {
      // add it to the inputString:
      inputString += inChar;
    }
  }
}

//初始化 LCD,並顯示韌體版本於 LCD 畫面上
void InitialLCD()
{
  for(int i=0; i<LCD_COUNT; i++)
  {
    lcd[i].begin(COLS, ROWS);
    lcd[i].backlight();
    lcd[i].clear();
    lcd[i].setCursor(0,0);
    lcd[i].print("Hi! I'm LCD#" + String(i+1) + ".");
    lcd[i].setCursor(0,1);
    lcd[i].print(VERSION);
  }
  delay(1000);
}

//顯示 s 內容於 LCD 上,s 為序列埠接收到的命令字串,傳入函式後會進行解析
void DisplayMsgOnLCD(String s)
{
  //將接收的訊息內容以逗號","分隔開,每組字串皆需要以逗號結尾,最多分割8組,多餘的則忽略
  //Example : "1,2,3,4,5,6,7,8,"
  SplitStr(s);
  //顯示分割後每組字串的內容
  for(int i=0; i<LCD_COUNT; i++)
  {
    lcd[i].clear();  
    //若字串內容為空,則不做任何改變
    if(lcdMsg[i] == "")
    {
      continue;
    }
    //若字串內容為"@CLS",則清除LCD畫面內容
    if(lcdMsg[i] == "@CLS")
    {
      lcd[i].clear();
      continue;
    }
    //若都不是,則顯示指定內容
    int lines = WarpStrLines(lcdMsg[i]);
    for(int j=0; j<lines; j++)
    {
      String ss = WarpStr(lcdMsg[i], j);
      lcd[i].setCursor(0,j);
      lcd[i].print(ss);  
    }
  }
  
}

//字串分割,將以','區隔的字串做分割,並依序放入lcdMsg字串陣列
void SplitStr(String s)
{
  //lcdMsg
  int idx = 0;
  int sp = 0;
  int ep = 0;
  for(unsigned int i=0; i<=s.length(); i++)
  {
    if(s.charAt(i) == ',')
    {
      ep = i;
      lcdMsg[idx] = s.substring(sp, ep);
      sp = ep+1;
      idx++;
      if(idx >= LCD_COUNT)
      {
        break;
      }
    }
  }
}

//字串自動換行,並回傳行數
int WarpStrLines(String s)
{
  int iLength = s.length();     // previous length of the String
  int iLines = (iLength / COLS) + (((iLength % COLS) > 0) ? 1 : 0);
  if(iLines > ROWS)
  {
    iLines = ROWS;
  }
  return iLines;
}

//字串自動換行,並回傳指定行數的內容
String WarpStr(String s, int line)
{
  unsigned int sp = (line * COLS);
  unsigned int ep = sp + COLS;
  if(ep > s.length())
  {
    ep = s.length(); 
  }
  return s.substring(sp, ep);
}