Руководство по 8x8 точечной матрице MAX7219 с Arduino + игра Pong
Точечная матрица, которую мы будем использовать в этом руководстве, представляет собой матрицу 8x8, что означает, что она имеет 8 столбцов и 8 строк, то есть содержит в общей сложности 64 светодиода.
Микросхема MAX7219 упрощает управление точечной матрицей, используя всего 3 цифровых вывода платы Arduino.
Лучший вариант — купить точечную матрицу с микросхемой MAX7219 в виде готового модуля, это упростит подключение. Вы можете посмотреть точечную матрицу на Maker Advisor и найти лучшую цену.
Вы можете управлять несколькими матрицами одновременно. Для этого нужно просто соединить их друг с другом, так как они имеют выводы с обеих сторон для расширения точечной матрицы.
Необходимые компоненты
Для этого руководства вам понадобятся:
Arduino UNO — читайте Лучшие стартовые наборы Arduino
Вы можете использовать приведенные выше ссылки или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!
Подключение выводов
Вам нужно подключить всего 5 выводов от точечной матрицы к плате Arduino. Подключение довольно простое:
Вывод точечной матрицы |
Подключение к Arduino Uno |
|---|---|
GND |
GND |
VCC |
5V |
DIN |
Цифровой вывод |
CS |
Цифровой вывод |
CLK |
Цифровой вывод |
Как управлять точечной матрицей с помощью Arduino
Для упрощения управления точечной матрицей вам нужно скачать и установить в Arduino IDE библиотеку LedControl. Чтобы установить библиотеку, выполните следующие шаги:
Нажмите здесь, чтобы скачать библиотеку LedControl. У вас должен появиться .zip-файл в папке загрузок
Распакуйте .zip-файл, и вы получите папку LedControl-master
Переименуйте папку из LedControl-master в LedControl
Переместите папку LedControl в папку libraries вашей установки Arduino IDE
Наконец, перезапустите Arduino IDE
Использование функций библиотеки LedControl
Самый простой способ отобразить что-либо на точечной матрице — использовать функции setLed(), setRow() или setColumn(). Эти функции позволяют управлять одним отдельным светодиодом, одной строкой или одним столбцом за раз.
Вот параметры для каждой функции:
*setLed(addr, row, col, state)*
addr — это адрес вашей матрицы, например, если у вас только 1 матрица, значение int addr будет равно нулю.
row — это строка, в которой расположен светодиод
col — это столбец, в которой расположен светодиод
state
Значение true или 1, если вы хотите включить светодиод
Значение false или 0, если вы хотите его выключить
*setRow(addr, row, value)*
*setCol(addr, column, value)*
Индексация
Как было сказано ранее, эта матрица имеет 8 столбцов и 8 строк. Каждый из них индексируется от 0 до 7. Вот рисунок для лучшего понимания:
Если вы хотите отобразить что-либо на матрице, вам просто нужно знать, в определенной строке или столбце, какие светодиоды включены или выключены.
Например, если вы хотите отобразить счастливое лицо, вот что вам нужно сделать:
Код
Вот простой скетч, который отображает три типа лиц: грустное лицо, нейтральное лицо и счастливое лицо. Загрузите следующий код на вашу плату:
/*
Created by Rui Santos
All the resources for this project:
https://randomnerdtutorials.com/
*/
#include "LedControl.h"
#include "binary.h"
/*
DIN connects to pin 12
CLK connects to pin 11
CS connects to pin 10
*/
LedControl lc=LedControl(12,11,10,1);
// delay time between faces
unsigned long delaytime=1000;
// happy face
byte hf[8]= {B00111100,B01000010,B10100101,B10000001,B10100101,B10011001,B01000010,B00111100};
// neutral face
byte nf[8]={B00111100, B01000010,B10100101,B10000001,B10111101,B10000001,B01000010,B00111100};
// sad face
byte sf[8]= {B00111100,B01000010,B10100101,B10000001,B10011001,B10100101,B01000010,B00111100};
void setup() {
lc.shutdown(0,false);
// Set brightness to a medium value
lc.setIntensity(0,8);
// Clear the display
lc.clearDisplay(0);
}
void drawFaces(){
// Display sad face
lc.setRow(0,0,sf[0]);
lc.setRow(0,1,sf[1]);
lc.setRow(0,2,sf[2]);
lc.setRow(0,3,sf[3]);
lc.setRow(0,4,sf[4]);
lc.setRow(0,5,sf[5]);
lc.setRow(0,6,sf[6]);
lc.setRow(0,7,sf[7]);
delay(delaytime);
// Display neutral face
lc.setRow(0,0,nf[0]);
lc.setRow(0,1,nf[1]);
lc.setRow(0,2,nf[2]);
lc.setRow(0,3,nf[3]);
lc.setRow(0,4,nf[4]);
lc.setRow(0,5,nf[5]);
lc.setRow(0,6,nf[6]);
lc.setRow(0,7,nf[7]);
delay(delaytime);
// Display happy face
lc.setRow(0,0,hf[0]);
lc.setRow(0,1,hf[1]);
lc.setRow(0,2,hf[2]);
lc.setRow(0,3,hf[3]);
lc.setRow(0,4,hf[4]);
lc.setRow(0,5,hf[5]);
lc.setRow(0,6,hf[6]);
lc.setRow(0,7,hf[7]);
delay(delaytime);
}
void loop(){
drawFaces();
}
В итоге у вас получится что-то вроде этого:
Игра Pong
Игра Pong, которую вы сейчас попробуете, была создана Alessandro Pasotti.
Для игры Pong вам нужно просто добавить потенциометр 1 кОм к предыдущей схеме. Соберите новую схему, как показано ниже:
Код игры Pong
Затем загрузите следующий код на вашу плату Arduino:
/*
* Play pong on an 8x8 matrix - project from itopen.it
*/
#include "LedControl.h"
#include "Timer.h"
#define POTPIN A5 // Potentiometer
#define PADSIZE 3
#define BALL_DELAY 200
#define GAME_DELAY 10
#define BOUNCE_VERTICAL 1
#define BOUNCE_HORIZONTAL -1
#define NEW_GAME_ANIMATION_SPEED 50
#define HIT_NONE 0
#define HIT_CENTER 1
#define HIT_LEFT 2
#define HIT_RIGHT 3
//#define DEBUG 1
byte sad[] = {
B00000000,
B01000100,
B00010000,
B00010000,
B00000000,
B00111000,
B01000100,
B00000000
};
byte smile[] = {
B00000000,
B01000100,
B00010000,
B00010000,
B00010000,
B01000100,
B00111000,
B00000000
};
Timer timer;
LedControl lc = LedControl(12,11,10,1);
byte direction; // Wind rose, 0 is north
int xball;
int yball;
int yball_prev;
byte xpad;
int ball_timer;
void setSprite(byte *sprite){
for(int r = 0; r < 8; r++){
lc.setRow(0, r, sprite[r]);
}
}
void newGame() {
lc.clearDisplay(0);
// initial position
xball = random(1, 7);
yball = 1;
direction = random(3, 6); // Go south
for(int r = 0; r < 8; r++){
for(int c = 0; c < 8; c++){
lc.setLed(0, r, c, HIGH);
delay(NEW_GAME_ANIMATION_SPEED);
}
}
setSprite(smile);
delay(1500);
lc.clearDisplay(0);
}
void setPad() {
xpad = map(analogRead(POTPIN), 0, 1020, 8 - PADSIZE, 0);
}
void debug(const char* desc){
#ifdef DEBUG
Serial.print(desc);
Serial.print(" XY: ");
Serial.print(xball);
Serial.print(", ");
Serial.print(yball);
Serial.print(" XPAD: ");
Serial.print(xpad);
Serial.print(" DIR: ");
Serial.println(direction);
#endif
}
int checkBounce() {
if(!xball || !yball || xball == 7 || yball == 6){
int bounce = (yball == 0 || yball == 6) ? BOUNCE_HORIZONTAL : BOUNCE_VERTICAL;
#ifdef DEBUG
debug(bounce == BOUNCE_HORIZONTAL ? "HORIZONTAL" : "VERTICAL");
#endif
return bounce;
}
return 0;
}
int getHit() {
if(yball != 6 || xball < xpad || xball > xpad + PADSIZE){
return HIT_NONE;
}
if(xball == xpad + PADSIZE / 2){
return HIT_CENTER;
}
return xball < xpad + PADSIZE / 2 ? HIT_LEFT : HIT_RIGHT;
}
bool checkLoose() {
return yball == 6 && getHit() == HIT_NONE;
}
void moveBall() {
debug("MOVE");
int bounce = checkBounce();
if(bounce) {
switch(direction){
case 0:
direction = 4;
break;
case 1:
direction = (bounce == BOUNCE_VERTICAL) ? 7 : 3;
break;
case 2:
direction = 6;
break;
case 6:
direction = 2;
break;
case 7:
direction = (bounce == BOUNCE_VERTICAL) ? 1 : 5;
break;
case 5:
direction = (bounce == BOUNCE_VERTICAL) ? 3 : 7;
break;
case 3:
direction = (bounce == BOUNCE_VERTICAL) ? 5 : 1;
break;
case 4:
direction = 0;
break;
}
debug("->");
}
// Check hit: modify direction is left or right
switch(getHit()){
case HIT_LEFT:
if(direction == 0){
direction = 7;
} else if (direction == 1){
direction = 0;
}
break;
case HIT_RIGHT:
if(direction == 0){
direction = 1;
} else if(direction == 7){
direction = 0;
}
break;
}
// Check orthogonal directions and borders ...
if((direction == 0 && xball == 0) || (direction == 4 && xball == 7)){
direction++;
}
if(direction == 0 && xball == 7){
direction = 7;
}
if(direction == 4 && xball == 0){
direction = 3;
}
if(direction == 2 && yball == 0){
direction = 3;
}
if(direction == 2 && yball == 6){
direction = 1;
}
if(direction == 6 && yball == 0){
direction = 5;
}
if(direction == 6 && yball == 6){
direction = 7;
}
// "Corner" case
if(xball == 0 && yball == 0){
direction = 3;
}
if(xball == 0 && yball == 6){
direction = 1;
}
if(xball == 7 && yball == 6){
direction = 7;
}
if(xball == 7 && yball == 0){
direction = 5;
}
yball_prev = yball;
if(2 < direction && direction < 6) {
yball++;
} else if(direction != 6 && direction != 2) {
yball--;
}
if(0 < direction && direction < 4) {
xball++;
} else if(direction != 0 && direction != 4) {
xball--;
}
xball = max(0, min(7, xball));
yball = max(0, min(6, yball));
debug("AFTER MOVE");
}
void gameOver() {
setSprite(sad);
delay(1500);
lc.clearDisplay(0);
}
void drawGame() {
if(yball_prev != yball){
lc.setRow(0, yball_prev, 0);
}
lc.setRow(0, yball, byte(1 << (xball)));
byte padmap = byte(0xFF >> (8 - PADSIZE) << xpad) ;
#ifdef DEBUG
//Serial.println(padmap, BIN);
#endif
lc.setRow(0, 7, padmap);
}
void setup() {
// The MAX72XX is in power-saving mode on startup,
// we have to do a wakeup call
pinMode(POTPIN, INPUT);
lc.shutdown(0,false);
// Set the brightness to a medium values
lc.setIntensity(0, 8);
// and clear the display
lc.clearDisplay(0);
randomSeed(analogRead(0));
#ifdef DEBUG
Serial.begin(9600);
Serial.println("Pong");
#endif
newGame();
ball_timer = timer.every(BALL_DELAY, moveBall);
}
void loop() {
timer.update();
// Move pad
setPad();
#ifdef DEBUG
Serial.println(xpad);
#endif
// Update screen
drawGame();
if(checkLoose()) {
debug("LOOSE");
gameOver();
newGame();
}
delay(GAME_DELAY);
}
Демонстрация
Вот финальная демонстрация того, как я играю в игру Pong. Веселитесь!
Подведение итогов
Вы когда-нибудь использовали точечную матрицу в своих проектах Arduino?
Оставьте комментарий ниже, чтобы поделиться своим опытом.