/*
  Controlling MAX7219 LED Dot Matrix using Arduino SPI.

  Copyright (C) 2021, Uri Shaked.
*/

#include <SPI.h>
#include <avr/pgmspace.h>

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

#define SEGMENT_COUNT 2
#define MAX7219_ROWS 8

#define INVERT true

uint8_t buffer[SEGMENT_COUNT][MAX7219_ROWS];

const byte digits[10][8] PROGMEM = {
  {B00000000, B00111000, B01000100, B01001100, B01010100, B01100100, B01000100, B00111000}, // 0
  {B00000000, B00010000, B00110000, B00010000, B00010000, B00010000, B00010000, B00111000}, // 1
  {B00000000, B00111000, B01000100, B00000100, B00001000, B00010000, B00100000, B01111100}, // 2
  {B00000000, B01111100, B00001000, B00010000, B00001000, B00000100, B01000100, B00111000}, // 3
  {B00000000, B00001000, B00011000, B00101000, B01001000, B01111100, B00001000, B00001000}, // 4
  {B00000000, B01111100, B01000000, B01111000, B00000100, B00000100, B01000100, B00111000}, // 5
  {B00000000, B00011000, B00100000, B01000000, B01111000, B01000100, B01000100, B00111000}, // 6
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01000000}, // 7
  {B00000000, B00111000, B01000100, B01000100, B00111000, B01000100, B01000100, B00111000}, // 8
  {B00000000, B00111000, B01000100, B01000100, B00111100, B00000100, B00001000, B00110000}, // 9
};

void clearBuffers() {
  for (int row = 0; row < SEGMENT_COUNT; row++) {
    for (int col = 0; col < 8; col++) {
      buffer[row][col] = 0;
    }
  }
}

void clearBuffer(int addr) {
    for (int col = 0; col < 8; col++) {
      buffer[addr][col] = 0;
    }
}

void setPixel(int x, int y) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(x);
  SPI.transfer(y);  
  digitalWrite(CS_PIN, HIGH);
}


void sendAll(int registerIndex, int value) {
  digitalWrite(CS_PIN, LOW);
  for (int i = 0; i < SEGMENT_COUNT; i++) {
    SPI.transfer(registerIndex);
    SPI.transfer(value);
  }
  digitalWrite(CS_PIN, HIGH);
}

void clearRow(int row) {
  sendAll(row, 0);
}

void clearDisplays() {
  for (int row = 1; row <= 8; row++) {
    clearRow(row);
  }
}

void setRow(int row) {

  digitalWrite(CS_PIN, LOW);
 
  for (int i=SEGMENT_COUNT-1; i>=0; i--) {
    SPI.transfer(row);
    #if INVERT
    SPI.transfer(reverse(buffer[i][row])); 
    #else
    SPI.transfer(buffer[i][row]); 
    #endif
  }

  digitalWrite(CS_PIN, HIGH);
}

void update() {
  clearDisplays();
  for (int i=SEGMENT_COUNT-1; i>0; i--) {
    for (int j=1; j<=8; j++) {
      setRow(j);
    }
    
  }
}

/*
void setDigit(int addr, int digit) {
  uint8_t *pattern = digits[digit];
  
  for (int i=8; i>=1; i--) {
    uint8_t line = pattern[i-1];
    buffer[addr][i] = line;
  }
  update();
}
*/

void setDigit(int addr, int digit) {
  uint8_t pattern[8];
  for (int i = 0; i < 8; i++) {
    pattern[i] = pgm_read_byte_near(&(digits[digit][i]));
  }

  for (int i = 8; i >= 1; i--) {
    uint8_t line = pattern[i - 1];
    buffer[addr][i] = line;
  }
  update();
}


void demo1() {
  setPixel(1,255);
  setPixel(1,254);
}

void demo2() {
  setDigit(0,9);
  setDigit(1,5);
}


byte reverse(unsigned char num) {
    byte  reversed = 0;
    int i;

    for (i = 0; i < 8; i++) {
        // Shift the reversed number to the left and add the least significant bit of the input number
        reversed = (reversed << 1) | (num & 1);
        num >>= 1;
    }

    return reversed;
}



void setup() {
  // Change SPI speed from 4mhz to 1mhz
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.begin();
  
  sendAll(0xf, 0); // Disable test mode
  sendAll(0xb, 7); // Set scanlines to 8
  //transfer(0x0A, 0x00); // Use lowest intensity
  clearDisplays();
  sendAll(0xc, 1); // Enable display

  //demo1();
  demo2();
}

void loop() {

}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
m1:V+
m1:GND
m1:DIN
m1:CS
m1:CLK
m1:V+.2
m1:GND.2
m1:DOUT
m1:CS.2
m1:CLK.2
m2:V+
m2:GND
m2:DIN
m2:CS
m2:CLK
m2:V+.2
m2:GND.2
m2:DOUT
m2:CS.2
m2:CLK.2