/*==============================================================================
 * Инициализация АЦП с использованием бибилотеки PLIB035
 *------------------------------------------------------------------------------
 * ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
 *==============================================================================
 *                                  ЦНИИ СЭТ
 *==============================================================================
 */

//-- Includes ------------------------------------------------------------------
#include "periph_config.h"

ADC_HandleTypeDef hadc;
//-- Defines -------------------------------------------------------------------

//-- Peripheral init functions -------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////SEQUENCORS/////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void adc_init_first(void)
{
#if (USE_ADC_SEQ0==1) || (USE_ADC_SEQ1==1) || (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
  // Настройка тактирования
  if(rcu_set_clock_adc(ADC_ClockSource, ADC_ClockMHz, ENABLE) != OK)
  {
    Error_Handler();
  }
  RCU_ADCRstCmd(ENABLE);
  // Включаем аналоговый модуль
  ADC_AM_Cmd(ENABLE); 
  
  hadc.Instance = ADC;
#endif
  
#if (USE_ADC_SEQ0==1)
  adc_seq_init(&hadc, ADC_SEQ_Num_0, &adc_seq0_config); 
  if(hadc.SEQ[ADC_SEQ_Num_0].Config->IT == ENABLE)
  {
    NVIC_EnableIRQ(ADC_SEQ0_IRQn);
  }
#endif
#if (USE_ADC_SEQ1==1)
  adc_seq_init(&hadc, ADC_SEQ_Num_1, &adc_seq1_config); 
  if(hadc.SEQ_Config[ADC_SEQ_Num_1]->IT == ENABLE)
  {
    NVIC_EnableIRQ(ADC_SEQ1_IRQn);
  }
#endif
#if (USE_ADC_DC0==1)
#endif
#if (USE_ADC_DC1==1)
#endif
#if (USE_ADC_DC2==1)
#endif
#if (USE_ADC_DC3==1)
#endif

#if (USE_ADC_SEQ0==1) || (USE_ADC_SEQ1==1) || (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
  uint32_t starttick = millis();
  while (!ADC_AM_ReadyStatus()) {
    if((millis() - starttick) > 1000)
      Error_Handler();
  }
#endif
}

/**
  * @brief  Инициализация секвенсора АЦП
  * @param  hadc указатель на хендл АЦП
  * @param  SEQ_Num номер секвенсора
  * @param  NewConfig указатель на новую конфигурацию ADC, иначе используется та, что в структуре
  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
  */
OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_SEQ_ExtInit_TypeDef *NewConfig)
{
  if(!hadc || !hadc->Instance)
    return ERROR;
  
  ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
  
  if(NewConfig != NULL)
  {
    hseq->Config = NewConfig;
  }
  if(hseq->Config == NULL)
  {
    return ERROR;
  }
  
  ADC_SEQ_Init(SEQ_Num, &hseq->Config->SEQ_Init);
  
  return OK;
}

/**
  * @brief  Установка коллбека секвенсора АЦП
  * @param  hadc указатель на хендл АЦП
  * @param  cb_type Тип коллбека
  * @param  Callback Функция коллбека
  * @retval void
  */
OperationStatus adc_seq_set_callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_CallbackTypeDef cb_type, void (*Callback)(void))
{
  if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
    return ERROR;
  
  ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
  
  switch(cb_type)
  {
    case ADC_Callback_Seq:
      hseq->Config->SEQCpltCallback = Callback;
      break;
    case ADC_Callback_Error:
      hseq->Config->ErrorCallback = Callback;
      break;
    case ADC_Callback_BuffFull:
      hseq->Config->BuffFullCallback = Callback;
      break;
    case ADC_Callback_BuffHalf:
      hseq->Config->BuffFullCallback = Callback;
      break;
    default:
      return ERROR;
  }
  return OK;
}


/**
  * @brief  Запуск секвенсора АЦП с буфером
  * @param  hadc указатель на хендл АЦП
  * @param  SEQ_Num номер секвенсора
  * @param  data_buffer указатель на буфер данных [ch][buffer_size] или NULL
  * @param  buffer_size размер буфера для каждого канала (0 если буфер не используется)
  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
  */
OperationStatus adc_seq_start(ADC_HandleTypeDef *hadc, 
                             ADC_SEQ_Num_TypeDef SEQ_Num,
                             uint16_t (*data_buffer)[],
                             uint32_t buffer_size)
{
  if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
    return ERROR;
  
  ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
  ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
  
  // Сохраняем буферные параметры
  hseq->data_buffer = (uint16_t *)data_buffer;
  hseq->buffer_size = buffer_size;
  hseq->buffer_count = 0; // Сбрасываем счетчик
  
  // Настраиваем прерывания если нужно
  if (conf->IT == ENABLE)
  {
    ADC_SEQ_ITConfig(SEQ_Num, conf->ITCount, DISABLE);
    ADC_SEQ_ITCmd(SEQ_Num, ENABLE);
  }
  
  // Включаем секвенсор
  ADC_SEQ_Cmd(SEQ_Num, ENABLE);
  
  // Помечаем как запущенный
  hseq->running = 1;
  
  return OK;
}


/**
  * @brief  Остановка секвенсора АЦП
  * @param  hadc указатель на хендл АЦП
  * @param  SEQ_Num номер секвенсора
  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
  */
OperationStatus adc_seq_stop(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
  if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
    return ERROR;
  
  ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
  
  // Выключаем секвенсор
  ADC_SEQ_Cmd(SEQ_Num, DISABLE);
  
  // Выключаем прерывания
  ADC_SEQ_ITCmd(SEQ_Num, DISABLE);
  
  // Сбрасываем флаг запуска
  hseq->running = 1;
  
  return OK;
}

/**
  * @brief  Получение текущего значения канала
  * @param  hadc указатель на хендл АЦП
  * @param  channel номер канала
  * @retval значение ADC (0 если данные невалидны)
  */
uint16_t adc_get_channel_value(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel)
{
  if (!hadc || channel >= ADC_CH_Total)
    return 0;
  
  if (hadc->ChannelValid[channel])
  {
    return hadc->ChannelData[channel];
  }
  
  return 0;
}

/**
  * @brief  Программный запуск преобразования
  */
void adc_sw_start(void)
{
    ADC_SEQ_SwStartCmd();
}



/**
  * @brief  Обработчик прерываний секвенсора
  * @param  hadc указатель на хендл АЦП
  * @param  SEQ_Num номер секвенсора
  * @retval void
  */
void adc_seq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
  if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
    return;
  
  ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
  ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
  
  // Проверяем флаг прерывания
  if (ADC_SEQ_ITMaskedStatus(SEQ_Num) == SET)
  {
    // Очищаем флаг
    ADC_SEQ_ITStatusClear(SEQ_Num);
    
    // Последний запрос в секвенсоре
    ADC_SEQ_ReqNum_TypeDef last_request = conf->SEQ_Init.ReqMax;
    
    // Читаем все доступные данные из FIFO
    uint8_t channels_processed = 0;
    while (ADC_SEQ_GetFIFOLoad(ADC_SEQ_Num_0))
    {
      uint32_t data = ADC_SEQ_GetFIFOData(SEQ_Num);
      ADC_SEQ_ReqNum_TypeDef req_num = ADC_SEQ_GetReqCurrent(SEQ_Num);
      
      if (req_num < ADC_SEQ_Req_Total)
      {
        ADC_CH_Num_TypeDef channel = conf->SEQ_Init.Req[req_num];
          
        if (channel < ADC_CH_Total)
        {
          // 1. Обновляем текущее значение
          hadc->ChannelData[channel] = (uint16_t)data;
          hadc->ChannelValid[channel] = 1;
          
          // 2. Записываем в буфер если он есть
          if (hseq->data_buffer && 
              hseq->buffer_size > 0)
          {
            // Вычисляем offset для [ch][buffer_size]
            uint32_t offset = channel * hseq->buffer_size + 
                              hseq->buffer_count;
            hseq->data_buffer[offset] = (uint16_t)data;
          }
          
          channels_processed++;
          
          // Если это последний канал в секвенсоре - увеличиваем счетчик буфера
          if (req_num == last_request)
          {
            hseq->buffer_count++;
          
            if(hseq->buffer_count == hseq->buffer_size/2)
            {
              if (conf->BuffHalfCallback)
              {
                conf->BuffHalfCallback();
              }
            }
            if(hseq->buffer_count >= hseq->buffer_size)
            {
              // Кольцевой буфер
              if(conf->buffer_circular)
                hseq->buffer_count = 0;
              else
                adc_seq_stop(hadc, SEQ_Num);
                
              if (conf->BuffFullCallback)
              {
                conf->BuffFullCallback();
              }
            }
          }
        }
      }
    }
    
    // Вызываем коллбек пользователя
    if (conf->SEQCpltCallback)
    {
      conf->SEQCpltCallback();
    }
  }
  
  // Обработка ошибок
  if (ADC_SEQ_DMAErrorStatus(SEQ_Num) == SET)
  {
    ADC_SEQ_DMAErrorStatusClear(SEQ_Num);
    
    if (conf->ErrorCallback)
    {
      conf->ErrorCallback();
    }
  }
}




////////////////////////////////////////////////////////////////////////////////
////////////////////////////DIGITAL COMPARATORS/////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

///**
//  * @brief  Инициализация цифрового компаратора АЦП
//  * @param  hadc указатель на хендл АЦП
//  * @param  DC_Num номер компаратора
//  * @param  NewConfig указатель на конфигурацию
//  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
//  */
//OperationStatus adc_dc_init(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num, ADC_DC_ExtInit_TypeDef *NewConfig)
//{
//    if(!hadc || !hadc->Instance)
//        return ERROR;
//    
//    if(NewConfig != NULL)
//    {
//        hadc->DC_Config[DC_Num] = NewConfig;
//    }
//    
//    if(hadc->DC_Config[DC_Num] == NULL)
//    {
//        return ERROR;
//    }
//    
//    // Инициализация компаратора
//    ADC_DC_Init(DC_Num, &hadc->DC_Config[DC_Num]->DC_Init);
//    
//    return OK;
//}


///**
//  * @brief  Установка коллбека цифрового компаратора АЦП
//  * @param  hadc указатель на хендл АЦП
//  * @param  DC_Num номер компаратора
//  * @param  cb_type тип коллбека
//  * @param  Callback функция коллбека
//  * @retval OperationStatus
//  */
//OperationStatus adc_dc_set_callback(ADC_HandleTypeDef* hadc, ADC_DC_Num_TypeDef DC_Num, ADC_CallbackTypeDef cb_type, void (*Callback)(void))
//{
//    if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
//        return ERROR;
//    
//    switch(cb_type)
//    {
//        case ADC_Callback_DC:
//            hadc->DC_Config[DC_Num]->DC_TrigCallback = Callback;
//            break;
//        case ADC_Callback_Error:
//            hadc->DC_Config[DC_Num]->ErrorCallback = Callback;
//            break;
//        default:
//            return ERROR;
//    }
//    return OK;
//}


///**
//  * @brief  Запуск цифрового компаратора АЦП
//  * @param  hadc указатель на хендл АЦП
//  * @param  DC_Num номер компаратора
//  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
//  */
//OperationStatus adc_dc_start(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
//    if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
//        return ERROR;
//    
//    ADC_DC_ExtInit_TypeDef *conf = hadc->DC_Config[DC_Num];
//    
//    // Включаем выход компаратора
//    ADC_DC_OutputCmd(DC_Num, ENABLE);
//    
//    // Настраиваем прерывания если нужно
//    if(conf->IT == ENABLE)
//    {
//        ADC_DC_ITCmd(DC_Num, ENABLE);
//    }
//    
//    return OK;
//}

///**
//  * @brief  Остановка цифрового компаратора АЦП
//  * @param  hadc указатель на хендл АЦП
//  * @param  DC_Num номер компаратора
//  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
//  */
//OperationStatus adc_dc_stop(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
//    if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
//        return ERROR;
//    
//    // Выключаем выход компаратора
//    ADC_DC_OutputCmd(DC_Num, DISABLE);
//    
//    // Выключаем прерывания
//    ADC_DC_ITCmd(DC_Num, DISABLE);
//    
//    return OK;
//}

//void adc_dc_handler(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
//}