大尺寸 8x8 LED 点阵模块(MAX7219)的多种显示与动态频谱实现
8x8 LED 点阵模块是嵌入式开发中常见的显示外设。本文介绍的大尺寸(6mm 方格)点阵屏通常采用 MAX7219 驱动芯片。该芯片支持串行输入,可级联,并具备 16 级亮度控制。通过简单的 3 线接口(DIN、CS、CLK),即可实现数字、字符以及复杂图形的动态显示。
项目一:字符与中文静态轮播
本方案通过直接操作寄存器的方式,控制 MAX7219 驱动点阵屏轮流显示数字 0-9 以及中文字符"中"和"国"。我们将自定义字模数据存储在二维数组中,并通过位移操作实现串行通信。
/*
硬件连接:
MAX7219 -> Arduino UNO
VCC -> 5V
GND -> GND
DIN -> D12
CS -> D11
CLK -> D10
*/
const int PIN_CLK = 10;
const int PIN_LOAD = 11;
const int PIN_DATA = 12;
// 定义字符字模:0-9, 中, 国
unsigned char glyph_table[12][8] = {
{0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C}, // 0
{0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x08}, // 1
{0x7E, 0x02, 0x02, 0x7E, 0x40, 0x40, 0x40, 0x7E}, // 2
{0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x3E, 0x00}, // 3
{0x08, 0x18, 0x28, 0x48, 0xFE, 0x08, 0x08, 0x08}, // 4
{0x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C, 0x00}, // 5
{0x3C, 0x20, 0x20, 0x3C, 0x24, 0x24, 0x3C, 0x00}, // 6
{0x3E, 0x22, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08}, // 7
{0x00, 0x3E, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x3E}, // 8
{0x3E, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x02, 0x3E}, // 9
{0x08, 0x7F, 0x49, 0x49, 0x7F, 0x08, 0x08, 0x08}, // 中
{0xFE, 0xFE, 0x92, 0xFE, 0x9A, 0xFE, 0x82, 0xFE} // 国
};
void send_byte(unsigned char data) {
for (int i = 8; i >= 1; i--) {
digitalWrite(PIN_CLK, LOW);
digitalWrite(PIN_DATA, (data & 0x80) ? HIGH : LOW);
data <<= 1;
digitalWrite(PIN_CLK, HIGH);
}
}
void write_reg(unsigned char addr, unsigned char val) {
digitalWrite(PIN_LOAD, LOW);
send_byte(addr);
send_byte(val);
digitalWrite(PIN_LOAD, HIGH);
}
void init_screen() {
write_reg(0x09, 0x00); // 译码模式
write_reg(0x0a, 0x03); // 亮度设置
write_reg(0x0b, 0x07); // 扫描界限
write_reg(0x0c, 0x01); // 开启模式
write_reg(0x0f, 0x00); // 测试关闭
}
void setup() {
pinMode(PIN_LOAD, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
pinMode(PIN_DATA, OUTPUT);
init_screen();
}
void loop() {
for (int char_idx = 0; char_idx < 12; char_idx++) {
for (int col = 1; col <= 8; col++) {
write_reg(col, glyph_table[char_idx][col - 1]);
}
delay(1000);
}
}
项目二:利用 Matrix 库实现 Sprite 图形控制
使用 Matrix 库可以更方便地处理点阵显示。通过 Sprite 对象,我们可以预定义图形并直接指定坐标绘制,非常适合需要精准定位的级联显示场景。
#include <Sprite.h>
#include <Matrix.h>
// 参数:DIN, CLK, CS, 模块数量
Matrix canvas = Matrix(12, 10, 11, 3);
Sprite glyph_L = Sprite(5, 8,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11111,
B11111
);
Sprite glyph_E = Sprite(5, 8,
B11111,
B11111,
B11000,
B11110,
B11110,
B11000,
B11111,
B11111
);
void setup() {
canvas.setBrightness(2);
}
void loop() {
canvas.clear();
delay(500);
// 在级联点阵的不同位置写入图形
canvas.write(17, 0, glyph_L);
delay(800);
canvas.write(9, 0, glyph_E);
delay(800);
canvas.clear();
}
项目三:横向滚动文本显示
在长条形级联屏幕中,横向滚动显示是最常见的功能。通过 MaxMatrix 库,可以轻松建立字母库并实现平滑移位。
#include <MaxMatrix.h>
#include <avr/pgmspace.h>
int DATA_IN = 12;
int CS_PIN = 11;
int CLK_PIN = 10;
int MODULE_COUNT = 3;
MaxMatrix led_wall(DATA_IN, CS_PIN, CLK_PIN, MODULE_COUNT);
// 示例字母库片段(空格, !, ")
const unsigned char FONT_LIB[] PROGMEM = {
3, 8, B00000000, B00000000, B00000000,
1, 8, B01011111,
3, 8, B00000011, B00000000, B00000011
};
char message[] = "ARDUINO RUN ";
byte render_buffer[10];
void scroll_text(char* text, int speed) {
while (*text != 0) {
char c = *text - 32;
// 从程序存储区拷贝字模数据
memcpy_P(render_buffer, FONT_LIB + 7 * c, 7);
led_wall.writeSprite(32, 0, render_buffer);
for (int i = 0; i < render_buffer[0] + 1; i++) {
delay(speed);
led_wall.shiftLeft(false, false);
}
text++;
}
}
void setup() {
led_wall.init();
led_wall.setIntensity(5);
}
void loop() {
scroll_text(message, 80);
}
项目四:基于 MAX9812 的动态音乐频谱
结合声音传感器(如 MAX9812),可以将环境音量实时映射到点阵屏上,形成跳动的频率柱。本方案采用峰峰值采样法,并在显示上增加了平滑下降效果。
#include "LedControl.h"
// DIN=12, CS=11, CLK=10, 1个模块
LedControl visualizer = LedControl(12, 11, 10, 1);
const int SENSOR_PIN = A4;
const int SAMPLE_WINDOW = 50; // 50ms采样窗口
unsigned int peak_to_peak = 0;
unsigned int last_level[8] = {0};
void setup() {
visualizer.shutdown(0, false);
visualizer.setIntensity(0, 4);
visualizer.clearDisplay(0);
}
void loop() {
unsigned long start_time = millis();
unsigned int sig_max = 0;
unsigned int sig_min = 1024;
while (millis() - start_time < SAMPLE_WINDOW) {
unsigned int raw_val = analogRead(SENSOR_PIN);
if (raw_val < 1024) {
if (raw_val > sig_max) sig_max = raw_val;
if (raw_val < sig_min) sig_min = raw_val;
}
}
peak_to_peak = sig_max - sig_min;
// 将 0-1023 的模拟量映射到点阵的 0-7 行
int display_height = map(peak_to_peak, 0, 800, 0, 8);
for (int i = 0; i < 8; i++) {
// 简单的缓慢坠落逻辑
if (display_height >= last_level[i]) {
last_level[i] = display_height;
} else if (last_level[i] > 0) {
last_level[i]--;
}
render_column(i, last_level[i]);
}
delay(10);
}
void render_column(int col, int height) {
byte row_data = 0;
for (int i = 0; i < height; i++) {
row_data |= (1 << i);
}
visualizer.setRow(0, col, row_data);
}
在实际应用中,通过级联多个 8x8 模块,可以构建更大面积的显示阵列。结合 FFT(快速傅里叶变换)算法,还可以在此硬件基础上实现更精确的分频频谱仪,展示不同频段的声音动态。