/** 
	**************************************************************************
	* @file general_gpio.c
	* @brief Модуль для инициализации портов.
	**************************************************************************
		@verbatim
		//-------------------Функции-------------------//
		Functions: users
			- GPIO_Clock_Enable							Инициализация тактирования порта
		@endverbatim
***************************************************************************/
#include "general_gpio.h"

//-------------------------------------------------------------------
//------------------------GPIO INIT FUNCTIONS------------------------

HAL_StatusTypeDef GPIO_Clock_Enable(GPIO_TypeDef *GPIOx)
{	
	HAL_StatusTypeDef status = HAL_OK;
	// choose port for enable clock
	if (GPIOx==GPIOA)
		__HAL_RCC_GPIOA_CLK_ENABLE();
	else if (GPIOx==GPIOB)
		__HAL_RCC_GPIOB_CLK_ENABLE();
	else if (GPIOx==GPIOC)
		__HAL_RCC_GPIOC_CLK_ENABLE();
	else if (GPIOx==GPIOD)
		__HAL_RCC_GPIOD_CLK_ENABLE();
	else if (GPIOx==GPIOE)
		__HAL_RCC_GPIOE_CLK_ENABLE();
	else
		status = HAL_ERROR;
	
	return status;
}

//------------------------GPIO INIT FUNCTIONS------------------------
//-------------------------------------------------------------------


//-------------------------------------------------------------------
//------------------------GPIO LED FUNCTIONS-------------------------

/**
  * @brief  Инициализировать светодиод (структуру светодиода)
  * @param led          - указатель на структуру светодиода
  * @param GPIOx        - указатель на структуру порта для светодиода
  * @param GPIO_PIN_X   - пин для светодиода
  * @param LED_On_State - состояния пина, при котором светодиод будет включен
  */
HAL_StatusTypeDef GPIO_LED_Init(GPIO_LEDTypeDef *led, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, uint8_t LED_ActiveLevel)
{
  if(check_null_ptr_2(led, GPIOx))
    return HAL_ERROR;
  
  led->LED_Port       = GPIOx;
  led->LED_Pin        = GPIO_PIN_X;
  led->LED_ActiveLvl  = LED_ActiveLevel;
  
  GPIO_LED_Off(led);
  return HAL_OK;
}

/**
  * @brief  Включить светодиод 
  * @param led - указатель на структуру светодиода
  */
HAL_StatusTypeDef GPIO_LED_On(GPIO_LEDTypeDef *led)
{
  if(check_null_ptr_1(led))
    return HAL_ERROR;
  
  led->state = LED_IS_ON;
  if(led->LED_Port != NULL)
    HAL_GPIO_WritePin(led->LED_Port, led->LED_Pin, led->LED_ActiveLvl);
  else
    return HAL_ERROR;
  
  return HAL_OK;
}
/**
  * @brief  Выключить светодиод 
  * @param led - указатель на структуру светодиода
  */
HAL_StatusTypeDef GPIO_LED_Off(GPIO_LEDTypeDef *led)
{
  if(check_null_ptr_1(led))
    return HAL_ERROR;
  
  led->state = LED_IS_OFF;
  if(led->LED_Port != NULL)
    HAL_GPIO_WritePin(led->LED_Port, led->LED_Pin, !led->LED_ActiveLvl);
  else
    return HAL_ERROR;
  
  
  return HAL_OK;
}

/**
  * @brief Активировать моргание светодиодом
  * @param led - указатель на структуру светодиода
  * @details  Функция ставит режим моргания, который после управляется в @ref GPIO_LED_Dynamic_Handle
	*/
HAL_StatusTypeDef GPIO_LED_Blink_Start(GPIO_LEDTypeDef *led, uint32_t period)
{
  if(check_null_ptr_2(led, led->LED_Port))
    return HAL_ERROR;  
  
  led->state = LED_IS_BLINKING;
  led->LED_Period = period;
    
  return HAL_OK;
}
  
/**
  * @brief Активировать моргание светодиодом
  * @param led - указатель на структуру светодиода
  * @details  Функция ставит режим моргания, который после управляется в @ref GPIO_LED_Dynamic_Handle
	*/
HAL_StatusTypeDef GPIO_LED_Fading_Start(GPIO_LEDTypeDef *led, uint32_t period)
{
  if(check_null_ptr_2(led, led->LED_Port))
    return HAL_ERROR;  
  
  led->state = LED_IS_FADING;
  led->LED_Period = period;
  
    
  return HAL_OK;
}

//uint8_t LED_PWM_FADING_DUTYS[LED_PWM_TICKS] = {0 1 2 3 4 5 6 7 8 9 10 11 12 }
/**
  * @brief Управление динамическими режимами свечения светодиода
  * @param led - указатель на структуру светодиода
  * @details  Функция моргает/плавно моргает светодиодом в неблокирующем режиме
  * Т.е. функцию надо вызывать постоянно, чтобы она мониторила тики 
  * и в нужный момент переключала светодиод
	*/
void GPIO_LED_Dynamic_Handle(GPIO_LEDTypeDef *led)
{
  if(check_null_ptr_2(led, led->LED_Port))
    return;
  
  /* Режим моргания светодиода */
  if(led->state == LED_IS_BLINKING)
  {
    uint32_t tickcurrent = HAL_GetTick();
    /* Ожидание истечения периода моргания */
    if((tickcurrent - led->tickprev) > led->LED_Period)
    {
      /* Моргание */
      HAL_GPIO_TogglePin(led->LED_Port, led->LED_Pin);
      
      led->tickprev = tickcurrent;
    }
  }
  /* Режим плавного моргания светодиода */
  else if(led->state == LED_IS_FADING)
  {
    static unsigned direction = 0;
    static int duty = 0;
    uint32_t tickcurrent = HAL_GetTick();
    /* Ожидание момента изменения яркости */
    /* Период ШИМ 20 мс, поэтому менять яроксть надо 40 раз за период (туда обратно) */
    if((tickcurrent - led->tickprev) > led->LED_Period/(LED_PWM_TICKS*2))
    {
      /* Формирование разтухания */
      if(direction == 0)
      {
        if(++duty >= LED_PWM_TICKS)
        {
          direction = 1;
          duty = LED_PWM_TICKS;
        }
      }
      /* Формирование затухания */
      else
      {
        if(--duty  <= 0)
        {
          direction = 0;
          duty = 0;
        }
      }
      led->tickprev = tickcurrent;
    }
    /* Формирование ШИМ для изменения яркости */
    int duty_crt = (duty*duty/LED_PWM_TICKS);
    if(tickcurrent%LED_PWM_TICKS < duty_crt)
    {
      HAL_GPIO_WritePin(led->LED_Port, led->LED_Pin, led->LED_ActiveLvl);
    }
    else
    {
      HAL_GPIO_WritePin(led->LED_Port, led->LED_Pin, !led->LED_ActiveLvl);
    }
  }
}
//------------------------GPIO LED FUNCTIONS-------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
//------------------------GPIO SW FUNCTIONS-------------------------

/**
* @brief  Инициализировать кнопку (структуру кнопки)
  * @param sw             - указатель на структуру кнопки
  * @param GPIOx          - указатель на структуру порта для кнопки
  * @param GPIO_PIN_X     - пин для кнопки
  * @param SW_ActiveLevel - состояния пина, когда кнопка нажата
  */
HAL_StatusTypeDef GPIO_Switch_Init(GPIO_SwitchTypeDef *sw, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, uint8_t SW_ActiveLevel)
{
  if(check_null_ptr_2(sw, GPIOx))
    return HAL_ERROR;
  
  sw->Sw_Port       = GPIOx;
  sw->Sw_Pin        = GPIO_PIN_X;
  sw->Sw_ActiveLvl  = SW_ActiveLevel;
	
	if(sw->Sw_FilterDelay == 0)
		sw->Sw_FilterDelay = 50;
	
  return HAL_OK;
}

/**
  * @brief Считать состоянии кнопки 
  * @param  sw - указатель на структуру кнопки
  * @return 1 - если кнопка нажата, 0 - если отжата
  * @details  Функция включает в себя неблокирующую проверку на дребезг
  * Т.е. функцию надо вызывать постоянно, чтобы она мониторила состояние кнопки
  */
uint8_t GPIO_Read_Swich(GPIO_SwitchTypeDef *sw)
{
  if(check_null_ptr_1(sw))
    return 0;
  
	sw->Sw_GrandPrevState = sw->Sw_PrevState;
  if(HAL_GPIO_ReadPin(sw->Sw_Port, sw->Sw_Pin) == sw->Sw_ActiveLvl)
  {
    sw->Sw_PrevState = 1;
    
    if(sw->tickprev == 0)
      sw->tickprev = HAL_GetTick();
    
    if((HAL_GetTick() - sw->tickprev) >= sw->Sw_FilterDelay)
    {
      if(HAL_GPIO_ReadPin(sw->Sw_Port, sw->Sw_Pin) == sw->Sw_ActiveLvl)
      {
        return 1;
      }
      else
      {
        sw->tickprev = 0;
        return 0;
      }
    }
  }
  else
  {
    sw->Sw_PrevState = 0;
  }
  return 0;
}
//------------------------GPIO SW FUNCTIONS-------------------------
//-------------------------------------------------------------------