/**
 * Projekt Kap09-SysTick-02 : Keine MCAL-Version verfuegbar
 * ========================================================
 *
 * Das Beispiel zeigt, dass mit nur einem Timer mehrere Aktionen
 * gesteuert werden können, wobei jede Aktion ein anderes Delay verwendet.
 *
 * ACHTUNG:
 * Dieses Beispiel ist noch lange nicht gut genug fuer den praktischen
 * Einsatz. Besser geeignete Beispiele folgen!
 */

#include <stm32f4xx.h>
#include <system_stm32f4xx.h>

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


uint16_t tick = 0UL;                            // Startwert des SysTick-Timers
bool     pin0TimerStarted = false;
bool     pin1TimerStarted = false;
uint32_t pin0Timer = 0UL;
uint32_t pin1Timer = 0UL;
bool     timerCorrected = false;
bool     timerTrigger = false;

#define SYSTICK_MAX     (10000)

void     SysTick_Handler(void);
void     UPDATETimers(void);
uint32_t getUptime(void);


int main(void)
{
    GPIO_TypeDef *port = GPIOA;

    SystemCoreClockUpdate();                    // SysTick: Konfig.
    SysTick_Config(SystemCoreClock / 1000);     // SysTick = 1 ms

    // Konfiguration von GPIOA/Pin0 und GPIOA/Pin1
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;        // GPIOA: Bustakt einschalten
    port->MODER  &= ~GPIO_MODER_MODE0_Msk;      // PA0: Reset
    port->MODER  |= GPIO_MODER_MODE0_0;         // PA0: Ouput
    port->OTYPER &= ~GPIO_OTYPER_OT0_Msk;       // PA0: Push-Pull-Mode ein
    port->PUPDR  &= ~GPIO_PUPDR_PUPD0_Msk;      // PA0: Pullup/Pulldown Reset
    port->PUPDR  |= GPIO_PUPDR_PUPD0_0;         // PA0: Int. Pullup-Wid. ein

    port->MODER  &= ~GPIO_MODER_MODE1_Msk;      // PA1: Reset
    port->MODER  |= GPIO_MODER_MODE1_0;         // PA1: Ouput
    port->OTYPER &= ~GPIO_OTYPER_OT1_Msk;       // PA1: Push-Pull-Mode ein
    port->PUPDR  &= ~GPIO_PUPDR_PUPD1_Msk;      // PA1: Pullup/Pulldown Reset
    port->PUPDR  |= GPIO_PUPDR_PUPD1_0;         // PA1: Int. Pullup-Wid. ein

    while(1)
    {
        // Zur Vermeidung eines Ueberlaufs des SysTick-Timers.
        UPDATETimers();

        // Pruefen, ob timerPin0 bereits laeuft
        if (false == pin0TimerStarted)
        {
            pin0Timer = getUptime() + 200;
            pin0TimerStarted = true;
        }

        if (true == pin0TimerStarted && getUptime() > pin0Timer)
        {
            if (port->IDR & GPIO_IDR_ID0)
            {
                port->BSRR = GPIO_BSRR_BR0;
            }
            else
            {
                port->BSRR = GPIO_BSRR_BS0;
            }
            pin0TimerStarted = false;
        }

        // Pruefen, ob timerPin1 bereits laeuft
        if (false == pin1TimerStarted)
        {
            pin1Timer = getUptime() + 400;
            pin1TimerStarted = true;
        }

        if (true == pin1TimerStarted && getUptime() > pin1Timer)
        {
            if (port->IDR & GPIO_IDR_ID1)
            {
                port->BSRR = GPIO_BSRR_BR1;
            }
            else
            {
                port->BSRR = GPIO_BSRR_BS1;
            }
            pin1TimerStarted = false;
        }
    }
}

/**
 * Interrupt-Service-Routine des SysTick-Timers. Wird nur in der
 * Bare-Metal-Version benoetigt.
 */
void SysTick_Handler(void)
{
    ++tick;

    if (tick > SYSTICK_MAX)
    {
        tick = 0UL;
        timerCorrected = true;
    }
}

/**
 * Abgelaufene Ticks des SysTick-Timers ermitteln
 */
uint32_t getUptime(void)
{
    return tick;
}

/**
 * Funktion zur Vermeidung des Ueberlaufens des SysTick-Timers.
 */
void UPDATETimers(void)
{
    if (true == timerCorrected)
    {
        pin0Timer -= SYSTICK_MAX;
        pin1Timer -= SYSTICK_MAX;
        timerCorrected = false;
    }
}
