diff --git a/README.md b/README.md index e501477..b24aee5 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ void loop() { - v1.2.2 - убран FastIO - v1.3 - мелкие доработки и оптимизация, добавил поворот матриц - v1.4 - добавил поддержку матричных дисплеев любой конфигурации (точка подключения, направление, чередование) +- v1.5 - добавил раздельное управление яркостью и питанием матриц ## Баги и обратная связь diff --git a/examples/emoji/emoji.ino b/examples/emoji/emoji.ino index 88f2afb..269410e 100644 --- a/examples/emoji/emoji.ino +++ b/examples/emoji/emoji.ino @@ -3,7 +3,7 @@ MAX7219 < 1, 1, 5 > mtrx; // одна матрица (1х1), пин CS на D5 // https://github.com/jorydotcom/matrix-emoji/blob/master/current-j5-emoji.js -// каждый массив - одby emoji +// каждый массив - один emoji const uint8_t angryface_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x18, 0x24, 0x42, 0x81}; const uint8_t circle_B[] PROGMEM = {0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; const uint8_t cdot_B[] PROGMEM = {0x3c, 0x42, 0x81, 0x99, 0x99, 0x81, 0x42, 0x3c}; diff --git a/examples/running/running.ino b/examples/running/running.ino new file mode 100644 index 0000000..0c95ff3 --- /dev/null +++ b/examples/running/running.ino @@ -0,0 +1,23 @@ +#include + +#include +#include + +MAX7219<4, 1, 5> mtrx; +RunningGFX run(&mtrx); +const char pstr_g[] PROGMEM = "Global pgm string"; + +void setup() { + mtrx.begin(); // запускаем + mtrx.setBright(5); // яркость 0..15 + + run.setSpeed(15); + // run.setText("hello"); + run.setText_P(pstr_g); + // run.setWindow(5, 15, 1); + run.start(); +} + +void loop() { + run.tick(); +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 08e34a4..82567b2 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,7 +9,6 @@ GyverMAX7219 KEYWORD1 MAX7219 KEYWORD1 - ####################################### # Methods and Functions (KEYWORD2) ####################################### diff --git a/library.properties b/library.properties index d67146f..e2dd027 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=GyverMAX7219 -version=1.4 +version=1.5 author=AlexGyver maintainer=AlexGyver sentence=Fast and light MAX7219 matrix library diff --git a/src/GyverMAX7219.h b/src/GyverMAX7219.h index 1a24d7e..446cb8a 100644 --- a/src/GyverMAX7219.h +++ b/src/GyverMAX7219.h @@ -7,7 +7,7 @@ - Подключение матриц зигзагом - Аппаратный и программный SPI - Невероятная оптимизация - + AlexGyver, alex@alexgyver.ru https://alexgyver.ru/ MIT License @@ -20,14 +20,15 @@ v1.2.2 - убран FastIO v1.3 - мелкие доработки и оптимизация, добавил поворот матриц v1.4 - добавил поддержку матричных дисплеев любой конфигурации (точка подключения, направление, чередование) + v1.5 - добавил раздельное управление яркостью и питанием матриц */ #ifndef _GyverMAX7219_h #define _GyverMAX7219_h #include +#include #include -#include "GyverGFX.h" #define GM_ZIGZAG 0 #define GM_SERIES 1 @@ -48,22 +49,22 @@ static SPISettings MAX_SPI_SETT(MAX_SPI_SPEED, MSBFIRST, SPI_MODE0); -template < uint8_t width, uint8_t height, uint8_t CSpin, uint8_t DATpin = 0, uint8_t CLKpin = 0 > +template class MAX7219 : public GyverGFX { -public: - MAX7219() : GyverGFX(width * 8, height * 8) { + public: + MAX7219() : GyverGFX(WIDTH * 8, HEIGHT * 8) { begin(); } - + // запустить void begin() { pinMode(CSpin, OUTPUT); if (DATpin == CLKpin) { - SPI.begin(); - } else { + SPI.begin(); + } else { pinMode(DATpin, OUTPUT); pinMode(CLKpin, OUTPUT); - } + } sendCMD(0x0f, 0x00); // отключить режим теста sendCMD(0x09, 0x00); // выключить декодирование sendCMD(0x0a, 0x00); // яркость @@ -71,83 +72,89 @@ class MAX7219 : public GyverGFX { sendCMD(0x0C, 0x01); // включить clearDisplay(); // очистить } - + // установить яркость [0-15] - void setBright(uint8_t value) { // 8x8: 0/8/15 - 30/310/540 ma - sendCMD(0x0a, value); // яркость 0-15 + void setBright(int value) { + sendCMD(0x0a, value); // 8x8: 0/8/15 - 30/310/540 ma + } + void setBright(uint8_t* values) { + sendCMDs(0x0a, values); } - + // переключить питание void setPower(bool value) { sendCMD(0x0c, value); } - + void setPower(bool* values) { + sendCMDs(0x0c, (uint8_t*)values); + } + // очистить void clear() { fillByte(0); } - + // залить void fill() { fillByte(255); } - + // залить байтом void fillByte(uint8_t data) { - for (int i = 0; i < width * height * 8; i++) buffer[i] = data; + for (uint16_t i = 0; i < WIDTH * HEIGHT * 8; i++) buffer[i] = data; } - + // установить точку void dot(int x, int y, uint8_t fill = 1) { - int pos = getPosition(x, y); + int16_t pos = getPosition(x, y); if (pos >= 0) bitWrite(buffer[pos], _bx, fill); } // получить точку - bool get(int x, int y) { - int pos = getPosition(x, y); + bool get(int16_t x, int16_t y) { + int16_t pos = getPosition(x, y); if (pos >= 0) return bitRead(buffer[pos], _bx); else return 0; } // обновить void update() { - int count = 0; - for (int k = 0; k < 8; k++) { + uint16_t count = 0; + for (uint8_t k = 0; k < 8; k++) { beginData(); - for (int i = 0; i < _amount; i++) sendData(8 - k, buffer[count++]); + for (uint16_t i = 0; i < _amount; i++) sendData(8 - k, buffer[count++]); endData(); } } - + // начать отправку void beginData() { if (DATpin == CLKpin) SPI.beginTransaction(MAX_SPI_SETT); - fastWrite(CSpin, 0); + fastWrite(CSpin, 0); } - + // закончить отправку - void endData() { + void endData() { fastWrite(CSpin, 1); if (DATpin == CLKpin) SPI.endTransaction(); } - + // отправка данных напрямую в матрицу (строка, байт) void sendByte(uint8_t address, uint8_t value) { beginData(); sendData(address + 1, value); endData(); } - + // очистить дисплей (не буфер) void clearDisplay() { - for (int k = 0; k < 8; k++) { + for (uint8_t k = 0; k < 8; k++) { beginData(); - for (int i = 0; i < _amount; i++) sendData(8 - k, 0); + for (uint16_t i = 0; i < _amount; i++) sendData(8 - k, 0); endData(); } } - + // поворот матриц (8x8): 0, 1, 2, 3 на 90 град по часовой стрелке void setRotation(uint8_t rot) { _rot = rot; @@ -157,12 +164,12 @@ class MAX7219 : public GyverGFX { void setFlip(bool x, bool y) { _flip = x | (y << 1); } - + // тип дисплея: построчный последовательный (GM_SERIES) или зигзаг GM_ZIGZAG void setType(bool type) { _type = type; } - + // ориентация (точка подключения дисплея) void setConnection(uint8_t conn) { _conn = conn; @@ -170,68 +177,98 @@ class MAX7219 : public GyverGFX { else size(_maxY, _maxX); } - uint8_t buffer[width * height * 8]; + uint8_t buffer[WIDTH * HEIGHT * 8]; -private: - int getPosition(int x, int y) { + private: + int16_t getPosition(int16_t x, int16_t y) { switch (_conn) { - //case GM_LEFT_TOP_RIGHT: break; - case GM_RIGHT_TOP_LEFT: flipX(x); flip(y); break; - case GM_RIGHT_BOTTOM_LEFT: flipY(y); flipX(x); break; - case GM_LEFT_BOTTOM_RIGHT: flipY(y); flip(y); break; - case GM_LEFT_TOP_DOWN: swap(x, y); flip(y); break; - case GM_RIGHT_TOP_DOWN: swap(x, y); flipY(y); break; - case GM_RIGHT_BOTTOM_UP: swap(x, y); flipX(x); flip(y); flipY(y); break; - case GM_LEFT_BOTTOM_UP: swap(x, y); flipX(x); break; + // case GM_LEFT_TOP_RIGHT: break; + case GM_RIGHT_TOP_LEFT: + flipX(x); + flip(y); + break; + case GM_RIGHT_BOTTOM_LEFT: + flipY(y); + flipX(x); + break; + case GM_LEFT_BOTTOM_RIGHT: + flipY(y); + flip(y); + break; + case GM_LEFT_TOP_DOWN: + swap(x, y); + flip(y); + break; + case GM_RIGHT_TOP_DOWN: + swap(x, y); + flipY(y); + break; + case GM_RIGHT_BOTTOM_UP: + swap(x, y); + flipX(x); + flip(y); + flipY(y); + break; + case GM_LEFT_BOTTOM_UP: + swap(x, y); + flipX(x); + break; } if (x < 0 || x >= _maxX || y < 0 || y >= _maxY) return -1; - int b = y; + int16_t b = y; switch (_rot) { - //case 0: break; - case 1: - y = (y & 0xF8) + (x & 7); - x = (x & 0xF8) + 7 - (b & 7); - break; - case 2: - flip(x); - flip(y); - break; - case 3: - y = (y & 0xF8) + 7 - (x & 7); - x = (x & 0xF8) + (b & 7); - break; + // case 0: break; + case 1: + y = (y & 0xF8) + (x & 7); + x = (x & 0xF8) + 7 - (b & 7); + break; + case 2: + flip(x); + flip(y); + break; + case 3: + y = (y & 0xF8) + 7 - (x & 7); + x = (x & 0xF8) + (b & 7); + break; } switch (_flip) { - //case 0: break; - case 1: flip(x); break; - case 2: flip(y); break; - case 3: flip(x); flip(y); break; + // case 0: break; + case 1: + flip(x); + break; + case 2: + flip(y); + break; + case 3: + flip(x); + flip(y); + break; } - + if (_type == GM_ZIGZAG) { - if ((y >> 3) & 1) { // если это нечётная матрица: (y / 8) % 2 - x = _maxX - 1 - x; // отзеркалить x - y = (y & 0xF8) + (7 - (y & 7)); // отзеркалить y: (y / 8) * 8 + (7 - (y % 8)); + if ((y >> 3) & 1) { // если это нечётная матрица: (y / 8) % 2 + x = _maxX - 1 - x; // отзеркалить x + y = (y & 0xF8) + (7 - (y & 7)); // отзеркалить y: (y / 8) * 8 + (7 - (y % 8)); } } - + _bx = x & 7; - return width * (height - 1 - (y >> 3)) + (width - 1 - (x >> 3)) + (y & 7) * width * height; // позиция в буфере + return WIDTH * (HEIGHT - 1 - (y >> 3)) + (WIDTH - 1 - (x >> 3)) + (y & 7) * WIDTH * HEIGHT; // позиция в буфере } - void flip(int& v) { - v = (v & 0xF8) + 7 - (v & 7); // (v / 8 + 1) * 8 - 1 - (v % 8) + void flip(int16_t& v) { + v = (v & 0xF8) + 7 - (v & 7); // (v / 8 + 1) * 8 - 1 - (v % 8) } - void flipX(int& v) { + void flipX(int16_t& v) { v = _maxX - v - 1; } - void flipY(int& v) { + void flipY(int16_t& v) { v = _maxY - v - 1; } - void swap(int& x, int& y) { - int b = y; + void swap(int16_t& x, int16_t& y) { + int16_t b = y; y = x; x = b; } @@ -247,14 +284,14 @@ class MAX7219 : public GyverGFX { digitalWrite(pin, val); #endif } - + void F_fastShiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data) { #if defined(AVR) - volatile uint8_t *_clk_port = portOutputRegister(digitalPinToPort(clockPin)); - volatile uint8_t *_dat_port = portOutputRegister(digitalPinToPort(dataPin)); + volatile uint8_t* _clk_port = portOutputRegister(digitalPinToPort(clockPin)); + volatile uint8_t* _dat_port = portOutputRegister(digitalPinToPort(dataPin)); uint8_t _clk_mask = digitalPinToBitMask(clockPin); uint8_t _dat_mask = digitalPinToBitMask(dataPin); - for (uint8_t i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { if (bitOrder == MSBFIRST) { if (data & (1 << 7)) *_dat_port |= _dat_mask; else *_dat_port &= ~_dat_mask; @@ -270,7 +307,6 @@ class MAX7219 : public GyverGFX { #else shiftOut(dataPin, clockPin, bitOrder, data); #endif - } void sendData(uint8_t address, uint8_t value) { if (DATpin == CLKpin) { @@ -283,14 +319,18 @@ class MAX7219 : public GyverGFX { } void sendCMD(uint8_t address, uint8_t value) { beginData(); - for (int i = 0; i < _amount; i++) sendData(address, value); + for (uint16_t i = 0; i < _amount; i++) sendData(address, value); + endData(); + } + void sendCMDs(uint8_t address, uint8_t* values) { + beginData(); + for (uint16_t i = 0; i < _amount; i++) sendData(address, values[_amount - i - 1]); endData(); } - - const int _amount = width * height; - const int _maxX = width * 8; - const int _maxY = height * 8; - int _row = 0, _count = 0; + + const uint16_t _amount = WIDTH * HEIGHT; + const int16_t _maxX = WIDTH * 8; + const int16_t _maxY = HEIGHT * 8; uint8_t _rot = 0, _bx = 0, _flip = 0, _conn = 0; bool _type = GM_ZIGZAG; };