/* 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);
}