#include "system.h"
#include "oneway.h"
#include "adcdma.h"

static AdcDma * pInstance = nullptr;

extern "C" void DMA1_Channel1_IRQHandler( void ) __attribute__((interrupt));
void DMA1_Channel1_IRQHandler( void ) {
  DMA1_Type::INTFR_DEF state (DMA1.INTFR);
  DMA1.INTFCR.R = state.R;  // clear all
  if (!pInstance) return;
  if      (state.B.HTIF1 != RESET) pInstance->send (false);
  else if (state.B.TCIF1 != RESET) pInstance->send (true);
}

static inline void EnableClock (void) noexcept {
  // Enable DMA
  RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
    r.B.SRAMEN = SET;
    r.B.DMA1EN = SET;
    return r.R;
  });
  // Enable ADC + GPIOA
  RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
    r.B.ADC1EN = SET;
    r.B.IOPAEN = SET;
    return r.R;
  });
  RCC.APB1PCENR.B.TIM3EN = SET; // Enable TIM3
  RCC.CFGR0.B.ADCPRE     = 3u;  // 11: PCLK2 divided by 8 // max 14 MHz (18) ?
  //  PIN PA2 / A2
  GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
    r.B.MODE2 = 0u;
    r.B.CNF2  = 0u;
    return r.R;
  });
}
static inline void TimerInit (uint32_t us) noexcept {
  TIM3.PSC.R    = 143u;          // 1 MHz Fs
  TIM3.ATRLR.R  = us - 1u;
  // TRGO update for ADC
  TIM3.CTLR2.B.MMS = 2u;
}
static inline void AdcCalibrate (void) noexcept {
  // RESET
  RCC.APB2PRSTR.B.ADC1RST = SET;
  RCC.APB2PRSTR.B.ADC1RST = RESET;
  // set channels
  
  ADC1.RSQR3__CHANNEL.B.SQ1__CHSEL    = 2u;    // CH2
  ADC1.SAMPTR2_CHARGE2.B.SMP2_TKCG2   = 7u;
  /*
  ADC1.RSQR3__CHANNEL.B.SQ1__CHSEL    = 16u;   // teplota
  ADC1.SAMPTR1_CHARGE1.B.SMP16_TKCG16 = 7u;
  */
  ADC1.RSQR1.B.L       = 0u;    // 1 regular conversion
  ADC1.CTLR1.B.SCAN    = SET;
  
  ADC1.CTLR2.B.ADON    = SET;
//ADC1.CTLR2.B.TSVREFE = SET;
  ADC1.CTLR2.B.RSTCAL  = SET;            // Launch the calibration by setting RSTCAL
  while (ADC1.CTLR2.B.RSTCAL != RESET);  // Wait until RSTCAL=0
  ADC1.CTLR2.B.CAL    = SET;             // Launch the calibration by setting CAL
  while (ADC1.CTLR2.B.CAL    != RESET);  // Wait until CAL=0  
}
typedef __SIZE_TYPE__ size_t;
static inline void Dma1Ch1Init (void * ptr) noexcept {
  // Configure the peripheral data register address
  DMA1.PADDR1.R = reinterpret_cast<size_t> (& ADC1.RDATAR_DR_ACT_DCG);
  // Configure the memory address
  DMA1.MADDR1.R = reinterpret_cast<size_t> (ptr);
  // Configure the number of DMA tranfer to be performs on DMA channel 1
  DMA1.CNTR1 .R = AFULL_LEN;
  // Configure increment, size, interrupts and circular mode
  DMA1.CFGR1.modify([] (DMA1_Type::CFGR1_DEF & r) -> auto {
    r.B.PL      = 3u;       // highest priority
    r.B.MEM2MEM = RESET;    // periferal -> memory
    r.B.MINC    = SET;      // memory increment
    r.B.MSIZE   = 1u;       // 16-bit
    r.B.PSIZE   = 1u;       // 16-bit
    r.B.HTIE    = SET;      // INT Enable HALF
    r.B.TCIE    = SET;      // INT Enable FULL
    r.B.CIRC    = SET;      // Circular MODE
    // Enable DMA Channel 1
    r.B.EN    = SET;
    return r.R;
  });  
}
static inline void AdcPostInit (void) noexcept {
  ADC1.CTLR2.modify([](ADC1_Type::CTLR2_DEF & r) -> auto {
    r.B.DMA     = SET;
    r.B.EXTTRIG = SET;
    r.B.EXTSEL  = 4u;       // TRGO event of timer 3
    r.B.SWSTART = SET;
    return r.R;
  });
}
////////////////////////////////////////////////////////////////////////////////////
AdcDma::AdcDma() noexcept : pL (buffer), pH (buffer + AHALF_LEN), dst (nullptr) {
  pInstance = this;
  EnableClock ();
  TimerInit   (1000u);
  NVIC.EnableIRQ (DMA1_Channel1_IRQn);
  AdcCalibrate();
  Dma1Ch1Init (buffer);
  AdcPostInit ();
  // start timer
  TIM3.CTLR1.B.CEN = SET;
}
inline void AdcDma::send(const bool b) {
  if (!dst) return;
  if (b) dst->Send (pH, AHALF_LEN);
  else   dst->Send (pL, AHALF_LEN);
}