Merge pull request #1467 from haukepetersen/add_stm32f4_pwm
cpu: added pwm driver for the stm32f4dev/timer
commit
b6cf2fa37f
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_stm32f4
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level GPIO driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <mail@haukepetersen.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/pwm.h"
|
||||
#include "periph_conf.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)
|
||||
{
|
||||
TIM_TypeDef *tim = NULL;
|
||||
GPIO_TypeDef *port = NULL;
|
||||
uint32_t pins[PWM_MAX_CHANNELS];
|
||||
uint32_t af = 0;
|
||||
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 = PWM_0_PORT;
|
||||
pins[0] = PWM_0_PIN_CH0;
|
||||
pins[1] = PWM_0_PIN_CH1;
|
||||
pins[2] = PWM_0_PIN_CH2;
|
||||
pins[3] = PWM_0_PIN_CH3;
|
||||
af = PWM_0_PIN_AF;
|
||||
channels = PWM_0_CHANNELS;
|
||||
pwm_clk = PWM_0_CLK;
|
||||
PWM_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
case PWM_1:
|
||||
tim = PWM_1_DEV;
|
||||
port = PWM_1_PORT;
|
||||
pins[0] = PWM_1_PIN_CH0;
|
||||
pins[1] = PWM_1_PIN_CH1;
|
||||
pins[2] = PWM_1_PIN_CH2;
|
||||
pins[3] = PWM_1_PIN_CH3;
|
||||
af = PWM_1_PIN_AF;
|
||||
channels = PWM_1_CHANNELS;
|
||||
pwm_clk = PWM_1_CLK;
|
||||
PWM_1_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* setup pins: alternate function */
|
||||
for (int i = 0; i < channels; i++) {
|
||||
port->MODER &= ~(3 << (pins[i] * 2));
|
||||
port->MODER |= (2 << (pins[i] * 2));
|
||||
if (pins[i] < 8) {
|
||||
port->AFR[0] &= ~(0xf << (pins[i] * 4));
|
||||
port->AFR[0] |= (af << (pins[i] * 4));
|
||||
} else {
|
||||
port->AFR[1] &= ~(0xf << ((pins[i] - 8) * 4));
|
||||
port->AFR[1] |= (af << ((pins[i] - 8) * 4));
|
||||
}
|
||||
}
|
||||
|
||||
/* reset timer configuration registers */
|
||||
tim->CR1 = 0;
|
||||
tim->CR2 = 0;
|
||||
tim->CCMR1 = 0;
|
||||
tim->CCMR2 = 0;
|
||||
|
||||
/* set c/c register to initial 0 */
|
||||
tim->CCR1 = 0;
|
||||
tim->CCR2 = 0;
|
||||
tim->CCR3 = 0;
|
||||
tim->CCR4 = 0;
|
||||
|
||||
/* set prescale and auto-reload registers to matching values for resolution and frequency */
|
||||
if (resolution > 0xffff || (resolution * frequency) > pwm_clk) {
|
||||
return -2;
|
||||
}
|
||||
tim->PSC = (pwm_clk / (resolution * frequency)) - 1;
|
||||
tim->ARR = resolution - 1;
|
||||
|
||||
/* set PWM mode */
|
||||
switch (mode) {
|
||||
case PWM_LEFT:
|
||||
tim->CCMR1 |= (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
|
||||
TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2);
|
||||
tim->CCMR2 |= (TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
|
||||
TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2);
|
||||
break;
|
||||
case PWM_RIGHT:
|
||||
tim->CCMR1 |= (TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
|
||||
TIM_CCMR1_OC2M_0 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2);
|
||||
tim->CCMR2 |= (TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
|
||||
TIM_CCMR2_OC4M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2);
|
||||
break;
|
||||
case PWM_CENTER:
|
||||
tim->CR1 |= (TIM_CR1_CMS_0 | TIM_CR1_CMS_1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* enable output on PWM pins */
|
||||
tim->CCER = (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E);
|
||||
|
||||
/* enable PWM outputs */
|
||||
tim->BDTR = TIM_BDTR_MOE;
|
||||
|
||||
/* enable timer ergo the PWM generation */
|
||||
pwm_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwm_set(pwm_t dev, int channel, unsigned int value)
|
||||
{
|
||||
TIM_TypeDef *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
|
||||
}
|
||||
|
||||
/* norm value to maximum possible value */
|
||||
if (value > 0xffff) {
|
||||
value = 0xffff;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case 0:
|
||||
tim->CCR1 = value;
|
||||
break;
|
||||
case 1:
|
||||
tim->CCR2 = value;
|
||||
break;
|
||||
case 2:
|
||||
tim->CCR3 = value;
|
||||
break;
|
||||
case 3:
|
||||
tim->CCR4 = 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->CR1 |= TIM_CR1_CEN;
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
case PWM_1:
|
||||
PWM_1_DEV->CR1 |= TIM_CR1_CEN;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_stop(pwm_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
case PWM_0:
|
||||
PWM_0_DEV->CR1 &= ~(TIM_CR1_CEN);
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
case PWM_1:
|
||||
PWM_1_DEV->CR1 &= ~(TIM_CR1_CEN);
|
||||
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 */
|
@ -0,0 +1,11 @@
|
||||
export APPLICATION = periph_pwm
|
||||
include ../Makefile.tests_common
|
||||
|
||||
BOARD_BLACKLIST := chronos mbed_lpc1768 msb-430 msb-430h native qemu-i386 redbee-econotag telosb \
|
||||
wsn430-v1_3b wsn430-v1_4 z1
|
||||
# all listed boards: no periph_conf.h defined,
|
||||
|
||||
USEMODULE += vtimer
|
||||
DISABLE_MODULE += auto_init
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
@ -0,0 +1,8 @@
|
||||
Expected result
|
||||
===============
|
||||
If everything is running as supposed to, you should see a 1KHz PWM with oscillating duty cycle
|
||||
on each channel of the selected PWM device.
|
||||
|
||||
Background
|
||||
==========
|
||||
Test for the low-level PWM driver.
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Test for low-level PWM drivers
|
||||
*
|
||||
* This test initializes the given PWM device to run at 1KHz with a 1000 step resolution.
|
||||
*
|
||||
* The PWM is then continuously oscillating it's duty cycle between 0% to 100% every 1s on
|
||||
* every channel.
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "board.h"
|
||||
#include "vtimer.h"
|
||||
#include "periph/pwm.h"
|
||||
|
||||
/* only compile this test if PWM devices are defined */
|
||||
#if PWM_NUMOF
|
||||
|
||||
#define WAIT (10000)
|
||||
#define STEP (10)
|
||||
|
||||
#define DEV PWM_0
|
||||
#define CHANNELS PWM_0_CHANNELS
|
||||
#define MODE PWM_LEFT
|
||||
|
||||
#define FREQU (1000U)
|
||||
#define STEPS (1000U)
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int res;
|
||||
int state = 0;
|
||||
int step = STEP;
|
||||
|
||||
puts("\nRIOT PWM test");
|
||||
puts("Connect an LED or scope to PWM pins to see something\n");
|
||||
|
||||
res = pwm_init(DEV, MODE, FREQU, STEPS);
|
||||
if (res == 0) {
|
||||
puts("PWM successfully initialized.\n");
|
||||
}
|
||||
else {
|
||||
puts("Errors while initializing PWM");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
for (int i = 0; i < CHANNELS; i++) {
|
||||
pwm_set(DEV, i, state);
|
||||
}
|
||||
|
||||
state += step;
|
||||
if (state <= 0 || state >= STEPS) {
|
||||
step = -step;
|
||||
}
|
||||
|
||||
vtimer_usleep(WAIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("\nRIOT PWM test");
|
||||
puts("There are no PWM devices defined for this board!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* PWM_NUMOF */
|
Loading…
Reference in New Issue