參考資料:https://www.bilibili.com/video/BV1Mb411e7re?p=21
一、點陣LED的理論知識
1. 點陣屏的開啟方式
預設情況下我的點陣屏是無法直接使用的,即使用了正確的程式碼也無法點亮點陣屏,此時我們需要先將點陣附近的跳線帽拔掉或移動到左邊才能開啟點陣屏:
2. LED點陣簡介
- LED點陣屏由若干個獨立的LED組成,LED以矩陣的形式排列,以燈珠亮滅來顯示文字、圖片、視訊等。LED點陣屏廣泛應用於各種公共場合,如汽車報站器、廣告屏以及公告牌等
- LED點陣屏分類
- 按顏色:單色、雙色、全綵
- 按畫素:8 * 8(開發板上的點陣)、16 * 16等(大規模的LED點陣通常由很多個小點陣拼接而成)
3. 點陣LED的結構和操作方式
示意圖:
- LED點陣屏的結構類似於數碼管,只不過是數碼管把每一列的畫素以“8”字型排列而已
- LED點陣屏與數碼管一樣,有共陰和共陽
兩種接法
,不同的接法對應的電路結構不同 - LED點陣屏需要進行逐行或逐列掃描,才能使所有LED同時顯示
例如我們當前希望左上到右下的對角線的LED燈亮,而其他的燈都不亮,可以按照如下方式執行:
- 第一行置一,其他行都置零,同時第一列置零,其他列都置一
即:
置一 | 置零 | |
---|---|---|
行 | 第1行 | 其他行 |
列 | 其他列 | 第1列 |
- 以上的操作是第一個週期的操作,然後第二個週期則和第一個週期基本相同,只是切換了置一和置零的行和列而已,後面往復進行即可
置一 | 置零 | |
---|---|---|
行 | 第2行 | 其他行 |
列 | 其他列 | 第2列 |
通過類似這種的行掃描或列掃描操作,我們即可使我們希望的LED同時發光了。
通俗地說就是:先使一行正常顯示,再使下一行正常顯示,只要速度夠快,就能看到LED同時顯示的情景了。
對應到廠商提供的原理圖:
P00~P07
用於控制列,而DPa~DPh
用於控制行。
4. 74HC595模組
為什麼我們會需要了解這個模組的相關內容呢,我們先仔細觀察一下原理圖:
可以發現這裡的8個輸出對應著LED點陣的8個行輸入,而且這個模組是只有3個輸入端的,這說明了我們可以通過使用這個模組,使用3個輸入即可得到LED點陣的8個行輸入了。
此外,這個圖中還包括了OE
引腳,可以看到它的上面有著一個橫槓,根據b站老師的說法,這樣的寫法表示該模組在低電平時有效,所以我們要使用這個模組之前需要將OE
與GND
相連,這也就是為什麼我們需要先把跳線帽移動到左側才能使用LED點陣模組。
PPT上的簡介
74HC595是序列輸入並行輸出的移位暫存器,可用3根線輸入序列資料,8根線輸出並行資料,多片級聯後,可輸出16位、24位、32位等,常用於IO口擴充套件。
序列轉並行的工作原理
- 首先先設定
SER
的值 - 使用時鐘傳送一個上升訊號,即給
SERCLK
置一,則全部資料向下移動一位,SER
中的資料被寫入到最上面 - 給
SERCLK
置零 - 是否夠8位資料?否:轉1;是:轉5
RCLK
置一,左邊的資料搬運到右邊,8個訊號同時輸出,然後RCLK
置零
級聯操作
如果需要更多的位數輸出,我們可以將QH'
接到下一個模組的SER
,這樣即可完成不斷擴充套件。
二、編碼實現
1. C51中的sfr和sbit【補充知識】
- sfr (special function register) : 特殊功能暫存器宣告
例:sfr P0=0x80;
宣告P0口暫存器,實體地址為0x80
- sbit (special bit) : 特殊位宣告
例:sbit P0_1 = 0x81;
或sbit P0_1 = P0^1;
宣告P0暫存器的第1位 - 可位定址/不可位定址:
在微控制器系統中,操作任意暫存器或者某一位的資料時,必須給出其實體地址,又因為一個暫存器裡有8位,所以位的數量是暫存器數量的8倍,微控制器無法對所有位進行編碼,故每8個暫存器中,只有一個是可以位定址的。對不可位定址的暫存器,若要只操作其中一位而不影響其它位時,可用&=
、|=
、^=
的方法進行位操作
tips:在使用暫存器中的某一位時,我們往往使用類似
sbit LED = P2^0;
的寫法獲取到某一位,但這種在宣告中使用到的'^'符號並不表示異或,而是進行取位。
2. 編寫74HC595的驅動程式
首先定義出三個需要使用的暫存器位
:
sbit RCK = P3 ^ 5;
sbit SRCLK = P3 ^ 6;
sbit SER = P3 ^ 4;
其中RCK
即為原理圖中的RCLK
,但由於和庫中的變數重名了,故改為RCK
。
然後我們需要編寫74HC595模組的驅動程式:
void _74HC595_WriteByte(unsigned char Byte) {
unsigned char i = 0;
// i為計數變數,重複執行8次
for (i = 0; i < 8; i++) {
// 每次取第i位(從左到右),寫入到SER中
SER = Byte & (0x80 >> i);
// SRCLK置一,使暫存器進行移位
SRCLK = 1;
SRCLK = 0;
}
// 將八位輸入到LED的8個行輸入中
RCK = 1;
RCK = 0;
}
即輸入一個byte(八位)的資料,可以並行地輸入到點陣LED的8個行輸入中。
3. 編寫控制單列亮燈的程式
程式碼如下:
void MatrixLED_ShowColumn(unsigned char column, unsigned char Data) {
_74HC595_WriteByte(Data);
// 對某列進行使能,列數為0~7
P0=~(0x80>>column);
}
我們先將資料寫入到點陣LED的左側輸入中,然後對某列進行使能,具體的操作為使P0
暫存器的某位置零,而其他位置一,因此我們可以將0x80
先右移column
位,然後取反,我們即可得到第column
位為0
的資料,接著賦值進去即可。
寫個簡單的程式進行測試:
void main(void) {
SRCLK = 0;
RCK = 0;
_74HC595_WriteByte(0xF0);
while (1) {
MatrixLED_ShowColumn(0,0xAA);
}
}
執行結果:
可以看到,第0列的LED燈按照0xAA【1010 1010】進行顯示了!
4. 控制多列同時發光
首先我們先討論一下消影的操作,當我們如果不進行消影時,段選的資料就會先寫入到上一次的選擇的位置中(在這裡是指上一列),因此就會發現新列中的資料在上一列也顯示了出來。
因此我們需要在每個週期的末尾加上延時和位清零的操作:
void MatrixLED_ShowColumn(unsigned char column, unsigned char Data) {
// 段選
_74HC595_WriteByte(Data);
// 位選
P0 = ~(0x80 >> column);
// 延時
deley(50);
// 位清零
P0 = 0xFF;
}
這樣我們可以先將希望展示的資料寫入到陣列中(8個byte資料,代表每列希望展示的資料),然後通過迴圈列掃描的方式展示出來:
void main(void) {
unsigned char i;
unsigned char Data[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
SRCLK = 0;
RCK = 0;
while (1) {
for (i = 0; i < 8; i++) {
MatrixLED_ShowColumn(i, Data[i]);
}
}
}
執行結果:
可以看到在執行結果中第3列燈【從3開始數】沒法點亮,估計是開發板是個次品,第三列的燈都沒法點亮,現在才發現QAQ。。。
顯示心形
先設計一個心形:
然後根據每列的資料,寫入到陣列中:
unsigned char Data[] = {0x18, 0x3c, 0x7e, 0x3f, 0x3f, 0x7e, 0x3c, 0x18};
執行結果:
5. 封裝成模組
// MatrixLED.c
#include "MatrixLED.h"
#include <Atmel/REGX52.H>
#include "Deley.h"
sbit RCK = P3 ^ 5;
sbit SRCLK = P3 ^ 6;
sbit SER = P3 ^ 4;
void MatrixLED_Init() {
SRCLK = 0;
RCK = 0;
}
void _74HC595_WriteByte(unsigned char Byte) {
unsigned char i = 0;
for (i = 0; i < 8; i++) {
SER = Byte & (0x80 >> i);
SRCLK = 1;
SRCLK = 0;
}
RCK = 1;
RCK = 0;
}
void MatrixLED_ShowColumn(unsigned char column, unsigned char Data) {
_74HC595_WriteByte(Data);
// 對某列進行使能,列數為0~7
MATRIX_LED_PORT = ~(0x80 >> column);
deley(50);
MATRIX_LED_PORT = 0xFF;
}
而MatrixLED.h
檔案只需加上三個函式的函式宣告即可。
6. LED點陣動畫
使用字模提取工具
我們需要先將【位元組倒序】的勾去掉:
然後點選【新建影像】按鈕新建影像,選擇8和32
我們畫出“Hello!”這樣的文字:
然後我們點選【取模方法】>【C51格式】即可得到影像的編碼:
然後我們將這些資料複製到程式碼中即可:
unsigned char animation[] = {
0x7F,0x08,0x08,0x08,0x7F,0x00,0x0E,0x15,0x15,0x15,0x08,0x00,0x7E,0x01,0x02,0x00,
0x7E,0x01,0x02,0x00,0x0E,0x11,0x11,0x0E,0x00,0x7D,0x00,0x00,0x00,0x00,0x00,0x00
}
為了使字幕經過之後可以有一段空白,我們需要為陣列的前後都加上8個0x00
:
unsigned char animation[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x0E, 0x15, 0x15, 0x15, 0x08, 0x00, 0x7E, 0x01, 0x02, 0x00,
0x7E, 0x01, 0x02, 0x00, 0x0E, 0x11, 0x11, 0x0E, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
程式碼編寫
為MatrixLED
模組新增一個函式:
void Matrix_ShowAll(unsigned char arr[], unsigned char startIndex) {
unsigned char i;
for (i = 0; i < 8; i++) {
MatrixLED_ShowColumn(i, arr[i + startIndex]);
}
}
然後主函式:
void main(void) {
unsigned char offset = 0, count = 0;
unsigned char animeSize = sizeof(animation) / sizeof(unsigned char);
MatrixLED_Init();
while (1) {
while (1) {
Matrix_ShowAll(animation, offset);
count++;
if (count == 10) {
count = 0;
break;
}
}
if (offset == animeSize - 8) {
offset = 0;
}
offset++;
}
}
這樣我們即可看到從左向右移動的“Hello!”字幕了!