1 /**
2 * \file
3 *
4 * \brief SAM PWM Driver for SAMB11
5 *
6 * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46 #include "pwm.h"
47 #include "system_sam_b.h"
48
49 /**
50 * \internal Get the register configuration values by PWM device
51 */
_pwm_reg_output_polarity(enum pwm_device_select device_select)52 static uint32_t _pwm_reg_output_polarity(enum pwm_device_select device_select)
53 {
54 switch (device_select) {
55 case PWM0:
56 return LPMCU_MISC_REGS_PWM0_CTRL_OUTPUT_POLARITY;
57 case PWM1:
58 return LPMCU_MISC_REGS_PWM1_CTRL_OUTPUT_POLARITY;
59 case PWM2:
60 return LPMCU_MISC_REGS_PWM2_CTRL_OUTPUT_POLARITY;
61 case PWM3:
62 return LPMCU_MISC_REGS_PWM3_CTRL_OUTPUT_POLARITY;
63
64 default:
65 return 0;
66 }
67 }
68
69 /**
70 * \internal Get the register configuration values by PWM device
71 */
_pwm_reg_agcdata_fmt(enum pwm_device_select device_select)72 static uint32_t _pwm_reg_agcdata_fmt(enum pwm_device_select device_select)
73 {
74 switch (device_select) {
75 case PWM0:
76 return LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_FMT;
77 case PWM1:
78 return LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_FMT;
79 case PWM2:
80 return LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_FMT;
81 case PWM3:
82 return LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_FMT;
83
84 default:
85 return 0;
86 }
87 }
88
89 /**
90 * \internal Get the register configuration values by PWM device
91 */
_pwm_reg_sample_method(enum pwm_device_select device_select,bool value)92 static uint32_t _pwm_reg_sample_method(enum pwm_device_select device_select, bool value)
93 {
94 switch (device_select) {
95 case PWM0:
96 return (LPMCU_MISC_REGS_PWM0_CTRL_SAMPLE_METHOD &
97 ((value) << LPMCU_MISC_REGS_PWM0_CTRL_SAMPLE_METHOD_Pos));
98 case PWM1:
99 return (LPMCU_MISC_REGS_PWM1_CTRL_SAMPLE_METHOD &
100 ((value) << LPMCU_MISC_REGS_PWM1_CTRL_SAMPLE_METHOD_Pos));
101 case PWM2:
102 return (LPMCU_MISC_REGS_PWM2_CTRL_SAMPLE_METHOD &
103 ((value) << LPMCU_MISC_REGS_PWM2_CTRL_SAMPLE_METHOD_Pos));
104 case PWM3:
105 return (LPMCU_MISC_REGS_PWM3_CTRL_SAMPLE_METHOD &
106 ((value) << LPMCU_MISC_REGS_PWM3_CTRL_SAMPLE_METHOD_Pos));
107
108 default:
109 return 0;
110 }
111 }
112
113 /**
114 * \internal Get the register configuration values by PWM device
115 */
_pwm_reg_period(enum pwm_device_select device_select,enum pwm_period value)116 static uint32_t _pwm_reg_period(enum pwm_device_select device_select, enum pwm_period value)
117 {
118 switch (device_select) {
119 case PWM0:
120 return LPMCU_MISC_REGS_PWM0_CTRL_PWM_PERIOD(value);
121 case PWM1:
122 return LPMCU_MISC_REGS_PWM1_CTRL_PWM_PERIOD(value);
123 case PWM2:
124 return LPMCU_MISC_REGS_PWM2_CTRL_PWM_PERIOD(value);
125 case PWM3:
126 return LPMCU_MISC_REGS_PWM3_CTRL_PWM_PERIOD(value);
127
128 default:
129 return 0;
130 }
131 }
132
133 /**
134 * \internal Get the register configuration values by PWM device
135 */
_pwm_reg_agcdata_in(enum pwm_device_select device_select,bool agcdata_format,uint8_t duty_cycle)136 static uint32_t _pwm_reg_agcdata_in(
137 enum pwm_device_select device_select, \
138 bool agcdata_format, \
139 uint8_t duty_cycle)
140 {
141 int32_t agcdata_in;
142
143 if (agcdata_format) {
144 agcdata_in = (uint16_t)((1024 * duty_cycle) / 100);
145 } else {
146 agcdata_in = (int16_t)((1024 * duty_cycle) / 100 - 512);
147 }
148 switch (device_select) {
149 case PWM0:
150 return LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_IN(agcdata_in);
151 case PWM1:
152 return LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_IN(agcdata_in);
153 case PWM2:
154 return LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_IN(agcdata_in);
155 case PWM3:
156 return LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_IN(agcdata_in);
157
158 default:
159 return 0;
160 }
161 }
162
163 /**
164 * \internal Get the register configuration values by PWM device
165 */
_pwm_reg_clock_sel(enum pwm_device_select device_select,enum pwm_clock_select value)166 static uint32_t _pwm_reg_clock_sel(enum pwm_device_select device_select, enum pwm_clock_select value)
167 {
168 switch (device_select) {
169 case PWM0:
170 return LPMCU_MISC_REGS_PWM0_CTRL_CLOCK_SEL(value);
171 case PWM1:
172 return LPMCU_MISC_REGS_PWM1_CTRL_CLOCK_SEL(value);
173 case PWM2:
174 return LPMCU_MISC_REGS_PWM2_CTRL_CLOCK_SEL(value);
175 case PWM3:
176 return LPMCU_MISC_REGS_PWM3_CTRL_CLOCK_SEL(value);
177
178 default:
179 return 0;
180 }
181 }
182
183 /**
184 * \brief Initializes a pwm configuration structure to defaults.
185 *
186 * Initializes a given pwm configuration structure to a set of
187 * known default values. This function should be called on all new
188 * instances of these configuration structures before being modified by the
189 * user application.
190 *
191 * The default configuration is as follows:
192 * \li Not to inverse the polarity
193 * \li Sample method 0
194 * \li PWM period is 4
195 * \li Duty cycle is 50%
196 * \li Clock is 26MHz
197 * \li Output frequency is 25.4KHz
198 * \li Pinmux pad
199 *
200 * \param[out] config Configuration structure to initialize to default values
201 */
pwm_get_config_defaults(struct pwm_config * const config)202 void pwm_get_config_defaults(struct pwm_config *const config)
203 {
204 config->output_polarity = false;
205 config->agcdata_format = false;
206 config->sample_method = PWM_SAMPLE_METHOD_0;
207 config->period = PWM_PERIOD_4;
208 config->duty_cycle = 50;
209 config->clock_select = PWM_CLOCK_SELECT_26_0;
210 config->pin_number_pad = 0;
211 config->pinmux_sel_pad = 0;
212 }
213
214 /**
215 * \brief Set the duty cycle of the PWM module.
216 *
217 * This function will set the duty cycle of the PWM module, based on the values
218 * of setting.
219 *
220 * \param[in] device_select PWM device
221 * \param[in] duty_cycle This value specifies the duty cycle(%)
222 */
pwm_set_duty_cycle(enum pwm_device_select device_select,uint8_t duty_cycle)223 void pwm_set_duty_cycle(enum pwm_device_select device_select, \
224 uint8_t duty_cycle)
225 {
226 bool agcdata_format;
227 uint32_t temp;
228
229 switch(device_select) {
230 case PWM0:
231 agcdata_format = LPMCU_MISC_REGS0->PWM0_CTRL.bit.AGCDATA_FMT;
232 temp = LPMCU_MISC_REGS0->PWM0_CTRL.reg;
233 temp &= ~LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_IN_Msk;
234 temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
235 LPMCU_MISC_REGS0->PWM0_CTRL.reg = temp;
236 break;
237
238 case PWM1:
239 agcdata_format = LPMCU_MISC_REGS0->PWM1_CTRL.bit.AGCDATA_FMT;
240 temp = LPMCU_MISC_REGS0->PWM1_CTRL.reg;
241 temp &= ~LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_IN_Msk;
242 temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
243 LPMCU_MISC_REGS0->PWM1_CTRL.reg = temp;
244 break;
245
246 case PWM2:
247 agcdata_format = LPMCU_MISC_REGS0->PWM2_CTRL.bit.AGCDATA_FMT;
248 temp = LPMCU_MISC_REGS0->PWM2_CTRL.reg;
249 temp &= ~LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_IN_Msk;
250 temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
251 LPMCU_MISC_REGS0->PWM2_CTRL.reg = temp;
252 break;
253
254 case PWM3:
255 agcdata_format = LPMCU_MISC_REGS0->PWM3_CTRL.bit.AGCDATA_FMT;
256 temp = LPMCU_MISC_REGS0->PWM3_CTRL.reg;
257 temp &= ~LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_IN_Msk;
258 temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
259 LPMCU_MISC_REGS0->PWM3_CTRL.reg = temp;
260 break;
261 }
262 }
263
264 /**
265 * \brief Set the period of the PWM module.
266 *
267 * This function will set the frequence of the PWM module, based on the values
268 * of setting period.
269 *
270 * \param[in] device_select PWM device
271 * \param[in] period Programmable PWM update period
272 */
pwm_set_period(enum pwm_device_select device_select,enum pwm_period period)273 void pwm_set_period(enum pwm_device_select device_select, \
274 enum pwm_period period)
275 {
276 uint32_t reg_value;
277 uint32_t temp;
278
279 if (period > PWM_PERIOD_8) {
280 reg_value = PWM_PERIOD_4;
281 }
282 reg_value = _pwm_reg_period(device_select, period);
283
284 switch(device_select) {
285 case PWM0:
286 temp = LPMCU_MISC_REGS0->PWM0_CTRL.reg;
287 temp &= ~LPMCU_MISC_REGS_PWM0_CTRL_PWM_PERIOD_Msk;
288 temp |= reg_value;
289 LPMCU_MISC_REGS0->PWM0_CTRL.reg = temp;
290 break;
291
292 case PWM1:
293 temp = LPMCU_MISC_REGS0->PWM1_CTRL.reg;
294 temp &= ~LPMCU_MISC_REGS_PWM1_CTRL_PWM_PERIOD_Msk;
295 temp |= reg_value;
296 LPMCU_MISC_REGS0->PWM1_CTRL.reg = temp;
297 break;
298
299 case PWM2:
300 temp = LPMCU_MISC_REGS0->PWM2_CTRL.reg;
301 temp &= ~LPMCU_MISC_REGS_PWM2_CTRL_PWM_PERIOD_Msk;
302 temp |= reg_value;
303 LPMCU_MISC_REGS0->PWM2_CTRL.reg = temp;
304 break;
305
306 case PWM3:
307 temp = LPMCU_MISC_REGS0->PWM3_CTRL.reg;
308 temp &= ~LPMCU_MISC_REGS_PWM3_CTRL_PWM_PERIOD_Msk;
309 temp |= reg_value;
310 LPMCU_MISC_REGS0->PWM3_CTRL.reg = temp;
311 break;
312 }
313 }
314
315 /**
316 * \brief Initializes the PWM module
317 *
318 * This function will initialize the PWM module, based on the values
319 * of the config struct.
320 *
321 * \param[in] device_select PWM device
322 * \param[in] config Pointer to the config struct
323 *
324 * \return The status of the configuration.
325 * \retval STATUS_ERR_UNSUPPORTED_DEV If unsupported device were provided
326 * \retval STATUS_OK If the configuration was written
327 */
pwm_init(enum pwm_device_select device_select,const struct pwm_config * const config)328 enum status_code pwm_init(enum pwm_device_select device_select, \
329 const struct pwm_config *const config)
330 {
331 uint32_t reg_value = 0;
332
333 if (device_select > PWM3) {
334 return STATUS_ERR_UNSUPPORTED_DEV;
335 }
336
337 if (config->output_polarity) {
338 reg_value |= _pwm_reg_output_polarity(device_select);
339 }
340
341 if (config->agcdata_format) {
342 reg_value |= _pwm_reg_agcdata_fmt(device_select);
343 }
344
345 reg_value |= _pwm_reg_sample_method(device_select, config->sample_method);
346
347 /* If period > 8 will be set to 4 as default. */
348 if (config->period > PWM_PERIOD_8) {
349 reg_value |= _pwm_reg_period(device_select, PWM_PERIOD_4);
350 } else {
351 reg_value |= _pwm_reg_period(device_select, config->period);
352 }
353 reg_value |= _pwm_reg_agcdata_in(device_select, config->agcdata_format, \
354 config->duty_cycle);
355
356 reg_value |= _pwm_reg_clock_sel(device_select, config->clock_select);
357
358 switch(device_select) {
359 case PWM0:
360 LPMCU_MISC_REGS0->PWM0_CTRL.reg = reg_value;
361 break;
362
363 case PWM1:
364 LPMCU_MISC_REGS0->PWM1_CTRL.reg = reg_value;
365 break;
366
367 case PWM2:
368 LPMCU_MISC_REGS0->PWM2_CTRL.reg = reg_value;
369 break;
370
371 case PWM3:
372 LPMCU_MISC_REGS0->PWM3_CTRL.reg = reg_value;
373 break;
374 }
375
376 struct gpio_config config_gpio;
377 gpio_get_config_defaults(&config_gpio);
378 config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
379 gpio_pin_set_config(config->pin_number_pad, &config_gpio);
380 gpio_pinmux_cofiguration(config->pin_number_pad, \
381 (uint16_t)(config->pinmux_sel_pad));
382
383 return STATUS_OK;
384 }
385
386 /**
387 * \brief Enables the PWM module
388 *
389 * This function will enable the PWM module.
390 *
391 * \param[in] device_select PWM device
392 */
pwm_enable(enum pwm_device_select device_select)393 void pwm_enable(enum pwm_device_select device_select)
394 {
395 switch (device_select) {
396 case PWM0:
397 system_clock_peripheral_enable(PERIPHERAL_PWM0);
398 LPMCU_MISC_REGS0->PWM0_CTRL.reg |= LPMCU_MISC_REGS_PWM0_CTRL_PWM_EN;
399 break;
400
401 case PWM1:
402 system_clock_peripheral_enable(PERIPHERAL_PWM1);
403 LPMCU_MISC_REGS0->PWM1_CTRL.reg |= LPMCU_MISC_REGS_PWM1_CTRL_PWM_EN;
404 break;
405
406 case PWM2:
407 system_clock_peripheral_enable(PERIPHERAL_PWM2);
408 LPMCU_MISC_REGS0->PWM2_CTRL.reg |= LPMCU_MISC_REGS_PWM2_CTRL_PWM_EN;
409 break;
410
411 case PWM3:
412 system_clock_peripheral_enable(PERIPHERAL_PWM3);
413 LPMCU_MISC_REGS0->PWM3_CTRL.reg |= LPMCU_MISC_REGS_PWM3_CTRL_PWM_EN;
414 break;
415 }
416 }
417
418 /**
419 * \brief Disable the PWM module
420 *
421 * This function will disable the PWM module.
422 *
423 * \param[in] device_select PWM device
424 */
pwm_disable(enum pwm_device_select device_select)425 void pwm_disable(enum pwm_device_select device_select)
426 {
427 switch (device_select) {
428 case PWM0:
429 system_clock_peripheral_disable(PERIPHERAL_PWM0);
430 LPMCU_MISC_REGS0->PWM0_CTRL.reg &= ~LPMCU_MISC_REGS_PWM0_CTRL_PWM_EN;
431 break;
432
433 case PWM1:
434 system_clock_peripheral_disable(PERIPHERAL_PWM1);
435 LPMCU_MISC_REGS0->PWM1_CTRL.reg &= ~LPMCU_MISC_REGS_PWM1_CTRL_PWM_EN;
436 break;
437
438 case PWM2:
439 system_clock_peripheral_disable(PERIPHERAL_PWM2);
440 LPMCU_MISC_REGS0->PWM2_CTRL.reg &= ~LPMCU_MISC_REGS_PWM2_CTRL_PWM_EN;
441 break;
442
443 case PWM3:
444 system_clock_peripheral_disable(PERIPHERAL_PWM3);
445 LPMCU_MISC_REGS0->PWM3_CTRL.reg &= ~LPMCU_MISC_REGS_PWM3_CTRL_PWM_EN;
446 break;
447 }
448 }
449
450 /**
451 * \brief Reset the PWM module
452 *
453 * This function will reset the PWM module.
454 *
455 * \param[in] device_select PWM device
456 */
pwm_reset(enum pwm_device_select device_select)457 void pwm_reset(enum pwm_device_select device_select)
458 {
459 switch (device_select) {
460 case PWM0:
461 system_peripheral_reset(PERIPHERAL_PWM0);
462 break;
463
464 case PWM1:
465 system_peripheral_reset(PERIPHERAL_PWM1);
466 break;
467
468 case PWM2:
469 system_peripheral_reset(PERIPHERAL_PWM2);
470 break;
471
472 case PWM3:
473 system_peripheral_reset(PERIPHERAL_PWM3);
474 break;
475 }
476 }