#include <Arduino.h>
#include <SPI.h>
#define NUM_ROWS 8
uint16_t matrix[NUM_ROWS] = {0};
uint8_t curRow = 0;
const int LATCH_PIN = 10;
const int OE_PIN = 9;
void setupTimer()
{
// https://www.8bit-era.cz/arduino-timer-interrupts-calculator.html
// TIMER 1 for interrupt frequency 8000 Hz:
cli(); // stop interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// set compare match register for 8000 Hz increments
OCR1A = 1999; // = 16000000 / (1 * 8000) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 1 prescaler
TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei(); // allow interrupts
}
void setup()
{
pinMode(OE_PIN, OUTPUT);
digitalWrite(OE_PIN, HIGH);
pinMode(LATCH_PIN, OUTPUT);
digitalWrite(LATCH_PIN, HIGH);
SPI.begin();
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
setupTimer();
Serial.begin(9600);
}
void loop()
{
if (Serial.available())
{
Serial.readBytes((uint8_t *)matrix, NUM_ROWS * sizeof(uint16_t));
}
}
static inline uint8_t reverse8(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;
}
ISR(TIMER1_COMPA_vect)
{
uint8_t rowByte = (1 << curRow);
uint8_t colA = (matrix[curRow] >> 8) & 0xFF;
uint8_t colB = matrix[curRow] & 0xFF;
PORTB |= (1 << PB1); // Disable output
PORTB &= ~(1 << PB2);
SPI.transfer(reverse8(colB)); // Region B SR
SPI.transfer(reverse8(colA)); // Region A SR
SPI.transfer(rowByte); // Row multiplexing SR
PORTB |= (1 << PB2);
PORTB &= ~(1 << PB1); // Re-enable output`
curRow++;
if (curRow >= NUM_ROWS)
curRow = 0;
}