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


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;
//    uint32_t      showDateTimer = 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;
//
//    bool          showDate = false;

//    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);
//    extiConfigIrq(btnPort, PIN13);

    // SPI-Interface: Konfiguration der GPIO-Pins
    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
    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, 15, 6, 0);
    ds3231SetDate(i2c, DS3231_ADDR, 02, 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);
            systickSetTicktime(&ds3231Timer, 500);
//            systickSetTicktime(&showDateTimer, 5000);
        }

        if (isSystickExpired(max7219Timer))
        {
            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);
        }
    }

    return 0;
}

//void EXTI15_10_IRQHandler(void)
//{
//    uint8_t dummy;
//
//    dummy = 0;
//}
