/* *************************************************************************************************
 * File:      analog.c
 * Date:      26 Mar 2026
 * Author:    Andrew Levido
 *
 * Copyright 2026 Imbrius Pty Ltd - All Rights Reserved
 *
 **************************************************************************************************/

/* Includes --------------------------------------------------------------------------------------*/
#include "analog.h"
#include "stm32l4xx_hal.h"


/* Defines ---------------------------------------------------------------------------------------*/
#define ADC_TIMEOUT     100             // 150 ms

#define DAC_V_MAX       12000           // mV
#define DAC_UA_MAX      4000            // nA
#define DAC_UA_MIN      250             // nA
#define DAC_NA_MAX      250000          // nA
#define DAC_NA_MIN      100             // nA

#define DAC_I_THR       125000          // nA

/* Typedefs & Enumerations -----------------------------------------------------------------------*/
typedef enum { DAC_DRV_OFF, DAC_DRV_V, DAC_DRV_ILO, DAC_DRV_IHI } dac_drive_t;

/* Global Variables ------------------------------------------------------------------------------*/
static struct {
  volatile int32_t vdda_mv;
  bool initialised;
  ADC_InjectionConfTypeDef config;

} adc;
/* Private Functions -----------------------------------------------------------------------------*/
void dac_set_drive(dac_drive_t drive)
{
  // turn all off
  HAL_GPIO_WritePin(MX_DV_GPIO_Port, MX_DV_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_DIL_GPIO_Port, MX_DIL_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_DIH_GPIO_Port, MX_DIH_Pin, GPIO_PIN_RESET);

  switch(drive) {
    case DAC_DRV_V:
      HAL_GPIO_WritePin(MX_DV_GPIO_Port, MX_DV_Pin, GPIO_PIN_SET);
      break;
    case DAC_DRV_ILO:
      HAL_GPIO_WritePin(MX_DIL_GPIO_Port, MX_DIL_Pin, GPIO_PIN_SET);
      break;
      case DAC_DRV_IHI:
        HAL_GPIO_WritePin(MX_DIH_GPIO_Port, MX_DIH_Pin, GPIO_PIN_SET);
        break;
      case DAC_DRV_OFF:
      default:
        break;
  }
}

/* Public Functions ------------------------------------------------------------------------------*/
int an_init(void)
{ 
  // initialise
  adc.vdda_mv = 3300;

  // Start DAC
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0);
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_2, DAC_ALIGN_12B_R, 0);
  HAL_DAC_Start(AN_DAC, DAC_CHANNEL_1);
  HAL_DAC_Start(AN_DAC, DAC_CHANNEL_2);

  // Configure Injected Channel for vrefint 
  adc.config.InjectedChannel = ADC_CHANNEL_VREFINT;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  adc.config.InjectedSamplingTime = ADC_SAMPLETIME_92CYCLES_5;
  adc.config.InjectedSingleDiff = ADC_SINGLE_ENDED;
  adc.config.InjectedOffsetNumber = ADC_OFFSET_NONE;
  adc.config.InjectedOffset = 0;
  adc.config.InjectedDiscontinuousConvMode = DISABLE;
  adc.config.AutoInjectedConv = DISABLE;
  adc.config.QueueInjectedContext = DISABLE;
  adc.config.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
  adc.config.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONV_EDGE_NONE;
  adc.config.InjecOversamplingMode = ENABLE;
  adc.config.InjecOversampling.Ratio = ADC_OVERSAMPLING_RATIO_256;
  adc.config.InjecOversampling.RightBitShift = ADC_RIGHTBITSHIFT_8;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) {
    return -1; 
  }

  // Calibrate ADC
  HAL_ADCEx_Calibration_Start(AN_ADC, ADC_SINGLE_ENDED);

  // Start injected conversion (vref) & wait for it to complete with timeout
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }

  // get results and scale to millivolts
  adc.vdda_mv = __HAL_ADC_CALC_VREFANALOG_VOLTAGE(HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);

  
  adc.initialised = true;
  return(ERR_OK);
}

void an_set_power(bool high)
{
  HAL_GPIO_WritePin(P_VSET_GPIO_Port, P_VSET_Pin, high == true ? GPIO_PIN_SET : GPIO_PIN_RESET);
}


void an_set_load_mv(int32_t load_mv)
{
  uint32_t dac_mv;

  if(adc.initialised == false) { return; }

  if(load_mv > DAC_V_MAX) { load_mv = DAC_V_MAX; }
  if(load_mv < 0) { load_mv = 0; }

  dac_mv = (load_mv * 1000) / AN_V_SCALE;     // DAC output in mv
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (4095 * dac_mv) / adc.vdda_mv);
}

void an_set_drive_mv(int32_t drive_mv)
{
  uint32_t dac_mv;

  if(adc.initialised == false) { return; }
  
  dac_set_drive(DAC_DRV_V);

  if(drive_mv > DAC_V_MAX) { drive_mv = DAC_V_MAX; }
  if(drive_mv < 0) { drive_mv = 0; }

  dac_mv = (drive_mv * 1000) / AN_V_SCALE;     // DAC output in mv
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_2, DAC_ALIGN_12B_R, (4095 * dac_mv) / adc.vdda_mv);
}


void an_set_drive_ua(int32_t drive_ua)
{
   uint32_t dac_mv;

  if(adc.initialised == false) { return; }

  if(drive_ua > DAC_UA_MAX) { drive_ua = DAC_UA_MAX; }
  if(drive_ua < -DAC_UA_MAX) { drive_ua = -DAC_UA_MAX; }
      
  dac_set_drive(DAC_DRV_IHI);
  dac_mv = ((AN_IHI_SCALE * (drive_ua ) / 100000L) + (adc.vdda_mv / 2));
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_2, DAC_ALIGN_12B_R, (4095 * dac_mv) / adc.vdda_mv);
}

void an_set_drive_na(int32_t drive_na)
{
  uint32_t dac_mv;

  if(adc.initialised == false) { return; }

  if(drive_na > DAC_NA_MAX) { drive_na = DAC_NA_MAX; }
  if(drive_na < -DAC_NA_MAX) { drive_na = -DAC_NA_MAX; }

  dac_set_drive(DAC_DRV_ILO);
  dac_mv = ((AN_ILO_SCALE * drive_na) / 1000000) + (adc.vdda_mv / 2);
  HAL_DAC_SetValue(AN_DAC, DAC_CHANNEL_2, DAC_ALIGN_12B_R, (4095 * dac_mv) / adc.vdda_mv);
}
void an_set_drive_none(void) 
{
  dac_set_drive(DAC_DRV_OFF);
}

void an_set_matrix(node_t node, lead_t lead)
{
  switch(node) {
    case NODE_POWER:
      // turn everything off
      HAL_GPIO_WritePin(MX_RP_GPIO_Port, MX_RP_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_GP_GPIO_Port, MX_GP_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_BP_GPIO_Port, MX_BP_Pin, GPIO_PIN_RESET);
      switch(lead) {
        case LEAD_RED:
          HAL_GPIO_WritePin(MX_RL_GPIO_Port, MX_RL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RP_GPIO_Port, MX_RP_Pin, GPIO_PIN_SET);
          break;
        case LEAD_GREEN:
          HAL_GPIO_WritePin(MX_GL_GPIO_Port, MX_GL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GP_GPIO_Port, MX_GP_Pin, GPIO_PIN_SET);
          break;
        case LEAD_BLUE:
          HAL_GPIO_WritePin(MX_BL_GPIO_Port, MX_BL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BP_GPIO_Port, MX_BP_Pin, GPIO_PIN_SET);
          break;
        case LEAD_NONE:
        default:
          break;
      }
      break;
    case NODE_LOAD:
      // turn everything off
      HAL_GPIO_WritePin(MX_RL_GPIO_Port, MX_RL_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_GL_GPIO_Port, MX_GL_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_BL_GPIO_Port, MX_BL_Pin, GPIO_PIN_RESET);
      switch(lead) {
        case LEAD_RED:
          HAL_GPIO_WritePin(MX_RP_GPIO_Port, MX_RP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RL_GPIO_Port, MX_RL_Pin, GPIO_PIN_SET);
          break;
        case LEAD_GREEN:
          HAL_GPIO_WritePin(MX_GP_GPIO_Port, MX_GP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GL_GPIO_Port, MX_GL_Pin, GPIO_PIN_SET);
          break;
        case LEAD_BLUE:
          HAL_GPIO_WritePin(MX_BP_GPIO_Port, MX_BP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BL_GPIO_Port, MX_BL_Pin, GPIO_PIN_SET);
          break;
        case LEAD_NONE:
        default:
          break;
      }
      break;
    case NODE_DRIVE:
      // turn everything off
      HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_RESET);
      switch(lead) {
        case LEAD_RED:
          HAL_GPIO_WritePin(MX_RP_GPIO_Port, MX_RP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RL_GPIO_Port, MX_RL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_SET);
          break;
        case LEAD_GREEN:
          HAL_GPIO_WritePin(MX_GP_GPIO_Port, MX_GP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GL_GPIO_Port, MX_GL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_SET);
          break;
        case LEAD_BLUE:
          HAL_GPIO_WritePin(MX_BP_GPIO_Port, MX_BP_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BL_GPIO_Port, MX_BL_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_SET);
          break;
        case LEAD_NONE:
        default:
          break;
      }
      break;
    default:
      break;
  }
}

void an_set_matrix_drive_short(lead_t lead_1, lead_t lead_2)
{
  an_set_matrix_drive_open();
  switch(lead_1) {
    case LEAD_RED: HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_SET); break;
    case LEAD_GREEN: HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_SET); break;
    case LEAD_BLUE: HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_SET); break;
    case LEAD_NONE:  break;
  }
    switch(lead_2) {
    case LEAD_RED: HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_SET); break;
    case LEAD_GREEN: HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_SET); break;
    case LEAD_BLUE: HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_SET); break;
    case LEAD_NONE:  break;
  }


}
void an_set_matrix_drive_open(void) 
{
  HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_RESET);
}

void an_set_matrix_all_open(void) 
{
  HAL_GPIO_WritePin(MX_RD_GPIO_Port, MX_RD_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_GD_GPIO_Port, MX_GD_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_BD_GPIO_Port, MX_BD_Pin, GPIO_PIN_RESET);

  HAL_GPIO_WritePin(MX_RL_GPIO_Port, MX_RL_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_GL_GPIO_Port, MX_GL_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_BL_GPIO_Port, MX_BL_Pin, GPIO_PIN_RESET);

  HAL_GPIO_WritePin(MX_RP_GPIO_Port, MX_RP_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_GP_GPIO_Port, MX_GP_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(MX_BP_GPIO_Port, MX_BP_Pin, GPIO_PIN_RESET);
}
int32_t an_get_red_mv(void) 
{
  uint32_t value;

  if(adc.initialised == false) { return -1; }

  // Configure Injected Channel for 1 conversions: vred
  adc.config.InjectedChannel = ADC_CHANNEL_5;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) { 
    return -1;
  }
  // Start injected conversion & wait for it to complete with timeout
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }
  value =__HAL_ADC_CALC_DATA_TO_VOLTAGE(adc.vdda_mv, HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);
  return((value * AN_RDC_SCALE) / 1000);
}

int32_t an_get_green_mv(void) 
{
  uint32_t value;

  if(adc.initialised == false) { return -1; }

  // Configure Injected Channel for 1 conversions: vred
  adc.config.InjectedChannel = ADC_CHANNEL_6;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) { 
    return -1;
  }
  // Start injected conversion & wait for it to complete with timeout
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }
  value =__HAL_ADC_CALC_DATA_TO_VOLTAGE(adc.vdda_mv, HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);
  return((value * AN_RDC_SCALE) / 1000);
}

int32_t an_get_blue_mv(void) 
{
  uint32_t value;

  if(adc.initialised == false) { return -1; }

  // Configure Injected Channel for 1 conversions: vred
  adc.config.InjectedChannel = ADC_CHANNEL_7;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) { 
    return -1;
  }
  // Start injected conversion & wait for it to complete with timeout
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }
  value =__HAL_ADC_CALC_DATA_TO_VOLTAGE(adc.vdda_mv, HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);
  return((value * AN_RDC_SCALE) / 1000);
}

int32_t an_get_load_ua(void) 
{
  int32_t value_1;
  int32_t value_2;

  if(adc.initialised == false) { return -1; }

  

  // Configure Injected Channel for 2 conversions: vred
  adc.config.InjectedChannel = ADC_CHANNEL_11;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) { 
    return -1000000;
  }
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }
  value_1 =__HAL_ADC_CALC_DATA_TO_VOLTAGE(adc.vdda_mv, HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);

  adc.config.InjectedChannel = ADC_CHANNEL_12;
  adc.config.InjectedRank = ADC_INJECTED_RANK_1;
  adc.config.InjectedNbrOfConversion = 1;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &adc.config) != HAL_OK) { 
    return -1000000;
  }
  // Start injected conversion & wait for it to complete with timeout
  HAL_ADCEx_InjectedStart(AN_ADC);
  if(HAL_ADCEx_InjectedPollForConversion(AN_ADC, ADC_TIMEOUT) != HAL_OK) {
    return -1;
  }
  value_2 =__HAL_ADC_CALC_DATA_TO_VOLTAGE(adc.vdda_mv, HAL_ADCEx_InjectedGetValue(AN_ADC, ADC_INJECTED_RANK_1), ADC_RESOLUTION_12B);
  return(((value_1 * AN_ILOAD_SCALE) - (value_2 * AN_ILOAD_SCALE))/ 1000);
}



int32_t an_get_vdda_mv(void) 
{
  if(adc.initialised == false) { return -1; }

  return(adc.vdda_mv);
}


/* End adc_dac */
