From afc4c3e8e427f5912f8b377266e0bda315cd53a7 Mon Sep 17 00:00:00 2001 From: Nico Tonnhofer Date: Thu, 24 Mar 2016 22:24:24 +0100 Subject: [PATCH] STM32F411: Allow non-PWM pins as heater output. --- arduino_stm32f411.h | 146 +++++++++++++++++++++++++++++++++++++++-- heater-stm32.c | 155 +++++++++++++++++++++++++------------------- 2 files changed, 229 insertions(+), 72 deletions(-) diff --git a/arduino_stm32f411.h b/arduino_stm32f411.h index f7c743e..4f660b0 100644 --- a/arduino_stm32f411.h +++ b/arduino_stm32f411.h @@ -34,6 +34,8 @@ Use alphas for PORT and numerics for PIN, close to the design. */ +#define NO_TIMER ((TIM_TypeDef *) 0) + #define PA_0_PIN 0 #define PA_0_PORT GPIOA #define PA_0_ADC 0 @@ -52,6 +54,10 @@ #define PA_2_PIN 2 #define PA_2_PORT GPIOA +#define PA_2_AF 0 +#define PA_2_TIMER NO_TIMER +#define PA_2_CHANNEL 1 +#define PA_2_INVERT 0 // #define PA_2_ADC 2 // #define PA_2_AF 3 // #define PA_2_TIMER TIM9 @@ -60,6 +66,10 @@ #define PA_3_PIN 3 #define PA_3_PORT GPIOA +#define PA_3_AF 0 +#define PA_3_TIMER NO_TIMER +#define PA_3_CHANNEL 1 +#define PA_3_INVERT 0 // #define PA_3_ADC 3 // #define PA_3_AF 3 // #define PA_3_TIMER TIM9 @@ -69,6 +79,10 @@ #define PA_4_PIN 4 #define PA_4_PORT GPIOA #define PA_4_ADC 4 +#define PA_4_AF 0 +#define PA_4_TIMER NO_TIMER +#define PA_4_CHANNEL 1 +#define PA_4_INVERT 0 #define PA_5_PIN 5 #define PA_5_PORT GPIOA @@ -81,6 +95,10 @@ #define PA_6_PIN 6 #define PA_6_PORT GPIOA #define PA_6_ADC 6 +#define PA_6_AF 0 +#define PA_6_TIMER NO_TIMER +#define PA_6_CHANNEL 1 +#define PA_6_INVERT 0 // #define PA_6_AF 2 // #define PA_6_TIMER TIM3 // #define PA_6_CHANNEL 1 @@ -89,6 +107,10 @@ #define PA_7_PIN 7 #define PA_7_PORT GPIOA #define PA_7_ADC 7 +#define PA_7_AF 0 +#define PA_7_TIMER NO_TIMER +#define PA_7_CHANNEL 1 +#define PA_7_INVERT 0 // #define PA_7_AF 1 // #define PA_7_TIMER TIM1 // #define PA_7_CHANNEL 1 @@ -117,22 +139,38 @@ #define PA_11_PIN 11 #define PA_11_PORT GPIOA -#define PA_11_AF 1 -#define PA_11_TIMER TIM1 -#define PA_11_CHANNEL 4 -#define PA_11_INVERT 0 +#define PA_11_AF 1 +#define PA_11_TIMER TIM1 +#define PA_11_CHANNEL 4 +#define PA_11_INVERT 0 #define PA_12_PIN 12 #define PA_12_PORT GPIOA +#define PA_12_AF 0 +#define PA_12_TIMER NO_TIMER +#define PA_12_CHANNEL 0 +#define PA_12_INVERT 0 #define PA_13_PIN 13 #define PA_13_PORT GPIOA +#define PA_13_AF 0 +#define PA_13_TIMER NO_TIMER +#define PA_13_CHANNEL 0 +#define PA_13_INVERT 0 #define PA_14_PIN 14 #define PA_14_PORT GPIOA +#define PA_14_AF 0 +#define PA_14_TIMER NO_TIMER +#define PA_14_CHANNEL 0 +#define PA_14_INVERT 0 #define PA_15_PIN 15 #define PA_15_PORT GPIOA +#define PA_15_AF 0 +#define PA_15_TIMER NO_TIMER +#define PA_15_CHANNEL 0 +#define PA_15_INVERT 0 // #define PA_15_AF 1 // #define PA_15_TIMER TIM2 // #define PA_15_CHANNEL 1 @@ -156,9 +194,17 @@ #define PB_2_PIN 2 #define PB_2_PORT GPIOB +#define PB_2_AF 0 +#define PB_2_TIMER NO_TIMER +#define PB_2_CHANNEL 1 +#define PB_2_INVERT 0 #define PB_3_PIN 3 #define PB_3_PORT GPIOB +#define PB_3_AF 0 +#define PB_3_TIMER NO_TIMER +#define PB_3_CHANNEL 1 +#define PB_3_INVERT 0 // #define PB_3_AF 1 // #define PB_3_TIMER TIM2 // #define PB_3_CHANNEL 2 @@ -215,9 +261,17 @@ #define PB_12_PIN 12 #define PB_12_PORT GPIOB +#define PB_12_AF 0 +#define PB_12_TIMER NO_TIMER +#define PB_12_CHANNEL 0 +#define PB_12_INVERT 0 #define PB_13_PIN 13 #define PB_13_PORT GPIOB +#define PB_13_AF 0 +#define PB_13_TIMER NO_TIMER +#define PB_13_CHANNEL 0 +#define PB_13_INVERT 0 // #define PB_13_AF 1 // #define PB_13_TIMER TIM1 // #define PB_13_CHANNEL 1 @@ -225,6 +279,10 @@ #define PB_14_PIN 14 #define PB_14_PORT GPIOB +#define PB_14_AF 0 +#define PB_14_TIMER NO_TIMER +#define PB_14_CHANNEL 0 +#define PB_14_INVERT 0 // #define PB_14_AF 1 // #define PB_14_TIMER TIM1 // #define PB_14_CHANNEL 2 @@ -232,6 +290,10 @@ #define PB_15_PIN 15 #define PB_15_PORT GPIOB +#define PB_15_AF 0 +#define PB_15_TIMER NO_TIMER +#define PB_15_CHANNEL 0 +#define PB_15_INVERT 0 // #define PB_15_AF 1 // #define PB_15_TIMER TIM1 // #define PB_15_CHANNEL 3 @@ -240,29 +302,57 @@ #define PC_0_PIN 0 #define PC_0_PORT GPIOC #define PC_0_ADC 10 +#define PC_0_AF 0 +#define PC_0_TIMER NO_TIMER +#define PC_0_CHANNEL 1 +#define PC_0_INVERT 0 #define PC_1_PIN 1 #define PC_1_PORT GPIOC #define PC_1_ADC 11 +#define PC_1_AF 0 +#define PC_1_TIMER NO_TIMER +#define PC_1_CHANNEL 1 +#define PC_1_INVERT 0 #define PC_2_PIN 2 #define PC_2_PORT GPIOC #define PC_2_ADC 12 +#define PC_2_AF 0 +#define PC_2_TIMER NO_TIMER +#define PC_2_CHANNEL 1 +#define PC_2_INVERT 0 #define PC_3_PIN 3 #define PC_3_PORT GPIOC #define PC_3_ADC 13 +#define PC_3_AF 0 +#define PC_3_TIMER NO_TIMER +#define PC_3_CHANNEL 1 +#define PC_3_INVERT 0 #define PC_4_PIN 4 #define PC_4_PORT GPIOC #define PC_4_ADC 14 +#define PC_4_AF 0 +#define PC_4_TIMER NO_TIMER +#define PC_4_CHANNEL 1 +#define PC_4_INVERT 0 #define PC_5_PIN 5 #define PC_5_PORT GPIOC #define PC_5_ADC 15 +#define PC_5_AF 0 +#define PC_5_TIMER NO_TIMER +#define PC_5_CHANNEL 1 +#define PC_5_INVERT 0 #define PC_6_PIN 6 #define PC_6_PORT GPIOC +#define PC_6_AF 0 +#define PC_6_TIMER NO_TIMER +#define PC_6_CHANNEL 1 +#define PC_6_INVERT 0 // #define PC_6_AF 2 // #define PC_6_TIMER TIM3 // #define PC_6_CHANNEL 1 @@ -270,6 +360,10 @@ #define PC_7_PIN 7 #define PC_7_PORT GPIOC +#define PC_7_AF 0 +#define PC_7_TIMER NO_TIMER +#define PC_7_CHANNEL 1 +#define PC_7_INVERT 0 // #define PC_7_AF 2 // #define PC_7_TIMER TIM3 // #define PC_7_CHANNEL 2 @@ -277,6 +371,10 @@ #define PC_8_PIN 8 #define PC_8_PORT GPIOC +#define PC_8_AF 0 +#define PC_8_TIMER NO_TIMER +#define PC_8_CHANNEL 1 +#define PC_8_INVERT 0 // #define PC_8_AF 2 // #define PC_8_TIMER TIM3 // #define PC_8_CHANNEL 3 @@ -284,6 +382,10 @@ #define PC_9_PIN 9 #define PC_9_PORT GPIOC +#define PC_9_AF 0 +#define PC_9_TIMER NO_TIMER +#define PC_9_CHANNEL 1 +#define PC_9_INVERT 0 // #define PC_9_AF 2 // #define PC_9_TIMER TIM3 // #define PC_9_CHANNEL 4 @@ -291,27 +393,63 @@ #define PC_10_PIN 10 #define PC_10_PORT GPIOC +#define PC_10_AF 0 +#define PC_10_TIMER NO_TIMER +#define PC_10_CHANNEL 0 +#define PC_10_INVERT 0 #define PC_11_PIN 11 #define PC_11_PORT GPIOC +#define PC_11_AF 0 +#define PC_11_TIMER NO_TIMER +#define PC_11_CHANNEL 0 +#define PC_11_INVERT 0 #define PC_12_PIN 12 #define PC_12_PORT GPIOC +#define PC_12_AF 0 +#define PC_12_TIMER NO_TIMER +#define PC_12_CHANNEL 1 +#define PC_12_INVERT 0 #define PC_13_PIN 13 #define PC_13_PORT GPIOC +#define PC_13_AF 0 +#define PC_13_TIMER NO_TIMER +#define PC_13_CHANNEL 1 +#define PC_13_INVERT 0 #define PC_14_PIN 14 #define PC_14_PORT GPIOC +#define PC_14_AF 0 +#define PC_14_TIMER NO_TIMER +#define PC_14_CHANNEL 1 +#define PC_14_INVERT 0 #define PC_15_PIN 15 #define PC_15_PORT GPIOC +#define PC_15_AF 0 +#define PC_15_TIMER NO_TIMER +#define PC_15_CHANNEL 1 +#define PC_15_INVERT 0 #define PD_2_PIN 2 #define PD_2_PORT GPIOD +#define PD_2_AF 0 +#define PD_2_TIMER NO_TIMER +#define PD_2_CHANNEL 1 +#define PD_2_INVERT 0 #define PH_0_PIN 0 #define PH_0_PORT GPIOH +#define PH_0_AF 0 +#define PH_0_TIMER NO_TIMER +#define PH_0_CHANNEL 1 +#define PH_0_INVERT 0 #define PH_1_PIN 1 #define PH_1_PORT GPIOH +#define PH_1_AF 0 +#define PH_1_TIMER NO_TIMER +#define PH_1_CHANNEL 1 +#define PH_1_INVERT 0 diff --git a/heater-stm32.c b/heater-stm32.c index 1b1aa62..8512837 100644 --- a/heater-stm32.c +++ b/heater-stm32.c @@ -61,13 +61,25 @@ #include "config_wrapper.h" trick. */ typedef struct { - /// Pointer to the capture compare register which changes PWM duty. - __IO uint32_t* ccr; + union { + /// Pointer to the capture compare register which changes PWM duty. + __IO uint32_t* ccr; + /// Pointer to the port for non-PWM pins. + __IO uint32_t* bsrr; + }; + uint16_t masked_pin; + uint8_t uses_pwm; } heater_definition_t; #undef DEFINE_HEATER #define DEFINE_HEATER(name, pin, pwm) \ - { &(pin ## _TIMER-> EXPANDER(CCR, pin ## _CHANNEL,)) }, + { \ + { pwm && pin ## _TIMER ? \ + &(pin ## _TIMER-> EXPANDER(CCR, pin ## _CHANNEL,)) : \ + &(pin ## _PORT->BSRR) }, \ + MASK(pin ## _PIN), \ + pwm && pin ## _TIMER \ + }, static const heater_definition_t heaters[NUM_HEATERS] = { #include "config_wrapper.h" }; @@ -141,70 +153,72 @@ void heater_init() { - PIOC_9 PWM3/4 02 SDA3 */ - if (NUM_HEATERS) { // At least one channel in use. - uint32_t freq; - uint8_t macro_mask; - - // Auto-generate pin setup. - #undef DEFINE_HEATER - #define DEFINE_HEATER(name, pin, pwm) \ - if (pin ## _TIMER == TIM1) { \ - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; } /* turn on TIM1 */ \ - else if (pin ## _TIMER == TIM2) { \ - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } /* turn on TIM2 */ \ - else if (pin ## _TIMER == TIM3) { \ - RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; } /* turn on TIM3 */ \ - else if (pin ## _TIMER == TIM4) { \ - RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; } /* turn on TIM4 */ \ - /* TIM5 is for stepper, TIM9, TIM10 and TIM11 are not used */ \ - pin ## _PORT->MODER &= ~(3 << (pin ## _PIN << 1)); /* reset pin mode */ \ - pin ## _PORT->MODER |= (2 << (pin ## _PIN << 1)); /* pin mode to AF */ \ - pin ## _PORT->OSPEEDR |= (3 << (pin ## _PIN << 1)); /* high speed */ \ - pin ## _PORT->PUPDR &= ~(3 << ((pin ## _PIN) << 1)); /* no pullup-pulldown */ \ - macro_mask = pin ## _PIN < 8 ? (pin ## _PIN) : (pin ## _PIN - 8); \ - /* we have to registers for AFR. ARF[0] for the first 8, ARF[1] for the second 8*/ \ - /* also we use this to keep the compiler happy and don't shift > 32 */ \ - if (pin ## _PIN < 8) { \ - pin ## _PORT->AFR[0] |= pin ## _AF << (macro_mask * 4); \ - } else { \ - pin ## _PORT->AFR[1] |= pin ## _AF << (macro_mask * 4); \ - } \ - /* AFR is a 64bit register, first half are for pin 0-7, second half 8-15 */ \ - pin ## _TIMER->CR1 |= TIM_CR1_ARPE; /* auto-reload preload */ \ - pin ## _TIMER->ARR = PWM_SCALE - 1; /* reset on auto reload at 254 */ \ - /* PWM_SCALE - 1, so CCR = 255 is full off. */ \ - pin ## _TIMER-> EXPANDER(CCR, pin ## _CHANNEL,) = 0; /* start off */ \ - freq = F_CPU / PWM_SCALE / pwm; /* Figure PWM freq. */ \ - if (freq > 65535) \ - freq = 65535; \ - if (freq < 1) \ - freq = 1; \ - pin ## _TIMER->PSC = freq - 1; /* 1kHz */ \ - macro_mask = pin ## _CHANNEL > 2 ? 2 : 1; \ - if (macro_mask == 1) { \ - pin ## _TIMER->CCMR1 |= 0x0D << (3 + (8 * (pin ## _CHANNEL / macro_mask - 1))); \ - /* ch 2 / mm 1 - 1 = 1, ch 1 / mm 1 - 1 = 0*/ \ - } else { \ - pin ## _TIMER->CCMR2 |= 0x0D << (3 + (8 * (pin ## _CHANNEL / macro_mask - 1))); \ - /* ch 4 / mm 2 - 1 = 1, ch 3 / mm 2 - 1 = 0*/ \ - } \ - pin ## _TIMER->CCER |= EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, E); \ - /* output enable */ \ - if (pin ## _INVERT) { \ - pin ## _TIMER->CCER |= EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, P); \ - } else { \ - pin ## _TIMER->CCER &= ~(EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, P)); \ - } \ - /* invert the signal for negated timers*/ \ - /* TODO: use this also with a XOR for inverted heaters later */ \ - pin ## _TIMER->EGR |= TIM_EGR_UG; /* update generation */ \ - pin ## _TIMER->CR1 |= TIM_CR1_CEN; /* enable counters */ + // Auto-generate pin setup. + #undef DEFINE_HEATER + #define DEFINE_HEATER(name, pin, pwm) \ + if (pwm && pin ## _TIMER) { \ + uint32_t freq; \ + uint8_t macro_mask; \ + if (pin ## _TIMER == TIM1) { \ + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; } /* turn on TIM1 */ \ + else if (pin ## _TIMER == TIM2) { \ + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } /* turn on TIM2 */ \ + else if (pin ## _TIMER == TIM3) { \ + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; } /* turn on TIM3 */ \ + else if (pin ## _TIMER == TIM4) { \ + RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; } /* turn on TIM4 */ \ + /* TIM5 is for stepper, TIM9, TIM10 and TIM11 are not used */ \ + pin ## _PORT->MODER &= ~(3 << (pin ## _PIN << 1)); /* reset pin mode */ \ + pin ## _PORT->MODER |= (2 << (pin ## _PIN << 1)); /* pin mode to AF */ \ + pin ## _PORT->OSPEEDR |= (3 << (pin ## _PIN << 1)); /* high speed */ \ + pin ## _PORT->PUPDR &= ~(3 << ((pin ## _PIN) << 1)); /* no pullup-pulldown */ \ + macro_mask = pin ## _PIN < 8 ? (pin ## _PIN) : (pin ## _PIN - 8); \ + /* we have to registers for AFR. ARF[0] for the first 8, ARF[1] for the second 8*/ \ + /* also we use this to keep the compiler happy and don't shift > 32 */ \ + if (pin ## _PIN < 8) { \ + pin ## _PORT->AFR[0] |= pin ## _AF << (macro_mask * 4); \ + } else { \ + pin ## _PORT->AFR[1] |= pin ## _AF << (macro_mask * 4); \ + } \ + /* AFR is a 64bit register, first half are for pin 0-7, second half 8-15 */ \ + pin ## _TIMER->CR1 |= TIM_CR1_ARPE; /* auto-reload preload */ \ + pin ## _TIMER->ARR = PWM_SCALE - 1; /* reset on auto reload at 254 */ \ + /* PWM_SCALE - 1, so CCR = 255 is full off. */ \ + pin ## _TIMER-> EXPANDER(CCR, pin ## _CHANNEL,) = 0; /* start off */ \ + freq = F_CPU / PWM_SCALE / (pwm ? pwm : 1); /* Figure PWM freq. */ \ + if (freq > 65535) \ + freq = 65535; \ + if (freq < 1) \ + freq = 1; \ + pin ## _TIMER->PSC = freq - 1; /* 1kHz */ \ + macro_mask = pin ## _CHANNEL > 2 ? 2 : 1; \ + if (macro_mask == 1) { \ + pin ## _TIMER->CCMR1 |= 0x0D << (3 + (8 * (pin ## _CHANNEL / macro_mask - 1))); \ + /* ch 2 / mm 1 - 1 = 1, ch 1 / mm 1 - 1 = 0*/ \ + } else { \ + pin ## _TIMER->CCMR2 |= 0x0D << (3 + (8 * (pin ## _CHANNEL / macro_mask - 1))); \ + /* ch 4 / mm 2 - 1 = 1, ch 3 / mm 2 - 1 = 0*/ \ + } \ + pin ## _TIMER->CCER |= EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, E); \ + /* output enable */ \ + if (pin ## _INVERT) { \ + pin ## _TIMER->CCER |= EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, P); \ + } else { \ + pin ## _TIMER->CCER &= ~(EXPANDER(TIM_CCER_CC, pin ## _CHANNEL, P)); \ + } \ + /* invert the signal for negated timers*/ \ + /* TODO: use this also with a XOR for inverted heaters later */ \ + pin ## _TIMER->EGR |= TIM_EGR_UG; /* update generation */ \ + pin ## _TIMER->CR1 |= TIM_CR1_CEN; /* enable counters */ \ + } \ + else { \ + SET_OUTPUT(pin); \ + WRITE(pin, 0); \ + } #include "config_wrapper.h" #undef DEFINE_HEATER - } /* NUM_HEATERS */ - #if 0 pid_init(i); #endif /* 0 */ @@ -226,12 +240,17 @@ void heater_set(heater_t index, uint8_t value) { heaters_runtime[index].heater_output = value; - // Remember, we scale, and duty cycle is inverted. - *heaters[index].ccr = (uint32_t)value * (PWM_SCALE / 255); - - if (DEBUG_PID && (debug_flags & DEBUG_PID)) - sersendf_P(PSTR("PWM %su = %lu\n"), index, *heaters[index].ccr); + if (heaters[index].uses_pwm) { + // Remember, we scale, and duty cycle is inverted. + *heaters[index].ccr = (uint32_t)value * (PWM_SCALE / 255); + if (DEBUG_PID && (debug_flags & DEBUG_PID)) + sersendf_P(PSTR("PWM %su = %lu\n"), index, *heaters[index].ccr); + } + else { + *(heaters[index].bsrr) = + heaters[index].masked_pin << ((value >= HEATER_THRESHOLD) ? 0 : 16); + } if (value) power_on(); }