t
Fork of mbed-dev by
Diff: targets/TARGET_Atmel/TARGET_SAM_CortexM4/pwmout_api.c
- Revision:
- 149:156823d33999
- Parent:
- 107:414e9c822e99
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_Atmel/TARGET_SAM_CortexM4/pwmout_api.c Fri Oct 28 11:17:30 2016 +0100 @@ -0,0 +1,307 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * 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 "mbed_assert.h" +#include "pwmout_api.h" + +#include "cmsis.h" +#include "tc.h" +#include "sysclk.h" +#include "PeripheralPins.h" + +extern uint8_t g_sys_init; + +/** Use TC Peripheral 0 **/ +#define TC TC0 + +static const uint32_t tc_prescalar[] = { + TC_CMR_TCCLKS_TIMER_CLOCK1, // MCK/2 + TC_CMR_TCCLKS_TIMER_CLOCK2, // MCK/8 + TC_CMR_TCCLKS_TIMER_CLOCK3, // MCK/32 + TC_CMR_TCCLKS_TIMER_CLOCK4, // MCK/128 +}; +static const uint32_t tc_prescalar_divider[] = { + 2, // MCK/2 + 8, // MCK/8 + 32, // MCK/32 + 128 // MCK/128 +}; + +uint32_t getpwmchannelid (uint32_t channel) +{ + switch (channel) { + case 0 : + return ID_TC0; + case 1 : + return ID_TC1; + case 2 : + return ID_TC2; + default : + MBED_ASSERT(false); + break; + } +} + +uint32_t getprescalarindex (uint16_t frequency) +{ + float time_period_ms; + time_period_ms = (1.0 / (float)frequency) * 1000.0; + if (time_period_ms <= 1.0) { + return 0; + } else if ((time_period_ms > 1.0) && (time_period_ms <= 4.0)) { + return 1; + } else if ((time_period_ms > 4.0) && (time_period_ms <= 16.0)) { + return 2; + } else { + return 3; + } +} + +static void setregisterabc (pwmout_t* obj) +{ + uint32_t ra, rb, rc; + /* Sanity check arguments */ + MBED_ASSERT(obj); + + /* Configure waveform frequency and duty cycle. */ + rc = (sysclk_get_peripheral_bus_hz(TC) / + tc_prescalar_divider[obj->prescalarindex] )/ + obj->waveconfig.us_frequency; + tc_write_rc(TC, obj->channel, rc); + switch (obj->ioline) { + case 0 : + ra = (100 - obj->waveconfig.us_dutycycle) * rc / 100; + if(ra <= 0) ra = 1; /*non zero value only*/ + tc_write_ra(TC, obj->channel, ra); + break; + case 1 : + rb = (100 - obj->waveconfig.us_dutycycle) * rc / 100; + if(rb <= 0) rb = 1; /*non zero value only*/ + tc_write_rb(TC, obj->channel, rb); + break; + default : + MBED_ASSERT(false); + break; + } +} + +void pwmout_inithw(pwmout_t* obj) +{ + uint32_t mode = 0; + /* Configure the PMC to enable the TC module. */ + sysclk_enable_peripheral_clock(getpwmchannelid(obj->channel)); +#if SAMG55 + /* Enable PCK output */ + pmc_disable_pck(PMC_PCK_3); + pmc_switch_pck_to_mck(PMC_PCK_3, PMC_PCK_PRES_CLK_1); + pmc_enable_pck(PMC_PCK_3); +#endif + switch (obj->ioline) { + case 0 : + mode = TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR; /* RA Compare Effect: set */ /* RC Compare Effect: clear */ + break; + case 1 : + mode = TC_CMR_BCPB_SET | TC_CMR_BCPC_CLEAR | TC_CMR_ABETRG; /* RB Compare Effect: set */ /* RC Compare Effect: clear */ /*Change external event selection from TIOB*/ + break; + default : + MBED_ASSERT(false); + break; + } + /* Disable TC TC_CHANNEL_WAVEFORM. */ + tc_stop(TC, obj->channel); + /* Init TC to waveform mode. */ + tc_init(TC, obj->channel, + /* Waveform Clock Selection */ + obj->waveconfig.ul_intclock + | TC_CMR_WAVE /* Waveform mode is enabled */ + | TC_CMR_CPCTRG /* UP mode with automatic trigger on RC Compare */ + | mode + ); +} + +/** Initialize PWM Module + * + * @param[in][out] obj The PWM object to initialize + * @return void + */ +void pwmout_init(pwmout_t* obj, PinName pin) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + uint32_t ioline = NC; + uint32_t channel = NC; + + if (g_sys_init == 0) { + sysclk_init(); + system_board_init(); + g_sys_init = 1; + } + if(pin != NC) { + pin_function(pin, pinmap_find_function(pin, PinMap_PWM)); + ioport_disable_pin(pin); + } + obj->pin = pin; + + ioline = pinmap_find_function(pin, PinMap_PWM_IO_Line); /*To find out which IO Line is associated with the pin and initialise accordingly*/ /*pinmap_find_function reused to find out iolin used*/ + MBED_ASSERT(ioline != NC); + obj->ioline = ioline; + + channel = pinmap_find_peripheral(pin, PinMap_PWM_IO_Line); /* PinMap_PWM_IO_Line contains channel number and ioline to be used*/ /*pinmap_find_peripheral function reused to find out channel number*/ + MBED_ASSERT(channel != NC); + obj->channel = channel; + obj->waveconfig.us_frequency = 500; + obj->waveconfig.us_dutycycle = 50; + + obj->prescalarindex = getprescalarindex(obj->waveconfig.us_frequency); + obj->waveconfig.ul_intclock = tc_prescalar[obj->prescalarindex]; + pwmout_inithw(obj); + + /*Set the registers a,b,c*/ + setregisterabc(obj); + + /* Enable TC TC_CHANNEL_WAVEFORM. */ + tc_start(TC, channel); +} + +/** Free the PWM Module + * + * @param[in] obj The PWM object to free + * @return void + */ +void pwmout_free(pwmout_t* obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + tc_stop(TC, obj->channel); +} + +/** Set the duty cycle of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] value New duty cycle to be set + * @return void + */ +void pwmout_write(pwmout_t* obj, float value) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + if (value < 0.0f) { + value = 0; + } else if (value > 1.0f) { + value = 1; + } + obj->waveconfig.us_dutycycle = (uint16_t)(value * 100); + tc_stop(TC, obj->channel); + + /*Set the registers a,b,c*/ + setregisterabc(obj); + + /* Enable TC TC_CHANNEL_WAVEFORM. */ + tc_start(TC, obj->channel); +} + +/** Get the duty cycle of PWM Waveform + * + * @param[in] obj The PWM object + * @return Current duty cycle + */ +float pwmout_read(pwmout_t* obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + return (obj->waveconfig.us_dutycycle / 100.0); +} + +/** Set the period of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] seconds New period in seconds + * @return void + */ +void pwmout_period(pwmout_t* obj, float seconds) +{ + pwmout_period_us(obj, seconds * 1000000.0f); +} + +/** Set the period of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] value New period in milliseconds + * @return void + */ +void pwmout_period_ms(pwmout_t* obj, int ms) +{ + pwmout_period_us(obj, ms * 1000); +} + +/** Set the period of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] us New period in microseconds + * @return void + */ +void pwmout_period_us(pwmout_t* obj, int us) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + float freq = ( 1.0 / us ) * 1000000.0; + + obj->waveconfig.us_frequency = (uint16_t) freq; + obj->prescalarindex = getprescalarindex(obj->waveconfig.us_frequency); + obj->waveconfig.ul_intclock = tc_prescalar[obj->prescalarindex]; + pwmout_inithw(obj); + + /*Set the registers a,b,c*/ + setregisterabc(obj); + + /* Enable TC TC_CHANNEL_WAVEFORM. */ + tc_start(TC, obj->channel); +} + +/** Set the pulse width of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] seconds New pulse width in seconds + * @return void + */ +void pwmout_pulsewidth(pwmout_t* obj, float seconds) +{ + pwmout_pulsewidth_us(obj, seconds * 1000000.0f); +} + +/** Set the pulse width of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] ms New pulse width in milliseconds + * @return void + */ +void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) +{ + pwmout_pulsewidth_us(obj, ms * 1000); +} + +/** Set the pulse width of PWM Waveform + * + * @param[in] obj The PWM object + * @param[in] us New pulse width in microseconds + * @return void + */ +void pwmout_pulsewidth_us(pwmout_t* obj, int us) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + float new_duty = (us / 1000000.0) * (float)obj->waveconfig.us_frequency; + pwmout_write(obj, new_duty); +}