#include <SPI.h>
// Definice pinů pro Arduino Mega
#define CS_PIN 53 // Chip Select pin (CS pin na Mega)
// MOSI je na pinu 51, nebo 11
// SCK je na pinu 52, nebo 13
// Nastavení počtu modulů
#define NUM_MODULES 12 // Počet propojených MAX7219 modulů
#define TOTAL_COLUMNS (NUM_MODULES * 8)
// Definice směru scrollování
#define SCROLL_LEFT_TO_RIGHT 0
#define SCROLL_RIGHT_TO_LEFT 1
#define SCROLL_TOP_TO_BOTTOM 2
#define SCROLL_BOTTOM_TO_TOP 3
// Odkomentuj zvoleny směr
#define SCROLL_DIRECTION SCROLL_LEFT_TO_RIGHT
//#define SCROLL_DIRECTION SCROLL_RIGHT_TO_LEFT
//#define SCROLL_DIRECTION SCROLL_TOP_TO_BOTTOM
//#define SCROLL_DIRECTION SCROLL_BOTTOM_TO_TOP
// Nastavení časování (v milisekundách)
#define SCROLL_SPEED_HORIZONTAL 50 // rychlost pro horizontální scrollování
#define SCROLL_SPEED_VERTICAL 100 // rychlost pro vertikální scrollování
#define TEXT_PAUSE 2000 // Pauza mezi texty
#define SEQUENCE_PAUSE 1000 // Pauza před opakováním celé sekvence
// Definice textů
const char* text1 = "ABCD "; // Přidat mezeru na konci pro oddělení textů
const char* text2 = "abcd ";
const char* text3 = "2024 ";
// Globální proměnné pro stav
int currentTextIndex = 0;
bool isWaitingBetweenTexts = false;
bool isWaitingForSequence = false;
unsigned long waitStartTime = 0;
unsigned long lastScrollTime = 0;
// Globální buffer pro data modulů
uint8_t moduleData[NUM_MODULES][8] = {0};
// Deklarace funkcí
void initializeReversedTexts();
// Font 8x8 - definice znaků (pouze vybrané znaky)
const uint8_t font[][8] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // mezera (32)
{0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x0C, 0x00}, // ! (33)
{0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00}, // " (34)
{0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // # (35)
{0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // $ (36)
{0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // % (37)
{0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // & (38)
{0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' (39)
{0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // ( (40)
{0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // ) (41)
{0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // * (42)
{0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // + (43)
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // , (44)
{0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // - (45)
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // . (46)
{0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // / (47)
{0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // 0 (48)
{0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // 1 (49)
{0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // 2 (50)
{0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // 3 (51)
{0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // 4 (52)
{0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // 5 (53)
{0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // 6 (54)
{0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // 7 (55)
{0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // 8 (56)
{0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // 9 (57)
{0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // : (58)
{0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // ; (59)
{0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // < (60)
{0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // = (61)
{0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // > (62)
{0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // ? (63)
{0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // @ (64)
// A-Z (65-90)
{0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // A (65)
{0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // B (66)
{0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // C (67)
{0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // D (68)
{0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // E (69)
{0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // F (70)
{0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // G (71)
{0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // H (72)
{0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // I (73)
{0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // J (74)
{0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // K (75)
{0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // L (76)
{0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // M (77)
{0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // N (78)
{0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // O (79)
{0x1F, 0x33, 0x33, 0x1F, 0x03, 0x03, 0x03, 0x00}, // P (80)
{0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // Q (81)
{0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // R (82)
{0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // S (83)
{0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // T (84)
{0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U (85)
{0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // V (86)
{0x63, 0x63, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // W (87)
{0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // X (88)
{0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // Y (89)
{0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // Z (90)
// a-z (97-122)
{0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x3E, 0x00}, // a (97)
{0x07, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00}, // b (98)
{0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // c (99)
{0x38, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x3E, 0x00}, // d (100)
{0x00, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // e (101)
{0x1C, 0x36, 0x06, 0x0F, 0x06, 0x06, 0x0F, 0x00}, // f (102)
{0x00, 0x00, 0x3E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // g (103)
{0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // h (104)
{0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // i (105)
{0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // j (106)
{0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // k (107)
{0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // l (108)
{0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // m (109)
{0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // n (110)
{0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // o (111)
{0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // p (112)
{0x00, 0x00, 0x3E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // q (113)
{0x00, 0x00, 0x1F, 0x33, 0x03, 0x03, 0x07, 0x00}, // r (114)
{0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // s (115)
{0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // t (116)
{0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // u (117)
{0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // v (118)
{0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // w (119)
{0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // x (120)
{0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // y (121)
{0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // z (122)
// České znaky (123-129)
{0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // á (123)
{0x3F, 0x06, 0x0C, 0x1E, 0x0C, 0x06, 0x3F, 0x00}, // é (124)
{0x0C, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // í (125)
{0x0C, 0x1E, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // ó (126)
{0x0C, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // ú (127)
{0x0C, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // ý (128)
{0x1E, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // ů (129)
};
// Struktura pro text
struct ScrollingText {
const char* text;
int16_t position; // Horizontální pozice
int16_t vposition; // Vertikální pozice
uint16_t length;
bool isScrolling;
int16_t startPosition;
int16_t endPosition;
int16_t vStartPosition; // Vertikální počáteční pozice
int16_t vEndPosition; // Vertikální koncová pozice
uint8_t direction;
};
ScrollingText texts[3];
// Funkce pro kontrolu, zda text dosáhl konce scrollování
bool hasReachedEnd(const ScrollingText* text) {
switch (text->direction) {
case SCROLL_RIGHT_TO_LEFT:
return text->position <= text->endPosition;
case SCROLL_LEFT_TO_RIGHT:
return text->position >= text->endPosition;
case SCROLL_TOP_TO_BOTTOM:
return text->vposition >= text->vEndPosition;
case SCROLL_BOTTOM_TO_TOP:
return text->vposition <= text->vEndPosition;
default:
return false;
}
}
// Funkce pro aktualizaci pozice textu
void updateTextPosition(ScrollingText* text) {
switch (text->direction) {
case SCROLL_RIGHT_TO_LEFT:
text->position--;
break;
case SCROLL_LEFT_TO_RIGHT:
text->position++;
break;
case SCROLL_TOP_TO_BOTTOM:
text->vposition++;
break;
case SCROLL_BOTTOM_TO_TOP:
text->vposition--;
break;
}
}
void drawChar(char c, int16_t position, int16_t vposition) {
if (c < 32 || c > 122) return; // Kontrola rozsahu pro všechna ASCII znaky
// Přímé mapování ASCII hodnoty na index ve fontu
if (c >= 32 && c <= 96) {
c = c - 32; // korekční hodnota pro pozice velkych pismen (32)
}
if (c >= 97 && c <= 122) {
c = c - 38; // korekční hodnota pro pozice malych pismen (38)
}
int charIndex = c;
// Pro každý řádek znaku
for (int row = 0; row < 8; row++) {
int actualRow = row + vposition;
if (actualRow >= 0 && actualRow < 8) { // Kontrola vertikální pozice
// Pro každý sloupec znaku
for (int col = 0; col < 8; col++) {
int16_t actualCol = position + col;
if (actualCol >= 0 && actualCol < TOTAL_COLUMNS) {
uint8_t module = (TOTAL_COLUMNS - 1 - actualCol) / 8;
uint8_t bitPosition = 7 - ((TOTAL_COLUMNS - 1 - actualCol) % 8);
// Získáme data pro tento řádek
uint8_t rowData = font[charIndex][row];
// Zjistíme, jestli je bit nastaven
bool isBitSet = rowData & (1 << col);
// Normální logika - 1 = LED svítí, 0 = LED nesvítí
if (isBitSet) {
moduleData[module][actualRow] |= (1 << bitPosition);
} else {
moduleData[module][actualRow] &= ~(1 << bitPosition);
}
}
}
}
}
}
void drawText(const char* text, int16_t position, int16_t vposition, uint16_t length) {
// Vyčistíme buffer před kreslením
memset(moduleData, 0, sizeof(moduleData));
// Pro vertikální scrollování vykresluje text na pevné horizontální pozici
if (texts[currentTextIndex].direction == SCROLL_TOP_TO_BOTTOM || texts[currentTextIndex].direction == SCROLL_BOTTOM_TO_TOP) {
position = 0; // Zarovnání vlevo
}
// Vykresluje všechna písmena textu
for (int i = 0; i < length; i++) {
drawChar(text[i], position + (i * 8), vposition);
}
}
void updateDisplay() {
for (int module = 0; module < NUM_MODULES; module++) {
for (int row = 0; row < 8; row++) {
sendData(module, row + 1, moduleData[module][row]);
}
}
}
// Funkce pro získání rychlosti scrollování podle směru
unsigned int getScrollSpeed(uint8_t direction) {
if (direction == SCROLL_TOP_TO_BOTTOM || direction == SCROLL_BOTTOM_TO_TOP) {
return 200; // Vertikální scrollování
} else {
return 100; // Horizontální scrollování
}
}
void setup() {
// Inicializace SPI komunikace
SPI.begin();
// Nastavení CS pinu jako výstup
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
// Inicializace všech MAX7219 čipů
max7219_init();
// Inicializace textů
initializeText(&texts[0], text1);
initializeText(&texts[1], text2);
initializeText(&texts[2], text3);
// Vyčištění displeje na začátku
max7219_clear();
// Inicializace stavových proměnných
currentTextIndex = 0;
isWaitingBetweenTexts = false;
isWaitingForSequence = false;
}
void loop() {
unsigned long currentTime = millis();
if (isWaitingForSequence) {
if (currentTime - waitStartTime >= SEQUENCE_PAUSE) {
isWaitingForSequence = false;
currentTextIndex = 0;
initializeText(&texts[currentTextIndex], text1);
}
return;
}
if (isWaitingBetweenTexts) {
if (currentTime - waitStartTime >= TEXT_PAUSE) {
isWaitingBetweenTexts = false;
currentTextIndex = (currentTextIndex + 1) % 3;
// Inicializace dalšího textu
switch (currentTextIndex) {
case 0:
initializeText(&texts[currentTextIndex], text1);
break;
case 1:
initializeText(&texts[currentTextIndex], text2);
break;
case 2:
initializeText(&texts[currentTextIndex], text3);
break;
}
}
return;
}
ScrollingText* currentText = &texts[currentTextIndex];
if (currentTime - lastScrollTime >= getScrollSpeed(currentText->direction)) {
lastScrollTime = currentTime;
if (currentText->isScrolling) {
updateTextPosition(currentText);
if (hasReachedEnd(currentText)) {
currentText->isScrolling = false;
isWaitingBetweenTexts = true;
waitStartTime = currentTime;
}
drawText(currentText->text, currentText->position, currentText->vposition, currentText->length);
updateDisplay();
}
}
}
void initializeText(ScrollingText* text, const char* content) {
text->text = content;
text->length = strlen(content);
text->isScrolling = true;
text->direction = SCROLL_DIRECTION;
// Výchozí hodnoty pro vertikální pozici
text->vposition = 0;
// Nastavení počáteční a koncové pozice podle směru
if (text->direction == SCROLL_RIGHT_TO_LEFT) {
text->startPosition = TOTAL_COLUMNS; // Začíná vpravo
text->position = text->startPosition;
text->endPosition = -(text->length * 8); // Končí vlevo
} else if (text->direction == SCROLL_LEFT_TO_RIGHT) {
text->startPosition = -(text->length * 8); // Začíná vlevo
text->position = text->startPosition;
text->endPosition = TOTAL_COLUMNS; // Končí vpravo
} else if (text->direction == SCROLL_TOP_TO_BOTTOM) {
text->vStartPosition = -8; // Začíná nad displejem
text->vposition = text->vStartPosition;
text->vEndPosition = 8; // Končí pod displejem
text->position = 0; // Horizontální pozice je fixní
} else if (text->direction == SCROLL_BOTTOM_TO_TOP) {
text->vStartPosition = 8; // Začíná pod displejem
text->vposition = text->vStartPosition;
text->vEndPosition = -8; // Končí nad displejem
text->position = 0; // Horizontální pozice je fixní
}
}
// Inicializace všech MAX7219 čipů
void max7219_init() {
for (int module = 0; module < NUM_MODULES; module++) {
sendCommand(module, 0x0C, 0x01); // Zapnutí MAX7219
sendCommand(module, 0x0F, 0x00); // Vypnutí testovacího režimu
sendCommand(module, 0x09, 0x00); // Dekódovací mód vypnut
sendCommand(module, 0x0B, 0x07); // Scan limit: všechny řádky
sendCommand(module, 0x0A, 0x08); // Jas: střední intenzita
}
max7219_clear();
}
// Odeslání dat na konkrétní řádek konkrétního modulu
void sendData(uint8_t module, uint8_t row, uint8_t data) {
digitalWrite(CS_PIN, LOW);
for (int i = NUM_MODULES - 1; i > module; i--) {
SPI.transfer(0x00);
SPI.transfer(0x00);
}
SPI.transfer(row);
SPI.transfer(reverseByte(data)); // Apply reverseByte to data before sending
for (int i = module - 1; i >= 0; i--) {
SPI.transfer(0x00);
SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
}
// Odeslání příkazu do konkrétního MAX7219
void sendCommand(uint8_t module, uint8_t address, uint8_t data) {
digitalWrite(CS_PIN, LOW);
for (int i = NUM_MODULES - 1; i > module; i--) {
SPI.transfer(0x00);
SPI.transfer(0x00);
}
SPI.transfer(address);
SPI.transfer(data);
for (int i = module - 1; i >= 0; i--) {
SPI.transfer(0x00);
SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
}
void max7219_clear() {
// Vyčistíme všechny moduly
for (int module = 0; module < NUM_MODULES; module++) {
for (int row = 1; row <= 8; row++) {
sendData(module, row, 0x00);
delay(1); // Krátká pauza pro zajištění spolehlivého vymazání
}
}
memset(moduleData, 0, sizeof(moduleData)); // Vyčistíme i buffer
}
// Funkce pro převrácení byte
uint8_t reverseByte(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}