// Copyright 2021 IOsetting <iosetting(at)outlook.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * Demo: MAX7219 8x8 LED Matrix 4 in 1
 * Board: STC8G1K08, STC8G1K08A
 * 
 * P5.4  -> DIN
 * P3.2  -> SCLK
 * P5.5  -> CS
*/

/**
 * 
 * PlatformIO configuration example

[env:stc8g1k08]
platform = intel_mcs51
board = stc8g1k08
build_flags =
    -D__CONF_FOSC=36864000UL
    -D__CONF_MCU_MODEL=MCU_MODEL_STC8G1K08
    -D__CONF_CLKDIV=0x00
    -D__CONF_IRCBAND=0x01
    -D__CONF_VRTRIM=0x20
    -D__CONF_IRTRIM=0xA3
    -D__CONF_LIRTRIM=0x03
upload_protocol = custom
upload_port = /dev/ttyUSB0
upload_speed = 115200
upload_flags =
    -p$UPLOAD_PORT
    -s$UPLOAD_SPEED
    -e
upload_command = ${platformio.packages_dir}/tool-stc8prog/stc8prog $UPLOAD_FLAGS -f $SOURCE

*/

#include "fw_hal.h"

#define MAX7219_CS P55
#define MAX7219_BLOCKS       4

#define DECODE_MODE  0x09
#define INTENSITY    0x0A
#define SCAN_LIMIT   0x0B
#define SHUT_DOWN    0x0C
#define DISPLAY_TEST 0x0F

const uint8_t numbers[]={
  0x3e,0x63,0x63,0x7f,0x63,0x63,0x63,0x63, //A
  0x7e,0x63,0x63,0x7e,0x63,0x63,0x63,0x7e, //B
  0x3e,0x63,0x63,0x60,0x60,0x63,0x63,0x3e, //C
  0x7e,0x63,0x63,0x63,0x63,0x63,0x63,0x7e, //D
  0x7f,0x60,0x60,0x7f,0x60,0x60,0x60,0x7f, //E
  0x7f,0x60,0x60,0x7e,0x60,0x60,0x60,0x60, //F
  0x3e,0x63,0x63,0x60,0x67,0x63,0x63,0x3e, //G
  0x63,0x63,0x63,0x7f,0x63,0x63,0x63,0x63, //H
  0x3f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3f, //I
  0x1f,0x06,0x06,0x06,0x06,0x66,0x66,0x3c, //J
  0x63,0x66,0x6c,0x78,0x6c,0x66,0x63,0x61, //K
  0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7f, //L
  0x63,0x77,0x7f,0x6b,0x63,0x63,0x63,0x63, //M
  0x63,0x63,0x73,0x7b,0x6f,0x67,0x63,0x63, //N
  0x3e,0x63,0x63,0x63,0x63,0x63,0x63,0x3e, //O
  0x7e,0x63,0x63,0x63,0x7e,0x60,0x60,0x60, //P
  0x3c,0x66,0x66,0x66,0x66,0x6e,0x66,0x3f, //Q
  0x7e,0x63,0x63,0x63,0x7e,0x6c,0x66,0x63, //R
  0x3e,0x63,0x63,0x60,0x3e,0x03,0x63,0x3e, //S
  0x3f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, //T
  0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3e, //U
  0x63,0x63,0x63,0x63,0x63,0x36,0x1c,0x08, //V
  0x63,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63, //W
  0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x63, //X
  0x33,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x0c, //Y
  0x7f,0x03,0x06,0x0c,0x18,0x30,0x60,0x7f, //Z
  0x3e,0x63,0x73,0x6b,0x67,0x63,0x63,0x3e, //0
  0x0c,0x1c,0x3c,0x0c,0x0c,0x0c,0x0c,0x3f, //1
  0x3e,0x63,0x63,0x06,0x0c,0x18,0x30,0x7f, //2
  0x3e,0x63,0x63,0x0e,0x03,0x63,0x63,0x3e, //3
  0x06,0x0e,0x1e,0x36,0x66,0x7f,0x06,0x06, //4
  0x7f,0x60,0x60,0x7e,0x03,0x03,0x03,0x7e, //5
  0x3e,0x63,0x60,0x7e,0x63,0x63,0x63,0x3e, //6
  0x7f,0x03,0x03,0x06,0x0c,0x18,0x18,0x18, //7
  0x3e,0x63,0x63,0x3e,0x63,0x63,0x63,0x3e, //8
  0x3e,0x63,0x63,0x63,0x3f,0x03,0x63,0x3e, //9
};

uint8_t val[MAX7219_BLOCKS];
uint8_t character_len = sizeof(numbers) / 8;

void MAX7219_singeWrite(uint8_t index, uint8_t addr, uint8_t dat)
{
    index = index % MAX7219_BLOCKS;
    MAX7219_CS = 0;
    SPI_TxRx(addr);
    SPI_TxRx(dat);
    while(index--)
    {
        SPI_TxRx(addr);
        SPI_TxRx(dat);
    }
    MAX7219_CS = 1;
}

void MAX7219_multiWrite(uint8_t addr, uint8_t len, uint8_t* dat)
{
    MAX7219_CS = 0;
    while (len--)
    {
        SPI_TxRx(addr);
        SPI_TxRx(*dat++);
    }
    MAX7219_CS = 1;
}

void MAX7219_init(void)
{
    for (uint8_t i = 0; i < MAX7219_BLOCKS; i++)
    {
        MAX7219_singeWrite(i, SHUT_DOWN, 0x01);    // 0x00:shutdown, 0x01:normal
        MAX7219_singeWrite(i, DECODE_MODE, 0x00);  // Bypass code B decoder, no-decode operation
        MAX7219_singeWrite(i, SCAN_LIMIT, 0x07);   // Scan-limit, 0:1-digit, 1:2-digits, ..., 7:8-digits
        MAX7219_singeWrite(i, INTENSITY, 0x00);    // 0x00:min, 0xFF:max
        MAX7219_singeWrite(i, DISPLAY_TEST, 0x00); // 0x00:normal, 0x01:test mode
    }
}

void SPI_init(void)
{
    // MAX7219 can work with frequency up to 20MHz
    SPI_SetClockPrescaler(SPI_ClockPreScaler_4);
    // Clock idles low
    SPI_SetClockPolarity(HAL_State_OFF);
    // Data transfer is driven by lower SS pin
    SPI_SetClockPhase(SPI_ClockPhase_LeadingEdge);
    // MSB first
    SPI_SetDataOrder(SPI_DataOrder_MSB);
    // Define the output pins
    SPI_SetPort(SPI_AlterPort_P12P54_P13_P14_P15);
    // Ignore SS pin, use MSTR to swith between master/slave mode
    SPI_IgnoreSlaveSelect(HAL_State_ON);
    // Master mode
    SPI_SetMasterMode(HAL_State_ON);
    // Start SPI
    SPI_SetEnabled(HAL_State_ON);
}

void main()
{
    /**
     *    ________________________
     *    |                       |
     *    |_______________________|
     *    A                       B
     * 
     * pos: point to the bit for boundary A
     * lpos: point to the bit for boundary B
    */
    uint16_t pos = 0, lpos = 0;
    uint8_t i, j, cpos = 0, bpos = 0, tcpos = 0;

    SYS_SetClock();
    // Configure GPIO pins before SPI and device
    // SCLK(P32), CS(P55) 
    GPIO_P3_SetMode(GPIO_Pin_2, GPIO_Mode_Output_PP);
    GPIO_P5_SetMode(GPIO_Pin_5, GPIO_Mode_Output_PP);
    // MOSI(P54)
    GPIO_P5_SetMode(GPIO_Pin_4, GPIO_Mode_InOut_QBD);
    // Configure SPI and device
    SPI_init();
    MAX7219_init();

    while(1)
    {
        lpos = pos + sizeof(numbers) - MAX7219_BLOCKS * 8;
        cpos = lpos / 8;        // char position
        bpos = lpos % 8;        // bit position
        for (i = 0; i < 8; i++) // fill each line
        {
            for (j = 0; j < MAX7219_BLOCKS; j++)
            {
                tcpos = (cpos + j) % character_len;
                val[j] = numbers[tcpos * 8 + i] << bpos;
                tcpos = (cpos + j + 1) % character_len;
                val[j] |= numbers[tcpos * 8 + i] >> (8 - bpos);
            }
            MAX7219_multiWrite(i + 1, MAX7219_BLOCKS, val);
        }
        pos = (pos + 1) % sizeof(numbers);
        SYS_Delay(100);
    }
}