/* * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-07-26 supperthomas first version * */ #include #include "rtdevice.h" #include "rtservice.h" #ifdef RT_USING_PWM #include struct mcu_pwm { struct rt_device_pwm pwm_device; nrfx_pwm_t *pwm_handle; nrf_pwm_values_individual_t m_demo1_seq_values; nrf_pwm_sequence_t m_demo1_seq; rt_uint8_t channel; char *name; rt_uint64_t pwm_src_clk; uint8_t channel_0_pin; uint8_t channel_1_pin; uint8_t channel_2_pin; uint8_t channel_3_pin; }; enum { #ifdef BSP_USING_PWM0 PWM0_INDEX, #endif #ifdef BSP_USING_PWM1 PWM1_INDEX, #endif #ifdef BSP_USING_PWM2 PWM2_INDEX, #endif #ifdef BSP_USING_PWM3 PWM3_INDEX, #endif }; #ifdef BSP_USING_PWM0 static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0); #define PWM0_CONFIG \ { \ .pwm_handle = &m_pwm0, \ .name = "pwm0", \ .pwm_src_clk = 1000000, \ } #endif #ifdef BSP_USING_PWM1 static nrfx_pwm_t m_pwm1 = NRFX_PWM_INSTANCE(1); #define PWM1_CONFIG \ { \ .pwm_handle = &m_pwm1, \ .name = "pwm1", \ .pwm_src_clk = 1000000, \ } #endif #ifdef BSP_USING_PWM2 static nrfx_pwm_t m_pwm2 = NRFX_PWM_INSTANCE(2); #define PWM2_CONFIG \ { \ .pwm_handle = &m_pwm2, \ .name = "pwm2", \ .pwm_src_clk = 1000000, \ } #endif #ifdef BSP_USING_PWM3 static nrfx_pwm_t m_pwm3 = NRFX_PWM_INSTANCE(3); #define PWM3_CONFIG \ { \ .pwm_handle = &m_pwm3, \ .name = "pwm3", \ .pwm_src_clk = 1000000, \ } #endif static struct mcu_pwm mcu_pwm_obj[] = { #ifdef BSP_USING_PWM0 PWM0_CONFIG, #endif #ifdef BSP_USING_PWM1 PWM1_CONFIG, #endif #ifdef BSP_USING_PWM2 PWM2_CONFIG, #endif #ifdef BSP_USING_PWM3 PWM3_CONFIG, #endif }; static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg); static struct rt_pwm_ops drv_ops = { drv_pwm_control }; static rt_err_t drv_pwm_enable(struct mcu_pwm *p_mcu, struct rt_pwm_configuration *configuration, rt_bool_t enable) { if (!enable) { nrfx_pwm_stop(p_mcu->pwm_handle, true); } else { (void)nrfx_pwm_simple_playback(p_mcu->pwm_handle, &p_mcu->m_demo1_seq, 1, NRFX_PWM_FLAG_LOOP); } return RT_EOK; } uint8_t mcu_get_channel_number(uint8_t channel) { if (channel & 0x01) { return 0; } else if (channel & 0x02) { return 1; } else if (channel & 0x04) { return 2; } else if (channel & 0x08) { return 3; } return 0; } static rt_err_t drv_pwm_get(struct mcu_pwm *pwm_handle, struct rt_pwm_configuration *configuration) { rt_uint8_t channel_number = mcu_get_channel_number(configuration->channel); uint8_t tick_pscond; tick_pscond = pwm_handle->pwm_src_clk / 1000000UL; configuration->period = pwm_handle->pwm_handle->p_registers->COUNTERTOP * 1000UL / tick_pscond; configuration->pulse = pwm_handle->pwm_handle->p_registers->SEQ[channel_number].PTR / tick_pscond; return RT_EOK; } static void nrfx_set_prioid(nrfx_pwm_t *pwm_handle, uint32_t perioid) { pwm_handle->p_registers->COUNTERTOP = perioid; } static rt_err_t drv_pwm_set(struct mcu_pwm *p_mcu, struct rt_pwm_configuration *configuration) { rt_uint32_t period, pulse; uint8_t tick_pscond; tick_pscond = p_mcu->pwm_src_clk / 1000000UL; p_mcu->pwm_handle->p_registers->COUNTERTOP = (unsigned long long)configuration->period * tick_pscond; if (configuration->channel & 0x01) { p_mcu->m_demo1_seq_values.channel_0 = configuration->pulse; } if (configuration->channel & 0x02) { p_mcu->m_demo1_seq_values.channel_1 = configuration->pulse; } if (configuration->channel & 0x04) { p_mcu->m_demo1_seq_values.channel_2 = configuration->pulse; } if (configuration->channel & 0x08) { p_mcu->m_demo1_seq_values.channel_3 = configuration->pulse; } 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; void *pwm_handle = (void *)device->parent.user_data; nrfx_pwm_t *p_handle = (nrfx_pwm_t *)pwm_handle; struct mcu_pwm *p_mcu = rt_container_of(p_handle, struct mcu_pwm, pwm_handle); switch (cmd) { case PWM_CMD_ENABLE: return drv_pwm_enable(p_mcu, configuration, RT_TRUE); case PWM_CMD_DISABLE: return drv_pwm_enable(p_mcu, configuration, RT_FALSE); case PWM_CMD_SET: return drv_pwm_set(p_mcu, configuration); case PWM_CMD_GET: return drv_pwm_get(p_mcu, configuration); default: return RT_EINVAL; } } static rt_err_t mcu_hw_pwm_init(struct mcu_pwm *device) { #define NRFX_PWM_PIN_INVERTED 0x80 #define _PRIO_APP_LOWEST 7 nrfx_pwm_config_t config0 = { .irq_priority = _PRIO_APP_LOWEST, .base_clock = NRF_PWM_CLK_1MHz, //default value .count_mode = NRF_PWM_MODE_UP, .top_value = 5000, //default vaule .load_mode = NRF_PWM_LOAD_INDIVIDUAL, .step_mode = NRF_PWM_STEP_AUTO }; rt_err_t result = RT_EOK; if (device->pwm_src_clk == 1000000) { config0.base_clock = NRF_PWM_CLK_1MHz; } else if (device->pwm_src_clk == 2000000) { config0.base_clock = NRF_PWM_CLK_2MHz; } else if (device->pwm_src_clk == 8000000) { config0.base_clock = NRF_PWM_CLK_8MHz; } else { config0.base_clock = NRF_PWM_CLK_1MHz; } if (device->channel & 0x01) { config0.output_pins[0] = device->channel_0_pin | NRFX_PWM_PIN_INVERTED; } if (device->channel & 0x02) { config0.output_pins[1] = device->channel_1_pin | NRFX_PWM_PIN_INVERTED; } if (device->channel & 0x04) { config0.output_pins[2] = device->channel_2_pin | NRFX_PWM_PIN_INVERTED; } if (device->channel & 0x08) { config0.output_pins[3] = device->channel_3_pin | NRFX_PWM_PIN_INVERTED; } device->m_demo1_seq.values.p_individual = &device->m_demo1_seq_values; device->m_demo1_seq.length = NRF_PWM_VALUES_LENGTH(device->m_demo1_seq_values), nrfx_pwm_init(device->pwm_handle, &config0, NULL, NULL); return result; } static void pwm_get_channel(void) { #ifdef BSP_USING_PWM0_CH0 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 0; mcu_pwm_obj[PWM0_INDEX].channel_0_pin = BSP_USING_PWM0_CH0; #endif #ifdef BSP_USING_PWM0_CH1 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 1; mcu_pwm_obj[PWM0_INDEX].channel_1_pin = BSP_USING_PWM0_CH1; #endif #ifdef BSP_USING_PWM0_CH2 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 2; mcu_pwm_obj[PWM0_INDEX].channel_2_pin = BSP_USING_PWM0_CH2; #endif #ifdef BSP_USING_PWM0_CH3 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 3; mcu_pwm_obj[PWM0_INDEX].channel_3_pin = BSP_USING_PWM0_CH3; #endif #ifdef BSP_USING_PWM1_CH0 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 0; mcu_pwm_obj[PWM1_INDEX].channel_0_pin = BSP_USING_PWM1_CH0; #endif #ifdef BSP_USING_PWM1_CH1 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 1; mcu_pwm_obj[PWM1_INDEX].channel_1_pin = BSP_USING_PWM1_CH1; #endif #ifdef BSP_USING_PWM1_CH2 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 2; mcu_pwm_obj[PWM1_INDEX].channel_2_pin = BSP_USING_PWM1_CH2; #endif #ifdef BSP_USING_PWM1_CH3 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 3; mcu_pwm_obj[PWM1_INDEX].channel_3_pin = BSP_USING_PWM1_CH3; #endif #ifdef BSP_USING_PWM2_CH0 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 0; mcu_pwm_obj[PWM2_INDEX].channel_0_pin = BSP_USING_PWM2_CH0; #endif #ifdef BSP_USING_PWM2_CH1 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 1; mcu_pwm_obj[PWM2_INDEX].channel_1_pin = BSP_USING_PWM2_CH1; #endif #ifdef BSP_USING_PWM2_CH2 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 2; mcu_pwm_obj[PWM2_INDEX].channel_2_pin = BSP_USING_PWM2_CH2; #endif #ifdef BSP_USING_PWM2_CH3 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 3; mcu_pwm_obj[PWM2_INDEX].channel_3_pin = BSP_USING_PWM2_CH3; #endif #ifdef BSP_USING_PWM3_CH0 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 0; mcu_pwm_obj[PWM3_INDEX].channel_0_pin = BSP_USING_PWM3_CH0; #endif #ifdef BSP_USING_PWM3_CH1 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 1; mcu_pwm_obj[PWM3_INDEX].channel_1_pin = BSP_USING_PWM3_CH1; #endif #ifdef BSP_USING_PWM3_CH2 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 2; mcu_pwm_obj[PWM3_INDEX].channel_2_pin = BSP_USING_PWM3_CH2; #endif #ifdef BSP_USING_PWM3_CH3 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 3; mcu_pwm_obj[PWM3_INDEX].channel_3_pin = BSP_USING_PWM3_CH3; #endif } static int mcu_pwm_init(void) { int i = 0; int result = RT_EOK; pwm_get_channel(); for (i = 0; i < sizeof(mcu_pwm_obj) / sizeof(mcu_pwm_obj[0]); i++) { /* pwm init */ if (mcu_hw_pwm_init(&mcu_pwm_obj[i]) != RT_EOK) { rt_kprintf("\r\n %s init failed", mcu_pwm_obj[i].name); result = -RT_ERROR; goto __exit; } else { rt_kprintf("\r\n %s init success", mcu_pwm_obj[i].name); /* register pwm device */ if (rt_device_pwm_register(&mcu_pwm_obj[i].pwm_device, mcu_pwm_obj[i].name, &drv_ops, &mcu_pwm_obj[i].pwm_handle) == RT_EOK) { rt_kprintf("\r\n %s register success", mcu_pwm_obj[i].name); } else { rt_kprintf("\r\n %s register failed", mcu_pwm_obj[i].name); result = -RT_ERROR; } } } __exit: return result; } INIT_DEVICE_EXPORT(mcu_pwm_init); /* test example */ #define PWM_DEV_NAME "pwm0" /* PWM name*/ #define PWM_DEV_CHANNEL 15 /* PWM channel */ struct rt_device_pwm *pwm_dev; static int pwm_led_sample(int argc, char *argv[]) { rt_uint32_t period, pulse, dir; period = 50000; /* 50ms*/ dir = 1; pulse = 0; pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); if (pwm_dev == RT_NULL) { rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME); return RT_ERROR; } rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); while (1) { rt_thread_mdelay(50); if (dir) { pulse += 500; } else { pulse -= 500; } if (pulse >= period) { dir = 0; } if (0 == pulse) { dir = 1; } rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } } MSH_CMD_EXPORT(pwm_led_sample, pwm sample); #endif