mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
132 lines
3.6 KiB
C
132 lines
3.6 KiB
C
/*
|
|
* Copyright (C) 2015 Hamburg University of Applied Sciences
|
|
* 2016 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_sam3
|
|
* @ingroup drivers_periph_pwm
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief CPU specific low-level PWM driver implementation for the SAM3X8E
|
|
*
|
|
* The SAM3 has only support for a single PWM device, so we accept only
|
|
* PWM_DEV(0) for this driver.
|
|
*
|
|
* @author Andreas "Paul" Pauli <andreas.pauli@haw-hamburg.de>
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "cpu.h"
|
|
#include "assert.h"
|
|
#include "periph/pwm.h"
|
|
#include "periph/gpio.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define PREA_MAX (10U)
|
|
|
|
static uint16_t pwm_period;
|
|
static uint8_t pwm_chan_mask;
|
|
|
|
uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res)
|
|
{
|
|
uint32_t pwm_clk = freq * res; /* Desired/real pwm_clock */
|
|
uint32_t diva = 1; /* Candidate for 8bit divider */
|
|
uint32_t prea = 0; /* Candidate for clock select */
|
|
|
|
/* check for valid device and mode (left-aligned PWM only so far) */
|
|
if ((dev != PWM_DEV(0)) || (mode != PWM_LEFT)) {
|
|
return 0;
|
|
}
|
|
|
|
/* check if target frequency and resolution is applicable */
|
|
if (pwm_clk > CLOCK_CORECLOCK) {
|
|
return 0;
|
|
}
|
|
|
|
/* calculate pre-scalers for targeted frequency and resolution:
|
|
* clk = CORECLOCK / (2 ^ prea) / diva
|
|
* width prea := [0, 10] and diva [1, 255]
|
|
*/
|
|
while ((diva = (CLOCK_CORECLOCK / pwm_clk / (1 << prea))) > 255) {
|
|
++prea;
|
|
}
|
|
/* make sure PREA does not exceed its limit */
|
|
if (prea > PREA_MAX) {
|
|
return 0;
|
|
}
|
|
|
|
/* activate PWM block by enabling it's clock. */
|
|
PMC->PMC_PCER1 = PMC_PCER1_PID36;
|
|
/* disable all channels to allow CPRD updates. */
|
|
PWM->PWM_DIS = 0xff;
|
|
/* configure clock generator */
|
|
PWM->PWM_CLK = PWM_CLK_PREA(prea) | PWM_CLK_DIVA(diva);
|
|
/* remember the used resolution (for cropping inputs later) */
|
|
pwm_period = res - 1;
|
|
|
|
/* setup the configured channels */
|
|
pwm_chan_mask = 0;
|
|
for (unsigned i = 0; i < PWM_CHAN_NUMOF; i++) {
|
|
/* configure the use pin */
|
|
gpio_init_mux(pwm_chan[i].pin, GPIO_MUX_B);
|
|
/* and setup the channel */
|
|
pwm_chan_mask |= (1 << pwm_chan[i].hwchan);
|
|
PWM->PWM_CH_NUM[pwm_chan[i].hwchan].PWM_CMR = PWM_CMR_CPRE_CLKA;
|
|
PWM->PWM_CH_NUM[pwm_chan[i].hwchan].PWM_CPRD = pwm_period;
|
|
PWM->PWM_CH_NUM[pwm_chan[i].hwchan].PWM_CDTY = 0;
|
|
}
|
|
|
|
/* enable all configured channels */
|
|
PWM->PWM_ENA = pwm_chan_mask;
|
|
|
|
/* and return the actual configured frequency */
|
|
return (CLOCK_CORECLOCK / (1 << prea) / diva / res);
|
|
}
|
|
|
|
uint8_t pwm_channels(pwm_t pwm)
|
|
{
|
|
(void)pwm;
|
|
assert(pwm == PWM_DEV(0));
|
|
return (uint8_t)PWM_CHAN_NUMOF;
|
|
}
|
|
|
|
/*
|
|
* Update duty-cycle in channel with value.
|
|
* If value is larger than resolution set by pwm_init() it is cropped.
|
|
*/
|
|
void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
|
|
{
|
|
(void)pwm;
|
|
assert((pwm == PWM_DEV(0)) && (channel < PWM_CHAN_NUMOF));
|
|
|
|
/* clip and set new value */
|
|
value = (value > pwm_period) ? pwm_period : value;
|
|
PWM->PWM_CH_NUM[pwm_chan[channel].hwchan].PWM_CDTYUPD = value;
|
|
}
|
|
|
|
void pwm_poweron(pwm_t pwm)
|
|
{
|
|
(void)pwm;
|
|
assert(pwm == PWM_DEV(0));
|
|
PMC->PMC_PCER1 = PMC_PCDR1_PID36;
|
|
PWM->PWM_ENA = pwm_chan_mask;
|
|
}
|
|
|
|
void pwm_poweroff(pwm_t pwm)
|
|
{
|
|
(void)pwm;
|
|
assert(pwm == PWM_DEV(0));
|
|
PWM->PWM_ENA = 0;
|
|
PMC->PMC_PCDR1 = PMC_PCDR1_PID36;
|
|
}
|