mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
Merge pull request #10498 from smlng/pr/adapt/7542
atmega: add periph_pwm (taken from #7542)
This commit is contained in:
commit
153759b926
@ -9,6 +9,7 @@ FEATURES_PROVIDED += periph_uart
|
||||
# Various other features (if any)
|
||||
ifeq (,$(filter jiminy-mega256rfr2,$(BOARD)))
|
||||
FEATURES_PROVIDED += arduino
|
||||
FEATURES_PROVIDED += periph_pwm
|
||||
endif
|
||||
|
||||
# The board MPU family (used for grouping by the CI system)
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
#ifndef PERIPH_CONF_H
|
||||
#define PERIPH_CONF_H
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -165,6 +167,46 @@ extern "C" {
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name PWM configuration
|
||||
*
|
||||
* The current implementation supports only 8-bit timers for PWM generation.
|
||||
* These timers are typically timer 0 and timer 2 in Atmega2560/1281/328p.
|
||||
*
|
||||
* Setting the first channel to GPIO_UNDEF allows multiple resolutions for the
|
||||
* PWM channel. Otherwise the resolution is fixed to 256, allowing duty cycle
|
||||
* values ranging from 0 to 255.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#if defined(CPU_ATMEGA328P)
|
||||
#define PWM_PINS_CH0 { GPIO_PIN(PORT_D, 6), GPIO_PIN(PORT_D, 5) }
|
||||
#define PWM_PINS_CH1 { GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_D, 3) }
|
||||
|
||||
#elif defined(CPU_ATMEGA2560)
|
||||
#define PWM_PINS_CH0 { GPIO_PIN(PORT_B, 7), GPIO_PIN(PORT_G, 5) }
|
||||
#define PWM_PINS_CH1 { GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_H, 6) }
|
||||
|
||||
#elif defined(CPU_ATMEGA1281)
|
||||
#define PWM_PINS_CH0 { GPIO_PIN(PORT_B, 7), GPIO_PIN(PORT_G, 5) }
|
||||
#define PWM_PINS_CH1 { GPIO_PIN(PORT_B, 4), GPIO_UNDEF }
|
||||
#endif
|
||||
|
||||
static const pwm_conf_t pwm_conf[] = {
|
||||
{
|
||||
.dev = MINI_TIMER0,
|
||||
.pin_ch = PWM_PINS_CH0,
|
||||
.div = MINI_TIMER0_DIV,
|
||||
},
|
||||
{
|
||||
.dev = MINI_TIMER2,
|
||||
.pin_ch = PWM_PINS_CH1,
|
||||
.div = MINI_TIMER2_DIV,
|
||||
}
|
||||
};
|
||||
#define PWM_NUMOF (sizeof(pwm_conf) / sizeof(pwm_conf[0]))
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -49,6 +49,16 @@ typedef struct {
|
||||
REG16 OCR[3]; /**< output compare */
|
||||
} mega_timer_t;
|
||||
|
||||
/**
|
||||
* @brief 8-bit timer register map
|
||||
*/
|
||||
typedef struct {
|
||||
REG8 CRA; /**< control A */
|
||||
REG8 CRB; /**< control B */
|
||||
REG8 CNT; /**< counter */
|
||||
REG8 OCR[2]; /**< output compare */
|
||||
} mini_timer_t;
|
||||
|
||||
/**
|
||||
* @brief UART register map
|
||||
*/
|
||||
@ -65,11 +75,21 @@ typedef struct {
|
||||
* @brief Timer register definitions and instances
|
||||
* @{
|
||||
*/
|
||||
#if defined(TCCR0A)
|
||||
#define MINI_TIMER0 ((mini_timer_t *)(uint16_t *)(&TCCR0A))
|
||||
#define MINI_TIMER0_DIV TIMER_DIV1_8_64_128_1024
|
||||
#endif
|
||||
|
||||
#if defined(TCCR1A)
|
||||
#define MEGA_TIMER1_BASE (uint16_t *)(&TCCR1A)
|
||||
#define MEGA_TIMER1 ((mega_timer_t *)MEGA_TIMER1_BASE)
|
||||
#endif
|
||||
|
||||
#if defined(TCCR2A)
|
||||
#define MINI_TIMER2 ((mini_timer_t *)(uint16_t *)(&TCCR2A))
|
||||
#define MINI_TIMER2_DIV TIMER_DIV1_8_32_64_128_256_1024
|
||||
#endif
|
||||
|
||||
#if defined(TCCR3A)
|
||||
#define MEGA_TIMER3_BASE (uint16_t *)(&TCCR3A)
|
||||
#define MEGA_TIMER3 ((mega_timer_t *)MEGA_TIMER3_BASE)
|
||||
|
||||
@ -23,10 +23,27 @@
|
||||
#ifndef PERIPH_CPU_COMMON_H
|
||||
#define PERIPH_CPU_COMMON_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/**
|
||||
* @brief Overwrite the default gpio_t type definition
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_GPIO_T
|
||||
typedef uint8_t gpio_t;
|
||||
/** @} */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Definition of a fitting UNDEF value
|
||||
*/
|
||||
#define GPIO_UNDEF (0xff)
|
||||
|
||||
/**
|
||||
* @brief Define a CPU specific GPIO pin generator macro
|
||||
*/
|
||||
@ -108,6 +125,27 @@ typedef enum {
|
||||
} spi_clk_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Bitmasks indicating which are the possible dividers for a timer
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_DIV1_8_64_128_1024 = 0x549, /**< 1/{1,8,64,128,1024} */
|
||||
TIMER_DIV1_8_32_64_128_256_1024 = 0x5E9, /**< 1/{1,8,32,64,128,256,1024} */
|
||||
} timer_div_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief PWM configuration
|
||||
* @{
|
||||
*/
|
||||
typedef struct {
|
||||
mini_timer_t *dev; /**< Timer used */
|
||||
gpio_t pin_ch[2]; /**< Output Pins */
|
||||
timer_div_t div; /**< Timer divider mask */
|
||||
} pwm_conf_t;
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
188
cpu/atmega_common/periph/pwm.c
Normal file
188
cpu/atmega_common/periph/pwm.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Víctor Ariño
|
||||
*
|
||||
* 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_atmega_common
|
||||
* @ingroup drivers_periph_pwm
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level PWM driver implementation
|
||||
*
|
||||
* @author Víctor Ariño <victor@lebrush.org>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "assert.h"
|
||||
#include "periph/pwm.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#ifdef PWM_NUMOF
|
||||
|
||||
#define WGM0 0
|
||||
#define WGM1 1
|
||||
#define WGM2 3
|
||||
#define COMA0 6
|
||||
#define COMA1 7
|
||||
|
||||
static struct {
|
||||
uint8_t CRA;
|
||||
uint8_t CRB;
|
||||
uint8_t res;
|
||||
} state[PWM_NUMOF];
|
||||
|
||||
static inline unsigned get_prescaler(pwm_t dev, uint32_t *scale)
|
||||
{
|
||||
uint16_t divmask = pwm_conf[dev].div;
|
||||
uint32_t target = *scale;
|
||||
uint8_t div, pre = 0;
|
||||
|
||||
for (div = 0; divmask > 0; div++) {
|
||||
if (divmask & 0x01) {
|
||||
pre++;
|
||||
*scale = (uint32_t)(1 << div);
|
||||
if (*scale >= target) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
divmask >>= 1;
|
||||
}
|
||||
|
||||
return pre;
|
||||
}
|
||||
|
||||
uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res)
|
||||
{
|
||||
/* only left implemented, max resolution 256 */
|
||||
assert(dev < PWM_NUMOF && mode == PWM_LEFT && res <= 256);
|
||||
/* resolution != 256 only valid if ch0 not used */
|
||||
assert(!(res != 256 && pwm_conf[dev].pin_ch[0] != GPIO_UNDEF));
|
||||
|
||||
/* disable PWM */
|
||||
pwm_conf[dev].dev->CRA = 0x00;
|
||||
pwm_conf[dev].dev->CRB = 0x00;
|
||||
pwm_conf[dev].dev->OCR[0] = 0;
|
||||
pwm_conf[dev].dev->OCR[1] = 0;
|
||||
|
||||
/* disable power reduction */
|
||||
if (dev) {
|
||||
power_timer2_enable();
|
||||
}
|
||||
else {
|
||||
power_timer0_enable();
|
||||
}
|
||||
|
||||
/* find out prescaler */
|
||||
uint32_t scale = (CLOCK_CORECLOCK / (freq * (uint32_t)res));
|
||||
unsigned pre = get_prescaler(dev, &scale);
|
||||
freq = (CLOCK_CORECLOCK / (scale * (uint32_t)res));
|
||||
|
||||
/* compute register values and enable pins */
|
||||
uint8_t cra = _BV(WGM1) | _BV(WGM0);
|
||||
uint8_t crb = 0;
|
||||
|
||||
res -= 1;
|
||||
/* configure pins and resolution. Output must be low at initialization.
|
||||
* Force the pin low to avoid flickering. */
|
||||
if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) {
|
||||
gpio_init(pwm_conf[dev].pin_ch[0], GPIO_OUT);
|
||||
gpio_clear(pwm_conf[dev].pin_ch[0]);
|
||||
}
|
||||
else {
|
||||
crb |= _BV(WGM2);
|
||||
pwm_conf[dev].dev->OCR[0] = (uint8_t)res;
|
||||
}
|
||||
|
||||
if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) {
|
||||
gpio_init(pwm_conf[dev].pin_ch[1], GPIO_OUT);
|
||||
gpio_clear(pwm_conf[dev].pin_ch[1]);
|
||||
}
|
||||
|
||||
pwm_conf[dev].dev->CRA = cra;
|
||||
pwm_conf[dev].dev->CRB = crb | (pre);
|
||||
state[dev].CRA = cra;
|
||||
state[dev].CRB = crb | (pre);
|
||||
state[dev].res = res;
|
||||
|
||||
/* return real frequency */
|
||||
return freq;
|
||||
}
|
||||
|
||||
uint8_t pwm_channels(pwm_t dev)
|
||||
{
|
||||
assert(dev < PWM_NUMOF);
|
||||
|
||||
/* a pwm with no channels enabled makes no sense. Assume at least one is
|
||||
* enabled */
|
||||
if (pwm_conf[dev].pin_ch[0] == GPIO_UNDEF ||
|
||||
pwm_conf[dev].pin_ch[1] == GPIO_UNDEF) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
void pwm_set(pwm_t dev, uint8_t ch, uint16_t value)
|
||||
{
|
||||
assert(dev < PWM_NUMOF && ch <= 1 && pwm_conf[dev].pin_ch[ch] != GPIO_UNDEF);
|
||||
|
||||
/* output flickers when duty cycle is 0 or 100%. Simply force the pin
|
||||
* low or high respectively to have a clean output. */
|
||||
uint8_t bit = (_BV(COMA1) >> (ch << 1));
|
||||
if (value >= state[dev].res) {
|
||||
pwm_conf[dev].dev->CRA &= ~bit;
|
||||
gpio_set(pwm_conf[dev].pin_ch[ch]);
|
||||
}
|
||||
else if (value == 0) {
|
||||
pwm_conf[dev].dev->CRA &= ~bit;
|
||||
gpio_clear(pwm_conf[dev].pin_ch[ch]);
|
||||
}
|
||||
else {
|
||||
pwm_conf[dev].dev->OCR[ch] = value;
|
||||
pwm_conf[dev].dev->CRA |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_poweron(pwm_t dev)
|
||||
{
|
||||
assert(dev < PWM_NUMOF);
|
||||
/* disable power reduction */
|
||||
if (dev) {
|
||||
power_timer2_enable();
|
||||
}
|
||||
else {
|
||||
power_timer0_enable();
|
||||
}
|
||||
pwm_conf[dev].dev->CRA = state[dev].CRA;
|
||||
pwm_conf[dev].dev->CRB = state[dev].CRB;
|
||||
}
|
||||
|
||||
void pwm_poweroff(pwm_t dev)
|
||||
{
|
||||
assert(dev < PWM_NUMOF);
|
||||
pwm_conf[dev].dev->CRA = 0x00;
|
||||
pwm_conf[dev].dev->CRB = 0x00;
|
||||
/* disable power reduction */
|
||||
if (dev) {
|
||||
power_timer2_disable();
|
||||
}
|
||||
else {
|
||||
power_timer0_disable();
|
||||
}
|
||||
|
||||
if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) {
|
||||
gpio_clear(pwm_conf[dev].pin_ch[0]);
|
||||
}
|
||||
|
||||
if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) {
|
||||
gpio_clear(pwm_conf[dev].pin_ch[1]);
|
||||
}
|
||||
}
|
||||
#endif /* PWM_NUMOF */
|
||||
@ -1,6 +1,8 @@
|
||||
BOARD ?= samr21-xpro
|
||||
include ../Makefile.tests_common
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-uno
|
||||
|
||||
FEATURES_REQUIRED = periph_pwm
|
||||
|
||||
USEMODULE += xtimer
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user