Arduino - Matrice de LED

L'affichage matriciel LED, également connu sous le nom d'affichage LED ou d'affichage matriciel à points, est largement utilisé. Dans ce tutoriel, nous allons apprendre :

Après cela, vous pouvez facilement adapter le code pour d'autres matrices LED telles que la matrice LED 16x8, la matrice LED 64x8 ...

À propos de la matrice LED

Affichage matriciel LED

Il existe de nombreux types de matrices LED. Avec Arduino, la matrice LED basée sur MAX7219 est largement utilisée. La matrice LED basée sur MAX7219 possède les caractéristiques suivantes :

  • L'unité de base d'une matrice LED est un bloc.
  • Chaque bloc possède une matrice LED 8x8 (64 LED) et un pilote MAX7219.
  • Il existe deux formes de blocs populaires : le module générique et le module FC-16.
  • Une matrice LED peut être composée d'un seul bloc ou de plusieurs blocs en chaîne en guirlande.
  • Vous pouvez acheter une matrice LED multi-blocs pré-assemblée (par exemple, 4-en-1, 8-en-1).
  • Vous pouvez également acheter plusieurs blocs et les câbler pour former une matrice LED de la taille souhaitée.
  • Vous déclarerez la taille de la matrice LED que vous utilisez dans le code Arduino.

Brochage

Schéma des broches de la matrice LED

Une matrice LED est constituée d'un ou plusieurs blocs. Chaque bloc comprend deux groupes de broches :

  • Groupe de broches d'entrée :
    • VCC : connecté à 5V.
    • GND : connecté à GND.
    • DIN est la broche de données, connectez-la à la broche SPI MOSI de l'Arduino.
    • CS : Sélection de puce, connectez-la à n'importe quelle broche numérique de l'Arduino.
    • CLK : broche d'horloge, connectez-la à la broche CLK SPI de l'Arduino.
  • Groupe de broches de sortie :
    • VCC : se connecte au VCC du module suivant.
    • GND : se connecte au GND du module suivant.
    • DOUT : Sortie de données, se connecte à la broche DIN du module suivant.
    • CS : se connecte au CS du module suivant.
    • CLK se connecte au CLK du module suivant.

Diagramme de câblage

Si la matrice LED est constituée d'un seul bloc :

  • Connectez les groupes de broches d'entrée à Arduino
  • Laissez le groupe de broches de sortie déconnecté
Schéma de câblage de la matrice LED 8x8 FC-16 Arduino

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Schéma de câblage générique de la matrice LED 8x8 Arduino

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Si la matrice LED est pré-assemblée en plusieurs blocs :

  • Connectez les groupes de broches d'entrée à l'Arduino
  • Laissez le groupe de broches de sortie déconnecté
Schéma de câblage de l'afficheur à matrice de LED Arduino

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Si la matrice LED est composée de plusieurs blocs assemblés par vos soins :

  • Connectez les groupes de broches d'entrée du premier bloc à Arduino
  • Connectez les groupes de broches de sortie de chaque bloc aux groupes de broches d'entrée du bloc suivant
  • Laissez le groupe de broches de sortie du dernier bloc déconnecté
Schéma de câblage de la matrice LED 32x8 Arduino FC-16

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Schéma générique de câblage de matrice LED 32x8 Arduino

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Car l'affichage consomme beaucoup de courant (jusqu'à 1A à la luminosité maximale) :

  • N'utilisez pas l'alimentation du pin 5V de l'Arduino.
  • Utilisez plutôt une alimentation externe de 5V. L'Arduino et la matrice de LED peuvent partager l'alimentation à partir d'un adaptateur de 5V.

Car Arduino se connecte à la matrice LED via les broches SPI :

  • La broche 13 (SCK) et la broche 11 (MOSI) sur l'Arduino Uno doivent être utilisées. Si vous utilisez une autre carte Arduino, consultez la documentation officielle pour les broches SPI équivalentes.
  • La broche 3 (CS) peut être changée pour n'importe quelle broche.

Comment programmer pour une matrice LED

Il n'est pas facile de contrôler la matrice LED. Heureusement, des bibliothèques sont disponibles pour faciliter la tâche. Voici un guide étape par étape sur la façon d'écrire le code Arduino pour contrôler la matrice LED.

  • Inclure les bibliothèques :
#include <MD_Parola.h> #include <MD_MAX72xx.h>
  • Précisez quel matériel est utilisé : GENERIC_HW ou FC16_HW.
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
  • Définissez combien de blocs LED sont utilisés. Par exemple, une matrice LED 4-en-1 possède 4 blocs.
#define MAX_DEVICES 4
  • Définissez la broche qui se connecte à la broche CS de la matrice de LED. Par exemple, la broche D3.
#define CS_PIN 3
  • Créez une nouvelle instance de la classe MD_Parola pour l'affichage de la matrice LED.
MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
  • Code dans la fonction setup() :
void setup() { ledMatrix.begin(); // initialiser l'objet ledMatrix.setIntensity(0); // régler la luminosité de l'affichage de la matrice LED (de 0 à 15) ledMatrix.displayClear(); // effacer l'affichage de la matrice LED }
  • Afficher du texte, un numéro et montrer des effets animés : voir la partie suivante.

Arduino - Code de matrice LED

Le code ci-dessous est pour un affichage matriciel LED FC-16 32x8 (4 blocs). Mais vous pouvez facilement l'adapter pour 8x8, 16x8, 64x8...

/* * Ce code Arduino a été développé par newbiely.fr * Ce code Arduino est mis à disposition du public sans aucune restriction. * Pour des instructions complètes et des schémas de câblage, veuillez visiter: * https://newbiely.fr/tutorials/arduino/arduino-led-matrix */ #include <MD_Parola.h> #include <MD_MAX72xx.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 // 4 blocks #define CS_PIN 3 // create an instance of the MD_Parola class MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); void setup() { ledMatrix.begin(); // initialize the object ledMatrix.setIntensity(15); // set the brightness of the LED matrix display (from 0 to 15) ledMatrix.displayClear(); // clear LED matrix display } void loop() { ledMatrix.setTextAlignment(PA_LEFT); ledMatrix.print("Left"); // display text delay(2000); ledMatrix.setTextAlignment(PA_CENTER); ledMatrix.print("Center"); // display text delay(2000); ledMatrix.setTextAlignment(PA_RIGHT); ledMatrix.print("Right"); // display text delay(2000); ledMatrix.setTextAlignment(PA_CENTER); ledMatrix.setInvert(true); ledMatrix.print("Invert"); // display text inverted delay(2000); ledMatrix.setInvert(false); ledMatrix.print(1234); // display number delay(2000); }

Étapes rapides

  • Connectez l'Arduino à la matrice de LED selon le schéma de câblage ci-dessus.
  • Connectez l'Arduino au PC via un câble USB.
  • Accédez à l'icône Libraries sur la barre latérale gauche de l'IDE Arduino.
  • Recherchez « MD_Parola », puis trouvez la bibliothèque MD_Parola.
  • Cliquez sur le bouton Install.
Bibliothèque Arduino MD_Parola
  • Vous serez invité à installer la bibliothèque MD_MAX72XX pour dépendance. Cliquez sur le bouton Install All.
Bibliothèque Arduino MD_MAX72XX
  • Copiez le code ci-dessus et ouvrez-le avec l'IDE Arduino
  • Cliquez sur le bouton Upload sur l'IDE Arduino pour téléverser le code vers Arduino
  • Regardez l'affichage de la matrice LED

Code Arduino pour matrice LED – Texte défilant

Lorsque vous souhaitez imprimer un long message qui est trop long pour tenir sur un affichage matriciel à LED, vous pouvez utiliser la technique de l'effet de défilement du texte.

Le code Arduino ci-dessous montre comment faire défiler un message sur l'affichage matriciel à LED.

/* * Ce code Arduino a été développé par newbiely.fr * Ce code Arduino est mis à disposition du public sans aucune restriction. * Pour des instructions complètes et des schémas de câblage, veuillez visiter: * https://newbiely.fr/tutorials/arduino/arduino-led-matrix */ #include <MD_Parola.h> #include <MD_MAX72xx.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 // 4 blocks #define CS_PIN 3 // create an instance of the MD_Parola class MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); void setup() { ledMatrix.begin(); // initialize the object ledMatrix.setIntensity(15); // set the brightness of the LED matrix display (from 0 to 15) ledMatrix.displayClear(); // clear led matrix display ledMatrix.displayScroll("Hello, DIYables", PA_CENTER, PA_SCROLL_LEFT, 100); } void loop() { if (ledMatrix.displayAnimate()) { ledMatrix.displayReset(); } }

Pour plus d'effets de texte, veuillez visiter Référence de la bibliothèque MD_Parola.

Vidéo

La démo vidéo ci-dessous utilise le code ci-dessous :

/* * Ce code Arduino a été développé par newbiely.fr * Ce code Arduino est mis à disposition du public sans aucune restriction. * Pour des instructions complètes et des schémas de câblage, veuillez visiter: * https://newbiely.fr/tutorials/arduino/arduino-led-matrix */ #include <MD_MAX72xx.h> #include <SPI.h> // ============================================================ // MAX7219 // ============================================================ #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CS_PIN 9 MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); // ============================================================ // BUTTONS // ============================================================ #define BTN_LEFT 2 #define BTN_RIGHT 3 #define BTN_ROTATE 4 #define BUZZER_PIN 6 // ============================================================ // GAME BOARD // ============================================================ #define BOARD_W 8 #define BOARD_H 32 bool board[BOARD_H][BOARD_W]; // ============================================================ // TIMING // ============================================================ unsigned long lastFallTime = 0; const unsigned long NORMAL_FALL = 700; const unsigned long SOFT_DROP_FALL = 80; #define LONG_PRESS_TIME 400 // ============================================================ // SCORE // ============================================================ unsigned long score = 0; unsigned int lines = 0; unsigned int level = 1; unsigned long highScore = 0; // ============================================================ // GAME STATE // ============================================================ bool gameOver = false; bool gameStarted = false; // ============================================================ // TETROMINO DEFINITIONS // ============================================================ struct Point { int x; int y; }; struct Tetromino { Point block[4]; uint8_t color; }; Tetromino pieces[7] = { // I { {{0,0}, {1,0}, {2,0}, {3,0}}, 0 }, // O { {{0,0}, {1,0}, {0,1}, {1,1}}, 1 }, // T { {{1,0}, {0,1}, {1,1}, {2,1}}, 2 }, // L { {{0,0}, {0,1}, {0,2}, {1,2}}, 3 }, // J { {{1,0}, {1,1}, {1,2}, {0,2}}, 4 }, // S { {{1,0}, {2,0}, {0,1}, {1,1}}, 5 }, // Z { {{0,0}, {1,0}, {1,1}, {2,1}}, 6 } }; // ============================================================ // CURRENT PIECE // ============================================================ int currentPiece; int currentX; int currentY; int rotationState = 0; // ============================================================ // BUTTON STATE // ============================================================ bool lastLeftState = HIGH; bool lastRightState = HIGH; bool lastRotateState = HIGH; unsigned long rotatePressTime = 0; bool softDrop = false; bool rotateHandled = false; // ============================================================ // DISPLAY COORDINATE MAPPING // ============================================================ void setPixel(int x, int y, bool state) { if (x < 0 || x >= BOARD_W || y < 0 || y >= BOARD_H) { return; } // Reverse vertical direction int physicalColumn = (BOARD_H - 1) - y; int physicalRow = x; mx.setPoint( physicalRow, physicalColumn, state ); } // ============================================================ // CLEAR DISPLAY // ============================================================ void clearDisplay() { mx.clear(); } // ============================================================ // DRAW BOARD // ============================================================ void drawBoard() { clearDisplay(); // Draw fixed blocks for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { if (board[y][x]) { setPixel( x, y, true ); } } } // Draw falling piece drawCurrentPiece(); } // ============================================================ // GET ROTATED BLOCK POSITION // ============================================================ Point getRotatedPoint( Point p, int rotation ) { Point r; switch (rotation % 4) { case 0: r.x = p.x; r.y = p.y; break; case 1: r.x = -p.y; r.y = p.x; break; case 2: r.x = -p.x; r.y = -p.y; break; case 3: r.x = p.y; r.y = -p.x; break; } return r; } // ============================================================ // CHECK COLLISION // ============================================================ bool checkCollision( int pieceX, int pieceY, int rotation ) { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotation ); int x = pieceX + p.x; int y = pieceY + p.y; // Left / Right wall if (x < 0 || x >= BOARD_W) { return true; } // Bottom if (y >= BOARD_H) { return true; } // Existing block if (y >= 0 && board[y][x]) { return true; } } return false; } // ============================================================ // DRAW CURRENT PIECE // ============================================================ void drawCurrentPiece() { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotationState ); int x = currentX + p.x; int y = currentY + p.y; if (x >= 0 && x < BOARD_W && y >= 0 && y < BOARD_H) { setPixel( x, y, true ); } } } // ============================================================ // RANDOM PIECE // ============================================================ void spawnPiece() { currentPiece = random(0, 7); rotationState = 0; currentX = 3; currentY = 0; // I piece if (currentPiece == 0) { currentX = 2; } // Game over if piece cannot spawn if (checkCollision( currentX, currentY, rotationState)) { gameOver = true; if (score > highScore) { highScore = score; } soundGameOver(); } } // ============================================================ // LOCK PIECE // ============================================================ void lockPiece() { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotationState ); int x = currentX + p.x; int y = currentY + p.y; if (x >= 0 && x < BOARD_W && y >= 0 && y < BOARD_H) { board[y][x] = true; } } soundLock(); // Check completed lines clearLines(); // New piece spawnPiece(); lastFallTime = millis(); } // ============================================================ // CLEAR LINES // ============================================================ void clearLines() { int cleared = 0; for (int y = BOARD_H - 1; y >= 0; y--) { bool full = true; for (int x = 0; x < BOARD_W; x++) { if (!board[y][x]) { full = false; break; } } if (full) { cleared++; // Move rows down for (int yy = y; yy > 0; yy--) { for (int x = 0; x < BOARD_W; x++) { board[yy][x] = board[yy - 1][x]; } } // Clear top row for (int x = 0; x < BOARD_W; x++) { board[0][x] = false; } // Check same row again y++; } } // Scoring if (cleared > 0) { lines += cleared; switch (cleared) { case 1: score += 100 * level; break; case 2: score += 300 * level; break; case 3: score += 500 * level; break; case 4: score += 800 * level; break; } // Increase level every 10 lines level = (lines / 10) + 1; soundLineClear(cleared); } } // ============================================================ // MOVE LEFT // ============================================================ void moveLeft() { if (gameOver) { return; } if (!checkCollision( currentX - 1, currentY, rotationState)) { currentX--; drawBoard(); soundLeft(); } } // ============================================================ // MOVE RIGHT // ============================================================ void moveRight() { if (gameOver) { return; } if (!checkCollision( currentX + 1, currentY, rotationState)) { currentX++; drawBoard(); soundRight(); } } // ============================================================ // ROTATE // ============================================================ void rotatePiece() { if (gameOver) { return; } int newRotation = (rotationState + 1) % 4; // Normal rotation if (!checkCollision( currentX, currentY, newRotation)) { rotationState = newRotation; drawBoard(); return; } // Wall kick LEFT if (!checkCollision( currentX - 1, currentY, newRotation)) { currentX--; rotationState = newRotation; drawBoard(); return; } // Wall kick RIGHT if (!checkCollision( currentX + 1, currentY, newRotation)) { currentX++; rotationState = newRotation; drawBoard(); return; } } // ============================================================ // MOVE DOWN // ============================================================ bool moveDown() { if (gameOver) { return false; } if (!checkCollision( currentX, currentY + 1, rotationState)) { currentY++; return true; } // Cannot move further lockPiece(); return false; } // ============================================================ // SOFT DROP // ============================================================ void handleSoftDrop() { if (gameOver) { return; } static unsigned long lastSoftDrop = 0; if (millis() - lastSoftDrop >= SOFT_DROP_FALL) { lastSoftDrop = millis(); if (moveDown()) { score += 1; } drawBoard(); } } // ============================================================ // NORMAL FALL // ============================================================ void handleNormalFall() { if (gameOver) { return; } unsigned long fallSpeed; fallSpeed = NORMAL_FALL - ((level - 1) * 50); if (fallSpeed < 100) { fallSpeed = 100; } if (millis() - lastFallTime >= fallSpeed) { lastFallTime = millis(); moveDown(); drawBoard(); } } // ============================================================ // BUTTON HANDLING // ============================================================ void handleButtons() { bool leftState = digitalRead(BTN_LEFT); bool rightState = digitalRead(BTN_RIGHT); bool rotateState = digitalRead(BTN_ROTATE); // ========================================================== // LEFT // ========================================================== if (leftState == LOW && lastLeftState == HIGH) { moveLeft(); } // ========================================================== // RIGHT // ========================================================== if (rightState == LOW && lastRightState == HIGH) { moveRight(); } // ========================================================== // ROTATE BUTTON PRESSED // ========================================================== if (rotateState == LOW && lastRotateState == HIGH) { rotatePressTime = millis(); rotateHandled = false; } // ========================================================== // ROTATE BUTTON HELD // ========================================================== if (rotateState == LOW) { if (!rotateHandled && millis() - rotatePressTime >= LONG_PRESS_TIME) { rotateHandled = true; softDrop = true; } } // ========================================================== // ROTATE BUTTON RELEASED // ========================================================== if (rotateState == HIGH && lastRotateState == LOW) { unsigned long pressDuration = millis() - rotatePressTime; // Short press = ROTATE if (pressDuration < LONG_PRESS_TIME && !rotateHandled) { rotatePiece(); soundRotate(); } // Stop soft drop softDrop = false; } lastLeftState = leftState; lastRightState = rightState; lastRotateState = rotateState; } // ============================================================ // START MUSIC // ============================================================ // // Arcade-style startup melody. // // Không dùng tone liên tục. // Mỗi nốt kết thúc trước khi nốt tiếp theo bắt đầu. // ============================================================ void playStartMusic() { clearDisplay(); // ---------------------------------------------------------- // Intro LED sweep // ---------------------------------------------------------- for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { setPixel( x, y, true ); } // Melody notes during sweep if (y == 0) { tone(BUZZER_PIN, 523, 100); } if (y == 4) { tone(BUZZER_PIN, 659, 100); } if (y == 8) { tone(BUZZER_PIN, 784, 100); } if (y == 12) { tone(BUZZER_PIN, 1047, 150); } if (y == 18) { tone(BUZZER_PIN, 784, 100); } if (y == 22) { tone(BUZZER_PIN, 1047, 100); } if (y == 26) { tone(BUZZER_PIN, 1319, 180); } delay(18); } delay(100); clearDisplay(); // ---------------------------------------------------------- // READY melody // ---------------------------------------------------------- tone(BUZZER_PIN, 784, 120); delay(150); tone(BUZZER_PIN, 988, 120); delay(150); tone(BUZZER_PIN, 1175, 120); delay(150); tone(BUZZER_PIN, 1568, 250); delay(300); clearDisplay(); noTone(BUZZER_PIN); } // ============================================================ // LEFT SOUND // ============================================================ void soundLeft() { tone( BUZZER_PIN, 650, 70 ); } // ============================================================ // RIGHT SOUND // ============================================================ void soundRight() { tone( BUZZER_PIN, 850, 70 ); } // ============================================================ // ROTATE SOUND // ============================================================ void soundLock() { tone(BUZZER_PIN, 450, 120); delay(130); tone(BUZZER_PIN, 300, 100); } // ============================================================ // ROTATE SOUND // ============================================================ void soundRotate() { tone( BUZZER_PIN, 1200, 100 ); } // ============================================================ // LINE CLEAR SOUND // ============================================================ void soundLineClear(int count) { if (count == 1) { tone( BUZZER_PIN, 1000, 120 ); delay(150); tone( BUZZER_PIN, 1400, 160 ); } else if (count == 2) { tone( BUZZER_PIN, 1000, 100 ); delay(120); tone( BUZZER_PIN, 1300, 100 ); delay(120); tone( BUZZER_PIN, 1700, 180 ); } else { tone( BUZZER_PIN, 1200, 100 ); delay(120); tone( BUZZER_PIN, 1500, 100 ); delay(120); tone( BUZZER_PIN, 1800, 100 ); delay(120); tone( BUZZER_PIN, 2200, 250 ); } } // ============================================================ // GAME OVER SOUND // ============================================================ void soundGameOver() { tone( BUZZER_PIN, 700, 180 ); delay(210); tone( BUZZER_PIN, 500, 180 ); delay(210); tone( BUZZER_PIN, 350, 220 ); delay(250); tone( BUZZER_PIN, 220, 450 ); delay(500); noTone(BUZZER_PIN); } // ============================================================ // GAME OVER ANIMATION // ============================================================ void gameOverAnimation() { for (int i = 0; i < 4; i++) { drawBoard(); delay(250); clearDisplay(); delay(250); } } // ============================================================ // RESET GAME // ============================================================ void resetGame() { for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { board[y][x] = false; } } score = 0; lines = 0; level = 1; gameOver = false; spawnPiece(); lastFallTime = millis(); drawBoard(); } // ============================================================ // SETUP // ============================================================ void setup() { Serial.begin(115200); // Buttons pinMode( BTN_LEFT, INPUT_PULLUP ); pinMode( BTN_RIGHT, INPUT_PULLUP ); pinMode( BTN_ROTATE, INPUT_PULLUP ); // Buzzer pinMode( BUZZER_PIN, OUTPUT ); // MAX7219 mx.begin(); mx.control( MD_MAX72XX::INTENSITY, 5 ); mx.control( MD_MAX72XX::UPDATE, true ); mx.clear(); // Random seed randomSeed( analogRead(A0) ); // ========================================================== // START MUSIC // ========================================================== playStartMusic(); // ========================================================== // START GAME // ========================================================== resetGame(); gameStarted = true; } // ============================================================ // MAIN LOOP // ============================================================ void loop() { if (!gameStarted) { return; } // ========================================================== // GAME OVER // ========================================================== if (gameOver) { gameOverAnimation(); delay(500); // Restart resetGame(); return; } // ========================================================== // INPUT // ========================================================== handleButtons(); // ========================================================== // SOFT DROP // ========================================================== if (softDrop) { handleSoftDrop(); } else { handleNormalFall(); } }

※ NOS MESSAGES

  • N'hésitez pas à partager le lien de ce tutoriel. Cependant, veuillez ne pas utiliser notre contenu sur d'autres sites web. Nous avons investi beaucoup d'efforts et de temps pour créer ce contenu, veuillez respecter notre travail !