1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/kinetis_common/pwm.c
Johann F c0628a3058 cpu/kinetis_common: initial import for kinetis_common
add peripheral drivers for Freescale Kinetis MCUs:
    adc driver
    cpuid driver
    gpio driver
    hwtimer_arch driver (hwtimer used Low Power Timer)
    i2c driver (master mode only)
    mcg driver
    pwm driver
    random_rnga driver
    random_rngb driver
    rtc driver
    spi driver
    timer driver (timer used Periodic Interrupt Timer)
    uart driver
  add doc.txt (configuration examples)

  random_rnga: Update RNGA driver in preparation for RNGB driver.
  random_rngb: Add RNGB driver.
  spi: refactor SPI to work for multiple CTARS, add spi_acquire, spi_release
  gpio: Add gpio_irq_enable, gpio_irq_disable. Refactor GPIO.
  gpio: Add gpio_irq_enable, gpio_irq_disable.
  gpio: Refactor ISR functions to work with all GPIOs (0-31) and all ports (PORTA-PORTH)
  adc: Refactor ADC, add calibration and scaling.
    Added integer scaling of results in adc_map.
    Handle precision setting in adc_init.
    Set ADC clock divider depending on module clock.
    Add ADC_1 as a possible device.
    Add ADC calibration procedure according to K60 ref manual.
    Handle ADC pins which are not part of the pin function mux.
  Signed-off-by: Joakim Gebart <joakim.gebart@eistec.se>
2015-02-04 14:50:54 +01:00

275 lines
6.0 KiB
C

/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
*
* 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_kinetis_common_pwm
*
* @{
*
* @file
* @brief Low-level PWM driver implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Johann Fischer <j.fischer@phytec.de>
* @author Jonas Remmert <j.remmert@phytec.de>
*
* @}
*/
#include <stdint.h>
#include <string.h>
#include "cpu.h"
#include "periph/pwm.h"
#include "periph_conf.h"
#include "hwtimer.h"
/* ignore file in case no PWM devices are defined */
#if PWM_NUMOF
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];
int channels = 0;
uint32_t pwm_clk = 0;
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;
break;
#endif
#if PWM_1_EN
case PWM_1:
tim = PWM_1_DEV;
break;
#endif
default:
return -1;
}
/* norm value to maximum possible value */
if (value > 0xffff) {
value = 0xffff;
}
switch (channel) {
case 0:
/* cppcheck-suppress nullPointer */
tim->CONTROLS[PWM_0_FTMCHAN_CH0].CnV = value;
break;
case 1:
/* cppcheck-suppress nullPointer */
tim->CONTROLS[PWM_0_FTMCHAN_CH1].CnV = value;
break;
case 2:
/* cppcheck-suppress nullPointer */
tim->CONTROLS[PWM_0_FTMCHAN_CH2].CnV = value;
break;
case 3:
/* cppcheck-suppress nullPointer */
tim->CONTROLS[PWM_0_FTMCHAN_CH3].CnV = value;
break;
default:
return -1;
}
return 0;
}
void pwm_start(pwm_t dev)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
PWM_0_DEV->SC |= FTM_SC_CLKS(1);
break;
#endif
#if PWM_1_EN
case PWM_1:
PWM_1_DEV->SC |= FTM_SC_CLKS(1);
break;
#endif
}
}
void pwm_stop(pwm_t dev)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
PWM_0_DEV->SC &= ~FTM_SC_CLKS_MASK;
break;
#endif
#if PWM_1_EN
case PWM_1:
PWM_1_DEV->SC &= ~FTM_SC_CLKS_MASK;
break;
#endif
}
}
void pwm_poweron(pwm_t dev)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
PWM_0_CLKEN();
break;
#endif
#if PWM_1_EN
case PWM_1:
PWM_1_CLKEN();
break;
#endif
}
}
void pwm_poweroff(pwm_t dev)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
PWM_0_CLKDIS();
break;
#endif
#if PWM_1_EN
case PWM_1:
PWM_1_CLKDIS();
break;
#endif
}
}
#endif /* PWM_NUMOF */