Hlavní

Málo nožiček na procesoru? Mnoho LEDek na třech pinech

Někdy se stane, že potřebujete mnohem více digidálních výstupů, než vám poskytuje Arduino Nano.
Můžete třeba použít většího brášku, Arduino Mega, ale je to něco za něco – vyšší cena a velikost základní desky.
Já si hraji s mnoha ledkami a světelnými efekty, a tak jsem podlehl kouzlu posuvných registrů, konkrétně 74hc595.

Posuvných registrů je velké množství, některé jsou s méně než 8 výstupními piny, pro některé potřebujete jiné množství ovládacích pinů. HC595 potřebuje 3. Ve spojení s Arduinem si můžete vybrat libovolné digitální piny a A0-A5 v režimu digital.

Podrobný popis posuvných registrů zde uvádět nebudu. Kdo chce, tak si najde krásné a podrobné rozbory jak to všechno funguje, pro hračičky stačí kus funkčního kódu, ze kterého se dá vycházet.

Pro úplné začátečníky řeknu, že princip posuvných (shift) registrů je v tom, že posílám za sebou jednotlivé bity (stavy hodnot, které chci mít na výstupních pinech) za sebou a v jednom okamžiku „cinknu“ všem obvodům na jedné noze: teď co si obdržel za data, tak je zobraz.

Přirovnal bych to k dětské hře, kdy skupina dětí obchází dokola rozmístěné židle a když se dá povel, tak si sednou. Rozdíl je v tom, že židlí je víc než dětí a „chybějící“ děti nahrazují „mezery“ do celkového počtu židlí.

Celá krása spočívá v tom, že těch registrů mohu mít teoreticky neomezené množství, takže jednoduše přes 3 piny ovládám nekonečno výstupů – třeba led, třeba motory, nebo houkačky nebo cokoliv, čemu stačí několik digitálních signálů.

Základní moduly podrobně popíšu příště, dnes začnu složitější sestavou.

Na schématu výše je několik modulů – 4 moduly se dvěma 595 (říkejme jim Mapa) a jeden vlevo (říkejme mu Kompas).

Mapa je složena ze čtyřech dvoushiftových modulů 4×4 led.

Uspořádání je takové, že každý hc595 ovládá 2×4 ledky. Jsou tedy v sérii.
Aby se mohlo „něco“ zobrazit na všech registrech, je potřeba postupně poslat 9 bajtů po 8 bitech.
Nejdříve devátý, pak osmý, 7, 6… až naposled první, který má číslo 0. To jsou ty šedé obdélníky s červeným číslem.
Pořadí ledek na každém modulu a registru je zobrazeno na obrázku.

Z toho vyplývá, že není možné jednoduše „lidsky“ zapisovat do jakési tabulky představující ledky a hned to poslat do řetězu 9 bajtů. Je nutné udělat převodní tabulku, do které zapíšeme , jaké místo – souřadnice – má ta která ledka. Protože Kompas není potřeba zahrnout do tabulky, stačí ho poslat jako jeden bajt připravený jinde, budeme se věnovat jen velké Mapě.

Mapa

V matematice označujeme bod souřadnicemi v hranatých závorkách [0,0], v C++ použijeme (0,0).

Proměnná ledMap[64] je jednorozměrné pole o 64 prvcích, která obsahuje souřadnice jednotlivých ledek, jak jsou uspořádány za sebou.
Vysvětlivky jsou uvnitř kódu, aby byl dobře čitelný a modifikovatelný.

Proč tak složitě? Záměr je, že Mapa bude modifikovatelná podle různého uspořádání shiftregistrů.
Celé uspořádání je nyní určené k soutěži Sklad kečupu – http://robotickyden.cz/2022/rules/2022-Ketchup_House-CZv1.pdf , kdy na tomto „displeji“ budu zobrazovat kde se robot právě nachází.

Použití programu a funkcí zatím nebudu rozebírat, důležité části kódu jsou komentované.
Ukázkové použití je na konci v hlavní smyčce loop, řádek 270+++

/* 
  zobrazovací tabulka 8x 74hc595 v matici 8x8 led
  na konci připojen "kompas" - 1x 74hc595 s led do kruhu 

  Navrhl, sestavil s Boží pomocí a AI odzkoušel: MerkurRobot.cz

*/
// Definice pinů Arduina pro shift registr
const int dataPin = 4;   // DS (Data) pin na prvním 74HC595
const int clockPin = 5;  // SH_CP (Clock) pin na prvním 74HC595
const int latchPin = 6;  // ST_CP (Latch) pin na prvním 74HC595

const int numShiftRegisters = 8; // Celkový počet zapojených 74HC595

// Struktura pro mapování LED: {číslo_čipu, číslo_pinu_na_čipu}
struct LedMapEntry {
  int chip;
  int pin; // Piny 0-7 odpovídají výstupům Q0-Q7 na 74HC595
};

// Tabulka 64 LED: {čip, pin} 
// (0,0) je vlevo dole
const LedMapEntry ledMap[64] = {
  //  řádek 0 (x=0)
  {2, 0}, {2, 4}, {3, 0}, {3, 4},   {6, 0}, {6, 4}, {7, 0}, {7, 4}, // y = 0 až 7
  //  řádek 1 
  {2, 1}, {2, 5}, {3, 1}, {3, 5},   {6, 1}, {6, 5}, {7, 1}, {7, 5}, // y = 0 až 7
  //  řádek 2
  {2, 2}, {2, 6}, {3, 2}, {3, 6},   {6, 2}, {6, 6}, {7, 2}, {7, 6}, // y = 0 až 7
  //  řádek 3
  {2, 3}, {2, 7}, {3, 3}, {3, 7},   {6, 3}, {6, 7}, {7, 3}, {7, 7}, // y = 0 až 7

  //  řádek 4 
  {0, 0}, {0, 4}, {1, 0}, {1, 4},   {4, 0}, {4, 4}, {5, 0}, {5, 4}, // y = 0 až 7
  //  řádek 5 
  {0, 1}, {0, 5}, {1, 1}, {1, 5},   {4, 1}, {4, 5}, {5, 1}, {5, 5}, // y = 0 až 7
  //  řádek 6
  {0, 2}, {0, 6}, {1, 2}, {1, 6},   {4, 2}, {4, 6}, {5, 2}, {5, 6}, // y = 0 až 7
  //  řádek 7 
  {0, 3}, {0, 7}, {1, 3}, {1, 7},   {4, 3}, {4, 7}, {5, 3}, {5, 7}  // y = 0 až 7
};

// Pole pro vizuální prezentaci stavu LED matice.
// Každý bit reprezentuje jednu LED.
// První byte (index 0) reprezentuje sloupec x=0, druhý x=1, atd.
// Bit 0 (nejméně významný) je y=0, Bit 7 (nejvíce významný) je y=7.
// Příklad: 0b00000001 rozsvítí LED na (x,0)
//          0b10000000 rozsvítí LED na (x,7)
byte LedOn[8] = {
  0b00000000, // Sloupec x=0 (levý okraj matice)
  0b00000000, // Sloupec x=1
  0b00000000, // Sloupec x=2
  0b00000000, // Sloupec x=3
  0b00000000, // Sloupec x=4
  0b00000000, // Sloupec x=5
  0b00000000, // Sloupec x=6
  0b00000000  // Sloupec x=7 (pravý okraj matice)
};

// Pomocné "pole" pro zobrazení směru jízdy
byte kompasLed = 0b11111111; //inverze - 1=nesvítí, 0=svítí

// Pomocné pole pro skládání dat pro 74HC595 čipy před odesláním.
// Index odpovídá číslu čipu (0-7).
byte ShiftOutData[numShiftRegisters];

void setup() {
  // Nastavení pinů jako výstupní
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  // Vynuluje všechny LED diody na začátku
  clearAllLeds();
  rozsvit_led(0,0); //bliknu, abych věděl orientaci displeje
  delay(500);
  zhasni_led(0,0);
  delay(500);
}

// Funkce pro rozsvícení LED na zadaných souřadnicích (x, y)
// Kde x, y jsou 0-7 a (0,0) je vlevo dole.
void rozsvit_led(int x, int y) {
  // Zkontroluje platnost souřadnic
  if (x < 0 || x >= 8 || y < 0 || y >= 8) {
    return; // Neplatné souřadnice, nic nedělá
  }

  // Nastaví bit pro danou LED v poli LedOn
  // (1 << y) vytvoří masku s bitem na pozici 'y' nastaveným na 1
  LedOn[x] |= (1 << y);

  // Aktualizuje data pro 74HC595 čipy
  updateShiftOutData();
  // Odešle data do 74HC595 čipů
  sendShiftOutData();
}

// Funkce pro zhasnutí LED na zadaných souřadnicích (x, y)
// Kde x, y jsou 0-7 a (0,0) je vlevo dole.
void zhasni_led(int x, int y) {
  // Zkontroluje platnost souřadnic
  if (x < 0 || x >= 8 || y < 0 || y >= 8) {
    return; // Neplatné souřadnice, nic nedělá
  }

  // Zhasne bit pro danou LED v poli LedOn
  // ~(1 << y) vytvoří masku s bitem na pozici 'y' nastaveným na 0 a ostatními na 1
  // Pomocí bitového AND se vynuluje pouze daný bit
  LedOn[x] &= ~(1 << y);

  // Aktualizuje data pro 74HC595 čipy
  updateShiftOutData();
  // Odešle data do 74HC595 čipů
  sendShiftOutData();
}

// Funkce pro zhasnutí všech LED diod
void clearAllLeds() {
  // Vynuluje pole LedOn
  for (int i = 0; i < 8; i++) {
    LedOn[i] = 0;
  }
  // Aktualizuje a odešle data pro 74HC595 čipy
  updateShiftOutData();
  sendShiftOutData();
}

// Pomocná funkce pro aktualizaci pole ShiftOutData na základě LedOn a ledMap
void updateShiftOutData() {
  // Vynuluje ShiftOutData pole pro novou iteraci
  for (int i = 0; i < numShiftRegisters; i++) {
    ShiftOutData[i] = 0;
  }

  // Projde celou virtuální matici LED (8x8)
  for (int x = 0; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
      // Zkontroluje, zda je LED (x,y) v poli LedOn rozsvícená
      if ((LedOn[x] >> y) & 0x01) { // Pokud je bit na pozici 'y' v LedOn[x] nastaven na 1
        // Získá mapování čipu a pinu pro aktuální LED
        int ledIndex = x * 8 + y; // Vypočítá index v jednorozměrném ledMap poli
        int chipNum = ledMap[ledIndex].chip;
        int pinNum = ledMap[ledIndex].pin;

        // Nastaví příslušný bit v ShiftOutData pro správný čip a pin
        ShiftOutData[chipNum] |= (1 << pinNum);
      }
    }
  }
}

// Funkce pro odeslání dat do všech shift registrů
void sendShiftOutData() {
  // Latch pin na LOW pro přípravu na přenos dat
  digitalWrite(latchPin, LOW);

  // Na konci připojena růžice - 1x 595
  // kompasLed = 0b11111111;
  shiftOut(dataPin, clockPin, MSBFIRST, kompasLed);

  // Posílá data do shift registrů od posledního (nejdále od Arduina) k prvnímu.
  // shiftOut posílá 8 bitů. První 8 bitů, které pošleme, se usadí v posledním 595.
  // Proto procházíme pole ShiftOutData odzadu.
  for (int i = numShiftRegisters - 1; i >= 0; i--) {
    shiftOut(dataPin, clockPin, MSBFIRST, ShiftOutData[i]);
  }

  // Latch pin na HIGH, aby se data zkopírovala na výstupy všech registrů
  digitalWrite(latchPin, HIGH);
}

// Funkce pro efekty
void uhlopricka_PH(int cas){
  for (int i = 0; i<8; i++){
    rozsvit_led(i, 7-i);
    delay(cas); // LED svítí 
    zhasni_led(i, 7-i);
    //delay(5); //
  }
}
void uhlopricka_LH(int cas){
  for (int i = 0; i<8; i++){
    rozsvit_led(i, i);
    delay(cas); // LED svítí 
    zhasni_led(i, i);
    //delay(5); //
  }
}
void uhlopricka_LD(int cas){
  for (int i = 7; i>=0; i--){
    rozsvit_led(i, 7-i);
    delay(cas); // LED svítí 
    zhasni_led(i, 7-i);
    //delay(5); //
  }
}
void uhlopricka_PD(int cas){
  for (int i = 0; i<8; i++){
    rozsvit_led(7-i, 7-i);
    delay(cas); // LED svítí 
    zhasni_led(7-i, 7-i);
    //delay(5); //
  }
}
void linie_vert_tam(int linka, int cas){
  for (int i = 0; i<8; i++){
    rozsvit_led(linka, i);
    delay(cas); // LED svítí 
    zhasni_led(linka, i);
    //delay(5); //
  }
}
void linie_vert_zpet(int linka, int cas){
  for (int i = 7; i>=0; i--){
    rozsvit_led(linka, i);
    delay(cas); // LED svítí 
    zhasni_led(linka, i);
    //delay(5); //
  }
}
void linie_horiz_tam(int linka, int cas){
  for (int i = 0; i<8; i++){
    rozsvit_led(i, linka);
    delay(cas); // LED svítí 
    zhasni_led(i, linka);
    //delay(5); //
  }
}
void linie_horiz_zpet(int linka, int cas){
  for (int i = 7; i>=0; i--){
    rozsvit_led(i, linka);
    delay(cas); // LED svítí 
    zhasni_led(i, linka);
    //delay(5); //
  }
}
void vert_on(int linka){
  for (int i = 0; i<8; i++){
    rozsvit_led(linka, i);
  }
}
void vert_off(int linka){
  for (int i = 0; i<8; i++){
    zhasni_led(linka, i);
  }
}

/*
 * Funkce pro nastavení bitu v proměnné kompasLed.
 * * @param poziceBitu: Pozice bitu, který chceme změnit (0 až 7).
 * @param stav: Hodnota, na kterou chceme bit nastavit (1 pro vypnuto, 0 pro zapnuto).
 */
void smer(int poziceBitu, int stav) {
  // Ověření, zda je pozice bitu v platném rozsahu 0-7
  if (poziceBitu >= 0 && poziceBitu <= 7) {
    if (stav == 0) { // Pokud chceme, aby LED svítila (stav je 0)
      // Nastavení bitu na pozici 'poziceBitu' na 0 (zapnutí, protože 0=svítí)
      // Používáme bitový AND (&) a bitový NOT (~)
      // ~ (1 << poziceBitu) vytvoří masku, kde je daný bit 0 a ostatní 1
      kompasLed = kompasLed & ~(1 << poziceBitu);
    } else { // Pokud chceme, aby LED nesvítila (stav je 1)
      // Nastavení bitu na pozici 'poziceBitu' na 1 (vypnutí, protože 1=nesvítí)
      // Používáme bitový OR (|) a bitový posun doleva (<<)
      kompasLed = kompasLed | (1 << poziceBitu);
    }
  } 
}

//hlavní smyčka+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void loop() {
  
  uhlopricka_PH(50);
  uhlopricka_LD(50);
/*  uhlopricka_PD(50);
  uhlopricka_LH(50);
 // linie_horiz_tam(1,50);
 // linie_horiz_zpet(1,50); 
 // linie_vert_tam(0,50);
 // linie_vert_zpet(0,50);
 */
  for (int i=0;i<8;i++){
    smer(i, 0);
    sendShiftOutData();
    delay(100);
    smer(i, 1);
    sendShiftOutData();
    delay(100);
    
  }
  vert_on(7);
  delay(1);
  vert_off(7);
  delay(100);
  
}
//konec hlavní smyčka+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Schéma zapojení modulu Led 4×4 – 2×595

Schéma modulu „Kompas“ – 8 led v kruhu

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *