/**
  ******************************************************************************
  * @file    China AC-meter
  * @author  Vladimir N. Shilov <shilow@ukr.net>
  * @version V0.0.1
  * @date    2016.11.09
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * ----------------------------------------------------------------------------
  * "THE BEER-WARE LICENSE" (Revision 42):
  * <shilow@ukr.net> wrote this file.  As long as you retain this notice you
  * can do whatever you want with this stuff. If we meet some day, and you think
  * this stuff is worth it, you can buy me a beer in return.   Shilov V.N.
  * ----------------------------------------------------------------------------
  *
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "stm8s.h"
//#include "rtos.h"

/* Private defines -----------------------------------------------------------*/
// for 1 msek ticks
#define TIM4_PERIOD  (uint8_t)124

#define ADC_PORT  GPIOD
#define ADC_PINS  GPIO_PIN_3
#define ADC_CHNL  ADC1_CHANNEL_4
#define ADC_SCHT  ADC1_SCHMITTTRIG_CHANNEL4
#define ADC_SMPLS 64

#define BTN_PORT  (GPIOA)
#define BTN_PINS  (GPIO_PIN_2)

/*
 AAA
F   B
F   B
 GGG
E   C
E   C
 DDD P
*/
#define LED_ANOD1_PORT  GPIOA
#define LED_ANOD1_PIN   GPIO_PIN_3
#define LED_ANOD2_PORT  GPIOD
#define LED_ANOD2_PIN   GPIO_PIN_6
#define LED_ANOD3_PORT  GPIOD
#define LED_ANOD3_PIN   GPIO_PIN_4

#define LED_SEG1_PORT   GPIOA
#define LED_SEG1_PINS   GPIO_PIN_1
#define LED_SEG_F       GPIO_PIN_1

#define LED_SEG2_PORT   GPIOB
#define LED_SEG2_PINS   (GPIO_PIN_5 | GPIO_PIN_4)
#define LED_SEG_A       GPIO_PIN_5
#define LED_SEG_B       GPIO_PIN_4

#define LED_SEG3_PORT   GPIOC
#define LED_SEG3_PINS   (GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_3 | GPIO_PIN_5)
#define LED_SEG_G       GPIO_PIN_3
#define LED_SEG_C       GPIO_PIN_4
#define LED_SEG_P       GPIO_PIN_5
#define LED_SEG_D       GPIO_PIN_6
#define LED_SEG_E       GPIO_PIN_7

#define LED_OUT_OFF   {LED_SEG1_PORT->ODR |= LED_SEG1_PINS; LED_SEG2_PORT->ODR |= LED_SEG2_PINS; LED_SEG3_PORT->ODR |= LED_SEG3_PINS;}
#define LED_OUT_DP    GPIOC->ODR &= (~LED_SEG_P);
#define LED_OUT_MM    GPIOC->ODR &= LED_SEG_G;
#define LED_OUT_0     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0x29;
#define LED_OUT_1     GPIOB->ODR &= 0xEF; GPIOC->ODR &= 0xE9;
#define LED_OUT_2     GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0x31;
#define LED_OUT_3     GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0xA1;
#define LED_OUT_4     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xEF; GPIOC->ODR &= 0xE1;
#define LED_OUT_5     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xDF; GPIOC->ODR &= 0xA1;
#define LED_OUT_6     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xDF; GPIOC->ODR &= 0x21;
#define LED_OUT_7     GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0xE9;
#define LED_OUT_8     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0x21;
#define LED_OUT_9     GPIOA->ODR &= 0xFD; GPIOB->ODR &= 0xCF; GPIOC->ODR &= 0xA1;

#define LED_ANODE_OFF {LED_ANOD1_PORT->ODR &= ~LED_ANOD1_PIN; GPIOD->ODR &= ~(LED_ANOD2_PIN | LED_ANOD3_PIN);}
#define LED_ANODE_ON1 LED_ANOD1_PORT->ODR |= LED_ANOD1_PIN;
#define LED_ANODE_ON2 LED_ANOD2_PORT->ODR |= LED_ANOD2_PIN;
#define LED_ANODE_ON3 LED_ANOD3_PORT->ODR |= LED_ANOD3_PIN;

#define SUB_SECOND_CNT  5
#define LED_ONE_PERIOD  5

#define ADC_ZERO_EEADDR   FLASH_DATA_START_PHYSICAL_ADDRESS

/* Private function prototypes -----------------------------------------------*/
static void ADC_CorrectZero(void);
static void ADC_Config(void);
static void GPIO_Config(void);
static void TIM1_Config(void);
static void TIM4_Config(void);
static void OutputToLed(void);
static void PrepareForLed(void);
//static void CountCapacity(void);

/* Private functions ---------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
__IO uint16_t ConversionBuffer[ADC_SMPLS];
__IO uint8_t BufferIndex = 0;
__IO uint8_t LedCnt = 0;
static uint16_t Current = 0;
static uint32_t Capacity = 0;
static uint8_t LedDigits[3] = {0, 0, 0};
static uint8_t LedPoint = 3;
static uint16_t SubSecondBfr = 0;
static uint8_t SubSecondCnt = SUB_SECOND_CNT;
static uint16_t ADC_ZeroFix = 0;

void main(void)
{
  /* Initialization of the clock to 16MHz */
  CLK->CKDIVR = 0x00;

  /* Disable clock of unused perephireal */
  /* UART, SPI, I2C */
  CLK->PCKENR1 = 0xF0;
  /* CAN */
  CLK->PCKENR2 = 0x0F;

  /* SWIM clock */
  CLK->SWIMCCR = 0x00;

  /* GPIO configuration ------------------------------------------*/
  GPIO_Config();

  /* ADC configuration -------------------------------------------*/
  ADC_Config();

  /* TIM1 configuration -----------------------------------------*/
  TIM1_Config();

  /* Initialize dispatcher */
  //RTOS_Init();
  //RTOS_SetTask(OutputToLed, 0, 5);
  //RTOS_SetTask(PrepareForLed, 210, 200);
  //RTOS_SetTask(CountCapacity, 1005, 1000);

  /* TIM4 configuration -----------------------------------------*/
  TIM4_Config();

  /* enable interrupts */
  enableInterrupts();

  /** If btn pin == 0 -- auto fix adc zero level
      else -- read zero value from eeprom
      Value stored in MSB:LSB
  */
  if((BTN_PORT->IDR & BTN_PINS) == 0){
    ADC_CorrectZero();
  } else {
    ADC_ZeroFix = (uint16_t)(FLASH_ReadByte(ADC_ZERO_EEADDR) << 8);
    ADC_ZeroFix |= FLASH_ReadByte((ADC_ZERO_EEADDR+1));
  }

  /* Infinite loop */
  while (1)
  {
    if (LedCnt >= LED_ONE_PERIOD) {
      LedCnt = 0;
      OutputToLed();
    }

    if (BufferIndex >= ADC_SMPLS) {
        BufferIndex = 0;

        /* Oversampling */
        uint32_t tbuf = 0;
        uint8_t i = 0;
        for(i=0; i<ADC_SMPLS; i++){
          tbuf += ConversionBuffer[i];
        }
        /* пример:
         ток 5А, на шунте 0.25В, на входе АЦП 2В,
         с опорным 5В получаем код 409
         после 64-х измерений будет 26176
         после деления на 8 -- 3272
         умножили на опорное напряжение -- 16360000
         поделили на разрядность АЦП -- 1997 мВ
         поделили на множитель ОУ -- 250 мВ
         и поделили на сопротивление шунта -- 5000 мА
        */
        if(tbuf > ADC_ZeroFix){
          tbuf -= ADC_ZeroFix;

          tbuf >>= 3; // pre div
          tbuf *= 3335; // Vref = Vcc
          tbuf = (tbuf + 4096) / 8191; // get ADC input voltage in mV
          tbuf *= 1000; // -- for OU divider //get voltage in uV
          tbuf = (tbuf + 3147) / 6294; // get voltage from shunt
          /* в Current ток в милиамперах */
          tbuf *= 1000; // get voltage in uV
          Current = (tbuf + 25) / 50; // shunt resistance in mOhms

          SubSecondBfr += Current;

        } else {
          tbuf = 0;
          Current = 0;
        }

        SubSecondCnt --;

        if(SubSecondCnt == 0){
          Capacity += ( (SubSecondBfr + (SUB_SECOND_CNT/2)) / SUB_SECOND_CNT );
          SubSecondBfr = 0;
          SubSecondCnt = SUB_SECOND_CNT;
        }

        PrepareForLed();

    } // End of if (BufferIndex >= ADC_SMPLS)

    //RTOS_DispatchTask();
    wfi();
  } // End of infinity

} // End of main()


/** Корректируем напряжение смещения нуля ОУ на входе АЦП */
static void ADC_CorrectZero(void) {
  /* ждём окончания цикла измерений */
  do {
  } while (BufferIndex < ADC_SMPLS);

  BufferIndex = 0;

  /* Получаем 16-ти битное значение от АЦП */
  uint8_t i = 0;
  for(i=0; i<ADC_SMPLS; i++){
    ADC_ZeroFix += ConversionBuffer[i];
  }

  /* Сохраняем полученное значение в EEPROM */

  /*Define FLASH programming time*/
  FLASH_SetProgrammingTime(FLASH_PROGRAMTIME_STANDARD);

  /* Unlock Data memory */
  FLASH_Unlock(FLASH_MEMTYPE_DATA);

  /* MSB */
  FLASH_ProgramByte(ADC_ZERO_EEADDR, (uint8_t)(ADC_ZeroFix>>8));
  /* LSB */
  FLASH_ProgramByte((ADC_ZERO_EEADDR+1), (uint8_t)(ADC_ZeroFix));

  /* Lock Data memory */
  FLASH_Lock(FLASH_MEMTYPE_DATA);

}

/** Prepare current value for output ot led */
static void PrepareForLed(void) {
  uint16_t value;

  if((BTN_PORT->IDR & BTN_PINS) == 0){
    value = Capacity / 3600;
  } else {
    value = Current;
  }

  if(value > 9999){
    /* XX.X Amper */
    LedPoint = 1;
    LedDigits[0] = value / 10000;
    value = value % 10000;
    LedDigits[1] = value / 1000;
    LedDigits[2] = (value % 1000) / 100;
  } else if(value > 999){
    /* X.XX Amper */
    LedPoint = 0;
    LedDigits[0] = value / 1000;
    value = value % 1000;
    LedDigits[1] = value / 100;
    LedDigits[2] = (value % 100) / 10;
  } else {
    /* XXX mili Amper */
    LedPoint = 3;
    LedDigits[0] = value / 100;
    value = value % 100;
    LedDigits[1] = value / 10;
    LedDigits[2] = value % 10;
  }
}

/** Output current value to next led */
static void OutputToLed(void) {
  static uint8_t ledn = 0;

  /* на пине PD2 должен быть меадр с периодом 10 мсек. */
  GPIOD->ODR ^= GPIO_PIN_2;

  /* all off */
  //LED_ANODE_OFF
  LED_ANOD1_PORT->ODR &= ~LED_ANOD1_PIN;
  LED_ANOD2_PORT->ODR &= ~(LED_ANOD2_PIN | LED_ANOD3_PIN);

  //LED_OUT_OFF
  LED_SEG1_PORT->ODR |= LED_SEG1_PINS;
  LED_SEG2_PORT->ODR |= LED_SEG2_PINS;
  LED_SEG3_PORT->ODR |= LED_SEG3_PINS;

  /* out next value */
  switch(LedDigits[ledn]){
    case 0:
      LED_OUT_0
      break;
    case 1:
      LED_OUT_1
      break;
    case 2:
      LED_OUT_2
      break;
    case 3:
      LED_OUT_3
      break;
    case 4:
      LED_OUT_4
      break;
    case 5:
      LED_OUT_5
      break;
    case 6:
      LED_OUT_6
      break;
    case 7:
      LED_OUT_7
      break;
    case 8:
      LED_OUT_8
      break;
    case 9:
      LED_OUT_9
      break;
    default:
      LED_OUT_MM
  }
  if(ledn == LedPoint) {
    LED_OUT_DP
  }

  /* Fire on nex led */
  switch(ledn){
  case 0:
    LED_ANODE_ON1
    ledn = 1;
    break;
  case 1:
    LED_ANODE_ON2
    ledn = 2;
    break;
  case 2:
    LED_ANODE_ON3
    ledn = 0;
    break;
  default:
    ledn = 0;
  }
}

/**
  * @brief  Configure GPIO for LEDs and buttons available on the evaluation board
  * @param  None
  * @retval None
  */
static void GPIO_Config(void)
{
  /* Initialize LEDs */
  GPIO_Init(LED_ANOD1_PORT, (GPIO_Pin_TypeDef)LED_ANOD1_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(LED_ANOD2_PORT, (GPIO_Pin_TypeDef)(LED_ANOD2_PIN | LED_ANOD3_PIN), GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(LED_SEG1_PORT, (GPIO_Pin_TypeDef)LED_SEG1_PINS, GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(LED_SEG2_PORT, (GPIO_Pin_TypeDef)LED_SEG2_PINS, GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(LED_SEG3_PORT, (GPIO_Pin_TypeDef)LED_SEG3_PINS, GPIO_MODE_OUT_PP_LOW_FAST);

  /* Initialize Button pin */
  GPIO_Init(BTN_PORT, (GPIO_Pin_TypeDef)BTN_PINS, GPIO_MODE_IN_PU_NO_IT);

  /*  Init GPIO for ADC */
  GPIO_Init(ADC_PORT, (GPIO_Pin_TypeDef)ADC_PINS, GPIO_MODE_IN_FL_NO_IT);

  /* Test output */
  GPIO_Init(GPIOD, (GPIO_Pin_TypeDef)GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);
}


/**
  * @brief  Configure ADC.
  * @param  None
  * @retval None
  */
static void ADC_Config()
{
  /* De-Init ADC peripheral*/
  ADC1_DeInit();

  /* Init ADC1 peripheral */
  ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC_CHNL, ADC1_PRESSEL_FCPU_D8, \
            ADC1_EXTTRIG_TIM, ENABLE, ADC1_ALIGN_RIGHT, ADC_SCHT, DISABLE);

  /* Enable EOC interrupt */
  ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);

  /*Start Conversion */
  //ADC1_StartConversion();
}

/**
  * @brief  Configure TIM1.
  * @param  None
  * @retval None
  */
static void TIM1_Config(void)
{
  TIM1_DeInit();

  /* Time Base configuration */
  /*
  Timer period - 3,125 ms
  TIM1_Period = 50
  TIM1_Prescaler = 1000
  TIM1_CounterMode = TIM1_COUNTERMODE_UP
  TIM1_RepetitionCounter = 0
  */
  TIM1_TimeBaseInit(999, TIM1_COUNTERMODE_UP, 49, 0);

  /* Trigrer configuration */
  TIM1_SelectOutputTrigger(TIM1_TRGOSOURCE_UPDATE);

  /* Update Interrupt Enable */
  //TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);

  /* Enable TIM1 */
  TIM1_Cmd(ENABLE);
}

/**
  * @brief  Configure TIM4.
  * @param  None
  * @retval None
  */
static void TIM4_Config(void)
{
  TIM4_DeInit();

  /*
  TIM4 configuration:
   - TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
     clock used is 16 MHz / 128 = 125 000 Hz
   - With 125 000 Hz we can generate time base:
     max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
     min time base is 0.016 ms if TIM4_PERIOD = 1   --> (  1 + 1) / 125000 = 0.016 ms
   - In this example we need to generate a time base equal to 1 ms
     so TIM4_PERIOD = (0.001 * 125000 - 1) = 124
   */

  /* Time base configuration */
  TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD);
  /* Clear TIM4 update flag */
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  /* Enable update interrupt */
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);

  /* Enable TIM4 */
  TIM4_Cmd(ENABLE);
}

#ifdef USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval : None
  */
void assert_failed(u8* file, u32 line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif


/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
