diff --git a/EncButton/EncButton.h b/EncButton/EncButton.h index d98ea8a..ee67ec0 100644 --- a/EncButton/EncButton.h +++ b/EncButton/EncButton.h @@ -11,6 +11,7 @@ - Опциональный режим callback (+22б SRAM на каждый экземпляр) v1.1 - пуллап отдельныи методом + v1.2 - можно передать конструктору параметр INPUT_PULLUP */ // =========== НАСТРОЙКИ (можно передефайнить из скетча) ============ @@ -63,7 +64,10 @@ enum eb_callback { // класс template < bool MODE, uint8_t S1, uint8_t S2 = 255, uint8_t KEY = 255 > class EncButton { -public: +public: + EncButton(byte mode = INPUT) { + if (mode == INPUT_PULLUP) pullUp(); + } void pullUp() { if (S2 == 255) { // обычная кнопка pinMode(S1, INPUT_PULLUP); diff --git a/microLED/microLED.h b/microLED/microLED.h index 49cf485..c87a738 100644 --- a/microLED/microLED.h +++ b/microLED/microLED.h @@ -74,6 +74,11 @@ Версия 3.3 - Исправлен критический баг с влиянием на другие пины + + Версия 3.4 + - Переработан ASM вывод, меньше весит, легче адаптируется под другие частоты / тайминги + - Добавлена поддержка LGT8F328P с частотой 32/16/8 MHz + - Переработан поллинг millis()/micros() - прямой вызов прерывания TIMER0_OVF, убран лишний код */ #ifndef microLED_h #define microLED_h @@ -191,331 +196,335 @@ class microLED { void end(); // закончить вывод потоком */ - int oneLedMax = 46; - int oneLedIdle = 2000; - mData leds[amount]; - byte white[(CHIP4COLOR) ? amount : 0]; - - void init() { - if (pin != MLED_NO_CLOCK) { - _dat_mask = digitalPinToBitMask(pin); - _dat_port = portOutputRegister(digitalPinToPort(pin)); - _dat_ddr = portModeRegister(digitalPinToPort(pin)); - *_dat_ddr |= _dat_mask; - } - if (pinCLK != MLED_NO_CLOCK) { - _clk_mask = digitalPinToBitMask(pinCLK); - _clk_port = portOutputRegister(digitalPinToPort(pinCLK)); - _clk_ddr = portModeRegister(digitalPinToPort(pinCLK)); - *_clk_ddr |= _clk_mask; - } - switch (chip) { - case LED_WS2811: oneLedMax = 46; oneLedIdle = 2000; break; - case LED_WS2812: oneLedMax = 30; oneLedIdle = 660; break; // 28/240 для ECO, 32/700 матрица - case LED_WS2813: oneLedMax = 30; oneLedIdle = 1266; break; - case LED_WS2815: oneLedMax = 10; oneLedIdle = 1753; break; - case LED_WS2818: oneLedMax = 46; oneLedIdle = 1900; break; - } - // oneLedMax = (ток ленты с одним горящим) - (ток выключенной ленты) - // oneLedIdle = (ток выключенной ленты) / (количество ледов) +int oneLedMax = 46; +int oneLedIdle = 2000; +mData leds[amount]; +byte white[(CHIP4COLOR) ? amount : 0]; + +void init() { + if (pin != MLED_NO_CLOCK) { + _dat_mask = digitalPinToBitMask(pin); + _dat_port = portOutputRegister(digitalPinToPort(pin)); + _dat_ddr = portModeRegister(digitalPinToPort(pin)); + *_dat_ddr |= _dat_mask; + } + if (pinCLK != MLED_NO_CLOCK) { + _clk_mask = digitalPinToBitMask(pinCLK); + _clk_port = portOutputRegister(digitalPinToPort(pinCLK)); + _clk_ddr = portModeRegister(digitalPinToPort(pinCLK)); + *_clk_ddr |= _clk_mask; + } + switch (chip) { + case LED_WS2811: oneLedMax = 46; oneLedIdle = 2000; break; + case LED_WS2812: oneLedMax = 30; oneLedIdle = 660; break; // 28/240 для ECO, 32/700 матрица + case LED_WS2813: oneLedMax = 30; oneLedIdle = 1266; break; + case LED_WS2815: oneLedMax = 10; oneLedIdle = 1753; break; + case LED_WS2818: oneLedMax = 46; oneLedIdle = 1900; break; + } + // oneLedMax = (ток ленты с одним горящим) - (ток выключенной ленты) + // oneLedIdle = (ток выключенной ленты) / (количество ледов) #ifdef MLED_USE_SPI - SPI.begin(); + SPI.begin(); #endif - } - - microLED() { - init(); - } - - microLED(uint8_t width, uint8_t height, M_type type, M_connection conn, M_dir dir) : - _width(width), _height(height), _matrixConfig( (uint8_t)conn | ((uint8_t)dir << 2) ), _matrixType( (uint8_t)type ) { - init(); - if (_matrixConfig == 4 || _matrixConfig == 13 || _matrixConfig == 14 || _matrixConfig == 7) _matrixW = height; - else _matrixW = width; - } - - void setBrightness(uint8_t newBright) { - _bright = getCRT(newBright); - } - - void clear() { - for (int i = 0; i < amount; i++) leds[i] = 0; - } - - void fill(mData color) { - for (int i = 0; i < amount; i++) leds[i] = color; - } - - void fill(int from, int to, mData color) { - for (int i = from; i <= to; i++) leds[i] = color; - } - - void fillGradient(int from, int to, mData color1, mData color2) { - for (int i = from; i < to; i++) leds[i] = getBlend(i-from, to-from, color1, color2); - } - - void set(int n, mData color) { - leds[n] = color; - } - - mData get(int num) { - return leds[num]; - } - - void fade(int num, byte val) { - leds[num] = getFade(leds[num], val); - } - - // ============================================== МАТРИЦА ============================================== - uint16_t getPixNumber(int x, int y) { - int thisX, thisY; - switch(_matrixConfig) { - case 0: thisX = x; thisY = y; break; - case 4: thisX = y; thisY = x; break; - case 1: thisX = x; thisY = (_height - y - 1); break; - case 13: thisX = (_height - y - 1); thisY = x; break; - case 10: thisX = (_width - x - 1); thisY = (_height - y - 1); break; - case 14: thisX = (_height - y - 1); thisY = (_width - x - 1); break; - case 11: thisX = (_width - x - 1); thisY = y; break; - case 7: thisX = y; thisY = (_width - x - 1); break; - } - - if (_matrixType || !(thisY % 2)) return (thisY * _matrixW + thisX); // если чётная строка - else return (thisY * _matrixW + _matrixW - thisX - 1); // если нечётная строка - } - - void set(int x, int y, mData color) { - if (x * y >= amount || x < 0 || y < 0 || x >= _width || y >= _height) return; - leds[getPixNumber(x, y)] = color; - } - - mData get(int x, int y) { - return leds[getPixNumber(x, y)]; - } - - void fade(int x, int y, byte val) { - int pix = getPixNumber(x, y); - leds[pix] = getFade(leds[pix], val); - } - - void drawBitmap8(int X, int Y, const uint8_t *frame, int width, int height) { - for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - set(x + X, y + Y, pgm_read_byte(&frame[x + (height - 1 - y) * width])); - } - void drawBitmap16(int X, int Y, const uint16_t *frame, int width, int height) { - for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - set(x + X, y + Y, pgm_read_word(&frame[x + (height - 1 - y) * width])); - } - void drawBitmap32(int X, int Y, const uint32_t *frame, int width, int height) { - for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - set(x + X, y + Y, pgm_read_dword(&frame[x + (height - 1 - y) * width])); - } - - // ============================================== УТИЛИТЫ ============================================== - void setMaxCurrent(int ma) { - _maxCurrent = ma; - } - - uint8_t correctBright(uint8_t bright) { - long sum = 0; - for (int i = 0; i < amount; i++) { - sum += fade8R(leds[i], bright); - sum += fade8G(leds[i], bright); - sum += fade8B(leds[i], bright); - } - - sum = ((long)sum >> 8) * oneLedMax / 3; // текущий "активный" ток ленты - int idle = (long)oneLedIdle * amount / 1000; // холостой ток ленты - if (sum == 0) return bright; - if ((sum + idle) < _maxCurrent) return bright; // ограничения нет - else return ((float)(_maxCurrent - idle) * bright / sum); // пересчёт яркости - } - - // ============================================== ВЫВОД ============================================== - // асм задержки - #define _1_NOP "NOP \n\t" - #define _2_NOP "RJMP .+0 \n\t" - #define _4_NOP _2_NOP _2_NOP - #define _8_NOP _4_NOP _4_NOP - // Издержки для бита H - 5 тактов, для бита L - 3 такта - - void begin() { - if (pin != MLED_NO_CLOCK) { - _mask_h = _dat_mask | *_dat_port; - _mask_l = ~_dat_mask & *_dat_port; - } - _showBright = _bright; - if (isr == CLI_HIGH) { // Макс приоритет, отправка всего буфера не может быть прервана - sregSave = SREG; - cli(); - } +} + +microLED() { + init(); +} + +microLED(uint8_t width, uint8_t height, M_type type, M_connection conn, M_dir dir) : + _width(width), _height(height), _matrixConfig( (uint8_t)conn | ((uint8_t)dir << 2) ), _matrixType( (uint8_t)type ) { + init(); + if (_matrixConfig == 4 || _matrixConfig == 13 || _matrixConfig == 14 || _matrixConfig == 7) _matrixW = height; + else _matrixW = width; +} + +void setBrightness(uint8_t newBright) { + _bright = getCRT(newBright); +} + +void clear() { + for (int i = 0; i < amount; i++) leds[i] = 0; +} + +void fill(mData color) { + for (int i = 0; i < amount; i++) leds[i] = color; +} + +void fill(int from, int to, mData color) { + for (int i = from; i <= to; i++) leds[i] = color; +} + +void fillGradient(int from, int to, mData color1, mData color2) { + for (int i = from; i < to; i++) leds[i] = getBlend(i - from, to - from, color1, color2); +} + +void set(int n, mData color) { + leds[n] = color; +} + +mData get(int num) { + return leds[num]; +} + +void fade(int num, byte val) { + leds[num] = getFade(leds[num], val); +} + +// ============================================== МАТРИЦА ============================================== +uint16_t getPixNumber(int x, int y) { + int thisX, thisY; + switch (_matrixConfig) { + case 0: thisX = x; thisY = y; break; + case 4: thisX = y; thisY = x; break; + case 1: thisX = x; thisY = (_height - y - 1); break; + case 13: thisX = (_height - y - 1); thisY = x; break; + case 10: thisX = (_width - x - 1); thisY = (_height - y - 1); break; + case 14: thisX = (_height - y - 1); thisY = (_width - x - 1); break; + case 11: thisX = (_width - x - 1); thisY = y; break; + case 7: thisX = y; thisY = (_width - x - 1); break; + } + + if (_matrixType || !(thisY % 2)) return (thisY * _matrixW + thisX); // если чётная строка + else return (thisY * _matrixW + _matrixW - thisX - 1); // если нечётная строка +} + +void set(int x, int y, mData color) { + if (x * y >= amount || x < 0 || y < 0 || x >= _width || y >= _height) return; + leds[getPixNumber(x, y)] = color; +} + +mData get(int x, int y) { + return leds[getPixNumber(x, y)]; +} + +void fade(int x, int y, byte val) { + int pix = getPixNumber(x, y); + leds[pix] = getFade(leds[pix], val); +} + +void drawBitmap8(int X, int Y, const uint8_t *frame, int width, int height) { + for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) + set(x + X, y + Y, pgm_read_byte(&frame[x + (height - 1 - y) * width])); +} +void drawBitmap16(int X, int Y, const uint16_t *frame, int width, int height) { + for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) + set(x + X, y + Y, pgm_read_word(&frame[x + (height - 1 - y) * width])); +} +void drawBitmap32(int X, int Y, const uint32_t *frame, int width, int height) { + for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) + set(x + X, y + Y, pgm_read_dword(&frame[x + (height - 1 - y) * width])); +} + +// ============================================== УТИЛИТЫ ============================================== +void setMaxCurrent(int ma) { + _maxCurrent = ma; +} + +uint8_t correctBright(uint8_t bright) { + long sum = 0; + for (int i = 0; i < amount; i++) { + sum += fade8R(leds[i], bright); + sum += fade8G(leds[i], bright); + sum += fade8B(leds[i], bright); + } + + sum = ((long)sum >> 8) * oneLedMax / 3; // текущий "активный" ток ленты + int idle = (long)oneLedIdle * amount / 1000; // холостой ток ленты + if (sum == 0) return bright; + if ((sum + idle) < _maxCurrent) return bright; // ограничения нет + else return ((float)(_maxCurrent - idle) * bright / sum); // пересчёт яркости +} + +// ============================================== ВЫВОД ============================================== +void begin() { + if (pin != MLED_NO_CLOCK) { + _mask_h = _dat_mask | *_dat_port; + _mask_l = ~_dat_mask & *_dat_port; + } + _showBright = _bright; + if (isr == CLI_HIGH) { // Макс приоритет, отправка всего буфера не может быть прервана + sregSave = SREG; + cli(); + } #ifdef MLED_USE_SPI - SPI.beginTransaction(SPISettings(MLED_SPI_CLOCK, MSBFIRST, SPI_MODE0)); + SPI.beginTransaction(SPISettings(MLED_SPI_CLOCK, MSBFIRST, SPI_MODE0)); #endif - if (chip == LED_APA102 || chip == LED_APA102_SPI) for (byte i = 0; i < 4; i++) sendRaw(0); - } - - void show() { - begin(); - if (_maxCurrent != 0 && amount != 0) _showBright = correctBright(_bright); - if (CHIP4COLOR) for (int i = 0; i < amount; i++) send(leds[i], white[i]); - else for (int i = 0; i < amount; i++) send(leds[i]); - end(); - } - - void send(mData color, byte thisWhite = 0) { - uint8_t data[3]; - // компилятор посчитает сдвиги - data[(order >> 4) & 0b11] = fade8R(color, _showBright); - data[(order >> 2) & 0b11] = fade8G(color, _showBright); - data[order & 0b11] = fade8B(color, _showBright); - if (CHIP4COLOR) thisWhite = fade8(thisWhite, _showBright); - - if (isr == CLI_AVER) { // Средний приоритет, текущий диод однозначно будет обновлен - sregSave = SREG; - cli(); - } - - if (chip == LED_APA102 || chip == LED_APA102_SPI) sendRaw(255); // старт байт SPI лент - - // отправляем RGB и W если есть - for (uint8_t i = 0; i < 3; i++) sendRaw(data[i]); - if (CHIP4COLOR) sendRaw(thisWhite); - - if (isr == CLI_AVER) SREG = sregSave; // Средний приоритет, вернуть прерывания - if (uptime && (isr == CLI_AVER || isr == CLI_HIGH)) systemUptimePoll(); // пнуть миллисы - } - - void sendRaw(byte data) { - if (isr == CLI_LOW) { // Низкий приоритет, текущий байт однозначно будет отправлен - sregSave = SREG; - cli(); - } - switch (chip) { - case LED_WS2811: - asm volatile ( - "LDI r17 ,8 \n\t" // Загружаем в счетчик циклов 8 - "_LOOP_START_%=: \n\t" // Начало основного цикла - "ST X, %[SET_H] \n\t" // Устанавливаем на выходе HIGH - "SBRS %[DATA], 7 \n\t" // Если текущий бит установлен - пропуск след. инстр. - "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW - "LSL %[DATA] \n\t" // Двигаем данные влево на один бит - #if(F_CPU == 16000000UL) - _8_NOP _4_NOP _2_NOP - #elif (F_CPU == 8000000UL) - _4_NOP _1_NOP - #endif - "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW - "DEC r17 \n\t" // Декремент счетчика циклов - "BRNE _LOOP_START_%= \n\t" // Переход на новый цикл, если счетчик не иссяк - : - :[S_REG]"I"(_SFR_IO_ADDR(SREG)), - [DATA] "r" (data), - "x" (_dat_port), - [SET_H] "r" (_mask_h), - [SET_L] "r" (_mask_l) - :"r17", "r18" - ); - break; - case LED_WS2812: - case LED_WS2813: - case LED_WS2815: - case LED_WS2818: - case LED_WS6812: - asm volatile - ( - "LDI r17 ,8 \n\t" // Загружаем в счетчик циклов 8 - "_LOOP_START_%=: \n\t" // Начало основного цикла - "ST X, %[SET_H] \n\t" // Устанавливаем на выходе HIGH - "SBRS %[DATA], 7 \n\t" // Если текущий бит установлен - пропуск след. инстр. - "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW - "LSL %[DATA] \n\t" // Двигаем данные влево на один бит - #if(F_CPU == 16000000UL) - _8_NOP - #elif (F_CPU == 8000000UL) - _1_NOP - #endif - "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW - "DEC r17 \n\t" // Декремент счетчика циклов - "BRNE _LOOP_START_%= \n\t" // Переход на новый цикл, если счетчик не иссяк - : - :[S_REG]"I"(_SFR_IO_ADDR(SREG)), - [DATA] "r" (data), - "x" (_dat_port), - [SET_H] "r" (_mask_h), - [SET_L] "r" (_mask_l) - :"r17", "r18" - ); - break; - case LED_APA102: - for (uint8_t i = 0; i < 8; i++) { - if (data & (1 << 7)) *_dat_port |= _dat_mask; - else *_dat_port &= ~_dat_mask; - *_clk_port |= _clk_mask; - *_clk_port &= ~_clk_mask; - data <<= 1; - } - break; - case LED_APA102_SPI: + if (chip == LED_APA102 || chip == LED_APA102_SPI) for (byte i = 0; i < 4; i++) sendRaw(0); +} + +void show() { + begin(); + if (_maxCurrent != 0 && amount != 0) _showBright = correctBright(_bright); + if (CHIP4COLOR) for (int i = 0; i < amount; i++) send(leds[i], white[i]); + else for (int i = 0; i < amount; i++) send(leds[i]); + end(); +} + +void send(mData color, byte thisWhite = 0) { + uint8_t data[3]; + // компилятор посчитает сдвиги + data[(order >> 4) & 0b11] = fade8R(color, _showBright); + data[(order >> 2) & 0b11] = fade8G(color, _showBright); + data[order & 0b11] = fade8B(color, _showBright); + if (CHIP4COLOR) thisWhite = fade8(thisWhite, _showBright); + + if (isr == CLI_AVER) { // Средний приоритет, текущий диод однозначно будет обновлен + sregSave = SREG; + cli(); + } + + if (chip == LED_APA102 || chip == LED_APA102_SPI) sendRaw(255); // старт байт SPI лент + + // отправляем RGB и W если есть + for (uint8_t i = 0; i < 3; i++) sendRaw(data[i]); + if (CHIP4COLOR) sendRaw(thisWhite); + + if (isr == CLI_AVER) SREG = sregSave; // Средний приоритет, вернуть прерывания + if (uptime && (isr == CLI_AVER || isr == CLI_HIGH)) systemUptimePoll(); // пнуть миллисы +} + +void sendRaw(byte data) { + uint8_t _loop_count = 0; // Счетчик для циклов отправки бит + uint8_t _delay_loop_count = 0; // Счетчик для циклов задержек asm + if (isr == CLI_LOW) { // Низкий приоритет, текущий байт однозначно будет отправлен + sregSave = SREG; + cli(); + } + switch (chip) { + case LED_WS2811: + asm volatile + ( + "LDI %[CNT] ,8 \n\t" // Загружаем в счетчик циклов 8 + "_LOOP_START_%=: \n\t" // Начало основного цикла + "ST X, %[SET_H] \n\t" // Устанавливаем на выходе HIGH +#if(F_CPU == 32000000UL) + "RJMP .+0 \n\t" // (LGT8 32MHZ) два дополнительных NOP +#endif + "SBRS %[DATA], 7 \n\t" // Если текущий бит установлен - пропуск след. инстр. + "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW + "LSL %[DATA] \n\t" // Двигаем данные влево на один бит + //----------------------------------------------------------------------------------------- +#if(F_CPU == 32000000UL) // (LGT8) delay 44 такта, 14 циклов по 2CK + загрузка 1CK + NOP + "LDI %[DELAY], 14 \n\t" +#elif(F_CPU == 16000000UL) // delay 14 тактов, 4 цикла по 3CK + загрузка 1CK + NOP + "LDI %[DELAY], 4 \n\t" // 1CK загрузить +#elif (F_CPU == 8000000UL) // delay 5 тактов, 1 цикл 3CK + загрузка 1CK + NOP + "LDI %[DELAY], 4 \n\t" // 1CK загрузить +#endif + "_DELAY_LOOP_%=: \n\t" // Цикл задержки + "DEC %[DELAY] \n\t" // 1CK декремент + "BRNE _DELAY_LOOP_%=\n\t" // 2CK переход + "NOP \n\t" // 1CK NOP + //----------------------------------------------------------------------------------------- + "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW + "DEC %[CNT] \n\t" // Декремент счетчика циклов + "BRNE _LOOP_START_%= \n\t" // Переход на новый цикл, если счетчик не иссяк + :[CNT] "+r" (_loop_count), + [DELAY] "+r" (_delay_loop_count) + :[DATA] "r" (data), + [SET_H] "r" (_mask_h), + [SET_L] "r" (_mask_l), + "x" (_dat_port) + ); + break; + case LED_WS2812: + case LED_WS2813: + case LED_WS2815: + case LED_WS2818: + case LED_WS6812: + asm volatile + ( + "LDI %[CNT] ,8 \n\t" // Загружаем в счетчик циклов 8 + "_LOOP_START_%=: \n\t" // Начало основного цикла + "ST X, %[SET_H] \n\t" // Устанавливаем на выходе HIGH +#if(F_CPU == 32000000UL) + "RJMP .+0 \n\t" // (LGT8 32MHZ) два дополнительных NOP +#endif + "SBRS %[DATA], 7 \n\t" // Если текущий бит установлен - пропуск след. инстр. + "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW + "LSL %[DATA] \n\t" // Двигаем данные влево на один бит + //----------------------------------------------------------------------------------------- +#if(F_CPU > 8000000UL) +#if(F_CPU == 32000000UL) // (LGT8) delay 29 тактов, 9 цикла по 3CK + загрузка 1CK + NOP + "LDI %[DELAY], 9 \n\t" +#elif(F_CPU == 16000000UL) // delay 8 тактов, 2 цикла по 3CK + загрузка 1CK + NOP + "LDI %[DELAY], 3 \n\t" +#endif + "_DELAY_LOOP_%=: \n\t" // Цикл задержки + "DEC %[DELAY] \n\t" // 1CK декремент + "BRNE _DELAY_LOOP_%=\n\t" // 2CK переход +#endif + "NOP \n\t" // 1CK NOP + //----------------------------------------------------------------------------------------- + "ST X, %[SET_L] \n\t" // Устанавливаем на выходе LOW + "DEC %[CNT] \n\t" // Декремент счетчика циклов + "BRNE _LOOP_START_%= \n\t" // Переход на новый цикл, если счетчик не иссяк + :[CNT] "+r" (_loop_count), + [DELAY] "+r" (_delay_loop_count) + :[DATA] "r" (data), + [SET_H] "r" (_mask_h), + [SET_L] "r" (_mask_l), + "x" (_dat_port) + ); + break; + case LED_APA102: + for (_loop_count = 0; _loop_count < 8; _loop_count++) { + if (data & (1 << 7)) *_dat_port |= _dat_mask; + else *_dat_port &= ~_dat_mask; + *_clk_port |= _clk_mask; + *_clk_port &= ~_clk_mask; + data <<= 1; + } + break; + case LED_APA102_SPI: #ifdef MLED_USE_SPI - SPI.transfer(data); + SPI.transfer(data); #endif - break; - } - if (isr == CLI_LOW) SREG = sregSave; // Низкий приоритет, вернуть прерывания - } - - void end() { - if (isr == CLI_HIGH) SREG = sregSave; // Макс приоритет, вернуть прерывания - if (chip == LED_APA102 || chip == LED_APA102_SPI) for (byte i = 0; i < 4; i++) sendRaw(0); -#ifdef MLED_USE_SPI - SPI.endTransaction(); + break; + } + if (isr == CLI_LOW) SREG = sregSave; // Низкий приоритет, вернуть прерывания +} + +void end() { + if (isr == CLI_HIGH) SREG = sregSave; // Макс приоритет, вернуть прерывания + if (chip == LED_APA102 || chip == LED_APA102_SPI) for (byte i = 0; i < 4; i++) sendRaw(0); +#ifdef MLED_USE_SPI + SPI.endTransaction(); #endif - } - +} + private: - uint8_t _bright = 50, _showBright = 50; - const uint8_t _matrixConfig = 0, _matrixType = 0, _width = 0, _height = 0; - uint8_t _matrixW = 0; - int _maxCurrent = 0; - - volatile uint8_t *_dat_port, *_dat_ddr; - volatile uint8_t *_clk_port, *_clk_ddr; - uint8_t _dat_mask; - uint8_t _clk_mask; - uint8_t _mask_h, _mask_l; - uint8_t sregSave = SREG; - -}; // класс +uint8_t _bright = 50, _showBright = 50; +const uint8_t _matrixConfig = 0, _matrixType = 0, _width = 0, _height = 0; +uint8_t _matrixW = 0; +int _maxCurrent = 0; + +volatile uint8_t *_dat_port, *_dat_ddr; +volatile uint8_t *_clk_port, *_clk_ddr; +uint8_t _dat_mask; +uint8_t _clk_mask; +uint8_t _mask_h, _mask_l; +uint8_t sregSave = SREG; + +}; // класс // Ручное обслуживание функций времени на период запрета прерываний -#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) -#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) -#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) -#define FRACT_MAX (1000 >> 3) void systemUptimePoll(void) { -#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - extern volatile unsigned long timer0_overflow_count; - extern volatile unsigned long timer0_millis; - extern unsigned char timer0_fract; - - if (TIFR0 & (1 << TOV0)) { - TIFR0 |= 1 << TOV0; - uint32_t m = timer0_millis; - uint8_t f = timer0_fract; - m += MILLIS_INC; - f += FRACT_INC; - if (f >= FRACT_MAX) { - f -= FRACT_MAX; - m += 1; - } - timer0_fract = f; - timer0_millis = m; - timer0_overflow_count++; - } +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168p__) \ + || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) \ + || defined(__AVR_ATmega32u4__) + if (TIFR0 & (1 << TOV0)) { // Если Timer0 досчитал до переполнения + asm volatile + ( + "CLI \n\t" // Запрещаем прерывания + "ICALL \n\t" // Прыгаем в прерывание TIMER0 OVERFLOW, где обслуживается millis() / micros() + ::"z"(TIMER0_OVF_vect_num * 2) // Адрес прерывания + ); + } #endif } #endif \ No newline at end of file diff --git a/microLED/tinyLED.h b/microLED/tinyLED.h index 409b4c3..2b27db1 100644 --- a/microLED/tinyLED.h +++ b/microLED/tinyLED.h @@ -13,6 +13,10 @@ // Версия 1.0, спасибище Nich1con за асм +// Версия 1.1 +// - Переработан ASM вывод, меньше весит, легче адаптируется под другие частоты / тайминги + + // выключить CRT, если не указано другого #if !defined(CRT_PGM) && !defined(CRT_SQUARE) && !defined(CRT_CUBIC) && !defined(CRT_OFF) #define CRT_OFF @@ -77,12 +81,6 @@ #define CLI_AVER 2 #define CLI_HIGH 3 -// нопы для 1пин лент -#define _1_NOP "nop \n\t" -#define _2_NOP "rjmp .+0 \n\t" -#define _4_NOP _2_NOP _2_NOP -#define _8_NOP _4_NOP _4_NOP - // ======== ЛИБА SPI ======== #if (TLED_CHIP == LED_APA102_SPI) // SPI лента #include @@ -133,6 +131,9 @@ class tinyLED { } void write(uint8_t data) { + uint8_t _loop_count = 0; // Счетчик для циклов отправки бит + uint8_t _delay_loop_count = 0; // Счетчик для циклов задержек asm + #ifndef TLED_STATIC_BRIGHT if (_bright != 255) data = fade8(data, _bright); #endif @@ -144,51 +145,57 @@ class tinyLED { #if (TLED_CHIP < 10) // 1-пин ленты asm volatile ( - "LDI r18,8 \n\t" // Счетчик 8ми циклов + "LDI %[CNT],8 \n\t" // Счетчик 8ми циклов "_LOOP_START_%=: \n\t" // Начало цикла "SBI %[PORT], %[PIN] \n\t" // HIGH на выход "SBRS %[DATA], 7 \n\t" // Если бит '7' установлен, пропуск след. инструкции "CBI %[PORT], %[PIN] \n\t" // LOW на выход - -#if (F_CPU == 9600000UL) // тини13 - // ======================== -#if (TLED_CHIP == 0) - _4_NOP -#elif (TLED_CHIP == 1) - _4_NOP _1_NOP -#elif (TLED_CHIP == 2) - _8_NOP -#endif - // ======================== -#elif (F_CPU == 8000000UL) -#if (TLED_CHIP == 2) - _4_NOP _1_NOP +//----------------------------------------------------------------------------------------- +#if (F_CPU == 8000000UL) && (TLED_CHIP != 2) + "NOP \n\t" // Единственный NOP для 8мгц #else - _1_NOP -#endif - // ======================== -#elif (F_CPU == 16000000UL) -#if (TLED_CHIP == 2) - _8_NOP _4_NOP _2_NOP -#else - _8_NOP -#endif - // ======================== -#endif + +#if (F_CPU == 16000000UL) +#if (TLED_CHIP == 2) // 14CK delay (4 * 3CK) + LDI 1CK + NOP + "LDI %[DELAY], 4 \n\t" + "NOP \n\t" +#else // 8CK delay (2 * 3CK) + LDI 1CK + NOP + "LDI %[DELAY], 2 \n\t" + "NOP \n\t" +#endif +#elif (F_CPU == 8000000UL) // 5CK delay (1 * 3CK) + LDI 1CK + NOP + "LDI %[DELAY], 1 \n\t" + "NOP \n\t" +#elif (F_CPU == 9600000UL) +#if (TLED_CHIP == 0) // 4CK delay (1 * 3CK) + LDI 1CK + "LDI %[DELAY], 1 \n\t" +#elif (TLED_CHIP == 1) // 5CK delay (1 * 3CK) + LDI 1CK + NOP + "LDI %[DELAY], 1 \n\t" + "NOP \n\t" +#elif (TLED_CHIP == 2) // 8CK delay (2 * 3CK) + LDI 1CK + NOP + "LDI %[DELAY], 2 \n\t" + "NOP \n\t" +#endif +#endif + "_DELAY_LOOP_%=: \n\t" // Цикл задержки + "DEC %[DELAY] \n\t" // 1CK декремент + "BRNE _DELAY_LOOP_%=\n\t" // 2CK переход +#endif +//----------------------------------------------------------------------------------------- "CBI %[PORT], %[PIN] \n\t" // LOW на выход "LSL %[DATA] \n\t" // Сдвигаем данные влево - "DEC r18 \n\t" // Декремент счетчика циклов + "DEC %[CNT] \n\t" // Декремент счетчика циклов "BRNE _LOOP_START_%= \n\t" // Переход в начало цикла - : + :[CNT] "+r" (_loop_count), + [DELAY] "+r" (_delay_loop_count) :[DATA]"r"(data), [PORT]"I"(_SFR_IO_ADDR(TLED_PORT)), [PIN]"I"(pin) - :"r17", "r18" ); #elif (TLED_CHIP < 20) // 2 пин ленты asm volatile ( - "LDI R16, 8 \n\t" + "LDI %[CNT], 8 \n\t" "LOOP_%=: \n\t" "CBI %[CLK_PORT],%[CLK] \n\t" "CBI %[DAT_PORT],%[DAT] \n\t" @@ -196,16 +203,14 @@ class tinyLED { "SBI %[DAT_PORT],%[DAT] \n\t" "SBI %[CLK_PORT],%[CLK] \n\t" "LSL %[DATA] \n\t" - "DEC R16 \n\t" + "DEC %[CNT] \n\t" "BRNE LOOP_%= \n\t" - : - : - [CLK_PORT]"I"(_SFR_IO_ADDR(TLED_CLK_PORT)), + :[CNT] "+r" (_loop_count) + :[CLK_PORT]"I"(_SFR_IO_ADDR(TLED_CLK_PORT)), [DAT_PORT]"I"(_SFR_IO_ADDR(TLED_DAT_PORT)), [CLK]"I"(pinC), [DAT]"I"(pinD), [DATA]"r"(data) - : "r16" ); #else // SPI ленты SPI.transfer(data);