🤖
LinkIt 7697 BlocklyDuino 使用指南
  • LinkIt 7697 BlocklyDuino 使用指南
    • 認識 LinkIt 7697
    • 認識 BlocklyDuino
    • BlocklyDuino v2 & v3 比較
    • 安裝開發環境 (v2)
      • 安裝 Arduino 編輯器
      • 安裝驅動並連接開發板
      • 安裝 BlocklyDuino 編輯器
      • 連結兩個編輯器並上傳程式
    • 安裝開發環境 (v3)
    • BlocklyDuino 基礎操作
    • 實作範例教學
      • A01. 內建 LED 閃爍
      • A02. 外接 LED 閃爍
      • A03. 外接 LED 呼吸燈
      • A04. RGB LED 顏色變化
      • A05. 外部按鈕控制開關
      • A06. 蜂鳴器播放聲音
      • A07 .可變電阻控制電壓輸出
      • A08. 伺服馬達控制
      • A21. DHT: 溫濕度感應器
      • A22. HTU21D: 溫溼度感應器
      • A23. PMSX003: PM2.5 感測器
      • A24. HC-SR04P: 超音波測距
      • A25. ADXL345: 加速規(動態)
      • A26. ADXL345: 加速規(手勢)
      • A27. 1602/2004: 液晶顯示模組
      • A28. WS2812: RGB 彩色燈條
      • A29. MFRC522: RFID
      • B00. Grove 相關基礎知識
      • B01. Grove 光感應器
      • B02. Grove 超音波測距感應器
      • B03. Grove 溫溼度感應器
      • B04. Grove 繼電器
      • B05. Grove LED 燈條
      • B06. Grove OLED 顯示器
      • C01. 雲端控制 LED 開關
      • C02. 上傳溫溼度到雲端
      • D01. 手機透過 BLE 讀寫裝置資料
      • E01. 遙控器: 按鈕控制 LED
    • 資源鏈結
  • LinkIt 7697 - Arduino IDE 開發指南
    • 環境設定
      • 設定 Arduino IDE
      • 將 LinkIt 7697 連接到電腦
        • 在 Windows 上安裝 CP2102N 驅動程式
        • 在 macOS 上安裝 CP2102N 驅動程式
      • 執行第一個程式
    • 開發指南
      • GPIO
      • UART
      • ADC
      • EINT (外部中斷 / External Interrupt)
      • I2C
      • SPI
      • EEPROM
      • Timer
      • Flash (索引式儲存空間)
      • RTC (Real-Time Clock)
      • Software Serial
      • 使用 Wi-Fi 函式庫
      • 使用 MCS 函式庫
        • 連接到 MCS
        • 連接到 MCSLite
          • 建立可透過 MCS Lite 控制的 Beacon
        • MCS 函式庫 API 使用手冊
          • MCSDevice
          • MCSLiteDeviceMCSLiteDevice
          • MCSDataChannel 相關類別
      • 使用 BLE 低功耗藍牙
        • 藍牙信標
        • 主控裝置
        • 周邊裝置
        • 已知限制
        • LBLE API 指南
          • LBLE
          • LBLECentral
          • LBLEPeripheral
          • LBLEAdvertisementData
          • LBLEService
          • LBLECharacteristic
          • LBLEAddress
          • LBLEUuid
          • LBLEClient
      • 使用 LinkIt Remote
        • LRemote API
        • LRemote 控制元件 API
      • LinkIt 7697 腳位的初始狀態
      • LinkIt 7697 供電說明
      • 更新 Bootloader 與韌體
      • Flash 燒錄工具
      • FOTA 更新
      • LinkIt 7697 的功能限制
      • 自行編譯 Arduino Package
      • 回報問題
    • 週邊元件連接教學
      • SMD 按鈕
      • RGB 三色 LED
      • 可變電阻
      • 九軸感應器 (MPU-9250)
      • 溫度與濕度感應器 (AM2302)
      • 超音波距離感應器
      • 光感應器 (LM358)
      • 0.96 吋 OLED 顯示模組 (SSD1308Z)
      • 長條型 LED 顯示模組 (MY9221)
      • 繼電器
      • 伺服馬達 (EMAX 9g ES08A Servo)
      • 使用 74HC595 驅動七段顯示器
      • 使用 MAX7219 驅動七段顯示器
      • 使用 MAX7219 驅動 8x8 矩陣式顯示器
      • 使用 OLED 模組 (如 SH1106 / SSD1306... 等模組)
      • 使用 PCF8574 / PCF8574A 驅動 1602 LCD
    • 開發套件與擴充板
      • Grove Starter Kit
      • Robot Shield
        • OTTO97
    • 下載
    • 資源連結
    • BSP 版本更新資訊
  • LinkIt 7697 - Development Guide for Arduino IDE
    • Environment Setup
      • Setup Arduino IDE
      • Connecting LinkIt 7697 to Computer
        • Install CP2102N Driver on Windows
        • Install CP2102N Driver on macOS
      • Run Your First Sketch
    • Developer Guide
      • GPIO
      • UART
      • ADC
      • External Interrupt
      • I2C
      • SPI
      • EEPROM
      • Timer
      • Flash (Key-Pair Storage)
      • RTC (Real-Time Clock)
      • Software Serial
      • Using the Wi-Fi Library
      • Using MCS Library
        • Connecting to MCS
        • Connecting to MCSLite
          • Build a Beacon controlled by MCS Lite
        • MCS Library API Reference
          • MCSDevice
          • MCSLiteDevice
          • MCSDataChannel Classes
      • Using Bluetooth
        • Beacons
        • Central Devices
        • Peripheral Devices
        • Limitations of LBLE library
        • LBLE Library API Guide
          • LBLE
          • LBLECentral
          • LBLEPeripheral
          • LBLEAdvertisementData
          • LBLEService
          • LBLECharacteristic
          • LBLEAddress
          • LBLEUuid
          • LBLEClient
      • Using LinkIt Remote
        • LRemote
        • LRemote Control Classes
      • Initial Pin State of LinkIt 7697
      • Powering the LinkIt 7697
      • Update Bootloader and Firmware
      • Flash Uploading Tool
      • FOTA Update
      • Limitations of LinkIt 7697
      • Source Code of Arduino Package
      • Issue Report
    • Tutorial
      • SMD Buttons
      • RGB LED
      • Potentiometer
      • IMU 9DOF v2.0 (MPU-9250)
      • Temperature and Humidity Sensor Pro (AM2302)
      • Grove Ultrasonic Ranger
      • Light Sensor (LM358)
      • Grove OLED Display 0.96" (SSD1308Z)
      • LED Bar v2.0 (MY9221)
      • Relay
      • Mini Servo (EMAX 9g ES08A Servo)
      • Driving 7-segment Displays with 74HC595
      • Driving 7-segment Displays with MAX7219
      • Driving 8x8 Dot Matrices with MAX7219
      • Using OLED module (SH1106 / SSD1306... etc.)
      • Driving 1602 LCD with PCF8574 / PCF8574A
    • Kits and Shields
      • Grove Starter Kit
      • Robot Shield
        • OTTO97
    • Download
    • Resources
    • BSP Release Notes
Powered by GitBook
On this page
  • 目錄
  • 七段顯示器與矩陣顯示器的對應關係
  • 範例一:使用單個顯示器
  • 範例二:串連多個顯示器
  • 範例三:字串跑馬燈
  • 結論
  1. LinkIt 7697 - Arduino IDE 開發指南
  2. 週邊元件連接教學

使用 MAX7219 驅動 8x8 矩陣式顯示器

Previous使用 MAX7219 驅動七段顯示器Next使用 OLED 模組 (如 SH1106 / SSD1306... 等模組)

Last updated 3 years ago

除了用於,MAX7219 另一種常見的應用為驅動 8x8 矩陣式 LED 顯示器。此教學文章將介紹如何使用單顆 MAX7219 驅動 8x8 矩陣式顯示器,以及如何串聯 (daisy-chain) 數個 MAX7219 以驅動多個 8x8 顯示矩陣。為了降低電路複雜度,本文使用 (內建 MAX7219) 作為範例使用元件。

目錄

  • 目錄

  • 七段顯示器與矩陣顯示器的對應關係

  • 硬體接線

  • 範例一:使用單個顯示器

  • 範例二:串連多個顯示器

  • 範例三:字串跑馬燈

  • 結論

七段顯示器與矩陣顯示器的對應關係

雖然七段顯示器與 8x8 矩陣式顯示器在外型上有極大的差異,但本質上兩者是相同的東西:皆為內含多顆 LED 的顯示裝置。一個七段顯示器含有 8 顆 LED (筆畫部分使用 7 顆 LED、小數點使用 1 顆 LED),而且根據之前的教學,得知 MAX7219 可同時驅動 8 組七段顯示器,也就是說,MAX7219 最多可驅動 8x8 = 64 顆 LED,而這個數字,正好就是 8x8 矩陣式顯示器內所具有的 LED 數目。因此,讓 MAX7219 以原始資料模式運作,即可用於驅動 8x8 矩陣顯示器。所以,如何將七段顯示器對應至矩陣顯示器?接下來的文章將以實際範例搭配對應程式碼進行說明。

上圖為矩陣顯示器的示意圖。當使用 MAX7219 的原始資料模式時 (若不熟悉 MAX7219 運作模式,可參考之前的教學範例),矩陣顯示器的每個欄位可對應到一個 Digit 暫存器,也就是說,第 0 欄對應到 Digit 0、第 1 欄對應到 Digit 1... 以此類推。而矩陣的每一列則對應到七段顯示器中的不同筆畫,從顯示器最上方開始,第 0 列對應到 Digit 暫存器資料的第 0 個位元 (LSB)、第 1 列對應到 Digit 暫存器的第 1 個位元... 依序下去,直到第 7 列對應到 Digit 暫存器的 MSB (第 7 位元)。以實際範例說明,例如要在矩陣顯示器上顯示數字 "7" 的圖像:

根據圖上的位元資訊,可知 "7" 的點陣圖樣可被表示為:

const byte bitmap_7[] =
{
  // define the dot pattern from column 0 ~ column 7
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, B00000000, B00000000
};

而這些資訊就是後續會送進 Digit 暫存器作為顯示用的原始資料。

硬體接線

需使用 LinkIt 7697 的下列五隻腳位:

  • 5V

  • GND

  • P15

  • P16

  • P17

分別將它們連接至顯示模組上的:

  • VCC

  • GND

  • CLK

  • CS

  • DIN

接線示意圖如下:

範例一:使用單個顯示器

第一個範例將依照前面章節介紹的點陣圖定義方式,在單個 8x8 矩陣式顯示器上顯示不同字元。下面的 sketch 程式碼會驅動顯示器顯示 "Hello, LinkIt7697" 字樣,並呈現如同影片中的效果:

這段程式碼是以之前七段顯示器教學的 sketch 為基礎,修改為驅動矩陣顯示器的版本:

//
// Use one MAX7219 to control a 8x8 dot matrix
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// 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)
#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)

// 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;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  B01000001, B01111111, B01000000, B00000000,   // l
  B01000001, B01111111, B01000000, B00000000,   // l
  B00111000, B01000100, B01000100, B00111000,   // o
  B10110000, B01110000, B00000000, B00000000,   // ,
  B01111111, B01000000, B01000000, B01000000,   // L
  B01001000, B01111010, B01000000, B00000000,   // i
  B01111100, B00000100, B00000100, B01111000,   // n
  B01111111, B00010000, B00101000, B01000100,   // k
  B01000001, B01111111, B01000001, B00000000,   // I
  B00001000, B01111110, B01001000, B00000000,   // t
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00111110, B01001001, B01001001, B00110000,   // 6
  B00000110, B01001001, B01001001, B00111110,   // 9
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

// 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 clear_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_register(MAX7219_COLUMN_REG(i), B00000000);
  }
}

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, 0x1);
  // 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);

  // clear matrix display
  clear_matrix();
}

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

  // init MAX2719 states
  init_max7219();
}

unsigned int char_index = 0;

void loop()  
{
  int i;
  
  // turn off display first
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);

  // display one bitmap
  for (i = 0; i < BYTE_PER_MAP; i++)
  {
    // starting from column 2
    set_register(MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);
  }

  // turn on display
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next character
  char_index++;
  // wrap around the character if needed
  if (char_index >= DISPLAY_STR_LENGTH)
  {
    char_index = 0;
  }
  
  delay(666);
}

程式說明

不同於七段顯示器,當使用 MAX7219 驅動矩陣式顯示器時,程式所要控制的是每個「欄位」的內容、而非每個「數字」的內容。所以為了增加可讀性,程式中將 Digit 暫存器的名稱改為 Column 暫存器:

#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)

並定義顯示器及圖形等相關基本參數:

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  ...
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

注意

由於每個字元只使用 4x8 個像素,所以定義點陣圖資訊時,只定義了其中 4 個欄位,其他欄位則保持空白、不顯示任何東西。

為了顯示自訂的點陣圖,MAX7219 需將每個 Column 暫存器皆設定為以原始資料模式運作:

// no decode mode for all positions. datasheet table 4set_register(MAX7219_DECODE_REG, B00000000);

接著依序將點陣圖資訊送進各個 Column 暫存器,進而組成欲顯示的圖形:

// turn off display firstset_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF); // display one bitmapfor (i = 0; i < BYTE_PER_MAP; i++){  // starting from column 2  set_register(MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);} // turn on displayset_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);

由於只使用 8x8 矩陣中的 4x8 點陣範圍 (並置中顯示),所以程式碼從顯示器的第 2 欄開始更新暫存器資訊,並且一共更新 BYTE_PER_MAP 個欄位。此外,由於顯示器「每一欄」的內容對應到點陣圖資料的「一個 byte」,因此 char_pattern 陣列中每張點陣圖的起始位置,都會是以 BYTE_PER_MAP 為倍數間隔開來。

範例二:串連多個顯示器

MAX7219 支援以串連模式 (daisy-chain) 連接數個 MAX7219 以驅動多個顯示器。當多個 MAX7219 IC 串連在一起時,送進第一顆 IC 的資料會在 16 個時脈訊號 (cycle) 後輸出至下一顆 IC;這個數字剛好與 MAX7219 收取一筆指令所花費的時間相同,因此這也是為何 MAX7219 被稱作為 shift register 的原因:當 IC 本身花 16 個時脈訊號的時間、將資料收進內部的 16-bit 儲存空間後,若還有更多的資料持續送進來,則 IC 會依「先進先出」的順序,開始將內部的 16-bit 資料以 bit 為單位往外輸出。所以,當欲傳送指令給串連在一起的 MAX7219 時,開發者需依「從遠到近」的順序發出給各個 MAX7219 的指令,才能正確地讓指令到達其所對應的 MAX7219。以下用動畫說明此傳輸過程:

此動畫中有三個 MAX7219 串聯在一起,所以若要傳送指令到所有的 MAX7219,則需要 16x3 = 48 個時脈訊號的時間。傳送流程一開始 LOAD 訊號被拉低,使 MAX7219 進入等待指令更新的狀態;在前 16 個時脈訊號中,開發板送出給 MAX7219 #2 (接線上離開發板最遠的 MAX7219) 的指令進入首個節點,接下來的 16 個時脈訊號,再送出給 MAX7219 #1 的指令。此時原本存在首個節點中的指令會被往後推出進到下一個節點。在最後一組的 16 個時脈訊號裡,開發板送出給 MAX7219 #0 的指令,並觸發原本存在首兩個節點中的指令繼續向後推進至最終的目的地。當所有指令都到達正確位置後,將 LOAD 訊號拉高,讓 MAX7219 依收到的指令進行動作。此流程即說明了當串聯在一起的 MAX7219 需接收新指令時,資料傳遞的順序及運作機制。但如果串連在一起的 MAX7219 中,只有單個 MAX7219 需要更新指令、其他的不需新指令,又該如何進行呢?

在接下來的多個矩陣顯示器串連範例中,會將前一個範例擴充為五個顯示器的組態,並透過使用 No-Op 指令,讓每個顯示器單獨顯示與上例相同的圖像:

硬體接線

將每個顯示模組的 DOUT 腳位接至下個顯示模組的 DIN 腳,而其他腳位則直接連接到下個模組的相同腳位即可。

注意

當串連多個矩陣顯示器時,請確保 5V 供電源能提供足夠的電流輸出,以免顯示模組因供電不足而無法正常運作。

以驅動單個顯示器的程式碼為基礎,將之修改為可驅動串連顯示器的版本。首先,先定義 No-Op 指令:

#define MAX7219_NOOP_REG        (0x00)

接著將 set_register() 擴充為 set_all_registers() 以及 set_single_register(),分別用來對串連在一起的 MAX7219 下指令。set_all_registers() 的作用為對所有 MAX7219 皆發出相同的指令,而 set_single_register() 則是針對單一 MAX7219 進行指令更新 (其他的 MAX7219 則不改變狀態):

// update a specific register value of all MAX7219svoid set_all_registers(byte address, byte value){  digitalWrite(data_latch_pin, LOW);   for (int i = 0; i < NUM_OF_MATRIXES; i++)  {    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);  }    digitalWrite(data_latch_pin, HIGH);} // only update the register in one MAX7219void set_single_register(int index, byte address, byte value){  // only process within valid index range  if (index >= 0 && index < NUM_OF_MATRIXES)  {    digitalWrite(data_latch_pin, LOW);     for (int i = NUM_OF_MATRIXES - 1; i >= 0; i--)    {      // for specified MAX7219, access the desired register      if (i == index)      {        shiftOut(data_input_pin, clock_pin, MSBFIRST, address);          }      else      {        // send No-Op operation to all other MAX7219s (the value is "don't-care" for No-Op command)        shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_NOOP_REG);      }            shiftOut(data_input_pin, clock_pin, MSBFIRST, value);    }      digitalWrite(data_latch_pin, HIGH);  }}

綜合上述改動,並利用這兩個 API 取代原本驅動單個顯示器範例中的 set_register() API 後,完整的程式範例如下:

//
// Use one MAX7219 to control 5 8x8 dot matrixes
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// 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)
#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)
#define MAX7219_NOOP_REG        (0x00)

// 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;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)
// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  B01000001, B01111111, B01000000, B00000000,   // l
  B01000001, B01111111, B01000000, B00000000,   // l
  B00111000, B01000100, B01000100, B00111000,   // o
  B10110000, B01110000, B00000000, B00000000,   // ,
  B01111111, B01000000, B01000000, B01000000,   // L
  B01001000, B01111010, B01000000, B00000000,   // i
  B01111100, B00000100, B00000100, B01111000,   // n
  B01111111, B00010000, B00101000, B01000100,   // k
  B01000001, B01111111, B01000001, B00000000,   // I
  B00001000, B01111110, B01001000, B00000000,   // t
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00111110, B01001001, B01001001, B00110000,   // 6
  B00000110, B01001001, B01001001, B00111110,   // 9
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

// update a specific register value of all MAX7219s
void set_all_registers(byte address, byte value)
{
  digitalWrite(data_latch_pin, LOW);

  for (int i = 0; i < NUM_OF_MATRIXES; i++)
  {
    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  }
  
  digitalWrite(data_latch_pin, HIGH);
}

// only update the register in one MAX7219
void set_single_register(int index, byte address, byte value)
{
  // only process for valid index range
  if (index >= 0 && index < NUM_OF_MATRIXES)
  {
    digitalWrite(data_latch_pin, LOW);

    for (int i = NUM_OF_MATRIXES - 1; i >= 0; i--)
    {
      // for specified MAX7219, access the desired register
      if (i == index)
      {
        shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    
      }
      else
      {
        // send No-Op operation to all other MAX7219s (the value is "don't-care" for No-Op command)
        shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_NOOP_REG);
      }
      
      shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
    }
  
    digitalWrite(data_latch_pin, HIGH);
  }
}

void clear_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_all_registers(MAX7219_COLUMN_REG(i), B00000000);
  }
}

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

  // clear matrix display
  clear_matrix();
}

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

  // init MAX2719 states
  init_max7219();
}

unsigned int char_index = 0;
unsigned int matrix_index = 0;

void loop()  
{
  int i;
  
  // turn off display first
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);

  // display one bitmap
  for (i = 0; i < BYTE_PER_MAP; i++)
  { 
    // starting from column 2
    set_single_register(matrix_index, MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);
  }

  // turn on display
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next character
  char_index++;
  // wrap around the character if needed
  if (char_index >= DISPLAY_STR_LENGTH)
  {
    char_index = 0;

    // move to the next matrix
    matrix_index++;

    if (matrix_index >= NUM_OF_MATRIXES)
    {
      clear_matrix();
      matrix_index = 0;
    }
  }
  
  delay(400);
}

此例使用了 5 個顯示器模組,若要更改模組數目,可直接修改第 34 行的定義:

// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

重點回顧

  • 若要對所有的 MAX7219 下同樣的指令,請使用 set_all_registers() API,就如同 init_max7219() 以及 clear_matrix() 皆為相同的概念。

  • 若只要對特定的 MAX7219 下指令,請使用 set_single_register() API,即可讓其他 MAX7219 不受影響。

範例三:字串跑馬燈

結合前面所述的概念,最後一個範例將展示如何進一步使用 5 個矩陣顯示器產生常見的跑馬燈效果:

完整的範例程式碼如下:

//
// Use multiple 8x8 dot matrix modules to display a marquee string
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// 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_COLUMN_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;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)

// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B00000000, B00000000, B01111111, B00001000, B00001000, B01111111, // H
  B00000000, B00000000, B00111000, B01010100, B01010100, B00011000, // e
  B00000000, B00000000, B01000001, B01111111, B01000000, B00000000, // l
  B00000000, B00000000, B01000001, B01111111, B01000000, B00000000, // l
  B00000000, B00000000, B00111000, B01000100, B01000100, B00111000, // o
  B00000000, B00000000, B10110000, B01110000, B00000000, B00000000, // ,
  B00000000, B00000000, B01111111, B01000000, B01000000, B01000000, // L
  B00000000, B00000000, B01001000, B01111010, B01000000, B00000000, // i
  B00000000, B00000000, B01111100, B00000100, B00000100, B01111000, // n
  B00000000, B00000000, B01111111, B00010000, B00101000, B01000100, // k
  B00000000, B00000000, B01000001, B01111111, B01000001, B00000000, // I
  B00000000, B00000000, B00001000, B01111110, B01001000, B00000000, // t
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, // 7
  B00000000, B00000000, B00111110, B01001001, B01001001, B00110000, // 6
  B00000000, B00000000, B00000110, B01001001, B01001001, B00111110, // 9
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, // 7
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000  // space
};

#define DISPLAY_COLUMN_LENGTH (sizeof(char_pattern))

// update a specific register value of all MAX7219s
void set_all_registers(byte address, byte value)
{
  digitalWrite(data_latch_pin, LOW);

  for (int i = 0; i < NUM_OF_MATRIXES; i++)
  {
    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  }
  
  digitalWrite(data_latch_pin, HIGH);
}

void clear_all_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_all_registers(MAX7219_COLUMN_REG(i), B00000000);
  }
}

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

  // clear matrix display
  clear_all_matrix();
}

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

  // init MAX2719 states
  init_all_max7219();
}

unsigned int starting_column_index = 0;

void loop()  
{
  int i, j;
  int target_column;

  // turn off display first
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  
  // update all columns
  for (i = 0; i < NUM_OF_COLUMNS; i++)
  {
    digitalWrite(data_latch_pin, LOW);
    
    // update all matrixes
    for (j = NUM_OF_MATRIXES - 1; j >= 0; j--)
    {
      // jump to the correct column position for each matrix
      target_column = starting_column_index + NUM_OF_COLUMNS * j + i;
      //  make sure the target column is within the bitmap array
      target_column %= DISPLAY_COLUMN_LENGTH;
      
      shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_COLUMN_REG(i));
      shiftOut(data_input_pin, clock_pin, MSBFIRST, char_pattern[target_column]);
    }

    // latch the data pin to make the register data takes effect
    digitalWrite(data_latch_pin, HIGH);
  }
  
  // turn on display
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next column
  starting_column_index++;
  // go back to the first column of the string bitmap to be displayed
  if (starting_column_index >= DISPLAY_COLUMN_LENGTH)
  {
    starting_column_index = 0;
  }

  delay(200);
}

結論

接續前篇的七段顯示器教學,本文完整地介紹了另一種 MAX7219 應用:不只用於驅動七段顯示器,利用原始資料模式的操作,MAX7219 也可用於驅動多個 LED、或是驅動矩陣式 LED。藉由使用 shift register,可大幅簡化建置 LED 顯示應用時的佈線及控制複雜度。

完成硬體線路連接後,接下來的文章將示範如何在顯示器上顯示圖案。

MAX7219 裡有一個特殊的暫存器,稱為 No-Op 暫存器 (位址為 0,請參閱 中的 Table 2)。當 MAX7219 在 LOAD 訊號為 HIGH 時看到 No-Op 指令,將不會進行任何動作、狀態也不會改變。下面的動畫展示「只有 MAX7219 #1 需收取新指令、其他節點皆不改變狀態」的狀況:

datasheet
驅動七段顯示器
8x8 矩陣式顯示模組