Timer based PWM output for MAX326XXFTHR platforms

Dependents:   MAX32620FTHR_PwmBlinky

Fork of MAX326XXFTHR_PwmOut by Jesse Marroquin

This library provides PWM output using the MAX32620/MAX32630 32-bit timers.

The mbed PwmOut API implementation uses Pulse Train peripherals.

The tables below contain the available GPIO pins that can be connected to the six 32-bit timers (TMR0-5). Timer 0 is used for the microsecond ticker API and is not available for PWM output. On the MAX32630FTHR platform, timer 5 is used by the BLE API and will not be available for PWM output if the BLE API is used.

MAX32620FTHR 32-Bit Timer PWM Mapping

TimerGPIO Port and Pin
TMR1P0_1, P0_7, P1_5, P3_1, P4_5, P5_3
TMR2P0_2, P1_0, P1_6, P2_4, P3_2, P4_0, P4_6, P5_4
TMR3P0_3, P1_1, P1_7, P2_5, P3_3, P4_7, P5_5
TMR4P0_4, P1_2, P2_6, P3_4, P5_0, P5_6
TMR5P0_5, P1_3, P2_7, P3_5, P5_1, P5_7

MAX32630FTHR 32-Bit Timer PWM Mapping

TimerGPIO Port and Pin
TMR1P3_1, P5_3
TMR2P2_4, P3_2, P4_0, P5_4
TMR3P2_5, P3_3, P5_5
TMR4P2_6, P3_4, P5_0, P5_6
TMR5P3_5, P5_1

Note GPIO P2_4, P2_5 and P2_6 are connected to onboard LEDs 1, 2 and 3.

Files at this revision

API Documentation at this revision

Comitter:
jessexm
Date:
Mon Apr 23 18:24:11 2018 +0000
Commit message:
Initial commit

Changed in this revision

MAX326XXFTHR_PwmOut.cpp Show annotated file Show diff for this revision Revisions of this file
MAX326XXFTHR_PwmOut.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r bb005bb0a24c MAX326XXFTHR_PwmOut.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX326XXFTHR_PwmOut.cpp	Mon Apr 23 18:24:11 2018 +0000
@@ -0,0 +1,358 @@
+/*******************************************************************************
+ * Copyright (C) 2018 Maxim Integrated Products, Inc., All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of Maxim Integrated
+ * Products, Inc. shall not be used except as stated in the Maxim Integrated
+ * Products, Inc. Branding Policy.
+ *
+ * The mere transfer of this software does not imply any licenses
+ * of trade secrets, proprietary technology, copyrights, patents,
+ * trademarks, maskwork rights, or any other form of intellectual
+ * property whatsoever. Maxim Integrated Products, Inc. retains all
+ * ownership rights.
+ *******************************************************************************
+ */
+
+#include "MAX326XXFTHR_PwmOut.h"
+#include "mbed_assert.h"
+#include "pinmap.h"
+#include "gpio_regs.h"
+#include "tmr_regs.h"
+
+#define PWM_TMR_PRESCALE  MXC_V_TMR_CTRL_PRESCALE_DIVIDE_BY_4
+#define PWM_TICKS_PER_US  ((SystemCoreClock / 1000000) >> PWM_TMR_PRESCALE)
+#define PWM_MAX_US        (0xFFFFFFFFUL / PWM_TICKS_PER_US)
+
+#define MXC_GPIO_OUT_MODE_FIELD_WIDTH 4
+#define MXC_GPIO_OUT_MODE_FIELD_MASK  ((uint32_t)0xFFFFFFFF >> (32 - MXC_GPIO_OUT_MODE_FIELD_WIDTH))
+#define MXC_GPIO_FUNC_SEL_FIELD_WIDTH 4
+#define MXC_GPIO_FUNC_SEL_FIELD_MASK  ((uint32_t)0xFFFFFFFF >> (32 - MXC_GPIO_FUNC_SEL_FIELD_WIDTH))
+
+static const PinMap PinMap_PWM[] = {
+#if defined(TARGET_MAX32620FTHR)
+    // Timer 0 not available; used for micro-second ticker
+    { P0_1, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P0_2, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P0_3, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P0_4, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P0_5, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P0_7, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_0, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_1, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_2, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_3, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_5, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_6, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P1_7, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_4, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_5, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_6, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_7, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_1, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_2, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_3, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_4, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_5, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P4_0, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P4_5, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P4_6, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P4_7, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_0, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_1, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_3, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_4, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_5, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_6, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_7, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+#elif defined(TARGET_MAX32630FTHR)
+    // Timer 0 not available; used for micro-second ticker
+    // Timer 5 not available if using BLE
+    { P2_4, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_5, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P2_6, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_1, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_2, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_3, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_4, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P3_5, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P4_0, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_0, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_1, MXC_BASE_TMR5, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_3, MXC_BASE_TMR1, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_4, MXC_BASE_TMR2, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_5, MXC_BASE_TMR3, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+    { P5_6, MXC_BASE_TMR4, MXC_V_GPIO_FUNC_SEL_MODE_TMR },
+#else
+    // Platform not supported
+#endif
+    { NC,   NC,   0 }
+};
+
+typedef struct {
+    mxc_tmr_regs_t *tmr;
+    uint32_t term_cnt32;
+    uint32_t pwm_cap32;
+    unsigned int port;
+    unsigned int port_pin;
+} tmr_obj_t;
+
+static tmr_obj_t tmr_obj[MXC_CFG_TMR_INSTANCES];
+
+static void tmr_handler(tmr_obj_t *obj)
+{
+    mxc_tmr_regs_t *tmr = obj->tmr;
+
+    // Assert GPIO control to have steady state low/zero percent duty-cycle
+    // Update function required to restore timer control of pin
+    if (obj->pwm_cap32 == 0) {
+        MXC_GPIO->func_sel[obj->port] &= ~(MXC_GPIO_FUNC_SEL_FIELD_MASK << (MXC_GPIO_FUNC_SEL_FIELD_WIDTH * obj->port_pin));
+    } else {
+        tmr->term_cnt32 = obj->term_cnt32;
+        tmr->pwm_cap32 = obj->pwm_cap32;
+    }
+
+    tmr->intfl = 1;
+    tmr->inten = 0;
+}
+
+static void tmr1_handler(void) { tmr_handler(&tmr_obj[1]); };
+static void tmr2_handler(void) { tmr_handler(&tmr_obj[2]); };
+static void tmr3_handler(void) { tmr_handler(&tmr_obj[3]); };
+static void tmr4_handler(void) { tmr_handler(&tmr_obj[4]); };
+static void tmr5_handler(void) { tmr_handler(&tmr_obj[5]); };
+
+void MAX326XXFTHR_PwmOut::pwmout_update(void)
+{
+    uint32_t tcnt = (pwm_period * PWM_TICKS_PER_US);
+    uint32_t tcap = (pulse_width * PWM_TICKS_PER_US);
+
+    // At init the timer will not be running
+    if (!(tmr->ctrl & MXC_F_TMR_CTRL_ENABLE0)) {
+        tmr->term_cnt32 = tcnt;
+        tmr->pwm_cap32 = tcap;
+    } else {
+        tmr_obj[tmr_idx].term_cnt32 = tcnt;
+        if (pulse_width == 0) {
+            tmr_obj[tmr_idx].pwm_cap32 = 0;
+            MXC_GPIO->out_val[tmr_obj[tmr_idx].port] &= ~(1 << tmr_obj[tmr_idx].port_pin);
+        } else if (pulse_width == pwm_period) {
+            tmr_obj[tmr_idx].pwm_cap32 = tcnt;
+        } else {
+            tmr_obj[tmr_idx].pwm_cap32 = tcap;
+        }
+
+        if ((last_pulse_width == 0) && (pulse_width != 0)) {
+            MXC_GPIO->func_sel[tmr_obj[tmr_idx].port] |= (MXC_V_GPIO_FUNC_SEL_MODE_TMR << (MXC_GPIO_FUNC_SEL_FIELD_WIDTH * tmr_obj[tmr_idx].port_pin));
+        }
+
+        last_pulse_width = pulse_width;
+
+        tmr->intfl = 1;
+        tmr->inten = 1;
+    }
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_init(PinName pin)
+{
+    int i;
+
+    // Make sure the pin is free for GPIO use
+    unsigned int port = (unsigned int)pin >> PORT_SHIFT;
+    unsigned int port_pin = (unsigned int)pin & ~(0xFFFFFFFF << PORT_SHIFT);
+    MBED_ASSERT(MXC_GPIO->free[port] & (MXC_V_GPIO_FREE_AVAILABLE << port_pin));
+
+    // Make sure the pin is not associated with a PT or TIMER
+    int pin_func = (MXC_GPIO->func_sel[port] >> (MXC_GPIO_FUNC_SEL_FIELD_WIDTH * port_pin)) & MXC_GPIO_FUNC_SEL_FIELD_MASK;
+    MBED_ASSERT(pin_func == MXC_V_GPIO_FUNC_SEL_MODE_GPIO);
+
+    // Search through PinMap to find the pin
+    for (i = 0; (PinMap_PWM[i].pin != NC) && (PinMap_PWM[i].pin != pin); i++);
+    MBED_ASSERT(PinMap_PWM[i].pin != NC);
+
+    // Check timer is available
+    tmr = (mxc_tmr_regs_t*)PinMap_PWM[i].peripheral;
+    MBED_ASSERT(!(tmr->ctrl & MXC_F_TMR_CTRL_ENABLE0));
+
+    // Prep for interrupt handler
+    tmr_idx = MXC_TMR_GET_IDX(tmr);
+    switch (tmr_idx) {
+        case 1:
+            NVIC_SetVector(TMR1_0_IRQn, (uint32_t)tmr1_handler);
+            NVIC_EnableIRQ(TMR1_0_IRQn);
+            break;
+        case 2:
+            NVIC_SetVector(TMR2_0_IRQn, (uint32_t)tmr2_handler);
+            NVIC_EnableIRQ(TMR2_0_IRQn);
+            break;
+        case 3:
+            NVIC_SetVector(TMR3_0_IRQn, (uint32_t)tmr3_handler);
+            NVIC_EnableIRQ(TMR3_0_IRQn);
+            break;
+        case 4:
+            NVIC_SetVector(TMR4_0_IRQn, (uint32_t)tmr4_handler);
+            NVIC_EnableIRQ(TMR4_0_IRQn);
+            break;
+        case 5:
+            NVIC_SetVector(TMR5_0_IRQn, (uint32_t)tmr5_handler);
+            NVIC_EnableIRQ(TMR5_0_IRQn);
+            break;
+        case 0:
+        default:
+            MBED_ASSERT(0);
+    }
+
+    tmr_obj[tmr_idx].tmr = tmr;
+    tmr_obj[tmr_idx].term_cnt32 = 0;
+    tmr_obj[tmr_idx].pwm_cap32 = 0;
+    tmr_obj[tmr_idx].port = port;
+    tmr_obj[tmr_idx].port_pin = port_pin;
+
+    this->pin = pin;
+
+    // Initial state
+    pwm_period = -1;
+    pulse_width = -1;
+    last_pulse_width = -1;
+
+    // Disable timer/clear settings
+    tmr->ctrl = 0;
+
+    // Set mode and polarity
+    tmr->ctrl = MXC_S_TMR_CTRL_MODE_PWM |
+                MXC_F_TMR_CTRL_POLARITY |
+                (PWM_TMR_PRESCALE << MXC_F_TMR_CTRL_PRESCALE_POS);
+
+    // Reset counts
+    tmr->count32 = 1;
+    tmr->term_cnt32 = 0;
+    tmr->pwm_cap32 = 0;
+
+    // Configure the pin
+    PinMode mode = (PinMode)PullNone;
+#ifdef OPEN_DRAIN_LEDS
+    if ((pin == LED1) || (pin == LED2) || (pin == LED3) || (pin == LED4)) {
+        mode = (PinMode)OpenDrain;
+        MXC_IOMAN->use_vddioh_0 |= (1 << ((8 * port) + port_pin));
+    }
+#endif
+    pin_mode(pin, mode);
+    pin_function(pin, PinMap_PWM[i].function);
+
+    // Default to 20ms: standard for servos, and fine for e.g. brightness control
+    pwmout_period_us(20000);
+    pwmout_write(0.0f);
+
+    // Set the drive mode to normal
+    MXC_SET_FIELD(&MXC_GPIO->out_mode[port],
+                  (MXC_GPIO_OUT_MODE_FIELD_MASK << (port_pin * MXC_GPIO_OUT_MODE_FIELD_WIDTH)),
+                  (MXC_V_GPIO_OUT_MODE_NORMAL << (port_pin * MXC_GPIO_OUT_MODE_FIELD_WIDTH)));
+
+    // Enable timer
+    tmr->ctrl |= MXC_F_TMR_CTRL_ENABLE0;
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_free()
+{
+    // GPIO control of pin
+    MXC_GPIO->func_sel[(unsigned int)pin >> PORT_SHIFT] &=
+        ~(MXC_GPIO_FUNC_SEL_FIELD_MASK << (MXC_GPIO_FUNC_SEL_FIELD_WIDTH *
+          (unsigned int)pin & ~(0xFFFFFFFF << PORT_SHIFT)));
+    // Disable timer
+    tmr->ctrl = 0;
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_write(float percent)
+{
+    // Saturate percent if outside of range
+    if (percent < 0.0f) {
+        percent = 0.0f;
+    } else if (percent > 1.0f) {
+        percent = 1.0f;
+    }
+
+    // Resize the pulse width to set the duty cycle
+    pwmout_pulsewidth_us((int)(percent * pwm_period));
+}
+
+float MAX326XXFTHR_PwmOut::pwmout_read()
+{
+    // Check for when pulsewidth or period equals 0
+    if((pulse_width == 0) || (pwm_period == 0)) {
+        return 0.0f;
+    }
+
+    // Return the duty cycle
+    return ((float)pulse_width / (float)pwm_period);
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_period(float seconds)
+{
+    pwmout_period_us((int)(seconds * 1000000.0f));
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_period_ms(int ms)
+{
+    pwmout_period_us(ms * 1000);
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_period_us(int us)
+{
+    // Check the range of the period
+    MBED_ASSERT((us >= 0) && (us <= (int)PWM_MAX_US));
+
+    // Set pulse width to half the period if uninitialized
+    if (pulse_width == -1) {
+        pulse_width = us / 2;
+    }
+
+    // Save the period
+    pwm_period = us;
+
+    // Update the registers
+    pwmout_update();
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_pulsewidth(float seconds)
+{
+    pwmout_pulsewidth_us((int)(seconds * 1000000.0f));
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_pulsewidth_ms(int ms)
+{
+    pwmout_pulsewidth_us(ms * 1000);
+}
+
+void MAX326XXFTHR_PwmOut::pwmout_pulsewidth_us(int us)
+{
+    // Check the range of the pulsewidth
+    MBED_ASSERT((us >= 0) && (us <= (int)PWM_MAX_US));
+
+    // Initialize period to double the pulsewidth if uninitialized
+    if (pwm_period == -1) {
+        pwm_period = 2 * us;
+    }
+
+    // Save the pulsewidth
+    pulse_width = us;
+
+    // Update the register
+    pwmout_update();
+}
diff -r 000000000000 -r bb005bb0a24c MAX326XXFTHR_PwmOut.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX326XXFTHR_PwmOut.h	Mon Apr 23 18:24:11 2018 +0000
@@ -0,0 +1,325 @@
+/* Original files PwmOut.h and pwmout_api.h contained the following copyright */
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 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.
+ */
+ /* Original files PwmOut.h and pwmout_api.h combined and modified:
+    23 April 2018 Maxim Integrated Products, Inc.
+ */
+  
+#ifndef _MAX326XXFTHR_PWMOUT_H_
+#define _MAX326XXFTHR_PWMOUT_H_
+
+#include "mbed.h"
+#include "PinNames.h"
+#include "platform/mbed_critical.h"
+#include "platform/mbed_power_mgmt.h"
+#include "tmr_regs.h"
+
+/**
+ * @brief Timer based PWM output for the MAX326XXFTHR platform
+ *
+ * @details This library provides PWM output using the MAX326XX 32-bit timers.
+ * The mbed PwmOut API implementation uses the MAX326XX Pulse Train peripherals.
+ * The table below contains the available GPIO pins that can be connected to the
+ * six 32-bit timers (TMR0-5).  Timer 0 is used for the microsecond ticker API
+ * and is not available for PWM output.  On the MAX32630FTHR platform, 
+ * timer 5 is used by the BLE API and will not be available for PWM output if 
+ * the BLE API is used.
+ *
+ * MAX32620FTHR 32-Bit Timer PWM Mapping
+ *
+ * Timer | GPIO Port and Pin                                                <br>
+ * TMR1  | P0_1, P0_7, P1_5,       P3_1,       P4_5, P5_3                   <br>
+ * TMR2  | P0_2, P1_0, P1_6, P2_4, P3_2, P4_0, P4_6, P5_4                   <br>
+ * TMR3  | P0_3  P1_1  P1_7, P2_5, P3_3,       P4_7, P5_5                   <br>
+ * TMR4  | P0_4  P1_2        P2_6, P3_4,       P5_0, P5_6                   <br>
+ * TMR5  | P0_5  P1_3        P2_7, P3_5,       P5_1, P5_7                   <br>
+ * 
+ * MAX32630FTHR 32-Bit Timer PWM Mapping
+ *
+ * Timer | GPIO Port and Pin                                                <br>
+ * TMR1  |       P3_1,              P5_3                                    <br>
+ * TMR2  | P2_4, P3_2,  P4_0,       P5_4                                    <br>
+ * TMR3  | P2_5, P3_3,              P5_5                                    <br>
+ * TMR4  | P2_6, P3_4,        P5_0, P5_6                                    <br>
+ * TMR5  |       P3_5,        P5_1                                          <br>
+ *
+ * Note that for MAX32620FTHR and MAX32630FTHR GPIOs P2_4, P2_5 and P2_6 are 
+ * connected to onboard LEDs 1, 2 and 3.
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "MAX326XXFTHR_PwmOut.h"
+ * 
+ * MAX326XXFTHR_PwmOut led[] = {
+ *     MAX326XXFTHR_PwmOut(LED1),
+ *     MAX326XXFTHR_PwmOut(LED2),
+ *     MAX326XXFTHR_PwmOut(LED3)
+ * };
+ * 
+ * int main()
+ * {
+ *     float dc;
+ *     unsigned int idx = 0;
+ * 
+ *     while (1) {
+ *         for (dc = 0.0f; dc <= 1.0f; dc += 0.01f) {
+ *             led[idx % 3].write(dc);
+ *             led[(idx + 1) % 3].write(1.0f - dc);
+ *             wait_ms(20);
+ *         }
+ *         idx++;
+ *     }
+ * }
+ * @endcode
+ */
+
+class MAX326XXFTHR_PwmOut
+{
+public:
+    
+    /** Create a PwmOut connected to the specified pin
+     *
+     *  @param pin PwmOut pin to connect to
+     */
+    MAX326XXFTHR_PwmOut(PinName pin) : _deep_sleep_locked(false) {
+        core_util_critical_section_enter();
+        pwmout_init(pin);
+        core_util_critical_section_exit();
+    }
+
+    ~MAX326XXFTHR_PwmOut() {
+        core_util_critical_section_enter();
+        unlock_deep_sleep();
+        core_util_critical_section_exit();
+    }
+
+    /** Set the ouput duty-cycle, specified as a percentage (float)
+     *
+     *  @param value A floating-point value representing the output duty-cycle,
+     *    specified as a percentage. The value should lie between
+     *    0.0f (representing on 0%) and 1.0f (representing on 100%).
+     *    Values outside this range will be saturated to 0.0f or 1.0f.
+     */
+    void write(float value) {
+        core_util_critical_section_enter();
+        lock_deep_sleep();
+        pwmout_write(value);
+        core_util_critical_section_exit();
+    }
+
+    /** Return the current output duty-cycle setting, measured as a percentage (float)
+     *
+     *  @returns
+     *    A floating-point value representing the current duty-cycle being output on the pin,
+     *    measured as a percentage. The returned value will lie between
+     *    0.0f (representing on 0%) and 1.0f (representing on 100%).
+     *
+     *  @note
+     *  This value may not match exactly the value set by a previous write().
+     */
+    float read() {
+        core_util_critical_section_enter();
+        float val = pwmout_read();
+        core_util_critical_section_exit();
+        return val;
+    }
+
+    /** Set the PWM period, specified in seconds (float), keeping the duty cycle the same.
+     *
+     *  @param seconds Change the period of a PWM signal in seconds (float) without modifying the duty cycle
+     *  @note
+     *   The resolution is currently in microseconds; periods smaller than this
+     *   will be set to zero.
+     */
+    void period(float seconds) {
+        core_util_critical_section_enter();
+        pwmout_period(seconds);
+        core_util_critical_section_exit();
+    }
+
+    /** Set the PWM period, specified in milli-seconds (int), keeping the duty cycle the same.
+     *  @param ms Change the period of a PWM signal in milli-seconds without modifying the duty cycle
+     */
+    void period_ms(int ms) {
+        core_util_critical_section_enter();
+        pwmout_period_ms(ms);
+        core_util_critical_section_exit();
+    }
+
+    /** Set the PWM period, specified in micro-seconds (int), keeping the duty cycle the same.
+     *  @param us Change the period of a PWM signal in micro-seconds without modifying the duty cycle
+     */
+    void period_us(int us) {
+        core_util_critical_section_enter();
+        pwmout_period_us(us);
+        core_util_critical_section_exit();
+    }
+
+    /** Set the PWM pulsewidth, specified in seconds (float), keeping the period the same.
+     *  @param seconds Change the pulse width of a PWM signal specified in seconds (float)
+     */
+    void pulsewidth(float seconds) {
+        core_util_critical_section_enter();
+        pwmout_pulsewidth(seconds);
+        core_util_critical_section_exit();
+    }
+
+    /** Set the PWM pulsewidth, specified in milli-seconds (int), keeping the period the same.
+     *  @param ms Change the pulse width of a PWM signal specified in milli-seconds
+     */
+    void pulsewidth_ms(int ms) {
+        core_util_critical_section_enter();
+        pwmout_pulsewidth_ms(ms);
+        core_util_critical_section_exit();
+    }
+
+    /** Set the PWM pulsewidth, specified in micro-seconds (int), keeping the period the same.
+     *  @param us Change the pulse width of a PWM signal specified in micro-seconds
+     */
+    void pulsewidth_us(int us) {
+        core_util_critical_section_enter();
+        pwmout_pulsewidth_us(us);
+        core_util_critical_section_exit();
+    }
+
+    /** A operator shorthand for write()
+     *  \sa PwmOut::write()
+     */
+    MAX326XXFTHR_PwmOut& operator= (float value) {
+        // Underlying call is thread safe
+        write(value);
+        return *this;
+    }
+
+    /** A operator shorthand for write()
+     * \sa PwmOut::write()
+     */
+    MAX326XXFTHR_PwmOut& operator= (PwmOut& rhs) {
+        // Underlying call is thread safe
+        write(rhs.read());
+        return *this;
+    }
+
+    /** An operator shorthand for read()
+     * \sa PwmOut::read()
+     */
+    operator float() {
+        // Underlying call is thread safe
+        return read();
+    }
+
+protected:
+    /** Lock deep sleep only if it is not yet locked */
+    void lock_deep_sleep() {
+        if (_deep_sleep_locked == false) {
+            sleep_manager_lock_deep_sleep();
+            _deep_sleep_locked = true;
+        }
+    }
+
+    /** Unlock deep sleep in case it is locked */
+    void unlock_deep_sleep() {
+        if (_deep_sleep_locked == true) {
+            sleep_manager_unlock_deep_sleep();
+            _deep_sleep_locked = false;
+        }
+    }
+
+    bool _deep_sleep_locked;
+
+private:
+    /** Initialize the pwm out peripheral and configure the pin
+     *
+     * @param obj The pwmout object to initialize
+     * @param pin The pwmout pin to initialize
+     */
+    void pwmout_init(PinName pin);
+
+    /** Deinitialize the pwmout object
+     *
+     * @param obj The pwmout object
+     */
+    void pwmout_free();
+
+    /** Set the output duty-cycle in range <0.0f, 1.0f>
+     *
+     * Value 0.0f represents 0 percentage, 1.0f represents 100 percent.
+     * @param obj     The pwmout object
+     * @param percent The floating-point percentage number
+     */
+    void pwmout_write(float percent);
+
+    /** Read the current float-point output duty-cycle
+     *
+     * @param obj The pwmout object
+     * @return A floating-point output duty-cycle
+     */
+    float pwmout_read();
+
+    /** Set the PWM period specified in seconds, keeping the duty cycle the same
+     *
+     * Periods smaller than microseconds (the lowest resolution) are set to zero.
+     * @param obj     The pwmout object
+     * @param seconds The floating-point seconds period
+     */
+    void pwmout_period(float seconds);
+
+    /** Set the PWM period specified in miliseconds, keeping the duty cycle the same
+     *
+     * @param obj The pwmout object
+     * @param ms  The milisecond period
+     */
+    void pwmout_period_ms(int ms);
+
+    /** Set the PWM period specified in microseconds, keeping the duty cycle the same
+     *
+     * @param obj The pwmout object
+     * @param us  The microsecond period
+     */
+    void pwmout_period_us(int us);
+
+    /** Set the PWM pulsewidth specified in seconds, keeping the period the same.
+     *
+     * @param obj     The pwmout object
+     * @param seconds The floating-point pulsewidth in seconds
+     */
+    void pwmout_pulsewidth(float seconds);
+
+    /** Set the PWM pulsewidth specified in miliseconds, keeping the period the same.
+     *
+     * @param obj The pwmout object
+     * @param ms  The floating-point pulsewidth in miliseconds
+     */
+    void pwmout_pulsewidth_ms(int ms);
+
+    /** Set the PWM pulsewidth specified in microseconds, keeping the period the same.
+     *
+     * @param obj The pwmout object
+     * @param us  The floating-point pulsewidth in microseconds
+     */
+    void pwmout_pulsewidth_us(int us);
+
+
+    PinName pin;
+    mxc_tmr_regs_t *tmr;
+    int tmr_idx;
+    int pwm_period;
+    int pulse_width;
+    int last_pulse_width;
+    void pwmout_update(void);
+};
+
+#endif