2015-12-13 11:00:06 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 Engineering-Spirit
|
2016-04-14 14:44:19 +02:00
|
|
|
* Copyright (C) 2016 OTA keys S.A.
|
2015-12-13 11:00:06 +01:00
|
|
|
*
|
|
|
|
* 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_stm32f2
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @brief Low-level PWM driver implementation
|
|
|
|
*
|
|
|
|
* @author Hauke Petersen <mail@haukepetersen.de>
|
|
|
|
* @author Fabian Nack <nack@inf.fu-berlin.de>
|
|
|
|
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
2016-04-14 14:44:19 +02:00
|
|
|
* @author Aurelien Gonce <aurelien.gonce@altran.fr>
|
2015-12-13 11:00:06 +01:00
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "periph/pwm.h"
|
|
|
|
#include "periph_conf.h"
|
2016-04-14 14:44:19 +02:00
|
|
|
#include "periph/timer.h"
|
2015-12-13 11:00:06 +01:00
|
|
|
|
|
|
|
/* ignore file in case no PWM devices are defined */
|
2016-04-14 14:44:19 +02:00
|
|
|
#if (PWM_NUMOF > 0)
|
2015-12-13 11:00:06 +01:00
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
/**
|
|
|
|
* @brief Get the timer device
|
|
|
|
*/
|
|
|
|
static inline TIM_TypeDef *get_tim_dev(pwm_t tim)
|
2015-12-13 11:00:06 +01:00
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
return timer_config[tim].dev;
|
|
|
|
}
|
2015-12-13 11:00:06 +01:00
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
/**
|
|
|
|
* @brief Get the pwm device
|
|
|
|
*/
|
|
|
|
static inline GPIO_TypeDef *get_pwm_port(pwm_t pwm)
|
|
|
|
{
|
|
|
|
return pwm_config[pwm].port;
|
|
|
|
}
|
2015-12-13 11:00:06 +01:00
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res)
|
|
|
|
{
|
|
|
|
GPIO_TypeDef *port = get_pwm_port(dev);
|
|
|
|
tim_t tim = pwm_config[dev].tim;
|
|
|
|
TIM_TypeDef *timer_dev = get_tim_dev(tim);
|
|
|
|
uint8_t channels = pwm_channels(tim);
|
|
|
|
uint32_t pins[channels];
|
|
|
|
|
|
|
|
/* enable timer peripheral clock */
|
|
|
|
pwm_poweron(tim);
|
|
|
|
|
|
|
|
/* pins configuration */
|
|
|
|
pins[0] = pwm_config[dev].CH0;
|
|
|
|
if (channels > 1) {
|
|
|
|
pins[1] = pwm_config[dev].CH1;
|
|
|
|
}
|
|
|
|
if (channels > 2) {
|
|
|
|
pins[2] = pwm_config[dev].CH2;
|
|
|
|
}
|
|
|
|
if (channels > 3) {
|
|
|
|
pins[3] = pwm_config[dev].CH3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable pwm peripheral */
|
|
|
|
if (pwm_config[dev].bus == AHB1) {
|
|
|
|
RCC->AHB1ENR |= pwm_config[dev].rcc_mask;
|
|
|
|
} else if (pwm_config[dev].bus == AHB2) {
|
|
|
|
RCC->AHB2ENR |= pwm_config[dev].rcc_mask;
|
|
|
|
} else {
|
|
|
|
RCC->AHB3ENR |= pwm_config[dev].rcc_mask;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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));
|
2016-04-14 14:44:19 +02:00
|
|
|
port->AFR[0] |= (pwm_config[dev].AF << (pins[i] * 4));
|
2015-12-13 11:00:06 +01:00
|
|
|
} else {
|
|
|
|
port->AFR[1] &= ~(0xf << ((pins[i] - 8) * 4));
|
2016-04-14 14:44:19 +02:00
|
|
|
port->AFR[1] |= (pwm_config[dev].AF << ((pins[i] - 8) * 4));
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset C/C and timer configuration register */
|
|
|
|
switch (channels) {
|
|
|
|
case 4:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR4 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
/* Fall through */
|
|
|
|
case 3:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR3 = 0;
|
|
|
|
timer_dev->CR2 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
/* Fall through */
|
|
|
|
case 2:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR2 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
/* Fall through */
|
|
|
|
case 1:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR1 = 0;
|
|
|
|
timer_dev->CR1 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set prescale and auto-reload registers to matching values for resolution and frequency */
|
2016-04-14 14:44:19 +02:00
|
|
|
if (res > 0xffff || (res * freq) > timer_config[tim].freq) {
|
|
|
|
return 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->PSC = (timer_config[tim].freq / (res * freq)) - 1;
|
|
|
|
timer_dev->ARR = res - 1;
|
2015-12-13 11:00:06 +01:00
|
|
|
/* calculate the actual PWM frequency */
|
2016-04-14 14:44:19 +02:00
|
|
|
freq = (timer_config[tim].freq / (res * (timer_dev->PSC + 1)));
|
2015-12-13 11:00:06 +01:00
|
|
|
|
|
|
|
/* set PWM mode */
|
|
|
|
switch (mode) {
|
|
|
|
case PWM_LEFT:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
|
|
|
|
TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2);
|
2015-12-13 11:00:06 +01:00
|
|
|
if (channels > 2) {
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCMR2 = (TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
|
|
|
|
TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PWM_RIGHT:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->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);
|
2015-12-13 11:00:06 +01:00
|
|
|
if (channels > 2) {
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->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);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PWM_CENTER:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCMR1 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
if (channels > 2) {
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCMR2 = 0;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CR1 |= (TIM_CR1_CMS_0 | TIM_CR1_CMS_1);
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable output on PWM pins */
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCER = (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E);
|
2015-12-13 11:00:06 +01:00
|
|
|
|
|
|
|
/* enable PWM outputs */
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->BDTR = TIM_BDTR_MOE;
|
2015-12-13 11:00:06 +01:00
|
|
|
|
|
|
|
/* enable timer ergo the PWM generation */
|
2016-04-14 14:44:19 +02:00
|
|
|
pwm_start(tim);
|
|
|
|
|
|
|
|
return freq;
|
|
|
|
}
|
2015-12-13 11:00:06 +01:00
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
uint8_t pwm_channels(pwm_t dev) {
|
|
|
|
return (timer_config[dev].channels);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
void pwm_set(pwm_t dev, uint8_t channel, uint16_t value)
|
2015-12-13 11:00:06 +01:00
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
tim_t tim = pwm_config[dev].tim;
|
|
|
|
TIM_TypeDef *timer_dev = get_tim_dev(tim);
|
2015-12-13 11:00:06 +01:00
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
if (channel >= pwm_channels(tim)) {
|
|
|
|
return;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* norm value to maximum possible value */
|
2016-04-14 14:44:19 +02:00
|
|
|
if (value > timer_dev->ARR) {
|
|
|
|
value = (uint16_t) timer_dev->ARR;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (channel) {
|
|
|
|
case 0:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR1 = value;
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
case 1:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR2 = value;
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
case 2:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR3 = value;
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
case 3:
|
2016-04-14 14:44:19 +02:00
|
|
|
timer_dev->CCR4 = value;
|
2015-12-13 11:00:06 +01:00
|
|
|
break;
|
|
|
|
default:
|
2016-04-14 14:44:19 +02:00
|
|
|
break;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwm_start(pwm_t dev)
|
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
get_tim_dev(dev)->CR1 |= TIM_CR1_CEN;
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pwm_stop(pwm_t dev)
|
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
get_tim_dev(dev)->CR1 &= ~(TIM_CR1_CEN);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pwm_poweron(pwm_t dev)
|
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
periph_clk_en(timer_config[dev].bus, timer_config[dev].rcc_mask);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pwm_poweroff(pwm_t dev)
|
|
|
|
{
|
2016-04-14 14:44:19 +02:00
|
|
|
periph_clk_dis(timer_config[dev].bus, timer_config[dev].rcc_mask);
|
2015-12-13 11:00:06 +01:00
|
|
|
}
|
|
|
|
|
2016-04-14 14:44:19 +02:00
|
|
|
#endif /* PWM_NUMOF > 0*/
|