1 /* mbed Microcontroller Library
2 * Copyright (c) 2013 Nordic Semiconductor
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 "mbed_assert.h"
17 #include "pwmout_api.h"
20 #include "mbed_error.h"
23 #define TIMER_PRECISION 4 //4us ticks
24 #define TIMER_PRESCALER 6 //4us ticks = 16Mhz/(2**6)
25 static const PinMap PinMap_PWM[] = {
58 static NRF_TIMER_Type *Timers[1] = {
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};
68 /** @brief Function for handling timer 2 peripheral interrupts.
73 void TIMER2_IRQHandler(void)
75 NRF_TIMER2->EVENTS_COMPARE[3] = 0;
76 NRF_TIMER2->CC[3] = PERIOD;
79 NRF_TIMER2->CC[0] = PULSE_WIDTH[0];
82 NRF_TIMER2->CC[1] = PULSE_WIDTH[1];
85 NRF_TIMER2->CC[2] = PULSE_WIDTH[2];
88 NRF_TIMER2->TASKS_START = 1;
94 /** @brief Function for initializing the Timer peripherals.
96 void timer_init(uint8_t pwmChoice)
98 NRF_TIMER_Type *timer = Timers[0];
99 timer->TASKS_STOP = 0;
101 if (pwmChoice == 0) {
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;
110 timer->CC[pwmChoice] = PULSE_WIDTH[pwmChoice];
112 //high priority application interrupt
113 NVIC_SetPriority(TIMER2_IRQn, 1);
114 NVIC_EnableIRQ(TIMER2_IRQn);
116 timer->TASKS_START = 0x01;
119 /** @brief Function for initializing the GPIO Tasks/Events peripheral.
121 void gpiote_init(PinName pin, uint8_t channel_number)
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 */
140 /* Launch the task to take the GPIOTE channel output to the desired level */
141 NRF_GPIOTE->TASKS_OUT[channel_number] = 1;
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);//
151 /* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
157 /** @brief Function for initializing the Programmable Peripheral Interconnect peripheral.
159 static void ppi_init(uint8_t pwm)
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];
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];
171 // Enable PPI channels.
172 NRF_PPI->CHEN |= (1 << channel_number) |
173 (1 << (channel_number + 1));
176 void setModulation(pwmout_t *obj, uint8_t toggle, uint8_t high)
179 NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
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);
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);
188 NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
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);
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);
200 void pwmout_init(pwmout_t *obj, PinName pin)
202 // determine the channel
203 uint8_t pwmOutSuccess = 0;
204 PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
206 MBED_ASSERT(pwm != (PWMName)NC);
208 if (PWM_taken[(uint8_t)pwm]) {
209 for (uint8_t i = 1; !pwmOutSuccess && (i<NO_PWMS); i++) {
218 PWM_taken[(uint8_t)pwm] = 1;
221 if (!pwmOutSuccess) {
222 error("PwmOut pin mapping failed. All available PWM channels are in use.");
228 gpiote_init(pin, (uint8_t)pwm);
229 ppi_init((uint8_t)pwm);
232 NRF_POWER->TASKS_CONSTLAT = 1;
235 timer_init((uint8_t)pwm);
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);
242 void pwmout_free(pwmout_t *obj)
244 MBED_ASSERT(obj->pwm != (PWMName)NC);
245 PWM_taken[obj->pwm] = 0;
246 pwmout_write(obj, 0);
249 void pwmout_write(pwmout_t *obj, float value)
251 uint16_t oldPulseWidth;
253 NRF_TIMER2->EVENTS_COMPARE[3] = 0;
254 NRF_TIMER2->TASKS_STOP = 1;
258 } else if (value > 1.0f) {
262 oldPulseWidth = ACTUAL_PULSE[obj->pwm];
263 ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = value * PERIOD;
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);
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;
280 float pwmout_read(pwmout_t *obj)
282 return ((float)PULSE_WIDTH[obj->pwm] / (float)PERIOD);
285 void pwmout_period(pwmout_t *obj, float seconds)
287 pwmout_period_us(obj, seconds * 1000000.0f);
290 void pwmout_period_ms(pwmout_t *obj, int ms)
292 pwmout_period_us(obj, ms * 1000);
295 // Set the PWM period, keeping the duty cycle the same.
296 void pwmout_period_us(pwmout_t *obj, int us)
298 uint32_t periodInTicks = us / TIMER_PRECISION;
300 NRF_TIMER2->EVENTS_COMPARE[3] = 0;
301 NRF_TIMER2->TASKS_STOP = 1;
303 if (periodInTicks>((1 << 16) - 1)) {
304 PERIOD = (1 << 16) - 1; //131ms
305 } else if (periodInTicks<5) {
308 PERIOD = periodInTicks;
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;
315 void pwmout_pulsewidth(pwmout_t *obj, float seconds)
317 pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
320 void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
322 pwmout_pulsewidth_us(obj, ms * 1000);
325 void pwmout_pulsewidth_us(pwmout_t *obj, int us)
327 uint32_t pulseInTicks = us / TIMER_PRECISION;
328 uint16_t oldPulseWidth = ACTUAL_PULSE[obj->pwm];
330 NRF_TIMER2->EVENTS_COMPARE[3] = 0;
331 NRF_TIMER2->TASKS_STOP = 1;
333 ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = pulseInTicks;
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);
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;