1 /* mbed Microcontroller Library
2 * Copyright (c) 2006-2013 ARM Limited
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include "pwmout_api.h"
24 #define TCR_CNT_EN 0x00000001
25 #define TCR_RESET 0x00000002
27 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
28 // PORT ID, PWM ID, Pin function
29 static const PinMap PinMap_PWM
[] = {
47 __IO
uint32_t *PWM_MATCH
[] = {
57 #define TCR_PWM_EN 0x00000008
59 static unsigned int pwm_clock_mhz
;
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
67 static const PinMap PinMap_PWM
[] = {
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 */
74 {P0_21
, PWM_4
, 1}, /* MR0 */
75 {P0_22
, PWM_5
, 2}, {P1_23
, PWM_5
, 1}, /* MR1 */
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 */
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 */
95 static timer_mr pwm_timer_map
[11] = {
96 {0, 0}, {0, 1}, {0, 2},
98 {2, 0}, {2, 1}, {2, 2},
99 {3, 0}, {3, 1}, {3, 2},
102 static LPC_CTxxBx_Type
*Timers
[4] = {
103 LPC_CT16B0
, LPC_CT16B1
,
104 LPC_CT32B0
, LPC_CT32B1
107 static unsigned int pwm_clock_mhz
;
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");
116 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
118 obj
->MR
= PWM_MATCH
[pwm
];
120 // ensure the power is on
121 LPC_SC
->PCONP
|= 1 << 6;
123 // ensure clock to /4
124 LPC_SC
->PCLKSEL0
&= ~(0x3 << 12); // pclk = /4
125 LPC_PWM1
->PR
= 0; // no pre-scale
127 // ensure single PWM mode
128 LPC_PWM1
->MCR
= 1 << 1; // reset TC on match 0
130 // enable the specific PWM output
131 LPC_PWM1
->PCR
|= 1 << (8 + pwm
);
133 pwm_clock_mhz
= SystemCoreClock
/ 4000000;
135 #elif defined(TARGET_LPC11U24)
139 timer_mr tid
= pwm_timer_map
[pwm
];
140 LPC_CTxxBx_Type
*timer
= Timers
[tid
.timer
];
145 // Power the correspondent timer
146 LPC_SYSCON
->SYSAHBCLKCTRL
|= 1 << (tid
.timer
+ 7);
148 /* Enable PWM function */
149 timer
->PWMC
= (1 << 3)|(1 << 2)|(1 << 1)|(1 << 0);
151 /* Reset Functionality on MR3 controlling the PWM period */
152 timer
->MCR
= 1 << 10;
154 pwm_clock_mhz
= SystemCoreClock
/ 1000000;
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);
161 pinmap_pinout(pin
, PinMap_PWM
);
164 void pwmout_free(pwmout_t
* obj
) {
168 void pwmout_write(pwmout_t
* obj
, float value
) {
171 } else if (value
> 1.0) {
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
);
179 // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
180 if (v
== LPC_PWM1
->MR0
) {
186 // accept on next period start
187 LPC_PWM1
->LER
|= 1 << obj
->pwm
;
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
);
194 timer
->TCR
= TCR_RESET
;
195 timer
->MR
[tid
.mr
] = t_off
;
196 timer
->TCR
= TCR_CNT_EN
;
200 float pwmout_read(pwmout_t
* obj
) {
202 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
203 v
= (float)(*obj
->MR
) / (float)(LPC_PWM1
->MR0
);
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
);
212 return (v
> 1.0) ? (1.0) : (v
);
215 void pwmout_period(pwmout_t
* obj
, float seconds
) {
216 pwmout_period_us(obj
, seconds
* 1000000.0f
);
219 void pwmout_period_ms(pwmout_t
* obj
, int ms
) {
220 pwmout_period_us(obj
, ms
* 1000);
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
;
230 LPC_PWM1
->TCR
= TCR_RESET
;
232 // set the global match register
233 LPC_PWM1
->MR0
= ticks
;
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
;
240 // set the channel latch to update value at next period start
241 LPC_PWM1
->LER
|= 1 << 0;
243 // enable counter and pwm, clear reset
244 LPC_PWM1
->TCR
= TCR_CNT_EN
| TCR_PWM_EN
;
246 #elif defined(TARGET_LPC11U24)
248 uint32_t period_ticks
= pwm_clock_mhz
* us
;
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
;
254 timer
->TCR
= TCR_RESET
;
255 timer
->MR3
= period_ticks
;
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
;
264 timer
->TCR
= TCR_CNT_EN
;
269 void pwmout_pulsewidth(pwmout_t
* obj
, float seconds
) {
270 pwmout_pulsewidth_us(obj
, seconds
* 1000000.0f
);
273 void pwmout_pulsewidth_ms(pwmout_t
* obj
, int ms
) {
274 pwmout_pulsewidth_us(obj
, ms
* 1000);
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
;
282 // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
283 if (v
== LPC_PWM1
->MR0
) {
287 // set the match register value
290 // set the channel latch to update value at next period start
291 LPC_PWM1
->LER
|= 1 << obj
->pwm
;
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
];
298 timer
->TCR
= TCR_RESET
;
299 if (t_on
> timer
->MR3
) {
300 pwmout_period_us(obj
, us
);
302 uint32_t t_off
= timer
->MR3
- t_on
;
303 timer
->MR
[tid
.mr
] = t_off
;
304 timer
->TCR
= TCR_CNT_EN
;