Cat hungry.png
Здравствуйте! Собираем деньги на перевод материалов по электронике(https://www.allaboutcircuits.com/education/). Реквизиты указаны здесь.

Arduino:Примеры/Гайд по использованию светодиодной матрицы MAX7219 с Arduino (плюс игра Pong)

Материал из Онлайн справочника
Перейти к: навигация, поиск

Перевод: Максим Кузьмин (Cubewriter)
Перевел 2686 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Ambox content.png Черновик


Гайд по использованию светодиодной матрицы MAX7219 с Arduino (плюс игра Pong)[1]

Светодиодная матрица, которую мы будем использовать в это статье, состоит из 8 рядов и 8 столбцов. То есть в общей сложности она оснащена 64 светодиодами.

Управление светодиодной матрицей можно упростить при помощи чипа MAX7219 – благодаря ему вам понадобится всего 3 цифровых контакта платы Arduino. Думаю, будет лучше купить модуль, в который уже встроены и светодиодная матрица, и чип MAX7219. Это упростит возню с проводами. Такой модуль можно купить на eBay менее чем за 2 доллара.

Dotmatrixphoto.jpg

Одновременно можно управлять несколькими такими матрицами. Вы даже можете собрать из них большую матрицу, т.к. у них есть специальные контакты для подключения друг к другу.

Необходимые компоненты

  • Одна светодиодная матрица (8 на 8) с чипом MAX7219 (см. на eBay)
  • Одна плата Arduino (см. на eBay)
  • Один потенциометр на 1 кОм
  • Провода-перемычки

Подключение контактов

В подключении светодиодной матрицы к плате Arduino нет ничего сложного. Вам понадобится подключить всего 5 контактов:

  • Контакт GND на матрице – к контакту GND на Arduino
  • Контакт VCC – к контакту 5V
  • Контакт DIN – к цифровому контакту
  • Контакт CS – к цифровому контакту
  • Контакт CLK – к цифровому контакту

Как управлять светодиодной матрицей с помощью Arduino

Чтобы упростить управление матрицей, вам понадобится загрузить и установить в IDE Arduino библиотеку «LedControl». Для этого проделайте следующее:

  • Кликните здесь, чтобы скачать ZIP-архив с библиотекой.
  • Распакуйте скачанный архив. В итоге у вас должна получиться папка под названием «LedControl-master».
  • Переименуйте ее на «LedControl».
  • Переместите папку «LedControl» в папку «libraries» IDE Arduino.
  • Перезапустите IDE Arduino.

Функции библиотеки LedControl

Самый простой способ показать что-нибудь на матрице – это воспользоваться функциями setLed(), setRow() и setColumn(). Они позволяют вам управлять, соответственно, одним светодиодом, одним рядом светодиодов или одним столбцом светодиодов.

Параметры для каждой функции:

  • setled(addr, row, col, state)
    • addr – адрес матрицы; к примеру, если вы используете всего одну матрицу, то в этом параметре должен стоять «0»
    • row – ряд, в котором находится светодиод
    • col – столбец, в котором находится светодиод
    • state – состояние светодиода; true или 1 – чтобы включить светодиод, false или 0 – чтобы выключить
  • setRow(addr, row, value)
  • setCol(addr, row, value)
    • value – значение, которое обозначает, какие светодиоды надо включить

Индекс

Итак, матрица состоит из 8 столбцов и 8 рядов. Каждый из них имеет индекс от «0» до «7». Ниже – рисунок для наглядности:

Index MAX7219 1.jpg

Таким образом, если вы хотите что-то показать на матрице, вам нужно задать ряд или столбец, а также то, какие светодиоды включить, а какие нет.

К примеру, если я хочу показать смайлик, то мне нужно сделать следующее:

Face MAX7219 2.jpg

Код

Ниже – простой скетч, который показывает на матрице три разные рожицы: веселую, «покерфейс» и грустную. Загрузите его на свою Arduino:

  1. /*
  2.  Автор – Руи Сантос (Rui Santos)
  3.  Более подробно о проекте на: http://randomnerdtutorials.com/
  4. */
  5.  
  6. #include "LedControl.h"
  7. #include "binary.h"
  8.  
  9. /*
  10.  Контакт DIN на матрице – к цифровому контакту 12 на Arduino
  11.  Контакт CLK – к цифровому контакту 11
  12.  Контакт CS – к цифровому контакту 10
  13. */
  14. LedControl lc=LedControl(12,11,10,1);
  15.  
  16. // задержка между разными рожицами:
  17. unsigned long delaytime=1000;
  18.  
  19. // веселая рожица:
  20. byte hf[8]= {B00111100,B01000010,B10100101,B10000001,B10100101,B10011001,B01000010,B00111100};
  21. // «покерфейс»:
  22. byte nf[8]={B00111100, B01000010,B10100101,B10000001,B10111101,B10000001,B01000010,B00111100};
  23. // грустная рожица:
  24. byte sf[8]= {B00111100,B01000010,B10100101,B10000001,B10011001,B10100101,B01000010,B00111100};
  25.  
  26. void setup() {
  27.   lc.shutdown(0,false);
  28.   // выставляем яркость на среднее значение:
  29.   lc.setIntensity(0,8);
  30.   // очищаем дисплей:
  31.   lc.clearDisplay(0);  
  32. }
  33.  
  34. void drawFaces(){
  35.   // показываем грустную рожицу:
  36.   lc.setRow(0,0,sf[0]);
  37.   lc.setRow(0,1,sf[1]);
  38.   lc.setRow(0,2,sf[2]);
  39.   lc.setRow(0,3,sf[3]);
  40.   lc.setRow(0,4,sf[4]);
  41.   lc.setRow(0,5,sf[5]);
  42.   lc.setRow(0,6,sf[6]);
  43.   lc.setRow(0,7,sf[7]);
  44.   delay(delaytime);
  45.  
  46.   // показываем «покерфейс»:
  47.   lc.setRow(0,0,nf[0]);
  48.   lc.setRow(0,1,nf[1]);
  49.   lc.setRow(0,2,nf[2]);
  50.   lc.setRow(0,3,nf[3]);
  51.   lc.setRow(0,4,nf[4]);
  52.   lc.setRow(0,5,nf[5]);
  53.   lc.setRow(0,6,nf[6]);
  54.   lc.setRow(0,7,nf[7]);
  55.   delay(delaytime);
  56.  
  57.   // показываем веселую рожицу:
  58.   lc.setRow(0,0,hf[0]);
  59.   lc.setRow(0,1,hf[1]);
  60.   lc.setRow(0,2,hf[2]);
  61.   lc.setRow(0,3,hf[3]);
  62.   lc.setRow(0,4,hf[4]);
  63.   lc.setRow(0,5,hf[5]);
  64.   lc.setRow(0,6,hf[6]);
  65.   lc.setRow(0,7,hf[7]);
  66.   delay(delaytime);
  67. }
  68.  
  69. void loop(){
  70.   drawFaces();
  71. }

В результате у вас должно получится примерно следующее:

Gif face final.gif

Игра «Pong»

Игра «Pong» для этого примера была написана Алессандро Пасотти (Alessandro Pasotti). Кроме того, для этого проекта вам нужно будет добавить к схеме выше потенциометр на 1 кОм. Подключите друг к другу компоненты как показано на рисунке ниже:

Pongping bb.jpg

Код

Теперь загрузите на Arduino вот этот код:

  1. /*  
  2.  *   Игра «Pong» на светодиодной матрице 8x8 (проект с itopen.it)
  3. */
  4.  
  5. #include "LedControl.h"
  6. #include "Timer.h"
  7.  
  8. #define POTPIN A5 // потенциометр
  9. #define PADSIZE 3
  10. #define BALL_DELAY 200
  11. #define GAME_DELAY 10
  12. #define BOUNCE_VERTICAL 1
  13. #define BOUNCE_HORIZONTAL -1
  14. #define NEW_GAME_ANIMATION_SPEED 50
  15. #define HIT_NONE 0
  16. #define HIT_CENTER 1
  17. #define HIT_LEFT 2
  18. #define HIT_RIGHT 3
  19.  
  20. //#define DEBUG 1
  21.  
  22. byte sad[] = {
  23. B00000000,
  24. B01000100,
  25. B00010000,
  26. B00010000,
  27. B00000000,
  28. B00111000,
  29. B01000100,
  30. B00000000
  31. };
  32.  
  33. byte smile[] = {
  34. B00000000,
  35. B01000100,
  36. B00010000,
  37. B00010000,
  38. B00010000,
  39. B01000100,
  40. B00111000,
  41. B00000000
  42. };
  43.  
  44. Timer timer;
  45.  
  46. LedControl lc = LedControl(12,11,10,1);
  47.  
  48. byte direction; // роза ветров, «0» - это север
  49. int xball;
  50. int yball;
  51. int yball_prev;
  52. byte xpad;
  53. int ball_timer;
  54.  
  55. void setSprite(byte *sprite){
  56.     for(int r = 0; r < 8; r++){
  57.         lc.setRow(0, r, sprite[r]);
  58.     }
  59. }
  60.  
  61. void newGame() {
  62.     lc.clearDisplay(0);
  63.     // начальная позиция:
  64.     xball = random(1, 7);
  65.     yball = 1;
  66.     direction = random(3, 6); // идем на юг
  67.     for(int r = 0; r < 8; r++){
  68.         for(int c = 0; c < 8; c++){
  69.             lc.setLed(0, r, c, HIGH);
  70.             delay(NEW_GAME_ANIMATION_SPEED);
  71.         }
  72.     }
  73.     setSprite(smile);
  74.     delay(1500);
  75.     lc.clearDisplay(0);
  76. }
  77.  
  78. void setPad() {
  79.     xpad = map(analogRead(POTPIN), 0, 1020, 8 - PADSIZE, 0);
  80. }
  81.  
  82. void debug(const char* desc){
  83. #ifdef DEBUG
  84.     Serial.print(desc);
  85.     Serial.print(" XY: ");  //  "Координаты X и Y: "
  86.     Serial.print(xball);
  87.     Serial.print(", ");
  88.     Serial.print(yball);
  89.     Serial.print(" XPAD: ");  //  "Расположение платформы: "
  90.     Serial.print(xpad);
  91.     Serial.print(" DIR: ");  //  " Направление: "
  92.     Serial.println(direction);
  93. #endif
  94. }
  95.  
  96. int checkBounce() {
  97.     if(!xball || !yball || xball == 7 || yball == 6){
  98.         int bounce = (yball == 0 || yball == 6) ? BOUNCE_HORIZONTAL : BOUNCE_VERTICAL;
  99. #ifdef DEBUG
  100.         debug(bounce == BOUNCE_HORIZONTAL ? "HORIZONTAL" : "VERTICAL");
  101. #endif
  102.         return bounce;
  103.     }
  104.     return 0;
  105. }
  106.  
  107. int getHit() {
  108.     if(yball != 6 || xball < xpad || xball > xpad + PADSIZE){
  109.         return HIT_NONE;
  110.     }
  111.     if(xball == xpad + PADSIZE / 2){
  112.         return HIT_CENTER;
  113.     }
  114.     return xball < xpad + PADSIZE / 2 ? HIT_LEFT : HIT_RIGHT;
  115. }
  116.  
  117. bool checkLoose() {
  118.     return yball == 6 && getHit() == HIT_NONE;
  119. }
  120.  
  121. void moveBall() {
  122.     debug("MOVE");
  123.     int bounce = checkBounce();
  124.     if(bounce) {
  125.         switch(direction){
  126.             case 0:
  127.                 direction = 4;
  128.             break;
  129.             case 1:
  130.                 direction = (bounce == BOUNCE_VERTICAL) ? 7 : 3;
  131.             break;
  132.             case 2:
  133.                 direction = 6;
  134.             break;
  135.             case 6:
  136.                 direction = 2;
  137.             break;
  138.             case 7:
  139.                 direction = (bounce == BOUNCE_VERTICAL) ? 1 : 5;
  140.             break;
  141.             case 5:
  142.                 direction = (bounce == BOUNCE_VERTICAL) ? 3 : 7;
  143.             break;
  144.             case 3:
  145.                 direction = (bounce == BOUNCE_VERTICAL) ? 5 : 1;
  146.             break;
  147.             case 4:
  148.                 direction = 0;
  149.             break;
  150.         }
  151.         debug("->");
  152.     }
  153.  
  154.     // проверяем удар: модифицируем направление (влево или вправо):
  155.     switch(getHit()){
  156.         case HIT_LEFT:
  157.             if(direction == 0){
  158.                 direction =  7;
  159.             } else if (direction == 1){
  160.                 direction = 0;
  161.             }
  162.         break;
  163.         case HIT_RIGHT:
  164.             if(direction == 0){
  165.                 direction = 1;
  166.             } else if(direction == 7){
  167.                 direction = 0;
  168.             }
  169.         break;
  170.     }
  171.  
  172.     // проверяем ортогональные направления и границы:
  173.     if((direction == 0 && xball == 0) || (direction == 4 && xball == 7)){
  174.         direction++;
  175.     }
  176.     if(direction == 0 && xball == 7){
  177.         direction = 7;
  178.     }
  179.     if(direction == 4 && xball == 0){
  180.         direction = 3;
  181.     }
  182.     if(direction == 2 && yball == 0){
  183.         direction = 3;
  184.     }
  185.     if(direction == 2 && yball == 6){
  186.         direction = 1;
  187.     }
  188.     if(direction == 6 && yball == 0){
  189.         direction = 5;
  190.     }
  191.     if(direction == 6 && yball == 6){
  192.         direction = 7;
  193.     }
  194.    
  195.     // при попадании в угол:
  196.     if(xball == 0 && yball == 0){
  197.         direction = 3;
  198.     }
  199.     if(xball == 0 && yball == 6){
  200.         direction = 1;
  201.     }
  202.     if(xball == 7 && yball == 6){
  203.         direction = 7;
  204.     }
  205.     if(xball == 7 && yball == 0){
  206.         direction = 5;
  207.     }
  208.  
  209.     yball_prev = yball;
  210.     if(2 < direction && direction < 6) {
  211.         yball++;
  212.     } else if(direction != 6 && direction != 2) {
  213.         yball--;
  214.     }
  215.     if(0 < direction && direction < 4) {
  216.         xball++;
  217.     } else if(direction != 0 && direction != 4) {
  218.         xball--;
  219.     }
  220.     xball = max(0, min(7, xball));
  221.     yball = max(0, min(6, yball));
  222.     debug("AFTER MOVE");
  223. }
  224.  
  225. void gameOver() {
  226.     setSprite(sad);
  227.     delay(1500);
  228.     lc.clearDisplay(0);
  229. }
  230.  
  231. void drawGame() {
  232.     if(yball_prev != yball){
  233.         lc.setRow(0, yball_prev, 0);
  234.     }
  235.     lc.setRow(0, yball, byte(1 << (xball)));
  236.     byte padmap = byte(0xFF >> (8 - PADSIZE) << xpad) ;
  237. #ifdef DEBUG
  238.     //Serial.println(padmap, BIN);
  239. #endif
  240.     lc.setRow(0, 7, padmap);
  241. }
  242.  
  243. void setup() {
  244.   // в начале MAX72xx находится в режиме сбережения энергии;
  245.   // нужно его «разбудить»:
  246.   pinMode(POTPIN, INPUT);
  247.  
  248.   lc.shutdown(0,false);
  249.   // выставляем яркость на среднюю величину:
  250.   lc.setIntensity(0, 8);
  251.   // очищаем дисплей:
  252.   lc.clearDisplay(0);
  253.   randomSeed(analogRead(0));
  254. #ifdef DEBUG
  255.   Serial.begin(9600);
  256.   Serial.println("Pong");
  257. #endif
  258.   newGame();
  259.   ball_timer = timer.every(BALL_DELAY, moveBall);
  260. }
  261.  
  262. void loop() {
  263.     timer.update();
  264.     // двигаем платформу:
  265.     setPad();
  266. #ifdef DEBUG
  267.     Serial.println(xpad);
  268. #endif
  269.     // обновляем данные на экране:
  270.     drawGame();
  271.     if(checkLoose()) {
  272.         debug("LOOSE");
  273.         gameOver();
  274.         newGame();
  275.     }
  276.     delay(GAME_DELAY);
  277. }

Демонстрация

Вот так игра в «Pong» выглядит вживую:

Gif game f.gif

См.также

Внешние ссылки

  1. randomnerdtutorials.com - Guide for 8×8 Dot Matrix MAX7219 with Arduino + Pong Game