/* * Copyright (c) 2020-2021, Bluetrum Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-01-28 greedyhao first version */ #include #ifdef RT_USING_PWM #include "pwm_config.h" //#define DRV_DEBUG #define LOG_TAG "drv.pwm" #include #define MAX_PERIOD 65535 #define MIN_PERIOD 3 #define MIN_PULSE 2 void hal_pwm_mspinit(void); enum { #ifdef BSP_USING_T3_PWM T3_PWM_INDEX, #endif #ifdef BSP_USING_T4_PWM T4_PWM_INDEX, #endif #ifdef BSP_USING_T5_PWM T5_PWM_INDEX, #endif #ifdef BSP_USING_LPWM0 LPWM0_INDEX, #endif #ifdef BSP_USING_LPWM1 LPWM1_INDEX, #endif #ifdef BSP_USING_LPWM2 LPWM2_INDEX, #endif #ifdef BSP_USING_LPWM3 LPWM3_INDEX, #endif }; struct ab32_pwm { struct rt_device_pwm pwm_device; hal_sfr_t pwm_handle; char *name; rt_uint8_t channel; rt_uint32_t period; rt_uint32_t pulse; }; static struct ab32_pwm ab32_pwm_obj[] = { #ifdef BSP_USING_T3_PWM T3_PWM_CONFIG, #endif #ifdef BSP_USING_T4_PWM T4_PWM_CONFIG, #endif #ifdef BSP_USING_T5_PWM T5_PWM_CONFIG, #endif #ifdef BSP_USING_LPWM0 LPWM0_CONFIG, #endif #ifdef BSP_USING_LPWM1 LPWM1_CONFIG, #endif #ifdef BSP_USING_LPWM2 LPWM2_CONFIG, #endif #ifdef BSP_USING_LPWM3 LPWM3_CONFIG, #endif }; static rt_err_t drv_pwm_enable(hal_sfr_t pwm, char *name, struct rt_pwm_configuration *configuration, rt_bool_t enable) { rt_uint8_t channel = configuration->channel; rt_uint8_t pwm_num = 0; if (!configuration->complementary) { if (name[0] == 'l') { pwm[PWMxCON] &= ~BIT(5); } } else { if (name[0] == 'l') { pwm[PWMxCON] |= BIT(5); } else { LOG_W("Timer no support complementary PWM output!"); } } if (!enable) { if (name[0] == 'l') { pwm_num = name[4] - '0'; pwm[PWMxCON] &= ~(1 << (pwm_num)); } else { if (channel & 0x1) { /* pwm0 */ pwm[TMRxCON] &= ~(1 << (9 + 0)); } if (channel & 0x2) { /* pwm1 */ pwm[TMRxCON] &= ~(1 << (9 + 1)); } if (channel & 0x4) { /* pwm2 */ pwm[TMRxCON] &= ~(1 << (9 + 2)); } } } else { if (name[0] == 'l') { pwm_num = name[4] - '0'; pwm[PWMxCON] |= 1 << (pwm_num); } else { if (channel & 0x1) { /* pwm0 */ pwm[TMRxCON] |= (1 << (9 + 0)); } if (channel & 0x2) { /* pwm1 */ pwm[TMRxCON] |= (1 << (9 + 1)); } if (channel & 0x4) { /* pwm2 */ pwm[TMRxCON] |= (1 << (9 + 2)); } } } return RT_EOK; } static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg) { struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg; struct ab32_pwm *pwm_obj = (struct ab32_pwm *)device->parent.user_data; hal_sfr_t pwm = pwm_obj->pwm_handle; char *name = pwm_obj->name; rt_uint8_t channel = configuration->channel; rt_uint32_t period, pulse; rt_uint64_t tim_clock, psc; if (name[0] == 'l') { tim_clock = 6500; /* lpwm clock is 6.5MHz */ } else { tim_clock = get_sysclk_nhz() / 1000ul; } switch (cmd) { case PWMN_CMD_ENABLE: configuration->complementary = RT_TRUE; case PWM_CMD_ENABLE: return drv_pwm_enable(pwm, name, configuration, RT_TRUE); case PWMN_CMD_DISABLE: configuration->complementary = RT_FALSE; case PWM_CMD_DISABLE: return drv_pwm_enable(pwm, name, configuration, RT_FALSE); case PWM_CMD_SET: pwm_obj->pulse = configuration->pulse; pwm_obj->period = configuration->period; period = pwm_obj->period * tim_clock / 1000000ul; psc = period / MAX_PERIOD + 1; period = period / psc; if (period < MIN_PERIOD) { period = MIN_PERIOD; } pulse = pwm_obj->pulse * tim_clock / psc / 1000000ul; if (pulse < MIN_PULSE) { pulse = MIN_PULSE; } else if (pulse > period) { pulse = period; } if (name[0] == 'l') { pwm[PWMxPR] = period - 1; switch (name[4] - '0') { case 0: /* lpwm0 */ pwm[PWMxxDUT] = pulse - 1; break; case 1: /* lpwm1 */ pwm[PWMxxDUT] = (pulse - 1) << 16; break; case 2: /* lpwm2 */ pwm[PWMyyDUT] = pulse - 1; break; case 3: /* lpwm3 */ pwm[PWMyyDUT] = (pulse - 1) << 16; break; default: break; } } else { pwm[TMRxPR] = period - 1; if (channel & 0x1) { /* pwm0 */ pwm[TMRxDUTY0] = pulse - 1; } if (channel & 0x2) { /* pwm1 */ pwm[TMRxDUTY1] = pulse - 1; } if (channel & 0x4) { /* pwm2 */ pwm[TMRxDUTY2] = pulse - 1; } } return RT_EOK; case PWM_CMD_GET: configuration->pulse = pwm_obj->pulse; configuration->period = pwm_obj->period; return RT_EOK; default: return -RT_EINVAL; } } static rt_err_t ab32_hw_pwm_init(struct ab32_pwm *device) { rt_err_t result = RT_EOK; hal_sfr_t pwm = RT_NULL; char *name = RT_NULL; RT_ASSERT(device != RT_NULL); pwm = (hal_sfr_t)device->pwm_handle; name = device->name; if (name[0] == 'l') { pwm[PWMxCON] = 0; } else { pwm[TMRxCON] &= ~(7 << 9); } return result; } static void pwm_get_channel(void) { #ifdef BSP_USING_T3_PWM0 ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_T3_PWM1 ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 1; #endif #ifdef BSP_USING_T3_PWM2 ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 2; #endif #ifdef BSP_USING_T4_PWM0 ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_T4_PWM1 ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 1; #endif #ifdef BSP_USING_T4_PWM2 ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 2; #endif #ifdef BSP_USING_T5_PWM0 ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_T5_PWM1 ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 1; #endif #ifdef BSP_USING_T5_PWM2 ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 2; #endif #ifdef BSP_USING_LPWM0 ab32_pwm_obj[LPWM0_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_LPWM1 ab32_pwm_obj[LPWM1_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_LPWM2 ab32_pwm_obj[LPWM2_INDEX].channel |= 1 << 0; #endif #ifdef BSP_USING_LPWM3 ab32_pwm_obj[LPWM3_INDEX].channel |= 1 << 0; #endif } static struct rt_pwm_ops drv_ops = { drv_pwm_control }; static int ab32_pwm_init(void) { int i = 0; int result = RT_EOK; pwm_get_channel(); hal_pwm_mspinit(); for (i = 0; i < sizeof(ab32_pwm_obj) / sizeof(ab32_pwm_obj[0]); i++) { /* pwm init */ if (ab32_hw_pwm_init(&ab32_pwm_obj[i]) != RT_EOK) { LOG_E("%s init failed", ab32_pwm_obj[i].name); result = -RT_ERROR; goto __exit; } else { LOG_D("%s init success", ab32_pwm_obj[i].name); /* register pwm device */ if (rt_device_pwm_register(&ab32_pwm_obj[i].pwm_device, ab32_pwm_obj[i].name, &drv_ops, (void *)&ab32_pwm_obj[i]) == RT_EOK) { LOG_D("%s register success", ab32_pwm_obj[i].name); } else { LOG_E("%s register failed", ab32_pwm_obj[i].name); result = -RT_ERROR; } } } __exit: return result; } INIT_DEVICE_EXPORT(ab32_pwm_init); #endif /* RT_USING_PWM */