Update to new build system.
[clinton/Smoothieware.git] / mbed / src / vendor / NXP / capi / pwmout_api.c
1 /* mbed Microcontroller Library
2 * Copyright (c) 2006-2013 ARM Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "pwmout_api.h"
17
18 #if DEVICE_PWMOUT
19
20 #include "cmsis.h"
21 #include "pinmap.h"
22 #include "error.h"
23
24 #define TCR_CNT_EN 0x00000001
25 #define TCR_RESET 0x00000002
26
27 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
28 // PORT ID, PWM ID, Pin function
29 static const PinMap PinMap_PWM[] = {
30 {P1_18, PWM_1, 2},
31 {P1_20, PWM_2, 2},
32 {P1_21, PWM_3, 2},
33 {P1_23, PWM_4, 2},
34 {P1_24, PWM_5, 2},
35 {P1_26, PWM_6, 2},
36 {P2_0 , PWM_1, 1},
37 {P2_1 , PWM_2, 1},
38 {P2_2 , PWM_3, 1},
39 {P2_3 , PWM_4, 1},
40 {P2_4 , PWM_5, 1},
41 {P2_5 , PWM_6, 1},
42 {P3_25, PWM_2, 3},
43 {P3_26, PWM_3, 3},
44 {NC, NC, 0}
45 };
46
47 __IO uint32_t *PWM_MATCH[] = {
48 &(LPC_PWM1->MR0),
49 &(LPC_PWM1->MR1),
50 &(LPC_PWM1->MR2),
51 &(LPC_PWM1->MR3),
52 &(LPC_PWM1->MR4),
53 &(LPC_PWM1->MR5),
54 &(LPC_PWM1->MR6)
55 };
56
57 #define TCR_PWM_EN 0x00000008
58
59 static unsigned int pwm_clock_mhz;
60
61 #elif defined(TARGET_LPC11U24)
62 /* To have a PWM where we can change both the period and the duty cycle,
63 * we need an entire timer. With the following conventions:
64 * * MR3 is used for the PWM period
65 * * MR0, MR1, MR2 are used for the duty cycle
66 */
67 static const PinMap PinMap_PWM[] = {
68 /* CT16B0 */
69 {P0_8 , PWM_1, 2}, {P1_13, PWM_1, 2}, /* MR0 */
70 {P0_9 , PWM_2, 2}, {P1_14, PWM_2, 2}, /* MR1 */
71 {P0_10, PWM_3, 3}, {P1_15, PWM_3, 2}, /* MR2 */
72
73 /* CT16B1 */
74 {P0_21, PWM_4, 1}, /* MR0 */
75 {P0_22, PWM_5, 2}, {P1_23, PWM_5, 1}, /* MR1 */
76
77 /* CT32B0 */
78 {P0_18, PWM_6, 2}, {P1_24, PWM_6, 1}, /* MR0 */
79 {P0_19, PWM_7, 2}, {P1_25, PWM_7, 1}, /* MR1 */
80 {P0_1 , PWM_8, 2}, {P1_26, PWM_8, 1}, /* MR2 */
81
82 /* CT32B1 */
83 {P0_13, PWM_9 , 3}, {P1_0, PWM_9 , 1}, /* MR0 */
84 {P0_14, PWM_10, 3}, {P1_1, PWM_10, 1}, /* MR1 */
85 {P0_15, PWM_11, 3}, {P1_2, PWM_11, 1}, /* MR2 */
86
87 {NC, NC, 0}
88 };
89
90 typedef struct {
91 uint8_t timer;
92 uint8_t mr;
93 } timer_mr;
94
95 static timer_mr pwm_timer_map[11] = {
96 {0, 0}, {0, 1}, {0, 2},
97 {1, 0}, {1, 1},
98 {2, 0}, {2, 1}, {2, 2},
99 {3, 0}, {3, 1}, {3, 2},
100 };
101
102 static LPC_CTxxBx_Type *Timers[4] = {
103 LPC_CT16B0, LPC_CT16B1,
104 LPC_CT32B0, LPC_CT32B1
105 };
106
107 static unsigned int pwm_clock_mhz;
108 #endif
109
110 void pwmout_init(pwmout_t* obj, PinName pin) {
111 // determine the channel
112 PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
113 if (pwm == (uint32_t)NC)
114 error("PwmOut pin mapping failed");
115
116 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
117 obj->pwm = pwm;
118 obj->MR = PWM_MATCH[pwm];
119
120 // ensure the power is on
121 LPC_SC->PCONP |= 1 << 6;
122
123 // ensure clock to /4
124 LPC_SC->PCLKSEL0 &= ~(0x3 << 12); // pclk = /4
125 LPC_PWM1->PR = 0; // no pre-scale
126
127 // ensure single PWM mode
128 LPC_PWM1->MCR = 1 << 1; // reset TC on match 0
129
130 // enable the specific PWM output
131 LPC_PWM1->PCR |= 1 << (8 + pwm);
132
133 pwm_clock_mhz = SystemCoreClock / 4000000;
134
135 #elif defined(TARGET_LPC11U24)
136 obj->pwm = pwm;
137
138 // Timer registers
139 timer_mr tid = pwm_timer_map[pwm];
140 LPC_CTxxBx_Type *timer = Timers[tid.timer];
141
142 // Disable timer
143 timer->TCR = 0;
144
145 // Power the correspondent timer
146 LPC_SYSCON->SYSAHBCLKCTRL |= 1 << (tid.timer + 7);
147
148 /* Enable PWM function */
149 timer->PWMC = (1 << 3)|(1 << 2)|(1 << 1)|(1 << 0);
150
151 /* Reset Functionality on MR3 controlling the PWM period */
152 timer->MCR = 1 << 10;
153
154 pwm_clock_mhz = SystemCoreClock / 1000000;
155 #endif
156 // default to 20ms: standard for servos, and fine for e.g. brightness control
157 pwmout_period_ms(obj, 20);
158 pwmout_write (obj, 0);
159
160 // Wire pinout
161 pinmap_pinout(pin, PinMap_PWM);
162 }
163
164 void pwmout_free(pwmout_t* obj) {
165 // [TODO]
166 }
167
168 void pwmout_write(pwmout_t* obj, float value) {
169 if (value < 0.0) {
170 value = 0.0;
171 } else if (value > 1.0) {
172 value = 1.0;
173 }
174
175 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
176 // set channel match to percentage
177 uint32_t v = (uint32_t)((float)(LPC_PWM1->MR0) * value);
178
179 // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
180 if (v == LPC_PWM1->MR0) {
181 v++;
182 }
183
184 *obj->MR = v;
185
186 // accept on next period start
187 LPC_PWM1->LER |= 1 << obj->pwm;
188
189 #elif defined(TARGET_LPC11U24)
190 timer_mr tid = pwm_timer_map[obj->pwm];
191 LPC_CTxxBx_Type *timer = Timers[tid.timer];
192 uint32_t t_off = timer->MR3 - (uint32_t)((float)(timer->MR3) * value);
193
194 timer->TCR = TCR_RESET;
195 timer->MR[tid.mr] = t_off;
196 timer->TCR = TCR_CNT_EN;
197 #endif
198 }
199
200 float pwmout_read(pwmout_t* obj) {
201 float v;
202 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
203 v = (float)(*obj->MR) / (float)(LPC_PWM1->MR0);
204
205 #elif defined(TARGET_LPC11U24)
206 timer_mr tid = pwm_timer_map[obj->pwm];
207 LPC_CTxxBx_Type *timer = Timers[tid.timer];
208 v = (float)(timer->MR3 - timer->MR[tid.mr]) / (float)(timer->MR3);
209
210 #endif
211
212 return (v > 1.0) ? (1.0) : (v);
213 }
214
215 void pwmout_period(pwmout_t* obj, float seconds) {
216 pwmout_period_us(obj, seconds * 1000000.0f);
217 }
218
219 void pwmout_period_ms(pwmout_t* obj, int ms) {
220 pwmout_period_us(obj, ms * 1000);
221 }
222
223 // Set the PWM period, keeping the duty cycle the same.
224 void pwmout_period_us(pwmout_t* obj, int us) {
225 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
226 // calculate number of ticks
227 uint32_t ticks = pwm_clock_mhz * us;
228
229 // set reset
230 LPC_PWM1->TCR = TCR_RESET;
231
232 // set the global match register
233 LPC_PWM1->MR0 = ticks;
234
235 // Scale the pulse width to preserve the duty ratio
236 if (LPC_PWM1->MR0 > 0) {
237 *obj->MR = (*obj->MR * ticks) / LPC_PWM1->MR0;
238 }
239
240 // set the channel latch to update value at next period start
241 LPC_PWM1->LER |= 1 << 0;
242
243 // enable counter and pwm, clear reset
244 LPC_PWM1->TCR = TCR_CNT_EN | TCR_PWM_EN;
245
246 #elif defined(TARGET_LPC11U24)
247 int i = 0;
248 uint32_t period_ticks = pwm_clock_mhz * us;
249
250 timer_mr tid = pwm_timer_map[obj->pwm];
251 LPC_CTxxBx_Type *timer = Timers[tid.timer];
252 uint32_t old_period_ticks = timer->MR3;
253
254 timer->TCR = TCR_RESET;
255 timer->MR3 = period_ticks;
256
257 // Scale the pulse width to preserve the duty ratio
258 if (old_period_ticks > 0) {
259 for (i=0; i<3; i++) {
260 uint32_t t_off = period_ticks - (uint32_t)(((uint64_t)timer->MR[i] * (uint64_t)period_ticks) / (uint64_t)old_period_ticks);
261 timer->MR[i] = t_off;
262 }
263 }
264 timer->TCR = TCR_CNT_EN;
265
266 #endif
267 }
268
269 void pwmout_pulsewidth(pwmout_t* obj, float seconds) {
270 pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
271 }
272
273 void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) {
274 pwmout_pulsewidth_us(obj, ms * 1000);
275 }
276
277 void pwmout_pulsewidth_us(pwmout_t* obj, int us) {
278 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
279 // calculate number of ticks
280 uint32_t v = pwm_clock_mhz * us;
281
282 // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
283 if (v == LPC_PWM1->MR0) {
284 v++;
285 }
286
287 // set the match register value
288 *obj->MR = v;
289
290 // set the channel latch to update value at next period start
291 LPC_PWM1->LER |= 1 << obj->pwm;
292
293 #elif defined(TARGET_LPC11U24)
294 uint32_t t_on = (uint32_t)(((uint64_t)SystemCoreClock * (uint64_t)us) / (uint64_t)1000000);
295 timer_mr tid = pwm_timer_map[obj->pwm];
296 LPC_CTxxBx_Type *timer = Timers[tid.timer];
297
298 timer->TCR = TCR_RESET;
299 if (t_on > timer->MR3) {
300 pwmout_period_us(obj, us);
301 }
302 uint32_t t_off = timer->MR3 - t_on;
303 timer->MR[tid.mr] = t_off;
304 timer->TCR = TCR_CNT_EN;
305
306 #endif
307 }
308
309 #endif