mirror of https://github.com/Desuuuu/klipper.git
stm32: Add hardware PWM support for STM32F1
This adds hardware PWM support for STM32F1 processors. This should also work for STM32 F0/F2/F4 processors, but I don't have one of those to test it. Signed-off-by: Michael Kurz <michi.kurz@gmail.com> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
39188e1cf3
commit
59c4c49893
|
@ -27,6 +27,7 @@ choice
|
|||
config MACH_STM32F103
|
||||
bool "STM32F103"
|
||||
select MACH_STM32F1
|
||||
select HAVE_GPIO_HARD_PWM
|
||||
config MACH_STM32F207
|
||||
bool "STM32F207"
|
||||
select MACH_STM32F2
|
||||
|
|
|
@ -48,6 +48,7 @@ serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c
|
|||
src-$(CONFIG_SERIAL) += $(serial-src-y) generic/serial_irq.c
|
||||
src-$(CONFIG_CANSERIAL) += stm32/can.c ../lib/fast-hash/fasthash.c
|
||||
src-$(CONFIG_CANSERIAL) += generic/canbus.c
|
||||
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += stm32/hard_pwm.c
|
||||
|
||||
dirs-$(CONFIG_CANSERIAL) += lib/fast-hash
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@ struct gpio_in gpio_in_setup(uint32_t pin, int32_t pull_up);
|
|||
void gpio_in_reset(struct gpio_in g, int32_t pull_up);
|
||||
uint8_t gpio_in_read(struct gpio_in g);
|
||||
|
||||
struct gpio_pwm {
|
||||
void *reg;
|
||||
};
|
||||
struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val);
|
||||
void gpio_pwm_write(struct gpio_pwm g, uint32_t val);
|
||||
|
||||
struct gpio_adc {
|
||||
void *adc;
|
||||
uint32_t chan;
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// Hardware PWM support on stm32
|
||||
//
|
||||
// Copyright (C) 2021 Michael Kurz <michi.kurz@gmail.com>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include "board/irq.h" // irq_save
|
||||
#include "command.h" // shutdown
|
||||
#include "gpio.h" // gpio_pwm_write
|
||||
#include "internal.h" // GPIO
|
||||
#include "sched.h" // sched_shutdown
|
||||
|
||||
#define MAX_PWM 255
|
||||
DECL_CONSTANT("PWM_MAX", MAX_PWM);
|
||||
|
||||
struct gpio_pwm_info {
|
||||
TIM_TypeDef* timer;
|
||||
uint8_t pin, channel, function;
|
||||
};
|
||||
|
||||
static const struct gpio_pwm_info pwm_regs[] = {
|
||||
{TIM2, GPIO('A', 0), 1, GPIO_FUNCTION(2)},
|
||||
{TIM2, GPIO('A', 1), 2, GPIO_FUNCTION(2)},
|
||||
{TIM2, GPIO('A', 2), 3, GPIO_FUNCTION(2)},
|
||||
{TIM2, GPIO('A', 3), 4, GPIO_FUNCTION(2)},
|
||||
{TIM2, GPIO('A', 15), 1, GPIO_FUNCTION(1)},
|
||||
{TIM2, GPIO('B', 3), 2, GPIO_FUNCTION(1)},
|
||||
{TIM2, GPIO('B', 10), 3, GPIO_FUNCTION(1)},
|
||||
{TIM2, GPIO('B', 11), 4, GPIO_FUNCTION(1)},
|
||||
{TIM3, GPIO('A', 6), 1, GPIO_FUNCTION(1)},
|
||||
{TIM3, GPIO('A', 7), 2, GPIO_FUNCTION(1)},
|
||||
{TIM3, GPIO('B', 0), 3, GPIO_FUNCTION(1)},
|
||||
{TIM3, GPIO('B', 1), 4, GPIO_FUNCTION(1)},
|
||||
{TIM3, GPIO('C', 6), 1, GPIO_FUNCTION(2)},
|
||||
{TIM3, GPIO('C', 7), 2, GPIO_FUNCTION(2)},
|
||||
{TIM3, GPIO('C', 8), 3, GPIO_FUNCTION(2)},
|
||||
{TIM3, GPIO('C', 9), 4, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('D', 12), 1, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('D', 13), 2, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('D', 14), 3, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('D', 15), 4, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('B', 6), 1, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('B', 7), 2, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('B', 8), 3, GPIO_FUNCTION(2)},
|
||||
{TIM4, GPIO('B', 9), 4, GPIO_FUNCTION(2)}
|
||||
};
|
||||
|
||||
struct gpio_pwm
|
||||
gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val){
|
||||
// Find pin in pwm_regs table
|
||||
const struct gpio_pwm_info* p = pwm_regs;
|
||||
for (;; p++) {
|
||||
if (p >= &pwm_regs[ARRAY_SIZE(pwm_regs)])
|
||||
shutdown("Not a valid PWM pin");
|
||||
if (p->pin == pin)
|
||||
break;
|
||||
}
|
||||
|
||||
// Map cycle_time to pwm clock divisor
|
||||
uint32_t pclk = get_pclock_frequency((uint32_t)p->timer);
|
||||
uint32_t pclock_div = CONFIG_CLOCK_FREQ / pclk;
|
||||
if (pclock_div > 1)
|
||||
pclock_div /= 2; // Timers run at twice the normal pclock frequency
|
||||
uint32_t prescaler = cycle_time / (pclock_div * (MAX_PWM - 1));
|
||||
if (prescaler > 0) {
|
||||
prescaler -= 1;
|
||||
} else if (prescaler > UINT16_MAX) {
|
||||
prescaler = UINT16_MAX;
|
||||
}
|
||||
|
||||
gpio_peripheral(p->pin, p->function, 0);
|
||||
|
||||
// Enable clock
|
||||
if (!is_enabled_pclock((uint32_t) p->timer)) {
|
||||
enable_pclock((uint32_t) p->timer);
|
||||
}
|
||||
|
||||
if (p->timer->CR1 & TIM_CR1_CEN) {
|
||||
if (p->timer->PSC != (uint16_t) prescaler) {
|
||||
shutdown("PWM already programmed at different speed");
|
||||
}
|
||||
} else {
|
||||
p->timer->PSC = (uint16_t) prescaler;
|
||||
p->timer->ARR = MAX_PWM - 1;
|
||||
p->timer->EGR |= TIM_EGR_UG;
|
||||
}
|
||||
|
||||
struct gpio_pwm channel;
|
||||
switch (p->channel) {
|
||||
case 1: {
|
||||
channel.reg = (void*) &p->timer->CCR1;
|
||||
p->timer->CCER &= ~TIM_CCER_CC1E;
|
||||
p->timer->CCMR1 &= ~(TIM_CCMR1_OC1M | TIM_CCMR1_CC1S);
|
||||
p->timer->CCMR1 |= (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
|
||||
TIM_CCMR1_OC1PE | TIM_CCMR1_OC1FE);
|
||||
gpio_pwm_write(channel, val);
|
||||
p->timer->CCER |= TIM_CCER_CC1E;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
channel.reg = (void*) &p->timer->CCR2;
|
||||
p->timer->CCER &= ~TIM_CCER_CC2E;
|
||||
p->timer->CCMR1 &= ~(TIM_CCMR1_OC2M | TIM_CCMR1_CC2S);
|
||||
p->timer->CCMR1 |= (TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 |
|
||||
TIM_CCMR1_OC2PE | TIM_CCMR1_OC2FE);
|
||||
gpio_pwm_write(channel, val);
|
||||
p->timer->CCER |= TIM_CCER_CC2E;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
channel.reg = (void*) &p->timer->CCR3;
|
||||
p->timer->CCER &= ~TIM_CCER_CC3E;
|
||||
p->timer->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_CC3S);
|
||||
p->timer->CCMR2 |= (TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
|
||||
TIM_CCMR2_OC3PE | TIM_CCMR2_OC3FE);
|
||||
gpio_pwm_write(channel, val);
|
||||
p->timer->CCER |= TIM_CCER_CC3E;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
channel.reg = (void*) &p->timer->CCR4;
|
||||
p->timer->CCER &= ~TIM_CCER_CC4E;
|
||||
p->timer->CCMR2 &= ~(TIM_CCMR2_OC4M | TIM_CCMR2_CC4S);
|
||||
p->timer->CCMR2 |= (TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 |
|
||||
TIM_CCMR2_OC4PE | TIM_CCMR2_OC4FE);
|
||||
gpio_pwm_write(channel, val);
|
||||
p->timer->CCER |= TIM_CCER_CC4E;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
shutdown("Invalid PWM channel");
|
||||
}
|
||||
// Enable PWM output
|
||||
p->timer->CR1 |= TIM_CR1_CEN;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void
|
||||
gpio_pwm_write(struct gpio_pwm g, uint32_t val) {
|
||||
*(volatile uint32_t*) g.reg = val;
|
||||
}
|
|
@ -134,6 +134,35 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
|||
stm32f1_alternative_remap(AFIO_MAPR_I2C1_REMAP_Msk,
|
||||
AFIO_MAPR_I2C1_REMAP);
|
||||
}
|
||||
} else if ((gpio == GPIO('A', 15)
|
||||
|| gpio == GPIO('B', 3)) && (func == 1)) {
|
||||
// TIM2 CH1/2
|
||||
stm32f1_alternative_remap(AFIO_MAPR_TIM2_REMAP_PARTIALREMAP1_Msk,
|
||||
AFIO_MAPR_TIM2_REMAP_PARTIALREMAP1);
|
||||
} else if ((gpio == GPIO('B', 10)
|
||||
|| gpio == GPIO('B', 11)) && (func == 1)) {
|
||||
// TIM2 CH3/4
|
||||
stm32f1_alternative_remap(AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2_Msk,
|
||||
AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2);
|
||||
} else if ((gpio == GPIO('B', 4)
|
||||
|| gpio == GPIO('B', 5)) && (func == 2)) {
|
||||
// TIM3 partial remap
|
||||
stm32f1_alternative_remap(AFIO_MAPR_TIM3_REMAP_PARTIALREMAP_Msk,
|
||||
AFIO_MAPR_TIM3_REMAP_PARTIALREMAP);
|
||||
} else if ((gpio == GPIO('C', 6)
|
||||
|| gpio == GPIO('C', 7)
|
||||
|| gpio == GPIO('C', 8)
|
||||
|| gpio == GPIO('C', 9)) && (func == 2)) {
|
||||
// TIM3 full remap
|
||||
stm32f1_alternative_remap(AFIO_MAPR_TIM3_REMAP_FULLREMAP_Msk,
|
||||
AFIO_MAPR_TIM3_REMAP_FULLREMAP);
|
||||
} else if ((gpio == GPIO('D', 12)
|
||||
|| gpio == GPIO('D', 13)
|
||||
|| gpio == GPIO('D', 14)
|
||||
|| gpio == GPIO('D', 15)) && (func == 2)) {
|
||||
// TIM4
|
||||
stm32f1_alternative_remap(AFIO_MAPR_TIM4_REMAP_Msk,
|
||||
AFIO_MAPR_TIM4_REMAP);
|
||||
}
|
||||
// Add more as needed
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue