
/*
 * timer2.c
 *
 *  Created on: 2 Apr 2018
 *      Author: Mike
 */

#include "ch.h"  // needs for all ChibiOS programs
#include "hal.h" // hardware abstraction layer header

#include "timer2.h"
#define  MICROSECS_PULSE 4

// with a dwell angle of 45 degrees , 4 cylinders and a maximum RPM of 5000
// freq = 5000/60 * 2 = 166Hz. Because the breaker might bounce , we accept the
// first pulse longer than 1/300 of a second as being a proper closure .
// the TIM2 counter counts in 1uS increments,
#define BREAKER_COUNT_MIN (1E6/(MICROSECS_PULSE * 300))

#define COUNT_FROM_RPM(RPM) ((1E6/(MICROSECS_PULSE * 30 / (RPM ) )))

int16_t nominal = 0;
uint16_t halfRot;
int16_t phase10 = 100; // 10 degrees
volatile uint16_t sampleVar;
volatile uint16_t sampleRef;

volatile uint16_t lastSampleRef = 0;

volatile uint8_t refCount = 0;
volatile uint8_t varCount = 0;

volatile uint16_t samplePeriod = 0;

static signed phaseSamp = 0;
static uint8_t validPhaseSamp = 0;

static uint8_t locked = 0; // in lock if true
int gainControl = 1000;

uint16_t rpm;
signed count;
signed delta;

void
recalcPhase ()
{
  nominal = halfRot * (long) (phase10) / 3600;
}

void
adjustRPM (void)
{
  if (rpm < 500)
    rpm = 500;
  if (rpm > 10000)
    rpm = 10000;

}

uint16_t
setRPM (uint16_t rpm_)
{
  if (rpm_ >= 500 && rpm_ < 10000)
    {
      rpm = rpm_;
      adjustRPM ();
    }
  return halfRot;
}

uint16_t
getRPM (void)
{
  return rpm;
}

signed
getDelta (void)
{
  return delta;
}

signed
getCount (void)
{
  return count;
}

void
setGain (int gain)
{
  gainControl = gain;
}

void
processPhase (void)
{
  // lpcl
  const signed pdClip = 1000;
  static signed pd;
  if (validPhaseSamp)
    {
      chSysLock ();

      pd = phaseSamp - nominal;

      validPhaseSamp = 0;
      chSysUnlock ();

      if (pd > pdClip)
	pd = pdClip;
      if (pd < -pdClip)
	pd = -pdClip;

    }
  else
    return;

  delta = pd;

  static int sampleAverage = 0;

  static int phaseAverage = 0;
  const int freqScale = 1024;  // scale up calculations

  const int periodScale = 8; // 1/periodScale  of difference between period measured and average is added to period.
  const int phaseScale = 500;
  const int phaseLim = 1; // total contribution of phase accumulator
  // measure sample period devi
  sampleAverage = sampleAverage
      + (samplePeriod * freqScale - sampleAverage) / periodScale;

  int32_t arr;

  // 	if(lock)

  int intSample = sampleAverage;

  // dont phase lock until r and v in lock
  if (locked == 1)
    {
      phaseAverage += pd;
    }
  else
    {
      phaseAverage = 0;
    }

  if(phaseAverage > phaseScale * phaseLim)
    phaseAverage = phaseScale * phaseLim;
  if (phaseAverage < -phaseScale * phaseLim)
    phaseAverage = -phaseScale *phaseLim;

  arr = sampleAverage / freqScale + phaseAverage / phaseScale;

// clamp values

  if (arr > 65535)
    arr = 65535;
  if (arr < 1000)
    arr = 1000;

  count = arr;

  TIM2->ARR = arr - 1;

  nominal = count * (long) (phase10) / 3600;

  float nomRPM = 30E6 / (MICROSECS_PULSE * arr);

  rpm = nomRPM;

  adjustRPM ();
}

// set the timing advance from reference in 0.1 degrees units
void
setAdvance (int16_t deg10)
{
  phase10 = deg10;

}

// specialist timer setup :
// timer 2 is a reloading counter with a cycle period controlled by its ARR register.
// Just before terminal count it produces a pulse of 200 microseconds using its CCR1 count compare register,
// used to drive the strobe LED.
// Timer 3 is then used to count the time of the reload of Timer 2 via its Trigger Out being selected as reload.
// The time is latched in TIM3  CCR2
// and ignition pulses are latched on TIM3 CCR1, to allow it to be used in a PLL.
//
void
initTimers ()
{
  rccEnableTIM2 (FALSE);
  rccResetTIM2 ();

  TIM2->PSC = 72 * MICROSECS_PULSE;
  TIM2->ARR = 60000;
  TIM2->CR1 = ~TIM_CR1_CKD & (TIM_CR1_CEN | TIM_CR1_ARPE);

  /// pulse width 200 uS
  TIM2->CCR1 = 200 / MICROSECS_PULSE;

  TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; //enabled and active high

  TIM2->CCMR1 = TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2
      | TIM_CCMR1_OC1PE;

  TIM2->CR2 = TIM_CR2_MMS_1; // trigger out is 010 = update

  rccEnableTIM3 (FALSE);
  rccResetTIM3 ();
  // TIM3 on the PA6 ... pins : remap code 00
  AFIO->MAPR &= ~AFIO_MAPR_TIM3_REMAP;

  TIM3->PSC = 72 * MICROSECS_PULSE;
  TIM3->ARR = 0xFFFF;

  TIM3->CCMR1 =
      TIM_CCMR1_CC1S_0 /* | TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1 | TIM_CCMR1_IC1F_2 */; // filter 16, input

  // link TIM3 ITR2 to TIM2 reload
  // use TS = 001 to make TRC from Tim2 TRIGGER
  TIM3->SMCR &= ~(TIM_SMCR_TS_Msk);
  TIM3->SMCR |= TIM_SMCR_TS_0; // select ITR2 as trigger source TRC

  TIM3->CCMR1 |= TIM_CCMR1_CC2S_1 | TIM_CCMR1_CC2S_0; //  The CC2S bits are 11, use TRC

  TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;

  TIM3->CR1 = ~TIM_CR1_CKD & (TIM_CR1_CEN | TIM_CR1_ARPE);

  nvicEnableVector (TIM3_IRQn, 4);

  TIM3->DIER |= TIM_DIER_CC1IE | TIM_DIER_CC2IE;
}

// timer 3 interrupt
void
VectorB4 (void)
{

  if (TIM3->SR & TIM_SR_CC1IF)
    {
      uint16_t sample = TIM3->CCR1;
//		if(sample-lastSampleRef >  1000 /*BREAKER_COUNT_MIN */)
	{

	  samplePeriod = sample - sampleRef;

	  sampleRef = sample;

	  ++refCount;
	}
      lastSampleRef = sample;

    }
  if (TIM3->SR & TIM_SR_CC2IF)
    {
      sampleVar = TIM3->CCR2;
      ++varCount;
    }

  if (refCount == 1 && varCount == 1)
    {
      if (sampleRef == sampleVar)
	phaseSamp = 0;
      else
	{

	  uint16_t refToVar = sampleRef - sampleVar;
	  uint16_t varToRef = sampleVar - sampleRef;

	  if (refToVar < varToRef)
	    phaseSamp = refToVar;
	  else if (varToRef <= refToVar)
	    phaseSamp = -varToRef;

	}

      validPhaseSamp = 1;
      refCount = 0;
      varCount = 0;
      locked = 1;
    }


  if (refCount > 1 || varCount > 1)
    {
      refCount = 0;
      varCount = 0;
      locked = 0;
    }

}

