/**
*   @copyright       © 2026 Microchip Technology Inc.
    Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS. 
*
*************************************************************************/

/**
 *  @file       diag_adc_shared.c
 *  @version    1.0
 *  @brief      This file has the implementation for the ADC common 
 *		        functions. @see diag_adc_shared.h
 *
 *************************************************************************/

#include <xc.h>
#include "diag_adc.h"
#include "diag_adc_shared.h"

#define DIAG_ADC_CONVERSION_TIMEOUT   0x50000U

#define DIAG_ADC_READY 0x80000000U

static uint32_t gADCControlReg, gADCSoftwareTriggerReg, gChannelControl1Reg;

//The base address for the ADC sfrs
const DIAG_ADC_BASE_ADDRESS baseAddress =
                    {
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CON,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1STAT,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CMPSTAT,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1SWTRG,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CH0CON,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CH0DATA,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CH0CMPLO,
                        /* LDRA_EXCLUDE 439 S <justification start>Variable collects the address of the SFR. Violation Ignored.<justification end> */
                        (uint32_t)&AD1CH0CMPHI                      
                    };

const uint16_t nextADCInstanceAddress [DIAG_ADC_MAX_INSTANCE] = { 0x0000U, 0x0200U};
static const uint16_t totalChannelsADCInstance [DIAG_ADC_MAX_INSTANCE] = {20U, 20U};

static void DIAG_ADC_ConfigurationSet(DIAG_ADC_CHANNELS adcChannel)
{
    volatile uint32_t *ptrSoftwaretrigger;
    volatile uint32_t *ptrChannelConfig1;
    volatile uint32_t *ptrADCControlReg;
    uint32_t value;
    uint32_t timoutConversion = DIAG_ADC_CONVERSION_TIMEOUT;
    uint32_t instance = DIAG_ADC_GetADCInstance(adcChannel);
    uint32_t channelNumber = DIAG_ADC_GetADCChannel(adcChannel);
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrChannelConfig1 = (uint32_t *)((baseAddress.chControl1Reg + nextADCInstanceAddress [instance])
            +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    value = *ptrChannelConfig1;
    value = value & 0x00077800U;
    value = value | 0x0000000E1U;
    *ptrChannelConfig1 = value;
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrADCControlReg = (uint32_t *)(baseAddress.controlReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrADCControlReg = 0x02008000U;
    
    //Wait until ADC is ready or timeout
    while(((*ptrADCControlReg & DIAG_ADC_READY) == 0U) && (timoutConversion != 0U))
    {
        timoutConversion--;
    }
    
    //Enable software trigger for the desired channel
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrSoftwaretrigger = (uint32_t *)(baseAddress.softTrigReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrSoftwaretrigger = ((*ptrSoftwaretrigger) | ((uint32_t)1U<<channelNumber));
}

uint16_t DIAG_ADC_GetADCInstance(DIAG_ADC_CHANNELS adcChannel)
{
    uint16_t instance = (uint16_t)adcChannel;
    
    instance = (uint16_t)(((instance) & (uint16_t)(0xFF00U)) >> (uint16_t)(8U));
    return instance;
}

uint16_t DIAG_ADC_GetADCChannel(DIAG_ADC_CHANNELS adcChannel)
{
    uint16_t channelNumber = (uint16_t)adcChannel;
    
    channelNumber = (uint16_t)((channelNumber) & (uint16_t)(0x00FFU));
    return channelNumber;
}

uint16_t DIAG_ADC_IsChannelValid(DIAG_ADC_CHANNELS adcChannel)
{
    uint16_t status;
    uint32_t instance;
    uint32_t channelNumber;

    instance = DIAG_ADC_GetADCInstance(adcChannel);
    /* LDRA_EXCLUDE 91 D <justification start>The variable is used in conditional check. Violation Ignored.<justification end> */
	channelNumber = DIAG_ADC_GetADCChannel(adcChannel);
    
    if((instance >= DIAG_ADC_MAX_INSTANCE) ||(channelNumber >= totalChannelsADCInstance [instance]))
    {
        status = 0U;
    }
    else
    {
        status = 1U;
    }
    
    return status;
}

void DIAG_ADC_StoreRegisters(DIAG_ADC_CHANNELS adcChannel)
{
    volatile uint32_t *ptrADCControlReg;
    volatile uint32_t *ptrADCSoftwareTriggerReg;
    volatile uint32_t *ptrChannelControl1Reg;
    uint32_t instance;
    uint32_t channelNumber;
    
    instance = DIAG_ADC_GetADCInstance(adcChannel);
	channelNumber = DIAG_ADC_GetADCChannel(adcChannel);
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrADCControlReg = (uint32_t *)(baseAddress.controlReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    gADCControlReg = *ptrADCControlReg;
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrADCSoftwareTriggerReg = (uint32_t *)(baseAddress.softTrigReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    gADCSoftwareTriggerReg = *ptrADCSoftwareTriggerReg;
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrChannelControl1Reg = (uint32_t *)((baseAddress.chControl1Reg + nextADCInstanceAddress [instance])
            +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    gChannelControl1Reg = *ptrChannelControl1Reg;
}

void DIAG_ADC_RestoreRegisters(DIAG_ADC_CHANNELS adcChannel)
{
    volatile uint32_t *ptrADCControlReg;
    volatile uint32_t *ptrADCSoftwareTriggerReg;
    volatile uint32_t *ptrChannelControl1Reg;
    uint32_t instance;
    uint32_t channelNumber;
    
    instance = DIAG_ADC_GetADCInstance(adcChannel);
	channelNumber = DIAG_ADC_GetADCChannel(adcChannel);
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrADCControlReg = (uint32_t *)(baseAddress.controlReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrADCControlReg = gADCControlReg;
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrADCSoftwareTriggerReg = (uint32_t *)(baseAddress.softTrigReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrADCSoftwareTriggerReg = gADCSoftwareTriggerReg;
    
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrChannelControl1Reg = (uint32_t *)((baseAddress.chControl1Reg + nextADCInstanceAddress [instance])
            +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrChannelControl1Reg = gChannelControl1Reg;
}

uint32_t DIAG_ADC_TriggerConversionAndGetResult(DIAG_ADC_CHANNELS adcChannel)
{
    volatile uint32_t *ptrConversionDone;
    volatile uint32_t *ptrChannelResult;
    uint32_t result;
    uint32_t timoutConversion = DIAG_ADC_CONVERSION_TIMEOUT;
    uint32_t instance = DIAG_ADC_GetADCInstance(adcChannel);
    uint32_t channelNumber = DIAG_ADC_GetADCChannel(adcChannel);
    
    DIAG_ADC_ConfigurationSet(adcChannel);
        
    //Assign the pointer to conversion status
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
	/*LDRA_EXCLUDE 105 D <justification start> The pointer variable is used for access, hence violation ignored <justification end> */
	ptrConversionDone = (uint32_t *)(baseAddress.dataStatReg + nextADCInstanceAddress [instance]);
    
    //Wait until the conversion is done or the timeout
    /*LDRA_NOANALYSIS*/
    while(((*ptrConversionDone & ((uint32_t)1U << channelNumber)) == 0U) && (timoutConversion != 0U))
    {
        timoutConversion--;
    }
    /*LDRA_ANALYSIS*/
    
    //Assign the pointer to the ADC result
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrChannelResult = (uint32_t *)((baseAddress.chDataReg + nextADCInstanceAddress [instance])
            +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    result = *ptrChannelResult;
    result = (result & (uint32_t)(0x00000FFFU));
    
    return result;
}
