2012/02/23

Arduino練習:以LiquidCrystal程式庫控制LCD(相容於Hitachi HD44780)

之前用四個七段顯示器製作時鐘用蜂鳴器演奏周杰倫的青花瓷,這一篇是關於LCD(Liquid Crystal Display)。

我的板子是Arduino Uno Rev 3,軟體開發環境為1.0版、Windows版。

電路圖(Fritzing格式)與程式原始碼,可在此下載

LCD有文字型與圖像型,有大有小。


我買的是相容於Hitachi HD44780的文字型LCD,2x16(2列、一列16個字),無背光,14隻腳。有背光的會多2隻腳,一隻接電源,一隻接地。

可以顯示英文字母、希臘字母、標點符號以及數學符號,還能往左和往右捲動、顯示游標等等。

這是我買的LCD,型號是LMC-STC2A16DRG。接腳有兩排、一排7個針腳。(有些型號為一排14個針腳。)


HD44780相容型LCD的腳位說明:

腳位 說明
1(Vss) 接地(0V)
2(Vdd) 電源(+5V)
3(Vo) 對比(0~+5V),接可變電阻調整對比
4(RS) Register Select:
  若為1,把D0~D7放進資料暫存器
  若為0,把D0~D7放進指令暫存器
5(R/W) Read/Write mode:
  若為1,從LCD讀取資料
  若為0,寫入資料到LCD
6(E) Enable:1代表可寫入LCD
7(D0) Bit 0(LSB)
8(D1) Bit 1
9(D2) Bit 2
10(D3) Bit 3
11(D4) Bit 4
12(D5) Bit 5
13(D6) Bit 6
14(D7) Bit (MSB)
15(A+) 背光,電源
16(K-) 背光,接地

LCD的控制,頗為低階、複雜,還好已經有人寫好程式庫了,本篇會以LiquidCrystal這套程式庫控制LCD。

電路圖:

從Arduino板把5V與GND接到麵包板。

LCD腳位1(Vss)接地。
LCD腳位2(Vdd)接5V。

10k ohm可變電阻,左腳接5V,右腳接地,中腳接LCD腳位3(Vo)。控制對比。

LCD腳位4(RS)接Arduino板的12。

LCD腳位5(R/W)接地。本範例只寫入不讀取。

LCD腳位6(E)接Arduino板的11。

LCD腳位11(D4 )接Arduino板的2。
LCD腳位12(D5 )接Arduino板的3。
LCD腳位13(D6 )接Arduino板的4。
LCD腳位14(D7 )接Arduino板的5。

LCD若只顯示文字,則大部分的功能只須使用4-bit模式即可,LCD腳位7、8、9、10就不用接了。


接的很亂。你可以買那種針腳為一排的LCD,加上排針,比較好接。


不知為何,我買的LCD,它腳位1跟2,跟上面列表相反。


接下來是程式碼,先寫個Hello, World!吧。

// 匯入LiquidCrystal程式庫得標頭檔
#include <LiquidCrystal.h>

// 初始化LCD,建立物件lcd,指定腳位。
// 順序是RS、Enable、D4、D5、D6、D7
// 要配合前面的接線順序
LiquidCrystal lcd(12, 11, 2, 3, 4, 5);

void setup(){
  // 指定LCD的尺寸,我用的是一列16個字,2列。
  lcd.begin(16, 2);

  // 印出Hello , World!
  lcd.print("Hello, World!");
}

void loop(){
  // 欄跟列從0開始數
  // 所以第0列是上面那一列,第1列是下面那一列。
  // 第0欄是最左邊,第15欄是最右邊。
  lcd.setCursor(0, 1); // 將游標移動第0欄、第1列。

  // 顯示板子重置後經過的秒數
  lcd.print(millis() / 1000);
}

成功後,就可以在LCD上面那一列看到Hello, World!,下面那一列看到秒數。

如果看不到畫面,調整看看可變電阻。

底下這支程式,會從序列埠接收資料,顯示在LCD上。顯示游標,指出下一個字元的位置。

#include
#define ROW_NUM 2 // 2列
#define COL_NUM 16 // 16欄(一列可顯示16個字)

int index = 0; // 游標的位置,也是下一字元的位置,範圍是0~31。
#define INDEX_MAX (COL_NUM * ROW_NUM) // 最多可輸入32個字
#define ROW(x) (x / COL_NUM) // 傳入index,可算出游標的列,0或1。
#define COL(x) (x % COL_NUM) // 傳入index,可算出游標的欄,0~15。

LiquidCrystal lcd(12, 11, 2, 3, 4, 5);

void setup(){
  Serial.begin(115200);
  lcd.begin(COL_NUM, ROW_NUM);
  lcd.setCursor(0, 0); // 設定游標位置
  lcd.cursor(); // 顯示游標
  lcd.blink(); // 讓游標閃爍
}

void loop(){
  int b;
  while( (b = Serial.read()) != -1){ // 從序列埠讀取資料
    // 若是可視字元,ASCII碼從0x20到0x7E
    if(0x20 <= b && b <= 0x7E){
      if(index == INDEX_MAX - 1){
        // 如果游標已經停在最後一個位置
        // 跳回最開頭,並且清除LCD上的顯示資料
        index = 0;
        lcd.clear();
      }

      lcd.write(b); // 輸出從序列埠收到的資料
      index++; // 指向下一個位置
      lcd.setCursor(COL(index), ROW(index));
    }
  }
}

這支程式,最後一個位置(row 1, col 15)並不會用來顯示資料,所以最大可顯示的字元個數是32 - 1 = 31個。

LiquidCrystal程式庫還有很多其他函式,可參考官方文件

譬如以cursor()、noCursor()顯示∕不顯示游標,blink()、noBlink()閃爍∕不閃爍游標,display()、noDisplay()顯示∕不顯示整個畫面,但資料沒有消失還是存在的,除了這些以外,還能scrollDisplayLeft()、scrollDisplayRight()將整個畫面向左、向右捲動,甚至開啟自動捲動autoscroll(),設定捲動方向leftToRight()∕rightToLeft(),等等,可以玩玩看。


參考資料:

79 comments:

  1. #include #include #include #include LiquidCrystal lcd(12, 11, 5, 4, 3, 2); int backLight = 13;  int val = 0;  int photocellPin = 0;  Servo myservo;  void setup() { lcd.begin(16,2); lcd.print("Val. : "); myservo.attach(9); myservo.write(0);  pinMode(photocellPin, INPUT); } void loop() {  val = analogRead(photocellPin); if( val<=4.6)  {  myservo.write(180); } else if(val>5.3) {  myservo.write(0); }  lcd.setCursor(0, 1); lcd.print(val,1); // 顯示到小數點後一位  delay; }  不好意思,想請問,這是我綜合一些程式所寫出來的,我是利用電壓大小來控制我的馬達,以及利用LCD來顯示我當時的電壓值,但不知未何顯示出的數字都不是我要的(程式碼錯誤嗎?),理論上認為接線無誤。 謝謝幫忙~

    ReplyDelete
    Replies
    1. analogRead讀到的值介於0~1023。不是0~5V。

      Delete
  2. Anonymous8/7/14 12:40

    請問
    http://i.imgur.com/9Ces9st.jpg?1
    要怎麼辦

    ReplyDelete
  3. 不好意思剛剛在那篇留言太多的版留言,所以在這裡再問一次><

    請問葉老師,現在我寫了一個存錢筒的程式(用紅外線的功能去感應通過的硬幣)然後在LCD上顯示總共多少錢
    以下是我寫的程式,也能完美執行
    #include
    #include

    LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display

    /*
    * irObjectDetection.pde: 紅外線物體偵測
    */

    int coin1 = 0;
    int coin5 = 0;
    int coin10 = 0;
    int coin50 = 0;

    //偵測請將A0,A1,A2,A3,A4

    const int irReceiver = 0; // 紅外線接收器A0
    const int irReceiver1 = 1; // 紅外線接收器A1
    const int irReceiver2 = 2; // 紅外線接收器A2
    const int irReceiver3 = 3; // 紅外線接收器A3
    const int irReceiver4 = 4; // 紅外線接收器A4

    ///紅外線irLed全部接pin3
    const int irLed = 3;

    //請LED接D6,D7,D8,D9,D10
    const int ledPin1 = 6; // LED
    const int ledPin2 = 7; // LED
    const int ledPin3 = 8; // LED
    const int ledPin4 = 9; // LED
    const int ledPin5 = 10; // LED
    const int ledPin = 13; // 紅外線指示燈

    const unsigned int frequency = 35000; // 發射頻率(單位: Hz)

    int price=0;
    int trydata=0;

    // 讓指示燈閃爍幾下
    void blinkLED(int ledPin)
    {
    for (int i=1; i<= 4; i++) {
    digitalWrite(ledPin, HIGH); // 打開指示燈
    delay(100);
    digitalWrite(ledPin, LOW); // 關掉指示燈
    delay(100);
    }
    }


    void setup()
    {
    lcd.init(); // initialize the lcd

    Serial.begin(9600); // 開啟 Serial port, 通訊速率為 9600 bps
    pinMode(irReceiver, INPUT); // 把 irReceiver 接腳設置為 INPUT
    pinMode(irLed, OUTPUT); // 把 irLed 接腳設置為 INPUT
    pinMode(ledPin, OUTPUT); // 把 ledPin 設置為 OUTPUT
    pinMode(ledPin1, OUTPUT); // 把 ledPin1 設置為 OUTPUT
    pinMode(ledPin2, OUTPUT); // 把 ledPin2 設置為 OUTPUT
    pinMode(ledPin3, OUTPUT); // 把 ledPin3 設置為 OUTPUT
    pinMode(ledPin4, OUTPUT); // 把 ledPin4 設置為 OUTPUT
    pinMode(ledPin5, OUTPUT); // 把 ledPin5 設置為 OUTPUT

    tone(irLed, frequency); // 產生指定頻率的脈波 (Pulses)

    // Print a message to the LCD.
    lcd.backlight();
    lcd.print("Insert Coin");
    }

    void closeallled()
    {
    trydata = 0;

    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    digitalWrite(ledPin5, LOW);
    }

    void loop()
    {
    int ir_status = analogRead(irReceiver); // 讀取 irReceiver 的狀態
    int ir_status1 = analogRead(irReceiver1); // 讀取 irReceiver1 的狀態
    int ir_status2 = analogRead(irReceiver2); // 讀取 irReceiver2 的狀態
    int ir_status3 = analogRead(irReceiver3); // 讀取 irReceiver3 的狀態
    int ir_status4 = analogRead(irReceiver4); // 讀取 irReceiver4 的狀態

    Serial.println(ir_status);
    Serial.println(ir_status1);
    Serial.println(ir_status2);
    Serial.println(ir_status3);
    Serial.println(ir_status4);


    if (ir_status > 1000 )
    {
    coin1++;
    closeallled();
    blinkLED(ledPin1);
    }
    else if (ir_status1 > 1000 )
    {
    coin5++;
    closeallled();
    blinkLED(ledPin2);
    }
    else if (ir_status2 > 1000)
    {
    coin10++;
    closeallled();
    blinkLED(ledPin3);
    }
    else if (ir_status3 > 1000 )
    {
    coin50++;
    closeallled();
    blinkLED(ledPin4);
    }
    else if (ir_status4 > 1000 )
    {
    closeallled();
    lcd.print("I'm full");
    blinkLED(ledPin5);
    }
    else if (trydata > 30)
    {
    trydata = 0;
    closeallled();
    }
    else
    {
    trydata++;
    }

    price = coin50 * 50 + coin10 * 10 + coin5 * 5 + coin1;

    if (price != 0)
    {
    lcd.clear();
    lcd.print(price);
    }

    delay(50);

    }
    但我現在要加按鈕(按鈕按下去會在LCD上顯示每個錢幣各有幾個,)請問程式要怎麼加(寫)呢?

    ReplyDelete
    Replies
    1. 宣告全域變數,依照按鈕按下幾次切換狀態,譬如:
      int state;
      在loop()裡,以digitalRead讀取按鈕的狀態,每當按下時,就切換state記錄的值,0變1,1變0。

      在loop()裡檢查state的值,若是0就顯示總金額,若是1就顯示每個錢幣各有幾個。

      Delete
    2. 如果可以的話,可以請葉老師直接幫我把按鈕的程式加進去上面的程式嗎?
      因為研究了很久還是寫不出來= =

      Delete
    3. 根據你原本的程式碼難度,加入按鈕功能應該算簡單的。
      祝你好運。

      Delete
    4. 啊勒!!! 幫忙寫?????

      Delete
  4. 請問怎麼讓第二行也顯示字元

    ReplyDelete
  5. 葉老師,您好,日前試Arduino+LCD時,遇到顯示方面的問題,想請教您~
    一開始的時候數值3顯示正常,數值120也顯示正常,但回到6的時候卻顯示成600,而88的時候則顯示成880,電源重開,則問題重複出現。
    下列為程式碼:

    //腳位定義
    #define KeyPadPin A0 //Analog按鍵
    //按鍵參數
    #define Offset 10
    #define SelectADC 639 //按鍵Select Analog值
    #define LeftADC 408 //按鍵Left Analog值
    #define UpADC 99 //按鍵Up Analog值
    #define DownADC 256 //按鍵Down Analog值
    #define RightADC 0 //按鍵Right Analog值
    //按鍵旗標
    int val ;
    union{
    unsigned char Val;
    struct
    {
    unsigned Select:1; //Select按鍵被按下旗標
    unsigned Left:1; //Left按鍵被按下旗標
    unsigned Up:1; //Up按鍵被按下旗標
    unsigned Down:1; //Down按鍵被按下旗標
    unsigned Right:1; //Right按鍵被按下旗標
    unsigned :1; //保留
    unsigned :1; //保留
    unsigned :1; //保留
    };
    }KeyDown;
    //按鍵參數
    int KeyVal;
    unsigned char KeyDownOld; //按鍵舊值暫存器
    unsigned int KeySelectVal, //Select按鍵按下計數器
    KeyLeftVal, //Left按鍵按下計數器
    KeyUpVal, //Up按鍵按下計數器
    KeyDownVal, //Down按鍵按下計數器
    KeyRightVal, //Right按鍵按下計數器
    dontPressVal; //未按下按鍵
    #include
    // initialize the library with the numbers of the interface pins
    /* LCD RS pin to digital pin 8
    * LCD Enable pin to digital pin 9
    * LCD D4 pin to digital pin 4
    * LCD D5 pin to digital pin 5
    * LCD D6 pin to digital pin 6
    * LCD D7 pin to digital pin 7
    * LCD BL pin to digital pin 10
    * KEY pin to analogl pin 0
    */
    LiquidCrystal lcd(8,9,4,5,6,7); //腳位定義 lcd(RS,E,D4,D5,D6,D7);
    void setup()
    {
    Serial.begin(9600);
    lcd.begin(16, 2);
    lcd.print("Motor RPM =");
    lcd.setCursor(0, 1);
    lcd.print("Driver RPM =");
    }
    //***************************
    //******* Main Loop *********
    //***************************
    void loop()
    {
    val = analogRead(1);
    Serial.println(val);
    int rpm = map(val,0,1023,0,120);
    lcd.setCursor(12,1);
    lcd.print(rpm);
    }

    ReplyDelete
    Replies
    1. 上一次若是600,下一次只輸出88,那就會變成880,因為上一次的0還留著。
      可呼叫clear()清除所有畫面,然後重新輸出。
      或輸出空格字元,清掉該清掉的位置。

      Delete
    2. 葉老師,我有試著在loop中插入clear(),不過原本我想要的畫面,"Driver RPM =###"就會只剩下###,而"空格字元"的部份可以怎麼用?是利用判斷式嗎?可以麻煩您指教一下?謝謝!!

      Delete
    3. 大概是這樣吧
      lcd.setCursor(12,1);
      lcd.print(" ");
      lcd.setCursor(12,1);
      lcd.print(rpm);

      Delete
    4. 葉老師,插入lcd.print(" ");,之後就可以了~感恩!!

      Delete
  6. 請問如何刪除單個字元呢~~?

    ReplyDelete
    Replies
    1. 以空白字元覆蓋。

      Delete
    2. 可以請教葉老師~~~
      空白字元是指:lcd.print(" "); ?這樣嗎?
      請問該段程式需放在哪裡才能正確執行~~?
      謝謝您的回答

      Delete
    3. > 空白字元是指:lcd.print(" "); ?這樣嗎?
      Yes.

      > 請問該段程式需放在哪裡才能正確執行~~?
      放在你想要進行覆蓋(清除)動作的地方啊。只有你才知道。

      Delete
    4. This comment has been removed by the author.

      Delete
    5. 在請問一下
      如果我想用4*4鍵盤上的"D"來做為 清除單個字元的功能
      請問該程是要如何寫?

      Delete
    6. 咳,偵測使用者按下"D",然後清除單個字元,不是嗎?
      > 如何寫?
      有什麼問題嗎?

      Delete
    7. 請問葉老師有程式可以參考嗎?
      不曉得從何寫起

      Delete
    8. 先寫偵測鍵盤按鍵的部份。
      然後加入條件判斷的程式,判斷若是D,就進行清除字元的動作。

      Delete
  7. 如果可以的話,可以請葉老師直接幫我寫出來嗎=2=?

    ReplyDelete
    Replies
    1. 拜託啦~~研究很久了 還是寫不出來~~
      拜託救命

      Delete
    2. 沒辦法。
      加油。

      Delete
    3. 請教一下 以下是我的程式 這是結合4*4鍵盤、LCD、RFID、繼電器下去搭配的門禁系統
      我獨立分成兩個程式撰寫
      第一個:4*4鍵盤與LCD
      第二個:RFID與繼電器
      獨力程式在執行時都沒有問題
      但是結合起來之後,卻發現RFID感應後繼電器有收到訊號,但是沒有做動
      可以請老師指點嗎

      Delete
    4. void loop(){
      // 讀取 Keypad 的輸入
      char key = keypad.getKey();

      // NO_KEY 代表沒有按鍵被按下
      if (key != NO_KEY){
      // 假如有按鍵被按下,就印出按鍵對應的字元
      Serial.println(key);
      lcd.print(key);
      }
      uchar i, tmp;
      uchar status;
      uchar str [MAX_LEN];
      uchar RC_size;
      uchar blockAddr; // select the operating block address 0 to 63



      passwd_flag=0;
      //Look for the card, return the card type
      status = MFRC522_Request (PICC_REQIDL, str);
      if (status == MI_OK)
      {
      Serial.println ("Find out a card");
      Serial.print (str [0], BIN);
      Serial.print (",");
      Serial.print (str [1], BIN);
      Serial.println ("");
      }


      // Anti-collision, return the card serial number 4-byte
      status = MFRC522_Anticoll (str);
      memcpy (serNum, str, 5);
      if (status == MI_OK)
      {


      Serial.println ("The card's number is:");
      Serial.print (serNum [0], BIN);
      Serial.print (",");
      Serial.print (serNum [1], BIN);
      Serial.print (",");
      Serial.print (serNum [2], BIN);
      Serial.print (",");
      Serial.print (serNum [3], BIN);
      Serial.print (",");
      Serial.print (serNum [4], BIN);
      Serial.println ("");
      }


      //Election card, return the card capacity
      RC_size = MFRC522_SelectTag (serNum);
      if (RC_size != 0)
      {
      Serial.print ("The size of the card is:");
      Serial.print (RC_size, DEC);
      Serial.println ("K bits");
      }


      //Card Reader
      blockAddr = 7; //data block 7
      status = MFRC522_Auth (PICC_AUTHENT1A, blockAddr, sectorKeyA [blockAddr / 4], serNum); //authentication
      if (status == MI_OK)
      {
      //Read data
      blockAddr = blockAddr - 3;
      status = MFRC522_Read (blockAddr, str);//讀取密碼
      if (status == MI_OK)
      {
      Serial.println ("Read from the card, the data is:");
      for (i = 0; i <16; i ++)//列印密碼
      {
      Serial.print (str [i], DEC);
      Serial.print (",");
      }
      Serial.println ("");

      for (i = 0; i <16; i ++)//驗證密碼
      {
      if(str[i]!=passwordData[i])//密碼錯誤
      {
      Serial.println ("Fails");
      passwd_flag=1;
      }

      }
      if(!passwd_flag)//密碼正確
      {
      Serial.println ("PASS");
      digitalWrite(relay_io, LOW); // 繼電器有動作後閉合
      delay(2000);
      digitalWrite(relay_io, HIGH); // 恢復繼電器狀態

      }


      }
      }

      Delete
    5. 我只要把下面這段輸出文字的程式拿掉,RFID與繼電器又恢復正常了,請問這是哪裡有衝突嗎?
      // 讀取 Keypad 的輸入
      char key = keypad.getKey();

      // NO_KEY 代表沒有按鍵被按下
      if (key != NO_KEY){
      // 假如有按鍵被按下,就印出按鍵對應的字元
      Serial.println(key);
      lcd.print(key);
      }

      Delete
    6. 看不出為什麼會有衝突。

      莫非因為電源不足?

      Delete
  8. 如果電源不足的話 老師有什麼方法可以改善?
    這個問題我已經卡了兩個禮拜了
    試過好多種寫法,但還是只能一個做動

    ReplyDelete
    Replies
    1. 我只是猜想而已。
      你的電源如何配置?Arduino的電源從何取得?
      鍵盤、LCD、RFID、繼電器的電源從何取得?

      或可到其他Arduino問答論壇,詢問其他人。

      Delete
  9. 目前都是從arduino分出來的
    都是從筆電USB供應的

    ReplyDelete
    Replies
    1. 雖然我不能肯定,但猜想電源方面或許有問題,僅是猜想。
      請試著從外部供應電源吧。

      可到各處問問其他人,
      Arduino討論專區- Robofun 機器人論壇-
      Cooper Maa
      Arduino官網
      等等

      Delete
  10. 葉老師,您好,請教一下,下列這個偵測的程式,在alarm機制的部份,我想要讓他如果發出警報,狀態一直持續的話,建議要如何寫??(目前alarm之後,如果偵測到正常,alarm就會取消),謝謝!!

    int potPin = A1;
    int val ;
    int val2 ;
    int val3=0 ;
    const int sensor = 2; // the number of the monitor on/off pin
    const int alarm = 3; // the number of the Digital pin for alarm control
    const int relay = 11; // the number of the Digital pin for sensor digital output
    void setup()
    {
    Serial.begin(9600);
    lcd.clear();
    lcd.begin(16, 2);
    lcd.print("MXIC Spin Status");
    lcd.setCursor(0, 1);
    lcd.print("RH =");
    // initialize the pin as an input:
    pinMode(relay,INPUT);
    // initialize the sensor pin as an input:
    pinMode(sensor, INPUT);
    // initialize the pin as an output:
    pinMode(alarm,OUTPUT);
    }
    void loop()
    {
    if (digitalRead(sensor) == HIGH)
    // sensor啟動
    {
    delay(2000);
    lcd.setCursor(7,1);
    lcd.print(" ");
    val =analogRead(potPin);
    val2 = 300-map(val, 0, 620, 0, 300);
    lcd.setCursor(5,1);
    lcd.print(" ");
    lcd.setCursor(7,1);
    lcd.print(val2);
    lcd.setCursor(10,1);
    lcd.print(" LUX ");
    if (digitalRead(relay) == HIGH )
    //偵測條件成立
    {
    val3=val3+1;
    if (val3 >= 3)
    //偵測次數超過3次
    {
    (digitalWrite(alarm, HIGH));
    //發出警報
    }
    }
    else
    {
    (digitalWrite(alarm, LOW));
    //不發出警報
    }
    delay(1000);
    }
    else
    {
    lcd.setCursor(5,1);
    lcd.print("Stand by ");
    }
    }

    ReplyDelete
    Replies

    1. else
      {
      (digitalWrite(alarm, LOW));
      //不發出警報
      }

      改成

      else if(val3 < 3)
      {
      (digitalWrite(alarm, LOW));
      //不發出警報
      }


      Delete
    2. 可以了!!謝謝葉老師~

      Delete
  11. 葉難老師您好,目前遇到一個問題
    我使用洞洞板自製了一塊Arduino
    LCD的正電與GND量測到的電壓是正常的5V
    可是LCD寫入的腳位E與DATA腳卻好像因電壓不足而無法寫入(約0.3V),因為我從一般的Arduino板上量測到的電壓正常好像也是要有5V才對,我查電路也沒有接錯的問題,
    請問問題可能會是出在哪裡呢?
    麻煩老師解惑

    ReplyDelete
    Replies
    1. 呵呵,既然沒有接錯的問題,那麼我該怎麼回答呢。

      您只有描述問題的現象,但沒有給我足夠的資訊,無法回答,只能猜測,
      LCD壞了,Arduino壞了,電源供應不足,電壓不穩,等等等等。

      Delete
    2. 老師好,抱歉是我沒描述清楚
      我的自製板上有照老師您的教學做穩壓電路,ATmega328也裝在Arduino上測試過ic沒問題,
      目前所能知道的就是LCD端的E端與DATA端電壓不足
      直接在ATmega328相對應的腳位上量到的電壓一樣不足。

      Delete
    3. 我非電子科系出身,恐怕沒辦法回答你的問題。
      請問你怎麼知道電壓不足呢?正常的電壓會是5V嗎?

      你的問題是LCD沒辦法顯示是吧?
      對比有調整嗎?

      Delete
    4. 我用三用電表量過各腳位的電壓
      正常來說要是5V
      可是我量到的都不足1V
      不是對比的問題,因為我有接可變電阻做調整過了
      目前猜測的想法是
      由於電壓不足造成LCD無法達到工作電壓,因此無法寫入與顯示字幕

      Delete
    5. 呃,這樣啊。
      > 由於電壓不足
      我也不知道怎麼辦耶。

      先用別的正常的Arduino板子試試,先確認LCD是好的。

      Delete
    6. 老師好,我後來在麵包板上做了一樣的配置發現不會有電壓不足的問題,但ATmega的作動反應有相當嚴重的延遲,不過對按鈕就不會有明顯延遲,但對LCD的寫入延遲就相當大,例如我在程式中寫了一個計時器但LCD上的時間幾乎1分鐘還會跳1秒。

      Delete
    7. > 老師好
      不用稱呼我為老師。

      > 不會有電壓不足的問題
      你只是描述現象,什麼資料也沒給我,我什麼也不能做。

      > 對LCD的寫入延遲就相當大
      同上。我只能就經驗猜測。猜測你程式有誤。

      > LCD上的時間幾乎1分鐘還會跳1秒
      看不懂。

      Delete
    8. 程式方面我用正常的Uno板做過確認還開始自製的
      LCD方面抱歉是我筆誤應該是
      幾乎每過1分鐘LCD上的時間才跳1秒
      在RESET後LCD的啟動與寫入也需要花幾秒鐘才能把預定要顯示的字全部寫入,不像使用Uno板一通電就全部寫入

      Delete
    9. > 用正常的Uno板做過確認才開始自製的
      既然如此,那我就不知道為什麼了。

      Delete
    10. 剛剛我懷疑是不是震盪器的品質不良照成ATmega作動出線延遲所以拿了不同賣場買的振盪器做測試發現我的懷疑沒錯,更換震盪器之後雖然還是有延遲但變成每2秒LCD上的時間就跳一秒

      Delete
    11. 恭喜。看來您對此方面比我還了解,抱歉沒幫上忙。

      可能震盪器頻率不對?

      Delete
    12. 因為我原本使用的震盪器是買ATmega時賣家附贈的,所以我就想有沒有可能附贈的是品質較低落的元件,不過既然還是會有延遲我猜可能那個賣家賣的ATmega品質也不怎麼樣,我可能會再找其他賣家的回來做比較測試,
      也感謝老師的指教

      Delete
    13. 你是用atmega328p?
      fuse應該是設為16MHz吧?
      振盪器也是16MHz?

      Delete
    14. 沒錯賣家附贈的確實也是16MHz的
      我另外買的也是16MHz只是不知道為甚麼會有這種情狀而已

      Delete
  12. 葉老師,您好:
    又來請教您~
    近期用HTU21D做一個溫濕度偵測的模組,由於要有Analog訊號(0~5V)輸出,想搭配MCP4725模塊使用,但以前沒用過,想請教您如何使用?
    1.SCL、SDA與HTU21D I2C訊號(A4、A5)連接就可以嗎?
    2.程式的部份,除了加入#include 以外,還需增加什麼嗎?
    #include
    #include "Adafruit_HTU21DF.h"

    // Connect Vin to 3-5VDC
    // Connect GND to ground
    // Connect SCL to I2C clock pin (A5 on UNO)
    // Connect SDA to I2C data pin (A4 on UNO)

    Adafruit_HTU21DF htu = Adafruit_HTU21DF();

    void setup() {
    Serial.begin(9600);
    if (!htu.begin()) {
    Serial.println("Couldn't find sensor!");
    while (1);
    }
    }


    void loop() {
    Serial.print("Temp: "); Serial.print(htu.readTemperature());
    Serial.print("\t\tHum: "); Serial.println(htu.readHumidity());
    delay(500);
    }

    ReplyDelete
    Replies
    1. 你是買adafruit的產品嗎?adafruit提供非常詳細的說明文件,
      例如MCP4725
      https://learn.adafruit.com/mcp4725-12-bit-dac-tutorial/overview
      照做就可以了。

      > 1.SCL、SDA與HTU21D I2C訊號(A4、A5)連接就可以嗎?
      yes.

      2. 程式的部份
      寫程式實作你想要的功能囉。

      Delete
    2. 葉老師~請教一下,連接後,溫度、濕度都可顯示,但MCP4725的OUT電壓固定在2.34V,不會隨著溫度、濕度變化。
      1.MCP4725說明的部分,有些不懂:A0 allow you to change the I2C address. By default (nothing attached to A0) the address is hex 0x62. If A0 is connected to VDD the address is 0x63. This lets you have two DAC boards connected to the same SDA/SCL I2C bus pins.
      我只有接1個,需要怎麼寫?
      我加入這行 dac.begin(0x62),會出現下列錯誤:
      Arduino:1.6.12 (Windows 10), 板子:"Arduino Nano, ATmega328"

      C:\Users\csus\Documents\Arduino\HTU21D\HTU21D.ino: In function 'void setup()':

      HTU21D:13: error: 'dac' was not declared in this scope

      dac.begin(0x64);

      ^

      exit status 1
      'dac' was not declared in this scope

      This report would have more information with
      "Show verbose output during compilation"
      option enabled in File -> Preferences.

      #include
      #include "Adafruit_HTU21DF.h"
      #include "Adafruit_MCP4725.h"
      // Connect Vin to 3-5VDC
      // Connect GND to ground
      // Connect SCL to I2C clock pin (A5 on UNO)
      // Connect SDA to I2C data pin (A4 on UNO)
      #define DAC_RESOLUTION (8)
      Adafruit_HTU21DF htu = Adafruit_HTU21DF();
      void setup() {
      Serial.begin(9600);
      Serial.println("HTU21D-F test");
      if (!htu.begin()) {
      Serial.println("Couldn't find sensor!");
      while (1);
      }
      }
      void loop() {
      Serial.print("Temp: "); Serial.print(htu.readTemperature());
      Serial.print("\t\tHum: "); Serial.println(htu.readHumidity());
      delay(500);
      }

      Delete
    3. > 1.MCP4725的A0
      MCP4725是I2C裝置,需要擁有獨一無二的位址,MCP4725的預設位址是0x62。
      若你想接多個MCP4725,第一個的A0要接GND(位址0x62),第二個的A0要接VDD(位址0x63)。

      既然你只用一個,位址會是0x62,程式裡要寫該值。

      2. 我加入這行 dac.begin(0x62)

      你少了
      Adafruit_MCP4725 dac;
      吧。

      Delete
  13. 葉老師~目前編譯OK了,但MCP4725的OUT電壓仍是固定在2.34V,不會隨著溫度、濕度變化,請教還有哪裡沒設定的嗎??
    #include
    #include "Adafruit_HTU21DF.h"
    #include "Adafruit_MCP4725.h"
    // Connect Vin to 3-5VDC
    // Connect GND to ground
    // Connect SCL to I2C clock pin (A5 on UNO)
    // Connect SDA to I2C data pin (A4 on UNO)
    #define DAC_RESOLUTION (8)
    Adafruit_HTU21DF htu = Adafruit_HTU21DF();
    Adafruit_MCP4725 dac;
    void setup() {
    Serial.begin(9600);
    Serial.println("HTU21D-F test");
    dac.begin(0x62);
    if (!htu.begin()) {
    Serial.println("Couldn't find sensor!");
    while (1);
    }
    }
    void loop() {
    Serial.print("Temp: "); Serial.print(htu.readTemperature());
    Serial.print("\t\tHum: "); Serial.println(htu.readHumidity());
    delay(500);
    }

    ReplyDelete
    Replies
    1. > 會隨著溫度、濕度變化,請教還有哪裡沒設定的嗎??
      因為程式裡根本沒有這部分的功能。

      Delete
  14. 那麼還請葉老師不吝指教或提示建議,要如何將HTU21D這個I2C的訊號解析為類比訊號輸出,感謝您!!

    ReplyDelete
    Replies
    1. 你下載的程式庫Adafruit_MCP4725,裡面不是有範例程式嗎?

      先begin(0x62);

      然後就呼叫setVoltage(value, storeflag); 設定你想輸出的類比訊號(電壓),
      value的範圍可從0到0x0FFF(因為是12 bits DAC),
      storeflag若傳入true,會把電壓值儲存到EEPROM,
      無此需要的話,傳入false即可;常常寫入EEPROM的話,壽命會減少。

      Delete
  15. 老師請問一下
    原本在將開關切在SPI,LCD可以正常動作
    然而切至SD模式 LCD無法顯示(白色方塊)

    ReplyDelete
  16. Anonymous15/6/17 00:03

    老師 請問怎麼讀取lcd顯示的內容? 是lcd.read嗎?

    ReplyDelete
    Replies
    1. 內建的LiquidCrystal程式庫應該無此功能。
      曾有人想加入,https://github.com/arduino-libraries/LiquidCrystal/issues/7
      但沒進去。

      Delete
  17. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 收到紅外線,根據收到的碼,換成你想顯示的文字,秀在lcd上。

      Delete
    2. This comment has been removed by the author.

      Delete
    3. 使用程式庫,自己寫程式,寫進你想要的應用功能。

      Delete
  18. 請問我的lcd 讀到的數字會停留在上面是有甚麼問題嗎
    例如:現在顯示18如果下一個要顯示8的話就會變成88
    如果一開始是100下一個顯示數字是9的話會變成900這是程式有問題嗎

    ReplyDelete
    Replies
    1. 沒有問題,你沒有清除,它就還留著。

      下clear()可清除。

      Delete
  19. 葉老師您好,我正在學習時鐘的程式,目前顯示沒問題了,可是在數字輸出的部分,怎麼讓1秒輸出01兩位數呢? 因為59秒之後會變成09秒19秒...99秒..10秒
    以下是我的程式碼,麻煩您了,謝謝
    #include
    #include
    #include

    DS3231 Clock;
    bool Century=false;
    bool h12;
    bool PM;
    byte ADay, AHour, AMinute, ASecond, ABits;
    bool ADy, A12h, Apm;
    byte year, month, date, DoW, hour, minute, second;

    //LED_Fundino紅色板子用
    //LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
    //LED_MH黑色板子用
    LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

    //建立O型
    byte OC[8] = {
    B11100,
    B10100,
    B11100,
    B00000,
    B00000,
    B00000,
    B00000,
    B00000
    };
    void setup() {
    // Start the I2C interface
    Wire.begin();
    //第一次使用要設定以下時間,之後就可以不用設定了
    //Clock.setSecond(00);//Set the second
    //Clock.setMinute(41);//Set the minute
    //Clock.setHour(14); //Set the hour
    //Clock.setDoW(4); //Set the day of the week
    //Clock.setDate(23); //Set the date of the month
    //Clock.setMonth(8); //Set the month of the year
    //Clock.setYear(17); //Set the year (Last two digits of the year)
    // Start the serial interface
    Serial.begin(115200);

    // 初始化 LCD,一行 16 的字元,共 2 行,預設開啟背光
    lcd.begin(16, 2);
    //建立OC型
    lcd.createChar (1, OC); // load OC to memory 1
    // 閃爍三次
    for(int i = 0; i < 1; i++) {
    lcd.backlight(); // 開啟背光
    delay(50);
    lcd.noBacklight(); // 關閉背光
    delay(50);
    }
    lcd.backlight();

    // 輸出初始化文字
    lcd.setCursor(0, 0); // 設定游標位置在第一行行首
    lcd.print("DCJH-Maker");
    delay(500);
    lcd.setCursor(0, 1); // 設定游標位置在第二行行首
    lcd.print("Hello,orangeV");
    delay(1000);
    lcd.clear();
    }

    void loop() {
    int second,minute,hour,date,month,year,temperature;
    second=Clock.getSecond();
    minute=Clock.getMinute();
    hour=Clock.getHour(h12, PM);
    date=Clock.getDate();
    month=Clock.getMonth(Century);
    year=Clock.getYear();

    temperature=Clock.getTemperature();

    Serial.print("20");
    Serial.print(year,DEC);
    Serial.print('-');
    Serial.print(month,DEC);
    Serial.print('-');
    Serial.print(date,DEC);
    Serial.print(' ');
    Serial.print(hour,DEC);
    Serial.print(':');
    Serial.print(minute,DEC);
    Serial.print(':');
    Serial.print(second,DEC);
    Serial.print('\n');
    Serial.print("Temperature=");
    Serial.print(temperature);
    Serial.print('\n');

    lcd.setCursor(0, 0); // 設定游標位置在第一行行首
    lcd.print("20");
    lcd.print(year,DEC);
    lcd.print("-");
    lcd.print(month,DEC);
    lcd.print("-");
    lcd.print(date,DEC);
    lcd.setCursor(12, 0);
    lcd.print(temperature);
    lcd.print(char(1));
    lcd.print("C");
    lcd.setCursor(0, 1); // 設定游標位置在第二行行首
    lcd.print(hour,DEC);
    lcd.setCursor(2, 1);
    lcd.print(":");
    lcd.setCursor(3, 1);
    lcd.print(minute,DEC);
    lcd.setCursor(5, 1);
    lcd.print(":");
    lcd.setCursor(6, 1);
    lcd.print(second,DEC);

    delay(1000);
    }

    ReplyDelete
    Replies
    1. lcd.setCursor(0, 1); // 設定游標位置在第二行行首
      if (hour<9) {lcd.print(" ");} //小時第一位補空白字元
      lcd.print(hour,DEC);
      lcd.setCursor(2, 1);
      lcd.print(":");
      lcd.setCursor(3, 1);
      if (minute<9) {lcd.print("0");} //分鐘第一位補"0"
      lcd.print(minute,DEC);
      lcd.setCursor(5, 1);
      lcd.print(":");
      lcd.setCursor(6, 1);
      if (second<9) {lcd.print("0");} //秒第一位補"0"
      lcd.print(second,DEC);

      Delete
  20. This comment has been removed by the author.

    ReplyDelete