1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #6423 from haukepetersen/opt_lpc11_pwm

cpu/lpc11u34: remodeled PWM driver
This commit is contained in:
Peter Kietzmann 2017-01-20 18:11:31 +01:00 committed by GitHub
commit 5880047d76
3 changed files with 108 additions and 220 deletions

View File

@ -20,6 +20,8 @@
#ifndef PERIPH_CONF_H
#define PERIPH_CONF_H
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -88,44 +90,30 @@ extern "C" {
* @brief PWM configuration
* @{
*/
#define PWM_0_EN 1
#define PWM_0_CHANNELS 3
#define PWM_1_EN 1
#define PWM_1_CHANNELS 3
#define PWM_NUMOF (2U)
static const pwm_conf_t pwm_config[] = {
{
.dev = LPC_CT16B0,
.pins = {
&LPC_IOCON->PIO1_13,
&LPC_IOCON->PIO1_14,
&LPC_IOCON->PIO1_15
},
.clk_bit = BIT7,
.af = 0x02
},
{
.dev = LPC_CT32B0,
.pins = {
&LPC_IOCON->PIO1_24,
&LPC_IOCON->PIO1_25,
&LPC_IOCON->PIO1_26
},
.clk_bit = BIT9,
.af = 0x01
}
};
/* PWM0 common configuration */
#define PWM_0_DEV LPC_CT16B0
#define PWM_0_CLK BIT7
/* PWM_0 channel configuration */
#define PWM_0_CH0_EN 1
#define PWM_0_CH0_IOCON LPC_IOCON->PIO1_13
#define PWM_0_CH0_AF 0x82
#define PWM_0_CH1_EN 1
#define PWM_0_CH1_IOCON LPC_IOCON->PIO1_14
#define PWM_0_CH1_AF 0x82
#define PWM_0_CH2_EN 1
#define PWM_0_CH2_IOCON LPC_IOCON->PIO1_15
#define PWM_0_CH2_AF 0x82
/* PWM1 common configuration */
#define PWM_1_DEV LPC_CT32B0
#define PWM_1_CLK BIT9
/* PWM_1 channel configuration */
#define PWM_1_CH0_EN 1
#define PWM_1_CH0_IOCON LPC_IOCON->PIO1_24
#define PWM_1_CH0_AF 0x81
#define PWM_1_CH1_EN 1
#define PWM_1_CH1_IOCON LPC_IOCON->PIO1_25
#define PWM_1_CH1_AF 0x81
#define PWM_1_CH2_EN 1
#define PWM_1_CH2_IOCON LPC_IOCON->PIO1_26
#define PWM_1_CH2_AF 0x81
#define PWM_NUMOF (sizeof(pwm_config) / sizeof(pwm_config[0]))
/* @} */
#ifdef __cplusplus

View File

@ -21,7 +21,7 @@
#define PERIPH_CPU_H
#include <stdint.h>
#include "periph/dev_enums.h"
#include "cpu.h"
#ifdef __cplusplus
extern "C" {
@ -61,6 +61,11 @@ typedef uint16_t gpio_t;
*/
#define GPIO_PIN(port, pin) (gpio_t)((port << 16) | pin)
/**
* @brief Number of PWM channels per PWM peripheral
*/
#define PWM_CHAN_NUMOF (3U)
/**
* @brief Override the default GPIO mode values
* @{
@ -99,6 +104,21 @@ typedef enum {
/** @} */
#endif /* ndef DOXYGEN */
/**
* @brief PWM channel configuration
*/
/**
* @brief PWM configuration
*/
typedef struct {
LPC_CTxxBx_Type *dev;
__IO uint32_t *pins[PWM_CHAN_NUMOF]; /**< set to NULL if channel is not used */
uint16_t clk_bit;
uint8_t af;
} pwm_conf_t;
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* Copyright (C) 2015-2017 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
@ -14,219 +14,99 @@
* @brief CPU specific low-level PWM driver implementation for LPC11U34
*
* @author Paul RATHGEB <paul.rathgeb@skynet.be>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include "bitarithm.h"
#include "periph/gpio.h"
#include "board.h"
#include "periph_conf.h"
#include "periph/pwm.h"
/* guard file in case no PWM device is defined */
#include "periph/pwm.h"
#if (PWM_0_EN || PWM_1_EN)
#ifdef PWM_NUMOF
static inline LPC_CTxxBx_Type *dev(pwm_t pwm)
{
return pwm_config[pwm].dev;
}
/**
* @note The LPC11U34 doesn't support centerized alignements
*/
uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res)
uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
/* This CPU doesn't support a centerized alignement */
if (mode == PWM_CENTER) {
return 0;
}
/* Check if the frequency and resolution is applicable */
if (CLOCK_CORECLOCK / (res * freq) <= 0) {
return 0;
}
#if PWM_0_CH0_EN
PWM_0_CH0_IOCON = (PWM_0_CH0_IOCON & ~(BIT7 | 7)) | PWM_0_CH0_AF;
#endif
#if PWM_0_CH1_EN
PWM_0_CH1_IOCON = (PWM_0_CH1_IOCON & ~(BIT7 | 7)) | PWM_0_CH1_AF;
#endif
#if PWM_0_CH2_EN
PWM_0_CH2_IOCON = (PWM_0_CH2_IOCON & ~(BIT7 | 7)) | PWM_0_CH2_AF;
#endif
/* The configuration involve that the peripheral is powered */
pwm_poweron(dev);
/* Enable timer and keep it in reset state */
PWM_0_DEV->TCR = BIT0 | BIT1;
/* Set the prescaler (CLOCK_CORECLOCK / resolution) */
PWM_0_DEV->PR = (CLOCK_CORECLOCK / (res * freq));
/* Reset timer on MR3 */
PWM_0_DEV->MCR = BIT10;
assert((pwm < PWM_NUMOF) && (mode != PWM_CENTER));
/* Set PWM period */
PWM_0_DEV->MR0 = res;
PWM_0_DEV->MR1 = res;
PWM_0_DEV->MR2 = res;
PWM_0_DEV->MR3 = res - 1;
/* Set mode for channels 0..2 */
PWM_0_DEV->EMR |= ((mode + 1) << 4);
PWM_0_DEV->EMR |= ((mode + 1) << 6);
PWM_0_DEV->EMR |= ((mode + 1) << 8);
/* Enable PWM channels 0..2 */
PWM_0_DEV->PWMC = BIT0 | BIT1 | BIT2;
#endif /* PWM_0_EN */
#if PWM_1_EN
case PWM_1:
/* This CPU doesn't support a centerized alignement */
if (mode == PWM_CENTER) {
return 0;
}
/* Check if the frequency and resolution is applicable */
if (CLOCK_CORECLOCK / (res * freq) <= 0) {
return 0;
}
#if PWM_1_CH0_EN
PWM_1_CH0_IOCON = (PWM_1_CH0_IOCON & ~(BIT7 | 7)) | PWM_1_CH0_AF;
#endif
#if PWM_1_CH1_EN
PWM_1_CH1_IOCON = (PWM_1_CH1_IOCON & ~(BIT7 | 7)) | PWM_1_CH1_AF;
#endif
#if PWM_1_CH2_EN
PWM_1_CH2_IOCON = (PWM_1_CH2_IOCON & ~(BIT7 | 7)) | PWM_1_CH2_AF;
#endif
/* The configuration involve that the peripheral is powered */
pwm_poweron(dev);
/* Enable timer and keep it in reset state */
PWM_1_DEV->TCR = BIT0 | BIT1;
/* Set the prescaler (CLOCK_CORECLOCK / resolution) */
PWM_1_DEV->PR = (CLOCK_CORECLOCK / (res * freq));
/* Reset timer on MR3 */
PWM_1_DEV->MCR = BIT10;
/* Set PWM period */
PWM_1_DEV->MR0 = res;
PWM_1_DEV->MR1 = res;
PWM_1_DEV->MR2 = res;
PWM_1_DEV->MR3 = res - 1;
/* Set mode for channels 0..2 */
PWM_1_DEV->EMR |= ((mode + 1) << 4);
PWM_1_DEV->EMR |= ((mode + 1) << 6);
PWM_1_DEV->EMR |= ((mode + 1) << 8);
/* Enable PWM channels 0..2 */
PWM_1_DEV->PWMC = BIT0 | BIT1 | BIT2;
#endif /* PWM_1_EN */
/* make sure the given frequency settings are applicable */
if (CLOCK_CORECLOCK < (res * freq)) {
return 0;
}
/* setup pins */
for (unsigned i = 0; i < PWM_CHAN_NUMOF; i++) {
if (pwm_config[pwm].pins[i]) {
*(pwm_config[pwm].pins[i]) = pwm_config[pwm].af;
}
}
/* power on and configure the timer */
pwm_poweron(pwm);
/* enable the timer and keep it in reset state */
dev(pwm)->TCR = BIT0 | BIT1;
/* set prescaler */
dev(pwm)->PR = (CLOCK_CORECLOCK / (res * freq));
/* reset timer on MR3 */
dev(pwm)->MCR = BIT10;
/* set PWM period */
dev(pwm)->MR0 = res;
dev(pwm)->MR1 = res;
dev(pwm)->MR2 = res;
dev(pwm)->MR3 = (res - 1);
/* set mode for channels 0, 1, and 2 */
dev(pwm)->EMR = (((mode + 1) << 4) | ((mode + 1) << 6) | ((mode + 1) << 8));
/* enable channels 0, 1, and 2 */
dev(pwm)->PWMC = (BIT0 | BIT1 | BIT2);
return freq;
}
uint8_t pwm_channels(pwm_t dev)
uint8_t pwm_channels(pwm_t pwm)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
return PWM_0_CHANNELS;
#endif
#if PWM_1_EN
case PWM_1:
return PWM_1_CHANNELS;
#endif
default:
return 0;
}
assert(pwm < PWM_NUMOF);
return (uint8_t)PWM_CHAN_NUMOF;
}
void pwm_set(pwm_t dev, uint8_t channel, uint16_t value)
void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
if (channel <= 2) {
PWM_0_DEV->MR[channel] = PWM_0_DEV->MR3 - value;
}
break;
#endif
#if PWM_1_EN
case PWM_1:
if (channel <= 2) {
PWM_1_DEV->MR[channel] = PWM_1_DEV->MR3 - value;
}
break;
#endif
}
assert((pwm < PWM_NUMOF) && (channel < PWM_CHAN_NUMOF));
dev(pwm)->MR[channel] = dev(pwm)->MR3 - value;
}
void pwm_start(pwm_t dev)
void pwm_start(pwm_t pwm)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
/* Start the counter */
PWM_0_DEV->TCR &= ~BIT1;
break;
#endif
#if PWM_1_EN
case PWM_1:
/* Start the counter */
PWM_1_DEV->TCR &= ~BIT1;
break;
#endif
}
assert(pwm < PWM_NUMOF);
dev(pwm)->TCR &= ~(BIT1);
}
void pwm_stop(pwm_t dev)
void pwm_stop(pwm_t pwm)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
/* Stop the counter */
PWM_0_DEV->TCR |= BIT1;
break;
#endif
#if PWM_1_EN
case PWM_1:
/* Stop the counter */
PWM_1_DEV->TCR |= BIT1;
break;
#endif
}
assert(pwm < PWM_NUMOF);
dev(pwm)->TCR |= (BIT1);
}
void pwm_poweron(pwm_t dev)
void pwm_poweron(pwm_t pwm)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
/* Enable clock for PWM_0 */
LPC_SYSCON->SYSAHBCLKCTRL |= PWM_0_CLK;
break;
#endif
#if PWM_1_EN
case PWM_1:
/* Enable clock for PWM_1 */
LPC_SYSCON->SYSAHBCLKCTRL |= PWM_1_CLK;
break;
#endif
}
assert(pwm < PWM_NUMOF);
LPC_SYSCON->SYSAHBCLKCTRL |= pwm_config[pwm].clk_bit;
}
void pwm_poweroff(pwm_t dev)
void pwm_poweroff(pwm_t pwm)
{
switch (dev) {
#if PWM_0_EN
case PWM_0:
/* Disable clock for PWM_0 */
LPC_SYSCON->SYSAHBCLKCTRL &= ~PWM_0_CLK;
break;
#endif
#if PWM_1_EN
case PWM_1:
/* Disable clock for PWM_1 */
LPC_SYSCON->SYSAHBCLKCTRL &= ~PWM_1_CLK;
break;
#endif
}
assert(pwm < PWM_NUMOF);
LPC_SYSCON->SYSAHBCLKCTRL &= ~(pwm_config[pwm].clk_bit);
}
#endif /* (PWM_0_EN || PWM_1_EN) */
#endif /* PWM_NUMOF */