如何使用 LiquidCrystal 函式庫的跑馬燈 Autoscroll 功能?

開發 Arduino 應用程式的過程,比較麻煩的是沒有螢幕顯示。除錯的時候,沒有比較直覺的方式來反覆測試結果。透過序列埠串口 Serial Port 來顯示溝通,是個方法,但有時候總是覺得要開Arduino內建的 Serial Monitor,或用像 Putty 這種終端機模擬程式來用,有點麻煩,尤其當板子頻繁插拔的時候更是如此。
買一片便宜的雙行顯示液晶螢幕,我覺得是個不錯的方案,像照片這種 16x2 的 LCD 螢幕 (兩行,每行16個英文字元),當作在電腦寫C語言常用的 printf 輸出,還挺方便的。買來後剪個瓦愣紙板把螢幕包好,或者3D列印個外殼保護,這小螢幕可以用更久不會壞。


LCD 1602 5V LCM IIC I2C 低價液晶螢幕,內建I2C轉接板
我認為,LCD螢幕的操作,不會比 serial port 難。而且目前 Arduino 已經預設安裝 LiquidCrystal 第三方的函式庫。若是買來的螢幕有轉成 I2C 信號,也可以去去下載 LiauidCrystal_I2C 這個函式庫來用,短短幾行就能把顯示功能寫出來。
個人比較偏好 I2C 版本的液晶螢幕,因為除了電源、接地,I2C的液晶螢幕只有佔到兩個腳位,對於腳位很有線的小板子來說,這還蠻重要的。
Arduino Uno或Arduino Pro Micro可以接到專用的 SCL 跟 SDA 兩個 pin,Arduino Nano 或Arduino Pro Micro,則是接到 A4 (SDA) 跟 A5 (SCL) 兩個腳位 (Arduino Pro Micro 的A4 A5是在裡面,要焊上排針來用)。
要怎麼在 LCD 螢幕上的上下兩行位置印出字來,可以自行 Google 一下,找些教學文章就能學會。這篇內容,主要針對被含糊帶過、或是寫得不清不楚的螢幕跑馬燈功能來鑽研。
從 LiquidCrystal_I2C 函式庫的原始碼來看,跑馬燈是 IC 內建的功能,函式庫只是去 IC 的暫存器,啟動跑馬燈功能,沒有做其他的事情。被啟用之後,液晶螢幕的控制 IC 會自行用捲動方式顯示文字。這樣不會為了跑馬燈功能,佔到程式碼的空間。
  void LiquidCrystal_I2C::autoscroll(void) {
    _displaymode |= LCD_ENTRYSHIFTINCREMENT;
    command(LCD_ENTRYMODESET | _displaymode);
  }

  void LiquidCrystal_I2C::noAutoscroll(void) {
     _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
     command(LCD_ENTRYMODESET | _displaymode);
  }
事實上,跑馬燈功能,對於這種小螢幕來說,可以擺脫空間限制,雖然跑馬燈文字,看久會眼花,但至少解決顯示一大段的文字的問題。比方功能出問題,要印一段文字給使用者看。

需要注意的是,跑馬燈功能只能支援單行顯示,若是設定成雙行,超過第24個字元之後,文字會從另外一行跑出來,原來那行,會顯示之前印過的內容,一整個看起來亂七八糟。不過這也能理解,一般文字捲動功能,多半是設計給那些只有單排的顯示螢幕使用,猜測原始IC中的這個功能,應該也是這樣的邏輯。

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 1);
//設定跑馬燈字串內容,每個單字盡量不要超過16個字母
char scrwords[] = "This is where the wealthy and the powerful rule. It is her world...a world apart from mine. Her name...is Catherine. From the moment I saw her, she captured my heart with her beauty, her warmth, and her courage. I knew then, as I know now, she would change my life...forever.\0";

void setup() {
  lcd.noBacklight(); //先將背光設定成不亮,初始化的時候,螢幕會是暗的
  //初始化LCD,init 這個指令,在原始碼中,實際執行 init_priv 這個函數,有順便執行 begin(),所以不用另外再下begin的命令,有些教學是用 begin。
  lcd.init();
  lcd.backlight(); //初始化之後記得打開背光
  lcd.autoscroll(); //啟用自動捲動輸出
  lcd.setCursor(16,0); //設定字元由螢幕最左邊跑出來
}

void loop() {
  char *aword;
  //從一字串中拆出一個一個字來顯示。用aword這個指標逐一抓出每個字元。
  //所謂指標,就是用來紀錄記憶體中某個位址的變數
  //For迴圈一開始,把指標指向字串的第0個字元的位址,&scrwors[0],aword = &scrwords[0]
  //aword++指示迴圈循序指到字串下一個字元的位址,直到發現\0這個"NULL"字元為止
  //並結束for迴圈,\0 被預先寫進去字串最後一個字元。
  //*aword 代表指到記憶體中,某字元位址的內容(也就是那個字元)。

  for(aword = &scrwords[0]; *aword != '\0'; aword++){
    lcd.print(*aword);
    delay(280);
  }
}

也可以用更直覺的方式,來顯示數字跑馬燈:
void loop() {
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
  for(int x = 0; x < 10; x++){
    lcd.print(x);
    delay(500);
  }
}

沒有留言:

張貼留言