t

Fork of mbed-dev by mbed official

Revision:
149:156823d33999
Parent:
107:414e9c822e99
diff -r 21d94c44109e -r 156823d33999 targets/TARGET_Atmel/TARGET_SAM_CortexM4/pwmout_api.c
--- /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);
+}