/*!
 * @file        main.c
 *
 * @brief       This file contains all the functions for the ADC peripheral
 *
 * @version     V1.0.0
 *
 * @date        2025-05-31
 *
 * @attention
 *
 *  Copyright (C) 2023-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes */
#include "main.h"

/* ADC Init */
void ADC_Init(void);
/* ADC Measure */
void ADC_Measure(void);
/* ADC Isr */
void ADC_Isr(void);
/* TMR Init */
void TMR_Init(void);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 */
int main(void)
{
    APM_MINI_LEDInit(LED2);     // Initialize LED2
    APM_MINI_LEDInit(LED3);     // Initialize LED3

    APM_MINI_LEDOff(LED2);      // Turn off LED2
    APM_MINI_LEDOff(LED3);      // Turn off LED3

    TMR_Init();                 // Initialize the timer
    ADC_Init();                 // Initialize the ADC

    while (1)                   // Main loop
    {
        /* Add main program loop logic here */
    }
}

/*!
 * @brief       ADC Calibration
 *
 * @param       None
 *
 * @retval      None
 */
void ADC_Calibration(void)
{
    uint8_t i = 0;                      // Loop counter
    int8_t offset = 0;                  // Store the ADC offset
    uint16_t adcData = 0;               // Store the ADC reading

    ADC_SetOffset(0);                   // Set initial offset of the ADC to 0
    ADC_EnableCompensation();           // Enable ADC compensation
    ADC_ClearStatusFlag(ADC_FLAG_CC);   // Clear ADC conversion complete flag
    ADC_Enable();                       // Enable the ADC module

    for (i = 0; i < 10; i++)            // Perform calibration 10 times
    {
        ADC_StartConversion();          // Start ADC conversion

        while (ADC_ReadStatusFlag(ADC_FLAG_CC) == RESET); // Wait for conversion to complete

        ADC_ClearStatusFlag(ADC_FLAG_CC); // Clear the conversion complete flag
    }

    ADC_Disable();                      // Disable the ADC module

    adcData = ADC_ReadData();           // Read data from the ADC
    offset = (int8_t)(0x800 - adcData); // Calculate the offset value
    ADC_SetOffset(offset);              // Set the ADC offset

    ADC_DisableCompensation();          // Disable ADC compensation
    ADC_ClearStatusFlag(ADC_FLAG_CC);   // Clear the status flag
}

/*!
 * @brief       ADC Initialization
 *
 * @param       None
 *
 * @retval      None
 */
void ADC_Init(void)
{
    ADC_Config_T adcConfig;             // Define ADC configuration structure
    GPIO_Config_T gpioConfig;           // Define GPIO configuration structure

    /* ADC GPIO configuration */
    gpioConfig.intEn = GPIO_EINT_DISABLE;       // Disable GPIO external interrupt
    gpioConfig.mode = GPIO_MODE_IN_FLOATING;    // Set GPIO in floating input mode
    gpioConfig.speed = GPIO_SPEED_10MHz;        // Set GPIO speed to 10MHz

    /* ADC_CHANNEL_4 */
    gpioConfig.pin = GPIO_PIN_3;        // Set the GPIO pin to PIN3
    GPIO_Config(GPIOD, &gpioConfig);    // Configure the specified GPIO port

    ADC_SetMode(ADC_MODE_SINGLE_END);   // Set ADC to single-ended mode

    /* ADC configuration */
    ADC_ConfigStructInit(&adcConfig);               // Initialize the ADC configuration structure
    adcConfig.channel = ADC_CHANNEL_4;              // Set the ADC channel to channel 4
    adcConfig.convMode = ADC_CONV_MODE_SINGLE;      // Set conversion mode to single conversion
    adcConfig.interrupt = ADC_INT_CC;               // Enable conversion complete interrupt
    adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;     // Set data alignment to right aligned
    adcConfig.extTrig = ADC_EXTTRG_TIMER;           // Set external trigger to timer
    adcConfig.scanMode = ADC_SCAN_MODE_DISABLE;     // Disable scanning mode

    ADC_Config(&adcConfig);     // Configure the ADC module

    /* ADC Calibration */
    ADC_Calibration();          // Call the ADC calibration function

    NVIC_EnableIRQRequest(ADC_IRQn, 1); // Enable ADC interrupt request with priority set to 1

    ADC_Enable();               // Enable the ADC module

    ADC_StartConversion();      // Start ADC conversion
}

/*!
 * @brief       TMR Initialization
 *
 * @param       None
 *
 * @retval      None
 */
void TMR_Init(void)
{
    RCM_EnableAPBPeriphClock(RCM_PERIPH_TMR1);  // Enable the clock for timer 1
    TMR1_TimeBaseConfig_T timeBaseConfig;       // Define the timer configuration structure

    timeBaseConfig.cntMode = TMR1_CNT_MODE_UP;  // Set counting mode to count up
    timeBaseConfig.count = 999;                 // Set count period to 999
    timeBaseConfig.divider = 47;                // Set divider to 47
    TMR1_ConfigTimerBase(TMR1, &timeBaseConfig);// Configure the basic parameters of the timer

    TMR1_ConfigMasterMode(TMR1, TMR1_MASTER_MODE_UPDATE); // Set timer master mode to update

    TMR1_Enable(TMR1);  // Enable timer 1
}

/*!
 * @brief       ADC_Isr
 *
 * @param       None
 *
 * @retval      None
 */
void ADC_Isr(void)
{
    if (ADC_ReadIntFlag(ADC_INT_FLAG_CC))       // Check for conversion complete interrupt
    {
        APM_MINI_LEDToggle(LED2);               // Toggle the state of LED2
    }

    ADC_ClearIntFlag(ADC_INT_FLAG_CC);          // Clear the conversion complete interrupt flag
}
