mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
kinetis: Refactor PWM periph implementation
- Set the proper prescaler value depending on requested frequency - Return the actual achieved frequency in pwm_init - Handle 1-8 channels depending on periph_conf.h instead of 4 fixed - Perform function argument verification before touching the hardware - Turn on PORT clock gate before touching PORT registers - Eliminate some magic numbers
This commit is contained in:
parent
fc926e57bf
commit
c85cc83a5a
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2015 Eistec AB
|
||||
*
|
||||
* 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
|
||||
@ -18,6 +19,7 @@
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
* @author Jonas Remmert <j.remmert@phytec.de>
|
||||
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
@ -34,131 +36,188 @@
|
||||
/* ignore file in case no PWM devices are defined */
|
||||
#if PWM_NUMOF
|
||||
|
||||
/* FTM channel look up tables */
|
||||
#if PWM_0_EN
|
||||
static const uint8_t ftm0chan[] = {
|
||||
#if PWM_0_CHANNELS > 0
|
||||
PWM_0_FTMCHAN_CH0,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 1
|
||||
PWM_0_FTMCHAN_CH1,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 2
|
||||
PWM_0_FTMCHAN_CH2,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 3
|
||||
PWM_0_FTMCHAN_CH3,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 4
|
||||
PWM_0_FTMCHAN_CH4,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 5
|
||||
PWM_0_FTMCHAN_CH5,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 6
|
||||
PWM_0_FTMCHAN_CH6,
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 7
|
||||
PWM_0_FTMCHAN_CH7,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
static const uint8_t ftm1chan[] = {
|
||||
#if PWM_1_CHANNELS > 0
|
||||
PWM_1_FTMCHAN_CH0,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 1
|
||||
PWM_1_FTMCHAN_CH1,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 2
|
||||
PWM_1_FTMCHAN_CH2,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 3
|
||||
PWM_1_FTMCHAN_CH3,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 4
|
||||
PWM_1_FTMCHAN_CH4,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 5
|
||||
PWM_1_FTMCHAN_CH5,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 6
|
||||
PWM_1_FTMCHAN_CH6,
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 7
|
||||
PWM_1_FTMCHAN_CH7,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
int pwm_init(pwm_t dev, pwm_mode_t mode, unsigned int frequency, unsigned int resolution)
|
||||
{
|
||||
FTM_Type *tim = NULL;
|
||||
PORT_Type *port[PWM_MAX_CHANNELS];
|
||||
/* cppcheck-suppress unassignedVariable */
|
||||
uint8_t pins[PWM_MAX_CHANNELS];
|
||||
uint8_t af[PWM_MAX_CHANNELS];
|
||||
/* cppcheck-suppress unassignedVariable */
|
||||
uint8_t ftmchan[PWM_MAX_CHANNELS];
|
||||
FTM_Type *ftm;
|
||||
int channels = 0;
|
||||
uint32_t pwm_clk = 0;
|
||||
const uint8_t *ftmchan = NULL;
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
channels = PWM_0_CHANNELS;
|
||||
pwm_clk = PWM_0_CLK;
|
||||
ftm = PWM_0_DEV;
|
||||
ftmchan = &ftm0chan[0];
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
channels = PWM_1_CHANNELS;
|
||||
pwm_clk = PWM_1_CLK;
|
||||
ftm = PWM_1_DEV;
|
||||
ftmchan = &ftm1chan[0];
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case PWM_LEFT:
|
||||
case PWM_RIGHT:
|
||||
case PWM_CENTER:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (resolution > (PWM_MAX_VALUE + 1) || (resolution * frequency) > pwm_clk) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Try to find a good prescaler value */
|
||||
/* The prescaler divides the module clock by a power of two, between 2^0 and 2^7 */
|
||||
uint8_t prescaler = 0;
|
||||
/* (resolution * frequency) is the number of timer ticks per second */
|
||||
while ((pwm_clk >> prescaler) > (resolution * frequency)) {
|
||||
++prescaler;
|
||||
if (prescaler > 7) {
|
||||
/* Module clock is too fast to reach the requested frequency using the
|
||||
* hardware supported prescaler values */
|
||||
/* Note: The frequency might be reachable if the requested resolution
|
||||
* is increased. */
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
/* The chosen prescaler yields a timer frequency which is the
|
||||
* nearest possible frequency less than the requested frequency */
|
||||
|
||||
/* Turn on the peripheral */
|
||||
pwm_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
tim = PWM_0_DEV;
|
||||
port[0] = PWM_0_PORT_CH0;
|
||||
port[1] = PWM_0_PORT_CH1;
|
||||
port[2] = PWM_0_PORT_CH2;
|
||||
port[3] = PWM_0_PORT_CH3;
|
||||
pins[0] = PWM_0_PIN_CH0;
|
||||
pins[1] = PWM_0_PIN_CH1;
|
||||
pins[2] = PWM_0_PIN_CH2;
|
||||
pins[3] = PWM_0_PIN_CH3;
|
||||
ftmchan[0] = PWM_0_FTMCHAN_CH0;
|
||||
ftmchan[1] = PWM_0_FTMCHAN_CH1;
|
||||
ftmchan[2] = PWM_0_FTMCHAN_CH2;
|
||||
ftmchan[3] = PWM_0_FTMCHAN_CH3;
|
||||
af[0] = PWM_0_PIN_AF_CH0;
|
||||
af[1] = PWM_0_PIN_AF_CH1;
|
||||
af[2] = PWM_0_PIN_AF_CH2;
|
||||
af[3] = PWM_0_PIN_AF_CH3;
|
||||
channels = PWM_0_CHANNELS;
|
||||
pwm_clk = PWM_0_CLK;
|
||||
PWM_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (channels > PWM_MAX_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->MODE = (1 << FTM_MODE_WPDIS_SHIFT);
|
||||
|
||||
/* setup pins, reset timer match value */
|
||||
for (int i = 0; i < channels; i++) {
|
||||
port[i]->PCR[pins[i]] = PORT_PCR_MUX(af[i]);
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[i].CnV = 0;
|
||||
}
|
||||
|
||||
/* reset timer configuration registers */
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->COMBINE = 0;
|
||||
|
||||
/* set prescale and mod registers to matching values for resolution and frequency */
|
||||
if (resolution > 0xffff || (resolution * frequency) > pwm_clk) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->SC = FTM_SC_PS((pwm_clk / (resolution * frequency)) - 1);
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->MOD = resolution;
|
||||
|
||||
/* set PWM mode */
|
||||
switch (mode) {
|
||||
case PWM_LEFT:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT |
|
||||
1 << FTM_CnSC_ELSB_SHIFT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PWM_RIGHT:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT |
|
||||
1 << FTM_CnSC_ELSA_SHIFT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PWM_CENTER:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT);
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->SC |= (1 << FTM_SC_CPWMS_SHIFT);
|
||||
break;
|
||||
}
|
||||
|
||||
/* enable timer ergo the PWM generation */
|
||||
pwm_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwm_set(pwm_t dev, int channel, unsigned int value)
|
||||
{
|
||||
FTM_Type *tim = NULL;
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
tim = PWM_0_DEV;
|
||||
#if PWM_0_CHANNELS > 0
|
||||
PWM_0_PORT_CH0->PCR[PWM_0_PIN_CH0] = PORT_PCR_MUX(PWM_0_PIN_AF_CH0);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 1
|
||||
PWM_0_PORT_CH1->PCR[PWM_0_PIN_CH1] = PORT_PCR_MUX(PWM_0_PIN_AF_CH1);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 2
|
||||
PWM_0_PORT_CH2->PCR[PWM_0_PIN_CH2] = PORT_PCR_MUX(PWM_0_PIN_AF_CH2);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 3
|
||||
PWM_0_PORT_CH3->PCR[PWM_0_PIN_CH3] = PORT_PCR_MUX(PWM_0_PIN_AF_CH3);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 4
|
||||
PWM_0_PORT_CH4->PCR[PWM_0_PIN_CH4] = PORT_PCR_MUX(PWM_0_PIN_AF_CH4);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 5
|
||||
PWM_0_PORT_CH5->PCR[PWM_0_PIN_CH5] = PORT_PCR_MUX(PWM_0_PIN_AF_CH5);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 6
|
||||
PWM_0_PORT_CH6->PCR[PWM_0_PIN_CH6] = PORT_PCR_MUX(PWM_0_PIN_AF_CH6);
|
||||
#endif
|
||||
#if PWM_0_CHANNELS > 7
|
||||
PWM_0_PORT_CH7->PCR[PWM_0_PIN_CH7] = PORT_PCR_MUX(PWM_0_PIN_AF_CH7);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
tim = PWM_1_DEV;
|
||||
PWM_1_PORT_CLKEN();
|
||||
#if PWM_1_CHANNELS > 0
|
||||
PWM_1_PORT_CH0->PCR[PWM_1_PIN_CH0] = PORT_PCR_MUX(PWM_1_PIN_AF_CH0);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 1
|
||||
PWM_1_PORT_CH1->PCR[PWM_1_PIN_CH1] = PORT_PCR_MUX(PWM_1_PIN_AF_CH1);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 2
|
||||
PWM_1_PORT_CH2->PCR[PWM_1_PIN_CH2] = PORT_PCR_MUX(PWM_1_PIN_AF_CH2);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 3
|
||||
PWM_1_PORT_CH3->PCR[PWM_1_PIN_CH3] = PORT_PCR_MUX(PWM_1_PIN_AF_CH3);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 4
|
||||
PWM_1_PORT_CH4->PCR[PWM_1_PIN_CH4] = PORT_PCR_MUX(PWM_1_PIN_AF_CH4);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 5
|
||||
PWM_1_PORT_CH5->PCR[PWM_1_PIN_CH5] = PORT_PCR_MUX(PWM_1_PIN_AF_CH5);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 6
|
||||
PWM_1_PORT_CH6->PCR[PWM_1_PIN_CH6] = PORT_PCR_MUX(PWM_1_PIN_AF_CH6);
|
||||
#endif
|
||||
#if PWM_1_CHANNELS > 7
|
||||
PWM_1_PORT_CH7->PCR[PWM_1_PIN_CH7] = PORT_PCR_MUX(PWM_1_PIN_AF_CH7);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
@ -166,36 +225,96 @@ int pwm_set(pwm_t dev, int channel, unsigned int value)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* norm value to maximum possible value */
|
||||
if (value > 0xffff) {
|
||||
value = 0xffff;
|
||||
/* disable write protect for changing settings */
|
||||
ftm->MODE = FTM_MODE_WPDIS_MASK;
|
||||
|
||||
/* reset timer match value */
|
||||
for (int i = 0; i < channels; i++) {
|
||||
ftm->CONTROLS[i].CnV = 0;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case 0:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH0].CnV = value;
|
||||
/* reset timer configuration registers */
|
||||
ftm->COMBINE = 0;
|
||||
ftm->CNTIN = 0;
|
||||
ftm->SWOCTRL = 0;
|
||||
|
||||
/* set prescale and mod registers to matching values for resolution and frequency */
|
||||
ftm->SC = FTM_SC_PS(prescaler);
|
||||
ftm->MOD = resolution - 1;
|
||||
|
||||
/* set PWM mode */
|
||||
uint32_t mode_mask = 0;
|
||||
switch (mode) {
|
||||
case PWM_LEFT:
|
||||
mode_mask = (1 << FTM_CnSC_MSB_SHIFT) | (1 << FTM_CnSC_ELSB_SHIFT);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH1].CnV = value;
|
||||
case PWM_RIGHT:
|
||||
mode_mask = (1 << FTM_CnSC_MSB_SHIFT) | (1 << FTM_CnSC_ELSA_SHIFT);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH2].CnV = value;
|
||||
case PWM_CENTER:
|
||||
mode_mask = (1 << FTM_CnSC_MSB_SHIFT);
|
||||
ftm->SC |= (1 << FTM_SC_CPWMS_SHIFT);
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck thinks ftmchan may be NULL here, but the variable is
|
||||
* assigned in all non-returning branches of the switch at the top of
|
||||
* this function. */
|
||||
/* cppcheck-suppress nullPointer ftmchan */
|
||||
ftm->CONTROLS[ftmchan[i]].CnSC = mode_mask;
|
||||
}
|
||||
|
||||
case 3:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH3].CnV = value;
|
||||
/* enable timer ergo the PWM generation */
|
||||
pwm_start(dev);
|
||||
|
||||
/* Return actual frequency */
|
||||
return (pwm_clk / (1 << prescaler)) / resolution;
|
||||
}
|
||||
|
||||
int pwm_set(pwm_t dev, int channel, unsigned int value)
|
||||
{
|
||||
FTM_Type *ftm;
|
||||
const uint8_t *ftmchan = NULL;
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
if (channel > PWM_0_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
ftm = PWM_0_DEV;
|
||||
ftmchan = &ftm0chan[0];
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
if (channel > PWM_1_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
ftm = PWM_1_DEV;
|
||||
ftmchan = &ftm1chan[0];
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* clamp value to maximum possible value */
|
||||
if (value > PWM_MAX_VALUE) {
|
||||
value = PWM_MAX_VALUE;
|
||||
}
|
||||
|
||||
/* cppcheck thinks ftmchan may be NULL here, but the variable is
|
||||
* assigned in all non-returning branches of the switch at the top of
|
||||
* this function. */
|
||||
/* cppcheck-suppress nullPointer */
|
||||
ftm->CONTROLS[ftmchan[channel]].CnV = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user