Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/TARGET_Silicon_Labs/TARGET_EFM32/pwmout_api.c@149:156823d33999, 2016-10-28 (annotated)
- Committer:
- <>
- Date:
- Fri Oct 28 11:17:30 2016 +0100
- Revision:
- 149:156823d33999
- Parent:
- targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/pwmout_api.c@144:ef7eb2e8f9f7
- Child:
- 150:02e0a0aed4ec
This updates the lib to the mbed lib v128
NOTE: This release includes a restructuring of the file and directory locations and thus some
include paths in your code may need updating accordingly.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
<> | 144:ef7eb2e8f9f7 | 1 | /***************************************************************************//** |
<> | 144:ef7eb2e8f9f7 | 2 | * @file pwmout_api.c |
<> | 144:ef7eb2e8f9f7 | 3 | ******************************************************************************* |
<> | 144:ef7eb2e8f9f7 | 4 | * @section License |
<> | 144:ef7eb2e8f9f7 | 5 | * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b> |
<> | 144:ef7eb2e8f9f7 | 6 | ******************************************************************************* |
<> | 144:ef7eb2e8f9f7 | 7 | * |
<> | 144:ef7eb2e8f9f7 | 8 | * SPDX-License-Identifier: Apache-2.0 |
<> | 144:ef7eb2e8f9f7 | 9 | * |
<> | 144:ef7eb2e8f9f7 | 10 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
<> | 144:ef7eb2e8f9f7 | 11 | * not use this file except in compliance with the License. |
<> | 144:ef7eb2e8f9f7 | 12 | * You may obtain a copy of the License at |
<> | 144:ef7eb2e8f9f7 | 13 | * |
<> | 144:ef7eb2e8f9f7 | 14 | * http://www.apache.org/licenses/LICENSE-2.0 |
<> | 144:ef7eb2e8f9f7 | 15 | * |
<> | 144:ef7eb2e8f9f7 | 16 | * Unless required by applicable law or agreed to in writing, software |
<> | 144:ef7eb2e8f9f7 | 17 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
<> | 144:ef7eb2e8f9f7 | 18 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
<> | 144:ef7eb2e8f9f7 | 19 | * See the License for the specific language governing permissions and |
<> | 144:ef7eb2e8f9f7 | 20 | * limitations under the License. |
<> | 144:ef7eb2e8f9f7 | 21 | * |
<> | 144:ef7eb2e8f9f7 | 22 | ******************************************************************************/ |
<> | 144:ef7eb2e8f9f7 | 23 | |
<> | 144:ef7eb2e8f9f7 | 24 | #include "device.h" |
<> | 144:ef7eb2e8f9f7 | 25 | #include "clocking.h" |
<> | 144:ef7eb2e8f9f7 | 26 | #if DEVICE_PWMOUT |
<> | 144:ef7eb2e8f9f7 | 27 | |
<> | 144:ef7eb2e8f9f7 | 28 | #include "mbed_assert.h" |
<> | 144:ef7eb2e8f9f7 | 29 | #include "pwmout_api.h" |
<> | 144:ef7eb2e8f9f7 | 30 | #include "cmsis.h" |
<> | 144:ef7eb2e8f9f7 | 31 | #include "pinmap.h" |
<> | 144:ef7eb2e8f9f7 | 32 | #include "PeripheralPins.h" |
<> | 144:ef7eb2e8f9f7 | 33 | #include "device_peripherals.h" |
<> | 144:ef7eb2e8f9f7 | 34 | #include "sleepmodes.h" |
<> | 144:ef7eb2e8f9f7 | 35 | |
<> | 144:ef7eb2e8f9f7 | 36 | #include "em_cmu.h" |
<> | 144:ef7eb2e8f9f7 | 37 | #include "em_gpio.h" |
<> | 144:ef7eb2e8f9f7 | 38 | #include "em_timer.h" |
<> | 144:ef7eb2e8f9f7 | 39 | |
<> | 144:ef7eb2e8f9f7 | 40 | static uint32_t pwm_prescaler_div; |
<> | 144:ef7eb2e8f9f7 | 41 | |
<> | 144:ef7eb2e8f9f7 | 42 | float pwmout_calculate_duty(uint32_t width_cycles, uint32_t period_cycles); |
<> | 144:ef7eb2e8f9f7 | 43 | void pwmout_write_channel(uint32_t channel, float value); |
<> | 144:ef7eb2e8f9f7 | 44 | |
<> | 144:ef7eb2e8f9f7 | 45 | uint32_t pwmout_get_channel_route(uint32_t channel) |
<> | 144:ef7eb2e8f9f7 | 46 | { |
<> | 144:ef7eb2e8f9f7 | 47 | MBED_ASSERT(channel != (PWMName) NC); |
<> | 144:ef7eb2e8f9f7 | 48 | |
<> | 144:ef7eb2e8f9f7 | 49 | switch (channel) { |
<> | 144:ef7eb2e8f9f7 | 50 | #ifdef TIMER_ROUTEPEN_CC0PEN |
<> | 144:ef7eb2e8f9f7 | 51 | case PWM_CH0: |
<> | 144:ef7eb2e8f9f7 | 52 | return TIMER_ROUTEPEN_CC0PEN; |
<> | 144:ef7eb2e8f9f7 | 53 | break; |
<> | 144:ef7eb2e8f9f7 | 54 | case PWM_CH1: |
<> | 144:ef7eb2e8f9f7 | 55 | return TIMER_ROUTEPEN_CC1PEN; |
<> | 144:ef7eb2e8f9f7 | 56 | break; |
<> | 144:ef7eb2e8f9f7 | 57 | case PWM_CH2: |
<> | 144:ef7eb2e8f9f7 | 58 | return TIMER_ROUTEPEN_CC2PEN; |
<> | 144:ef7eb2e8f9f7 | 59 | break; |
<> | 144:ef7eb2e8f9f7 | 60 | case PWM_CH3: |
<> | 144:ef7eb2e8f9f7 | 61 | return TIMER_ROUTEPEN_CC3PEN; |
<> | 144:ef7eb2e8f9f7 | 62 | break; |
<> | 144:ef7eb2e8f9f7 | 63 | #else |
<> | 144:ef7eb2e8f9f7 | 64 | case PWM_CH0: |
<> | 144:ef7eb2e8f9f7 | 65 | return TIMER_ROUTE_CC0PEN; |
<> | 144:ef7eb2e8f9f7 | 66 | break; |
<> | 144:ef7eb2e8f9f7 | 67 | case PWM_CH1: |
<> | 144:ef7eb2e8f9f7 | 68 | return TIMER_ROUTE_CC1PEN; |
<> | 144:ef7eb2e8f9f7 | 69 | break; |
<> | 144:ef7eb2e8f9f7 | 70 | case PWM_CH2: |
<> | 144:ef7eb2e8f9f7 | 71 | return TIMER_ROUTE_CC2PEN; |
<> | 144:ef7eb2e8f9f7 | 72 | break; |
<> | 144:ef7eb2e8f9f7 | 73 | #endif |
<> | 144:ef7eb2e8f9f7 | 74 | default: |
<> | 144:ef7eb2e8f9f7 | 75 | return 0; |
<> | 144:ef7eb2e8f9f7 | 76 | } |
<> | 144:ef7eb2e8f9f7 | 77 | } |
<> | 144:ef7eb2e8f9f7 | 78 | |
<> | 144:ef7eb2e8f9f7 | 79 | /* |
<> | 144:ef7eb2e8f9f7 | 80 | * Disables the route location given. Returns true if it was enabled, false if it wasn't. |
<> | 144:ef7eb2e8f9f7 | 81 | */ |
<> | 144:ef7eb2e8f9f7 | 82 | bool pwmout_disable_channel_route(uint32_t routeloc) { |
<> | 144:ef7eb2e8f9f7 | 83 | #ifdef TIMER_ROUTEPEN_CC0PEN |
<> | 144:ef7eb2e8f9f7 | 84 | if(PWM_TIMER->ROUTEPEN & routeloc) { |
<> | 144:ef7eb2e8f9f7 | 85 | //This channel was in use, so disable |
<> | 144:ef7eb2e8f9f7 | 86 | PWM_TIMER->ROUTEPEN &= ~routeloc; |
<> | 144:ef7eb2e8f9f7 | 87 | return true; |
<> | 144:ef7eb2e8f9f7 | 88 | } |
<> | 144:ef7eb2e8f9f7 | 89 | #else |
<> | 144:ef7eb2e8f9f7 | 90 | if(PWM_TIMER->ROUTE & routeloc) { |
<> | 144:ef7eb2e8f9f7 | 91 | //This channel was in use, so disable |
<> | 144:ef7eb2e8f9f7 | 92 | PWM_TIMER->ROUTE &= ~routeloc; |
<> | 144:ef7eb2e8f9f7 | 93 | return true; |
<> | 144:ef7eb2e8f9f7 | 94 | } |
<> | 144:ef7eb2e8f9f7 | 95 | #endif |
<> | 144:ef7eb2e8f9f7 | 96 | return false; |
<> | 144:ef7eb2e8f9f7 | 97 | } |
<> | 144:ef7eb2e8f9f7 | 98 | |
<> | 144:ef7eb2e8f9f7 | 99 | /* |
<> | 144:ef7eb2e8f9f7 | 100 | * Check if a channel is active |
<> | 144:ef7eb2e8f9f7 | 101 | */ |
<> | 144:ef7eb2e8f9f7 | 102 | bool pwmout_channel_route_active(uint32_t routeloc) { |
<> | 144:ef7eb2e8f9f7 | 103 | #ifdef TIMER_ROUTEPEN_CC0PEN |
<> | 144:ef7eb2e8f9f7 | 104 | if(PWM_TIMER->ROUTEPEN & routeloc) { |
<> | 144:ef7eb2e8f9f7 | 105 | return true; |
<> | 144:ef7eb2e8f9f7 | 106 | } |
<> | 144:ef7eb2e8f9f7 | 107 | #else |
<> | 144:ef7eb2e8f9f7 | 108 | if(PWM_TIMER->ROUTE & routeloc) { |
<> | 144:ef7eb2e8f9f7 | 109 | return true; |
<> | 144:ef7eb2e8f9f7 | 110 | } |
<> | 144:ef7eb2e8f9f7 | 111 | #endif |
<> | 144:ef7eb2e8f9f7 | 112 | return false; |
<> | 144:ef7eb2e8f9f7 | 113 | } |
<> | 144:ef7eb2e8f9f7 | 114 | |
<> | 144:ef7eb2e8f9f7 | 115 | /* |
<> | 144:ef7eb2e8f9f7 | 116 | * Set the given route PEN flag |
<> | 144:ef7eb2e8f9f7 | 117 | */ |
<> | 144:ef7eb2e8f9f7 | 118 | void pwmout_set_channel_route(uint32_t routeloc) { |
<> | 144:ef7eb2e8f9f7 | 119 | #ifdef TIMER_ROUTEPEN_CC0PEN |
<> | 144:ef7eb2e8f9f7 | 120 | PWM_TIMER->ROUTEPEN |= routeloc; |
<> | 144:ef7eb2e8f9f7 | 121 | #else |
<> | 144:ef7eb2e8f9f7 | 122 | PWM_TIMER->ROUTE |= routeloc; |
<> | 144:ef7eb2e8f9f7 | 123 | #endif |
<> | 144:ef7eb2e8f9f7 | 124 | } |
<> | 144:ef7eb2e8f9f7 | 125 | |
<> | 144:ef7eb2e8f9f7 | 126 | /* |
<> | 144:ef7eb2e8f9f7 | 127 | * Check if all routes are disabled |
<> | 144:ef7eb2e8f9f7 | 128 | */ |
<> | 144:ef7eb2e8f9f7 | 129 | bool pwmout_all_inactive(void) { |
<> | 144:ef7eb2e8f9f7 | 130 | #ifdef TIMER_ROUTEPEN_CC0PEN |
<> | 144:ef7eb2e8f9f7 | 131 | if(PWM_TIMER->ROUTEPEN == _TIMER_ROUTEPEN_RESETVALUE) { |
<> | 144:ef7eb2e8f9f7 | 132 | return true; |
<> | 144:ef7eb2e8f9f7 | 133 | } |
<> | 144:ef7eb2e8f9f7 | 134 | #else |
<> | 144:ef7eb2e8f9f7 | 135 | if(PWM_TIMER->ROUTE & (TIMER_ROUTE_CC0PEN | TIMER_ROUTE_CC1PEN | TIMER_ROUTE_CC2PEN)) { |
<> | 144:ef7eb2e8f9f7 | 136 | return true; |
<> | 144:ef7eb2e8f9f7 | 137 | } |
<> | 144:ef7eb2e8f9f7 | 138 | #endif |
<> | 144:ef7eb2e8f9f7 | 139 | return false; |
<> | 144:ef7eb2e8f9f7 | 140 | } |
<> | 144:ef7eb2e8f9f7 | 141 | |
<> | 144:ef7eb2e8f9f7 | 142 | void pwmout_enable_pins(pwmout_t *obj, uint8_t enable) |
<> | 144:ef7eb2e8f9f7 | 143 | { |
<> | 144:ef7eb2e8f9f7 | 144 | if (enable) { |
<> | 144:ef7eb2e8f9f7 | 145 | pin_mode(obj->pin, PushPull); |
<> | 144:ef7eb2e8f9f7 | 146 | } else { |
<> | 144:ef7eb2e8f9f7 | 147 | // TODO_LP return PinMode to the previous state |
<> | 144:ef7eb2e8f9f7 | 148 | pin_mode(obj->pin, Disabled); |
<> | 144:ef7eb2e8f9f7 | 149 | } |
<> | 144:ef7eb2e8f9f7 | 150 | } |
<> | 144:ef7eb2e8f9f7 | 151 | |
<> | 144:ef7eb2e8f9f7 | 152 | void pwmout_enable(pwmout_t *obj, uint8_t enable){ |
<> | 144:ef7eb2e8f9f7 | 153 | if (enable) { |
<> | 144:ef7eb2e8f9f7 | 154 | // Set mode to PWM |
<> | 144:ef7eb2e8f9f7 | 155 | PWM_TIMER->CC[obj->channel].CTRL = TIMER_CC_CTRL_MODE_PWM; |
<> | 144:ef7eb2e8f9f7 | 156 | } else { |
<> | 144:ef7eb2e8f9f7 | 157 | // Set mode to default (== disabled) |
<> | 144:ef7eb2e8f9f7 | 158 | PWM_TIMER->CC[obj->channel].CTRL = _TIMER_CC_CTRL_MODE_DEFAULT; |
<> | 144:ef7eb2e8f9f7 | 159 | } |
<> | 144:ef7eb2e8f9f7 | 160 | } |
<> | 144:ef7eb2e8f9f7 | 161 | |
<> | 144:ef7eb2e8f9f7 | 162 | void pwmout_init(pwmout_t *obj, PinName pin) |
<> | 144:ef7eb2e8f9f7 | 163 | { |
<> | 144:ef7eb2e8f9f7 | 164 | obj->channel = (PWMName) pinmap_peripheral(pin, PinMap_PWM); |
<> | 144:ef7eb2e8f9f7 | 165 | obj->pin = pin; |
<> | 144:ef7eb2e8f9f7 | 166 | MBED_ASSERT(obj->channel != (PWMName) NC); |
<> | 144:ef7eb2e8f9f7 | 167 | |
<> | 144:ef7eb2e8f9f7 | 168 | /* Turn on clock */ |
<> | 144:ef7eb2e8f9f7 | 169 | CMU_ClockEnable(PWM_TIMER_CLOCK, true); |
<> | 144:ef7eb2e8f9f7 | 170 | |
<> | 144:ef7eb2e8f9f7 | 171 | /* Turn on timer */ |
<> | 144:ef7eb2e8f9f7 | 172 | if(!(PWM_TIMER->STATUS & TIMER_STATUS_RUNNING)) { |
<> | 144:ef7eb2e8f9f7 | 173 | TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; |
<> | 144:ef7eb2e8f9f7 | 174 | TIMER_Init(PWM_TIMER, &timerInit); |
<> | 144:ef7eb2e8f9f7 | 175 | } |
<> | 144:ef7eb2e8f9f7 | 176 | |
<> | 144:ef7eb2e8f9f7 | 177 | // Set route enable |
<> | 144:ef7eb2e8f9f7 | 178 | if(pwmout_channel_route_active(pwmout_get_channel_route(obj->channel))) { |
<> | 144:ef7eb2e8f9f7 | 179 | //This channel was already in use |
<> | 144:ef7eb2e8f9f7 | 180 | //TODO: gracefully handle this case. mbed_error? |
<> | 144:ef7eb2e8f9f7 | 181 | return; |
<> | 144:ef7eb2e8f9f7 | 182 | } else { |
<> | 144:ef7eb2e8f9f7 | 183 | pwmout_set_channel_route(pwmout_get_channel_route(obj->channel)); |
<> | 144:ef7eb2e8f9f7 | 184 | blockSleepMode(EM1); |
<> | 144:ef7eb2e8f9f7 | 185 | pwmout_enable(obj, true); |
<> | 144:ef7eb2e8f9f7 | 186 | pwmout_enable_pins(obj, true); |
<> | 144:ef7eb2e8f9f7 | 187 | } |
<> | 144:ef7eb2e8f9f7 | 188 | |
<> | 144:ef7eb2e8f9f7 | 189 | // Set route location |
<> | 144:ef7eb2e8f9f7 | 190 | #ifdef _TIMER_ROUTELOC0_CC0LOC_LOC0 |
<> | 144:ef7eb2e8f9f7 | 191 | switch (obj->channel) { |
<> | 144:ef7eb2e8f9f7 | 192 | case PWM_CH0: |
<> | 144:ef7eb2e8f9f7 | 193 | PWM_TIMER->ROUTELOC0 &= ~_TIMER_ROUTELOC0_CC0LOC_MASK; |
<> | 144:ef7eb2e8f9f7 | 194 | PWM_TIMER->ROUTELOC0 |= pinmap_find_function(pin,PinMap_PWM) << _TIMER_ROUTELOC0_CC0LOC_SHIFT; |
<> | 144:ef7eb2e8f9f7 | 195 | break; |
<> | 144:ef7eb2e8f9f7 | 196 | case PWM_CH1: |
<> | 144:ef7eb2e8f9f7 | 197 | PWM_TIMER->ROUTELOC0 &= ~_TIMER_ROUTELOC0_CC1LOC_MASK; |
<> | 144:ef7eb2e8f9f7 | 198 | PWM_TIMER->ROUTELOC0 |= pinmap_find_function(pin,PinMap_PWM)<< _TIMER_ROUTELOC0_CC1LOC_SHIFT; |
<> | 144:ef7eb2e8f9f7 | 199 | break; |
<> | 144:ef7eb2e8f9f7 | 200 | case PWM_CH2: |
<> | 144:ef7eb2e8f9f7 | 201 | PWM_TIMER->ROUTELOC0 &= ~_TIMER_ROUTELOC0_CC2LOC_MASK; |
<> | 144:ef7eb2e8f9f7 | 202 | PWM_TIMER->ROUTELOC0 |= pinmap_find_function(pin,PinMap_PWM) << _TIMER_ROUTELOC0_CC2LOC_SHIFT; |
<> | 144:ef7eb2e8f9f7 | 203 | break; |
<> | 144:ef7eb2e8f9f7 | 204 | case PWM_CH3: |
<> | 144:ef7eb2e8f9f7 | 205 | PWM_TIMER->ROUTELOC0 &= ~_TIMER_ROUTELOC0_CC3LOC_MASK; |
<> | 144:ef7eb2e8f9f7 | 206 | PWM_TIMER->ROUTELOC0 |= pinmap_find_function(pin,PinMap_PWM) << _TIMER_ROUTELOC0_CC3LOC_SHIFT; |
<> | 144:ef7eb2e8f9f7 | 207 | break; |
<> | 144:ef7eb2e8f9f7 | 208 | default: |
<> | 144:ef7eb2e8f9f7 | 209 | MBED_ASSERT(false); |
<> | 144:ef7eb2e8f9f7 | 210 | } |
<> | 144:ef7eb2e8f9f7 | 211 | #else |
<> | 144:ef7eb2e8f9f7 | 212 | // On P1, the route location is statically defined for the entire timer. |
<> | 144:ef7eb2e8f9f7 | 213 | PWM_TIMER->ROUTE &= ~_TIMER_ROUTE_LOCATION_MASK; |
<> | 144:ef7eb2e8f9f7 | 214 | if(pwmout_all_inactive()) { |
<> | 144:ef7eb2e8f9f7 | 215 | PWM_TIMER->ROUTE |= pinmap_find_function(pin,PinMap_PWM) << _TIMER_ROUTE_LOCATION_SHIFT; |
<> | 144:ef7eb2e8f9f7 | 216 | } else { |
<> | 144:ef7eb2e8f9f7 | 217 | MBED_ASSERT((pinmap_find_function(pin,PinMap_PWM) << _TIMER_ROUTE_LOCATION_SHIFT) == (PWM_TIMER->ROUTE & _TIMER_ROUTE_LOCATION_MASK)); |
<> | 144:ef7eb2e8f9f7 | 218 | } |
<> | 144:ef7eb2e8f9f7 | 219 | #endif |
<> | 144:ef7eb2e8f9f7 | 220 | |
<> | 144:ef7eb2e8f9f7 | 221 | // Set default 20ms frequency and 0ms pulse width |
<> | 144:ef7eb2e8f9f7 | 222 | pwmout_period(obj, 0.02); |
<> | 144:ef7eb2e8f9f7 | 223 | } |
<> | 144:ef7eb2e8f9f7 | 224 | |
<> | 144:ef7eb2e8f9f7 | 225 | void pwmout_free(pwmout_t *obj) |
<> | 144:ef7eb2e8f9f7 | 226 | { |
<> | 144:ef7eb2e8f9f7 | 227 | if(pwmout_disable_channel_route(pwmout_get_channel_route(obj->channel))) { |
<> | 144:ef7eb2e8f9f7 | 228 | //Channel was previously enabled, so do housekeeping |
<> | 144:ef7eb2e8f9f7 | 229 | unblockSleepMode(EM1); |
<> | 144:ef7eb2e8f9f7 | 230 | } else { |
<> | 144:ef7eb2e8f9f7 | 231 | //This channel was disabled already |
<> | 144:ef7eb2e8f9f7 | 232 | } |
<> | 144:ef7eb2e8f9f7 | 233 | |
<> | 144:ef7eb2e8f9f7 | 234 | pwmout_enable_pins(obj, false); |
<> | 144:ef7eb2e8f9f7 | 235 | |
<> | 144:ef7eb2e8f9f7 | 236 | if(pwmout_all_inactive()) { |
<> | 144:ef7eb2e8f9f7 | 237 | //Stop timer |
<> | 144:ef7eb2e8f9f7 | 238 | PWM_TIMER->CMD = TIMER_CMD_STOP; |
<> | 144:ef7eb2e8f9f7 | 239 | while(PWM_TIMER->STATUS & TIMER_STATUS_RUNNING); |
<> | 144:ef7eb2e8f9f7 | 240 | |
<> | 144:ef7eb2e8f9f7 | 241 | //Disable clock |
<> | 144:ef7eb2e8f9f7 | 242 | CMU_ClockEnable(PWM_TIMER_CLOCK, false); |
<> | 144:ef7eb2e8f9f7 | 243 | } |
<> | 144:ef7eb2e8f9f7 | 244 | } |
<> | 144:ef7eb2e8f9f7 | 245 | |
<> | 144:ef7eb2e8f9f7 | 246 | void pwmout_write(pwmout_t *obj, float value) |
<> | 144:ef7eb2e8f9f7 | 247 | { |
<> | 144:ef7eb2e8f9f7 | 248 | pwmout_write_channel(obj->channel, value); |
<> | 144:ef7eb2e8f9f7 | 249 | } |
<> | 144:ef7eb2e8f9f7 | 250 | |
<> | 144:ef7eb2e8f9f7 | 251 | void pwmout_write_channel(uint32_t channel, float value) { |
<> | 144:ef7eb2e8f9f7 | 252 | uint32_t width_cycles = 0; |
<> | 144:ef7eb2e8f9f7 | 253 | if (value < 0.0f) { |
<> | 144:ef7eb2e8f9f7 | 254 | width_cycles = 0; |
<> | 144:ef7eb2e8f9f7 | 255 | } else if (value >= 1.0f) { |
<> | 144:ef7eb2e8f9f7 | 256 | width_cycles = PWM_TIMER->TOPB + 1; |
<> | 144:ef7eb2e8f9f7 | 257 | } else { |
<> | 144:ef7eb2e8f9f7 | 258 | width_cycles = (uint16_t)((float)PWM_TIMER->TOPB * value); |
<> | 144:ef7eb2e8f9f7 | 259 | } |
<> | 144:ef7eb2e8f9f7 | 260 | |
<> | 144:ef7eb2e8f9f7 | 261 | TIMER_CompareBufSet(PWM_TIMER, channel, width_cycles); |
<> | 144:ef7eb2e8f9f7 | 262 | } |
<> | 144:ef7eb2e8f9f7 | 263 | |
<> | 144:ef7eb2e8f9f7 | 264 | float pwmout_read(pwmout_t *obj) |
<> | 144:ef7eb2e8f9f7 | 265 | { |
<> | 144:ef7eb2e8f9f7 | 266 | return pwmout_calculate_duty(TIMER_CaptureGet(PWM_TIMER, obj->channel), TIMER_TopGet(PWM_TIMER)); |
<> | 144:ef7eb2e8f9f7 | 267 | } |
<> | 144:ef7eb2e8f9f7 | 268 | |
<> | 144:ef7eb2e8f9f7 | 269 | float pwmout_calculate_duty(uint32_t width_cycles, uint32_t period_cycles) { |
<> | 144:ef7eb2e8f9f7 | 270 | if(width_cycles > period_cycles) { |
<> | 144:ef7eb2e8f9f7 | 271 | return 1.0f; |
<> | 144:ef7eb2e8f9f7 | 272 | } |
<> | 144:ef7eb2e8f9f7 | 273 | else if (width_cycles == 0) { |
<> | 144:ef7eb2e8f9f7 | 274 | return 0.0f; |
<> | 144:ef7eb2e8f9f7 | 275 | } |
<> | 144:ef7eb2e8f9f7 | 276 | else { |
<> | 144:ef7eb2e8f9f7 | 277 | return (float) width_cycles / (float) period_cycles; |
<> | 144:ef7eb2e8f9f7 | 278 | } |
<> | 144:ef7eb2e8f9f7 | 279 | } |
<> | 144:ef7eb2e8f9f7 | 280 | |
<> | 144:ef7eb2e8f9f7 | 281 | // Set the PWM period, keeping the duty cycle the same. |
<> | 144:ef7eb2e8f9f7 | 282 | void pwmout_period(pwmout_t *obj, float seconds) |
<> | 144:ef7eb2e8f9f7 | 283 | { |
<> | 144:ef7eb2e8f9f7 | 284 | // Find the lowest prescaler divider possible. |
<> | 144:ef7eb2e8f9f7 | 285 | // This gives us max resolution for a given period |
<> | 144:ef7eb2e8f9f7 | 286 | |
<> | 144:ef7eb2e8f9f7 | 287 | //The value of the top register if prescaler is set to 0 |
<> | 144:ef7eb2e8f9f7 | 288 | uint32_t cycles = (uint32_t)REFERENCE_FREQUENCY * seconds; |
<> | 144:ef7eb2e8f9f7 | 289 | pwm_prescaler_div = 0; |
<> | 144:ef7eb2e8f9f7 | 290 | |
<> | 144:ef7eb2e8f9f7 | 291 | //The top register is only 16 bits, so we keep dividing till we are below 0xFFFF |
<> | 144:ef7eb2e8f9f7 | 292 | while (cycles > 0xFFFF) { |
<> | 144:ef7eb2e8f9f7 | 293 | cycles /= 2; |
<> | 144:ef7eb2e8f9f7 | 294 | pwm_prescaler_div++; |
<> | 144:ef7eb2e8f9f7 | 295 | |
<> | 144:ef7eb2e8f9f7 | 296 | //Max pwm_prescaler_div supported is 10 |
<> | 144:ef7eb2e8f9f7 | 297 | if (pwm_prescaler_div > 10) { |
<> | 144:ef7eb2e8f9f7 | 298 | pwm_prescaler_div = 10; |
<> | 144:ef7eb2e8f9f7 | 299 | cycles = 0xFFFF; //Set it to max possible value; |
<> | 144:ef7eb2e8f9f7 | 300 | break; |
<> | 144:ef7eb2e8f9f7 | 301 | } |
<> | 144:ef7eb2e8f9f7 | 302 | } |
<> | 144:ef7eb2e8f9f7 | 303 | |
<> | 144:ef7eb2e8f9f7 | 304 | //Check if anything changed |
<> | 144:ef7eb2e8f9f7 | 305 | if(((PWM_TIMER->CTRL & ~_TIMER_CTRL_PRESC_MASK) == (pwm_prescaler_div << _TIMER_CTRL_PRESC_SHIFT)) && (TIMER_TopGet(PWM_TIMER) == cycles)) return; |
<> | 144:ef7eb2e8f9f7 | 306 | |
<> | 144:ef7eb2e8f9f7 | 307 | //Save previous period for recalculation of duty cycles |
<> | 144:ef7eb2e8f9f7 | 308 | uint32_t previous_period_cycles = PWM_TIMER->TOPB; |
<> | 144:ef7eb2e8f9f7 | 309 | |
<> | 144:ef7eb2e8f9f7 | 310 | //Set prescaler |
<> | 144:ef7eb2e8f9f7 | 311 | PWM_TIMER->CTRL = (PWM_TIMER->CTRL & ~_TIMER_CTRL_PRESC_MASK) | (pwm_prescaler_div << _TIMER_CTRL_PRESC_SHIFT); |
<> | 144:ef7eb2e8f9f7 | 312 | |
<> | 144:ef7eb2e8f9f7 | 313 | //Set Top Value, which controls the PWM period |
<> | 144:ef7eb2e8f9f7 | 314 | TIMER_TopBufSet(PWM_TIMER, cycles); |
<> | 144:ef7eb2e8f9f7 | 315 | |
<> | 144:ef7eb2e8f9f7 | 316 | //For each active channel, re-calculate the compare value |
<> | 144:ef7eb2e8f9f7 | 317 | uint32_t channel = 0; |
<> | 144:ef7eb2e8f9f7 | 318 | while(pwmout_get_channel_route(channel) != 0) { |
<> | 144:ef7eb2e8f9f7 | 319 | if(pwmout_channel_route_active(channel)) { |
<> | 144:ef7eb2e8f9f7 | 320 | //recalc and reset compare value |
<> | 144:ef7eb2e8f9f7 | 321 | pwmout_write_channel(channel, pwmout_calculate_duty(PWM_TIMER->CC[channel].CCVB, previous_period_cycles)); |
<> | 144:ef7eb2e8f9f7 | 322 | } |
<> | 144:ef7eb2e8f9f7 | 323 | channel++; |
<> | 144:ef7eb2e8f9f7 | 324 | } |
<> | 144:ef7eb2e8f9f7 | 325 | } |
<> | 144:ef7eb2e8f9f7 | 326 | |
<> | 144:ef7eb2e8f9f7 | 327 | void pwmout_period_ms(pwmout_t *obj, int ms) |
<> | 144:ef7eb2e8f9f7 | 328 | { |
<> | 144:ef7eb2e8f9f7 | 329 | pwmout_period(obj, ms / 1000.0f); |
<> | 144:ef7eb2e8f9f7 | 330 | } |
<> | 144:ef7eb2e8f9f7 | 331 | |
<> | 144:ef7eb2e8f9f7 | 332 | void pwmout_period_us(pwmout_t *obj, int us) |
<> | 144:ef7eb2e8f9f7 | 333 | { |
<> | 144:ef7eb2e8f9f7 | 334 | pwmout_period(obj, us / 1000000.0f); |
<> | 144:ef7eb2e8f9f7 | 335 | } |
<> | 144:ef7eb2e8f9f7 | 336 | |
<> | 144:ef7eb2e8f9f7 | 337 | void pwmout_pulsewidth(pwmout_t *obj, float seconds) |
<> | 144:ef7eb2e8f9f7 | 338 | { |
<> | 144:ef7eb2e8f9f7 | 339 | uint16_t width_cycles = (uint16_t) (((float) (REFERENCE_FREQUENCY >> pwm_prescaler_div)) * seconds); |
<> | 144:ef7eb2e8f9f7 | 340 | TIMER_CompareBufSet(PWM_TIMER, obj->channel, width_cycles); |
<> | 144:ef7eb2e8f9f7 | 341 | } |
<> | 144:ef7eb2e8f9f7 | 342 | |
<> | 144:ef7eb2e8f9f7 | 343 | void pwmout_pulsewidth_ms(pwmout_t *obj, int ms) |
<> | 144:ef7eb2e8f9f7 | 344 | { |
<> | 144:ef7eb2e8f9f7 | 345 | uint16_t width_cycles = (uint16_t) ((REFERENCE_FREQUENCY >> pwm_prescaler_div) * ms) / 1000; |
<> | 144:ef7eb2e8f9f7 | 346 | TIMER_CompareBufSet(PWM_TIMER, obj->channel, width_cycles); |
<> | 144:ef7eb2e8f9f7 | 347 | } |
<> | 144:ef7eb2e8f9f7 | 348 | |
<> | 144:ef7eb2e8f9f7 | 349 | void pwmout_pulsewidth_us(pwmout_t *obj, int us) |
<> | 144:ef7eb2e8f9f7 | 350 | { |
<> | 144:ef7eb2e8f9f7 | 351 | uint16_t width_cycles = (uint16_t) ((REFERENCE_FREQUENCY >> pwm_prescaler_div) * us) / 1000000; |
<> | 144:ef7eb2e8f9f7 | 352 | TIMER_CompareBufSet(PWM_TIMER, obj->channel, width_cycles); |
<> | 144:ef7eb2e8f9f7 | 353 | } |
<> | 144:ef7eb2e8f9f7 | 354 | |
<> | 144:ef7eb2e8f9f7 | 355 | #endif |