/*
 * Created on: Jul 27, 2024
 *
 * because the ambient light sensor is facing toward the bottom, and
 * this badge has an "ADJUST" knob that is intended for brightness,
 * the light sensor code is incomplete.
 *
 * if you want to use it, copy code from another badge, where it's also
 * likely incomplete lol
 */

#include <ch32v00x.h>



// deadzone fudge factors for potentionmeter. test on actual badge
// and see if necessary, or if pot hardware + adc periph is good enough
#define POT_LO          40
#define POT_HI          (1024-POT_LO)



uint16_t adc_val[16];
uint16_t adc_val_idx;
uint16_t adc_avg;

uint16_t lsens_val;



void adc_init()
{
    ADC_InitTypeDef  adc = {0};
    GPIO_InitTypeDef gpio = {0};


    RCC_ADCCLKConfig(RCC_PCLK2_Div4);

    gpio.GPIO_Pin = GPIO_Pin_3;
    gpio.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOD, &gpio);

    ADC_DeInit(ADC1);
    adc.ADC_Mode = ADC_Mode_Independent;
    adc.ADC_ScanConvMode = DISABLE;
    adc.ADC_ContinuousConvMode = DISABLE;
    adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    adc.ADC_DataAlign = ADC_DataAlign_Right;
    adc.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &adc);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_241Cycles);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_73Cycles);
    ADC_Calibration_Vol(ADC1, ADC_CALVOL_50PERCENT);
    ADC_Cmd(ADC1, ENABLE);

    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

void adc_calc_avg()
{
    uint8_t i;
    uint16_t sum = 0;

    for (i = 0; i < 16; i++) {
        sum += adc_val[i];
    }

    sum >>= 4;

    adc_avg = sum;
}

void adc_convert()
{
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

void adc_read()
{
    if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {
        adc_val[adc_val_idx++] = ADC_GetConversionValue(ADC1);
        adc_val_idx &= 0x0f;
        if (!adc_val_idx) adc_calc_avg();

        if (ADC1->CTLR1 & ADC_JAUTO) {
            if (ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC)) {
                lsens_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);

                // reset LSENS
                ADC_AutoInjectedConvCmd(ADC1, DISABLE);
            }
        }
    }
}

void adc_use_lsens()
{
    GPIO_InitTypeDef gpio = {0};

    // configure GPIO
    gpio.GPIO_Pin = GPIO_Pin_2;
    gpio.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &gpio);

    // enable injected channel
    ADC_AutoInjectedConvCmd(ADC1, ENABLE);
}

void adc_stop_lsens()
{
    GPIO_InitTypeDef gpio = {0};

    // configure GPIO
    gpio.GPIO_Pin = GPIO_Pin_2;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &gpio);
}

uint8_t adc_get_pot()
{
    uint32_t pot;

    // set lower bound
    pot = adc_avg;
    if (pot < POT_LO) pot = 0; else pot -= POT_LO;

    // scale to upper bound
    pot <<= 10;
    pot /= POT_HI;

    // 8 bits is good enough for us
    pot >>= 2;

    return 255 - (pot & 0xff);
}

uint16_t adc_get_lsens()
{
    return lsens_val;
}
