]> git.donarmstrong.com Git - qmk_firmware.git/blob - tool/mbed/mbed-sdk/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/pwmout_api.c
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[qmk_firmware.git] / tool / mbed / mbed-sdk / libraries / mbed / targets / hal / TARGET_NORDIC / TARGET_MCU_NRF51822 / pwmout_api.c
1 /* mbed Microcontroller Library
2  * Copyright (c) 2013 Nordic Semiconductor
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 #include "mbed_error.h"
21
22 #define NO_PWMS         3
23 #define TIMER_PRECISION 4 //4us ticks
24 #define TIMER_PRESCALER 6 //4us ticks  =   16Mhz/(2**6)
25 static const PinMap PinMap_PWM[] = {
26     {p0,  PWM_1, 1},
27     {p1,  PWM_1, 1},
28     {p2,  PWM_1, 1},
29     {p3,  PWM_1, 1},
30     {p4,  PWM_1, 1},
31     {p5,  PWM_1, 1},
32     {p6,  PWM_1, 1},
33     {p7,  PWM_1, 1},
34     {p8,  PWM_1, 1},
35     {p9,  PWM_1, 1},
36     {p10,  PWM_1, 1},
37     {p11,  PWM_1, 1},
38     {p12,  PWM_1, 1},
39     {p13,  PWM_1, 1},
40     {p14,  PWM_1, 1},
41     {p15,  PWM_1, 1},
42     {p16,  PWM_1, 1},
43     {p17,  PWM_1, 1},
44     {p18,  PWM_1, 1},
45     {p19,  PWM_1, 1},
46     {p20,  PWM_1, 1},
47     {p21,  PWM_1, 1},
48     {p22,  PWM_1, 1},
49     {p23,  PWM_1, 1},
50     {p24,  PWM_1, 1},
51     {p25,  PWM_1, 1},
52     {p28,  PWM_1, 1},
53     {p29,  PWM_1, 1},
54     {p30,  PWM_1, 1},
55     {NC, NC, 0}
56 };
57
58 static NRF_TIMER_Type *Timers[1] = {
59     NRF_TIMER2
60 };
61
62 uint16_t PERIOD            = 20000 / TIMER_PRECISION;  //20ms
63 uint8_t PWM_taken[NO_PWMS] = {0, 0, 0};
64 uint16_t PULSE_WIDTH[NO_PWMS] = {1, 1, 1}; //set to 1 instead of 0
65 uint16_t ACTUAL_PULSE[NO_PWMS] = {0, 0, 0};
66
67
68 /** @brief Function for handling timer 2 peripheral interrupts.
69  */
70 #ifdef __cplusplus
71 extern "C" {
72 #endif
73 void TIMER2_IRQHandler(void)
74 {
75     NRF_TIMER2->EVENTS_COMPARE[3] = 0;
76     NRF_TIMER2->CC[3]             =  PERIOD;
77
78     if (PWM_taken[0]) {
79         NRF_TIMER2->CC[0] = PULSE_WIDTH[0];
80     }
81     if (PWM_taken[1]) {
82         NRF_TIMER2->CC[1] = PULSE_WIDTH[1];
83     }
84     if (PWM_taken[2]) {
85         NRF_TIMER2->CC[2] = PULSE_WIDTH[2];
86     }
87
88     NRF_TIMER2->TASKS_START = 1;
89 }
90
91 #ifdef __cplusplus
92 }
93 #endif
94 /** @brief Function for initializing the Timer peripherals.
95  */
96 void timer_init(uint8_t pwmChoice)
97 {
98     NRF_TIMER_Type *timer = Timers[0];
99     timer->TASKS_STOP = 0;
100
101     if (pwmChoice == 0) {
102         timer->POWER     = 0;
103         timer->POWER     = 1;
104         timer->MODE      = TIMER_MODE_MODE_Timer;
105         timer->BITMODE   = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
106         timer->PRESCALER = TIMER_PRESCALER;
107         timer->CC[3]     = PERIOD;
108     }
109
110     timer->CC[pwmChoice] = PULSE_WIDTH[pwmChoice];
111
112     //high priority application interrupt
113     NVIC_SetPriority(TIMER2_IRQn, 1);
114     NVIC_EnableIRQ(TIMER2_IRQn);
115
116     timer->TASKS_START = 0x01;
117 }
118
119 /** @brief Function for initializing the GPIO Tasks/Events peripheral.
120  */
121 void gpiote_init(PinName pin, uint8_t channel_number)
122 {
123     // Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
124     NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
125                             | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
126                             | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
127                             | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
128                             | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
129     NRF_GPIO->OUTCLR = (1UL << pin);
130     // Configure GPIOTE channel 0 to toggle the PWM pin state
131     // @note Only one GPIOTE task can be connected to an output pin.
132     /* Configure channel to Pin31, not connected to the pin, and configure as a tasks that will set it to proper level */
133     NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
134                                          (31UL << GPIOTE_CONFIG_PSEL_Pos) |
135                                          (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
136     /* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
137     __NOP();
138     __NOP();
139     __NOP();
140     /* Launch the task to take the GPIOTE channel output to the desired level */
141     NRF_GPIOTE->TASKS_OUT[channel_number] = 1;
142
143     /* Finally configure the channel as the caller expects. If OUTINIT works, the channel is configured properly.
144        If it does not, the channel output inheritance sets the proper level. */
145     NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
146                                          ((uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos) |
147                                          ((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
148                                          ((uint32_t)GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos); // ((uint32_t)GPIOTE_CONFIG_OUTINIT_High <<
149                                                                                                              // GPIOTE_CONFIG_OUTINIT_Pos);//
150
151     /* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
152     __NOP();
153     __NOP();
154     __NOP();
155 }
156
157 /** @brief Function for initializing the Programmable Peripheral Interconnect peripheral.
158  */
159 static void ppi_init(uint8_t pwm)
160 {
161     //using ppi channels 0-7 (only 0-7 are available)
162     uint8_t channel_number = 2 * pwm;
163     NRF_TIMER_Type *timer  = Timers[0];
164
165     // Configure PPI channel 0 to toggle ADVERTISING_LED_PIN_NO on every TIMER1 COMPARE[0] match
166     NRF_PPI->CH[channel_number].TEP     = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
167     NRF_PPI->CH[channel_number + 1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
168     NRF_PPI->CH[channel_number].EEP     = (uint32_t)&timer->EVENTS_COMPARE[pwm];
169     NRF_PPI->CH[channel_number + 1].EEP = (uint32_t)&timer->EVENTS_COMPARE[3];
170
171     // Enable PPI channels.
172     NRF_PPI->CHEN |= (1 << channel_number) |
173                      (1 << (channel_number + 1));
174 }
175
176 void setModulation(pwmout_t *obj, uint8_t toggle, uint8_t high)
177 {
178     if (high) {
179         NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
180         if (toggle) {
181             NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
182                                             ((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
183         } else {
184             NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
185             NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos);
186         }
187     } else {
188         NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
189
190         if (toggle) {
191             NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
192                                             ((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
193         } else {
194             NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
195             NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
196         }
197     }
198 }
199
200 void pwmout_init(pwmout_t *obj, PinName pin)
201 {
202     // determine the channel
203     uint8_t pwmOutSuccess = 0;
204     PWMName pwm           = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
205
206     MBED_ASSERT(pwm != (PWMName)NC);
207
208     if (PWM_taken[(uint8_t)pwm]) {
209         for (uint8_t i = 1; !pwmOutSuccess && (i<NO_PWMS); i++) {
210             if (!PWM_taken[i]) {
211                 pwm           = (PWMName)i;
212                 PWM_taken[i]  = 1;
213                 pwmOutSuccess = 1;
214             }
215         }
216     } else {
217         pwmOutSuccess           = 1;
218         PWM_taken[(uint8_t)pwm] = 1;
219     }
220
221     if (!pwmOutSuccess) {
222         error("PwmOut pin mapping failed. All available PWM channels are in use.");
223     }
224
225     obj->pwm = pwm;
226     obj->pin = pin;
227
228     gpiote_init(pin, (uint8_t)pwm);
229     ppi_init((uint8_t)pwm);
230
231     if (pwm == 0) {
232         NRF_POWER->TASKS_CONSTLAT = 1;
233     }
234
235     timer_init((uint8_t)pwm);
236
237     //default to 20ms: standard for servos, and fine for e.g. brightness control
238     pwmout_period_ms(obj, 20);
239     pwmout_write    (obj, 0);
240 }
241
242 void pwmout_free(pwmout_t *obj)
243 {
244     MBED_ASSERT(obj->pwm != (PWMName)NC);
245     PWM_taken[obj->pwm] = 0;
246     pwmout_write(obj, 0);
247 }
248
249 void pwmout_write(pwmout_t *obj, float value)
250 {
251     uint16_t oldPulseWidth;
252
253     NRF_TIMER2->EVENTS_COMPARE[3] = 0;
254     NRF_TIMER2->TASKS_STOP        = 1;
255
256     if (value < 0.0f) {
257         value = 0.0;
258     } else if (value > 1.0f) {
259         value = 1.0;
260     }
261
262     oldPulseWidth          = ACTUAL_PULSE[obj->pwm];
263     ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm]  = value * PERIOD;
264
265     if (PULSE_WIDTH[obj->pwm] == 0) {
266         PULSE_WIDTH[obj->pwm] = 1;
267         setModulation(obj, 0, 0);
268     } else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
269         PULSE_WIDTH[obj->pwm] = PERIOD - 1;
270         setModulation(obj, 0, 1);
271     } else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
272         setModulation(obj, 1, oldPulseWidth == PERIOD);
273     }
274
275     NRF_TIMER2->INTENSET    = TIMER_INTENSET_COMPARE3_Msk;
276     NRF_TIMER2->SHORTS      = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
277     NRF_TIMER2->TASKS_START = 1;
278 }
279
280 float pwmout_read(pwmout_t *obj)
281 {
282     return ((float)PULSE_WIDTH[obj->pwm] / (float)PERIOD);
283 }
284
285 void pwmout_period(pwmout_t *obj, float seconds)
286 {
287     pwmout_period_us(obj, seconds * 1000000.0f);
288 }
289
290 void pwmout_period_ms(pwmout_t *obj, int ms)
291 {
292     pwmout_period_us(obj, ms * 1000);
293 }
294
295 // Set the PWM period, keeping the duty cycle the same.
296 void pwmout_period_us(pwmout_t *obj, int us)
297 {
298     uint32_t periodInTicks = us / TIMER_PRECISION;
299
300     NRF_TIMER2->EVENTS_COMPARE[3] = 0;
301     NRF_TIMER2->TASKS_STOP        = 1;
302
303     if (periodInTicks>((1 << 16) - 1)) {
304         PERIOD = (1 << 16) - 1; //131ms
305     } else if (periodInTicks<5) {
306         PERIOD = 5;
307     } else {
308         PERIOD = periodInTicks;
309     }
310     NRF_TIMER2->INTENSET    = TIMER_INTENSET_COMPARE3_Msk;
311     NRF_TIMER2->SHORTS      = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
312     NRF_TIMER2->TASKS_START = 1;
313 }
314
315 void pwmout_pulsewidth(pwmout_t *obj, float seconds)
316 {
317     pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
318 }
319
320 void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
321 {
322     pwmout_pulsewidth_us(obj, ms * 1000);
323 }
324
325 void pwmout_pulsewidth_us(pwmout_t *obj, int us)
326 {
327     uint32_t pulseInTicks  = us / TIMER_PRECISION;
328     uint16_t oldPulseWidth = ACTUAL_PULSE[obj->pwm];
329
330     NRF_TIMER2->EVENTS_COMPARE[3] = 0;
331     NRF_TIMER2->TASKS_STOP        = 1;
332
333     ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm]  = pulseInTicks;
334
335     if (PULSE_WIDTH[obj->pwm] == 0) {
336         PULSE_WIDTH[obj->pwm] = 1;
337         setModulation(obj, 0, 0);
338     } else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
339         PULSE_WIDTH[obj->pwm] = PERIOD - 1;
340         setModulation(obj, 0, 1);
341     } else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
342         setModulation(obj, 1, oldPulseWidth == PERIOD);
343     }
344     NRF_TIMER2->INTENSET    = TIMER_INTENSET_COMPARE3_Msk;
345     NRF_TIMER2->SHORTS      = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
346     NRF_TIMER2->TASKS_START = 1;
347 }