Timer based PWM output for MAX326XXFTHR platforms
Dependents: MAX32620FTHR_PwmBlinky
Fork of MAX326XXFTHR_PwmOut by
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
Timer | GPIO Port and Pin |
TMR1 | P0_1, P0_7, P1_5, P3_1, P4_5, P5_3 |
TMR2 | P0_2, P1_0, P1_6, P2_4, P3_2, P4_0, P4_6, P5_4 |
TMR3 | P0_3, P1_1, P1_7, P2_5, P3_3, P4_7, P5_5 |
TMR4 | P0_4, P1_2, P2_6, P3_4, P5_0, P5_6 |
TMR5 | P0_5, P1_3, P2_7, P3_5, P5_1, P5_7 |
MAX32630FTHR 32-Bit Timer PWM Mapping
Timer | GPIO Port and Pin |
TMR1 | P3_1, P5_3 |
TMR2 | P2_4, P3_2, P4_0, P5_4 |
TMR3 | P2_5, P3_3, P5_5 |
TMR4 | P2_6, P3_4, P5_0, P5_6 |
TMR5 | P3_5, P5_1 |
Note GPIO P2_4, P2_5 and P2_6 are connected to onboard LEDs 1, 2 and 3.
MAX326XXFTHR_PwmOut.cpp
- Committer:
- jessexm
- Date:
- 2018-04-23
- Revision:
- 0:bb005bb0a24c
File content as of revision 0:bb005bb0a24c:
/******************************************************************************* * 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(); }