LED maticový displej s ESP32 a MAX7219

10.03.2023 Arduino #esp32 #max7219 #display

Ukázka zapojení maticového displeje s ESP32 a jeho asynchronní ovládání prostřednictvím prohlížeče.


MAX7219 LED bodový maticový displej

MAX7219 LED maticový displej je jedním z populárních, který je na trhu k dispozici a který používají studenti, nadšenci elektroniky a zejména v aplikacích průmyslových displejů. Obecně jsou dostupné dva typy modulů. Jedná se o modul FC-16 a generický modul.

Každý modul se skládá ze dvou jednotek. Jedním je 8X8 LED bodová matice a druhým je MAX7219 IC.

LED bodová matice

LED bodové matice jsou k dispozici v různých rozměrech (7×5,8×8, 7×15 atd.). Níže je uvedena typická matice 8×8 LED, která se skládá ze 64 LED, 8 pro každý řádek a sloupec. Každá LED je adresována číslem řádku a sloupce. Každá LED se označuje jako tečka.

Pro vytvoření matice 8×8 bodů jsou všechny vývody anod spojeny dohromady v řadách R1 až R8, podobně jsou katody spojeny dohromady ve sloupcích C1 až C8. Důvodem pro spojení všech řádků a sloupců dohromady je úspora požadovaného počtu pinů pro ovládání každého LED bodu. Tím se požadovaný počet I/O pinů snížil na 16. V opačném případě budeme potřebovat 64 pinů pro ovládání matice 8×8 LED. Tento způsob ovládání velkého počtu LED s několika vývody je známý jako multiplexování.

Chcete-li zapnout konkrétní funkci, musíme přivést kladné napětí na příslušný řádek této tečky a záporné nebo uzemněné na příslušný sloupec této tečky. Pokud má řádek kladné napětí a sloupec záporné, bude svítit pouze konkrétní LED.

Například, pokud chcete svítit LED připojenou mezi R2 a C1, musíme použít logiku 00000010 na řádek R2 a 11111110 na sloupec C1. Pokud je R5 vytažen vysoko a C4 nízko, pak by LED v páté řadě a čtvrtém sloupci byla vysoká. Podobnou logiku lze také použít na další řádky a sloupce pro zapnutí a vypnutí každého řádku a sloupce LED. Kromě toho, abychom zobrazili text na jehličkové matici, řídíme každou LED velmi vysokou rychlostí, takže lidské oko má pocit, že LED diody neustále svítí.

MAX7219 IC

MAX7219  je integrovaný obvod ovladače displeje se společnou katodou se sériovými vstupy a výstupními kolíky. Má nastavitelnou proudovou schopnost, kterou lze nastavit pouze pomocí jednoho externího odporu. Kromě toho má čtyřvodičové sériové rozhraní, které lze snadno připojit ke všem mikroprocesorům. Může řídit 64 jednotlivých LED připojených na jeho výstupních pinech pomocí pouze 4 vodičů pomocí mikrokontroleru.

Kromě toho má MAX7219 vestavěný dekodér BCD, který usnadňuje použití se sedmi segmentovými numerickými displeji. Navíc má 8×8 statickou RAM, kterou můžeme použít k ukládání čísel. Je to jeden z nejpopulárnějších IC ovladače displeje.

Některé klíčové funkce MAX7219 zahrnují:

  1. Jedná se o IC displeje s LED ovladačem se sériovým rozhraním 10 MHz, které umožňuje uživateli vybrat číslici dekódování/bez dekódování.
  2. Jeho provoz je specifikován v rozsahu napětí +4,0 až +5,5V. Normálně se používá napájecí napětí +5V.
  3. Poskytuje funkci digitálního a analogového ovládání intenzity jasu a režim vypnutí 150 µA, ve kterém je proud všech segmentů přitahován k zemi.
  4. Má velmi nízkou spotřebu energie.
  5. Data se zobrazují na segmentech se zpožděním 2,2 ms.
  6. MAX7219 funguje dobře v rozsahu teplot 0°C až +70°C.
  7. Maximální proud pro každý segmentový kolík je 100 mA a pro každý zemnicí kolík DIGIT je 500 mA

MAX7219 Pinout bodového maticového modulu

Modul MAX7219 má 5 svorek skládajících se z SPI a napájecích svorek. Oba typy modulů mají na dvou stranách stejné připojení. Na jedné straně jsou vstupní připojení a na druhé straně jsou výstupní připojení Níže si můžete prohlédnout pinout generického modulu.

Vstupní připojení

Vstupní připojení matice LED jsou spojena s deskou ESP32.

  • VCC: Tento kolík napájí modul MAX7219. Pokud je jas nastaven na poloviční hodnotu, je propojen s pinem Vin ESP32.
  • GND: Toto je zemnící kolík, který by měl být spojen se zemnicím kolíkem ESP32.
  • DIN: Toto jsou data v pinu. Používá se jako vstup SPI do modulu.
  • CS: Toto je kolík Chip Select pro komunikaci SPI.
  • CLK: Toto se nazývá pin 'Serial Clock', který se používá ve výstupu sériového hodinového signálu SPI.

Výstupní připojení

Výstupní přípojky matice LED se připojují k dalšímu modulu, pokud je potřeba připojit více jednotek.

  • VCC: Je připojen k VCC (5V) na dalším modulu
  • GND: Je připojeno ke GND na dalším modulu
  • DOUT: Toto je pin pro výstup dat. Připojuje se na DIN pin dalšího modulu.
  • CS: Toto je připojeno ke kolíku CS dalšího modulu
  • CLK: Toto je připojeno k pinu CLK dalšího modulu

Propojení MAX7219 LED bodového maticového modulu s ESP32

Jsou vyžadovány následující komponenty:

  • deska ESP32
  • Modul MAX7219
  • Spojovací dráty

Pro tento projekt budeme používat modul FC-16 sestávající ze čtyř jednotek.

Nyní vám ukážeme, jak propojit modul MAX7219 a desku ESP32 dohromady. Níže uvedená tabulka ukazuje výchozí piny SPI pro ESP32.

Názvy pinů Pin ESP32
MOSI GPIO23
MISO GPIO19
SCK GPIO18
CS GPIO5

Nyní se podívejme, jak propojit modul MAX7219 a desku ESP32. Níže uvedená tabulka ukazuje spojení mezi těmito dvěma zařízeními:

Modul MAX7219 Pin ESP32
VCC Vin
GND GND
DIN GPIO23
CS GPIO5
CLK GPIO18

Jak je vidět z tabulky, propojíme svorku VCC modulu MAX7219 s Vin desky ESP32. Uvnitř náčrtu programu totiž udržíme jas displeje na minimu. V opačném případě použijte samostatné napájení 5V. Oba důvody budou společné. Výchozí SPI GPIO piny ESP32 se používají pro připojení ke každému ze zbývajících SPI terminálů modulu MAX7219.

Základní kód

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Uncomment according to your hardware type
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW

#define MAX_DEVICES 4
#define CS_PIN 5

MD_Parola Display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void setup() {

  Display.begin();
  Display.setIntensity(0);
  Display.displayClear();
  Display.displayScroll("SCROLL", PA_RIGHT, PA_SCROLL_LEFT, 150);
}

void loop() {
  if (Display.displayAnimate()) {
    Display.displayReset();
  }
}

setup()

Uvnitř funkce setup() po inicializaci matice LED, nastavení jejího jasu a vymazání displeje zavoláme funkci displayScroll() pro nastavení akce rolování. Tato funkce má čtyři argumenty. Prvním parametrem je text, který chcete posouvat. V našem případě je text 'SCROLL.' Druhým parametrem je zarovnání textu v případě zpoždění. Toto jsme nastavili na 'PA_RIGHT'. Třetí argument je směr svitku. Nastavili jsme to na 'PA_SCROLL_LEFT', což znamená, že text se bude posouvat směrem doleva. Konečně čtvrtým parametrem je čas v milisekundách mezi textovými rámečky. Čím vyšší hodnota, tím pomalejší bude pohyb textu.

Display.displayScroll("SCROLL", PA_RIGHT, PA_SCROLL_LEFT, 150);

smyčka()

Uvnitř funkce loop() použijeme příkaz if s funkcí displayAnimate(), která spustí rolování textu. To se vrátí na hodnotu true po dokončení každého rolování a poté bude zavolána funkce displayReset() k resetování displeje. Text se tak posouvá donekonečna.

void loop() {
  if (Display.displayAnimate()) {
    Display.displayReset();
  }
}

Rozšíření kódu na ovládání prostřednictvím internetového prohlížeče

Na ESP32 se zprovozní server, který pomocí asynchronního volání bude ovládat základní funkce maticového displeje.

#include "FS.h"
#include "SD.h"
#include "SPI.h"

#include "Arduino.h"
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESP32AnalogRead.h>

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <base64.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4  //Maximum single module connected
#define CS_PIN 5
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
uint8_t frameDelay = 25;  // default frame delay value
textEffect_t  scrollEffect = PA_SCROLL_LEFT;
#define BUF_SIZE  512
char curMessage[BUF_SIZE]="Start";
char newMessage[BUF_SIZE];

const char* ssid = "MatrixEsp";
const char* password = "12345678";

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
    <!DOCTYPE HTML><html>
    <head>
      <title>ESP Web Server</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <style>
        html {font-family: Arial; display: inline-block; text-align: center;}
        h2 {font-size: 18px;}
        p {font-size: 12px;}
        body {
                font-family: Arial,Helvetica,sans-serif;
                background: #181818;
                color: #EFEFEF;
                font-size: 16px
            }
            form{width:100%;}
            button{padding:5px 15px;}
            section.main {
                display: flex
            }

            section.main {
                flex-direction: column
            }
            #content {
                display: flex;
                flex-wrap: wrap;
                align-items: stretch
            }
            .input-group {
                display: flex;
                flex-wrap: nowrap;
                line-height: 22px;
                margin: 15px 0;
                width:100%;
                text-align:left;
            }

            .input-group>label {
                display: inline-block;
                padding-right: 10px;
                min-width: 35%
            }

            .input-group input,.input-group select {
                flex-grow: 1
            }

            .default-action{
              text-align: right;
              width: 100%;
            }
      </style>
    </head>
    <body>
        <section class="main">
        <div id="content">
            <h2>Wireless Notice Board</h2>
            <form id="dataform">
            <div class="input-group">
              <label>Message:</label>
              <input type="text" name="message" id="message">
            </div>
            <div class="input-group">
              Normal <input type="radio" name="invert" id="invert0" value="0" checked>  
              Inverse <input type="radio" name="invert" id="invert1" value="1"> 
            </div>
            <div class="input-group">
              Left Scroll <input type="radio" name="scrolltype" id="scrolltypeL" value="L" checked> 
              Right Scroll <input type="radio" name="scrolltype" id="scrolltypeR" value="R"> 
            </div>
            <div class="input-group">
               <label>Speed:</label>
              Fast <input type="range" name="speed" id="speed" min="10" max="200" value="25"> Slow
            </div>
            <div class="input-group">
              <div id="setdatacont" class="default-action">
                <input type="button" name="setdata" id="setdata" value="Set" onclick="setData();">
              </div>
            </div>
            </form>
          </div>

        </div>
        </section>
    <script>

    function setData() {
      var message=document.getElementById("dataform").message.value;
      var invert=document.getElementById("dataform").invert.value;
      var scroll=document.getElementById("dataform").scrolltype.value;
      var speed=document.getElementById("dataform").speed.value;

      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/setdata?message="+message+"&invert="+invert+"&scroll="+scroll+"&speed="+speed, true); 
      xhr.send();
    }

    </script>
    </body>
    </html>
)rawliteral";

void setup() {
  Serial.begin(115200);
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });

  server.on("/setdata", HTTP_GET, [] (AsyncWebServerRequest *request) {

      if (request->hasParam("message")) {
        String newMessage=request->getParam("message")->value();
        if(newMessage != ""){
          strcpy(curMessage, newMessage.c_str());
        }
      }

      if (request->hasParam("invert")) {
        String newInvert=request->getParam("invert")->value();
        if(newInvert == "1"){
          P.setInvert(1);
        }else{
          P.setInvert(0);
        }
      }

      if (request->hasParam("scroll")) {
        String scroll=request->getParam("scroll")->value();
        scrollEffect = (scroll == "R" ? PA_SCROLL_RIGHT : PA_SCROLL_LEFT);
        P.setTextEffect(scrollEffect, scrollEffect);
        P.displayReset();
      }

      if (request->hasParam("speed")) {
        String speed=request->getParam("speed")->value();
        int16_t speedF = atoi(speed.c_str());
         P.setSpeed(speedF);
         frameDelay = speedF;
      }

    request->send(200, "text/plain", "OK");
  });

  server.begin();
  Serial.println("HTTP server started");

  P.begin();
  P.setIntensity(0);
  P.displayClear();
  P.displaySuspend(false);
  P.displayScroll(curMessage, PA_LEFT, scrollEffect, frameDelay);
}

void loop() {
    if (P.displayAnimate()){
      P.displayReset();
    }
}

Literatura:

[1] LED Dot Matrix Display with ESP32 and MAX7219 Microcontrollers Lab [online]. USA: 2023 [cit. 2023-03-10]. Dostupné z: https://microcontrollerslab.com/led-dot-matrix-display-esp32-max7219/

[2] Wireless Notice Board Using ESP8266 & Matrix Display Electro Gadget [online]. USA: 2023 [cit. 2023-03-10]. Dostupné z: https://circuitdiagrams.in/wireless-notice-board-using-esp8266/?fbclid=IwAR3yqvxbplwper8lcgcyeYklj5_-aYbn8cuEADQpNMB8YhUcrRWcVs4qNRo