]> git.donarmstrong.com Git - qmk_firmware.git/blob - tool/mbed/mbed-sdk/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC408X/TARGET_LPC4088_DM/pwmout_api.c
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[qmk_firmware.git] / tool / mbed / mbed-sdk / libraries / mbed / targets / hal / TARGET_NXP / TARGET_LPC408X / TARGET_LPC4088_DM / 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 "mbed_assert.h"
17 #include "pwmout_api.h"
18 #include "cmsis.h"
19 #include "pinmap.h"
20
21 #define TCR_CNT_EN       0x00000001
22 #define TCR_RESET        0x00000002
23
24 //  PORT ID, PWM ID, Pin function
25 static const PinMap PinMap_PWM[] = {
26     {P1_5,  PWM0_3, 3},
27     {P1_20, PWM1_2, 2},
28     {P1_23, PWM1_4, 2},
29     {P1_24, PWM1_5, 2},
30     {NC, NC, 0}
31 };
32
33 static const uint32_t PWM_mr_offset[7] = {
34     0x18, 0x1C, 0x20, 0x24, 0x40, 0x44, 0x48
35 };
36
37 #define TCR_PWM_EN       0x00000008
38 static unsigned int pwm_clock_mhz;
39
40 void pwmout_init(pwmout_t* obj, PinName pin) {
41     // determine the channel
42     PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
43     MBED_ASSERT(pwm != (PWMName)NC);
44
45     obj->channel = pwm;
46     obj->pwm = LPC_PWM0;
47
48     if (obj->channel > 6) { // PWM1 is used if pwm > 6
49       obj->channel -= 6;
50       obj->pwm = LPC_PWM1;
51     }
52
53     obj->MR = (__IO uint32_t *)((uint32_t)obj->pwm + PWM_mr_offset[obj->channel]);
54
55     // ensure the power is on
56     if (obj->pwm == LPC_PWM0) {
57         LPC_SC->PCONP |= 1 << 5;
58     } else {
59         LPC_SC->PCONP |= 1 << 6;
60     }
61
62     obj->pwm->PR = 0;                     // no pre-scale
63
64     // ensure single PWM mode
65     obj->pwm->MCR = 1 << 1; // reset TC on match 0
66
67     // enable the specific PWM output
68     obj->pwm->PCR |= 1 << (8 + obj->channel);
69
70     pwm_clock_mhz = PeripheralClock / 1000000;
71
72     // default to 20ms: standard for servos, and fine for e.g. brightness control
73     pwmout_period_ms(obj, 20);
74     pwmout_write    (obj, 0);
75
76     // Wire pinout
77     pinmap_pinout(pin, PinMap_PWM);
78 }
79
80 void pwmout_free(pwmout_t* obj) {
81     // [TODO]
82 }
83
84 void pwmout_write(pwmout_t* obj, float value) {
85     if (value < 0.0f) {
86         value = 0.0;
87     } else if (value > 1.0f) {
88         value = 1.0;
89     }
90
91     // set channel match to percentage
92     uint32_t v = (uint32_t)((float)(obj->pwm->MR0) * value);
93
94     // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
95     if (v == obj->pwm->MR0) {
96         v++;
97     }
98
99     *obj->MR = v;
100
101     // accept on next period start
102     obj->pwm->LER |= 1 << obj->channel;
103 }
104
105 float pwmout_read(pwmout_t* obj) {
106     float v = (float)(*obj->MR) / (float)(obj->pwm->MR0);
107     return (v > 1.0f) ? (1.0f) : (v);
108 }
109
110 void pwmout_period(pwmout_t* obj, float seconds) {
111     pwmout_period_us(obj, seconds * 1000000.0f);
112 }
113
114 void pwmout_period_ms(pwmout_t* obj, int ms) {
115     pwmout_period_us(obj, ms * 1000);
116 }
117
118 // Set the PWM period, keeping the duty cycle the same.
119 void pwmout_period_us(pwmout_t* obj, int us) {
120     // calculate number of ticks
121     uint32_t ticks = pwm_clock_mhz * us;
122
123     // set reset
124     obj->pwm->TCR = TCR_RESET;
125
126     // set the global match register
127     obj->pwm->MR0 = ticks;
128
129     // Scale the pulse width to preserve the duty ratio
130     if (obj->pwm->MR0 > 0) {
131         *obj->MR = (*obj->MR * ticks) / obj->pwm->MR0;
132     }
133
134     // set the channel latch to update value at next period start
135     obj->pwm->LER |= 1 << 0;
136
137     // enable counter and pwm, clear reset
138     obj->pwm->TCR = TCR_CNT_EN | TCR_PWM_EN;
139 }
140
141 void pwmout_pulsewidth(pwmout_t* obj, float seconds) {
142     pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
143 }
144
145 void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) {
146     pwmout_pulsewidth_us(obj, ms * 1000);
147 }
148
149 void pwmout_pulsewidth_us(pwmout_t* obj, int us) {
150     // calculate number of ticks
151     uint32_t v = pwm_clock_mhz * us;
152
153     // workaround for PWM1[1] - Never make it equal MR0, else we get 1 cycle dropout
154     if (v == obj->pwm->MR0) {
155         v++;
156     }
157
158     // set the match register value
159     *obj->MR = v;
160
161     // set the channel latch to update value at next period start
162     obj->pwm->LER |= 1 << obj->channel;
163 }