/**
 * Kap11-GPTIM-03-PWM
 * ==================
 *
 * Nutzt Kanal 1 des Timers TIM2 zum Dimmen einer LED. Hierfuer wird
 * der Ausgang von Kanal 1 in der Betriebsart "PWM mode 1" verwendet.
 * Wir verwenden Timer TIM2, weil dieser sowohl aufwaerts wie auch
 * abwaerts zaehlen kann.
 *
 * Der PWM-Modus funktioniert ebenfalls unabhaengig vom Mikrocontroller.
 * Das Beispiel berechnet aber in der while(1)-Schleife in einem 10ms-
 * Rhythmus neue Werte für das TIMx->CCRn-Register, das den Capture-/
 * Compare-Wert des Timers festlegt. Der 10ms-Rhythmus wird hier wieder
 * mit dem SysTick-Timer erzeugt.
 *
 * Damit das Listing nicht zu lang wird, nutzt das Beispiel den SysTick-
 * Timer ausschliesslich in der MCAL-Version.
 */

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

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

#include <stm32f4xx.h>
#include <mcalSysTick.h>

#ifdef MCAL
#include <mcalGPIO.h>
#include <mcalTimer/mcalTimer.h>
#endif


/**
 * Globale Variablen und Definitionen
 */
#define UP      (0)
#define DOWN    (1)

bool timerTrigger = false;

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

    bool          direction  = UP;
    uint32_t      pwmTimer   = 0UL;
    uint32_t      preloadVal = 100UL;

    // SysTick-Timer konfigurieren
    systickInit(SYSTICK_1MS);
    systickSetTicktime(&pwmTimer, 10);

#ifdef MCAL     // Start der MCAL-Version

    // Konfiguration von GPIOA/Pin15, Modus: Alternative Funktion AF1
    gpioSelectPort(port);
    gpioSelectPinMode(port, PIN15, ALTFUNC);
    gpioSelectAltFunc(port, PIN15, AF1);

    // TIM2: Konfiguration
    timerSelectTimer(tim);
    timerSetPrescaler(tim, 10);
    timerSetAutoReloadValue(tim, 10000);
    timerResetCounter(tim);

    timerSetOutputCompareMode(tim, TIMIO_CH1, CHN_PWM_MODE_1);
    timerSetPreloadValue(tim, TIMIO_CH1, preloadVal);
    timerEnableCaptureCompareChannel(tim, TIMIO_CH1);
    timerStartTimer(tim);

    while (1)
    {
        if (true == timerTrigger)
        {
            systickUpdateTimer(&pwmTimer);
        }

        if (isSystickExpired(pwmTimer))
        {
            timerGetCaptureComparePreloadValue(tim, TIMIO_CH1, &preloadVal);
            if (UP == direction)
            {
                preloadVal = preloadVal * 105/100;
                timerSetCaptureComparePreloadValue(tim, TIMIO_CH1, preloadVal);

                if (preloadVal > 10000)
                {
                    direction = DOWN;
                }
            }
            else
            {
                preloadVal = preloadVal * 95/100;
                timerSetCaptureComparePreloadValue(tim, TIMIO_CH1, preloadVal);

                if (preloadVal < 100)
                {
                    direction = UP;
                }
            }
            systickSetTicktime(&pwmTimer, 10);
        }
    }

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

    // GPIOA/Pin15 konfigurieren: Alternative Funktion AF1 (Timer TIM2)
    RCC->AHB1ENR    |= RCC_AHB1ENR_GPIOAEN;     // GPIOA: Bustakt aktivieren
    port->MODER  &= ~GPIO_MODER_MODER15_Msk;    // PA15: Reset
    port->MODER  |= GPIO_MODER_MODER15_1;       // PA15: AF-Modus aktivieren
    port->AFR[1] &= ~GPIO_AFRH_AFSEL15_Msk;     // PA15: AF zuruecksetzen
    port->AFR[1] |= GPIO_AFRH_AFSEL15_0;        // PA15: Bit 0 --> AF1

    // Timer TIM2 konfigurieren
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;         // TIM2: Bustakt aktivieren
    tim->PSC     = 10;                          // TIM2: Prescaler = 10
    tim->ARR     = 10000;                       // TIM2: Auto-Reload-Register

    // TIM2: Output-Compare-Modus konfigurieren
    tim->CCMR1   &= ~(TIM_CCMR1_CC1S_Msk | TIM_CCMR1_OC1M_Msk);
    tim->CCMR1   |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;    // PWM = 0b110

    tim->CCMR1   |= TIM_CCMR1_OC1PE;            // TIM2: Preload aktivieren
    tim->CCR1    = preloadVal;                  // TIM2: Preload-Wert setzen
    tim->CCER    |= TIM_CCER_CC1E;              // TIM2: Kanal 1 --> Output
    tim->CNT     = 0;                           // TIM2: Counter reset
    tim->CR1     |= TIM_CR1_CEN;                // TIM2: Timerstarten

    while(1)
    {
        if (true == timerTrigger)
        {
            systickUpdateTimer(&pwmTimer);
        }

        if (isSystickExpired(pwmTimer))
        {
            preloadVal = tim->CCR1;
            if (UP == direction)
            {
                preloadVal = preloadVal * 105/100;
                tim->CCR1 = preloadVal;

                if (preloadVal > 10000)
                {
                    direction = DOWN;
                }
            }
            else
            {
                preloadVal = preloadVal * 95/100;
                tim->CCR1 = preloadVal;

                if (preloadVal < 100)
                {
                    direction = UP;
                }
            }
            systickSetTicktime(&pwmTimer, 10);
        }
    }

#endif      // Ende: Direkte Registerprogrammierung

}
