rt-thread/bsp/hc32f4a0/drivers/drv_pwm.c

529 lines
14 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 <board.h>
#include <rtdbg.h>
#ifdef RT_USING_PWM
#if !defined(BSP_USING_PWM1) && !defined(BSP_USING_PWM2) && !defined(BSP_USING_PWM3) && \
!defined(BSP_USING_PWM4) && !defined(BSP_USING_PWM5) && !defined(BSP_USING_PWM6) && \
!defined(BSP_USING_PWM7) && !defined(BSP_USING_PWM8) && !defined(BSP_USING_PWM9) && \
!defined(BSP_USING_PWM10) && !defined(BSP_USING_PWM11) && !defined(BSP_USING_PWM12)
#error "Please define at least one BSP_USING_PWMx"
/* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable PWM */
#endif
#define PWM_MAX_PERIOD (65535U)
#define PWM_MIN_PERIOD (1U)
#define PWM_MIN_PULSE (1U)
#define PWM_MAX_CHANNEL (TMRA_CH_4)
enum
{
#ifdef BSP_USING_PWM1
PWM1_INDEX,
#endif
#ifdef BSP_USING_PWM2
PWM2_INDEX,
#endif
#ifdef BSP_USING_PWM3
PWM3_INDEX,
#endif
#ifdef BSP_USING_PWM4
PWM4_INDEX,
#endif
#ifdef BSP_USING_PWM5
PWM5_INDEX,
#endif
#ifdef BSP_USING_PWM6
PWM6_INDEX,
#endif
#ifdef BSP_USING_PWM7
PWM7_INDEX,
#endif
#ifdef BSP_USING_PWM8
PWM8_INDEX,
#endif
#ifdef BSP_USING_PWM9
PWM9_INDEX,
#endif
#ifdef BSP_USING_PWM10
PWM10_INDEX,
#endif
#ifdef BSP_USING_PWM11
PWM11_INDEX,
#endif
#ifdef BSP_USING_PWM12
PWM12_INDEX,
#endif
};
struct hc32_pwm_config
{
struct rt_device_pwm pwm_device;
M4_TMRA_TypeDef *timer_periph;
rt_uint8_t channel;
char *name;
};
#ifndef HC32_PWM_CONFIG
#define HC32_PWM_CONFIG(periph, ch, label) \
{ \
.timer_periph = periph, \
.channel = ch, \
.name = label \
}
#endif /* HC32_PWM_CONFIG */
static struct hc32_pwm_config pwm_obj[] =
{
#ifdef BSP_USING_PWM1
HC32_PWM_CONFIG(M4_TMRA_1 , 0, "pwm1"),
#endif
#ifdef BSP_USING_PWM2
HC32_PWM_CONFIG(M4_TMRA_2 , 0, "pwm2"),
#endif
#ifdef BSP_USING_PWM3
HC32_PWM_CONFIG(M4_TMRA_3 , 0, "pwm3"),
#endif
#ifdef BSP_USING_PWM4
HC32_PWM_CONFIG(M4_TMRA_4 , 0, "pwm4"),
#endif
#ifdef BSP_USING_PWM5
HC32_PWM_CONFIG(M4_TMRA_5 , 0, "pwm5"),
#endif
#ifdef BSP_USING_PWM6
HC32_PWM_CONFIG(M4_TMRA_6 , 0, "pwm6"),
#endif
#ifdef BSP_USING_PWM7
HC32_PWM_CONFIG(M4_TMRA_7 , 0, "pwm7"),
#endif
#ifdef BSP_USING_PWM8
HC32_PWM_CONFIG(M4_TMRA_8 , 0, "pwm8"),
#endif
#ifdef BSP_USING_PWM9
HC32_PWM_CONFIG(M4_TMRA_9 , 0, "pwm9"),
#endif
#ifdef BSP_USING_PWM10
HC32_PWM_CONFIG(M4_TMRA_10 , 0, "pwm10"),
#endif
#ifdef BSP_USING_PWM11
HC32_PWM_CONFIG(M4_TMRA_11 , 0, "pwm11"),
#endif
#ifdef BSP_USING_PWM12
HC32_PWM_CONFIG(M4_TMRA_12 , 0, "pwm12"),
#endif
};
static rt_uint16_t hc32_pwm_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_pwm_clock_config(M4_TMRA_TypeDef *TMRAx, en_functional_state_t enNewState)
{
rt_uint32_t timer_periph;
rt_uint16_t unit_num;
unit_num = hc32_pwm_get_unit_number(TMRAx);
timer_periph = PWC_FCG2_TMRA_1 << unit_num;
PWC_Fcg2PeriphClockCmd(timer_periph, enNewState);
}
static rt_err_t hc32_pwm_enable(M4_TMRA_TypeDef *TMRAx, struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
if (configuration->channel > PWM_MAX_CHANNEL)
{
return RT_EINVAL;
}
if (!enable)
{
TMRA_PWM_Cmd(TMRAx, configuration->channel, Disable);
}
else
{
TMRA_PWM_Cmd(TMRAx, configuration->channel, Enable);
}
return RT_EOK;
}
static rt_err_t hc32_pwm_get(M4_TMRA_TypeDef *TMRAx, struct rt_pwm_configuration *configuration)
{
stc_clk_freq_t stcClkFreq;
rt_uint32_t clk_freq;
rt_uint16_t unit_num;
rt_uint16_t div_val;
CLK_GetClockFreq(&stcClkFreq);
unit_num = hc32_pwm_get_unit_number(TMRAx);
if (unit_num >= 4)
{
clk_freq = stcClkFreq.pclk1Freq;
}
else
{
clk_freq = stcClkFreq.pclk0Freq;
}
/* Convert nanosecond to frequency and duty cycle */
div_val = 0x01 << (READ_REG32_BIT(TMRAx->BCSTR, TMRA_BCSTR_CKDIV) >> TMRA_BCSTR_CKDIV_POS);
clk_freq /= 1000000UL;
configuration->period = (TMRA_GetPeriodVal(TMRAx) + 1) * div_val * 1000UL / clk_freq;
configuration->pulse = (TMRA_GetCmpVal(TMRAx, configuration->channel) + 1) * div_val * 1000UL / clk_freq;
return RT_EOK;
}
static rt_err_t hc32_pwm_set(M4_TMRA_TypeDef *TMRAx, struct rt_pwm_configuration *configuration)
{
rt_uint32_t period, pulse;
rt_uint64_t clk_div;
stc_clk_freq_t stcClkFreq;
rt_uint32_t clk_freq;
rt_uint16_t unit_num;
rt_uint16_t div_val;
CLK_GetClockFreq(&stcClkFreq);
unit_num = hc32_pwm_get_unit_number(TMRAx);
if (unit_num >= 4)
{
clk_freq = stcClkFreq.pclk1Freq;
}
else
{
clk_freq = stcClkFreq.pclk0Freq;
}
/* Convert nanosecond to frequency and duty cycle */
clk_freq /= 1000000UL;
period = (unsigned long long)configuration->period * clk_freq / 1000UL;
clk_div = period / PWM_MAX_PERIOD + 1;
if (clk_div > 1024)
{
return RT_EINVAL;
}
else if (clk_div != 1)
{
for (div_val=512; div_val>1; div_val>>=1)
{
if (clk_div > div_val)
{
clk_div = div_val << 1;
break;
}
}
}
period = period / clk_div;
TMRA_SetPCLKDiv(TMRAx, ((__CLZ(__RBIT(clk_div))) << TMRA_BCSTR_CKDIV_POS));
if (period < PWM_MIN_PERIOD)
{
period = PWM_MIN_PERIOD;
}
TMRA_SetPeriodVal(TMRAx, period - 1);
pulse = (unsigned long long)configuration->pulse * clk_freq / clk_div / 1000UL;
if (pulse < PWM_MIN_PULSE)
{
pulse = PWM_MIN_PULSE;
}
else if (pulse > period)
{
pulse = period;
}
TMRA_SetCmpVal(TMRAx, configuration->channel, pulse - 1);
TMRA_SetCntVal(TMRAx, 0);
return RT_EOK;
}
static rt_err_t hc32_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
{
struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
M4_TMRA_TypeDef *timer_periph = (M4_TMRA_TypeDef *)device->parent.user_data;
switch (cmd)
{
case PWM_CMD_ENABLE:
return hc32_pwm_enable(timer_periph, configuration, RT_TRUE);
case PWM_CMD_DISABLE:
return hc32_pwm_enable(timer_periph, configuration, RT_FALSE);
case PWM_CMD_SET:
return hc32_pwm_set(timer_periph, configuration);
case PWM_CMD_GET:
return hc32_pwm_get(timer_periph, configuration);
default:
return RT_EINVAL;
}
}
extern rt_err_t rt_hw_board_pwm_init(M4_TMRA_TypeDef *TMRAx);
static rt_err_t hc32_pwm_init(struct hc32_pwm_config *device)
{
rt_err_t result = RT_EOK;
stc_tmra_init_t stcTmraInit;
stc_tmra_pwm_cfg_t stcPwmCfg;
RT_ASSERT(device != RT_NULL);
/* Enable Timer peripheral clock. */
hc32_pwm_clock_config(device->timer_periph, Enable);
/* pwm pin configuration */
result = rt_hw_board_pwm_init(device->timer_periph);
if (RT_EOK == result)
{
TMRA_DeInit(device->timer_periph);
TMRA_StructInit(&stcTmraInit);
stcTmraInit.u32PCLKDiv = TMRA_PCLK_DIV1;
stcTmraInit.u32CntOvfOp = TMRA_OVF_CNT_CONTINUE;
stcTmraInit.u32PeriodVal = 0xFFFF;
stcTmraInit.u32CntVal = 0;
TMRA_Init(device->timer_periph, &stcTmraInit);
/* Set the comparison reference value */
TMRA_PWM_StructInit(&stcPwmCfg);
stcPwmCfg.u32StartPolarity = TMRA_PWM_START_HIGH;
stcPwmCfg.u32StopPolarity = TMRA_PWM_STOP_LOW;
stcPwmCfg.u32CmpPolarity = TMRA_PWM_CMP_LOW;
stcPwmCfg.u32PeriodPolarity = TMRA_PWM_PERIOD_HIGH;
/* config pwm channel */
if (0 != (device->channel & 0x01))
{
TMRA_PWM_Config(device->timer_periph, TMRA_CH_1, &stcPwmCfg);
TMRA_SetCmpVal(device->timer_periph, TMRA_CH_1, 0x7FFF);
}
if (0 != (device->channel & 0x02))
{
TMRA_PWM_Config(device->timer_periph, TMRA_CH_2, &stcPwmCfg);
TMRA_SetCmpVal(device->timer_periph, TMRA_CH_2, 0x7FFF);
}
if (0 != (device->channel & 0x04))
{
TMRA_PWM_Config(device->timer_periph, TMRA_CH_3, &stcPwmCfg);
TMRA_SetCmpVal(device->timer_periph, TMRA_CH_3, 0x7FFF);
}
if (0 != (device->channel & 0x08))
{
TMRA_PWM_Config(device->timer_periph, TMRA_CH_4, &stcPwmCfg);
TMRA_SetCmpVal(device->timer_periph, TMRA_CH_4, 0x7FFF);
}
/* start timer */
TMRA_Start(device->timer_periph);
}
return result;
}
static void hc32_pwm_get_channel(void)
{
#ifdef BSP_USING_PWM1_CH1
pwm_obj[PWM1_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM1_CH2
pwm_obj[PWM1_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM1_CH3
pwm_obj[PWM1_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM1_CH4
pwm_obj[PWM1_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM2_CH1
pwm_obj[PWM2_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM2_CH2
pwm_obj[PWM2_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM2_CH3
pwm_obj[PWM2_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM2_CH4
pwm_obj[PWM2_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM3_CH1
pwm_obj[PWM3_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM3_CH2
pwm_obj[PWM3_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM3_CH3
pwm_obj[PWM3_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM3_CH4
pwm_obj[PWM3_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM4_CH1
pwm_obj[PWM4_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM4_CH2
pwm_obj[PWM4_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM4_CH3
pwm_obj[PWM4_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM4_CH4
pwm_obj[PWM4_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM5_CH1
pwm_obj[PWM5_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM5_CH2
pwm_obj[PWM5_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM5_CH3
pwm_obj[PWM5_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM5_CH4
pwm_obj[PWM5_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM6_CH1
pwm_obj[PWM6_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM6_CH2
pwm_obj[PWM6_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM6_CH3
pwm_obj[PWM6_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM6_CH4
pwm_obj[PWM6_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM7_CH1
pwm_obj[PWM7_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM7_CH2
pwm_obj[PWM7_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM7_CH3
pwm_obj[PWM7_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM7_CH4
pwm_obj[PWM7_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM8_CH1
pwm_obj[PWM8_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM8_CH2
pwm_obj[PWM8_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM8_CH3
pwm_obj[PWM8_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM8_CH4
pwm_obj[PWM8_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM9_CH1
pwm_obj[PWM9_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM9_CH2
pwm_obj[PWM9_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM9_CH3
pwm_obj[PWM9_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM9_CH4
pwm_obj[PWM9_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM10_CH1
pwm_obj[PWM10_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM10_CH2
pwm_obj[PWM10_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM10_CH3
pwm_obj[PWM10_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM10_CH4
pwm_obj[PWM10_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM11_CH1
pwm_obj[PWM11_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM11_CH2
pwm_obj[PWM11_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM11_CH3
pwm_obj[PWM11_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM11_CH4
pwm_obj[PWM11_INDEX].channel |= (0x01 << 3);
#endif
#ifdef BSP_USING_PWM12_CH1
pwm_obj[PWM12_INDEX].channel |= (0x01 << 0);
#endif
#ifdef BSP_USING_PWM12_CH2
pwm_obj[PWM12_INDEX].channel |= (0x01 << 1);
#endif
#ifdef BSP_USING_PWM12_CH3
pwm_obj[PWM12_INDEX].channel |= (0x01 << 2);
#endif
#ifdef BSP_USING_PWM12_CH4
pwm_obj[PWM12_INDEX].channel |= (0x01 << 3);
#endif
}
static struct rt_pwm_ops pwm_ops =
{
.control = hc32_pwm_control
};
static int rt_hw_pwm_init(void)
{
int i = 0;
int result = RT_EOK;
hc32_pwm_get_channel();
for (i = 0; i < sizeof(pwm_obj) / sizeof(pwm_obj[0]); i++)
{
if (hc32_pwm_init(&pwm_obj[i]) != RT_EOK)
{
LOG_E("%s init failed", pwm_obj[i].name);
result = -RT_ERROR;
}
else
{
LOG_D("%s init success", pwm_obj[i].name);
/* register pwm device */
if (rt_device_pwm_register(&pwm_obj[i].pwm_device, pwm_obj[i].name, &pwm_ops, pwm_obj[i].timer_periph) == RT_EOK)
{
LOG_D("%s register success", pwm_obj[i].name);
}
else
{
LOG_E("%s register failed", pwm_obj[i].name);
result = -RT_ERROR;
}
}
}
return result;
}
INIT_DEVICE_EXPORT(rt_hw_pwm_init);
#endif /* RT_USING_PWM */