/**
 * Kap18-SPI-7-Segment-Display
 * ===========================
 *
 * Ansteuerung einer 7-Segment-Anzeige mit dem MAX7219 ueber SPI.
 */

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

/**
 * Kommentar in Zeile 15 entfernen, wenn Sie die MCAL testen möchten.
 */
//#define MCAL

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


#include <mcalSysTick.h>
#include <mcalGPIO.h>
#include <mcalSPI.h>
#include <max7219.h>

void max7219Init(SPI_TypeDef *spi);

bool timerTrigger = false;


int main(void)
{
    GPIO_TypeDef *port = GPIOA;
    SPI_TypeDef  *spi  = SPI1;
    uint32_t      spiTimer = 0UL;
    uint32_t      data = 1234UL;
    uint8_t       d0, d1, d2, d3, d4, d5, d6, d7;

#ifdef MCAL     // Start der MCAL-Version

    // Initialisiert den Systick-Timer
    systickInit(SYSTICK_1MS);
    systickSetMillis(&spiTimer, 1);

    // SPI: Wir verwenden GPIOA
    gpioInitPort(port);
    gpioSelectPinMode(port, MAX7219_CS, OUTPUT);        // PA4 = ~CS
    gpioSelectPushPullMode(port, MAX7219_CS, PULLUP);
    gpioSelectPinMode(port, MAX7219_CLK, ALTFUNC);      // PA5 = SPI1 Clock
    gpioSelectAltFunc(port, MAX7219_CLK, AF5);
    gpioSelectPinMode(port, MAX7219_MOSI, ALTFUNC);     // PA7 = SPI1 MOSI
    gpioSelectAltFunc(port, MAX7219_MOSI, AF5);

    // Init SPI1
    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

#else       // Ende der MCAL-Version, Beginn: Direkte Registerprogrammierung

    // Konfiguration des SysTick-Timers
    SystemCoreClockUpdate();
    SysTick_Config(SystemCoreClock / 1000);

    // Konfiguration von GPIOA
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;            // GPIOA: Bustakt aktivieren
    port->MODER  &= ~GPIO_MODER_MODE4_Msk;          // PA4  : Reset
    port->MODER  |= GPIO_MODER_MODE4_0;             // PA4  : Output

    port->MODER  &= ~GPIO_MODER_MODE5_Msk;          // PA5  : Reset der Betriebsart
    port->MODER  |= GPIO_MODER_MODE5_1;             // PA5  : AltFunc-Modus ein (SPI CLK)
    port->AFR[0] |= (GPIO_AFRL_AFRL5_2 |            // PA5  : AF5 waehlen
                     GPIO_AFRL_AFRL5_0);

    port->MODER  &= ~GPIO_MODER_MODE7_Msk;          // PA7  : Reset der Betriebsart
    port->MODER  |= GPIO_MODER_MODE7_1;             // PA7  : AltFunc-Modus ein (SPI MOSI)
    port->AFR[0] |= (GPIO_AFRL_AFRL7_2 |            // PA7  : AF5 waehlen
                     GPIO_AFRL_AFRL7_0);

    // Konfiguration von SPI1: CLKDIV = 16, DATA_FORMAT = 8 Bit
    // Die &= ~xxx-Verknuepfungen sind nicht zwingend erforderlich, da alle Bits von CR1
    // zunaechst zurueckgesetzt werden. Es ist aber sauberer, dies dennoch zu tun, um ein
    // besseres Verstaendnis von SPI zu erlangen.
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;             // SPI1 : Bustakt aktivieren
    spi->CR1     =  0;                              // SPI1 : Reset CR1
    spi->CR1     |= SPI_CR1_BR_1 | SPI_CR1_BR_0;    // SPI1 : CLKDIV = 16
    spi->CR1     &= ~SPI_CR1_DFF_Msk;               // SPI1 : Datenformat = 8 Bit
    spi->CR1     |= SPI_CR1_SSM;                    // SPI1 : SSM aktivieren
    spi->CR1     |= SPI_CR1_SSI;                    // SPI1 : SSI Level High
    spi->CR1     |= SPI_CR1_MSTR;                   // SPI1 : Master Mode aktivieren
    spi->CR1     &= ~SPI_CR1_CPHA_Msk;              // SPI1 : Clockphase 1. Flanke
    spi->CR1     &= ~SPI_CR1_CPOL_Msk;              // SPI1 : Idle = Low setzen
    spi->CR1     |= SPI_CR1_SPE;                    // SPI1 : Enable
    spi->CR2     = 0;

#endif      // Ende: Direkte Registerprogrammierung

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

//    max7219Shutdown(spi);
    max7219TurnOn(spi);

    while (1)
    {
        if (timerTrigger == true)
        {
            DECREMENT_TIMER(spiTimer);
            timerTrigger = false;
        }

        if (isSystickExpired(spiTimer))
        {
            systickSetMillis(&spiTimer, 1);

            // Berechnung der einzelnen Stellen der Ziffern
            d0 = data % 10;
            d1 = (data / 10) % 10;
            d2 = (data / 100)  % 10;
            d3 = (data / 1000) % 10;
            d4 = (data / 10000) % 10;
            d5 = (data / 100000) % 10;
            d6 = (data / 1000000) % 10;
            d7 = (data / 10000000) % 10;

            // Prueft das gewaehlte Datenformat
            if (!(spi->CR1 & SPI_CR1_DFF))
            {
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG0);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d0);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG1);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d1);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG2);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d2);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG3);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d3);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG4);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d4);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG5);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d5);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG6);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d6);

                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, REG_DIG7);
                spiWriteByte(spi, MAX7219_CS_PORT, MAX7219_CS, d7);
            }
            else
            {
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG0_SL8 | d0));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG1_SL8 | d1));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG2_SL8 | d2));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG3_SL8 | d3));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG4_SL8 | d4));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG5_SL8 | d5));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG6_SL8 | d6));
                spiWriteWord(spi, MAX7219_CS_PORT, MAX7219_CS, (REG_DIG7_SL8 | d7));
            }

            if (++data > 99999999)
            {
                data = 0;
            }
        }
    }

    return 0;
}


