# 使用 PCF8574 / PCF8574A 驅動 1602 LCD

在各式應用當中，常會使用 LCD 作為互動顯示的裝置，其中 Hitachi [HD44780](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) 1602 LCD 為一常見的 LCD 顯示模組。本文將介紹如何使用 LinkIt 7697 連接 [1602 I2C LCD 顯示模組](https://robotkingdom.com.tw/product/grove-lcd-rgb-backlight-v4-0/) (使用 [PCF8574A](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf) 驅動 IC)，並提供[函式庫](https://github.com/MediaTek-Labs/linkit-7697-peripheral-drivers-for-arduino/tree/master/LiquidCrystal_I2C)範例說明如何驅動此顯示器。

![](/files/W1m1X9XROWxdVV034Yjt)

### 下載及安裝函式庫 <a href="#driving1602lcdwithpcf8574pcf8574a-xia-zai-ji-an-zhuang-han-shi-ku" id="driving1602lcdwithpcf8574pcf8574a-xia-zai-ji-an-zhuang-han-shi-ku"></a>

[下載驅動程式](https://github.com/MediaTek-Labs/linkit-7697-peripheral-drivers-for-arduino/tree/master/LiquidCrystal_I2C)

以 Francisco Malpartida 開發的函式庫作為移植基礎，LinkIt 7697 的 Arduino BSP 提供了精簡版本的 LCD 驅動程式。開發者可[下載](https://github.com/MediaTek-Labs/linkit-7697-peripheral-drivers-for-arduino/tree/master/LiquidCrystal_I2C)並依照「安裝驅動程式」章節的[說明](https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduino-ide-kai-fa-zhi-nan/kai-fa-tao-jian-yu-kuo-chong-ban/grove-starter-kit)於 Arduino 開發環境中設置此驅動程式；安裝完成後，可執行 **File / Examples / LiquidCrystal\_I2C / HelloLinkIt7697** 範例來點亮 LCD 模組。在實際執行 **HelloLinkIt7697** 前，可先參考後續章節的說明，以了解硬體接線配置及函式庫的使用方式。

### 硬體接線 <a href="#driving1602lcdwithpcf8574pcf8574a-ying-ti-jie-xian" id="driving1602lcdwithpcf8574pcf8574a-ying-ti-jie-xian"></a>

透過 LinkIt 7697 上的這四根腳與 LCD 模組相連：

* 5V
* GND
* I2C Clock (P8)
* I2C Data (P9)

它們分別接到模組上的：

* VCC
* GND
* SCL
* SDA

對應的電路接線圖如下：

![](/files/VB1SdEMJ6zcjoRsfXIea)

**\[模組背面示意圖。黃色框為接線排針位置]**

![](/files/LuGmzQlhWi6hCliTnVqn)

完成線路連接後，即可執行 **HelloLinkIt7697** 範例並看見如影片中的顯示畫面：

{% embed url="<https://youtu.be/fYf2_1wg2iE>" %}

#### 疑難排除 <a href="#driving1602lcdwithpcf8574pcf8574a-yi-nan-pai-chu" id="driving1602lcdwithpcf8574pcf8574a-yi-nan-pai-chu"></a>

執行上述範例時，若遇到不如預期的狀況，可查看是否是以下的問題：

* **LCD 螢幕沒有顯示畫面**：可轉動模組背後的可變電阻 (黃框處) 進行顯示器對比的調整。

![](/files/GcDniirzmy55bv0ajunC)

* **LCD 顯示字元為實心方塊**：這是由於程式碼沒有正確指定 I2C 裝置的地址。以範例程式為例，預設使用的驅動 IC 為 **PCF8574A**，其對應到的 I2C 地址是 **0x3F**。若開發者的顯示模組是使用另一顆驅動 IC：**PCF8574** (型號中沒有後綴的 "**A**")，那麼其地址則為 **0x27**。指定裝置地址的方式將在後續文章介紹，若要獲得更多模組 I2C 地址設定方式的說明，請參考附錄章節。

![](/files/3qkVOOI77fn6EshwRNEv)

![](/files/Wz6huIWWffje0uzWoGRu)

### LiquidCrystal\_I2C 函式庫 <a href="#driving1602lcdwithpcf8574pcf8574aliquidcrystali2c-han-shi-ku" id="driving1602lcdwithpcf8574pcf8574aliquidcrystali2c-han-shi-ku"></a>

透過使用 [LiquidCrystal\_I2C 函式庫](https://github.com/MediaTek-Labs/linkit-7697-peripheral-drivers-for-arduino/tree/master/LiquidCrystal_I2C)，開發者可以輕易地控制 1602 顯示模組。接下來將介紹如何利用相關的 API，進行顯示模組的基本操作。

#### LiquidCrystal\_I2C() <a href="#driving1602lcdwithpcf8574-pcf8574a-liquidcrystal_i2c" id="driving1602lcdwithpcf8574-pcf8574a-liquidcrystal_i2c"></a>

使用此建構函式初始化顯示模組並指定裝置 I2C 地址。例如下面的程式碼將 PCF8574A LCD 模組 I2C 地址設定為 **0x3F**：

```
// set the I2C address of the LCD controller
//    0x3F for PCF8574A
//    0x27 for PCF8574
LiquidCrystal_I2C lcd(0x3F);
```

{% hint style="danger" %}
若該 LCD 模組的驅動 IC 為 **PCF8574** (而非 PCF8574**A**)，則需將地址設定為 0x27 (而非 0x3F) 才能正確驅動 LCD。
{% endhint %}

#### begin() <a href="#driving1602lcdwithpcf8574-pcf8574a-begin" id="driving1602lcdwithpcf8574-pcf8574a-begin"></a>

此函式定義 LCD 顯示區域。以 1602 LCD 為例，需以 (16, 2) 做為此 API 的輸入參數：

```
// init the 1602 (2 rows / 16 columns) LCD
lcd.begin(16, 2);
```

#### home() <a href="#driving1602lcdwithpcf8574-pcf8574a-home" id="driving1602lcdwithpcf8574-pcf8574a-home"></a>

將游標移動至顯示器的起點位置。

#### clear() <a href="#driving1602lcdwithpcf8574-pcf8574a-clear" id="driving1602lcdwithpcf8574-pcf8574a-clear"></a>

清除顯示器上的所有內容及設定。

#### setCursor() <a href="#driving1602lcdwithpcf8574-pcf8574a-setcursor" id="driving1602lcdwithpcf8574-pcf8574a-setcursor"></a>

將游標移動至指定的位置。

#### print() <a href="#driving1602lcdwithpcf8574-pcf8574a-print" id="driving1602lcdwithpcf8574-pcf8574a-print"></a>

於游標所在處顯示字元。開發者可使用 ASCII 字元指定欲顯示的字、或是自訂字元供顯示器顯示。若要顯示 ASCII 字元，直接將該字串傳入 API 即可：

```
// display ASCII characters directly
lcd.print("LinkIt 7697");
```

如果要顯示自訂字元，需先透過下面介紹的 API 註冊對應的點陣圖。

#### createChar() <a href="#driving1602lcdwithpcf8574-pcf8574a-createchar" id="driving1602lcdwithpcf8574-pcf8574a-createchar"></a>

透過此 API 定義自訂字元。在 1602 LCD 上，每個字元都可被表示為 5x8 的矩陣、並透過一個 8 bytes 的陣列定義。陣列中每個 byte 的最低 5 位元定義了矩陣中每個點的狀態。以「向上箭頭」符號為例，該點陣圖的定義方式為：

![](/files/3SkDo9GvOik5MMDsNvjh)

所以該陣列的內容表示為：

```
// define the up-arrow bitmap
const uint8_t up_arrow[8] = {0x04, 0x0E, 0x1F, 0x04, 0x04, 0x04, 0x00, 0x00};
```

藉由呼叫 *createChar()* API，即可將此點陣圖搭配指定的索引值設定至顯示模組中：

```
// register the bitmap to index 3
lcd.createChar(3, (uint8_t *)up_arrow);
```

上面這段程式碼將「向上箭頭」點陣圖指定至索引值 **3** 的位置，所以開發者若要顯示此字元，可如下呼叫 *print()* API 傳入索引值 **3**：

```
// show the bitmap (with index 3) on the LCD
lcd.print(char(3));
```

{% hint style="danger" %}
使用限制

1602 LCD 最高支援 **8** 組使用者自定義字元，因此可註冊的索引值範圍為 **0** 到 **7**。
{% endhint %}

#### display() / noDisplay() <a href="#driving1602lcdwithpcf8574-pcf8574a-display-nodisplay" id="driving1602lcdwithpcf8574-pcf8574a-display-nodisplay"></a>

開啟 / 關閉顯示器 (僅文字部分，不包括背光)。此 API 的其中一種應用為在保留背光點亮的狀況下，進行文字閃爍效果。

#### backlight() / noBacklight() <a href="#driving1602lcdwithpcf8574-pcf8574a-backlight-nobacklight" id="driving1602lcdwithpcf8574-pcf8574a-backlight-nobacklight"></a>

開啟 / 關閉顯示模組的背光 (不包含文字部分)。

#### scrollDisplayLeft() / scrollDisplayRight() <a href="#driving1602lcdwithpcf8574-pcf8574a-scrolldisplayleft-scrolldisplayright" id="driving1602lcdwithpcf8574-pcf8574a-scrolldisplayleft-scrolldisplayright"></a>

將螢幕上的文字左移 / 右移一個字元的位置。模組透過改變顯示器的座標系來達成平移效果，實際上每個字元本身的座標位置並沒有改變。

#### leftToRight() / rightToLeft() <a href="#driving1602lcdwithpcf8574-pcf8574a-lefttoright-righttoleft" id="driving1602lcdwithpcf8574-pcf8574a-lefttoright-righttoleft"></a>

定義游標移動的方向。

### HelloLinkIt7697 範例 <a href="#driving1602lcdwithpcf8574pcf8574ahellolinkit7697-fan-li" id="driving1602lcdwithpcf8574pcf8574ahellolinkit7697-fan-li"></a>

在前面的影片中，展示了上述 API 提供的功能，開發者可參考 **HelloLinkIt7697** 程式碼 (如下) 了解驅動函式庫的使用方法。

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

// set the I2C address of the LCD controller
//    0x3F for PCF8574A
//    0x27 for PCF8574
LiquidCrystal_I2C lcd(0x3F);

// define the custom bitmaps
// up to 8 bitmaps are supported
const uint8_t my_bitmap[][8] =
{
  // chequer
  {0x15, 0x0A, 0x15, 0x0A, 0x15, 0x0A, 0x15, 0x00},
  // up arrow
  {0x04, 0x0E, 0x1F, 0x04, 0x04, 0x04, 0x00, 0x00},
  // down arrow
  {0x00, 0x00, 0x04, 0x04, 0x04, 0x1F, 0x0E, 0x04},
  // rectangle
  {0x00, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x00},
  // up-left arrow
  {0x1F, 0x1E, 0x1C, 0x1A, 0x11, 0x00, 0x00, 0x00},
  // up-right arrow
  {0x1F, 0x0F, 0x07, 0x0B, 0x11, 0x00, 0x00, 0x00},
  // down-left arrow
  {0x00, 0x00, 0x00, 0x11, 0x1A, 0x1C, 0x1E, 0x1F},
  // down-right arrow
  {0x00, 0x00, 0x00, 0x11, 0x0B, 0x07, 0x0F, 0x1F},
};

#define LONG_DELAY()  delay(1000)
#define SHORT_DELAY() delay(500)
#define ANIM_DELAY()  delay(400)

void setup()
{
  int i;
  int bitmap_size = sizeof(my_bitmap) / sizeof(my_bitmap[0]);

  // init the 1602 (2 rows / 16 columns) LCD
  lcd.begin(16, 2);

  // register the custom bitmaps
  for (i = 0; i < bitmap_size; i++)
  {
    lcd.createChar(i, (uint8_t *)my_bitmap[i]);
  }

  // move the cursor to 0
  lcd.home();
  lcd.print("          Hello!");
  
  // scroll left for 5 positions
  for (int i = 0; i < 5; i++)
  {
    lcd.scrollDisplayLeft();
    
    ANIM_DELAY();
  }

  // clear the LCD contents and the scrolling offset
  lcd.clear();

  // turn off the backlight
  lcd.noBacklight();
  
  LONG_DELAY();

  // turn on the backlight
  lcd.backlight();
  
  lcd.print("     Hello!     ");
  // change to the starting position of the next line
  lcd.setCursor ( 0, 1 );
  lcd.print("  LinkIt 7697   ");

  // blinks the texts
  for (i = 0; i < 2; i++)
  {
    ANIM_DELAY();
    lcd.noDisplay();
    
    ANIM_DELAY();
    lcd.display();
  }
  
  LONG_DELAY();
  LONG_DELAY();

  // clear all LCD contents and settings
  lcd.clear();
  
  SHORT_DELAY();
}

void loop()
{
  int i, ch_map;

  // clear the LCD and set the string direction to left-to-right
  lcd.clear();
  lcd.leftToRight();  
  lcd.print("    LeftToR");
  
  LONG_DELAY();

  lcd.setCursor(4, 0);
  lcd.print("12345678");
  
  SHORT_DELAY();
  
  lcd.setCursor(4, 0);

  // display the custom characters. randomly select a character bitmap
  for (i = 0; i < 8; i++)
  {
    ch_map = random(8);
    
    lcd.print(char(ch_map));

    ANIM_DELAY();
  }

  lcd.setCursor(0, 1);
  lcd.print("    RightToL");
  LONG_DELAY();

  // set the string direction to right-to-left
  lcd.rightToLeft();
  lcd.setCursor(11, 1);
  lcd.print("12345678");
  SHORT_DELAY();
  
  lcd.setCursor(11, 1);

  // display the custom characters. randomly select a character bitmap
  for (i = 0; i < 8; i++)
  {
    ch_map = random(8);

    lcd.print(char(ch_map));

    ANIM_DELAY();
  }
}
```

### 附錄 <a href="#driving1602lcdwithpcf8574pcf8574a-fu-lu" id="driving1602lcdwithpcf8574pcf8574a-fu-lu"></a>

#### 顯示模組的 I2C 地址定義 <a href="#driving1602lcdwithpcf8574pcf8574a-xian-shi-mo-zu-de-i2c-di-zhi-ding-yi-i2caddress" id="driving1602lcdwithpcf8574pcf8574a-xian-shi-mo-zu-de-i2c-di-zhi-ding-yi-i2caddress"></a>

根據 [datasheet](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf) 的描述，**PCF8574A** 預設的 I2C 裝置地址為 **0x3F**：

![](/files/jKAIG1gV44Qt2m3lWSw7)

而 **PCF8574** 的地址為 **0x27**：

![](/files/enAyel2YqQvGEgLRKS2T)

由於驅動函式庫使用 7-bit 的 I2C 地址格式定義，因此使用 0x3F / 0x27 作為裝置地址的設定值。除了預設的地址值外，為了避免電路中有其他 I2C 裝置佔用相同的地址，模組亦提供開發者自訂 I2C 裝置地址的彈性。透過連結下圖黃框中的 A0 / A1 / A2 焊點，即可改變模組的裝置地址。

![](/files/JTYjMV4Bq9E5O1Atf2zU)

PCF8574A / PCF8574 的裝置地址 (參見上表中 Address 欄位的 A0 / A1 / A2) 是由驅動晶片的 A0 / A1 / A2 腳位決定。這三隻腳預設為上拉至高電位，搭配上面 Table 4 及 Table 5 的表格描述，可了解為何裝置預設的地址分別為 0x3F / 0x27。相關腳位對應的電路圖如下：

![](/files/XEbJb1ZvBrkhVa1PoeAy)

舉例來說，若開發者將 0R 電阻上在 A0 焊點，那麼 A0 的地址位元就會從 1 變成 **0**，對應上面的 Address 定義表格，會讓 PCF8574A 的地址從 0x3F 變成 **0x3E** (或者是 PCF8574 的地址從 0x27 變為 **0x26**)。依照同樣的方法調整 A1 和 A2 位元，即可自訂出適合實際應用的 I2C 裝置地址。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduinoide/zhou-bian-yuan-jian-lian-jie-jiao-xue/shi-yong-pcf8574-pcf8574a-qu-dong-1602-lcd.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
