/**
  ******************************************************************************
  * @file    VAPC-meter
  * @author  Vladimir N. Shilov <shilow@ukr.net>
  * @version V0.0.1
  * @date    2015.05.15
  * @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 "stm8l15x.h"
#include "rtos.h"
#include "max7219.h"
#include "adc.h"

/** @addtogroup STM8L15x_StdPeriph_Template
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
typedef enum _modes_led {
  dispNoState = 0x00,
  displayU,
  displayI,
  displayP,
  displayAH,
  displayWH,
  displayT
} mode_led_t;

typedef enum _events {
  eventNoEvent  = 0x00,
  eventShortPress,
  eventLongPress
} event_t;

/* Private define ------------------------------------------------------------*/
#define LED_RED_PORT    GPIOB
#define LED_RED_PIN     GPIO_Pin_7
#define LED_GREEN_PORT  GPIOC
#define LED_GREEN_PIN   GPIO_Pin_4

#define BTNs_PORT       GPIOC
#define BTN1_PIN        GPIO_Pin_0
#define BTN2_PIN        GPIO_Pin_1
#define BTN_SCAN_PERIOD 10
// кнопка считается нажатой через BTN_FACTOR * BTN_SCAN_PERIOD ms
#define BTN_FACTOR      5

// размер "медленного" буфера
#define VALUES_BUFFER_SIZE  10

/* Private macro -------------------------------------------------------------*/
#define LED_RED_ON      LED_RED_PORT->ODR &= (uint8_t)(~LED_RED_PIN)
#define LED_RED_OFF     LED_RED_PORT->ODR |= LED_RED_PIN
#define LED_GREEN_ON    LED_GREEN_PORT->ODR &= (uint8_t)(~LED_GREEN_PIN)
#define LED_GREEN_OFF   LED_GREEN_PORT->ODR |= LED_GREEN_PIN

/* Private constant ----------------------------------------------------------*/
// перевод числа 0-7 в номер индикатора
static const max7219_reg_t dig[8] = {
	RegDigit0, RegDigit1, RegDigit2, RegDigit3,
	RegDigit4, RegDigit5, RegDigit6, RegDigit7
};

// перевод значения 0x00 - 0x0F в код индикатора
static const max7219_sym_t num[16] = {
	Sym_0, Sym_1, Sym_2, Sym_3, Sym_4, Sym_5, Sym_6, Sym_7,
	Sym_8, Sym_9, Sym_A, Sym_b, Sym_C, Sym_d, Sym_E, Sym_F
};

/* Private variables ---------------------------------------------------------*/
static struct _slowBuf {
  uint16_t Voltage[VALUES_BUFFER_SIZE];
  uint16_t Current[VALUES_BUFFER_SIZE];
  uint8_t idx;
} slowBuf;

/* averaged values for 1 sek */
static uint16_t Voltage;
static uint16_t Current;
static uint32_t Power;
static uint32_t CapacityAH = 0;
static uint32_t CapacityWH = 0;
static struct _timer {
  uint8_t hh;
  uint8_t mm;
  uint8_t ss;
} Timer;

static uint8_t btn1Cnt;
static uint8_t btn2Cnt;

static mode_led_t displayTopLine, displayBotLine;

/* Private function prototypes -----------------------------------------------*/
static void GPIO_Config(void);
static void CLK_Config(void);

static void showValue(uint32_t val, uint8_t pos);
static void showU(uint8_t pos);
static void showI(uint8_t pos);
static void showP(uint8_t pos);
static void showAH(uint8_t pos);
static void showWH(uint8_t pos);
static void showT(uint8_t pos);

static void showLabelU(uint8_t pos);
static void showLabelI(uint8_t pos);
static void showLabelP(uint8_t pos);
static void showLabelAH(uint8_t pos);
static void showLabelWH(uint8_t pos);
static void showLabelT(uint8_t pos);

static void displayMode(mode_led_t * disp, event_t event, uint8_t pos);

static void blankLine(uint8_t pos);

/* RTOS function prototypes -----------------------------------------------*/
static void getADCValues(void);
static void calculateValues(void);
static void showTopLineU(void);
static void showTopLineI(void);
static void showTopLineP(void);
static void showTopLineAH(void);
static void showTopLineWH(void);
static void showTopLineT(void);
static void showBotLineU(void);
static void showBotLineI(void);
static void showBotLineP(void);
static void showBotLineAH(void);
static void showBotLineWH(void);
static void showBotLineT(void);

static void blankTopLine(void);
static void blankBotLine(void);

static void btnScan(void);
static void btnTest(uint8_t btnPin, uint8_t *pressCount, void (*taskShortPress)(void), void (*taskLongPress)(void));
static void btn1Short(void);
static void btn1Long(void);
static void btn2Short(void);
static void btn2Long(void);

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

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
void main(void)
{
  /* Clock configuration -----------------------------------------*/
  CLK_Config();

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

  /* RTOS Configuration */
  RTOS_Config();

   /* ADC Configuration and start */
  ADC_Config();

  /* MAX7219 Configuration */
  MAX7219_Config();

  /* ROTS tasks */
  RTOS_SetTask(btnScan, 0, BTN_SCAN_PERIOD);
  RTOS_SetTask(getADCValues, 101, 100);
  RTOS_SetTask(calculateValues, 102, 1000);
  RTOS_SetTask(showTopLineU, 103, 200);
  RTOS_SetTask(showBotLineI, 104, 200);
  displayTopLine = displayU;
  displayBotLine = displayI;

  /* Infinite loop */
  while (1) {
      RTOS_DispatchTask();
      wfi();
  }
} // End of main()

/**
  * Забираем обработанные данные из быстрого буфера
  * и складываем в медленный.
  */
static void getADCValues(void) {
  uint16_t * avgVal;

  avgVal = ADC_GetValues();

  slowBuf.idx ++;
  if (slowBuf.idx >= VALUES_BUFFER_SIZE) {
    slowBuf.idx = 0;
  }

  slowBuf.Voltage[slowBuf.idx] = *avgVal;
  slowBuf.Current[slowBuf.idx] = *(avgVal + 1);

}

/**
  * Average values from slow buffer.
  * Calculate Power, Capacitance AH and WH.
  */
static void calculateValues(void) {
  uint8_t i;
  uint32_t v=0, c=0; // for max values 10 * 12 000 mA, and 10 * 36 000 mV

  for (i=0; i<VALUES_BUFFER_SIZE; i++) {
    v += slowBuf.Voltage[i];
    c += slowBuf.Current[i];
  }

  Voltage = (v + (VALUES_BUFFER_SIZE/2)) / VALUES_BUFFER_SIZE;
  Current = (c + (VALUES_BUFFER_SIZE/2)) / VALUES_BUFFER_SIZE;

  if (Current > 0) {

    // millivolt * milliamper = microwatt, convert it to milliwatt
    Power = ((uint32_t)(v * c) + 500) / 1000;

    CapacityAH += Current;
    CapacityWH += Power;

    Timer.ss ++;
    if (Timer.ss > 59) {
      Timer.ss = 0;
      Timer.mm ++;
      if (Timer.mm > 59) {
        Timer.mm = 0;
        Timer.hh ++;
      }
    }
  }

}

/**
  * вывод инфы на верхнем индикаторе
  */
static void showTopLineU(void) {
  showU(0);
}

static void showTopLineI(void) {
  showI(0);
}

static void showTopLineP(void) {
  showP(0);
}

static void showTopLineAH(void) {
  showAH(0);
}

static void showTopLineWH(void) {
  showWH(0);
}

static void showTopLineT(void) {
  showT(0);
}

static void blankTopLine(void) {
  static uint8_t cnt = 0;
  if (cnt < 4) {
    blankLine(0);
    cnt ++;
  } else {
    RTOS_DeleteTask(blankTopLine);
    cnt = 0;
  }
}

/**
  * вывод инфы на нижнем индикаторе
  */
static void showBotLineU(void) {
  showU(4);
}

static void showBotLineI(void) {
  showI(4);
}

static void showBotLineP(void) {
  showP(4);
}

static void showBotLineAH(void) {
  showAH(4);
}

static void showBotLineWH(void) {
  showWH(4);
}

static void showBotLineT(void) {
  showT(4);
}

static void blankBotLine(void) {
  static uint8_t cnt = 0;
  if (cnt < 4) {
    blankLine(4);
    cnt ++;
  } else {
    RTOS_DeleteTask(blankBotLine);
    cnt = 0;
  }
}

/**
  * Output given value to given indicator
  * param1: value to show, from '0.000' to '999.9'
  * param2: starting position -- 0 for top
  * any other for bottom.
  */
static void showValue(uint32_t val, uint8_t pos) {
  uint8_t tmp;

  if (pos > 0) {
    pos = 4;
  }

  if (val >= 100000) { // 000.0
    tmp = (uint8_t)(val / 100000);
    val %= 100000;
    MAX7219_WriteData(dig[pos], num[tmp]);

    tmp = (uint8_t)(val / 10000);
    val %= 10000;
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

    tmp = (uint8_t)(val / 1000);
    val %= 1000;
    pos ++;
    MAX7219_WriteData(dig[pos], (num[tmp] | Sym_Dot));

    tmp = (uint8_t)(val / 100);
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

  } else if (val >= 10000) { // 00.00
    tmp = (uint8_t)(val / 10000);
    val %= 10000;
    MAX7219_WriteData(dig[pos], num[tmp]);

    tmp = (uint8_t)(val / 1000);
    val %= 1000;
    pos ++;
    MAX7219_WriteData(dig[pos], (num[tmp] | Sym_Dot));

    tmp = (uint8_t)(val / 100);
    val %= 100;
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

    tmp = (uint8_t)(val / 10);
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

  } else { // 0.000
    tmp = (uint8_t)(val / 1000);
    val %= 1000;
    MAX7219_WriteData(dig[pos], (num[tmp] | Sym_Dot));

    tmp = (uint8_t)(val / 100);
    val %= 100;
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

    tmp = (uint8_t)(val / 10);
    val %= 10;
    pos ++;
    MAX7219_WriteData(dig[pos], num[tmp]);

    pos ++;
    tmp = val;
    MAX7219_WriteData(dig[pos], num[tmp]);
  }

}

/**
  * fill line by blank symbol
  */
void blankLine(uint8_t pos) {
  static uint8_t i;
  for (i=0; i<4; i++) {
    MAX7219_WriteData(dig[pos+i], Sym_BLANK);
  }
}

/**
  * Output voltage values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showU(uint8_t pos) {
  //showValue(slowBuf.Voltage[slowBuf.idx], pos);
  showValue(Voltage, pos);
}

/**
  * Output current values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showI(uint8_t pos) {
  //showValue(slowBuf.Current[slowBuf.idx], pos);
  showValue(Current, pos);
}

/**
  * Output power values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showP(uint8_t pos) {
  showValue(Power, pos);
}

/**
  * Output current capacity values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showAH(uint8_t pos) {
  showValue(((CapacityAH + 1800) / 3600), pos);
}

/**
  * Output power capacity values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showWH(uint8_t pos) {
  showValue(((CapacityWH + 1800) / 3600), pos);
}

/**
  * Output time values to given indicator
  * param: starting position -- 0 for top
  * any other for bottom.
  */
static void showT(uint8_t pos) {
  static uint8_t halfsek = 0;
  uint8_t tmp;

  if (Timer.hh > 0) {
    tmp = Timer.hh / 10;
    MAX7219_WriteData(dig[pos], num[tmp]);
    pos ++;
    tmp = Timer.hh % 10;
    if (halfsek == 0) {
      MAX7219_WriteData(dig[pos], num[tmp]);
    } else {
      MAX7219_WriteData(dig[pos], (num[tmp] | Sym_Dot));
    }
    pos ++;
    tmp = Timer.mm / 10;
    MAX7219_WriteData(dig[pos], num[tmp]);
    pos ++;
    tmp = Timer.mm % 10;
    MAX7219_WriteData(dig[pos], num[tmp]);
  } else {
    tmp = Timer.mm / 10;
    MAX7219_WriteData(dig[pos], num[Timer.mm >> 4]);
    pos ++;
    tmp = Timer.mm % 10;
    MAX7219_WriteData(dig[pos], (num[tmp] | Sym_Dot));
    pos ++;
    tmp = Timer.ss / 10;
    MAX7219_WriteData(dig[pos], num[tmp]);
    pos ++;
    tmp = Timer.ss % 10;
    MAX7219_WriteData(dig[pos], num[tmp]);
  }

  if (halfsek == 0) {
    halfsek = 1;
  } else {
    halfsek = 0;
  }
}

/**
  * @brief  Configure GPIO for button available on the VAPC board
  * @param  None
  * @retval None
  */
static void GPIO_Config(void)
{
  /* Configure GPIO used to drive LEDs */
  GPIO_Init(LED_RED_PORT, LED_RED_PIN, GPIO_Mode_Out_PP_High_Fast);
  GPIO_Init(LED_GREEN_PORT, LED_GREEN_PIN, GPIO_Mode_Out_PP_High_Fast);

  /* Configure GPIO used for buttons */
  GPIO_Init(BTNs_PORT, BTN1_PIN, GPIO_Mode_In_FL_No_IT);
  GPIO_Init(BTNs_PORT, BTN2_PIN, GPIO_Mode_In_FL_No_IT);

  /* Enable general interrupts */
  enableInterrupts();
}

/**
  * @brief  Configure system clock to run at Maximum clock speed
  * @param  None
  * @retval None
  */
static void CLK_Config(void)
{
  CLK_DeInit();

  /* Configure the clock source */
  CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_HSI);

  /* Configure the System clock frequency to run at 16Mhz */
  CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);

  CLK_HSICmd(ENABLE);
}

/** опрос кнопок */
static void btnScan(void) {
  btnTest(BTN1_PIN, &btn1Cnt, btn1Short, btn1Long);
  btnTest(BTN2_PIN, &btn2Cnt, btn2Short, btn2Long);
}

/** проверка конкретной кнопки */
static void btnTest(uint8_t btnPin, uint8_t *pressCount, void (*taskShortPress)(void), void (*taskLongPress)(void)) {
  if ((BTNs_PORT->IDR & btnPin) == 0) {
    if (*pressCount < 255) {
      *pressCount = *pressCount + 1;
    }
/*
    if (*pressCount ==  BTN_FACTOR) {
         // short
      } else if(*pressCount == (BTN_FACTOR * 10)) {
         // long
      }
*/
  } else {
    if (*pressCount >= (BTN_FACTOR * 10)) {
      RTOS_SetTask(taskLongPress, 1, 0); // вызов функции при длительном нажатии
    } else if (*pressCount >= BTN_FACTOR) {
      RTOS_SetTask(taskShortPress, 1, 0); // вызов функции при коротком нажатии
    }
    *pressCount = 0;
  }
}

/** переключение режимов отображения */
static void displayMode(mode_led_t * disp, event_t event, uint8_t pos) {
  if (pos > 0) {
    pos = 4;
  }

  if (event == eventShortPress) {
    switch (*disp) {
    case dispNoState:
      showLabelU(pos);
      if (pos == 0) {
        RTOS_SetTask(showTopLineU, 1000, 250);
      } else {
        RTOS_SetTask(showBotLineU, 1000, 250);
      }
      *disp = displayU;
      break;
    case displayU:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineU);
        RTOS_SetTask(showTopLineI, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineU);
        RTOS_SetTask(showBotLineI, 1000, 250);
      }
      showLabelI(pos);
      *disp = displayI;
      break;
    case displayI:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineI);
        RTOS_SetTask(showTopLineP, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineI);
        RTOS_SetTask(showBotLineP, 1000, 250);
      }
      showLabelP(pos);
      *disp = displayP;
      break;
    case displayP:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineP);
        RTOS_SetTask(showTopLineAH, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineP);
        RTOS_SetTask(showBotLineAH, 1000, 250);
      }
      showLabelAH(pos);
      *disp = displayAH;
      break;
    case displayAH:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineAH);
        RTOS_SetTask(showTopLineWH, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineAH);
        RTOS_SetTask(showBotLineWH, 1000, 250);
      }
      showLabelWH(pos);
      *disp = displayWH;
      break;
    case displayWH:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineWH);
        RTOS_SetTask(showTopLineT, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineWH);
        RTOS_SetTask(showBotLineT, 1000, 250);
      }
      showLabelT(pos);
      *disp = displayT;
      break;
    case displayT:
      if (pos == 0) {
        RTOS_DeleteTask(showTopLineT);
        RTOS_SetTask(showTopLineU, 1000, 250);
      } else {
        RTOS_DeleteTask(showBotLineT);
        RTOS_SetTask(showBotLineU, 1000, 250);
      }
      showLabelU(pos);
      *disp = displayU;
      break;
    }
  } else if (event == eventLongPress) {
    /* КА долгих нажатий */
    switch (*disp) {
    case displayAH:
      if (pos == 0) {
        RTOS_SetTask(blankTopLine, 0, 500);
        RTOS_SetTask(showTopLineAH, 250, 500);
      } else {
        RTOS_SetTask(blankBotLine, 0, 500);
        RTOS_SetTask(showBotLineAH, 250, 500);
      }
      showLabelAH(pos);
      break;
    case displayWH:
      if (pos == 0) {
        RTOS_SetTask(blankTopLine, 0, 500);
        RTOS_SetTask(showTopLineWH, 250, 500);
      } else {
        RTOS_SetTask(blankBotLine, 0, 500);
        RTOS_SetTask(showBotLineWH, 250, 500);
      }
      showLabelWH(pos);
      break;
    case displayT:
      if (pos == 0) {
        RTOS_SetTask(blankTopLine, 0, 500);
        RTOS_SetTask(showTopLineT, 250, 500);
      } else {
        RTOS_SetTask(blankBotLine, 0, 500);
        RTOS_SetTask(showBotLineT, 250, 500);
      }
      showLabelT(pos);
      break;
    }
  }
}

/** обработчик короткого нажатия первой кнопки */
static void btn1Short(void) {
  displayMode(&displayTopLine, eventShortPress, 0);
}

/** обработчик длинного нажатия первой кнопки */
static void btn1Long(void) {
  displayMode(&displayTopLine, eventLongPress, 0);
}

/** обработчик короткого нажатия второй кнопки */
static void btn2Short(void) {
  displayMode(&displayBotLine, eventShortPress, 4);
}

/** обработчик длинного нажатия второй кнопки */
static void btn2Long(void) {
  Timer.ss = 0;
  Timer.mm = 0;
  Timer.hh = 0;
  CapacityAH = 0;
  CapacityWH = 0;
  displayMode(&displayBotLine, eventLongPress, 4);
}

/**
  * show label for selected mode
  */
static void showLabelU(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_U);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_BLANK);
}

static void showLabelI(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_1);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_BLANK);
}

static void showLabelP(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_P);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_BLANK);
}

static void showLabelAH(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_A);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_H);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
}

static void showLabelWH(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_P);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_H);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
}

static void showLabelT(uint8_t pos) {
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_t);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_Minus);
  pos ++;
  MAX7219_WriteData(dig[pos], Sym_BLANK);
}


#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(uint8_t* file, uint32_t 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****/
