Skip to content

Commit eaa9d3b

Browse files
authored
Merge pull request #19 from nvlsianpu/add_nrf52_pwm_rebase
Add nrf52 PWM implementation.
2 parents 0d9dc1e + 5d5ea9a commit eaa9d3b

File tree

6 files changed

+1731
-6
lines changed

6 files changed

+1731
-6
lines changed

hal/targets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,6 @@
17731773
"NRF52_PAN_62",
17741774
"NRF52_PAN_63"
17751775
],
1776-
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"]
1776+
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"]
17771777
}
17781778
}

hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c

Lines changed: 287 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,49 +44,333 @@
4444

4545
#if DEVICE_PWMOUT
4646

47-
// TODO - provide an implementation.
47+
#include "app_util_platform.h"
48+
#include "nrf_drv_pwm.h"
4849

49-
//#include "nrf_pwm.h"
50+
#define MAX_PWM_COUNTERTOP (0x7FFF) // 0x7FFF is the max of COUNTERTOP value for the PWM peripherial of the nRF52.
51+
#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP * 8) // PWM hw is driven by 16 MHz clock, hence the tick is 1_us/16,
52+
// and 128 is the max prescaler value.
53+
#define MAX_PWM_PERIOD_MS ((MAX_PWM_PERIOD_US / 1000) + 1) // approximations advance
54+
#define MAX_PWM_PERIOD_S ((MAX_PWM_PERIOD_US / 1000000) + 1) // approximations advance
5055

56+
57+
#define PWM_INSTANCE_COUNT (PWM_COUNT) // import from the nrf_drv_config.h file
58+
59+
///> instances of nRF52 PWM driver
60+
static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
61+
{
62+
#if PWM0_ENABLED
63+
NRF_DRV_PWM_INSTANCE(0),
64+
#endif
65+
#if PWM1_ENABLED
66+
NRF_DRV_PWM_INSTANCE(1),
67+
#endif
68+
#if PWM2_ENABLED
69+
NRF_DRV_PWM_INSTANCE(2)
70+
#endif
71+
};
72+
73+
typedef struct
74+
{
75+
uint32_t period_us;
76+
uint32_t duty_us;
77+
float duty;
78+
} pwm_signal_t; /// PWM signal description type
79+
80+
typedef struct
81+
{
82+
nrf_drv_pwm_t * p_pwm_driver;
83+
pwm_signal_t signal;
84+
volatile nrf_pwm_values_common_t seq_values[1];
85+
} pwm_t; /// internal PWM instance support type
86+
87+
static pwm_t m_pwm[PWM_INSTANCE_COUNT] =
88+
{
89+
#if PWM0_ENABLED
90+
{.p_pwm_driver = NULL},
91+
#endif
92+
#if PWM1_ENABLED
93+
{.p_pwm_driver = NULL},
94+
#endif
95+
#if PWM2_ENABLED
96+
{.p_pwm_driver = NULL}
97+
#endif
98+
}; /// Array of internal PWM instances.
99+
100+
typedef struct
101+
{
102+
uint16_t period_hwu; // unit related to pwm_clk
103+
uint16_t duty_hwu; // unit related to pwm_clk
104+
nrf_pwm_clk_t pwm_clk;
105+
} pulsewidth_set_t; /// helper type for timing calculations
106+
107+
108+
static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization);
109+
51110
void pwmout_init(pwmout_t *obj, PinName pin)
52111
{
112+
uint32_t i;
113+
114+
for (i = 0; PWM_INSTANCE_COUNT; i++)
115+
{
116+
if (m_pwm[i].p_pwm_driver == NULL) // a driver instance not assigned to the obj?
117+
{
118+
obj->pin = pin;
119+
120+
obj->pwm_channel = i;
121+
122+
m_pwm[i].p_pwm_driver = (nrf_drv_pwm_t *) &m_pwm_driver[i];
123+
m_pwm[i].signal.period_us = 200000; // 0.02 s
124+
m_pwm[i].signal.duty_us = 100000;
125+
m_pwm[i].signal.duty = 0.5f;
126+
127+
obj->pwm_struct = &m_pwm[i];
128+
129+
internal_pwmout_exe(obj, true, true);
130+
131+
break;
132+
}
133+
}
134+
135+
MBED_ASSERT(i != PWM_INSTANCE_COUNT); // assert if free instance was not found.
53136
}
54137

55138
void pwmout_free(pwmout_t *obj)
56139
{
140+
nrf_drv_pwm_uninit( (nrf_drv_pwm_t*) obj->pwm_struct );
141+
142+
m_pwm[obj->pwm_channel].p_pwm_driver = NULL;
57143
}
58144

59145
void pwmout_write(pwmout_t *obj, float percent)
60146
{
147+
148+
if (percent < 0)
149+
{
150+
percent = 0;
151+
}
152+
else if (percent > 1)
153+
{
154+
percent = 1;
155+
}
156+
157+
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
158+
159+
p_pwm_signal->duty = percent;
160+
161+
int us = (((int)p_pwm_signal->period_us) * percent);
162+
163+
pwmout_pulsewidth_us(obj, us);
61164
}
62165

63166
float pwmout_read(pwmout_t *obj)
64167
{
65-
return 0.0f;
168+
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
169+
170+
return (float)p_pwm_signal->duty_us / (float)p_pwm_signal->period_us;
66171
}
67172

68173
void pwmout_period(pwmout_t *obj, float seconds)
69174
{
175+
// raught saturation < 0, quasi-max>
176+
if (seconds > MAX_PWM_PERIOD_S)
177+
{
178+
seconds = MAX_PWM_PERIOD_S;
179+
}
180+
else if (seconds < 0)
181+
{
182+
seconds = 0; // f. pwmout_period_us will set period to min. value
183+
}
184+
185+
int us = seconds * 1000000;
186+
187+
pwmout_period_us(obj, us);
70188
}
71189

72190
void pwmout_period_ms(pwmout_t *obj, int ms)
73191
{
192+
// reught saturation < 0, quasi-max>
193+
if (ms > MAX_PWM_PERIOD_MS)
194+
{
195+
ms = MAX_PWM_PERIOD_MS;
196+
}
197+
else if (ms < 0)
198+
{
199+
ms = 0; // f. pwmout_period_us will set period to min. value
200+
}
201+
202+
int us = ms * 1000;
203+
204+
pwmout_period_us(obj, us);
74205
}
75206

207+
76208
void pwmout_period_us(pwmout_t *obj, int us)
77209
{
210+
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
211+
212+
// saturation <1, real-max>
213+
if (us > MAX_PWM_PERIOD_US)
214+
{
215+
us = MAX_PWM_PERIOD_US;
216+
}
217+
else if (us < 1)
218+
{
219+
us = 1;
220+
}
221+
222+
p_pwm_signal->duty_us = (int)((float)us * p_pwm_signal->duty);
223+
224+
p_pwm_signal->period_us = us;
225+
226+
internal_pwmout_exe(obj, true, false);
78227
}
79228

80229
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
81230
{
231+
// raught saturation < 0, quasi-max>
232+
if (seconds > MAX_PWM_PERIOD_S)
233+
{
234+
seconds = MAX_PWM_PERIOD_S;
235+
}
236+
else if (seconds < 0)
237+
{
238+
seconds = 0;
239+
}
240+
241+
int us = seconds * 1000000;
242+
243+
pwmout_pulsewidth_us(obj,us);
82244
}
83245

84246
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
85247
{
248+
// raught saturation < 0, quasi-max>
249+
if (ms > MAX_PWM_PERIOD_MS)
250+
{
251+
ms = MAX_PWM_PERIOD_MS;
252+
}
253+
else if (ms < 0)
254+
{
255+
ms = 0;
256+
}
257+
258+
int us = ms * 1000;
259+
260+
pwmout_pulsewidth_us(obj, us);
86261
}
87262

88263
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
89264
{
265+
// saturation <0, real-max>
266+
if (us > MAX_PWM_PERIOD_US)
267+
{
268+
us = MAX_PWM_PERIOD_US;
269+
}
270+
else if (us < 0)
271+
{
272+
us = 0;
273+
}
274+
275+
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
276+
277+
p_pwm_signal->duty_us = us;
278+
p_pwm_signal->duty = us / p_pwm_signal->period_us;
279+
280+
internal_pwmout_exe(obj, false, false);
281+
}
282+
283+
284+
285+
286+
287+
288+
static ret_code_t pulsewidth_us_set_get(int period_hwu, int duty_hwu, pulsewidth_set_t * p_settings)
289+
{
290+
uint16_t div;
291+
nrf_pwm_clk_t pwm_clk = NRF_PWM_CLK_16MHz;
292+
293+
for(div = 1; div <= 128 ; div <<= 1) // 128 is the maximum of clock prescaler for PWM peripherial
294+
{
295+
if (MAX_PWM_COUNTERTOP >= period_hwu)
296+
{
297+
p_settings->period_hwu = period_hwu; // unit [us/16 * div]
298+
p_settings->duty_hwu = duty_hwu; // unit [us/16 * div]
299+
p_settings->pwm_clk = pwm_clk;
300+
301+
return NRF_SUCCESS;
302+
}
303+
304+
period_hwu >>= 1;
305+
duty_hwu >>= 1;
306+
pwm_clk++;
307+
}
308+
309+
return NRF_ERROR_INVALID_PARAM;
310+
}
311+
312+
313+
static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization)
314+
{
315+
pulsewidth_set_t pulsewidth_set;
316+
pwm_signal_t * p_pwm_signal;
317+
nrf_drv_pwm_t * p_pwm_driver;
318+
ret_code_t ret_code;
319+
320+
p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
321+
322+
if (NRF_SUCCESS == pulsewidth_us_set_get(p_pwm_signal->period_us * 16, // base clk for PWM is 16 MHz
323+
p_pwm_signal->duty_us * 16, // base clk for PWM is 16 MHz
324+
&pulsewidth_set))
325+
{
326+
p_pwm_driver = (((pwm_t*)obj->pwm_struct)->p_pwm_driver);
327+
328+
const nrf_pwm_sequence_t seq =
329+
{
330+
.values.p_common = (nrf_pwm_values_common_t*) (((pwm_t*)obj->pwm_struct)->seq_values),
331+
.length = 1,
332+
.repeats = 0,
333+
.end_delay = 0
334+
};
335+
336+
(((pwm_t*)obj->pwm_struct)->seq_values)[0] = pulsewidth_set.duty_hwu | 0x8000;
337+
338+
if (new_period)
339+
{
340+
nrf_drv_pwm_config_t config0 =
341+
{
342+
.output_pins =
343+
{
344+
obj->pin | NRF_DRV_PWM_PIN_INVERTED, // channel 0
345+
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
346+
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
347+
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
348+
},
349+
.irq_priority = APP_IRQ_PRIORITY_LOW,
350+
.base_clock = pulsewidth_set.pwm_clk,
351+
.count_mode = NRF_PWM_MODE_UP,
352+
.top_value = pulsewidth_set.period_hwu,
353+
.load_mode = NRF_PWM_LOAD_COMMON,
354+
.step_mode = NRF_PWM_STEP_AUTO
355+
};
356+
357+
if (!initialization)
358+
{
359+
nrf_drv_pwm_uninit(p_pwm_driver);
360+
}
361+
362+
ret_code = nrf_drv_pwm_init( p_pwm_driver, &config0, NULL);
363+
364+
MBED_ASSERT(ret_code == NRF_SUCCESS); // assert if free instance was not found.
365+
}
366+
367+
nrf_drv_pwm_simple_playback(p_pwm_driver, &seq, 0, NRF_DRV_PWM_FLAG_LOOP);
368+
}
369+
else
370+
{
371+
MBED_ASSERT(0); // force assertion
372+
}
373+
90374
}
91375

92376
#endif // DEVICE_PWMOUT

0 commit comments

Comments
 (0)