前言:
感覺 Arduino 平台的崛起, 跟所選用微處理器有很大的關係. 新一代 Atmel 所開發的 ATmega 8位元微處理器, 豐富的 I/O 介面, 完善高階的開發環境, 真的是將垂垂老矣的 8051 遠遠地甩在身後. ATmega 蠻方便的就是已經內建三種重要的資料傳輸匯流介面: USART, I2C 及 SPI. 因為內建的, 所以不需要任何額外的電路, 就可以很方便的跟各種裝置溝通囉.
這篇就來做 SPI 傳輸介面的練習吧: 利用個8x8的 LED 陣列, 來做個迷你跑馬燈囉!
筆者在練習這個的時候, 剛好遇上 2018年4月10日, 是我們最愛的紐西蘭女高音歌手海莉的生日. 身為海莉的鐵竿粉絲, 往年都是送上小禮物例如親自畫的海莉畫像給女神慶祝生日的. 但是今年已經黔驢技窮, 不知道要送什麼了. 剛好利用這個迷你跑馬燈, 簡單地跑個慶祝的話語, 海莉妹妹, 生日快樂喲!
SPI (Serial Peripheral Interface) 介面:
先來簡單介紹一下硬體上很重要的 SPI 介面。 SPI介面的全名是 Serial Peripheral Interface (序列周邊介面),最早是由 Motorola 公司所推出的一種同步全雙工序列介面,它允許兩個裝置之間以同步,全雙工,序列的方式進行高速數據通信。且速度較其他兩個常用的 UART,I2C 介面的速度還要快,所以廣泛的被使用在各種重視傳輸速度的電子裝置上,例如 SD記憶卡,佳能 Canon 相機的 EF接環,數位/類比轉換IC,液晶顯示器,... etc.
SPI 通訊的主軸如下圖所示,其實關鍵是兩的環狀的頭尾相連的8位元位移暫存器,在一個時脈內,頭尾互相交換資料。總共需要四個通訊接腳,定義如下:
MOSI (Master Output Slave Input): 主機往周邊發送訊號的訊號線
MISO (Master Input Slave Output): 周邊往主機發送訊號的訊號線
SS (Slave Select): 周邊通訊選擇 (SPI 可以支援多重周邊通訊),用來指定要連線的週邊設備,邏輯假(Low) 代表選取,邏輯真(High) 代表未選。
SCK (Serial Clock): 主機跟周邊兩邊交換資料時兩邊的時脈基準
以上是單一周邊的連接方式,多周邊的連接方式如下 (這時候 Master 需要針對每個 Slave 都要有一個 SS 周邊選擇致能控制):
特點:
1. 透過主從的方式支援多重周邊通訊:一台主機可以跟多台周邊對談交換資料。主機藉由對不同周邊的 SS 周邊選擇訊號線的致能,選擇所欲交談的對象。
2. 採用同步的方式來傳輸交換數據:主機須於 SCK 訊號線提供兩周邊對談所需的同步時脈訊號。因為採用時脈來同步雙方數據交換,所以雙方通訊的速度由此時脈訊號的速度來決定,理論上最大傳輸速度沒有限制,實際上可以達到 20 Mbit/sec 的高資料傳輸速率。
3. 全雙工的方式主機跟周邊同時交換資料:在一個時脈週期內, MOSI/MISO 兩條線同時對周邊跟主機交換資料。
時脈極性 CPOL (Clock Polarity):定義idle時時脈訊號的電位基準,低電位 CPOL= 0, 高電位 CPOL= 1
時脈相位 CPHA (Clock Phase):定義資料是由時脈的前緣觸發還是後緣觸發。 CPHA=0 時前緣觸發,觸發時採樣。CPHA=1 時後緣觸發,觸發時採樣。
實際上有定義了四種資料模式:
資料模式 | 時脈極性 CPOL | 時脈相位 CPHA |
MODE 01 | 0 | 0 |
MODE 02 | 0 | 1 |
MODE 03 | 1 | 0 |
MODE 04 | 1 | 1 |
Arduino 的 SPI 介面
Arduino 的 ATmega 微處理器內建 SPI 介面,接腳如下定義:
SS: Pin 10
MOSI : Pin 11
MISO: Pin 12
SCK: Pin 13
要控制多周邊的情況下, 假如 Arduino 是主機(Master),主機需要額外的 digital I/O 來做其他周邊的 SS (Slave Select) 選擇。
MAX7219 組裝 8x8 LED陣列:
我們這個迷你跑馬燈,主角是一個 8x8 的 LED 陣列。 這個 LED 陣列總共有 8x8 = 64 個數量龐大的 LED,要控制這麼多的 LED習慣上會採用專門設計的IC來控制。MAX7219 就是這樣的一個控制IC,被設計來控制驅動 8x8 的 LED陣列,或是8個七段顯示器。然後它採用 SPI 介面來跟外界溝通。
所以市面上很容易買到現成已經用 MAX7219 跟 8x8 LED 陣列製作好的模組,透過現成的模組,很方便就可以組成我們的迷你跑馬燈。而控制這種 8x8 LED陣列的主軸,自然轉向如何透過 SPI 通訊介面來跟 MAX7219 溝通!
MAX7219 的控制:
因爲筆者買到的已經是 MAX7219跟 8x8 LED 矩陣在一起的模組囉,所以細部電路就不管他囉!我們只要專心在如何跟 MAX7219 溝通跟控制就好。
MAX7219 內部一共有14個八位元的暫存器,一一解說如下
暫存器名稱 | 位址 (16進位) | 用途 |
Digit 0 | 0x1 | LED矩陣第1行內容 |
Digit 1 | 0x2 | LED矩陣第2行內容 |
Digit 2 | 0x3 | LED矩陣第3行內容 |
Digit 3 | 0x4 | LED矩陣第4行內容 |
Digit 4 | 0x5 | LED矩陣第5行內容 |
Digit 5 | 0x6 | LED矩陣第6行內容 |
Digit 6 | 0x7 | LED矩陣第7行內容 |
Digit 7 | 0x8 | LED矩陣第8行內容 |
No-Op | 0x0 | 未使用 |
Decode Mode | 0x9 | 是否啟用BCD解碼 (七段顯示器 only) |
Intensity | 0xA | LED 顯示亮度 |
Scan Limit | 0xB | 可顯示的 LED 陣列或七段顯示器的數目 |
Shutdown | 0xC | 關閉所有LED,但仍可接收資料 |
Display Test | 0xF | 顯示器測試模式開啟(讓所有LED全亮,目視測試誰壞了) |
MAX7219 的運作方式很簡單,它會不斷的檢視這14個暫存器的內容,根據內容採取對應的控制動作。所以我們只要透過 SPI 介面,根據我們的需求,不斷更新這14個暫存器的內容就可以囉。
MAX7219 SPI 協定:
MAX7219 的封包每次 16位元 (2 byte),前 8位元(低位元組)是資料,給暫存器用。後 4位元(高位元組)是暫存器位址,最後 4位元未使用(高位元組)。
真實傳送時,須從高位元組(暫存器位址)先傳送,再傳送低位元組(8位元資料)
Arduino 的好處是,底層的 SPI 驅動程式碼已經幫我們完成的差不多囉,我們只要把它們引用進來就可以囉。 SPI 的函式庫放在 SPI.h
#include <SPI.h>
所以透過這個函式庫,跟 MAX7219 通訊的核心程式碼如下
void SendTo7219(const byte reg, const byte data) {
digitalWrite(SS, LOW); Slave Select 設為Low, 準備傳送資料
SPI.transfer(reg); 先送後八位元位址,指名暫存器的位址。
SPI.transfer(data); 後送前八位元資料,給暫存器用。
digitalWrite(SS, HIGH); Slave Select 設為High, 結束資料傳送
}
所以透過這個 SendTo7219,就可以自由的透過 SPI 介面,傳送控制 7219 的任一個暫存器的數值,進而控制我們 8x8 LED 矩陣迷你跑馬燈的行為!
MAX7219 的起始設定
底下為一開始設定的程式碼
void setup() {
// put your setup code here, to run once:
SPI.begin(); SPI介面,開始通訊
SendTo7219(DecodeMode,0); DecodeMode=0, 不使用BCD解碼
SendTo7219(Intensity,1); Intensity=1, LED顯示亮度最暗(0-255)
SendTo7219(ScanLimit,7); ScanLimit=8, LED全部使用
SendTo7219(DisplayTest,0); DisplayTest=0, LED測試模式關閉
SendTo7219(Shutdown,1); Shutdown=1, 開啟所有LED顯示
UpdateDisplay();
}
程式碼控制邏輯:
程式碼這邊重點是個 buf[8] 的一個 8 byte 的主矩陣,這個主矩陣上面每個 bit 是跟 8x8 LED 陣列相相呼應,它的資料也對應 7219 的暫存器 Digit 0 - Digit 7。
程式裡面每呼叫一次 UpdateDisplay(), 主矩陣 buf[8] 的資料,立即會跟 7219 裡 Digit 0 - 7 的矩陣資料同步,意即 LED圖形就會立即被更新囉!
void UpdateDisplay() {
for (byte i=0; i<8; i++) {
SendTo7219(i+1,buf[i]);
}
}
再來是字型資料,既然是跑馬燈,就要有所有的英文字型資料。字型資料放在矩陣 font[128][8] 的一個二維矩陣。第一個索引是完全遵照 ASC code 的順序的,所以只要用字元的 ASC code,就可以索引到對應的8x8 共 8 bytes 所組成的英文字母字型資料囉。
const byte font[127][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x00
{ 0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E }, // 0x01
...
最後是字型圖形的捲動
這裏介紹另外一個陣列 msg[], 這裏是來放你所想要顯示的訊息。
這裏的字型資料是有訣竅的,MAX7219 Digit 0 - 7 ,每個暫存器的一個byte 剛好是顯示一行 LED 的點,這裏這些字型資料的字是設計會跟每行LED垂直的。所以要讓字型捲動,只要很簡單的照順序的搬移主矩陣 buf[8] 裡每個 byte,空出來的位置放上下一個字的字型資料。等全部完成後,最後再 UpdateDisplay() 一下,讓它們顯示出來,就搞定囉!人眼也會覺得似乎圖形正從水平方向捲動中呢!
void ScrollChar(byte chr) {
for (byte i=0; i<8; i++) { 掃過下一個字chr 的所有圖形資料
for (byte j=0; j<7; j++) {
buf[j] = buf[j+1]; 搬移主矩陣的資料,往 index=0 移動
}
buf[7]=font[chr][i]; 主矩陣 buf[7] 等於下一個字chr 的資料
UpdateDisplay() ; 更新並顯示圖形
delay(100); 停個 0.1 秒
}
}
8x8 LED 點矩陣模組 (已內建MAX7219):
正面照片
背面照片
迷你跑馬燈硬體接線
整個外型圖
正面照片
背面接腳照片
這裏,還要再次來推廣一下 Arduino 的空白 Shield 擴充卡
Arduino的空白 Shield,通常還有附一塊小麵包版,黏上去空白Shield之後,真的很方便。真的好像樂高積木一樣,只要透過杜邦接頭就可以很容易地連接跟組合,完全不需要動到焊槍就可以完成很多有趣的作品喲!
真的要好好的在 DIY Maker 界,讓我們這種業餘玩家好好的推廣把玩啦!
最後,原始程式碼列表
//
// scrolling text example on LED matrix
// SPI interface practice
//
// Frank Lin 2018.9.30
//
#include <SPI.h>
#define Intensity 0xA
#define DecodeMode 0x9
#define ScanLimit 0xB
#define DisplayTest 0xF
#define Shutdown 0xC
const byte font[127][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x00
{ 0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E }, // 0x01
{ 0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E }, // 0x02
{ 0x0E, 0x1F, 0x3F, 0x7E, 0x3F, 0x1F, 0x0E, 0x00 }, // 0x03
{ 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 }, // 0x04
{ 0x18, 0xBA, 0xFF, 0xFF, 0xFF, 0xBA, 0x18, 0x00 }, // 0x05
{ 0x10, 0xB8, 0xFC, 0xFF, 0xFC, 0xB8, 0x10, 0x00 }, // 0x06
{ 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00 }, // 0x07
{ 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF }, // 0x08
{ 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00 }, // 0x09
{ 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF }, // 0x0A
{ 0x70, 0xF8, 0x88, 0x88, 0xFD, 0x7F, 0x07, 0x0F }, // 0x0B
{ 0x00, 0x4E, 0x5F, 0xF1, 0xF1, 0x5F, 0x4E, 0x00 }, // 0x0C
{ 0xC0, 0xE0, 0xFF, 0x7F, 0x05, 0x05, 0x07, 0x07 }, // 0x0D
{ 0xC0, 0xFF, 0x7F, 0x05, 0x05, 0x65, 0x7F, 0x3F }, // 0x0E
{ 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99 }, // 0x0F
{ 0x7F, 0x3E, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x00 }, // 0x10
{ 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x3E, 0x7F, 0x00 }, // 0x11
{ 0x00, 0x24, 0x66, 0xFF, 0xFF, 0x66, 0x24, 0x00 }, // 0x12
{ 0x00, 0x5F, 0x5F, 0x00, 0x00, 0x5F, 0x5F, 0x00 }, // 0x13
{ 0x06, 0x0F, 0x09, 0x7F, 0x7F, 0x01, 0x7F, 0x7F }, // 0x14
{ 0x40, 0xDA, 0xBF, 0xA5, 0xFD, 0x59, 0x03, 0x02 }, // 0x15
{ 0x00, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00 }, // 0x16
{ 0x80, 0x94, 0xB6, 0xFF, 0xFF, 0xB6, 0x94, 0x80 }, // 0x17
{ 0x00, 0x04, 0x06, 0x7F, 0x7F, 0x06, 0x04, 0x00 }, // 0x18
{ 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 }, // 0x19
{ 0x08, 0x08, 0x08, 0x2A, 0x3E, 0x1C, 0x08, 0x00 }, // 0x1A
{ 0x08, 0x1C, 0x3E, 0x2A, 0x08, 0x08, 0x08, 0x00 }, // 0x1B
{ 0x3C, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00 }, // 0x1C
{ 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x3E, 0x1C, 0x08 }, // 0x1D
{ 0x30, 0x38, 0x3C, 0x3E, 0x3E, 0x3C, 0x38, 0x30 }, // 0x1E
{ 0x06, 0x0E, 0x1E, 0x3E, 0x3E, 0x1E, 0x0E, 0x06 }, // 0x1F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' '
{ 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00, 0x00 }, // '!'
{ 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x00 }, // '"'
{ 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // '#'
{ 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // '$'
{ 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // '%'
{ 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // '&'
{ 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '''
{ 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // '('
{ 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // ')'
{ 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // '*'
{ 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // '+'
{ 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // ','
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // '-'
{ 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // '.'
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // '/'
{ 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // '0'
{ 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // '1'
{ 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // '2'
{ 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // '3'
{ 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // '4'
{ 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // '5'
{ 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // '6'
{ 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // '7'
{ 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // '8'
{ 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // '9'
{ 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // ':'
{ 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // ';'
{ 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // '<'
{ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // '='
{ 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // '>'
{ 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // '?'
{ 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // '@'
{ 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // 'A'
{ 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // 'B'
{ 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // 'C'
{ 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // 'D'
{ 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // 'E'
{ 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // 'F'
{ 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // 'G'
{ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // 'H'
{ 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // 'I'
{ 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // 'J'
{ 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // 'K'
{ 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // 'L'
{ 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // 'M'
{ 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // 'N'
{ 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // 'O'
{ 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // 'P'
{ 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // 'Q'
{ 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // 'R'
{ 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // 'S'
{ 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // 'T'
{ 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // 'U'
{ 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // 'V'
{ 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // 'W'
{ 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // 'X'
{ 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // 'Y'
{ 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // 'Z'
{ 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // '['
{ 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // backslash
{ 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // ']'
{ 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // '^'
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // '_'
{ 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // '`'
{ 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // 'a'
{ 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // 'b'
{ 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // 'c'
{ 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // 'd'
{ 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // 'e'
{ 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // 'f'
{ 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // 'g'
{ 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // 'h'
{ 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // 'i'
{ 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // 'j'
{ 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // 'k'
{ 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // 'l'
{ 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // 'm'
{ 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // 'n'
{ 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // 'o'
{ 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // 'p'
{ 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // 'q'
{ 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // 'r'
{ 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // 's'
{ 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // 't'
{ 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // 'u'
{ 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // 'v'
{ 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // 'w'
{ 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // 'x'
{ 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // 'y'
{ 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // 'z'
{ 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // '{'
{ 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // '|'
{ 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // '}'
{ 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 } // '~'
};
byte buf[8] = {0,0,0,0,0,0,0,0};
char msg[] = { 'H', 'a', 'p', 'p', 'y', ' ', 'B', 'i', 'r', 't', 'h', 'd', 'a', 'y', ' ', 'H', 'a', 'y', 'l', 'e', 'y', ' ', 'W', 'e', 's', 't', 'e', 'n', 'r', 'a', ' '};
void SendTo7219(const byte reg, const byte data) {
digitalWrite(SS, LOW);
SPI.transfer(reg);
SPI.transfer(data);
digitalWrite(SS, HIGH);
}
void UpdateDisplay() {
for (byte i=0; i<8; i++) {
SendTo7219(i+1,buf[i]);
}
}
void ScrollChar(byte chr) {
for (byte i=0; i<8; i++) {
for (byte j=0; j<7; j++) {
buf[j] = buf[j+1];
}
buf[7]=font[chr][i];
UpdateDisplay() ;
delay(100);
}
}
void setup() {
// put your setup code here, to run once:
SPI.begin();
SendTo7219(DecodeMode,0);
SendTo7219(Intensity,1);
SendTo7219(ScanLimit,7);
SendTo7219(DisplayTest,0);
SendTo7219(Shutdown,1);
UpdateDisplay();
}
void loop() {
for (int i=0; i<sizeof(msg); i++) {
ScrollChar(msg[i]);
}
}
xxx