552 lines
15 KiB
C
552 lines
15 KiB
C
/*
|
|
* Copyright (C) 2020, Huada Semiconductor Co., Ltd.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2020-10-30 CDT first version
|
|
*/
|
|
|
|
#include <rtdevice.h>
|
|
#include <rtdbg.h>
|
|
|
|
#ifdef RT_USING_HWTIMER
|
|
|
|
#if !defined(BSP_USING_TIMER1) && !defined(BSP_USING_TIMER2) && !defined(BSP_USING_TIMER3) && \
|
|
!defined(BSP_USING_TIMER4) && !defined(BSP_USING_TIMER5) && !defined(BSP_USING_TIMER6) && \
|
|
!defined(BSP_USING_TIMER7) && !defined(BSP_USING_TIMER8) && !defined(BSP_USING_TIMER9) && \
|
|
!defined(BSP_USING_TIMER10) && !defined(BSP_USING_TIMER11) && !defined(BSP_USING_TIMER12)
|
|
#error "Please define at least one BSP_USING_TIMERx"
|
|
/* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable TIMER */
|
|
#endif
|
|
|
|
#include "drv_hwtimer.h"
|
|
#include "drv_irq.h"
|
|
|
|
enum
|
|
{
|
|
#ifdef BSP_USING_TIMER1
|
|
TIMER1_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER2
|
|
TIMER2_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER3
|
|
TIMER3_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER4
|
|
TIMER4_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER5
|
|
TIMER5_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER6
|
|
TIMER6_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER7
|
|
TIMER7_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER8
|
|
TIMER8_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER9
|
|
TIMER9_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER10
|
|
TIMER10_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER11
|
|
TIMER11_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_TIMER12
|
|
TIMER12_INDEX,
|
|
#endif
|
|
};
|
|
|
|
#ifdef BSP_USING_TIMER1
|
|
static void timer1_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER2
|
|
static void timer2_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER3
|
|
static void timer3_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER4
|
|
static void timer4_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER5
|
|
static void timer5_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER6
|
|
static void timer6_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER7
|
|
static void timer7_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER8
|
|
static void timer8_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER9
|
|
static void timer9_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER10
|
|
static void timer10_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER11
|
|
static void timer11_irq_handler(void);
|
|
#endif
|
|
#ifdef BSP_USING_TIMER12
|
|
static void timer12_irq_handler(void);
|
|
#endif
|
|
|
|
struct hc32_hwtimer_config
|
|
{
|
|
rt_hwtimer_t time_device;
|
|
M4_TMRA_TypeDef *timer_periph;
|
|
struct hc32_irq_config irq_config;
|
|
func_ptr_t irq_callback;
|
|
uint32_t extend_div;
|
|
uint32_t extend_cnt;
|
|
char *name;
|
|
};
|
|
|
|
#ifndef HC32_TIMER_CONFIG
|
|
#define HC32_TIMER_CONFIG(periph, irq, label, src, irq_info) \
|
|
{ \
|
|
.timer_periph = periph, \
|
|
.irq_callback = irq, \
|
|
.name = label, \
|
|
.irq_config = irq_info, \
|
|
.irq_config.int_src = src, \
|
|
}
|
|
#endif /* HC32_TIMER_CONFIG */
|
|
|
|
static struct hc32_hwtimer_config hwtimer_obj[] =
|
|
{
|
|
#ifdef BSP_USING_TIMER1
|
|
HC32_TIMER_CONFIG(M4_TMRA_1, timer1_irq_handler, "timer1", INT_TMRA_1_OVF, TIMER1_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER2
|
|
HC32_TIMER_CONFIG(M4_TMRA_2, timer2_irq_handler, "timer2", INT_TMRA_2_OVF, TIMER2_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER3
|
|
HC32_TIMER_CONFIG(M4_TMRA_3, timer3_irq_handler, "timer3", INT_TMRA_3_OVF, TIMER3_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER4
|
|
HC32_TIMER_CONFIG(M4_TMRA_4, timer4_irq_handler, "timer4", INT_TMRA_4_OVF, TIMER4_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER5
|
|
HC32_TIMER_CONFIG(M4_TMRA_5, timer5_irq_handler, "timer5", INT_TMRA_5_OVF, TIMER5_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER6
|
|
HC32_TIMER_CONFIG(M4_TMRA_6, timer6_irq_handler, "timer6", INT_TMRA_6_OVF, TIMER6_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER7
|
|
HC32_TIMER_CONFIG(M4_TMRA_7, timer7_irq_handler, "timer7", INT_TMRA_7_OVF, TIMER7_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER8
|
|
HC32_TIMER_CONFIG(M4_TMRA_8, timer8_irq_handler, "timer8", INT_TMRA_8_OVF, TIMER8_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER9
|
|
HC32_TIMER_CONFIG(M4_TMRA_9, timer9_irq_handler, "timer9", INT_TMRA_9_OVF, TIMER9_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER10
|
|
HC32_TIMER_CONFIG(M4_TMRA_10, timer10_irq_handler, "timer10", INT_TMRA_10_OVF, TIMER10_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER11
|
|
HC32_TIMER_CONFIG(M4_TMRA_11, timer11_irq_handler, "timer11", INT_TMRA_11_OVF, TIMER11_IRQ_CONFIG),
|
|
#endif
|
|
#ifdef BSP_USING_TIMER12
|
|
HC32_TIMER_CONFIG(M4_TMRA_12, timer12_irq_handler, "timer12", INT_TMRA_12_OVF, TIMER12_IRQ_CONFIG),
|
|
#endif
|
|
};
|
|
|
|
static void hc32_timer_irq_handler(struct hc32_hwtimer_config *timer_config)
|
|
{
|
|
if ((++timer_config->extend_cnt) >= timer_config->extend_div)
|
|
{
|
|
timer_config->extend_cnt = 0;
|
|
rt_device_hwtimer_isr(&timer_config->time_device);
|
|
}
|
|
TMRA_ClrStatus(timer_config->timer_periph, (TMRA_FLAG_OVF | TMRA_FLAG_UNF));
|
|
}
|
|
|
|
#ifdef BSP_USING_TIMER1
|
|
static void timer1_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER1_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER2
|
|
static void timer2_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER2_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER3
|
|
static void timer3_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER3_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER4
|
|
static void timer4_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER4_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER5
|
|
static void timer5_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER5_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER6
|
|
static void timer6_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER6_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER7
|
|
static void timer7_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER7_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER8
|
|
static void timer8_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER8_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER9
|
|
static void timer9_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER9_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER10
|
|
static void timer10_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER10_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER11
|
|
static void timer11_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER11_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BSP_USING_TIMER12
|
|
static void timer12_irq_handler(void)
|
|
{
|
|
/* enter interrupt */
|
|
rt_interrupt_enter();
|
|
hc32_timer_irq_handler(&hwtimer_obj[TIMER12_INDEX]);
|
|
/* leave interrupt */
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
static rt_uint16_t hc32_timer_get_unit_number(M4_TMRA_TypeDef *TMRAx)
|
|
{
|
|
rt_uint16_t unit_num;
|
|
const rt_uint32_t unit_step = 0x400U;
|
|
|
|
if (((rt_uint32_t)TMRAx) >= ((rt_uint32_t)M4_TMRA_1))
|
|
{
|
|
unit_num = (((rt_uint32_t)TMRAx) - ((rt_uint32_t)M4_TMRA_1)) / unit_step;
|
|
}
|
|
else
|
|
{
|
|
unit_num = (((rt_uint32_t)TMRAx) - ((rt_uint32_t)M4_TMRA_5)) / unit_step + 4;
|
|
}
|
|
|
|
return unit_num;
|
|
}
|
|
|
|
static void hc32_timer_clock_config(M4_TMRA_TypeDef *TMRAx, en_functional_state_t enNewState)
|
|
{
|
|
rt_uint32_t timer_periph;
|
|
rt_uint16_t unit_num;
|
|
|
|
unit_num = hc32_timer_get_unit_number(TMRAx);
|
|
timer_periph = PWC_FCG2_TMRA_1 << unit_num;
|
|
PWC_Fcg2PeriphClockCmd(timer_periph, enNewState);
|
|
}
|
|
|
|
static void hc32_timer_get_div_value(M4_TMRA_TypeDef *TMRAx, uint32_t freq, uint16_t *div, uint32_t *extend_div)
|
|
{
|
|
stc_clk_freq_t stcClkFreq;
|
|
rt_uint32_t divisor, remainder;
|
|
rt_uint16_t div_val;
|
|
rt_uint32_t clk_freq;
|
|
rt_uint16_t unit_num;
|
|
|
|
CLK_GetClockFreq(&stcClkFreq);
|
|
unit_num = hc32_timer_get_unit_number(TMRAx);
|
|
if (unit_num >= 4)
|
|
{
|
|
clk_freq = stcClkFreq.pclk1Freq;
|
|
}
|
|
else
|
|
{
|
|
clk_freq = stcClkFreq.pclk0Freq;
|
|
}
|
|
divisor = clk_freq / freq;
|
|
remainder = clk_freq % freq;
|
|
for (div_val=1; div_val<=1024; div_val<<=1)
|
|
{
|
|
if (((divisor % 2) == 0) && (remainder == 0))
|
|
{
|
|
remainder = divisor % 2;
|
|
divisor = divisor / 2;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
*extend_div = divisor;
|
|
*div = (__CLZ(__RBIT(div_val))) << TMRA_BCSTR_CKDIV_POS;
|
|
}
|
|
|
|
static void hc32_timer_interrupt_config(struct rt_hwtimer_device *timer, en_functional_state_t enNewState)
|
|
{
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
if (INT_MAX == timer_config->irq_config.int_src)
|
|
{
|
|
LOG_D("%s interrupt init failed", timer_config->name);
|
|
return;
|
|
}
|
|
|
|
if (Enable == enNewState)
|
|
{
|
|
/* Enable the specified interrupts of Timer */
|
|
hc32_install_irq_handler(&timer_config->irq_config, timer_config->irq_callback, RT_FALSE);
|
|
NVIC_EnableIRQ(timer_config->irq_config.irq);
|
|
TMRA_IntCmd(timer_config->timer_periph, TMRA_INT_OVF, Enable);
|
|
}
|
|
else
|
|
{
|
|
/* Disable the specified interrupts of Timer */
|
|
TMRA_IntCmd(timer_config->timer_periph, TMRA_INT_OVF, Disable);
|
|
NVIC_DisableIRQ(timer_config->irq_config.irq);
|
|
}
|
|
}
|
|
|
|
static void hc32_timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
|
|
{
|
|
stc_tmra_init_t stcTmraInit;
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
|
|
RT_ASSERT(timer != RT_NULL);
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
if (state)
|
|
{
|
|
/* Enable Timer peripheral clock. */
|
|
hc32_timer_clock_config(timer_config->timer_periph, Enable);
|
|
TMRA_DeInit(timer_config->timer_periph);
|
|
TMRA_StructInit(&stcTmraInit);
|
|
stcTmraInit.u32CntDir = TMRA_DIR_UP;
|
|
TMRA_Init(timer_config->timer_periph, &stcTmraInit);
|
|
LOG_D("%s init success", timer_config->name);
|
|
}
|
|
else
|
|
{
|
|
TMRA_DeInit(timer_config->timer_periph);
|
|
hc32_timer_interrupt_config(timer, Disable);
|
|
/* Disable Timer peripheral clock. */
|
|
hc32_timer_clock_config(timer_config->timer_periph, Disable);
|
|
}
|
|
}
|
|
|
|
static rt_err_t hc32_timer_start(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode)
|
|
{
|
|
rt_err_t result = RT_EOK;
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
|
|
RT_ASSERT(timer != RT_NULL);
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
if (mode == HWTIMER_MODE_ONESHOT)
|
|
{
|
|
TMRA_SetOvfOperation(timer_config->timer_periph, TMRA_OVF_CNT_STOP);
|
|
}
|
|
else
|
|
{
|
|
TMRA_SetOvfOperation(timer_config->timer_periph, TMRA_OVF_CNT_CONTINUE);
|
|
}
|
|
timer_config->extend_cnt = 0;
|
|
TMRA_SetCntVal(timer_config->timer_periph, 0);
|
|
TMRA_SetPeriodVal(timer_config->timer_periph, cnt - 1);
|
|
hc32_timer_interrupt_config(timer, Enable);
|
|
TMRA_Start(timer_config->timer_periph);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void hc32_timer_stop(struct rt_hwtimer_device *timer)
|
|
{
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
|
|
RT_ASSERT(timer != RT_NULL);
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
/* stop timer */
|
|
TMRA_Stop(timer_config->timer_periph);
|
|
hc32_timer_interrupt_config(timer, Disable);
|
|
TMRA_SetCntVal(timer_config->timer_periph, 0);
|
|
}
|
|
|
|
static rt_uint32_t hc32_timer_get_counter(struct rt_hwtimer_device *timer)
|
|
{
|
|
rt_uint32_t count_val;
|
|
rt_uint32_t period_val;
|
|
float temp;
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
|
|
RT_ASSERT(timer != RT_NULL);
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
period_val = TMRA_GetPeriodVal(timer_config->timer_periph);
|
|
count_val = TMRA_GetCntVal(timer_config->timer_periph);
|
|
temp = (timer_config->extend_cnt * period_val + count_val);
|
|
temp = temp / (timer_config->extend_div * period_val) * period_val;
|
|
count_val = (rt_uint32_t)temp;
|
|
|
|
return count_val;
|
|
}
|
|
|
|
static rt_err_t hc32_timer_ctrl(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args)
|
|
{
|
|
struct hc32_hwtimer_config *timer_config = RT_NULL;
|
|
rt_err_t result = RT_EOK;
|
|
|
|
RT_ASSERT(timer != RT_NULL);
|
|
RT_ASSERT(args != RT_NULL);
|
|
timer_config = (struct hc32_hwtimer_config *)timer;
|
|
switch (cmd)
|
|
{
|
|
case HWTIMER_CTRL_FREQ_SET:
|
|
{
|
|
rt_uint32_t freq;
|
|
rt_uint16_t div_val;
|
|
rt_uint32_t extend_div_val;
|
|
|
|
/* set timer frequency */
|
|
freq = *((rt_uint32_t *)args);
|
|
hc32_timer_get_div_value(timer_config->timer_periph, freq, &div_val, &extend_div_val);
|
|
TMRA_SetPCLKDiv(timer_config->timer_periph, div_val);
|
|
timer_config->extend_div = extend_div_val;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
result = -RT_ENOSYS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static const struct rt_hwtimer_ops hwtimer_ops =
|
|
{
|
|
.init = hc32_timer_init,
|
|
.start = hc32_timer_start,
|
|
.stop = hc32_timer_stop,
|
|
.count_get = hc32_timer_get_counter,
|
|
.control = hc32_timer_ctrl,
|
|
};
|
|
|
|
static const struct rt_hwtimer_info hwtimer_info =
|
|
{
|
|
.maxfreq = 30000000,
|
|
.minfreq = 1000,
|
|
.maxcnt = 0xFFFF,
|
|
.cntmode = HWTIMER_CNTMODE_UP,
|
|
};
|
|
|
|
static int rt_hwtimer_init(void)
|
|
{
|
|
int i = 0;
|
|
int result = RT_EOK;
|
|
|
|
for (i = 0; i < sizeof(hwtimer_obj) / sizeof(hwtimer_obj[0]); i++)
|
|
{
|
|
hwtimer_obj[i].time_device.info = &hwtimer_info;
|
|
hwtimer_obj[i].time_device.ops = &hwtimer_ops;
|
|
if (rt_device_hwtimer_register(&hwtimer_obj[i].time_device, hwtimer_obj[i].name, hwtimer_obj[i].timer_periph) == RT_EOK)
|
|
{
|
|
LOG_D("%s register success", hwtimer_obj[i].name);
|
|
}
|
|
else
|
|
{
|
|
LOG_E("%s register failed", hwtimer_obj[i].name);
|
|
result = -RT_ERROR;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
INIT_BOARD_EXPORT(rt_hwtimer_init);
|
|
|
|
#endif /* RT_USING_HWTIMER */
|