/**************************************************************************//** * * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-3-16 YH First version * ******************************************************************************/ #include #if defined(BSP_USING_EPWM) #define LOG_TAG "drv.epwm" #define DBG_ENABLE #define DBG_SECTION_NAME "drv.epwm" #define DBG_LEVEL DBG_INFO #define DBG_COLOR #include #include #include #include #include "NuMicro.h" enum { EPWM_START = -1, #if defined(BSP_USING_EPWM0) EPWM0_IDX, #endif #if defined(BSP_USING_EPWM1) EPWM1_IDX, #endif EPWM_CNT }; struct nu_epwm { struct rt_device_pwm dev; char *name; EPWM_T *epwm_base; }; typedef struct nu_epwm *nu_epwm_t; static struct nu_epwm nu_epwm_arr [] = { #if defined(BSP_USING_EPWM0) { .name = "epwm0", .epwm_base = EPWM0, }, #endif #if defined(BSP_USING_EPWM1) { .name = "epwm1", .epwm_base = EPWM1, }, #endif {0} }; /* epwm nu_epwm */ static rt_err_t nu_epwm_control(struct rt_device_pwm *device, int cmd, void *arg); static struct rt_pwm_ops nu_epwm_ops = { .control = nu_epwm_control }; static rt_err_t nu_epwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable) { rt_err_t result = RT_EOK; EPWM_T *pwm_base = ((nu_epwm_t)device)->epwm_base; rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel; if (enable == RT_TRUE) { EPWM_EnableOutput(pwm_base, 1 << pwm_channel); EPWM_Start(pwm_base, 1 << pwm_channel); } else { EPWM_DisableOutput(pwm_base, 1 << pwm_channel); EPWM_ForceStop(pwm_base, 1 << pwm_channel); } return result; } static rt_err_t nu_epwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration) { if ((((struct rt_pwm_configuration *)configuration)->period) <= 0) return -(RT_ERROR); rt_uint8_t pwm_channel_pair; rt_uint32_t pwm_freq, pwm_dutycycle ; EPWM_T *pwm_base = ((nu_epwm_t)device)->epwm_base; rt_uint8_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel; rt_uint32_t pwm_period = ((struct rt_pwm_configuration *)configuration)->period; rt_uint32_t pwm_pulse = ((struct rt_pwm_configuration *)configuration)->pulse; rt_uint32_t pre_pwm_prescaler = EPWM_GET_PRESCALER(pwm_base, pwm_channel); if ((pwm_channel % 2) == 0) pwm_channel_pair = pwm_channel + 1; else pwm_channel_pair = pwm_channel - 1; pwm_freq = 1000000000 / pwm_period; pwm_dutycycle = (pwm_pulse * 100) / pwm_period; EPWM_ConfigOutputChannel(pwm_base, pwm_channel, pwm_freq, pwm_dutycycle) ; if ((pre_pwm_prescaler != 0) || (EPWM_GET_CNR(pwm_base, pwm_channel_pair) != 0) || (EPWM_GET_CMR(pwm_base, pwm_channel_pair) != 0)) { if (pre_pwm_prescaler < EPWM_GET_PRESCALER(pwm_base, pwm_channel)) { EPWM_SET_CNR(pwm_base, pwm_channel_pair, ((EPWM_GET_CNR(pwm_base, pwm_channel_pair) + 1) * (pre_pwm_prescaler + 1)) / (EPWM_GET_PRESCALER(pwm_base, pwm_channel) + 1)); EPWM_SET_CMR(pwm_base, pwm_channel_pair, (EPWM_GET_CMR(pwm_base, pwm_channel_pair) * (pre_pwm_prescaler + 1)) / (EPWM_GET_PRESCALER(pwm_base, pwm_channel) + 1)); } else if (pre_pwm_prescaler > EPWM_GET_PRESCALER(pwm_base, pwm_channel)) { EPWM_SET_CNR(pwm_base, pwm_channel, ((EPWM_GET_CNR(pwm_base, pwm_channel) + 1) * (EPWM_GET_PRESCALER(pwm_base, pwm_channel) + 1)) / (pre_pwm_prescaler + 1)); EPWM_SET_CMR(pwm_base, pwm_channel, (EPWM_GET_CMR(pwm_base, pwm_channel) * (EPWM_GET_PRESCALER(pwm_base, pwm_channel) + 1)) / (pre_pwm_prescaler + 1)); } } return RT_EOK; } static rt_uint32_t nu_epwm_clksr(struct rt_device_pwm *device) { rt_uint32_t u32Src, u32EPWMClockSrc; EPWM_T *pwm_base = ((nu_epwm_t)device)->epwm_base; if (pwm_base == EPWM0) { u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_EPWM0SEL_Msk; } else /* (epwm == EPWM1) */ { u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_EPWM1SEL_Msk; } if (u32Src == 0U) { /* clock source is from PLL clock */ u32EPWMClockSrc = CLK_GetPLLClockFreq(); } else { /* clock source is from PCLK */ SystemCoreClockUpdate(); if (pwm_base == EPWM0) { u32EPWMClockSrc = CLK_GetPCLK0Freq(); } else /* (epwm == EPWM1) */ { u32EPWMClockSrc = CLK_GetPCLK1Freq(); } } return u32EPWMClockSrc; } static rt_err_t nu_epwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration) { rt_uint32_t pwm_real_period, pwm_real_duty, time_tick, u32EPWMClockSrc ; EPWM_T *pwm_base = ((nu_epwm_t)device)->epwm_base; rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel; rt_uint32_t pwm_prescale = EPWM_GET_PRESCALER(pwm_base, pwm_channel); rt_uint32_t pwm_period = EPWM_GET_CNR(pwm_base, pwm_channel); rt_uint32_t pwm_pulse = EPWM_GET_CMR(pwm_base, pwm_channel); u32EPWMClockSrc = nu_epwm_clksr(device); time_tick = 1000000000000 / u32EPWMClockSrc; pwm_real_period = (((pwm_prescale + 1) * (pwm_period + 1)) * time_tick) / 1000; pwm_real_duty = (((pwm_prescale + 1) * pwm_pulse * time_tick)) / 1000; ((struct rt_pwm_configuration *)configuration)->period = pwm_real_period; ((struct rt_pwm_configuration *)configuration)->pulse = pwm_real_duty; LOG_I("%s %d %d %d\n", ((nu_epwm_t)device)->name, configuration->channel, configuration->period, configuration->pulse); return RT_EOK; } static rt_err_t nu_epwm_control(struct rt_device_pwm *device, int cmd, void *arg) { struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg; RT_ASSERT(device != RT_NULL); RT_ASSERT(configuration != RT_NULL); if (((((struct rt_pwm_configuration *)configuration)->channel) + 1) > EPWM_CHANNEL_NUM) return -(RT_ERROR); switch (cmd) { case PWM_CMD_ENABLE: return nu_epwm_enable(device, configuration, RT_TRUE); case PWM_CMD_DISABLE: return nu_epwm_enable(device, configuration, RT_FALSE); case PWM_CMD_SET: return nu_epwm_set(device, configuration); case PWM_CMD_GET: return nu_epwm_get(device, configuration); } return -(RT_EINVAL); } int rt_hw_epwm_init(void) { rt_err_t ret; int i; for (i = (EPWM_START + 1); i < EPWM_CNT; i++) { ret = rt_device_pwm_register(&nu_epwm_arr[i].dev, nu_epwm_arr[i].name, &nu_epwm_ops, RT_NULL); RT_ASSERT(ret == RT_EOK); } return 0; } INIT_DEVICE_EXPORT(rt_hw_epwm_init); #ifdef RT_USING_FINSH #include #ifdef FINSH_USING_MSH static int pwm_get(int argc, char **argv) { int result = 0; struct rt_device_pwm *device = RT_NULL; struct rt_pwm_configuration configuration = {0}; if (argc != 3) { rt_kprintf("Usage: pwm_get pwm1 1\n"); result = -RT_ERROR; goto _exit; } device = (struct rt_device_pwm *)rt_device_find(argv[1]); if (!device) { result = -RT_EIO; goto _exit; } configuration.channel = atoi(argv[2]); result = rt_device_control(&device->parent, PWM_CMD_GET, &configuration); _exit: return result; } MSH_CMD_EXPORT(pwm_get, pwm_get epwm1 1); #endif /* FINSH_USING_MSH */ #endif /* RT_USING_FINSH */ #endif