/*
 * ADC.c
 *
 * Created: Tue Sep 13 2022 19:51:36
 *  Author Chris
 */
// ============================================================================================
// Includes
#include "Core1_ADC.h"

#include "hardware/adc.h"
#include "hardware/irq.h"
#include "hardware/divider.h"


// ============================================================================================
// Defines
#define YES							true
#define NO							false

#define ANALOG_REFERENCE_mV			3300ul
#define ADC_INPUT_PIN				29


// ============================================================================================
// Variables
static uint32_t _ADC_Result_Value;
static uint32_t _ADC_Result_mV;

const uint32_t	_ADC_Result_Num		= 11;
const uint32_t	_ADC_Result_Denum	= 1;
const bool		_ADC_Result_LowPass	= NO;


// ============================================================================================
// Function Declarations
void Core1_ADC_Select_Next_MUX_Channel();
void Core1_ADC_Set_Mux(uint channel);
void Core1_ADC_Store_ADC_Result(uint16_t adc_value);
uint32_t Core1_ADC_Low_Pass_Filter(int current_value, int new_value);


/*******************************************************************
	Interrupt Service Routines
*******************************************************************/
bool ISR_ADC()
{
	Core1_ADC_Store_ADC_Result(adc_fifo_get());
}


/*******************************************************************
	Functions
*******************************************************************/
void Core1_ADC_Init()
{
	_ADC_Result_Value 	= 0;
	_ADC_Result_mV 		= 0;
	
	adc_gpio_init(ADC_INPUT_PIN);
	adc_init();
	adc_select_input(3);
	adc_fifo_setup(
		true,  	// Write each completed conversion to the sample FIFO
		false, 	// Enable DMA data request (DREQ)
		1,	   	// DREQ (and IRQ) asserted when at least 1 sample present
		false, 	// We won't see the ERR bit because of 8 bit reads; disable.
		false	// Shift each sample to 8 bits when pushing to FIFO
	);
	adc_set_clkdiv(5999);		// One measurement every 0.125ms -> 8 measurements per 1 ms
	adc_irq_set_enabled(true);

	irq_set_exclusive_handler(ADC_IRQ_FIFO, (void *)ISR_ADC);
	irq_set_enabled(ADC_IRQ_FIFO, true);

	adc_run(true);
}

uint16_t Core1_ADC_Get_Result_Value(uint channel)
{
	return _ADC_Result_Value;
}

uint16_t Core1_ADC_Get_Result_mV(uint channel)
{
	return _ADC_Result_mV;
}


/*******************************************************************
	Internal Functions
*******************************************************************/
void Core1_ADC_Store_ADC_Result(uint16_t adc_value)
{
	if(_ADC_Result_LowPass == YES) {
		_ADC_Result_Value = (uint16_t)Core1_ADC_Low_Pass_Filter((float)_ADC_Result_Value, (float)adc_value);
	} else {
		_ADC_Result_Value = (uint32_t)adc_value;
	}

	_ADC_Result_mV	= ((_ADC_Result_Value * ANALOG_REFERENCE_mV * _ADC_Result_Num) / 4096) / _ADC_Result_Denum;
}

uint32_t Core1_ADC_Low_Pass_Filter(int current_value, int new_value)
{
	// Link: https://kiritchatterjee.wordpress.com/2014/11/10/a-simple-digital-low-pass-filter-in-c/
	
	const int Beta = 6;

	current_value = (current_value << Beta) - current_value; 
	current_value += new_value;
	current_value >>= Beta;

	return (uint32_t)current_value;
}
