/* mbed Microcontroller Library * Copyright (c) 2018 GigaDevice Semiconductor Inc. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pwmout_api.h" #include "cmsis.h" #include "pinmap.h" #include "mbed_error.h" #include "PeripheralPins.h" #define DEV_PWMOUT_APB_MASK 0x00010000U #define DEV_PWMOUT_APB1 0U #define DEV_PWMOUT_APB2 1U static uint32_t timer_get_clock(uint32_t timer_periph); static void dev_pwmout_init(pwmout_t *obj) { timer_oc_parameter_struct timer_ocintpara; timer_parameter_struct timer_initpara; MBED_ASSERT(obj); uint32_t periph = obj->pwm; switch (periph) { case TIMER0: rcu_periph_clock_enable(RCU_TIMER0); break; case TIMER1: rcu_periph_clock_enable(RCU_TIMER1); break; case TIMER2: rcu_periph_clock_enable(RCU_TIMER2); break; case TIMER3: rcu_periph_clock_enable(RCU_TIMER3); break; case TIMER4: rcu_periph_clock_enable(RCU_TIMER4); break; case TIMER7: rcu_periph_clock_enable(RCU_TIMER7); break; case TIMER8: rcu_periph_clock_enable(RCU_TIMER8); break; case TIMER9: rcu_periph_clock_enable(RCU_TIMER9); break; case TIMER10: rcu_periph_clock_enable(RCU_TIMER10); break; case TIMER11: rcu_periph_clock_enable(RCU_TIMER11); break; case TIMER12: rcu_periph_clock_enable(RCU_TIMER12); break; case TIMER13: rcu_periph_clock_enable(RCU_TIMER13); break; } /* configure TIMER base function */ timer_initpara.prescaler = 119; timer_initpara.period = 9999; timer_initpara.clockdivision = 0; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_init(obj->pwm, &timer_initpara); /* configure TIMER channel output function */ timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_ocintpara.outputstate = TIMER_CCX_ENABLE; timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE; timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_HIGH; timer_channel_output_config(obj->pwm, obj->ch, &timer_ocintpara); timer_channel_output_mode_config(obj->pwm, obj->ch, TIMER_OC_MODE_PWM0); timer_channel_output_fast_config(obj->pwm, obj->ch, TIMER_OC_FAST_DISABLE); timer_primary_output_config(obj->pwm, ENABLE); } static uint8_t dev_pwmout_apb_check(uint32_t periph) { uint8_t reval = DEV_PWMOUT_APB1; /* check peripherals belongs to APB1 or APB2 */ if (DEV_PWMOUT_APB_MASK == (periph & DEV_PWMOUT_APB_MASK)) { reval = DEV_PWMOUT_APB2; } return reval; } void pwmout_init(pwmout_t *obj, PinName pin) { MBED_ASSERT(obj); obj->pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM); MBED_ASSERT(obj->pwm != (PWMName)NC); uint32_t function = pinmap_function(pin, PinMap_PWM); MBED_ASSERT(function != (uint32_t)NC); obj->ch = GD_PIN_CHANNEL_GET(function); /* Peripheral initialization */ dev_pwmout_init(obj); /* pin function initialization */ pinmap_pinout(pin, PinMap_PWM); } void pwmout_free(pwmout_t *obj) { timer_channel_output_state_config(obj->pwm, obj->ch, TIMER_CCX_DISABLE); } void pwmout_write(pwmout_t *obj, float value) { uint16_t period; uint16_t pulse; timer_disable(obj->pwm); /* overflow protection */ if (value < (float)0.0) { value = 0.0; } else if (value > (float)1.0) { value = 1.0; } period = TIMER_CAR(obj->pwm); pulse = (uint16_t)(period * value); timer_channel_output_pulse_value_config(obj->pwm, obj->ch, pulse); timer_enable(obj->pwm); } float pwmout_read(pwmout_t *obj) { float value = 0; uint16_t period; uint16_t pulse; period = TIMER_CAR(obj->pwm); switch (obj->ch) { case TIMER_CH_0: pulse = TIMER_CH0CV(obj->pwm); break; case TIMER_CH_1: pulse = TIMER_CH1CV(obj->pwm); break; case TIMER_CH_2: pulse = TIMER_CH2CV(obj->pwm); break; case TIMER_CH_3: pulse = TIMER_CH3CV(obj->pwm); break; default: error("Error: pwm channel error! \r\n"); } /* calculated waveform duty ratio */ value = (float)(pulse) / (float)(period); if (value > (float)1.0) { value = (float)1.0; } return value; } void pwmout_period(pwmout_t *obj, float seconds) { pwmout_period_us(obj, seconds * 1000000.0f); } void pwmout_period_ms(pwmout_t *obj, int ms) { pwmout_period_us(obj, ms * 1000); } void pwmout_period_us(pwmout_t *obj, int us) { uint32_t ultemp = 0; uint32_t timer_clk = 0; uint32_t period = us - 1; uint32_t prescaler; float duty_ratio; duty_ratio = pwmout_read(obj); timer_disable(obj->pwm); timer_clk = timer_get_clock(obj->pwm); ultemp = (timer_clk / 1000000); prescaler = ultemp; obj->cnt_unit = 1; while (period > 0xFFFF) { obj->cnt_unit = obj->cnt_unit << 1; period = period >> 1; prescaler = ultemp * obj->cnt_unit; } if (prescaler > 0xFFFF) { error("Error: TIMER prescaler value is overflow \r\n"); } timer_autoreload_value_config(obj->pwm, period); timer_prescaler_config(obj->pwm, prescaler - 1, TIMER_PSC_RELOAD_NOW); ultemp = duty_ratio * us; pwmout_pulsewidth_us(obj, ultemp); timer_enable(obj->pwm); } void pwmout_pulsewidth(pwmout_t *obj, float seconds) { pwmout_pulsewidth_us(obj, seconds * 1000000.0f); } void pwmout_pulsewidth_ms(pwmout_t *obj, int ms) { pwmout_pulsewidth_us(obj, ms * 1000); } void pwmout_pulsewidth_us(pwmout_t *obj, int us) { uint32_t pulse; uint32_t period; period = TIMER_CAR(obj->pwm); pulse = us / obj->cnt_unit; if (pulse > period) { pulse = period; } timer_channel_output_pulse_value_config(obj->pwm, obj->ch, pulse); } static uint32_t timer_get_clock(uint32_t timer_periph) { uint32_t timerclk; if ((TIMER0 == timer_periph) || (TIMER7 == timer_periph) || (TIMER8 == timer_periph) || (TIMER9 == timer_periph) || (TIMER10 == timer_periph)) { /* get the current APB2 TIMER clock source */ if (RCU_APB2_CKAHB_DIV1 == (RCU_CFG0 & RCU_CFG0_APB2PSC)) { timerclk = rcu_clock_freq_get(CK_APB2); } else { timerclk = rcu_clock_freq_get(CK_APB2) * 2; } } else { /* get the current APB1 TIMER clock source */ if (RCU_APB1_CKAHB_DIV1 == (RCU_CFG0 & RCU_CFG0_APB1PSC)) { timerclk = rcu_clock_freq_get(CK_APB1); } else { timerclk = rcu_clock_freq_get(CK_APB1) * 2; } } return timerclk; } const PinMap *pwmout_pinmap() { return PinMap_PWM; }