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/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c@137:28723fa54344, 2016-05-23 (annotated)
- Committer:
- mbed_official
- Date:
- Mon May 23 15:15:12 2016 +0100
- Revision:
- 137:28723fa54344
- Parent:
- 108:e46d5651bd87
- Child:
- 144:ef7eb2e8f9f7
Synchronized with git revision b395dd5956dcbb5c3f4336d2d917210041741871
Full URL: https://github.com/mbedmicro/mbed/commit/b395dd5956dcbb5c3f4336d2d917210041741871/
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| bogdanm | 0:9b334a45a8ff | 1 | /* mbed Microcontroller Library |
| bogdanm | 0:9b334a45a8ff | 2 | * Copyright (c) 2006-2013 ARM Limited |
| bogdanm | 0:9b334a45a8ff | 3 | * |
| bogdanm | 0:9b334a45a8ff | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| bogdanm | 0:9b334a45a8ff | 5 | * you may not use this file except in compliance with the License. |
| bogdanm | 0:9b334a45a8ff | 6 | * You may obtain a copy of the License at |
| bogdanm | 0:9b334a45a8ff | 7 | * |
| bogdanm | 0:9b334a45a8ff | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| bogdanm | 0:9b334a45a8ff | 9 | * |
| bogdanm | 0:9b334a45a8ff | 10 | * Unless required by applicable law or agreed to in writing, software |
| bogdanm | 0:9b334a45a8ff | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| bogdanm | 0:9b334a45a8ff | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| bogdanm | 0:9b334a45a8ff | 13 | * See the License for the specific language governing permissions and |
| bogdanm | 0:9b334a45a8ff | 14 | * limitations under the License. |
| bogdanm | 0:9b334a45a8ff | 15 | */ |
| bogdanm | 0:9b334a45a8ff | 16 | #include "mbed_assert.h" |
| bogdanm | 0:9b334a45a8ff | 17 | #include "pwmout_api.h" |
| bogdanm | 0:9b334a45a8ff | 18 | #include "cmsis.h" |
| bogdanm | 0:9b334a45a8ff | 19 | #include "pinmap.h" |
| bogdanm | 0:9b334a45a8ff | 20 | #include "mbed_error.h" |
| bogdanm | 0:9b334a45a8ff | 21 | |
| bogdanm | 0:9b334a45a8ff | 22 | // Ported from LPC824 and adapted. |
| bogdanm | 0:9b334a45a8ff | 23 | |
| bogdanm | 0:9b334a45a8ff | 24 | #if DEVICE_PWMOUT |
| bogdanm | 0:9b334a45a8ff | 25 | |
| bogdanm | 0:9b334a45a8ff | 26 | #define PWM_IRQn SCT_IRQn |
| bogdanm | 0:9b334a45a8ff | 27 | |
| bogdanm | 0:9b334a45a8ff | 28 | // Bit flags for used SCT Outputs |
| bogdanm | 0:9b334a45a8ff | 29 | static unsigned char sct_used = 0; |
| bogdanm | 0:9b334a45a8ff | 30 | static int sct_inited = 0; |
| bogdanm | 0:9b334a45a8ff | 31 | |
| bogdanm | 0:9b334a45a8ff | 32 | // Find available output channel |
| bogdanm | 0:9b334a45a8ff | 33 | // Max number of PWM outputs is 4 on LPC812 |
| bogdanm | 0:9b334a45a8ff | 34 | static int get_available_sct() { |
| bogdanm | 0:9b334a45a8ff | 35 | int i; |
| bogdanm | 0:9b334a45a8ff | 36 | |
| bogdanm | 0:9b334a45a8ff | 37 | // Find available output channel 0..3 |
| bogdanm | 0:9b334a45a8ff | 38 | // Also need one Match register per channel |
| bogdanm | 0:9b334a45a8ff | 39 | for (i = 0; i < CONFIG_SCT_nOU; i++) { |
| bogdanm | 0:9b334a45a8ff | 40 | if ((sct_used & (1 << i)) == 0) |
| bogdanm | 0:9b334a45a8ff | 41 | return i; |
| bogdanm | 0:9b334a45a8ff | 42 | } |
| bogdanm | 0:9b334a45a8ff | 43 | return -1; |
| bogdanm | 0:9b334a45a8ff | 44 | } |
| bogdanm | 0:9b334a45a8ff | 45 | |
| bogdanm | 0:9b334a45a8ff | 46 | // Any Port pin may be used for PWM. |
| bogdanm | 0:9b334a45a8ff | 47 | // Max number of PWM outputs is 4 |
| bogdanm | 0:9b334a45a8ff | 48 | void pwmout_init(pwmout_t* obj, PinName pin) { |
| mbed_official | 108:e46d5651bd87 | 49 | MBED_ASSERT(pin != (PinName)NC); |
| bogdanm | 0:9b334a45a8ff | 50 | |
| bogdanm | 0:9b334a45a8ff | 51 | int sct_n = get_available_sct(); |
| bogdanm | 0:9b334a45a8ff | 52 | if (sct_n == -1) { |
| bogdanm | 0:9b334a45a8ff | 53 | error("No available SCT Output"); |
| bogdanm | 0:9b334a45a8ff | 54 | } |
| bogdanm | 0:9b334a45a8ff | 55 | |
| bogdanm | 0:9b334a45a8ff | 56 | sct_used |= (1 << sct_n); |
| bogdanm | 0:9b334a45a8ff | 57 | |
| bogdanm | 0:9b334a45a8ff | 58 | obj->pwm = (LPC_SCT_TypeDef*)LPC_SCT; |
| bogdanm | 0:9b334a45a8ff | 59 | obj->pwm_ch = sct_n; |
| bogdanm | 0:9b334a45a8ff | 60 | |
| bogdanm | 0:9b334a45a8ff | 61 | LPC_SCT_TypeDef* pwm = obj->pwm; |
| bogdanm | 0:9b334a45a8ff | 62 | |
| bogdanm | 0:9b334a45a8ff | 63 | // Init SCT on first use |
| bogdanm | 0:9b334a45a8ff | 64 | if (! sct_inited) { |
| bogdanm | 0:9b334a45a8ff | 65 | sct_inited = 1; |
| bogdanm | 0:9b334a45a8ff | 66 | |
| bogdanm | 0:9b334a45a8ff | 67 | // Enable the SCT clock |
| bogdanm | 0:9b334a45a8ff | 68 | LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8); |
| bogdanm | 0:9b334a45a8ff | 69 | |
| bogdanm | 0:9b334a45a8ff | 70 | // Clear peripheral reset the SCT: |
| bogdanm | 0:9b334a45a8ff | 71 | LPC_SYSCON->PRESETCTRL |= (1 << 8); |
| bogdanm | 0:9b334a45a8ff | 72 | |
| bogdanm | 0:9b334a45a8ff | 73 | // Two 16-bit counters, autolimit (ie reset on Match_0) |
| bogdanm | 0:9b334a45a8ff | 74 | pwm->CONFIG |= ((0x3 << 17) | 0x01); |
| bogdanm | 0:9b334a45a8ff | 75 | |
| bogdanm | 0:9b334a45a8ff | 76 | // halt and clear the counter |
| bogdanm | 0:9b334a45a8ff | 77 | pwm->CTRL_U |= (1 << 2) | (1 << 3); |
| bogdanm | 0:9b334a45a8ff | 78 | |
| bogdanm | 0:9b334a45a8ff | 79 | // System Clock (30 Mhz) -> Prescaler -> us_ticker (1 MHz) |
| bogdanm | 0:9b334a45a8ff | 80 | pwm->CTRL_U &= ~(0x7F << 5); |
| bogdanm | 0:9b334a45a8ff | 81 | pwm->CTRL_U |= (((SystemCoreClock/1000000 - 1) & 0x7F) << 5); |
| bogdanm | 0:9b334a45a8ff | 82 | |
| bogdanm | 0:9b334a45a8ff | 83 | pwm->EVENT[0].CTRL = (1 << 12) | 0; // Event_0 on Match_0 |
| bogdanm | 0:9b334a45a8ff | 84 | pwm->EVENT[0].STATE = 0xFFFFFFFF; // All states |
| bogdanm | 0:9b334a45a8ff | 85 | |
| bogdanm | 0:9b334a45a8ff | 86 | // unhalt the counter: |
| bogdanm | 0:9b334a45a8ff | 87 | // - clearing bit 2 of the CTRL register |
| bogdanm | 0:9b334a45a8ff | 88 | pwm->CTRL_U &= ~(1 << 2); |
| bogdanm | 0:9b334a45a8ff | 89 | |
| bogdanm | 0:9b334a45a8ff | 90 | } |
| bogdanm | 0:9b334a45a8ff | 91 | |
| bogdanm | 0:9b334a45a8ff | 92 | // LPC81x has only one SCT and 4 Outputs |
| bogdanm | 0:9b334a45a8ff | 93 | // LPC82x has only one SCT and 6 Outputs |
| bogdanm | 0:9b334a45a8ff | 94 | // LPC1549 has 4 SCTs and 16 Outputs |
| bogdanm | 0:9b334a45a8ff | 95 | switch(sct_n) { |
| bogdanm | 0:9b334a45a8ff | 96 | case 0: |
| bogdanm | 0:9b334a45a8ff | 97 | // SCTx_OUT0 |
| bogdanm | 0:9b334a45a8ff | 98 | LPC_SWM->PINASSIGN[6] &= ~0xFF000000; |
| bogdanm | 0:9b334a45a8ff | 99 | LPC_SWM->PINASSIGN[6] |= (pin << 24); |
| bogdanm | 0:9b334a45a8ff | 100 | break; |
| bogdanm | 0:9b334a45a8ff | 101 | case 1: |
| bogdanm | 0:9b334a45a8ff | 102 | // SCTx_OUT1 |
| bogdanm | 0:9b334a45a8ff | 103 | LPC_SWM->PINASSIGN[7] &= ~0x000000FF; |
| bogdanm | 0:9b334a45a8ff | 104 | LPC_SWM->PINASSIGN[7] |= (pin); |
| bogdanm | 0:9b334a45a8ff | 105 | break; |
| bogdanm | 0:9b334a45a8ff | 106 | case 2: |
| bogdanm | 0:9b334a45a8ff | 107 | // SCTx_OUT2 |
| bogdanm | 0:9b334a45a8ff | 108 | LPC_SWM->PINASSIGN[7] &= ~0x0000FF00; |
| bogdanm | 0:9b334a45a8ff | 109 | LPC_SWM->PINASSIGN[7] |= (pin << 8); |
| bogdanm | 0:9b334a45a8ff | 110 | break; |
| bogdanm | 0:9b334a45a8ff | 111 | case 3: |
| bogdanm | 0:9b334a45a8ff | 112 | // SCTx_OUT3 |
| bogdanm | 0:9b334a45a8ff | 113 | LPC_SWM->PINASSIGN[7] &= ~0x00FF0000; |
| bogdanm | 0:9b334a45a8ff | 114 | LPC_SWM->PINASSIGN[7] |= (pin << 16); |
| bogdanm | 0:9b334a45a8ff | 115 | break; |
| bogdanm | 0:9b334a45a8ff | 116 | default: |
| bogdanm | 0:9b334a45a8ff | 117 | break; |
| bogdanm | 0:9b334a45a8ff | 118 | } |
| bogdanm | 0:9b334a45a8ff | 119 | |
| bogdanm | 0:9b334a45a8ff | 120 | pwm->EVENT[sct_n + 1].CTRL = (1 << 12) | (sct_n + 1); // Event_n on Match_n |
| bogdanm | 0:9b334a45a8ff | 121 | pwm->EVENT[sct_n + 1].STATE = 0xFFFFFFFF; // All states |
| bogdanm | 0:9b334a45a8ff | 122 | |
| bogdanm | 0:9b334a45a8ff | 123 | pwm->OUT[sct_n].SET = (1 << 0); // All PWM channels are SET on Event_0 |
| bogdanm | 0:9b334a45a8ff | 124 | pwm->OUT[sct_n].CLR = (1 << (sct_n + 1)); // PWM ch is CLRed on Event_(ch+1) |
| bogdanm | 0:9b334a45a8ff | 125 | |
| bogdanm | 0:9b334a45a8ff | 126 | // default to 20ms: standard for servos, and fine for e.g. brightness control |
| bogdanm | 0:9b334a45a8ff | 127 | pwmout_period_ms(obj, 20); // 20ms period |
| bogdanm | 0:9b334a45a8ff | 128 | pwmout_write (obj, 0.0); // 0ms pulsewidth, dutycycle 0 |
| bogdanm | 0:9b334a45a8ff | 129 | } |
| bogdanm | 0:9b334a45a8ff | 130 | |
| bogdanm | 0:9b334a45a8ff | 131 | void pwmout_free(pwmout_t* obj) { |
| bogdanm | 0:9b334a45a8ff | 132 | // PWM channel is now free |
| bogdanm | 0:9b334a45a8ff | 133 | sct_used &= ~(1 << obj->pwm_ch); |
| bogdanm | 0:9b334a45a8ff | 134 | |
| bogdanm | 0:9b334a45a8ff | 135 | // Disable the SCT clock when all channels free |
| bogdanm | 0:9b334a45a8ff | 136 | if (sct_used == 0) { |
| bogdanm | 0:9b334a45a8ff | 137 | LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 8); |
| bogdanm | 0:9b334a45a8ff | 138 | sct_inited = 0; |
| bogdanm | 0:9b334a45a8ff | 139 | }; |
| bogdanm | 0:9b334a45a8ff | 140 | } |
| bogdanm | 0:9b334a45a8ff | 141 | |
| bogdanm | 0:9b334a45a8ff | 142 | // Set new dutycycle (0.0 .. 1.0) |
| bogdanm | 0:9b334a45a8ff | 143 | void pwmout_write(pwmout_t* obj, float value) { |
| bogdanm | 0:9b334a45a8ff | 144 | //value is new dutycycle |
| bogdanm | 0:9b334a45a8ff | 145 | if (value < 0.0f) { |
| bogdanm | 0:9b334a45a8ff | 146 | value = 0.0; |
| bogdanm | 0:9b334a45a8ff | 147 | } else if (value > 1.0f) { |
| bogdanm | 0:9b334a45a8ff | 148 | value = 1.0; |
| bogdanm | 0:9b334a45a8ff | 149 | } |
| bogdanm | 0:9b334a45a8ff | 150 | |
| bogdanm | 0:9b334a45a8ff | 151 | // Match_0 is PWM period. Compute new endtime of pulse for current channel |
| bogdanm | 0:9b334a45a8ff | 152 | uint32_t t_off = (uint32_t)((float)(obj->pwm->MATCHREL[0].U) * value); |
| bogdanm | 0:9b334a45a8ff | 153 | obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U = t_off; // New endtime |
| mbed_official | 108:e46d5651bd87 | 154 | |
| mbed_official | 137:28723fa54344 | 155 | // Clear OxRES (conflict resolution register) bit first, effect of simultaneous set and clear on output x |
| mbed_official | 137:28723fa54344 | 156 | int offset = (obj->pwm_ch * 2); |
| mbed_official | 137:28723fa54344 | 157 | obj->pwm->RES &= ~(0x3 << offset); |
| mbed_official | 137:28723fa54344 | 158 | |
| mbed_official | 108:e46d5651bd87 | 159 | if (value == 0.0f) { // duty is 0% |
| mbed_official | 137:28723fa54344 | 160 | // Clear output |
| mbed_official | 137:28723fa54344 | 161 | obj->pwm->RES |= (0x2 << offset); |
| mbed_official | 108:e46d5651bd87 | 162 | // Set CLR event to be same as SET event, makes output to be 0 (low) |
| mbed_official | 108:e46d5651bd87 | 163 | obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << 0); |
| mbed_official | 108:e46d5651bd87 | 164 | } else { |
| mbed_official | 137:28723fa54344 | 165 | // Set output |
| mbed_official | 137:28723fa54344 | 166 | obj->pwm->RES |= (0x1 << offset); |
| mbed_official | 108:e46d5651bd87 | 167 | // Use normal CLR event (current SCT ch + 1) |
| mbed_official | 108:e46d5651bd87 | 168 | obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << ((obj->pwm_ch) + 1)); |
| mbed_official | 108:e46d5651bd87 | 169 | } |
| bogdanm | 0:9b334a45a8ff | 170 | } |
| bogdanm | 0:9b334a45a8ff | 171 | |
| bogdanm | 0:9b334a45a8ff | 172 | // Get dutycycle (0.0 .. 1.0) |
| bogdanm | 0:9b334a45a8ff | 173 | float pwmout_read(pwmout_t* obj) { |
| bogdanm | 0:9b334a45a8ff | 174 | uint32_t t_period = obj->pwm->MATCHREL[0].U; |
| bogdanm | 0:9b334a45a8ff | 175 | |
| bogdanm | 0:9b334a45a8ff | 176 | //Sanity check |
| bogdanm | 0:9b334a45a8ff | 177 | if (t_period == 0) { |
| bogdanm | 0:9b334a45a8ff | 178 | return 0.0; |
| bogdanm | 0:9b334a45a8ff | 179 | }; |
| bogdanm | 0:9b334a45a8ff | 180 | |
| bogdanm | 0:9b334a45a8ff | 181 | uint32_t t_off = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U; |
| bogdanm | 0:9b334a45a8ff | 182 | float v = (float)t_off/(float)t_period; |
| bogdanm | 0:9b334a45a8ff | 183 | //Sanity check |
| bogdanm | 0:9b334a45a8ff | 184 | return (v > 1.0f) ? (1.0f) : (v); |
| bogdanm | 0:9b334a45a8ff | 185 | } |
| bogdanm | 0:9b334a45a8ff | 186 | |
| bogdanm | 0:9b334a45a8ff | 187 | // Set the PWM period, keeping the duty cycle the same (for this channel only!). |
| bogdanm | 0:9b334a45a8ff | 188 | void pwmout_period(pwmout_t* obj, float seconds){ |
| bogdanm | 0:9b334a45a8ff | 189 | pwmout_period_us(obj, seconds * 1000000.0f); |
| bogdanm | 0:9b334a45a8ff | 190 | } |
| bogdanm | 0:9b334a45a8ff | 191 | |
| bogdanm | 0:9b334a45a8ff | 192 | // Set the PWM period, keeping the duty cycle the same (for this channel only!). |
| bogdanm | 0:9b334a45a8ff | 193 | void pwmout_period_ms(pwmout_t* obj, int ms) { |
| bogdanm | 0:9b334a45a8ff | 194 | pwmout_period_us(obj, ms * 1000); |
| bogdanm | 0:9b334a45a8ff | 195 | } |
| bogdanm | 0:9b334a45a8ff | 196 | |
| bogdanm | 0:9b334a45a8ff | 197 | // Set the PWM period, keeping the duty cycle the same (for this channel only!). |
| bogdanm | 0:9b334a45a8ff | 198 | void pwmout_period_us(pwmout_t* obj, int us) { |
| bogdanm | 0:9b334a45a8ff | 199 | |
| bogdanm | 0:9b334a45a8ff | 200 | uint32_t t_period = obj->pwm->MATCHREL[0].U; // Current PWM period |
| bogdanm | 0:9b334a45a8ff | 201 | obj->pwm->MATCHREL[0].U = (uint32_t)us; // New PWM period |
| bogdanm | 0:9b334a45a8ff | 202 | |
| mbed_official | 108:e46d5651bd87 | 203 | // Sanity check |
| bogdanm | 0:9b334a45a8ff | 204 | if (t_period == 0) { |
| mbed_official | 108:e46d5651bd87 | 205 | return; |
| bogdanm | 0:9b334a45a8ff | 206 | } |
| mbed_official | 108:e46d5651bd87 | 207 | else { |
| mbed_official | 108:e46d5651bd87 | 208 | int cnt = sct_used; |
| mbed_official | 108:e46d5651bd87 | 209 | int ch = 0; |
| mbed_official | 108:e46d5651bd87 | 210 | // Update match period for exising PWM channels |
| mbed_official | 108:e46d5651bd87 | 211 | do { |
| mbed_official | 108:e46d5651bd87 | 212 | // Get current pulse width |
| mbed_official | 108:e46d5651bd87 | 213 | uint32_t t_off = obj->pwm->MATCHREL[ch + 1].U; |
| mbed_official | 108:e46d5651bd87 | 214 | // Get the duty |
| mbed_official | 108:e46d5651bd87 | 215 | float v = (float)t_off/(float)t_period; |
| mbed_official | 108:e46d5651bd87 | 216 | // Update pulse width for this channel |
| mbed_official | 108:e46d5651bd87 | 217 | obj->pwm->MATCHREL[ch + 1].U = (uint32_t)((float)us * (float)v); |
| mbed_official | 108:e46d5651bd87 | 218 | // Get next used SCT channel |
| mbed_official | 108:e46d5651bd87 | 219 | cnt = cnt >> 1; |
| mbed_official | 108:e46d5651bd87 | 220 | ch++; |
| mbed_official | 108:e46d5651bd87 | 221 | } while (cnt != 0); |
| mbed_official | 108:e46d5651bd87 | 222 | } |
| bogdanm | 0:9b334a45a8ff | 223 | } |
| bogdanm | 0:9b334a45a8ff | 224 | |
| bogdanm | 0:9b334a45a8ff | 225 | |
| bogdanm | 0:9b334a45a8ff | 226 | //Set pulsewidth |
| bogdanm | 0:9b334a45a8ff | 227 | void pwmout_pulsewidth(pwmout_t* obj, float seconds) { |
| bogdanm | 0:9b334a45a8ff | 228 | pwmout_pulsewidth_us(obj, seconds * 1000000.0f); |
| bogdanm | 0:9b334a45a8ff | 229 | } |
| bogdanm | 0:9b334a45a8ff | 230 | |
| bogdanm | 0:9b334a45a8ff | 231 | //Set pulsewidth |
| bogdanm | 0:9b334a45a8ff | 232 | void pwmout_pulsewidth_ms(pwmout_t* obj, int ms){ |
| bogdanm | 0:9b334a45a8ff | 233 | pwmout_pulsewidth_us(obj, ms * 1000); |
| bogdanm | 0:9b334a45a8ff | 234 | } |
| bogdanm | 0:9b334a45a8ff | 235 | |
| bogdanm | 0:9b334a45a8ff | 236 | //Set pulsewidth |
| bogdanm | 0:9b334a45a8ff | 237 | void pwmout_pulsewidth_us(pwmout_t* obj, int us) { |
| mbed_official | 108:e46d5651bd87 | 238 | if (us == 0) { // pulse width is 0 |
| mbed_official | 108:e46d5651bd87 | 239 | // Set CLR event to be same as SET event, makes output to be 0 (low) |
| mbed_official | 108:e46d5651bd87 | 240 | obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << 0); |
| mbed_official | 108:e46d5651bd87 | 241 | } else { |
| mbed_official | 108:e46d5651bd87 | 242 | // Use normal CLR event (current SCT ch + 1) |
| mbed_official | 108:e46d5651bd87 | 243 | obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << ((obj->pwm_ch) + 1)); |
| mbed_official | 108:e46d5651bd87 | 244 | } |
| bogdanm | 0:9b334a45a8ff | 245 | //Should add Sanity check to make sure pulsewidth < period! |
| bogdanm | 0:9b334a45a8ff | 246 | obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U = (uint32_t)us; // New endtime for this channel |
| bogdanm | 0:9b334a45a8ff | 247 | } |
| bogdanm | 0:9b334a45a8ff | 248 | |
| bogdanm | 0:9b334a45a8ff | 249 | #endif |
