/**
 * Kap11-GPTIM-02
 * ==============
 *
 * TIM12/Ch1: Output-Compare-Modus --> Ansteuerung einer LED, Betriebsart =
 *            Toggle-on-match.
 * TIM12/CH2: Input-Capture-Modus  --> Zaehlt die Impulse von TIM12/CH1 und
 *            stoppt den Timer, wenn CH1 10-mal invertiert wurde.
 */
#include <stdint.h>
#include <stdbool.h>

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

#include <stm32f4xx.h>

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

/**
 * Globale Variablen und Definitionen
 */
bool tim12Ch2PulseDetected = false;

int main(void)
{
    GPIO_TypeDef *port = GPIOB;
    TIM_TypeDef  *tim  = TIM12;

#ifdef MCAL     // Start der MCAL-Version

    // Konfiguration von GPIOA/Pin14 und 15, Modus: Alternative Funktion AF9
    gpioSelectPort(port);
    gpioSelectPinMode(port, PIN14, ALTFUNC);
    gpioSelectAltFunc(port, PIN14, AF9);

    gpioSelectPinMode(port, PIN15, ALTFUNC);
    gpioSelectAltFunc(port, PIN15, AF9);

    // TIM12: Konfiguration von Kanal 1 als Output, Toggel-Modus
    timerSelectTimer(tim);                          // TIM12: Bustakt ein
    timerSetPrescaler(tim, 1600);                   // TIM12: Takt = 100 kHz
    timerSetAutoReloadValue(tim, 5000);            // TIM12: ARR einstellen

    // Der Output soll bei jedem Impuls invertiert werden.
    timerSetOutputCompareMode(tim, TIMIO_CH1, CHN_TOGGLE_ON_MATCH);
    timerSetPreloadValue(tim, TIMIO_CH1, 0);
    timerEnableCaptureCompareChannel(tim, TIMIO_CH1);

    // TIM12: Konfiguration von Kanal 2 als Input, verbunden mit CH1.
    timerSetInputCaptureMode(tim, TIMIO_CH2, MAP_CHN_TO_SAME_TIMER);
    timerEnableCaptureCompareChannel(tim, TIMIO_CH2);
    timerEnableInterrupt(tim, TIM_CAPCOMP_IRQ_CH2); // IRQ fuer CH2 aktivieren

    timerResetCounter(tim);
    timerStartTimer(tim);

    NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);

    while (1)
    {
        if (tim12Ch2PulseDetected)
        {
            timerStopTimer(tim);
            tim12Ch2PulseDetected = false;
        }
    }

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

    // GPIOB: Bustakt anschalten
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;     // GPIOB: Bustakt aktivieren

    // Konfiguration von GPIOB / Pin 14
    port->MODER  &= ~GPIO_MODER_MODER14_Msk; // PB14: Reset auf Input
    port->MODER  |= GPIO_MODER_MODER14_1;    // PB14: AF-Modus aktivieren
    port->AFR[1] &= ~GPIO_AFRH_AFSEL14_Msk;  // PB14: AF zuruecksetzen
    port->AFR[1] |= GPIO_AFRH_AFSEL14_3 | GPIO_AFRH_AFSEL14_0; // AF9

    // Konfiguration von GPIOB / Pin 15
    port->MODER  &= ~GPIO_MODER_MODER15_Msk; // PB14: Reset auf Input
    port->MODER  |= GPIO_MODER_MODER15_1;    // PB14: AF-Modus aktivieren
    port->AFR[1] &= ~GPIO_AFRH_AFSEL15_Msk;  // PB14: AF zuruecksetzen
    port->AFR[1] |= GPIO_AFRH_AFSEL15_3 | GPIO_AFRH_AFSEL15_0; // AF9

    // TIM12: Basis-Konfiguration
    RCC->APB1ENR |= RCC_APB1ENR_TIM12EN;    // tim: Bustakt einschalten
    tim->PSC     = (1600 - 1);              // tim: Prescaler einstellen
    tim->ARR     = (5000 - 1);              // tim: Auto-Reload-Wert setzen

    // TIM12/Kanal 1 konfigurieren
    tim->CCMR1 &= ~(TIM_CCMR1_CC1S_Msk | TIM_CCMR1_OC1M_Msk |
                    TIM_CCMR1_CC2S_Msk);
    tim->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0;  // Toggle on match
    tim->CCMR1 |= TIM_CCMR1_OC1PE;          // tim: Preload enable
    tim->CCR1 = 0;                          // tim: Preload-Wert laden
    tim->CCER |= TIM_CCER_CC1E;             // tim: Kanal 1 aktivieren

    // TIM12/Kanal 2 konfigurieren
    tim->CCMR1 |= TIM_CCMR1_CC2S_1;         // tim: Mapping = gleicher Timer
    tim->CCER  |= TIM_CCER_CC2E;            // tim: Kanal 2 aktivieren
    tim->DIER  |= TIM_DIER_CC2IE;           // tim: Kanal 2-Interrupt

    tim->CNT  = 0;                          // tim: Counter zuruecksetzen
    tim->CR1  |= TIM_CR1_CEN;               // tim: Timer starten

    NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);

    while (1)
    {
        if (tim12Ch2PulseDetected)
        {
            tim->CR1 &= ~TIM_CR1_CEN_Msk;          // TIM12: Stopp
            tim12Ch2PulseDetected = false;
        }
    }

#endif      // Ende: Direkte Registerprogrammierung
}

/**
 * Interrupt-Service-Routine ISR fuer TIM12.
 *
 * Die ISR zaehlt die Impulse, die an Kanal 2 erkannt werden und inkrementiert
 * die statische Zaehlervariable count. Wenn diese > 9 wird, wird das Flag
 * tim12Ch2PulseDetected auf true gesetzt und hiermit der Timer gestoppt.
 */
void TIM8_BRK_TIM12_IRQHandler(void)
{
    static uint8_t count = 0;

    if (TIM12->SR & TIM_SR_CC2IF)
    {
        TIM12->SR &= ~TIM_SR_CC2IF_Msk;

        ++count;

        if (count > 9)
        {
            tim12Ch2PulseDetected = true;
        }
    }
}
