/* 7 rows LED bar firmware / library * Copyright (C) 2019 asdrea * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include #include #include "charset.h" #include "ledbar.h" static SPISettings spi_settings(1000000, LSBFIRST, SPI_MODE2); // Double buffer ROWSxCOLS bits static uint8_t ledbar_framebuf[2][LEDBAR_ROWS][LEDBAR_COLS >> 3]; static volatile uint8_t cur_buf = 0, swap_buf = 0; static inline uint8_t getbuf(ledbar_buf_t b) { return (b == LEDBAR_FRONTBUF ? cur_buf : (1 - cur_buf)); } void ledbar_init(void) { uint8_t i; for(i = 0; i < LEDBAR_ROWS; ++i) { pinMode(LEDBAR_PIN_ROWS[i], OUTPUT); digitalWrite(LEDBAR_PIN_ROWS[i], LOW); } pinMode(LEDBAR_PIN_STROBE, OUTPUT); digitalWrite(LEDBAR_PIN_STROBE, LOW); SPI.begin(); memset(ledbar_framebuf, 0, 2 * LEDBAR_ROWS * (LEDBAR_COLS >> 3)); // Set timer1 interrupt at ~350Hz cli(); TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; OCR1A = 44; // = 16e6 / (350 * 1024) - 1 (must be <65536) TCCR1B |= (1 << WGM12); // turn on CTC mode TCCR1B |= (1 << CS12) | (1 << CS10); // Set CS10 and CS12 bits for 1024 prescaler TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt sei(); } void ledbar_wait_vsync(void) { while(swap_buf); } void ledbar_swap_buf(void) { swap_buf = 1; } void ledbar_clear(ledbar_buf_t buf) { memset(ledbar_framebuf[getbuf(buf)], 0, LEDBAR_ROWS * (LEDBAR_COLS >> 3)); } #define _LEDBAR_SETCHAR_SETCOL(X) \ if(b < (8 - X)) { \ c0 |= ((chbuf[X] >> k) & 0x1) << ((7 - X) - b); \ } \ else { \ c1 |= ((chbuf[X] >> k) & 0x1) << ((15 - X) - b); \ } void ledbar_setchar(ledbar_buf_t buf, int16_t col, uint8_t ch) { uint8_t i, k, *p; uint8_t chbuf[5]; uint8_t c0 = 0, c1 = 0; int8_t block = (col >> 3); int8_t b = col - (block << 3); uint8_t buf_id = getbuf(buf); if(block < -1 || block >= (LEDBAR_COLS >> 3)) return; memcpy_P(chbuf, CHARSET[ch] + i, 5); for(i = 0; i < LEDBAR_ROWS; ++i) { p = ledbar_framebuf[buf_id][i] + block; if(block >= 0) c0 = p[0]; if(block < ((LEDBAR_COLS >> 3) - 1)) c1 = p[1]; k = LEDBAR_ROWS - i - 1; // Optimize this shit? _LEDBAR_SETCHAR_SETCOL(4); _LEDBAR_SETCHAR_SETCOL(3); _LEDBAR_SETCHAR_SETCOL(2); _LEDBAR_SETCHAR_SETCOL(1); c0 |= ((chbuf[0] >> k) & 0x1) << (7 - b); // == _LEDBAR_SETCHAR_SETCOL(0) if(block >= 0) p[0] = c0; if(block < ((LEDBAR_COLS >> 3) - 1)) p[1] = c1; } } void ledbar_settext(ledbar_buf_t buf, int16_t col, const char* text, uint8_t spacing) { uint16_t i; for(i = 0; text[i]; ++i) { ledbar_setchar(buf, col, text[i]); col += 5 + spacing; } } ISR(TIMER1_COMPA_vect) { static uint8_t cur_row = LEDBAR_ROWS - 1; uint8_t i, rowbuf[LEDBAR_COLS >> 3]; // Power-off previous row digitalWrite(LEDBAR_PIN_ROWS[cur_row++], LOW); if(cur_row >= LEDBAR_ROWS) { cur_row = 0; if(swap_buf) { // Swap buffers only on the first row (i.e. sync @50Hz) cur_buf = 1 - cur_buf; swap_buf = 0; } } // Buffer column data (SPI.transfer() modify the content) for(i = 0; i < (LEDBAR_COLS >> 3); ++i) rowbuf[i] = ledbar_framebuf[cur_buf][cur_row][(LEDBAR_COLS >> 3) - i - 1]; // Write column data SPI.beginTransaction(spi_settings); SPI.transfer(rowbuf, LEDBAR_COLS >> 3); SPI.endTransaction(); // Strobe digitalWrite(LEDBAR_PIN_STROBE, HIGH); digitalWrite(LEDBAR_PIN_STROBE, LOW); // Need a delay here? Glitchy? // Power-on current row digitalWrite(LEDBAR_PIN_ROWS[cur_row], HIGH); }