diff --git a/arduino_stm32f411.h b/arduino_stm32f411.h index 95940fc..f7c743e 100644 --- a/arduino_stm32f411.h +++ b/arduino_stm32f411.h @@ -37,18 +37,34 @@ #define PA_0_PIN 0 #define PA_0_PORT GPIOA #define PA_0_ADC 0 +#define PA_0_AF 1 +#define PA_0_TIMER TIM2 +#define PA_0_CHANNEL 1 +#define PA_0_INVERT 0 #define PA_1_PIN 1 #define PA_1_PORT GPIOA #define PA_1_ADC 1 +#define PA_1_AF 1 +#define PA_1_TIMER TIM2 +#define PA_1_CHANNEL 2 +#define PA_1_INVERT 0 #define PA_2_PIN 2 #define PA_2_PORT GPIOA -#define PA_2_ADC 2 +// #define PA_2_ADC 2 +// #define PA_2_AF 3 +// #define PA_2_TIMER TIM9 +// #define PA_2_CHANNEL 1 +// #define PA_2_INVERT 0 #define PA_3_PIN 3 #define PA_3_PORT GPIOA -#define PA_3_ADC 3 +// #define PA_3_ADC 3 +// #define PA_3_AF 3 +// #define PA_3_TIMER TIM9 +// #define PA_3_CHANNEL 2 +// #define PA_3_INVERT 0 #define PA_4_PIN 4 #define PA_4_PORT GPIOA @@ -57,26 +73,54 @@ #define PA_5_PIN 5 #define PA_5_PORT GPIOA #define PA_5_ADC 5 +#define PA_5_AF 1 +#define PA_5_TIMER TIM2 +#define PA_5_CHANNEL 1 +#define PA_5_INVERT 0 #define PA_6_PIN 6 #define PA_6_PORT GPIOA #define PA_6_ADC 6 +// #define PA_6_AF 2 +// #define PA_6_TIMER TIM3 +// #define PA_6_CHANNEL 1 +// #define PA_6_INVERT 0 #define PA_7_PIN 7 #define PA_7_PORT GPIOA #define PA_7_ADC 7 +// #define PA_7_AF 1 +// #define PA_7_TIMER TIM1 +// #define PA_7_CHANNEL 1 +// #define PA_7_INVERT 1 #define PA_8_PIN 8 #define PA_8_PORT GPIOA +#define PA_8_AF 1 +#define PA_8_TIMER TIM1 +#define PA_8_CHANNEL 1 +#define PA_8_INVERT 0 #define PA_9_PIN 9 #define PA_9_PORT GPIOA +#define PA_9_AF 1 +#define PA_9_TIMER TIM1 +#define PA_9_CHANNEL 2 +#define PA_9_INVERT 0 #define PA_10_PIN 10 #define PA_10_PORT GPIOA +#define PA_10_AF 1 +#define PA_10_TIMER TIM1 +#define PA_10_CHANNEL 3 +#define PA_10_INVERT 0 #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_12_PIN 12 #define PA_12_PORT GPIOA @@ -89,53 +133,109 @@ #define PA_15_PIN 15 #define PA_15_PORT GPIOA +// #define PA_15_AF 1 +// #define PA_15_TIMER TIM2 +// #define PA_15_CHANNEL 1 +// #define PA_15_INVERT 0 #define PB_0_PIN 0 #define PB_0_PORT GPIOB #define PB_0_ADC 8 +#define PB_0_AF 2 +#define PB_0_TIMER TIM3 +#define PB_0_CHANNEL 3 +#define PB_0_INVERT 0 #define PB_1_PIN 1 #define PB_1_PORT GPIOB #define PB_1_ADC 9 +#define PB_1_AF 2 +#define PB_1_TIMER TIM3 +#define PB_1_CHANNEL 4 +#define PB_1_INVERT 0 #define PB_2_PIN 2 #define PB_2_PORT GPIOB #define PB_3_PIN 3 #define PB_3_PORT GPIOB +// #define PB_3_AF 1 +// #define PB_3_TIMER TIM2 +// #define PB_3_CHANNEL 2 +// #define PB_3_INVERT 0 #define PB_4_PIN 4 #define PB_4_PORT GPIOB +#define PB_4_AF 2 +#define PB_4_TIMER TIM3 +#define PB_4_CHANNEL 1 +#define PB_4_INVERT 0 #define PB_5_PIN 5 #define PB_5_PORT GPIOB +#define PB_5_AF 2 +#define PB_5_TIMER TIM3 +#define PB_5_CHANNEL 1 +#define PB_5_INVERT 0 #define PB_6_PIN 6 #define PB_6_PORT GPIOB +#define PB_6_AF 2 +#define PB_6_TIMER TIM4 +#define PB_6_CHANNEL 1 +#define PB_6_INVERT 0 #define PB_7_PIN 7 #define PB_7_PORT GPIOB +#define PB_7_AF 2 +#define PB_7_TIMER TIM4 +#define PB_7_CHANNEL 2 +#define PB_7_INVERT 0 #define PB_8_PIN 8 #define PB_8_PORT GPIOB +#define PB_8_AF 2 +#define PB_8_TIMER TIM4 +#define PB_8_CHANNEL 3 +#define PB_8_INVERT 0 #define PB_9_PIN 9 #define PB_9_PORT GPIOB +#define PB_9_AF 2 +#define PB_9_TIMER TIM4 +#define PB_9_CHANNEL 4 +#define PB_9_INVERT 0 #define PB_10_PIN 10 #define PB_10_PORT GPIOB +#define PB_10_AF 1 +#define PB_10_TIMER TIM2 +#define PB_10_CHANNEL 3 +#define PB_10_INVERT 0 #define PB_12_PIN 12 #define PB_12_PORT GPIOB #define PB_13_PIN 13 #define PB_13_PORT GPIOB +// #define PB_13_AF 1 +// #define PB_13_TIMER TIM1 +// #define PB_13_CHANNEL 1 +// #define PB_13_INVERT 1 #define PB_14_PIN 14 #define PB_14_PORT GPIOB +// #define PB_14_AF 1 +// #define PB_14_TIMER TIM1 +// #define PB_14_CHANNEL 2 +// #define PB_14_INVERT 1 #define PB_15_PIN 15 #define PB_15_PORT GPIOB +// #define PB_15_AF 1 +// #define PB_15_TIMER TIM1 +// #define PB_15_CHANNEL 3 +// #define PB_15_INVERT 1 #define PC_0_PIN 0 #define PC_0_PORT GPIOC @@ -163,15 +263,31 @@ #define PC_6_PIN 6 #define PC_6_PORT GPIOC +// #define PC_6_AF 2 +// #define PC_6_TIMER TIM3 +// #define PC_6_CHANNEL 1 +// #define PC_6_INVERT 0 #define PC_7_PIN 7 #define PC_7_PORT GPIOC +// #define PC_7_AF 2 +// #define PC_7_TIMER TIM3 +// #define PC_7_CHANNEL 2 +// #define PC_7_INVERT 0 #define PC_8_PIN 8 #define PC_8_PORT GPIOC +// #define PC_8_AF 2 +// #define PC_8_TIMER TIM3 +// #define PC_8_CHANNEL 3 +// #define PC_8_INVERT 0 #define PC_9_PIN 9 #define PC_9_PORT GPIOC +// #define PC_9_AF 2 +// #define PC_9_TIMER TIM3 +// #define PC_9_CHANNEL 4 +// #define PC_9_INVERT 0 #define PC_10_PIN 10 #define PC_10_PORT GPIOC diff --git a/heater-lpc.c b/heater-lpc.c index 647b55f..5c96d49 100644 --- a/heater-lpc.c +++ b/heater-lpc.c @@ -5,7 +5,7 @@ For test cases see the intro comment in heater.c. */ -#if defined TEACUP_C_INCLUDE && defined __ARMEL__ +#if defined TEACUP_C_INCLUDE && defined __ARM_LPC1114__ #include "cmsis-lpc11xx.h" #include @@ -216,4 +216,4 @@ void heater_set(heater_t index, uint8_t value) { } } -#endif /* defined TEACUP_C_INCLUDE && defined __ARMEL__ */ +#endif /* defined TEACUP_C_INCLUDE && defined __ARM_LPC1114__ */ diff --git a/heater-stm32.c b/heater-stm32.c new file mode 100644 index 0000000..eca0c5c --- /dev/null +++ b/heater-stm32.c @@ -0,0 +1,187 @@ + +/** \file + \brief Manage heaters, including PID and PWM, ARM specific part. +*/ + +#if defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ + +/** + Test configuration. +*/ +#ifdef EECONFIG + #error EEPROM handling (EECONFIG) not yet supported on ARM. +#endif + +#ifdef BANG_BANG + #error BANG_BANG not supported on ARM. You may set PWM frequency of one \ + or all heater(s) to zero, which gives similar, better behaviour. +#endif + +/** \def PWM_SCALE + + G-code standard (if such a thing exists at all) gives a heater setting + range between 0 (off) and 255 (full on), so we let the PWM timers count up + to 255. Doing so allows to set the prescaler for these frequencies (all on + a 96 MHz CPU clock): + + prescaler frequency prescaler frequency + + 0 367.5 kHz 4 75.3 kHz + 1 188.2 kHz 5 62.7 kHz + 2 125.5 kHz ... ... + 3 94.1 kHz 65535 5.74 Hz + + As one can see, frequency steps are rather coarse on the high end and + become finer grained the lower it gets. + + If this range is generally too high for your purposes, you can set PWM_SCALE + to multiples of 255 to lower the range. Doubling it to 510 moves the + frequency range to 2.9 Hz...183.8 kHz, quadrupling it to 1020 moves the range + to 1.5 Hz...91.9 kHz and so on. The highest allowed number is 65535. + + That said, code below calculates the best prescaler value for a configured + frequency, so you should bother about PWM_SCALE only of you need frequencies + below 6 Hz. +*/ +#define PWM_SCALE 255 + + +/** Initialise heater subsystem. + + Initialise PWM timers, etc. Inspired by heater-arm_lpc11xx.c (pwm.c in LPC1343CodeBase): + + https://github.com/microbuilder/LPC1343CodeBase + + Note that PWM is inversed, pins start at Low by chip design. When the pin's + counter is reached, they're set to High. Reaching the timer reset counter is + programmed to reset everything and start over. Thus, having both counter + matches similar gives a low duty, having the pin counter match zero gives + full on. + + For simplicity we reset all timer counters always on Match 3 and always + at PWM_SCALE (255 per default), so setting a pin match to PWM_SCALE / 2 + gives 50% duty, setting it to PWM_SCALE gives full off. This choice disallows + using a pin connected to a Match 3, working around this would make code much + more complicated (and still not allow to use more than 3 pins per timer). + + On ARM we can define a PWM frequency pretty fine grained, so we take the + 'pwm' value of DEFINE_HEATER() not only wether to use PWM at all, but also + to define the PWM frequency. Float values are allowed. + + If there's more than one pin on a timer, they share the same PWM frequency; + the frequency choosen is the one of the pin defined last. +*/ +void heater_init() { + /** + Pins on the STM32F411RE are usable as following, N are negated pin (active low) + some pins are commented out (-) because they are shared. You can change this + in arduino_stm32f4xx. But take care! You could pwm two pins simultanious or disable + other important functions (serial connection). + PWM5 = TIM5 = Stepper timer. + + pin timer/channel alt func for PWM other uses + + PIOA_0 PWM2/1, 5/1 01, 02 AD0 + PIOA_1 PWM2/2, 5/2 01, 02 MOSI4, AD1 + - PIOA_2 PWM2/3, 5/3, 9/1 01, 02, 03 TX2, AD2, UART! + - PIOA_3 PWM2/4, 5/4, 9/2 01, 02, 03 RX2, AD3, UART! + - PIOA_5 PWM2/1 01 LED1, SCK1, AD5 + - PIOA_6 PWM3/1 02 MISO1, AD6 + - PIOA_7 PWM1/1N, 3/2 01, 02 MOSI1, AD7 + PIOA_8 PWM1/1 01 SCL1 + PIOA_9 PWM1/2 01 TX1 + PIOA_10 PWM1/3 01 MOSI5, RX1 + PIOA_11 PWM1/4 01 TX6, MISO4 + - PIOA_15 PWM2/1 01 NSS1, TX1 + + PIOB_0 PWM1/2N, 3/3 01, 02 SCK5, CK5, AD8 + PIOB_1 PWM1/3N, 3/4 01, 02 NSS4, WS5, AD9 + - PIOB_3 PWM2/2 01 SDA2, SCK3 + PIOB_4 PWM3/1 02 SDA3, MISO3 + PIOB_5 PWM3/2 02 MOSI3 + PIOB_6 PWM4/1 02 SCL1, TX1 + PIOB_7 PWM4/2 02 SDA1, RX1 + PIOB_8 PWM4/3, 10/1 02, 03 SCL1, MOSI5 + PIOB_9 PWM4/4, 11/1 02, 03 SDA1, NSS2 + PIOB_10 PWM2/3 01 SCL3 + - PIOB_13 PWM1/1N 01 SCK2 + - PIOB_14 PWM1/2N 01 MISO2 + - PIOB_15 PWM1/3N 01 MOSI2 + + - PIOC_6 PWM3/1 02 MCK2, TX6 + - PIOC_7 PWM3/2 02 SCK2, RX6 + - PIOC_8 PWM3/3 02 SDA3 + - PIOC_9 PWM3/4 02 SDA3 + + */ + if (NUM_HEATERS) { // At least one channel in use. + + // For simplicity, turn on all timers, not only the used ones. + // TODO: turn on only the used ones. we know pin ## TIMER. + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM9EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM10EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM11EN; + + // Auto-generate pin setup. + + // some helper macros + #define _EXPANDER(pre, val, post) pre ## val ## post + #define EXPANDER(pre, val, post) _EXPANDER(pre, val, post) + + #undef DEFINE_HEATER + uint8_t macro_mask; + #define DEFINE_HEATER(name, pin, pwm) \ + 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,) = PWM_SCALE * 0.1; /* testing*/ \ + pin ## _TIMER->PSC = F_CPU / PWM_SCALE / 1000 - 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 */ + + #include "config_wrapper.h" + #undef DEFINE_HEATER + + } /* NUM_HEATERS */ + +#if 0 + pid_init(i); +#endif /* 0 */ +} + +#endif /* defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ */ diff --git a/heater.c b/heater.c index 56ff2b0..35af62d 100644 --- a/heater.c +++ b/heater.c @@ -26,6 +26,7 @@ #define TEACUP_C_INCLUDE #include "heater-avr.c" #include "heater-lpc.c" +#include "heater-stm32.c" #undef TEACUP_C_INCLUDE #include