|
@@ -0,0 +1,220 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2006-2021, RT-Thread Development Team
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
+ *
|
|
|
+ * Change Logs:
|
|
|
+ * Date Author Notes
|
|
|
+ * 2021-10-25 KevinXu first version
|
|
|
+ */
|
|
|
+
|
|
|
+#include "drv_pwm.h"
|
|
|
+
|
|
|
+#ifdef RT_USING_PWM
|
|
|
+
|
|
|
+/* Declare the control function first */
|
|
|
+static rt_err_t drv_pwm_control(struct rt_device_pwm *, int, void *);
|
|
|
+static struct rt_pwm_ops drv_ops =
|
|
|
+{
|
|
|
+ drv_pwm_control
|
|
|
+};
|
|
|
+
|
|
|
+static struct ra_pwm ra6m4_pwm_obj[BSP_PWMS_NUM] =
|
|
|
+{
|
|
|
+#ifdef BSP_USING_PWM0
|
|
|
+ [BSP_PWM0_INDEX] = PWM_DRV_INITIALIZER(0),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM1
|
|
|
+ [BSP_PWM1_INDEX] = PWM_DRV_INITIALIZER(1),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM2
|
|
|
+ [BSP_PWM2_INDEX] = PWM_DRV_INITIALIZER(2),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM3
|
|
|
+ [BSP_PWM3_INDEX] = PWM_DRV_INITIALIZER(3),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM4
|
|
|
+ [BSP_PWM4_INDEX] = PWM_DRV_INITIALIZER(4),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM5
|
|
|
+ [BSP_PWM5_INDEX] = PWM_DRV_INITIALIZER(5),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM6
|
|
|
+ [BSP_PWM6_INDEX] = PWM_DRV_INITIALIZER(6),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM7
|
|
|
+ [BSP_PWM7_INDEX] = PWM_DRV_INITIALIZER(7),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM8
|
|
|
+ [BSP_PWM8_INDEX] = PWM_DRV_INITIALIZER(8),
|
|
|
+#endif
|
|
|
+#ifdef BSP_USING_PWM9
|
|
|
+ [BSP_PWM9_INDEX] = PWM_DRV_INITIALIZER(9),
|
|
|
+#endif
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/* Convert the raw PWM period counts into ns */
|
|
|
+static rt_uint32_t _convert_counts_ns(uint32_t source_div, uint32_t raw)
|
|
|
+{
|
|
|
+ uint32_t pclkd_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKD) >> source_div;
|
|
|
+ uint32_t ns = (uint32_t)(((uint64_t)raw * 1000000000ULL) / pclkd_freq_hz);
|
|
|
+ return ns;
|
|
|
+}
|
|
|
+
|
|
|
+/* Convert ns into raw PWM period counts */
|
|
|
+static rt_uint32_t _convert_ns_counts(uint32_t source_div, uint32_t raw)
|
|
|
+{
|
|
|
+ uint32_t pclkd_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKD) >> source_div;
|
|
|
+ uint32_t counts = (uint32_t)(((uint64_t)raw * (uint64_t)pclkd_freq_hz) / 1000000000ULL);
|
|
|
+ return counts;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* PWM_CMD_ENABLE or PWM_CMD_DISABLE */
|
|
|
+static rt_err_t drv_pwm_enable(struct ra_pwm *device,
|
|
|
+ struct rt_pwm_configuration *configuration,
|
|
|
+ rt_bool_t enable)
|
|
|
+{
|
|
|
+ fsp_err_t err = FSP_SUCCESS;
|
|
|
+
|
|
|
+ if (enable)
|
|
|
+ {
|
|
|
+ err = R_GPT_Start(device->g_ctrl);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ err = R_GPT_Stop(device->g_ctrl);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (err == FSP_SUCCESS) ? RT_EOK : -RT_ERROR;
|
|
|
+}
|
|
|
+
|
|
|
+/* PWM_CMD_GET */
|
|
|
+static rt_err_t drv_pwm_get(struct ra_pwm *device,
|
|
|
+ struct rt_pwm_configuration *configuration)
|
|
|
+{
|
|
|
+ timer_info_t info;
|
|
|
+ if (R_GPT_InfoGet(device->g_ctrl, &info) != FSP_SUCCESS)
|
|
|
+ return -RT_ERROR;
|
|
|
+
|
|
|
+ configuration->pulse =
|
|
|
+ _convert_counts_ns(device->g_cfg->source_div, device->g_cfg->duty_cycle_counts);
|
|
|
+ configuration->period =
|
|
|
+ _convert_counts_ns(device->g_cfg->source_div, info.period_counts);
|
|
|
+ configuration->channel = device->g_cfg->channel;
|
|
|
+
|
|
|
+ return RT_EOK;
|
|
|
+}
|
|
|
+
|
|
|
+/* PWM_CMD_SET */
|
|
|
+static rt_err_t drv_pwm_set(struct ra_pwm *device,
|
|
|
+ struct rt_pwm_configuration *conf)
|
|
|
+{
|
|
|
+ uint32_t counts;
|
|
|
+ fsp_err_t fsp_erra;
|
|
|
+ fsp_err_t fsp_errb;
|
|
|
+ rt_err_t rt_err;
|
|
|
+ uint32_t pulse;
|
|
|
+ uint32_t period;
|
|
|
+ struct rt_pwm_configuration orig_conf;
|
|
|
+
|
|
|
+ rt_err = drv_pwm_get(device, &orig_conf);
|
|
|
+ if (rt_err != RT_EOK)
|
|
|
+ {
|
|
|
+ return rt_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Pulse cannot last longer than period. */
|
|
|
+ period = conf->period;
|
|
|
+ pulse = (period >= conf->pulse) ? conf->pulse : period;
|
|
|
+
|
|
|
+ /* Not to set period again if it's not changed. */
|
|
|
+ if (period != orig_conf.period)
|
|
|
+ {
|
|
|
+ counts = _convert_ns_counts(device->g_cfg->source_div, period);
|
|
|
+ fsp_erra = R_GPT_PeriodSet(device->g_ctrl, counts);
|
|
|
+ if (fsp_erra != FSP_SUCCESS)
|
|
|
+ {
|
|
|
+ return -RT_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Two pins of a channel will not be separated. */
|
|
|
+ counts = _convert_ns_counts(device->g_cfg->source_div, pulse);
|
|
|
+ fsp_erra = R_GPT_DutyCycleSet(device->g_ctrl, counts, GPT_IO_PIN_GTIOCA);
|
|
|
+ fsp_errb = R_GPT_DutyCycleSet(device->g_ctrl, counts, GPT_IO_PIN_GTIOCB);
|
|
|
+ if (fsp_erra != FSP_SUCCESS || fsp_errb != FSP_SUCCESS)
|
|
|
+ {
|
|
|
+ return -RT_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_EOK;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implement of control method in struct rt_pwm_ops.
|
|
|
+ */
|
|
|
+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 ra_pwm *pwm_device = (struct ra_pwm *)device->parent.user_data;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * There's actually only one GPT timer with 10 channels. In this case, the
|
|
|
+ * timer is separated into 10 PWM devices, so each device has only one
|
|
|
+ * channel.
|
|
|
+ */
|
|
|
+ if (configuration->channel != 0)
|
|
|
+ {
|
|
|
+ return -RT_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (cmd)
|
|
|
+ {
|
|
|
+ case PWM_CMD_ENABLE:
|
|
|
+ return drv_pwm_enable(pwm_device, configuration, RT_TRUE);
|
|
|
+ case PWM_CMD_DISABLE:
|
|
|
+ return drv_pwm_enable(pwm_device, configuration, RT_FALSE);
|
|
|
+ case PWM_CMD_GET:
|
|
|
+ return drv_pwm_get(pwm_device, configuration);
|
|
|
+ case PWM_CMD_SET:
|
|
|
+ return drv_pwm_set(pwm_device, configuration);
|
|
|
+ default:
|
|
|
+ return -RT_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_EOK;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * This is to register the PWM device
|
|
|
+ *
|
|
|
+ * Note that the PWM driver only supports one fixed pin.
|
|
|
+ */
|
|
|
+int rt_hw_pwm_init(void)
|
|
|
+{
|
|
|
+ rt_err_t ret = RT_EOK;
|
|
|
+ rt_err_t rt_err = RT_EOK;
|
|
|
+ fsp_err_t fsp_err = FSP_SUCCESS;
|
|
|
+
|
|
|
+ for (int i = 0; i < BSP_PWMS_NUM; i++)
|
|
|
+ {
|
|
|
+ fsp_err = R_GPT_Open(ra6m4_pwm_obj[i].g_ctrl,
|
|
|
+ ra6m4_pwm_obj[i].g_cfg);
|
|
|
+
|
|
|
+ rt_err = rt_device_pwm_register(&ra6m4_pwm_obj[i].pwm_device,
|
|
|
+ ra6m4_pwm_obj[i].name,
|
|
|
+ &drv_ops,
|
|
|
+ &ra6m4_pwm_obj[i]);
|
|
|
+
|
|
|
+ if (fsp_err != FSP_SUCCESS || rt_err != RT_EOK)
|
|
|
+ {
|
|
|
+ ret = -RT_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+INIT_BOARD_EXPORT(rt_hw_pwm_init);
|
|
|
+#endif /* RT_USING_PWM */
|