# 使用 MAX7219 驅動七段顯示器

在前一篇教學裡，介紹了如何利用 74HC595 shift register 驅動七段顯示器的基本概念。在接下來的文章中，將會說明如何使用另一顆 LED 驅動 IC：MAX7219，來驅動七段顯示器 (最高支援八個七段顯示器；換句話說，相當於同時驅動 64 顆 LED)。藉由修改 74HC595 教學範例的程式，使 LinkIt 7697 得以使用[八位數七段顯示器模組](https://robotkingdom.com.tw/product/%E5%85%AB%E4%BD%8D%E6%95%B8%E4%B8%83%E6%AE%B5%E9%A1%AF%E7%A4%BA%E5%99%A8-595%E9%A9%85%E5%8B%95/) (內建 MAX7219)，並透過這個過程讓開發者了解 MAX7219 的主要功能及運作機制。

### 目錄 <a href="#driving7segmentdisplayswithmax7219-mu-lu" id="driving7segmentdisplayswithmax7219-mu-lu"></a>

* 目錄
* 指令格式
* 硬體接線
* 暫存器定義
  * Decode Mode (0x09)
  * Digit 0 \~ Digit 7 data (0x01 \~ 0x08)
  * Intensity (0x0A)
  * Scan Limit (0x0B)
  * Shutdown (0x0C)
  * Display Test (0x0F)
* 範例：顯示遞增數值
* 結論

### 指令格式 <a href="#driving7segmentdisplayswithmax7219-zhi-ling-ge-shi" id="driving7segmentdisplayswithmax7219-zhi-ling-ge-shi"></a>

在深入解析 MAX7219 前，第一步要先了解如何與 MAX7219 溝通。執行任何一項 MAX7219 的功能時，都需要對它發出指令，待 MAX7219 收到指令後，它會依指令更新其內部暫存器的值、進而觸發動作。指令格式為固定 16-bit 長度的「暫存器位址 / 資料數值」組合，各個位元的定義如下 (X 表示可為任意值)：

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2F0wdYG1JIVV4oxQLT4k9f%2F16bit_cmd.png?alt=media\&token=51f1423b-9ea8-4a6a-9aa3-f2d3d71d1a9f)

發送指令的方式為：拉低 **LOAD** 訊號、在 **CLK** 訊號打 16 個時脈的過程中，透過 **DIN** 腳位依序把 16-bit 的指令送進 MAX7219，送完後拉高 **LOAD** 訊號即完成一筆指令的傳輸。對應的時序圖示意為：

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FwgNm2kkEn3SNZeojlFVr%2Ftgram.png?alt=media\&token=d06b8182-6132-48f1-9c85-32443f3de8bd)

透過使用 [shiftOut()](https://www.arduino.cc/en/Reference/ShiftOut) API 產生時脈訊號及傳送資料，在 Arduino 裡的實作如下：

```
// define pins used on LinkIt 7697
// pin 13 of MAX7219 (CLK)
const int clock_pin = 15;
// pin 12 of MAX7219 (LOAD)
const int data_latch_pin = 16;
// pin 1 of MAX7219 (DIN)
const int data_input_pin = 17;
 
// update the register value of MAX7219
void set_register(byte address, byte value)  
{
  digitalWrite(data_latch_pin, LOW);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  digitalWrite(data_latch_pin, HIGH);
}
```

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

LinkIt 7697 透過下列腳位與八位數顯示器模組相連：

* 5V
* GND
* P15
* P16
* P17

該五隻腳位分別接到顯示模組上的：

* VCC
* GND
* CLK
* CS (即 MAX7219 的 LOAD 腳位)
* DIN

接線示意圖為：

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FSR5dQA4HxugXoSDVKLdo%2Fmax7219.png?alt=media\&token=34ce2b2b-c612-428b-ba00-dbeb39aa99ab)

{% hint style="danger" %}
顯示模組需使用 5V 電源輸入，若僅供給 3.3V 則顯示器亮度會受到影響。
{% endhint %}

### 暫存器定義 <a href="#driving7segmentdisplayswithmax7219-zan-cun-qi-ding-yi" id="driving7segmentdisplayswithmax7219-zan-cun-qi-ding-yi"></a>

要順利驅動 MAX7219，需正確設定對應的暫存器。在此範例中，開發者需要設定下列六類型的暫存器：

| 暫存器名稱                   | 位址           | 說明                         |
| ----------------------- | ------------ | -------------------------- |
| Decode Mode             | 0x09         | 開啟 / 關閉 decode 模式          |
| Intensity               | 0x0A         | 調整顯示器亮度                    |
| Scan Limit              | 0x0B         | 設定欲啟動的顯示器數目                |
| Shutdown                | 0x0C         | 開啟 / 關閉顯示模組                |
| Display Test            | 0x0F         | 開啟 / 關閉測試模式 (顯示器的所有筆畫皆會亮起) |
| Digit 0 \~ Digit 7 data | 0x01 \~ 0x08 | 控制位數 0 到位數 7 的顯示資料         |

在 Arduino 範例程式裡將它們定義為：

```
// the MAX7219 address map (datasheet table 2)
#define MAX7219_DECODE_REG      (0x09)
#define MAX7219_DIGIT_REG(pos)  ((pos) + 1)
#define MAX7219_INTENSITY_REG   (0x0A)
#define MAX7219_SCANLIMIT_REG   (0x0B)
#define MAX7219_SHUTDOWN_REG    (0X0C)
#define MAX7219_DISPLAYTEST_REG (0x0F)
```

針對各個暫存器，對應的操作說明及範例如下：

#### Decode Mode (0x09) <a href="#driving7-segmentdisplayswithmax7219-decodemode-0x09" id="driving7-segmentdisplayswithmax7219-decodemode-0x09"></a>

MAX7219 針對每個顯示器，皆分別支援兩種資料顯示模式：1) 原始資料模式 2) BCD 碼模式。例如：

```
set_register(MAX7219_DECODE_REG, B00011010);
```

此例中，位元 1、3、4 被設為 **1**，因此表示位數 1、位數 3、位數 4 被設定為 **BCD 碼 decode 模式**。而剩下的位數 0 / 2 / 5 / 6 / 7 則為**原始資料模式**。

#### Digit 0 \~ Digit 7 data (0x01 \~ 0x08) <a href="#driving7-segmentdisplayswithmax7219-digit0-digit7data-0x01-0x08" id="driving7-segmentdisplayswithmax7219-digit0-digit7data-0x01-0x08"></a>

在**原始資料模式**中，開發者需自行指定七段顯示器中每個筆畫的明滅資訊，就如同 74HC595 教學中提及的一樣。

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FkZKXrT4YDcOsSPFS2j7a%2F7segment.png?alt=media\&token=b570611e-d70c-4838-b348-7aa8a127a1e1)

舉例來說，若開發者想在位數 **3** 顯示數字 "7"，表示七段顯示器的筆畫 "A"、"B"、"C" (參考上圖) 需要被開啟；對應到暫存器的值，即 Digit 3 暫存器的第 4、5、6 個位元 (上圖表格) 需設定為 1：

```
set_register(MAX7219_DIGIT_REG(3), B01110000);
```

雖然在原始資料模式中指定筆畫的方式較為複雜，但它提供了開發者顯示各種圖形組合的彈性：

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FmAZcGHjT3CcEXeCL6T3c%2Fall_comb.png?alt=media\&token=28441658-c5c6-42f8-8731-11532b9a98d4)

除了原始資料模式外，MAX7219 還提供了另一種叫做 **BCD 碼 decode 模式**的操作方法。在 **decode 模**式下，每個字的筆畫構成已被事先定義、並依照 BCD 碼 B 區 (見下表) 的規範儲存在 MAX7219 當中。因此要顯示這些字時，僅需提供每個字所對應的索引編號即可。

&#x20;![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2Fo0jYwXr8Vx8JpBzdYR3k%2Fcodeb.png?alt=media\&token=21e9171a-3e5d-4803-aec9-96dda09bd257)

**\*不論在原始資料模式或是 decode 模式中，若要顯示小數點，都需自行將 Digit 暫存器的第 7 位元 (D7) 設成 1。**

也就是說，若開發者要顯示數字 "0" 到 "9"、"-"、"E"、"H"、"L"、"P" 及 " " (空格) 這十六種字元，在 decode 模式下，不需自行傳送筆畫定義資訊給 MAX7219、直接給 MAX7219 字元的索引值即可。例如，要使用 decode 模式在位數 5 顯示子母 "E"，則需把 Decode Mode 暫存器的 D5 設成 1，接著把 Digit 5 data 暫存器設為 11 (也就是二進位的 B1011，見上表定義)，就可以讓顯示器顯示出字母 "E"。對應的程式碼如下：

```
// Make Digit 5 is under BCD Code B decode mode
set_register(MAX7219_DECODE_REG, B00100000);
// Make it display character "E"
set_register(MAX7219_DIGIT_REG(5), 11);
```

#### Intensity (0x0A) <a href="#driving7-segmentdisplayswithmax7219-intensity-0x0a" id="driving7-segmentdisplayswithmax7219-intensity-0x0a"></a>

調整 LED 顯示器的亮度。值域範圍為 0x0 (最暗) 到 0xF (最亮)。

```
// Dimmest
set_register(MAX7219_INTENSITY_REG, 0x0);
// Brightest
set_register(MAX7219_INTENSITY_REG, 0xF);
```

#### Scan Limit (0x0B) <a href="#driving7-segmentdisplayswithmax7219-scanlimit-0x0b" id="driving7-segmentdisplayswithmax7219-scanlimit-0x0b"></a>

設定模組上要開啟的顯示器個數 (即顯示位數)。下面分別為開啟一個位數、五個位數、以及八位數全開的範例：

```
// Only Digit 0 are turned on
set_register(MAX7219_SCANLIMIT_REG, 0);
// Only Digit 0 ~ Digit 4 are turned on
set_register(MAX7219_SCANLIMIT_REG, 4);
// All digits (Digit 0 ~ Digit 7) are turned on
set_register(MAX7219_SCANLIMIT_REG, 7);
```

#### Shutdown (0x0C) <a href="#driving7-segmentdisplayswithmax7219-shutdown-0x0c" id="driving7-segmentdisplayswithmax7219-shutdown-0x0c"></a>

開啟 / 關閉顯示器模組。

```
#define MAX7219_OFF (0x0)
#define MAX7219_ON  (0x1)

// Turn off the display
set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
// Turn on the display
set_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);
```

#### Display Test (0x0F) <a href="#driving7-segmentdisplayswithmax7219-displaytest-0x0f" id="driving7-segmentdisplayswithmax7219-displaytest-0x0f"></a>

開啟 / 關閉測試模式。開啟此模式時，顯示器的所有筆畫都會亮起。

```
// Enter test mode. No matter what data bytes of each digit register are, all display segments are on
set_register(MAX7219_DISPLAYTEST_REG, MAX7219_ON);
// Non-test mode. Display segments according to the data byte and decode mode for each digit
set_register(MAX7219_DISPLAYTEST_REG, MAX7219_OFF);
```

### 範例：顯示遞增數值 <a href="#driving7segmentdisplayswithmax7219-fan-li-xian-shi-di-zeng-shu-zhi" id="driving7segmentdisplayswithmax7219-fan-li-xian-shi-di-zeng-shu-zhi"></a>

結合上述的暫存器說明並修改之前的 74HC595 範例，下列的 Arduino sketch 展示了如何在八位數七段顯示器模組上顯示遞增數值 (及小數點移動) 的效果。

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

```
//
// Use one MAX7219 to control 8-digit seven-segment display
// the display module: http://www.icshop.com.tw/product_info.php/products_id/20686
//
// MAX7219 datasheet: https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
//

// the MAX7219 address map (datasheet table 2)
#define MAX7219_DECODE_REG      (0x09)
#define MAX7219_INTENSITY_REG   (0x0A)
#define MAX7219_SCANLIMIT_REG   (0x0B)
#define MAX7219_SHUTDOWN_REG    (0X0C)
#define MAX7219_DISPLAYTEST_REG (0x0F)
#define MAX7219_DIGIT_REG(pos)  ((pos) + 1)

// shutdown mode (datasheet table 3)
#define MAX7219_OFF             (0x0)
#define MAX7219_ON              (0x1)

// pin 13 of MAX7219 (CLK)
const int clock_pin = 15;
// pin 12 of MAX7219 (LOAD)
const int data_latch_pin = 16;
// pin 1 of MAX7219 (DIN)
const int data_input_pin = 17;

// digit pattern for a 7-segment display. datasheet table 5
const byte digit_pattern[16] =
{
  B01111110,  // 0
  B00110000,  // 1
  B01101101,  // 2
  B01111001,  // 3
  B00110011,  // 4
  B01011011,  // 5
  B01011111,  // 6
  B01110000,  // 7
  B01111111,  // 8
  B01111011,  // 9
  B01110111,  // A
  B00011111,  // b
  B01001110,  // C
  B00111101,  // d
  B01001111,  // E
  B01000111   // F
};

#define DP_FLAG       (B10000000)
#define NUM_OF_DIGITS (8)

unsigned int counter = 0;
unsigned int digit_base = 16;

// update the register value of MAX7219
void set_register(byte address, byte value)  
{
  digitalWrite(data_latch_pin, LOW);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  digitalWrite(data_latch_pin, HIGH);
}

void init_max7219()
{
  // disable test mode. datasheet table 10
  set_register(MAX7219_DISPLAYTEST_REG, MAX7219_OFF);
  // set medium intensity. datasheet table 7
  set_register(MAX7219_INTENSITY_REG, 0x8);
  // turn off display. datasheet table 3
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  // drive 8 digits. datasheet table 8
  set_register(MAX7219_SCANLIMIT_REG, 7);
  // no decode mode for all positions. datasheet table 4
  set_register(MAX7219_DECODE_REG, B00000000);
}

void setup()  
{
  // init pin states
  pinMode(clock_pin, OUTPUT);
  pinMode(data_latch_pin, OUTPUT);    
  pinMode(data_input_pin, OUTPUT);

  // init MAX2719 states
  init_max7219();
}

void loop()  
{
  int i;
  unsigned int number;
  unsigned int digit_value;
  byte byte_data;
  
  number = counter++;
  
  // turn off display first
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);

  // get every values in each position of those 8 digits based on "digit_base"
  //
  // digit_base should be <= 16
  //
  // for example, if digit_base := 2, binary values will be shown. If digit_base := 16, hexidecimal values will be shown
  //
  for (i = 0; i < NUM_OF_DIGITS; i++)
  {
    digit_value = number % digit_base;
    number /= digit_base;

    byte_data = digit_pattern[digit_value];

    if (counter % NUM_OF_DIGITS == i)
    {
      byte_data |= DP_FLAG;
    }

    set_register(MAX7219_DIGIT_REG(i), byte_data);
  }

  // turn on display
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);
  
  delay(50);
}
```

此範例的基本配置如下：

* 以十六進位遞增數值。
* 使用**原始資料模式**定義字元筆畫，
* 使用中等顯示亮度。

可透過一些修改進行顯示效果的改變。例如，要從原本的 16 進位數值變為 10 進位 / 8 進位 / 2 進位模式時，可修改**第 52 行**的 *digit\_base* 將之設定為 10、8 或是 2。下面的程式碼為修改成 10 進位的範例：

```
// change the counting mode to decimal mode
unsigned int digit_base = 10;
```

若要將資料解讀模式從**原始資料模式**改為 **BCD 碼 decode 模式**，則需要修改兩個地方。首先在**第 74 行**將八個顯示器都設定為 decode 模式：

```
// enable decode mode for all positions. datasheet table 4
set_register(MAX7219_DECODE_REG, B11111111);
```

接著在**第 111 行**，將送進 MAX7219 的資料從筆畫資訊改為索引索引值：

```
digit_value = number % digit_base;
number /= digit_base;
// no need to get the raw segment patterns. use the value directly to index the BCD code
byte_data = digit_value; // byte_data = digit_pattern[digit_value];
```

### 結論 <a href="#driving7segmentdisplayswithmax7219-jie-lun" id="driving7segmentdisplayswithmax7219-jie-lun"></a>

透過本教學的內容，開發者可了解 MAX7219 的使用方法。雖然 MAX7219 的主要功能為驅動七段顯示器，但它也可用於其他的 LED 驅動場景。例如 [8x8 矩陣式顯示器](https://robotkingdom.com.tw/product/max7219dotmatrix/)就是另一種常見的 MAX7219 應用：利用**原始資料模式**的設定方法來控制矩陣式顯示器。詳細的原理及做法，將會在下一篇教學裡進行詳細介紹。
