/**
  *********************************************************************************
  *
  * @file    hal_tim.c
  * @brief   TIM module driver.
  *	     This is the common part of the TIM initialization
  *
  * @version V1.0
  * @date    06 Nov 2017
  * @author  AE Team
  * @note
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  *********************************************************************************
  */

#include <string.h>
#include "hal_tim.h"
#include "hal_cmu.h"


/** @addtogroup ES32FXXX_HAL
  * @{
  */

/** @defgroup TIM TIM
  * @brief TIM module driver
  * @{
  */
#ifdef HAL_TIM

/** @defgroup TIM_Private_Functions TIM Private Functions
  * @{
  */
static void tim_base_set_config(TIM_TypeDef *TIMx, tim_base_init_t *init);
static void tim_oc1_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config);
static void tim_oc2_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config);
static void tim_oc3_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config);
static void tim_oc4_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config);
static void tim_ccx_channel_cmd(TIM_TypeDef* TIMx, tim_channel_t ch, type_func_t state);
static void tim_ti1_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter);
static void tim_ti1_set_config_stage(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity, uint32_t ic_filter);
static void tim_ti2_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter);
static void tim_ti2_set_config_stage(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity, uint32_t ic_filter);
static void tim_ti3_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter);
static void tim_ti4_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter);
static void tim_etr_set_config(TIM_TypeDef* TIMx, tim_etr_psc_t psc, tim_clock_polarity_t polarity, uint32_t filter);
static void tim_slave_set_config(tim_handle_t *hperh, tim_slave_config_t *config);
#ifdef HAL_DMA
static void tim_dma_oc_cplt(void *arg);
static void tim_dma_capture_cplt(void *arg);
static void tim_dma_period_elapse_cplt(void *arg);
static void tim_dma_error(void *arg);
#endif
/**
  * @}
  */

/** @defgroup  TIM_Public_Functions TIM Public Functions
  * @{
  */

/** @defgroup TIM_Public_Functions_Group1 TIM Base functions
  * @brief    Time Base functions
  *
  * @verbatim
  ==============================================================================
              ##### Time Base functions #####
  ==============================================================================
  [..]
    This section provides functions allowing to:
    (+) Initialize and configure the TIM base.
    (+) Reset the TIM base.
    (+) Start the Time Base.
    (+) Stop the Time Base.
    (+) Start the Time Base and enable interrupt.
    (+) Stop the Time Base and disable interrupt.
    (+) Start the Time Base and enable DMA transfer.
    (+) Stop the Time Base and disable DMA transfer.

    @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM Time base Unit according to the specified
  *         parameters in the tim_handle_t and create the associated handle.
  * @param  hperh: TIM base handle
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_base_init(tim_handle_t *hperh)
{
	if(hperh == NULL)
		return ERROR;

	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_COUNTER_MODE(hperh->init.mode));
	assert_param(IS_TIM_CLOCK_DIVISION(hperh->init.clk_div));

	if (hperh->state == TIM_STATE_RESET)
		hperh->lock = UNLOCK;

	hperh->state = TIM_STATE_BUSY;
	tim_base_set_config(hperh->perh, &hperh->init);
	hperh->state = TIM_STATE_READY;

	return OK;
}

/**
  * @brief  Reset the TIM base peripheral
  * @param  hperh: TIM base handle
  * @retval Status, see @ref hal_status_t.
  */
void tim_base_reset(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	hperh->state = TIM_STATE_BUSY;
	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_RESET;
	__UNLOCK(hperh);

	return;
}

/**
  * @brief  Starts the TIM Base generation.
  * @param  hperh: TIM handle
  * @retval None
  */
void tim_base_start(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	hperh->state = TIM_STATE_BUSY;
	TIM_ENABLE(hperh);
	hperh->state = TIM_STATE_READY;

	return;
}

/**
  * @brief  Stops the TIM Base generation.
  * @param  hperh: TIM handle
  * @retval None
  */
void tim_base_stop(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	hperh->state= TIM_STATE_BUSY;
	TIM_DISABLE(hperh);
	hperh->state= TIM_STATE_READY;

	return;
}

/**
  * @brief  Starts the TIM Base generation in interrupt mode.
  * @param  hperh: TIM handle
  * @retval None
  */
void tim_base_start_by_it(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	tim_interrupt_config(hperh, TIM_IT_UPDATE, ENABLE);
	TIM_ENABLE(hperh);

	return;
}

/**
  * @brief  Stops the TIM Base generation in interrupt mode.
  * @param  hperh: TIM handle
  * @retval None
  */
void tim_base_stop_by_it(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	tim_interrupt_config(hperh, TIM_IT_UPDATE, DISABLE);
	TIM_DISABLE(hperh);

	return;
}

#ifdef HAL_DMA
/**
  * @brief  Starts the TIM Base generation in DMA mode.
  * @param  hperh: TIM handle
  * @param  hdma: Pointer to dma_handle_t.
  * @param  buf: The source Buffer address.
  * @param  len: The length of buffer to be transferred from memory to TIM peripheral
  * @param  dma_ch: Channel of DMA.
  * @retval Status, see @ref hal_status_t.
*/
hal_status_t tim_base_start_by_dma(tim_handle_t *hperh, dma_handle_t *hdma,
                                  uint16_t *buf, uint32_t len, uint8_t dma_ch)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	if ((hperh->state == TIM_STATE_BUSY))
		 return BUSY;
	if ((hperh->state == TIM_STATE_READY)) {
		if (((uint32_t)buf == 0 ) || (len == 0))
			return ERROR;
	}

	hperh->state   = TIM_STATE_BUSY;

	if (hdma->perh == NULL)
		hdma->perh = DMA0;

	hdma->cplt_cbk = tim_dma_period_elapse_cplt;
	hdma->cplt_arg = (void *)hperh;
	hdma->err_cbk  = tim_dma_error;
	hdma->err_arg  = (void *)hperh;

	dma_config_struct(&hdma->config);
	hdma->config.src        = (void *)buf;
	hdma->config.dst        = (void *)&hperh->perh->ARR.Word;
	hdma->config.size       = len;
	hdma->config.data_width = DMA_DATA_SIZE_HALFWORD;
	hdma->config.src_inc    = DMA_DATA_INC_HALFWORD;
	hdma->config.dst_inc    = DMA_DATA_INC_NONE;
	hdma->config.msigsel    = DMA_MSIGSEL_TIM_UPDATE;
	hdma->config.channel    = dma_ch;

	if (hperh->perh == TIM0)
		hdma->config.msel = DMA_MSEL_TIM0;
	else if (hperh->perh == TIM1)
		hdma->config.msel = DMA_MSEL_TIM1;
	else if (hperh->perh == TIM2)
		hdma->config.msel = DMA_MSEL_TIM2;
	else if (hperh->perh == TIM3)
		hdma->config.msel = DMA_MSEL_TIM3;
	else if (hperh->perh == TIM4)
		hdma->config.msel = DMA_MSEL_TIM4;
	else if (hperh->perh == TIM5)
		hdma->config.msel = DMA_MSEL_TIM5;
	else if (hperh->perh == TIM6)
		hdma->config.msel = DMA_MSEL_TIM6;
	else if (hperh->perh == TIM7)
		hdma->config.msel = DMA_MSEL_TIM7;
	else
		;

	dma_config_basic(hdma);
	tim_dma_req_config(hperh, TIM_DMA_UPDATE, ENABLE);
	TIM_ENABLE(hperh);

	return OK;
}

/**
  * @brief  Stops the TIM Base generation in DMA mode.
  * @param  hperh: TIM handle
  * @retval None
*/
void tim_base_stop_by_dma(tim_handle_t *hperh)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	tim_dma_req_config(hperh, TIM_DMA_UPDATE, DISABLE);;
	TIM_DISABLE(hperh);;
	hperh->state= TIM_STATE_READY;

	return;
}
#endif
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group2 TIM Output Compare functions
  *  @brief   Time Output Compare functions
  *
  * @verbatim
  ==============================================================================
                  ##### Time Output Compare functions #####
  ==============================================================================
  [..]
    This section provides functions allowing to:
    (+) Initialize and configure the TIM Output Compare.
    (+) Start the Time Output Compare.
    (+) Stop the Time Output Compare.
    (+) Start the Time Output Compare and enable interrupt.
    (+) Stop the Time Output Compare and disable interrupt.
    (+) Start the Time Output Compare and enable DMA transfer.
    (+) Stop the Time Output Compare and disable DMA transfer.

    @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM Output Compare according to the specified
  *         parameters in the tim_handle_t and create the associated handle.
  * @param  hperh: TIM handle
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_oc_init(tim_handle_t *hperh)
{
	return tim_base_init(hperh);
}

/**
  * @brief  Starts the TIM Output Compare signal generation.
  * @param  hperh: TIM handle
  * @param  ch : TIM Channel to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_oc_start(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_ENABLE(hperh);

	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Output Compare signal generation.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channel to be disabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_oc_stop(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_DISABLE(hperh);

	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}

/**
  * @brief  Starts the TIM Output Compare signal generation in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channel to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_oc_start_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_interrupt_config(hperh, TIM_IT_CC1, ENABLE);
		break;

	case TIM_CHANNEL_2:
		tim_interrupt_config(hperh, TIM_IT_CC2, ENABLE);
		break;

	case TIM_CHANNEL_3:
		tim_interrupt_config(hperh, TIM_IT_CC3, ENABLE);
		break;

	case TIM_CHANNEL_4:
		tim_interrupt_config(hperh, TIM_IT_CC4, ENABLE);
		break;

	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_ENABLE(hperh);

	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Output Compare signal generation in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channel to be disabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_oc_stop_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_interrupt_config(hperh, TIM_IT_CC1, DISABLE);
		break;

	case TIM_CHANNEL_2:
		tim_interrupt_config(hperh, TIM_IT_CC2, DISABLE);
		break;

	case TIM_CHANNEL_3:
		tim_interrupt_config(hperh, TIM_IT_CC3, DISABLE);
		break;

	case TIM_CHANNEL_4:
		tim_interrupt_config(hperh, TIM_IT_CC4, DISABLE);
		break;

	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_DISABLE(hperh);

	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}

#ifdef HAL_DMA
/**
  * @brief  Starts the TIM Output Compare signal generation in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @param  hdma: Pointer to dma_handle_t.
  * @param  buf: The source Buffer address.
  * @param  len: The length of buffer to be transferred from memory to TIM peripheral
  * @param  dma_ch: Channel of DMA.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_oc_start_by_dma(tim_handle_t *hperh, tim_channel_t ch,
                      dma_handle_t *hdma, uint16_t *buf, uint32_t len, uint8_t dma_ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	if ((hperh->state == TIM_STATE_BUSY))
		 return BUSY;
	if ((hperh->state == TIM_STATE_READY)) {
		if (((uint32_t)buf == 0 ) || (len == 0))
			return ERROR;
	}

	hperh->state   = TIM_STATE_BUSY;

	if (hdma->perh == NULL)
		hdma->perh = DMA0;

	hdma->cplt_cbk = tim_dma_oc_cplt;
	hdma->cplt_arg = (void *)hperh;
	hdma->err_cbk  = tim_dma_error;
	hdma->err_arg  = (void *)hperh;

	dma_config_struct(&hdma->config);
	hdma->config.src        = (void *)buf;
	hdma->config.size       = len;
	hdma->config.data_width = DMA_DATA_SIZE_HALFWORD;
	hdma->config.src_inc    = DMA_DATA_INC_HALFWORD;
	hdma->config.dst_inc    = DMA_DATA_INC_NONE;
	hdma->config.channel    = dma_ch;

	if (hperh->perh == TIM0)
		hdma->config.msel = DMA_MSEL_TIM0;
	else if (hperh->perh == TIM1)
		hdma->config.msel = DMA_MSEL_TIM1;
	else if (hperh->perh == TIM2)
		hdma->config.msel = DMA_MSEL_TIM2;
	else if (hperh->perh == TIM3)
		hdma->config.msel = DMA_MSEL_TIM3;
	else if (hperh->perh == TIM4)
		hdma->config.msel = DMA_MSEL_TIM4;
	else if (hperh->perh == TIM5)
		hdma->config.msel = DMA_MSEL_TIM5;
	else if (hperh->perh == TIM6)
		hdma->config.msel = DMA_MSEL_TIM6;
	else if (hperh->perh == TIM7)
		hdma->config.msel = DMA_MSEL_TIM7;
	else
		;//do nothing

	switch (ch) {
	case TIM_CHANNEL_1:
		hdma->config.dst     = (void *)&hperh->perh->CCR1.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH1;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC1, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_1;
		break;

	case TIM_CHANNEL_2:
		hdma->config.dst     = (void *)&hperh->perh->CCR2.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH2;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC2, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_2;
		break;

	case TIM_CHANNEL_3:
		hdma->config.dst     = (void *)&hperh->perh->CCR3.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH3;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC3, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_3;
		break;

	case TIM_CHANNEL_4:
		hdma->config.dst     = (void *)&hperh->perh->CCR4.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH4;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC4, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_4;
		break;

	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_ENABLE(hperh);

	TIM_ENABLE(hperh);
	return OK;
}

/**
  * @brief  Stops the TIM Output Compare signal generation in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
*/
void tim_oc_stop_by_dma(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_dma_req_config(hperh, TIM_DMA_CC1, DISABLE);
		break;

	case TIM_CHANNEL_2:
		tim_dma_req_config(hperh, TIM_DMA_CC2, DISABLE);
		break;

	case TIM_CHANNEL_3:
		tim_dma_req_config(hperh, TIM_DMA_CC3, DISABLE);
		break;

	case TIM_CHANNEL_4:
		tim_dma_req_config(hperh, TIM_DMA_CC4, DISABLE);
		break;

	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_DISABLE(hperh);

	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}
#endif
/**
  * @}
  */

/** @defgroup  TIM_Public_Functions_Group3 TIM PWM functions
  * @brief     TIM PWM functions
  *
  * @verbatim
	==============================================================================
	##### Time PWM functions #####
	==============================================================================
	[..]
		This section provides functions allowing to:
		(+) Initialize and configure the TIM PWM.
		(+) Start the Time PWM.
		(+) Stop the Time PWM.
		(+) Start the Time PWM and enable interrupt.
		(+) Stop the Time PWM and disable interrupt.
		(+) Start the Time PWM and enable DMA transfer.
		(+) Stop the Time PWM and disable DMA transfer.

    @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM PWM Time Base according to the specified
  *         parameters in the tim_handle_t and create the associated handle.
  * @param  hperh: TIM handle
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_pwm_init(tim_handle_t *hperh)
{
	return tim_base_init(hperh);
}

/**
  * @brief  Starts the PWM signal generation.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_pwm_start(tim_handle_t *hperh, tim_channel_t ch)
{
	tim_oc_start(hperh, ch);
	return;
}

/**
  * @brief  Stops the PWM signal generation.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_pwm_stop(tim_handle_t *hperh, tim_channel_t ch)
{
	tim_oc_stop(hperh, ch);
	return;
}

/**
  * @brief  Starts the PWM signal generation in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channel to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_pwm_start_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	tim_oc_start_by_it(hperh, ch);
	return;
}

/**
  * @brief  Stops the PWM signal generation in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_pwm_stop_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	tim_oc_stop_by_it(hperh, ch);
	return;
}

#ifdef HAL_DMA
/**
  * @brief  Starts the TIM PWM signal generation in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @param  hdma: Pointer to dma_handle_t.
  * @param  buf: The source Buffer address.
  * @param  len: The length of buffer to be transferred from memory to TIM peripheral
  * @param  dma_ch: Channel of DMA.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_pwm_start_by_dma(tim_handle_t *hperh, tim_channel_t ch,
                      dma_handle_t *hdma, uint16_t *buf, uint32_t len, uint8_t dma_ch)
{
	return tim_oc_start_by_dma(hperh, ch, hdma, buf, len, dma_ch);
}

/**
  * @brief  Stops the TIM PWM signal generation in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_pwm_stop_by_dma(tim_handle_t *hperh, tim_channel_t ch)
{
	tim_oc_stop_by_dma(hperh, ch);
	return;
}
#endif
/**
  * @brief  Set the PWM freq.
  * @param  hperh: TIM handle
  * @param  freq: PWM freq to set
  * @retval None
  */
void tim_pwm_set_freq(tim_handle_t *hperh, uint16_t freq)
{
	uint32_t _arr = cmu_get_pclk1_clock() / freq - 1;

	hperh->perh->ARR.ARR = _arr;
	hperh->init.period   = _arr;
}

/**
  * @brief  Set the PWM duty.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @param  duty: PWM duty to set
  * @retval None
  */
void tim_pwm_set_duty(tim_handle_t *hperh, tim_channel_t ch, uint16_t duty)
{
	uint32_t tmp = (hperh->init.period + 1) * duty / 100 - 1;

	if (ch == TIM_CHANNEL_1)
		hperh->perh->CCR1.CCR1 = tmp;
	else if (ch == TIM_CHANNEL_2)
		hperh->perh->CCR2.CCR2 = tmp;
	else if (ch == TIM_CHANNEL_3)
		hperh->perh->CCR3.CCR3 = tmp;
	else if (ch == TIM_CHANNEL_4)
		hperh->perh->CCR4.CCR4 = tmp;
	else {
		;/* do nothing */
	}
}

/**
  * @brief  Set capture the PWM.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be captured the PWM
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  * @retval None
  */
void tim_pwm_set_input(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_PWM_INPUT_INSTANCE(hperh->perh, ch));

	hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_DISABLE;
	switch(ch) {
	case TIM_CHANNEL_1:
		hperh->perh->CCMR1.IC.CC1S = TIM_IC_SELECT_DIRECTTI;
		hperh->perh->CCMR1.IC.CC2S = TIM_IC_SELECT_INDIRECTTI;
		hperh->perh->CCER.CC1P     = TIM_IC_POLARITY_RISING;
		hperh->perh->CCER.CC1NP    = TIM_IC_POLARITY_RISING;
		hperh->perh->CCER.CC2P     = TIM_IC_POLARITY_FALLING;
		hperh->perh->CCER.CC2NP    = TIM_IC_POLARITY_RISING;
		hperh->perh->SMCR.TS       = TIM_TS_TI1FP1;
		hperh->perh->SMCR.SMS      = TIM_SLAVE_MODE_RESET;
		break;
	case TIM_CHANNEL_2:
		hperh->perh->CCMR1.IC.CC1S = TIM_IC_SELECT_INDIRECTTI;
		hperh->perh->CCMR1.IC.CC2S = TIM_IC_SELECT_DIRECTTI;
		hperh->perh->CCER.CC1P     = TIM_IC_POLARITY_RISING;
		hperh->perh->CCER.CC1NP    = TIM_IC_POLARITY_FALLING;
		hperh->perh->CCER.CC2P     = TIM_IC_POLARITY_FALLING;
		hperh->perh->CCER.CC2NP    = TIM_IC_POLARITY_RISING;
		hperh->perh->SMCR.TS       = TIM_TS_TI2FP2;
		hperh->perh->SMCR.SMS      = TIM_SLAVE_MODE_RESET;
		break;
	default:
		break;
	}

	hperh->perh->CCER.CC1E = 1;
	hperh->perh->CCER.CC2E = 1;

	return;
}
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group4 TIM Input Capture functions
  * @brief    Time Input Capture functions
  *
  * @verbatim
  ==============================================================================
              ##### Time Input Capture functions #####
  ==============================================================================
 [..]
   This section provides functions allowing to:
   (+) Initialize and configure the TIM Input Capture.
   (+) Start the Time Input Capture.
   (+) Stop the Time Input Capture.
   (+) Start the Time Input Capture and enable interrupt.
   (+) Stop the Time Input Capture and disable interrupt.
   (+) Start the Time Input Capture and enable DMA transfer.
   (+) Stop the Time Input Capture and disable DMA transfer.

  * @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM Input Capture Time base according to the specified
  *         parameters in the tim_handle_t and create the associated handle.
  * @param  hperh: TIM handle
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_ic_init(tim_handle_t *hperh)
{
	return tim_base_init(hperh);
}

/**
  * @brief  Starts the TIM Input Capture measurement.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_ic_start(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);
	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Input Capture measurement.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_ic_stop(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);
	TIM_DISABLE(hperh);
	return;
}

/**
  * @brief  Starts the TIM Input Capture measurement in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_ic_start_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_interrupt_config(hperh, TIM_IT_CC1, ENABLE);
		break;
	case TIM_CHANNEL_2:
		tim_interrupt_config(hperh, TIM_IT_CC2, ENABLE);
		break;
	case TIM_CHANNEL_3:
		tim_interrupt_config(hperh, TIM_IT_CC3, ENABLE);
		break;
	case TIM_CHANNEL_4:
		tim_interrupt_config(hperh, TIM_IT_CC4, ENABLE);
		break;
	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);
	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Input Capture measurement in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_ic_stop_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_interrupt_config(hperh, TIM_IT_CC1, DISABLE);
		break;
	case TIM_CHANNEL_2:
		tim_interrupt_config(hperh, TIM_IT_CC2, DISABLE);
		break;
	case TIM_CHANNEL_3:
		tim_interrupt_config(hperh, TIM_IT_CC3, DISABLE);
		break;
	case TIM_CHANNEL_4:
		tim_interrupt_config(hperh, TIM_IT_CC4, DISABLE);
		break;
	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);
	TIM_DISABLE(hperh);
	return;
}

#ifdef HAL_DMA
/**
  * @brief  Starts the TIM Input Capture measurement in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @param  hdma: Pointer to dma_handle_t.
  * @param  buf: The destination Buffer address.
  * @param  len: The length of buffer to be transferred TIM peripheral to memory
  * @param  dma_ch: Channel of DMA.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_ic_start_by_dma(tim_handle_t *hperh, tim_channel_t ch,
                      dma_handle_t *hdma, uint16_t *buf, uint32_t len, uint8_t dma_ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	if ((hperh->state == TIM_STATE_BUSY))
		 return BUSY;
	if ((hperh->state == TIM_STATE_READY)) {
		if (((uint32_t)buf == 0 ) || (len == 0))
			return ERROR;
	}

	hperh->state   = TIM_STATE_BUSY;

	if (hdma->perh == NULL)
		hdma->perh = DMA0;

	hdma->cplt_cbk = tim_dma_capture_cplt;
	hdma->cplt_arg = (void *)hperh;
	hdma->err_cbk  = tim_dma_error;
	hdma->err_arg  = (void *)hperh;

	dma_config_struct(&hdma->config);
	hdma->config.dst     = (void *)buf;
	hdma->config.size    = len;
	hdma->config.data_width = DMA_DATA_SIZE_HALFWORD;
	hdma->config.src_inc = DMA_DATA_INC_NONE;
	hdma->config.dst_inc = DMA_DATA_INC_HALFWORD;
	hdma->config.channel = dma_ch;

	if (hperh->perh == TIM0)
		hdma->config.msel = DMA_MSEL_TIM0;
	else if (hperh->perh == TIM1)
		hdma->config.msel = DMA_MSEL_TIM1;
	else if (hperh->perh == TIM2)
		hdma->config.msel = DMA_MSEL_TIM2;
	else if (hperh->perh == TIM3)
		hdma->config.msel = DMA_MSEL_TIM3;
	else if (hperh->perh == TIM4)
		hdma->config.msel = DMA_MSEL_TIM4;
	else if (hperh->perh == TIM5)
		hdma->config.msel = DMA_MSEL_TIM5;
	else if (hperh->perh == TIM6)
		hdma->config.msel = DMA_MSEL_TIM6;
	else if (hperh->perh == TIM7)
		hdma->config.msel = DMA_MSEL_TIM7;
	else
		;/* do nothing */

	switch (ch) {
	case TIM_CHANNEL_1:
		hdma->config.src     = (void *)&hperh->perh->CCR1.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH1;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC1, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_1;
		break;

	case TIM_CHANNEL_2:
		hdma->config.src     = (void *)&hperh->perh->CCR2.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH2;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC2, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_2;
		break;

	case TIM_CHANNEL_3:
		hdma->config.src     = (void *)&hperh->perh->CCR3.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH3;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC3, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_3;
		break;

	case TIM_CHANNEL_4:
		hdma->config.src     = (void *)&hperh->perh->CCR4.Word;
		hdma->config.msigsel = DMA_MSIGSEL_TIM_CH4;
		dma_config_basic(hdma);
		tim_dma_req_config(hperh, TIM_DMA_CC4, ENABLE);
		hperh->ch = TIM_ACTIVE_CHANNEL_4;
		break;

	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, ENABLE);
	TIM_ENABLE(hperh);
	return OK;
}

/**
  * @brief  Stops the TIM Input Capture measurement in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval None
  */
void tim_ic_stop_by_dma(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_dma_req_config(hperh, TIM_DMA_CC1, DISABLE);
		break;
	case TIM_CHANNEL_2:
		tim_dma_req_config(hperh, TIM_DMA_CC2, DISABLE);
		break;
	case TIM_CHANNEL_3:
		tim_dma_req_config(hperh, TIM_DMA_CC3, DISABLE);
		break;
	case TIM_CHANNEL_4:
		tim_dma_req_config(hperh, TIM_DMA_CC4, DISABLE);
		break;
	default:
		break;
	}

	tim_ccx_channel_cmd(hperh->perh, ch, DISABLE);
	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}
#endif
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group5 TIM One Pulse functions
  * @brief    Time One Pulse functions
  *
  * @verbatim
  ==============================================================================
                        ##### Time One Pulse functions #####
  ==============================================================================
  [..]
    This section provides functions allowing to:
    (+) Initialize and configure the TIM One Pulse.
    (+) Start the Time One Pulse.
    (+) Stop the Time One Pulse.
    (+) Start the Time One Pulse and enable interrupt.
    (+) Stop the Time One Pulse and disable interrupt.
    (+) Start the Time One Pulse and enable DMA transfer.
    (+) Stop the Time One Pulse and disable DMA transfer.

  * @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM One Pulse Time Base according to the specified
  *         parameters in the tim_handle_t and create the associated handle.
  * @param  hperh: TIM handle
  * @param  mode: Select the One pulse mode.
  *         This parameter can be one of the following values:
  *            @arg TIM_OP_MODE_SINGLE: Only one pulse will be generated.
  *            @arg TIM_OP_MODE_REPETITIVE: Repetitive pulses wil be generated.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_one_pulse_init(tim_handle_t *hperh, tim_op_mode_t mode)
{
	if(hperh == NULL)
		return ERROR;

	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_COUNTER_MODE(hperh->init.mode));
	assert_param(IS_TIM_CLOCK_DIVISION(hperh->init.clk_div));
	assert_param(IS_TIM_OP_MODE(mode));

	if (hperh->state == TIM_STATE_RESET)
		hperh->lock = UNLOCK;

	hperh->state = TIM_STATE_BUSY;
	tim_base_set_config(hperh->perh, &hperh->init);
	hperh->perh->CR1.OPM = mode;
	hperh->state = TIM_STATE_READY;

	return OK;
}

/**
  * @brief  Starts the TIM One Pulse signal generation.
  * @param  hperh: TIM One Pulse handle
  * @param  ch: TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_OP_OUTPUT_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_OP_OUTPUT_CHANNEL_2: TIM Channel 2 selected
  * @retval None
  */
void tim_one_pulse_start(tim_handle_t *hperh, tim_op_output_channel_t ch)
{
	/* Enable the Capture compare and the Input Capture channels
	 * (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
	 * if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
	 * if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
	 * in all combinations, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be enabled together
	 */
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_ENABLE(hperh);

	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM One Pulse signal generation.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_OP_OUTPUT_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_OP_OUTPUT_CHANNEL_2: TIM Channel 2 selected
  * @retval None
  */
void tim_one_pulse_stop(tim_handle_t *hperh, tim_op_output_channel_t ch)
{
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_DISABLE(hperh);

	TIM_DISABLE(hperh);
	return;
}

/**
  * @brief  Starts the TIM One Pulse signal generation in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_OP_OUTPUT_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_OP_OUTPUT_CHANNEL_2: TIM Channel 2 selected
  * @retval None
  */
void tim_one_pulse_start_by_it(tim_handle_t *hperh, tim_op_output_channel_t ch)
{
	/* Enable the Capture compare and the Input Capture channels
	 * (in the OPM Mode the two possible channels that can be used are TIM_CHANNEL_1 and TIM_CHANNEL_2)
	 * if TIM_CHANNEL_1 is used as output, the TIM_CHANNEL_2 will be used as input and
	 * if TIM_CHANNEL_1 is used as input, the TIM_CHANNEL_2 will be used as output
	 * in all combinations, the TIM_CHANNEL_1 and TIM_CHANNEL_2 should be enabled together
	 */
	tim_interrupt_config(hperh, TIM_IT_CC1, ENABLE);
	tim_interrupt_config(hperh, TIM_IT_CC2, ENABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_ENABLE(hperh);

	return;
}

/**
  * @brief  Stops the TIM One Pulse signal generation in interrupt mode.
  * @param  hperh : TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_OP_OUTPUT_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_OP_OUTPUT_CHANNEL_2: TIM Channel 2 selected
  * @retval None
  */
void tim_one_pulse_stop_by_it(tim_handle_t *hperh, tim_op_output_channel_t ch)
{
	tim_interrupt_config(hperh, TIM_IT_CC1, DISABLE);
	tim_interrupt_config(hperh, TIM_IT_CC2, DISABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
	tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);

	if(IS_TIM_BREAK_INSTANCE(hperh->perh) != RESET)
		TIM_MOE_DISABLE(hperh);

	TIM_DISABLE(hperh);
	return;
}
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group6 TIM Encoder functions
  * @brief    TIM Encoder functions
  *
  * @verbatim
	==============================================================================
	##### Time Encoder functions #####
	==============================================================================
	[..]
		This section provides functions allowing to:
		(+) Initialize and configure the TIM Encoder.
		(+) Start the Time Encoder.
		(+) Stop the Time Encoder.
		(+) Start the Time Encoder and enable interrupt.
		(+) Stop the Time Encoder and disable interrupt.
		(+) Start the Time Encoder and enable DMA transfer.
		(+) Stop the Time Encoder and disable DMA transfer.

  * @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM Encoder Interface and create the associated handle.
  * @param  hperh: TIM handle
  * @param  config: TIM Encoder Interface configuration structure
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_encoder_init(tim_handle_t *hperh,  tim_encoder_init_t *config)
{
	if (hperh == NULL)
		return ERROR;

	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));
	assert_param(IS_TIM_ENCODER_MODE(config->mode));
	assert_param(IS_TIM_IC_POLARITY(config->ic1_polarity));
	assert_param(IS_TIM_IC_POLARITY(config->ic2_polarity));
	assert_param(IS_TIM_IC_SELECT(config->ic1_select));
	assert_param(IS_TIM_IC_SELECT(config->ic2_select));
	assert_param(IS_TIM_IC_PSC(config->ic1_psc));
	assert_param(IS_TIM_IC_PSC(config->ic2_psc));
	assert_param(IS_TIM_IC_FILTER(config->ic1_filter));
	assert_param(IS_TIM_IC_FILTER(config->ic2_filter));

	if (hperh->state == TIM_STATE_RESET)
		hperh->lock = UNLOCK;

	hperh->state         = TIM_STATE_BUSY;
	hperh->perh->SMCR.SMS = 0;
	tim_base_set_config(hperh->perh, &hperh->init);

	hperh->perh->SMCR.SMS        = config->mode;
	hperh->perh->CCMR1.IC.CC1S   = config->ic1_select;
	hperh->perh->CCMR1.IC.CC2S   = config->ic2_select;
	hperh->perh->CCMR1.IC.IC1PSC = config->ic1_psc;
	hperh->perh->CCMR1.IC.IC2PSC = config->ic2_psc;
	hperh->perh->CCMR1.IC.IC1F   = config->ic1_filter;
	hperh->perh->CCMR1.IC.IC2F   = config->ic2_filter;
	hperh->perh->CCER.CC1P       = (config->ic1_polarity & 0x1);
	hperh->perh->CCER.CC1NP      = ((config->ic1_polarity >> 1) & 0x1);
	hperh->perh->CCER.CC2P       = (config->ic2_polarity & 0x1);
	hperh->perh->CCER.CC2NP      = ((config->ic2_polarity >> 1) & 0x1);

	hperh->state = TIM_STATE_READY;
	return OK;
}

/**
  * @brief  Starts the TIM Encoder Interface.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @retval None
  */
void tim_encoder_start(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		break;
	case TIM_CHANNEL_2:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		break;
	default:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		break;
	}

	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Encoder Interface.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @retval None
  */
void tim_encoder_stop(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		break;
	case TIM_CHANNEL_2:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		break;
	default:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		break;
	}

	TIM_DISABLE(hperh);
	return;
}

/**
  * @brief  Starts the TIM Encoder Interface in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @retval None
  */
void tim_encoder_start_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		tim_interrupt_config(hperh, TIM_IT_CC1, ENABLE);
		break;
	case TIM_CHANNEL_2:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		tim_interrupt_config(hperh, TIM_IT_CC2, ENABLE);
		break;
	default:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		tim_interrupt_config(hperh, TIM_IT_CC1, ENABLE);
		tim_interrupt_config(hperh, TIM_IT_CC2, ENABLE);
		break;
	}

	TIM_ENABLE(hperh);
	return;
}

/**
  * @brief  Stops the TIM Encoder Interface in interrupt mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @retval None
  */
void tim_encoder_stop_by_it(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		tim_interrupt_config(hperh, TIM_IT_CC1, DISABLE);
		break;
	case TIM_CHANNEL_2:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		tim_interrupt_config(hperh, TIM_IT_CC2, DISABLE);
		break;
	default:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		tim_interrupt_config(hperh, TIM_IT_CC1, DISABLE);
		tim_interrupt_config(hperh, TIM_IT_CC2, DISABLE);
		break;
	}

	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}

#ifdef HAL_DMA
/**
  * @brief  Starts the TIM Encoder Interface in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @param  hdma1: Pointer to dma_handle_t.
  * @param  hdma2: Pointer to dma_handle_t.
  * @param  buf1: The destination Buffer address. Reading data from CCR1.
  * @param  buf2: The destination Buffer address. Reading data from CCR2.
  * @param  len: The length of buffer to be transferred TIM peripheral to memory
  * @param  dma_ch1: Channel of DMA.
  * @param  dma_ch2: Channel of DMA.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_encoder_start_by_dma(tim_handle_t *hperh, tim_channel_t ch,
                           dma_handle_t *hdma1, dma_handle_t *hdma2, uint16_t *buf1,
			   uint16_t *buf2, uint32_t len, uint8_t dma_ch1, uint8_t dma_ch2)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	if ((hperh->state == TIM_STATE_BUSY))
		 return BUSY;
	if ((hperh->state == TIM_STATE_READY)) {
		if (((uint32_t)buf1 == 0) || ((uint32_t)buf2 == 0) || (len == 0))
			return ERROR;
	}

	hperh->state   = TIM_STATE_BUSY;
	hdma1->cplt_cbk = tim_dma_capture_cplt;
	hdma1->cplt_arg = (void *)hperh;
	hdma1->err_cbk  = tim_dma_error;
	hdma1->err_arg  = (void *)hperh;

	dma_config_struct(&hdma1->config);
	hdma1->config.size       = len;
	hdma1->config.data_width = DMA_DATA_SIZE_HALFWORD;
	hdma1->config.src_inc    = DMA_DATA_INC_NONE;
	hdma1->config.dst_inc    = DMA_DATA_INC_HALFWORD;

	if (hperh->perh == TIM0)
		hdma1->config.msel = DMA_MSEL_TIM0;
	else if (hperh->perh == TIM1)
		hdma1->config.msel = DMA_MSEL_TIM1;
	else if (hperh->perh == TIM2)
		hdma1->config.msel = DMA_MSEL_TIM2;
	else if (hperh->perh == TIM3)
		hdma1->config.msel = DMA_MSEL_TIM3;
	else if (hperh->perh == TIM4)
		hdma1->config.msel = DMA_MSEL_TIM4;
	else if (hperh->perh == TIM5)
		hdma1->config.msel = DMA_MSEL_TIM5;
	else if (hperh->perh == TIM6)
		hdma1->config.msel = DMA_MSEL_TIM6;
	else if (hperh->perh == TIM7)
		hdma1->config.msel = DMA_MSEL_TIM7;
	else
		;/* do nothing */

	switch (ch) {
	case TIM_CHANNEL_1:
		hdma1->config.src     = (void *)&hperh->perh->CCR1.Word;
		hdma1->config.dst     = (void *)buf1;
		hdma1->config.msigsel = DMA_MSIGSEL_TIM_CH1;
		hdma1->config.channel = dma_ch1;
		dma_config_basic(hdma1);
		tim_dma_req_config(hperh, TIM_DMA_CC1, ENABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		TIM_ENABLE(hperh);
		break;

	case TIM_CHANNEL_2:
		hdma1->config.src     = (void *)&hperh->perh->CCR2.Word;
		hdma1->config.dst     = (void *)buf2;
		hdma1->config.msigsel = DMA_MSIGSEL_TIM_CH2;
		hdma1->config.channel = dma_ch2;
		dma_config_basic(hdma1);
		tim_dma_req_config(hperh, TIM_DMA_CC2, ENABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		TIM_ENABLE(hperh);
		break;

	default:
		hdma2->cplt_cbk = tim_dma_capture_cplt;
		hdma2->cplt_arg = (void *)hperh;
		hdma2->err_cbk  = tim_dma_error;
		hdma2->err_arg  = (void *)hperh;
		memcpy(&hdma2->config, &hdma1->config, sizeof(dma_config_t));

		hdma1->config.src     = (void *)&hperh->perh->CCR1.Word;
		hdma1->config.dst     = (void *)buf1;
		hdma1->config.msigsel = DMA_MSIGSEL_TIM_CH1;
		hdma1->config.channel = dma_ch1;
		dma_config_basic(hdma1);
		tim_dma_req_config(hperh, TIM_DMA_CC1, ENABLE);

		hdma2->config.src     = (void *)&hperh->perh->CCR2.Word;
		hdma2->config.dst     = (void *)buf2;
		hdma2->config.msigsel = DMA_MSIGSEL_TIM_CH2;
		hdma2->config.channel = dma_ch2;
		dma_config_basic(hdma2);
		tim_dma_req_config(hperh, TIM_DMA_CC2, ENABLE);

		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, ENABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, ENABLE);
		TIM_ENABLE(hperh);
		break;
	}

	return OK;
}

/**
  * @brief  Stops the TIM Encoder Interface in DMA mode.
  * @param  hperh: TIM handle
  * @param  ch: TIM Channels to be disabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_ALL: TIM Channel 1 and TIM Channel 2 are selected
  * @retval None
  */
void tim_encoder_stop_by_dma(tim_handle_t *hperh, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		tim_dma_req_config(hperh, TIM_DMA_CC1, DISABLE);
		break;
	case TIM_CHANNEL_2:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		tim_dma_req_config(hperh, TIM_DMA_CC2, DISABLE);
		break;
	default:
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_1, DISABLE);
		tim_ccx_channel_cmd(hperh->perh, TIM_CHANNEL_2, DISABLE);
		tim_dma_req_config(hperh, TIM_DMA_CC1, DISABLE);
		tim_dma_req_config(hperh, TIM_DMA_CC2, DISABLE);
		break;
	}

	TIM_DISABLE(hperh);
	hperh->state = TIM_STATE_READY;
	return;
}
#endif
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group7 Peripheral Control functions
 *  @brief    Peripheral Control functions
 *
 * @verbatim
  ==============================================================================
                   ##### Peripheral Control functions #####
  ==============================================================================
 [..]
   This section provides functions allowing to:
      (+) Configure The Input Output channels for OC, PWM, IC or One Pulse mode.
      (+) Configure External Clock source.
      (+) Configure Complementary channels, break features and dead time.
      (+) Configure Master and the Slave synchronization.
      (+) Handle TIM interrupt.
      (+) Get TIM compare register's vale.
      (+) Configure TIM interrupt ENABLE/DISABLE.
      (+) Get TIM interrupt source status.
      (+) Get TIM interrupt flag status.
      (+) Clear TIM interrupt flag.

    @endverbatim
  * @{
  */
/**
  * @brief  Initializes the TIM Output Compare Channels according to the specified
  *         parameters in the tim_oc_init_t.
  * @param  hperh: TIM handle
  * @param  config: TIM Output Compare configuration structure
  * @param  ch: TIM Channels to be enabled
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1 selected
  *		@arg TIM_CHANNEL_2: TIM Channel 2 selected
  *		@arg TIM_CHANNEL_3: TIM Channel 3 selected
  *		@arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_oc_config_channel(tim_handle_t *hperh, tim_oc_init_t* config, tim_channel_t ch)
{
	assert_param(IS_TIM_CCX_INSTANCE(hperh->perh, ch));
	assert_param(IS_TIM_OC_MODE(config->oc_mode));
	assert_param(IS_TIM_OC_POLARITY(config->oc_polarity));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	switch (ch){
	case TIM_CHANNEL_1:
		tim_oc1_set_config(hperh->perh, config);
		break;

	case TIM_CHANNEL_2:
		tim_oc2_set_config(hperh->perh, config);
		break;

	case TIM_CHANNEL_3:
		tim_oc3_set_config(hperh->perh, config);
		break;

	case TIM_CHANNEL_4:
		tim_oc4_set_config(hperh->perh, config);
		break;

	default:
		break;
	}

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Initializes the TIM Input Capture Channels according to the specified
  *         parameters in the tim_ic_init_t.
  * @param  hperh: TIM handle
  * @param  config: TIM Input Capture configuration structure
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_ic_config_channel(tim_handle_t *hperh, tim_ic_init_t* config, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));
	assert_param(IS_TIM_IC_POLARITY(config->ic_polarity));
	assert_param(IS_TIM_IC_SELECT(config->ic_select));
	assert_param(IS_TIM_IC_PSC(config->ic_psc));
	assert_param(IS_TIM_IC_FILTER(config->ic_filter));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	switch (ch) {
	case TIM_CHANNEL_1:
		tim_ti1_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);
		hperh->perh->CCMR1.IC.IC1PSC = config->ic_psc;
		break;

	case TIM_CHANNEL_2:
		tim_ti2_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);
		hperh->perh->CCMR1.IC.IC2PSC = config->ic_psc;
		break;

	case TIM_CHANNEL_3:
		tim_ti3_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);
		hperh->perh->CCMR2.IC.IC3PSC = config->ic_psc;
		break;

	case TIM_CHANNEL_4:
		tim_ti4_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);
		hperh->perh->CCMR2.IC.IC4PSC = config->ic_psc;
		break;

	default:
		break;
	}

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Initializes the TIM One Pulse Channels according to the specified
  *         parameters in the tim_one_pulse_init_t.
  * @param  hperh: TIM handle
  * @param  config: TIM One Pulse configuration structure
  * @param  ch_out: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  * @param  ch_in: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_one_pulse_config_channel(tim_handle_t *hperh, tim_one_pulse_init_t *config,
                                                   tim_channel_t ch_out,  tim_channel_t ch_in)
{
	tim_oc_init_t tmp;

	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));
	assert_param(IS_TIM_OC_MODE(config->mode));
	assert_param(IS_TIM_OC_POLARITY(config->oc_polarity));
	assert_param(IS_TIM_OCN_POLARITY(config->ocn_polarity));
	assert_param(IS_TIM_OCIDLE_STATE(config->oc_idle_state));
	assert_param(IS_TIM_OCNIDLE_STATE(config->ocn_idle_state));
	assert_param(IS_TIM_IC_POLARITY(config->ic_polarity));
	assert_param(IS_TIM_IC_SELECT(config->ic_select));
	assert_param(IS_TIM_IC_FILTER(config->ic_filter));

	if (ch_out == ch_in)
		return ERROR;

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	tmp.oc_mode        = config->mode;
	tmp.pulse          = config->pulse;
	tmp.oc_polarity    = config->oc_polarity;
	tmp.ocn_polarity   = config->ocn_polarity;
	tmp.oc_idle_state  = config->oc_idle_state;
	tmp.ocn_idle_state = config->ocn_idle_state;

	switch (ch_out) {
	case TIM_CHANNEL_1:
		tim_oc1_set_config(hperh->perh, &tmp);
		break;
	case TIM_CHANNEL_2:
		tim_oc2_set_config(hperh->perh, &tmp);
		break;
	default:
		break;
	}

	switch (ch_in) {
	case TIM_CHANNEL_1:
		tim_ti1_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);

		hperh->perh->CCMR1.IC.IC1PSC = 0;
		hperh->perh->SMCR.TS         = TIM_TS_TI1FP1;
		hperh->perh->SMCR.SMS        = TIM_SLAVE_MODE_TRIGGER;
		break;

	case TIM_CHANNEL_2:
		tim_ti2_set_config(hperh->perh, config->ic_polarity, config->ic_select, config->ic_filter);

		hperh->perh->CCMR1.IC.IC2PSC = 0;
		hperh->perh->SMCR.TS         = TIM_TS_TI2FP2;
		hperh->perh->SMCR.SMS        = TIM_SLAVE_MODE_TRIGGER;
		break;
	default:
		break;
	}

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Configures the OCRef clear feature
  * @param  hperh: TIM handle
  * @param  config: pointer to a TIM_ClearInputConfigTypeDef structure that
  *         contains the OCREF clear feature and parameters for the TIM peripheral.
  * @param  ch: specifies the TIM Channel
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1
  *            @arg TIM_CHANNEL_2: TIM Channel 2
  *            @arg TIM_CHANNEL_3: TIM Channel 3
  *            @arg TIM_CHANNEL_4: TIM Channel 4
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_config_oc_ref_clear(tim_handle_t *hperh, tim_clear_input_config_t *config, tim_channel_t ch)
{
	assert_param(IS_TIM_CC2_INSTANCE(hperh->perh));
	assert_param(IS_FUNC_STATE(config->state));
	assert_param(IS_TIM_CLEAR_INPUT_SOURCE(config->source));
	assert_param(IS_TIM_CLEAR_INPUT_POLARITY(config->polarity));
	assert_param(IS_TIM_ETR_PSC(config->psc));
	assert_param(IS_TIM_IC_FILTER(config->filter));

	/* to be added */
	return OK;
}

/**
  * @brief  Configures the clock source to be used
  * @param  hperh: TIM handle
  * @param  config: pointer to a tim_clock_config_t structure that
  *         contains the clock source information for the TIM peripheral.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_config_clock_source(tim_handle_t *hperh, tim_clock_config_t *config)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_CLOCK_SOURCE(config->source));
	assert_param(IS_TIM_CLOCK_POLARITY(config->polarity));
	assert_param(IS_TIM_ETR_PSC(config->psc));
	assert_param(IS_TIM_IC_FILTER(config->filter));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;
	hperh->perh->SMCR.Word = 0;

	switch (config->source) {
	case TIM_CLOCK_SOURCE_INTERNAL:
		hperh->perh->SMCR.SMS = 0;
		break;

	case TIM_CLOCK_SOURCE_ETRMODE1:
		tim_etr_set_config(hperh->perh, config->psc, config->polarity, config->filter);
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		hperh->perh->SMCR.TS  = TIM_TS_ETRF;
		break;

	case TIM_CLOCK_SOURCE_ETRMODE2:
		tim_etr_set_config(hperh->perh, config->psc, config->polarity, config->filter);
		hperh->perh->SMCR.ECE = 1;
		break;

	case TIM_CLOCK_SOURCE_TI1:
		tim_ti1_set_config_stage(hperh->perh, (tim_ic_polarity_t)config->polarity, config->filter);
		hperh->perh->SMCR.TS  = TIM_TS_TI1FP1;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_TI2:
		tim_ti2_set_config_stage(hperh->perh, (tim_ic_polarity_t)config->polarity, config->filter);
		hperh->perh->SMCR.TS  = TIM_TS_TI2FP2;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_TI1ED:
		tim_ti1_set_config_stage(hperh->perh, (tim_ic_polarity_t)config->polarity, config->filter);
		hperh->perh->SMCR.TS  = TIM_TS_TI1F_ED;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_ITR0:
		hperh->perh->SMCR.TS  = TIM_TS_ITR0;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_ITR1:
		hperh->perh->SMCR.TS  = TIM_TS_ITR1;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_ITR2:
		hperh->perh->SMCR.TS  = TIM_TS_ITR2;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;

	case TIM_CLOCK_SOURCE_ITR3:
		hperh->perh->SMCR.TS  = TIM_TS_ITR3;
		hperh->perh->SMCR.SMS = TIM_SLAVE_MODE_EXTERNAL1;
		break;
	default:
		break;
	}

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Selects the signal connected to the TI1 input: direct from CH1_input
  *         or a XOR combination between CH1_input, CH2_input & CH3_input
  * @param  hperh: TIM handle.
  * @param  ti1_select: Indicate whether or not channel 1 is connected to the
  *         output of a XOR gate.
  *         This parameter can be one of the following values:
  *            @arg 0: The TIMx_CH1 pin is connected to TI1 input
  *            @arg 1: The TIMx_CH1, CH2 and CH3
  *            pins are connected to the TI1 input (XOR combination)
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_config_ti1_input(tim_handle_t *hperh, uint32_t ti1_select)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));

	hperh->perh->CR2.TI1S = ti1_select;
	return OK;
}

/**
  * @brief  Configures the TIM in Slave mode
  * @param  hperh: TIM handle.
  * @param  config: pointer to a tim_slave_config_t structure that
  *         contains the selected trigger (internal trigger input, filtered
  *         timer input or external trigger input) and the Slave
  *         mode (Disable, Reset, Gated, Trigger, External clock mode 1).
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_slave_config_sync(tim_handle_t *hperh, tim_slave_config_t *config)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_SLAVE_MODE(config->mode));
	assert_param(IS_TIM_TS(config->input));
	assert_param(IS_TIM_CLOCK_POLARITY(config->polarity));
	assert_param(IS_TIM_ETR_PSC(config->psc));
	assert_param(IS_TIM_IC_FILTER(config->filter));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	tim_slave_set_config(hperh, config);
	tim_interrupt_config(hperh, TIM_IT_TRIGGER, DISABLE);
	tim_dma_req_config(hperh, TIM_DMA_TRIGGER, DISABLE);

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Configures the TIM in Slave mode in interrupt mode
  * @param  hperh: TIM handle.
  * @param  config: pointer to a tim_slave_config_t structure that
  *         contains the selected trigger (internal trigger input, filtered
  *         timer input or external trigger input) and the ) and the Slave
  *         mode (Disable, Reset, Gated, Trigger, External clock mode 1).
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_slave_config_sync_by_it(tim_handle_t *hperh, tim_slave_config_t *config)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_SLAVE_MODE(config->mode));
	assert_param(IS_TIM_TS(config->input));
	assert_param(IS_TIM_CLOCK_POLARITY(config->polarity));
	assert_param(IS_TIM_ETR_PSC(config->psc));
	assert_param(IS_TIM_IC_FILTER(config->filter));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	tim_slave_set_config(hperh, config);
	tim_interrupt_config(hperh, TIM_IT_TRIGGER, ENABLE);
	tim_dma_req_config(hperh, TIM_DMA_TRIGGER, DISABLE);

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Generate a software event
  * @param  hperh: TIM handle
  * @param  event: specifies the event source.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t tim_generate_event(tim_handle_t *hperh, tim_event_source_t event)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_EVENT_SOURCE(event));

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;
	hperh->perh->EGR.Word = event;
	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Read the captured value from Capture Compare unit
  * @param  hperh: TIM handle.
  * @param  ch: TIM Channels to be enabled
  *         This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1 : TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2 : TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3 : TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4 : TIM Channel 4 selected
  * @retval Captured value
  */
uint32_t tim_read_capture_value(tim_handle_t *hperh, tim_channel_t ch)
{
	uint32_t tmp;

	__LOCK(hperh);
	hperh->state = TIM_STATE_BUSY;

	switch (ch) {
	case TIM_CHANNEL_1:
		tmp = hperh->perh->CCR1.Word;
		break;
	case TIM_CHANNEL_2:
		tmp = hperh->perh->CCR2.Word;
		break;
	case TIM_CHANNEL_3:
		tmp = hperh->perh->CCR3.Word;
		break;
	case TIM_CHANNEL_4:
		tmp = hperh->perh->CCR4.Word;
		break;
	default:
		break;
	}

	hperh->state = TIM_STATE_READY;
	__UNLOCK(hperh);
	return tmp;
}

/**
  * @brief  This function handles TIM interrupts requests.
  * @param  hperh: TIM handle
  * @retval None
  */
void tim_irq_handle(tim_handle_t *hperh)
{
	/* Capture or compare 1 event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_CC1)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_CC1)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_CC1);
		hperh->ch = TIM_ACTIVE_CHANNEL_1;

		/* Input capture event */
		if ((hperh->perh->CCMR1.OC.CC1S) != 0) {
			if (hperh->capture_cbk)
				hperh->capture_cbk(hperh);
		}
		else {	/* Output compare event */
			if (hperh->delay_elapse_cbk)
				hperh->delay_elapse_cbk(hperh);
			if (hperh->pwm_pulse_finish_cbk)
				hperh->pwm_pulse_finish_cbk(hperh);
		}

		hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	}
	/* Capture or compare 2 event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_CC2)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_CC2)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_CC2);
		hperh->ch = TIM_ACTIVE_CHANNEL_2;

		/* Input capture event */
		if ((hperh->perh->CCMR1.OC.CC2S) != 0) {
			if (hperh->capture_cbk)
				hperh->capture_cbk(hperh);
		}
		else {	/* Output compare event */
			if (hperh->delay_elapse_cbk)
				hperh->delay_elapse_cbk(hperh);
			if (hperh->pwm_pulse_finish_cbk)
				hperh->pwm_pulse_finish_cbk(hperh);
		}

		hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	}
	/* Capture or compare 3 event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_CC3)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_CC3)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_CC3);
		hperh->ch = TIM_ACTIVE_CHANNEL_3;

		/* Input capture event */
		if ((hperh->perh->CCMR2.OC.CC3S) != 0) {
			if (hperh->capture_cbk)
				hperh->capture_cbk(hperh);
		}
		else {	/* Output compare event */
			if (hperh->delay_elapse_cbk)
				hperh->delay_elapse_cbk(hperh);
			if (hperh->pwm_pulse_finish_cbk)
				hperh->pwm_pulse_finish_cbk(hperh);
		}

		hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	}
	/* Capture or compare 4 event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_CC4)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_CC4)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_CC4);
		hperh->ch = TIM_ACTIVE_CHANNEL_4;

		/* Input capture event */
		if ((hperh->perh->CCMR2.OC.CC4S) != 0) {
			if (hperh->capture_cbk)
				hperh->capture_cbk(hperh);
		}
		else {	/* Output compare event */
			if (hperh->delay_elapse_cbk)
				hperh->delay_elapse_cbk(hperh);
			if (hperh->pwm_pulse_finish_cbk)
				hperh->pwm_pulse_finish_cbk(hperh);
		}

		hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	}

	/* TIM Update event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_UPDATE)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_UPDATE)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_UPDATE);

		if (hperh->period_elapse_cbk)
			hperh->period_elapse_cbk(hperh);
	}

	/* TIM Break input event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_BREAK)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_BREAK)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_BREAK);

		if (hperh->break_cbk)
			hperh->break_cbk(hperh);
	}

	/* TIM Trigger detection event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_TRIGGER)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_TRIGGER)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_TRIGGER);

		if (hperh->trigger_cbk)
			hperh->trigger_cbk(hperh);
	}

	/* TIM commutation event */
	if (((tim_get_flag_status(hperh, TIM_FLAG_COM)) != RESET) &&
                           ((tim_get_it_status(hperh, TIM_IT_COM)) != RESET)) {
		tim_clear_flag_status(hperh, TIM_FLAG_COM);

		if (hperh->com_cbk)
			hperh->com_cbk(hperh);
	}

	return;
}

/**
  * @brief  Configure DMA request source.
  * @param  hperh: TIM handle
  * @param  req: DMA request source.
  * @param  state: New state of the specified DMA request.
  * @retval None
  */
void tim_dma_req_config(tim_handle_t *hperh, tim_dma_req_t req, type_func_t state)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_DMA_REQ(req));
	assert_param(IS_FUNC_STATE(state));

	if (state == ENABLE)
		SET_BIT(hperh->perh->DMAEN.Word, req);
	else
		CLEAR_BIT(hperh->perh->DMAEN.Word, req);

	return;
}

/**
  * @brief  Enable/disable the specified TIM interrupts.
  * @param  hperh: Pointer to a tim_handle_t structure.
  * @param  it: Specifies the tim interrupt sources to be enabled or disabled.
  *         This parameter can be one of the @ref tim_it_t.
  * @param  state: New state of the specified TIM interrupts.
  *         This parameter can be:
  *             @arg ENABLE
  *             @arg DISABLE
  * @retval None
  */
void tim_interrupt_config(tim_handle_t *hperh, tim_it_t it, type_func_t state)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_IT(it));
	assert_param(IS_FUNC_STATE(state));

	if (state == ENABLE)
		SET_BIT(hperh->perh->IER.Word, it);
	else
		CLEAR_BIT(hperh->perh->IER.Word, it);

	return;
}

/**
  * @brief  Get the status of TIM interrupt source.
  * @param  hperh: Pointer to a tim_handle_t structure.
  * @param  it: Specifies the TIM interrupt source.
  *         This parameter can be one of the @ref tim_it_t.
  * @retval Status:
  *           - 0: RESET
  *           - 1: SET
  */
it_status_t tim_get_it_status(tim_handle_t *hperh, tim_it_t it)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_IT(it));

	if (hperh->perh->IVS.Word & it)
		return SET;

	return RESET;
}

/**
  * @brief  Get the status of TIM interrupt flag.
  * @param  hperh: Pointer to a tim_handle_t structure.
  * @param  flag: Specifies the TIM interrupt flag.
  *         This parameter can be one of the @ref tim_flag_t.
  * @retval Status:
  *           - 0: RESET
  *           - 1: SET
  */
flag_status_t tim_get_flag_status(tim_handle_t *hperh, tim_flag_t flag)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_FLAG(flag));

	if (hperh->perh->RIF.Word & flag)
		return SET;

	return RESET;
}

/**
  * @brief  Clear the TIM interrupt flag.
  * @param  hperh: Pointer to a uart_handle_t structure.
  * @param  flag: Specifies the TIM interrupt flag.
  *         This parameter can be one of the @ref tim_flag_t.
  * @retval None
  */
void tim_clear_flag_status(tim_handle_t *hperh, tim_flag_t flag)
{
	assert_param(IS_TIM_INSTANCE(hperh->perh));
	assert_param(IS_TIM_FLAG(flag));

	hperh->perh->ICR.Word = flag;
	return;
}
/**
  * @}
  */

/** @defgroup TIM_Public_Functions_Group8 Peripheral State functions
  * @brief    Peripheral State functions
  *
  * @verbatim
  ==============================================================================
                        ##### Peripheral State functions #####
  ==============================================================================
    [..]
    This subsection permit to get in run-time the status of the peripheral
    and the data flow.

    @endverbatim
  * @{
  */

/**
  * @brief  Return the TIM Base state
  * @param  hperh: TIM handle
  * @retval TIM peripheral state
  */
tim_state_t tim_get_state(tim_handle_t *hperh)
{
	return hperh->state;
}
/**
  * @}
  */
/**
  * @}
  */

/** @addtogroup TIM_Private_Functions
  * @{
  */

#ifdef HAL_DMA
/**
  * @brief  TIM DMA out compare complete callback.
  * @param  arg: pointer to TIM handle.
  * @retval None
  */
void tim_dma_oc_cplt(void *arg)
{
	tim_handle_t *hperh = (tim_handle_t *)arg;

	if (hperh->delay_elapse_cbk)
		hperh->delay_elapse_cbk(hperh);

	if (hperh->pwm_pulse_finish_cbk)
		hperh->pwm_pulse_finish_cbk(hperh);

	hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	return;
}

/**
  * @brief  TIM DMA Capture complete callback.
  * @param  arg: pointer to TIM handle.
  * @retval None
  */
void tim_dma_capture_cplt(void *arg)
{
	tim_handle_t *hperh = (tim_handle_t *)arg;

	if (hperh->capture_cbk)
		hperh->capture_cbk(hperh);

	hperh->ch = TIM_ACTIVE_CHANNEL_CLEARED;
	return;
}

/**
  * @brief  TIM DMA Period Elapse complete callback.
  * @param  arg: pointer to TIM handle.
  * @retval None
  */
void tim_dma_period_elapse_cplt(void *arg)
{
	tim_handle_t *hperh = (tim_handle_t *)arg;

	if (hperh->period_elapse_cbk)
		hperh->period_elapse_cbk(hperh);

	hperh->state = TIM_STATE_READY;
	return;
}

/**
  * @brief  TIM DMA error callback
  * @param  arg: pointer to TIM handle.
  * @retval None
  */
void tim_dma_error(void *arg)
{
	tim_handle_t *hperh = (tim_handle_t *)arg;

	hperh->state = TIM_STATE_READY;
	if (hperh->error_cbk)
		hperh->error_cbk(hperh);

	return;
}
#endif

/**
  * @brief  Time Base configuration
  * @param  TIMx: TIM periheral
  * @param  init: TIM Base configuration structure
  * @retval None
  */
static void tim_base_set_config(TIM_TypeDef *TIMx, tim_base_init_t *init)
{
	assert_param(IS_TIM_COUNTER_MODE(init->mode));
	assert_param(IS_TIM_CLOCK_DIVISION(init->clk_div));

	if (init->mode == TIM_COUNTER_MODE_UP || init->mode == TIM_COUNTER_MODE_DOWN) {
		TIMx->CR1.CMS = 0;
		TIMx->CR1.DIR = init->mode;
	}
	else {
		TIMx->CR1.CMS = init->mode - 1;
	}

	if(IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx))
		TIMx->CR1.CKD = init->clk_div;

	TIMx->ARR.ARR = init->period;
	TIMx->PSC.PSC = init->prescaler;
	TIMx->CNT.CNT = 0;

	if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx))
		TIMx->RCR.REP = init->re_cnt;

	TIMx->EGR.UG = 1;
	TIMx->ICR.Word = TIM_FLAG_UPDATE;
}

/**
  * @brief  Time Ouput Compare 1 configuration
  * @param  TIMx: Select the TIM peripheral
  * @param  oc_config: The ouput configuration structure
  * @retval None
  */
static void tim_oc1_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config)
{
	TIMx->CCER.CC1E     = 0;
	TIMx->CCMR1.OC.OC1M = 0;
	TIMx->CCMR1.OC.CC1S = 0;
	TIMx->CCMR1.OC.OC1M = oc_config->oc_mode;
	TIMx->CCER.CC1P     = oc_config->oc_polarity;

	if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_1)) {
		assert_param(IS_TIM_OCN_POLARITY(oc_config->ocn_polarity));
		TIMx->CCER.CC1NP = oc_config->ocn_polarity;
		TIMx->CCER.CC1NE = 0;
	}

	if (IS_TIM_BREAK_INSTANCE(TIMx)) {
		assert_param(IS_TIM_OCNIDLE_STATE(oc_config->ocn_idle_state));
		assert_param(IS_TIM_OCIDLE_STATE(oc_config->oc_idle_state));

		TIMx->CR2.OIS1  = oc_config->oc_idle_state;
		TIMx->CR2.OIS1N = oc_config->ocn_idle_state;
	}

	TIMx->CCR1.CCR1 = oc_config->pulse;
}

/**
  * @brief  Time Ouput Compare 2 configuration
  * @param  TIMx: Select the TIM peripheral
  * @param  oc_config: The ouput configuration structure
  * @retval None
  */
static void tim_oc2_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config)
{
	TIMx->CCER.CC2E     = 0;
	TIMx->CCMR1.OC.OC2M = 0;
	TIMx->CCMR1.OC.CC2S = 0;
	TIMx->CCMR1.OC.OC2M = oc_config->oc_mode;
	TIMx->CCER.CC2P     = oc_config->oc_polarity;

	if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_2)) {
		assert_param(IS_TIM_OCN_POLARITY(oc_config->ocn_polarity));
		TIMx->CCER.CC2NP = oc_config->ocn_polarity;
		TIMx->CCER.CC2NE = 0;
	}

	if (IS_TIM_BREAK_INSTANCE(TIMx)) {
		assert_param(IS_TIM_OCNIDLE_STATE(oc_config->ocn_idle_state));
		assert_param(IS_TIM_OCIDLE_STATE(oc_config->oc_idle_state));

		TIMx->CR2.OIS2= oc_config->oc_idle_state;
		TIMx->CR2.OIS2N= oc_config->ocn_idle_state;
	}

	TIMx->CCR2.CCR2 = oc_config->pulse;
}

/**
  * @brief  Time Ouput Compare 3 configuration
  * @param  TIMx: Select the TIM peripheral
  * @param  oc_config: The ouput configuration structure
  * @retval None
  */
static void tim_oc3_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config)
{
	TIMx->CCER.CC3E     = 0;
	TIMx->CCMR2.OC.OC3M = 0;
	TIMx->CCMR2.OC.CC3S = 0;
	TIMx->CCMR2.OC.OC3M = oc_config->oc_mode;
	TIMx->CCER.CC3P     = oc_config->oc_polarity;

	if (IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_3)) {
		assert_param(IS_TIM_OCN_POLARITY(oc_config->ocn_polarity));
		TIMx->CCER.CC3NP = oc_config->ocn_polarity;
		TIMx->CCER.CC3NE = 0;
	}

	if (IS_TIM_BREAK_INSTANCE(TIMx)) {
		assert_param(IS_TIM_OCNIDLE_STATE(oc_config->ocn_idle_state));
		assert_param(IS_TIM_OCIDLE_STATE(oc_config->oc_idle_state));

		TIMx->CR2.OIS3= oc_config->oc_idle_state;
		TIMx->CR2.OIS3N= oc_config->ocn_idle_state;
	}

	TIMx->CCR3.CCR3 = oc_config->pulse;
}

/**
  * @brief  Time Ouput Compare 4 configuration
  * @param  TIMx: Select the TIM peripheral
  * @param  oc_config: The ouput configuration structure
  * @retval None
  */
static void tim_oc4_set_config(TIM_TypeDef *TIMx, tim_oc_init_t *oc_config)
{
	TIMx->CCER.CC4E     = 0;
	TIMx->CCMR2.OC.OC4M = 0;
	TIMx->CCMR2.OC.CC4S = 0;
	TIMx->CCMR2.OC.OC4M = oc_config->oc_mode;
	TIMx->CCER.CC4P     = oc_config->oc_polarity;

	if(IS_TIM_BREAK_INSTANCE(TIMx)) {
		assert_param(IS_TIM_OCIDLE_STATE(oc_config->oc_idle_state));
		TIMx->CR2.OIS4= oc_config->oc_idle_state;
	}

	TIMx->CCR4.CCR4 = oc_config->pulse;
}

/**
  * @brief  Enables or disables the TIM Capture Compare Channel x.
  * @param  TIMx: Select the TIM peripheral
  * @param  ch: specifies the TIM Channel
  *		This parameter can be one of the following values:
  *		@arg TIM_CHANNEL_1: TIM Channel 1
  *		@arg TIM_CHANNEL_2: TIM Channel 2
  *		@arg TIM_CHANNEL_3: TIM Channel 3
  *		@arg TIM_CHANNEL_4: TIM Channel 4
  * @param  state: specifies the TIM Channel CCxE bit new state.
  *		This parameter can be: TIM_CCx_ENABLE or TIM_CCx_Disable.
  * @retval None
  */
static void tim_ccx_channel_cmd(TIM_TypeDef* TIMx, tim_channel_t ch, type_func_t state)
{
	assert_param(IS_TIM_CC2_INSTANCE(TIMx));
	assert_param(IS_TIM_CHANNELS(ch));

	switch(ch)
	{
		case TIM_CHANNEL_1:
			TIMx->CCER.CC1E = 0;
			TIMx->CCER.CC1E = state;
			break;
		case TIM_CHANNEL_2:
			TIMx->CCER.CC2E = 0;
			TIMx->CCER.CC2E = state;
			break;
		case TIM_CHANNEL_3:
			TIMx->CCER.CC3E = 0;
			TIMx->CCER.CC3E = state;
			break;
		case TIM_CHANNEL_4:
			TIMx->CCER.CC4E = 0;
			TIMx->CCER.CC4E = state;
			break;
		default:
			break;

	}
}

/**
  * @brief  Configure the TI1 as Input.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_select: specifies the input to be used.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti1_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter)
{
	TIMx->CCER.CC1E     = 0;
	TIMx->CCMR1.IC.CC1S = ic_select;
	TIMx->CCMR1.IC.IC1F = ic_filter;
	TIMx->CCER.CC1P     = (ic_polarity & 0x1);
	TIMx->CCER.CC1NP    = ((ic_polarity >> 1) & 0x1);

	return;
}

/**
  * @brief  Configure the Polarity and Filter for TI1.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti1_set_config_stage(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity, uint32_t ic_filter)
{
	TIMx->CCMR1.IC.IC1F = ic_filter;
	TIMx->CCER.CC1P     = (ic_polarity & 0x1);
	TIMx->CCER.CC1NP    = ((ic_polarity >> 1) & 0x1);
	return;
}

/**
  * @brief  Configure the TI2 as Input.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_select: specifies the input to be used.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti2_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter)
{
	TIMx->CCER.CC2E     = 0;
	TIMx->CCMR1.IC.CC2S = ic_select;
	TIMx->CCMR1.IC.IC2F = ic_filter;
	TIMx->CCER.CC2P     = (ic_polarity & 0x1);
	TIMx->CCER.CC2NP    = ((ic_polarity >> 1) & 0x1);

	return;
}

/**
  * @brief  Configure the Polarity and Filter for TI2.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti2_set_config_stage(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity, uint32_t ic_filter)
{
	TIMx->CCMR1.IC.IC2F = ic_filter;
	TIMx->CCER.CC2P     = (ic_polarity & 0x1);
	TIMx->CCER.CC2NP    = ((ic_polarity >> 1) & 0x1);
	return;
}

/**
  * @brief  Configure the TI3 as Input.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_select: specifies the input to be used.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti3_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter)
{
	TIMx->CCER.CC3E     = 0;
	TIMx->CCMR2.IC.CC3S = ic_select;
	TIMx->CCMR2.IC.IC3F = ic_filter;
	TIMx->CCER.CC3P     = (ic_polarity & 0x1);
	TIMx->CCER.CC3NP    = ((ic_polarity >> 1) & 0x1);

	return;
}

/**
  * @brief  Configure the TI4 as Input.
  * @param  TIMx: Select the TIM peripheral.
  * @param  ic_polarity: The Input Polarity.
  * @param  ic_select: specifies the input to be used.
  * @param  ic_filter: Specifies the Input Capture Filter.
  *         This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  */
static void tim_ti4_set_config(TIM_TypeDef *TIMx, tim_ic_polarity_t ic_polarity,
                                   tim_ic_select_t ic_select, uint32_t ic_filter)
{
	TIMx->CCER.CC4E     = 0;
	TIMx->CCMR2.IC.CC4S = ic_select;
	TIMx->CCMR2.IC.IC4F = ic_filter;
	TIMx->CCER.CC4P     = (ic_polarity & 0x1);

	return;
}

/**
  * @brief  Configures the TIMx External Trigger (ETR).
  * @param  TIMx: Select the TIM peripheral
  * @param  psc: The external Trigger Prescaler.
  * @param  polarity: The external Trigger Polarity.
  * @param  filter: External Trigger Filter.
  *         This parameter must be a value between 0x00 and 0x0F
  * @retval None
  */
static void tim_etr_set_config(TIM_TypeDef* TIMx, tim_etr_psc_t psc, tim_clock_polarity_t polarity, uint32_t filter)
{
	TIMx->SMCR.ETF  = filter;
	TIMx->SMCR.ETPS = psc;
	TIMx->SMCR.ECE  = 0;
	TIMx->SMCR.ETP  = polarity;
	return;
}

/**
  * @brief  Time Slave configuration
  * @param  hperh: pointer to a tim_handle_t structure that contains
  *         the configuration information for TIM module.
  * @param  config: The slave configuration structure
  * @retval None
  */
static void tim_slave_set_config(tim_handle_t *hperh, tim_slave_config_t *config)
{
	hperh->perh->SMCR.TS  = config->input;
	hperh->perh->SMCR.SMS = config->mode;

	switch (config->input) {
	case TIM_TS_ETRF:
		tim_etr_set_config(hperh->perh, config->psc, config->polarity, config->filter);
		break;

	case TIM_TS_TI1F_ED:
		hperh->perh->CCER.CC1E = 0;
		hperh->perh->CCMR1.IC.IC1F = config->filter;
		break;

	case TIM_TS_TI1FP1:
		tim_ti1_set_config_stage(hperh->perh, (tim_ic_polarity_t)config->polarity, config->filter);
		break;

	case TIM_TS_TI2FP2:
		tim_ti2_set_config_stage(hperh->perh, (tim_ic_polarity_t)config->polarity, config->filter);
		break;

	default:
		break;
	}
}
/**
  * @}
  */
#endif /* HAL_TIM */
/**
  * @}
  */
/**
  * @}
  */
