mbed-os

Fork of mbed-os by erkin yucel

Committer:
elessair
Date:
Sun Oct 23 15:10:02 2016 +0000
Revision:
0:f269e3021894
Initial commit

Who changed what in which revision?

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