/**
 * Kap18-DS3231I2C-To-Display-MAX7219-SPI
 * ======================================
 *
 * Dieses Beispiel ist eine Kombination der Projekte Kap17-I2C-RTC-mit-DS3231
 * und Kap18-SPI-7-Segment-Display. Es verwendet die DS3231-Echtzeituhr, die
 * ueber I2C1 mit dem STM32F446 verbunden ist und zeigt Uhrzeit und Datum auf
 * dem an SPI1 angeschlossenen achtstelligen 7-Segment-Display an. Es verwendet
 * zudem den User-Button des NUCLEO-Boards. Standardmaessig wird die aktuelle
 * Uhrzeit angezeigt. Wird der User-Button betaetigt, so wird fuer fuenf Sekunden
 * das Datum angzeigt. Nach Ablauf der fuenf Sekunden wird wieder die Uhrzeit
 * angezeigt.
 *
 * Das Beispiel ist nicht vollstaendig! Es fehlen beispielsweise Funktionen zur
 * Einstellung von Datum und Uhrzeit: Sie werden einmalig vor dem Eintritt in
 * die while(1)-Schleife vorgegeben. Vor einem erneuten Reset des NUCLEO-Boards
 * sollten die entsprechenden Zeilen mit Kommentarzeichen versehen werden.
 */

#include <stm32f4xx.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

/**
 * Kommentar einfuegen fuer SPI-16-Bit-Datenformat
 */
#define SPI_DATAFORMAT_8Bit


#include <mcalSystem.h>
#include <mcalSysTick.h>
#include <mcalGPIO.h>
#include <mcalSPI.h>
#include <mcalI2C.h>
#include <mcalEXTI.h>
#include <max7219.h>
#include <ds3231RTC.h>

bool     timerTrigger = false;
bool     showDateBtn  = false;
uint32_t showDateTimer = 0UL;

int main(void)
{
    GPIO_TypeDef *spiPort = GPIOA;
    GPIO_TypeDef *i2cPort = GPIOB;
    GPIO_TypeDef *btnPort = GPIOC;

    SPI_TypeDef  *spi   = SPI1;
    I2C_TypeDef  *i2c   = I2C1;

    uint32_t      pclk1Freq     = 0UL;
    uint32_t      max7219Timer  = 0UL;
    uint32_t      ds3231Timer   = 0UL;

    uint8_t       seconds = 0U;
    uint8_t       minutes = 0U;
    uint8_t       hours   = 0U;
    uint8_t       day     = 20;
    uint8_t       month   = 8;
    uint8_t       year    = 21;

    uint32_t *timerList[] = { &max7219Timer, &ds3231Timer, &showDateTimer };
//    uint32_t *timerList[] = { &max7219Timer, &ds3231Timer };
    size_t    arraySize   = sizeof(timerList) / sizeof(timerList[0]);

    // Initialisiert den Systick-Timer
    systickInit(SYSTICK_1MS);
    systickSetTicktime(&ds3231Timer, 500);
    systickSetTicktime(&max7219Timer, 500);
    systickSetTicktime(&showDateTimer, 5000);

    // Konfiguration des User-Buttons
    gpioSelectPort(btnPort);                        // Bustakt aktivieren
    gpioSelectPinMode(btnPort, PIN13, INPUT);

    // Konfiguration des EXTI des User-Buttons
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;           // SYSCFG: Bustakt aktivieren
    extiConfigIrq(btnPort, PIN13);
    extiSetTriggerEdge(PIN13, FALLING_EDGE);
    extiEnableIrq(PIN13);

    // SPI-Interface: Konfiguration der GPIO-Pins fuer das Display
    gpioSelectPort(spiPort);
    gpioSelectPinMode(spiPort, PIN4, OUTPUT);       // PA4 = ~CS
    gpioSelectPushPullMode(spiPort, PIN4, PULLUP);
    gpioSelectPinMode(spiPort, PIN5, ALTFUNC);      // PA5 = SPI1 Clock
    gpioSelectAltFunc(spiPort, PIN5, AF5);
    gpioSelectPinMode(spiPort, PIN7, ALTFUNC);      // PA7 = SPI1 MOSI
    gpioSelectAltFunc(spiPort, PIN7, AF5);

    // I2C-Interface:: Konfiguration der GPIO-Pins fuer die DS3231-RTC
    gpioSelectPort(i2cPort);
    gpioSelectPinMode(i2cPort, PIN8, ALTFUNC);      // PB8 : AF-Modus
    gpioSelectAltFunc(i2cPort, PIN8, AF4);          // PB8 : I2C1 SCL
    gpioSetOutputType(i2cPort, PIN8, OPENDRAIN);
    gpioSelectPushPullMode(i2cPort, PIN8, PULLUP);  // Interner Pullup

    gpioSelectPinMode(i2cPort, PIN9, ALTFUNC);      // PB9 : AF-Modus
    gpioSelectAltFunc(i2cPort, PIN9, AF4);          // PB9 : I2C1 SDA
    gpioSetOutputType(i2cPort, PIN9, OPENDRAIN);
    gpioSelectPushPullMode(i2cPort, PIN9, PULLUP);  // Interner Pullup

    // SPI-Interface: Konfiguration
    spiSelectSPI(spi);

#ifdef SPI_DATAFORMAT_8Bit
    spiInitSPI(spi, CLK_DIV_16, DATA_FORMAT_8, SSM_ON, SSI_LVL_HIGH, MASTER, SPI_PHASE_EDGE_1, SPI_IDLE_LOW);
#else
    spiInitSPI(spi, CLK_DIV_16, DATA_FORMAT_16, SSM_ON, SSI_LVL_HIGH, MASTER, SPI_PHASE_EDGE_1, SPI_IDLE_LOW);
#endif

    // I2C-Interface: Konfiguration
    pclk1Freq = sysGetPclk1Freq();
    i2cSelectI2C(i2c);
    i2cInitI2C(i2c, pclk1Freq, I2C_DUTY_CYCLE_2, 17, I2C_CLOCK_100);

    // Init MAX7219
    max7219Init(spi);
    max7219SetDecodeMode(spi, DECODE_ALL);
    max7219ResetDigits(spi, 0);

    ds3231SetTime(i2c, DS3231_ADDR, 14, 24, 0);
    ds3231SetDate(i2c, DS3231_ADDR, 2, 9, 21);

    NVIC_EnableIRQ(EXTI15_10_IRQn);                 // User-Button

    while (1)
    {
        if (timerTrigger == true)
        {
            systickUpdateTimerList((uint32_t *) timerList, arraySize);
        }

        if (isSystickExpired(ds3231Timer))
        {
            ds3231GetTime(i2c, DS3231_ADDR, &hours, &minutes, &seconds);
            ds3231GetDate(i2c, DS3231_ADDR, &day, &month, &year);
            systickSetTicktime(&ds3231Timer, 500);
        }

        if (isSystickExpired(max7219Timer) && (false == showDateBtn))
        {
            spiSendData(spi, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG0, seconds % 10);
            spiSendData(spi, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG1, seconds / 10);

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG2, 0x0A);            // Bindestrich

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG3, minutes % 10);
            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG4, minutes / 10);

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG5, 0x0A);            // Bindestrich

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG6, hours % 10);
            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG7, hours / 10);

            systickSetTicktime(&max7219Timer, 500);
        }

        if (showDateBtn == true && isSystickExpired(showDateTimer))
        {
            showDateBtn = false;
        }

        if (showDateBtn == true)
        {

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG7, day / 10);
            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG6, (day % 10) | 0x80);

            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG4, month / 10);
            spiSendData(SPI1, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG3, (month % 10) | 0x80);

            spiSendData(spi, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG1, year / 10);
            spiSendData(spi, MAX7219_CS_PORT, MAX7219_CS_PIN, REG_DIG0, (year % 10) | 0x80);
        }
}

    return 0;
}

void EXTI15_10_IRQHandler(void)
{
    extiResetPendingIRQ(EXTI_PIN13);
    systickSetTicktime(&showDateTimer, 5000);
    showDateBtn = true;
}
