8x8 LED 点阵模块
之前我们已经点亮过数码管了,今天我们来学习 8x8 点阵 LED 屏,先通过点阵屏显示一个图案,最终实现使用 PS2 摇杆控制点阵屏制作一个 LED 移动的小游戏。
实验原理
LED 8*8 点阵屏模块是一种常见的 LED 屏幕模块,它由 8 行 8 列的 LED 点阵组成。每个 LED 点可以控制亮灭,通过对每个点的亮灭状态的控制,可以在屏幕上显示出各种图案和文字等信息。
这个 8x8 点阵的原理,其实与数码管是一样的。看看下面的原理图就知道了。
从图中可以看出,LED 点阵屏由 64 个发光二极管组成,且每个发光二极管是放置在行线和列线的交叉点上,当对应的某一列置 1 电平,某一行置 0 电平,则相应的二极管就亮,我们这款点阵屏是共阴型的,共阳型则相反,给列置 0 电平,给行置 1 电平。
直接使用 I/O 口驱动,占用较多的 I/O 口资源,特别是随着点阵屏的数量增加,所以一般的应用,会选择专用的驱动芯片,例如 74HC595,MAX7219 等等。
硬件电路设计
物料清单(BOM 表):
材料名称 | 数量 |
---|---|
PS2 摇杆模块 | 1 |
8*8 LED 点阵屏 | 1 |
杜邦线(跳线) | 若干 |
PS2 模块的 +5V 引脚接 ESP32 的 3V3 引脚,GND 接 GND,SW 接 D34,X 接 D15,Y 接 D35。
LED 点阵屏的 1-8 分别接 D23、D22、D21、D19、D18、D5、D4、D2;9 - 16 分别接 D13、D12、D14、D27、D26、D25、D33、D32。
软件程序设计
1. 循环遍历所有 LED
我们第一个程序就先写检测一下是否所有的 LED 都可以正常工作,代码如下:
// 定义行引脚数组
int row_array[8] = {13, 25, 2, 27, 23, 4, 22, 18};
// 定义列引脚数组
int col_array[8] = {26, 21, 19, 12, 5, 14, 33, 32};
void setup() {
// 配置所有行引脚为输出模式,初始化为高电平
for (int i=0;i<8;i++) {
pinMode(row_array[i], OUTPUT);
digitalWrite(row_array[i], HIGH);
}
// 配置所有列引脚为输出模式,初始化为低电平
for (int i=0;i<8;i++) {
pinMode(col_array[i], OUTPUT);
digitalWrite(col_array[i], LOW);
}
}
void loop() {
// 遍历所有的 LED
for (int i=0;i<8;i++) {
digitalWrite(row_array[i], LOW);
for (int j=0;j<8;j++) {
digitalWrite(col_array[j], HIGH);
delay(100);
digitalWrite(col_array[j], LOW);
}
digitalWrite(row_array[i], HIGH);
}
}
2. 在点阵屏上显示图案
这里我们需要再次用到取模软件,与之前不同的是,这里我们需要新建图像并且,设置图像的宽和高为 8,这样就能保证与我们的点阵屏对应。
字模设置也需要改一下,
接着在屏幕上画出我们想要显示的图案,比如一个爱心,然后生成字模。
这里,我们获取了一个 8 个 16 进制数的列表,每个 16 进制数转换成二进制就是 LED 点阵屏每行所有 LED 的逻辑,
{0x00,0x66,0xFF,0xFF,0xFF,0x7E,0x3C,0x18}
在 Arduino 中,想要把 16 进制数转二进制时,可以使用 bitRead()
函数,该函数在单片机中使用时比较频繁的,尤其对于数码管以及与数码管类似的存在未操作的器件中使用较多。这里我们介绍一下它的使用方法。
bitRead(x, n)
参数说明:
x
: 被读取位的数值;n
: 被读取的位置(右起第一位为 0 位,第二位为 1,以此类推。)- 返回值:1 或 0。
因此,我们的代码可以这么写:
// 定义行引脚数组
int row_array[8] = {13, 25, 2, 27, 23, 4, 22, 18};
// 定义列引脚数组
int col_array[8] = {26, 21, 19, 12, 5, 14, 33, 32};
// 定义图案逻辑数组
int hex_array[8] = {0x00,0x66,0xFF,0xFF,0xFF,0x7E,0x3C,0x18};
void setup() {
// 设置串口波特率
Serial.begin(9600);
// 配置所有行引脚为输出模式,初始化为高电平
for (int i=0;i<8;i++) {
pinMode(row_array[i], OUTPUT);
digitalWrite(row_array[i], HIGH);
}
// 配置所有列引脚为输出模式,初始化为低电平
for (int i=0;i<8;i++) {
pinMode(col_array[i], OUTPUT);
digitalWrite(col_array[i], LOW);
}
}
void loop() {
for (int i=0;i<8;i++) {
for (int j=0;j<8;j++) {
digitalWrite(col_array[j], bitRead(hex_array[i], j));
}
digitalWrite(row_array[i], LOW);
delay(1);
digitalWrite(row_array[i], HIGH);
}
}
3. 使用 PS2 摇杆控制 LED 移动
最后,我们就可以写摇杆控制 LED移动的代码了:
#define PS2_X 15
#define PS2_Y 35
// 定义行引脚数组
int row_array[8] = {13, 25, 2, 27, 23, 4, 22, 18};
// 定义列引脚数组
int col_array[8] = {26, 21, 19, 12, 5, 14, 33, 32};
// 初始化 LED 位置
int led_pos[2] = {1, 1};
// 初始化摇杆信号变量
int x_value;
int y_value;
void setup() {
// 配置 PS2 摇杆引脚
pinMode(PS2_X, INPUT);
pinMode(PS2_Y, INPUT);
// 配置所有行引脚为输出模式,初始化为高电平
for (int i=0;i<8;i++) {
pinMode(row_array[i], OUTPUT);
digitalWrite(row_array[i], HIGH);
}
// 配置所有列引脚为输出模式,初始化为低电平
for (int i=0;i<8;i++) {
pinMode(col_array[i], OUTPUT);
digitalWrite(col_array[i], LOW);
}
}
void loop() {
// 读取摇杆信号
x_value = analogRead(PS2_X);
y_value = analogRead(PS2_Y);
// 清除 LED 之前的状态
digitalWrite(row_array[led_pos[0]], HIGH);
digitalWrite(col_array[led_pos[1]], LOW);
// 检测 x 轴是否移动
if (x_value > 4095 / 2 + 300 && led_pos[0] < 7) {
led_pos[0] += 1;
}else if (x_value < 4095 / 2 - 300 && led_pos[0] > 0) {
led_pos[0] -= 1;
}
// 检测 y 轴是否移动
if (y_value > 4095 / 2 + 300 && led_pos[1] > 0) {
led_pos[1] -= 1;
}else if (y_value < 4095 / 2 -300 && led_pos[1] < 7) {
led_pos[1] += 1;
}
// 显示新位置的 LED
digitalWrite(row_array[led_pos[0]], LOW);
digitalWrite(col_array[led_pos[1]], HIGH);
delay(50);
}