
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
