aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorD4SK <konstantin.vogel@gmx.net>2021-05-30 01:19:39 +0100
committerKevin O'Connor <kevin@koconnor.net>2021-10-06 18:20:29 -0400
commit0a55489e2c8899b2a5cff75dbbd40d7ed5791e22 (patch)
tree2855536ca3554470cef04f11955ef6ebf59c68a1
parent28b3c9e88c2103e67e0aaeeecd79eb4b827fecb0 (diff)
downloadkutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.tar.gz
kutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.tar.xz
kutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.zip
stm32: Add initial support for stm32h7
Signed-off-by: Konstantin Vogel <konstantin.vogel@gmx.net> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--src/stm32/Kconfig13
-rw-r--r--src/stm32/Makefile6
-rw-r--r--src/stm32/internal.h2
-rw-r--r--src/stm32/stm32h7.c231
-rw-r--r--src/stm32/stm32h7_adc.c234
-rw-r--r--src/stm32/stm32h7_serial.c94
-rw-r--r--src/stm32/watchdog.c5
7 files changed, 583 insertions, 2 deletions
diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig
index 82e76a92..b500f06c 100644
--- a/src/stm32/Kconfig
+++ b/src/stm32/Kconfig
@@ -7,8 +7,8 @@ config STM32_SELECT
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
- select HAVE_GPIO_I2C if !MACH_STM32F031
- select HAVE_GPIO_SPI if !MACH_STM32F031
+ select HAVE_GPIO_I2C if !(MACH_STM32F031 || MACH_STM32H7)
+ select HAVE_GPIO_SPI if !(MACH_STM32F031 || MACH_STM32H7)
select HAVE_GPIO_BITBANGING if !MACH_STM32F031
select HAVE_STRICT_TIMING
select HAVE_CHIPID
@@ -58,6 +58,9 @@ choice
config MACH_STM32F072
bool "STM32F072"
select MACH_STM32F0
+ config MACH_STM32H750
+ bool "STM32H750"
+ select MACH_STM32H7
endchoice
config MACH_STM32F0
@@ -68,6 +71,8 @@ config MACH_STM32F2
bool
config MACH_STM32F4
bool
+config MACH_STM32H7
+ bool
config HAVE_STM32_USBFS
bool
default y if MACH_STM32F103 || MACH_STM32F042 || MACH_STM32F070
@@ -91,6 +96,7 @@ config MCU
default "stm32f407xx" if MACH_STM32F407
default "stm32f429xx" if MACH_STM32F429
default "stm32f446xx" if MACH_STM32F446
+ default "stm32h750xx" if MACH_STM32H750
config CLOCK_FREQ
int
@@ -101,6 +107,7 @@ config CLOCK_FREQ
default 84000000 if MACH_STM32F401
default 168000000 if MACH_STM32F405 || MACH_STM32F407 || MACH_STM32F429
default 180000000 if MACH_STM32F446
+ default 480000000 if MACH_STM32H750
config FLASH_SIZE
hex
@@ -110,6 +117,7 @@ config FLASH_SIZE
default 0x10000 if MACH_STM32F103 # Flash size of stm32f103x8 (64KiB)
default 0x40000 if MACH_STM32F2 || MACH_STM32F401
default 0x80000 if MACH_STM32F405 || MACH_STM32F407 || MACH_STM32F429 || MACH_STM32F446
+ default 0x20000 if MACH_STM32H7 # Flash size of stm32h750 (128KiB)
config RAM_START
hex
@@ -124,6 +132,7 @@ config RAM_SIZE
default 0x20000 if MACH_STM32F207
default 0x10000 if MACH_STM32F401
default 0x20000 if MACH_STM32F405 || MACH_STM32F407 || MACH_STM32F429 || MACH_STM32F446
+ default 0x20000 if MACH_STM32H7
config STACK_SIZE
int
diff --git a/src/stm32/Makefile b/src/stm32/Makefile
index 31fefb6c..afc3809f 100644
--- a/src/stm32/Makefile
+++ b/src/stm32/Makefile
@@ -8,6 +8,7 @@ dirs-$(CONFIG_MACH_STM32F0) += lib/stm32f0
dirs-$(CONFIG_MACH_STM32F1) += lib/stm32f1
dirs-$(CONFIG_MACH_STM32F2) += lib/stm32f2
dirs-$(CONFIG_MACH_STM32F4) += lib/stm32f4
+dirs-$(CONFIG_MACH_STM32H7) += lib/stm32h7
MCU := $(shell echo $(CONFIG_MCU))
MCU_UPPER := $(shell echo $(CONFIG_MCU) | tr a-z A-Z | tr X x)
@@ -17,6 +18,7 @@ CFLAGS-$(CONFIG_MACH_STM32F1) += -mcpu=cortex-m3 -Ilib/stm32f1/include
CFLAGS-$(CONFIG_MACH_STM32F2) += -mcpu=cortex-m3 -Ilib/stm32f2/include
CFLAGS-$(CONFIG_MACH_STM32F4) += -mcpu=cortex-m4 -Ilib/stm32f4/include
CFLAGS-$(CONFIG_MACH_STM32F4) += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
+CFLAGS-$(CONFIG_MACH_STM32H7) += -mcpu=cortex-m7 -Ilib/stm32h7/include
CFLAGS += $(CFLAGS-y) -D$(MCU_UPPER) -mthumb -Ilib/cmsis-core -Ilib/fast-hash
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
@@ -39,12 +41,16 @@ src-$(CONFIG_MACH_STM32F2) += stm32/adc.c stm32/i2c.c
src-$(CONFIG_MACH_STM32F4) += ../lib/stm32f4/system_stm32f4xx.c
src-$(CONFIG_MACH_STM32F4) += stm32/stm32f4.c generic/armcm_timer.c
src-$(CONFIG_MACH_STM32F4) += stm32/adc.c stm32/i2c.c
+src-$(CONFIG_MACH_STM32H7) += ../lib/stm32h7/system_stm32h7xx.c
+src-$(CONFIG_MACH_STM32H7) += stm32/stm32h7.c generic/armcm_timer.c
+src-$(CONFIG_MACH_STM32H7) += stm32/stm32h7_adc.c
src-$(CONFIG_HAVE_GPIO_SPI) += stm32/spi.c
usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c
usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c
src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c
serial-src-y := stm32/serial.c
serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c
+serial-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_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
diff --git a/src/stm32/internal.h b/src/stm32/internal.h
index 2538bbaa..9c0da6c6 100644
--- a/src/stm32/internal.h
+++ b/src/stm32/internal.h
@@ -12,6 +12,8 @@
#include "stm32f2xx.h"
#elif CONFIG_MACH_STM32F4
#include "stm32f4xx.h"
+#elif CONFIG_MACH_STM32H7
+#include "stm32h7xx.h"
#endif
extern GPIO_TypeDef * const digital_regs[];
diff --git a/src/stm32/stm32h7.c b/src/stm32/stm32h7.c
new file mode 100644
index 00000000..c89dd9c5
--- /dev/null
+++ b/src/stm32/stm32h7.c
@@ -0,0 +1,231 @@
+// Code to setup clocks and gpio on stm32h7
+//
+// Copyright (C) 2020 Konstantin Vogel <konstantin.vogel@gmx.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+
+// USB and I2C is not supported, SPI is untested!
+
+#include "autoconf.h" // CONFIG_CLOCK_REF_FREQ
+#include "board/armcm_boot.h" // VectorTable
+#include "board/irq.h" // irq_disable
+#include "command.h" // DECL_CONSTANT_STR
+#include "internal.h" // enable_pclock
+#include "sched.h" // sched_main
+
+#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 4)
+
+// Enable a peripheral clock
+void
+enable_pclock(uint32_t periph_base)
+{
+ // periph_base determines in which bitfield at wich position to set a bit
+ // E.g. D2_AHB1PERIPH_BASE is the adress offset of the given bitfield
+ if (periph_base < D2_APB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_APB1PERIPH_BASE) / 0x400;
+ RCC->APB1LENR |= (1<<pos); // we assume it is not in APB1HENR
+ RCC->APB1LENR;
+ } else if (periph_base < D2_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_APB2PERIPH_BASE) / 0x400;
+ RCC->APB2ENR |= (1<<pos);
+ RCC->APB2ENR;
+ } else if (periph_base < D2_AHB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_AHB1PERIPH_BASE) / 0x400;
+ RCC->AHB1ENR |= (1<<pos);
+ RCC->AHB1ENR;
+ } else if (periph_base < D1_APB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_AHB2PERIPH_BASE) / 0x400;
+ RCC->AHB2ENR |= (1<<pos);
+ RCC->AHB2ENR;
+ } else if (periph_base < D1_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D1_APB1PERIPH_BASE) / 0x400;
+ RCC->APB3ENR |= (1<<pos);
+ RCC->APB3ENR;
+ } else if (periph_base < D3_APB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D1_AHB1PERIPH_BASE) / 0x400;
+ RCC->AHB3ENR |= (1<<pos);
+ RCC->AHB3ENR;
+ } else if (periph_base < D3_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D3_APB1PERIPH_BASE) / 0x400;
+ RCC->APB4ENR |= (1<<pos);
+ RCC->APB4ENR;
+ } else {
+ uint32_t pos = (periph_base - D3_AHB1PERIPH_BASE) / 0x400;
+ RCC->AHB4ENR |= (1<<pos);
+ RCC->AHB4ENR;
+ }
+}
+
+// Check if a peripheral clock has been enabled
+int
+is_enabled_pclock(uint32_t periph_base)
+{
+ if (periph_base < D2_APB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_APB1PERIPH_BASE) / 0x400;
+ return RCC->APB1LENR & (1<<pos); // we assume it is not in APB1HENR
+ } else if (periph_base < D2_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_APB2PERIPH_BASE) / 0x400;
+ return RCC->APB2ENR & (1<<pos);
+ } else if (periph_base < D2_AHB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_AHB1PERIPH_BASE) / 0x400;
+ return RCC->AHB1ENR & (1<<pos);
+ } else if (periph_base < D1_APB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D2_AHB2PERIPH_BASE) / 0x400;
+ return RCC->AHB2ENR & (1<<pos);
+ } else if (periph_base < D1_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D1_APB1PERIPH_BASE) / 0x400;
+ return RCC->APB3ENR & (1<<pos);
+ } else if (periph_base < D3_APB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D1_AHB1PERIPH_BASE) / 0x400;
+ return RCC->AHB3ENR & (1<<pos);
+ } else if (periph_base < D3_AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - D3_APB1PERIPH_BASE) / 0x400;
+ return RCC->APB4ENR & (1<<pos);
+ } else {
+ uint32_t pos = (periph_base - D3_AHB1PERIPH_BASE) / 0x400;
+ return RCC->AHB4ENR & (1<<pos);
+ }
+}
+
+// Return the frequency of the given peripheral clock
+uint32_t
+get_pclock_frequency(uint32_t periph_base)
+{
+ return FREQ_PERIPH;
+}
+
+// Enable a GPIO peripheral clock
+void
+gpio_clock_enable(GPIO_TypeDef *regs)
+{
+ enable_pclock((uint32_t)regs);
+}
+
+// Set the mode and extended function of a pin
+void
+gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
+{
+ GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
+
+ // Enable GPIO clock
+ gpio_clock_enable(regs);
+
+ // Configure GPIO
+ uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
+ uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
+ uint32_t pos = gpio % 16, af_reg = pos / 8;
+ uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
+ uint32_t m_shift = pos * 2, m_msk = 0x03 << m_shift;
+
+ regs->AFR[af_reg] = (regs->AFR[af_reg] & ~af_msk) | (func << af_shift);
+ regs->MODER = (regs->MODER & ~m_msk) | (mode_bits << m_shift);
+ regs->PUPDR = (regs->PUPDR & ~m_msk) | (pup << m_shift);
+ regs->OTYPER = (regs->OTYPER & ~(1 << pos)) | (od << pos);
+ regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (0x02 << m_shift);
+}
+
+#if !CONFIG_STM32_CLOCK_REF_INTERNAL
+DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PH0,PH1");
+#endif
+
+// Main clock and power setup called at chip startup
+static void
+clock_setup(void)
+{
+ // Set this despite correct defaults.
+ // "The software has to program the supply configuration in PWR control
+ // register 3" (pg. 259)
+ // Only a single write is allowed (pg. 304)
+ PWR->CR3 = (PWR->CR3 | PWR_CR3_LDOEN) & ~(PWR_CR3_BYPASS | PWR_CR3_SCUEN);
+ while (!(PWR->CSR1 & PWR_CSR1_ACTVOSRDY))
+ ;
+ // (HSE 25mhz) /DIVM1(5) (pll_base 5Mhz) *DIVN1(192) (pll_freq 960Mhz)
+ // /DIVP1(2) (SYSCLK 480Mhz)
+ uint32_t pll_base = 5000000;
+ // Only even dividers (DIVP1) are allowed
+ uint32_t pll_freq = CONFIG_CLOCK_FREQ * 2;
+ if (!CONFIG_STM32_CLOCK_REF_INTERNAL) {
+ // Configure PLL from external crystal (HSE)
+ RCC->CR |= RCC_CR_HSEON;
+ while(!(RCC->CR & RCC_CR_HSERDY))
+ ;
+ MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_PLLSRC_Msk,
+ RCC_PLLCKSELR_PLLSRC_HSE);
+ MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_DIVM1_Msk,
+ (CONFIG_CLOCK_REF_FREQ/pll_base) << RCC_PLLCKSELR_DIVM1_Pos);
+ } else {
+ // Configure PLL from internal 64Mhz oscillator (HSI)
+ // HSI frequency of 64Mhz is integer divisible with 4Mhz
+ pll_base = 4000000;
+ MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_PLLSRC_Msk,
+ RCC_PLLCKSELR_PLLSRC_HSI);
+ MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_DIVM1_Msk,
+ (64000000/pll_base) << RCC_PLLCKSELR_DIVM1_Pos);
+ }
+ // Set input frequency range of PLL1 according to pll_base
+ // 3 = 8-16Mhz, 2 = 4-8Mhz
+ MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLL1RGE_Msk, RCC_PLLCFGR_PLL1RGE_2);
+ // Disable unused PLL1 outputs
+ MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVR1EN_Msk, 0);
+ MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVQ1EN_Msk, 0);
+ // This is necessary, default is not 1!
+ MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVP1EN_Msk, RCC_PLLCFGR_DIVP1EN);
+ // Set multiplier DIVN1 and post divider DIVP1
+ // 001 = /2, 010 = not allowed, 0011 = /4 ...
+ MODIFY_REG(RCC->PLL1DIVR, RCC_PLL1DIVR_N1_Msk,
+ (pll_freq/pll_base - 1) << RCC_PLL1DIVR_N1_Pos);
+ MODIFY_REG(RCC->PLL1DIVR, RCC_PLL1DIVR_P1_Msk,
+ (pll_freq/CONFIG_CLOCK_FREQ - 1) << RCC_PLL1DIVR_P1_Pos);
+
+ // Pwr
+ MODIFY_REG(PWR->D3CR, PWR_D3CR_VOS_Msk, PWR_D3CR_VOS);
+ while (!(PWR->D3CR & PWR_D3CR_VOSRDY))
+ ;
+ // Enable VOS0 (overdrive)
+ if (CONFIG_CLOCK_FREQ > 400000000) {
+ RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN;
+ SYSCFG->PWRCR |= SYSCFG_PWRCR_ODEN;
+ while (!(PWR->D3CR & PWR_D3CR_VOSRDY))
+ ;
+ }
+
+ // Set flash latency according to clock frequency (pg.159)
+ uint32_t flash_acr_latency = (CONFIG_CLOCK_FREQ > 450000000) ?
+ FLASH_ACR_LATENCY_4WS : FLASH_ACR_LATENCY_2WS;
+ MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY_Msk, flash_acr_latency);
+ MODIFY_REG(FLASH->ACR, FLASH_ACR_WRHIGHFREQ_Msk, FLASH_ACR_WRHIGHFREQ_1);
+ while (!(FLASH->ACR & flash_acr_latency))
+ ;
+
+ // Set HPRE, D1PPRE, D2PPRE, D2PPRE2, D3PPRE dividers
+ MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_HPRE_Msk, RCC_D1CFGR_HPRE_DIV2);
+ MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1PPRE_Msk, RCC_D1CFGR_D1PPRE_DIV2);
+ MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE1_Msk, RCC_D2CFGR_D2PPRE1_DIV2);
+ MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE2_Msk, RCC_D2CFGR_D2PPRE2_DIV2);
+ MODIFY_REG(RCC->D3CFGR, RCC_D3CFGR_D3PPRE_Msk, RCC_D3CFGR_D3PPRE_DIV2);
+
+ // Switch on PLL1
+ RCC->CR |= RCC_CR_PLL1ON;
+ while (!(RCC->CR & RCC_CR_PLL1RDY))
+ ;
+
+ // Switch system clock source (SYSCLK) to PLL1
+ MODIFY_REG(RCC->CFGR, RCC_CFGR_SW_Msk, RCC_CFGR_SW_PLL1);
+ while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL1)
+ ;
+}
+
+// Main entry point - called from armcm_boot.c:ResetHandler()
+void
+armcm_main(void)
+{
+ // Run SystemInit() and then restore VTOR
+ SystemInit();
+
+ SCB->VTOR = (uint32_t)VectorTable;
+
+ clock_setup();
+
+ sched_main();
+}
diff --git a/src/stm32/stm32h7_adc.c b/src/stm32/stm32h7_adc.c
new file mode 100644
index 00000000..792a3335
--- /dev/null
+++ b/src/stm32/stm32h7_adc.c
@@ -0,0 +1,234 @@
+// ADC functions on STM32H7
+//
+// Copyright (C) 2020 Konstantin Vogel <konstantin.vogel@gmx.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "board/irq.h" // irq_save
+#include "board/misc.h" // timer_from_us
+#include "command.h" // shutdown
+#include "compiler.h" // ARRAY_SIZE
+#include "generic/armcm_timer.h" // udelay
+#include "gpio.h" // gpio_adc_setup
+#include "internal.h" // GPIO
+#include "sched.h" // sched_shutdown
+
+// Number of samples is 2^OVERSAMPLES_EXPONENT (exponent can be 0-10)
+#define OVERSAMPLES_EXPONENT 3
+#define OVERSAMPLES (1 << OVERSAMPLES_EXPONENT)
+
+// LDORDY registers are missing from CMSIS (only available on revision V!)
+#define ADC_ISR_LDORDY_Pos (12U)
+#define ADC_ISR_LDORDY_Msk (0x1UL << ADC_ISR_LDORDY_Pos)
+#define ADC_ISR_LDORDY ADC_ISR_LDORDY_Msk
+
+DECL_CONSTANT("ADC_MAX", 4095);
+
+// GPIOs like A0_C are not covered!
+// This always gives the pin connected to the positive channel
+static const uint8_t adc_pins[] = {
+ // ADC1
+ 0, // PA0_C ADC12_INP0
+ 0, // PA1_C ADC12_INP1
+ GPIO('F', 11), // ADC1_INP2
+ GPIO('A', 6), // ADC12_INP3
+ GPIO('C', 4), // ADC12_INP4
+ GPIO('B', 1), // ADC12_INP5
+ GPIO('F', 12), // ADC1_INP6
+ GPIO('A', 7), // ADC12_INP7
+ GPIO('C', 5), // ADC12_INP8
+ GPIO('B', 0), // ADC12_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('C', 3), // ADC12_INP13
+ GPIO('A', 2), // ADC12_INP14
+ GPIO('A', 3), // ADC12_INP15
+ GPIO('A', 0), // ADC1_INP16
+ GPIO('A', 1), // ADC1_INP17
+ GPIO('A', 4), // ADC12_INP18
+ GPIO('A', 5), // ADC12_INP19
+ // ADC2
+ 0, // PA0_C ADC12_INP0
+ 0, // PA1_C ADC12_INP1
+ GPIO('F', 13), // ADC2_INP2
+ GPIO('A', 6), // ADC12_INP3
+ GPIO('C', 4), // ADC12_INP4
+ GPIO('B', 1), // ADC12_INP5
+ GPIO('F', 14), // ADC2_INP6
+ GPIO('A', 7), // ADC12_INP7
+ GPIO('C', 5), // ADC12_INP8
+ GPIO('B', 0), // ADC12_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('C', 3), // ADC12_INP13
+ GPIO('A', 2), // ADC12_INP14
+ GPIO('A', 3), // ADC12_INP15
+ 0, // dac_out1
+ 0, // dac_out2
+ GPIO('A', 4), // ADC12_INP18
+ GPIO('A', 5), // ADC12_INP19
+ // ADC3
+ 0, // PC2_C ADC3_INP0
+ 0, // PC3_C ADC3_INP1
+ GPIO('F', 9) , // ADC3_INP2
+ GPIO('F', 7), // ADC3_INP3
+ GPIO('F', 5), // ADC3_INP4
+ GPIO('F', 3), // ADC3_INP5
+ GPIO('F', 10), // ADC3_INP6
+ GPIO('F', 8), // ADC3_INP7
+ GPIO('F', 6), // ADC3_INP8
+ GPIO('F', 4), // ADC3_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('H', 2), // ADC3_INP13
+ GPIO('H', 3), // ADC3_INP14
+ GPIO('H', 4), // ADC3_INP15
+ GPIO('H', 5), // ADC3_INP16
+ 0, // Vbat/4
+ 0, // VSENSE
+ 0, // VREFINT
+};
+
+
+// ADC timing:
+// ADC clock=30Mhz, Tconv=6.5, Tsamp=64.5, total=2.3666us*OVERSAMPLES
+
+struct gpio_adc
+gpio_adc_setup(uint32_t pin)
+{
+ // Find pin in adc_pins table
+ int chan;
+ for (chan=0; ; chan++) {
+ if (chan >= ARRAY_SIZE(adc_pins))
+ shutdown("Not a valid ADC pin");
+ if (adc_pins[chan] == pin)
+ break;
+ }
+
+ // Determine which ADC block to use, enable peripheral clock
+ // (SYSCLK 480Mhz) /HPRE(2) /CKMODE divider(4) /additional divider(2)
+ // (ADC clock 30Mhz)
+ ADC_TypeDef *adc;
+ if (chan >= 40){
+ adc = ADC3;
+ enable_pclock(ADC3_BASE);
+ MODIFY_REG(ADC3_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ } else if (chan >= 20){
+ adc = ADC2;
+ RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
+ MODIFY_REG(ADC12_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ } else{
+ adc = ADC1;
+ RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
+ MODIFY_REG(ADC12_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ }
+ chan %= 20;
+
+ // Enable the ADC
+ if (!(adc->CR & ADC_CR_ADEN)){
+ // Pwr
+ // Exit deep power down
+ MODIFY_REG(adc->CR, ADC_CR_DEEPPWD_Msk, 0);
+ // Switch on voltage regulator
+ adc->CR |= ADC_CR_ADVREGEN;
+ while(!(adc->ISR & ADC_ISR_LDORDY))
+ ;
+ // Set Boost mode for 25Mhz < ADC clock <= 50Mhz
+ MODIFY_REG(adc->CR, ADC_CR_BOOST_Msk, 0b11 << ADC_CR_BOOST_Pos);
+
+ // Calibration
+ // Set calibration mode to Single ended (not differential)
+ MODIFY_REG(adc->CR, ADC_CR_ADCALDIF_Msk, 0);
+ // Enable linearity calibration
+ MODIFY_REG(adc->CR, ADC_CR_ADCALLIN_Msk, ADC_CR_ADCALLIN);
+ // Start the calibration
+ MODIFY_REG(adc->CR, ADC_CR_ADCAL_Msk, ADC_CR_ADCAL);
+ while(adc->CR & ADC_CR_ADCAL)
+ ;
+
+ // Enable ADC
+ // "Clear the ADRDY bit in the ADC_ISR register by writing ‘1’"
+ adc->ISR |= ADC_ISR_ADRDY;
+ adc->CR |= ADC_CR_ADEN;
+ while(!(adc->ISR & ADC_ISR_ADRDY))
+ ;
+
+ // Set 64.5 ADC clock cycles sample time for every channel
+ // (Reference manual pg.940)
+ uint32_t aticks = 0b101;
+ // Channel 0-9
+ adc->SMPR1 = (aticks | (aticks << 3) | (aticks << 6)
+ | (aticks << 9) | (aticks << 12) | (aticks << 15)
+ | (aticks << 18) | (aticks << 21) | (aticks << 24)
+ | (aticks << 27));
+ // Channel 10-19
+ adc->SMPR2 = (aticks | (aticks << 3) | (aticks << 6)
+ | (aticks << 9) | (aticks << 12) | (aticks << 15)
+ | (aticks << 18) | (aticks << 21) | (aticks << 24)
+ | (aticks << 27));
+ // Disable Continuous Mode
+ MODIFY_REG(adc->CFGR, ADC_CFGR_CONT_Msk, 0);
+ // Set to 12 bit
+ MODIFY_REG(adc->CFGR, ADC_CFGR_RES_Msk, 0b110 << ADC_CFGR_RES_Pos);
+ // Set hardware oversampling
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_ROVSE_Msk, ADC_CFGR2_ROVSE);
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_OVSR_Msk,
+ (OVERSAMPLES - 1) << ADC_CFGR2_OVSR_Pos);
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_OVSS_Msk,
+ OVERSAMPLES_EXPONENT << ADC_CFGR2_OVSS_Pos);
+ }
+ gpio_peripheral(pin, GPIO_ANALOG, 0);
+ // Preselect (connect) channel
+ adc->PCSEL |= (1 << chan);
+ return (struct gpio_adc){ .adc = adc, .chan = chan };
+}
+
+// Try to sample a value. Returns zero if sample ready, otherwise
+// returns the number of clock ticks the caller should wait before
+// retrying this function.
+uint32_t
+gpio_adc_sample(struct gpio_adc g)
+{
+ ADC_TypeDef *adc = g.adc;
+ // Conversion ready
+ // EOC flag is cleared by hardware when reading DR
+ // the channel condition only works if this ist the only channel
+ // on the sequence and length set to 1 (ADC_SQR1_L = 0000)
+ if (adc->ISR & ADC_ISR_EOC && adc->SQR1 == (g.chan << 6))
+ return 0;
+ // Conversion started but not ready or wrong channel
+ if (adc->CR & ADC_CR_ADSTART)
+ return timer_from_us(10);
+ // Start sample
+ adc->SQR1 = (g.chan << 6);
+ adc->CR |= ADC_CR_ADSTART;
+ // Should take 2.3666us, add 1us for clock synchronisation etc.
+ return timer_from_us(1 + 2.3666*OVERSAMPLES);
+}
+
+// Read a value; use only after gpio_adc_sample() returns zero
+uint16_t
+gpio_adc_read(struct gpio_adc g)
+{
+ ADC_TypeDef *adc = g.adc;
+ return adc->DR;
+}
+
+// Cancel a sample that may have been started with gpio_adc_sample()
+void
+gpio_adc_cancel_sample(struct gpio_adc g)
+{
+ ADC_TypeDef *adc = g.adc;
+ irqstatus_t flag = irq_save();
+ // ADSTART is not as long true as SR_STRT on stm32f4
+ if ((adc->CR & ADC_CR_ADSTART || adc->ISR & ADC_ISR_EOC)
+ && adc->SQR1 == (g.chan << 6))
+ gpio_adc_read(g);
+ irq_restore(flag);
+}
diff --git a/src/stm32/stm32h7_serial.c b/src/stm32/stm32h7_serial.c
new file mode 100644
index 00000000..58e42da5
--- /dev/null
+++ b/src/stm32/stm32h7_serial.c
@@ -0,0 +1,94 @@
+// STM32H7 serial
+//
+// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "autoconf.h" // CONFIG_SERIAL_BAUD
+#include "board/armcm_boot.h" // armcm_enable_irq
+#include "board/serial_irq.h" // serial_rx_byte
+#include "command.h" // DECL_CONSTANT_STR
+#include "internal.h" // enable_pclock
+#include "sched.h" // DECL_INIT
+
+// Select the configured serial port
+#if CONFIG_STM32_SERIAL_USART1
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA10,PA9");
+ #define GPIO_Rx GPIO('A', 10)
+ #define GPIO_Tx GPIO('A', 9)
+ #define USARTx USART1
+ #define USARTx_IRQn USART1_IRQn
+#elif CONFIG_STM32_SERIAL_USART1_ALT_PB7_PB6
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PB7,PB6");
+ #define GPIO_Rx GPIO('B', 7)
+ #define GPIO_Tx GPIO('B', 6)
+ #define USARTx USART1
+ #define USARTx_IRQn USART1_IRQn
+#elif CONFIG_STM32_SERIAL_USART2
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA3,PA2");
+ #define GPIO_Rx GPIO('A', 3)
+ #define GPIO_Tx GPIO('A', 2)
+ #define USARTx USART2
+ #define USARTx_IRQn USART2_IRQn
+#elif CONFIG_STM32_SERIAL_USART2_ALT_PD6_PD5
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PD6,PD5");
+ #define GPIO_Rx GPIO('D', 6)
+ #define GPIO_Tx GPIO('D', 5)
+ #define USARTx USART2
+ #define USARTx_IRQn USART2_IRQn
+#elif CONFIG_STM32_SERIAL_USART3
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PB11,PB10");
+ #define GPIO_Rx GPIO('B', 11)
+ #define GPIO_Tx GPIO('B', 10)
+ #define USARTx USART3
+ #define USARTx_IRQn USART3_IRQn
+#elif CONFIG_STM32_SERIAL_USART3_ALT_PD9_PD8
+ DECL_CONSTANT_STR("RESERVE_PINS_serial", "PD9,PD8");
+ #define GPIO_Rx GPIO('D', 9)
+ #define GPIO_Tx GPIO('D', 8)
+ #define USARTx USART3
+ #define USARTx_IRQn USART3_IRQn
+#endif
+
+#define CR1_FLAGS (USART_CR1_UE | USART_CR1_RE | USART_CR1_TE \
+ | USART_CR1_RXNEIE)
+
+void
+USARTx_IRQHandler(void)
+{
+ uint32_t isr = USARTx->ISR;
+ if (isr & (USART_ISR_RXNE_RXFNE | USART_ISR_ORE))
+ serial_rx_byte(USARTx->RDR);
+ //USART_ISR_TXE_TXFNF only works with Fifo mode disabled
+ if (isr & USART_ISR_TXE_TXFNF && USARTx->CR1 & USART_CR1_TXEIE) {
+ uint8_t data;
+ int ret = serial_get_tx_byte(&data);
+ if (ret)
+ USARTx->CR1 = CR1_FLAGS;
+ else
+ USARTx->TDR = data;
+ }
+}
+
+void
+serial_enable_tx_irq(void)
+{
+ USARTx->CR1 = CR1_FLAGS | USART_CR1_TXEIE;
+}
+
+void
+serial_init(void)
+{
+ enable_pclock((uint32_t)USARTx);
+
+ uint32_t pclk = get_pclock_frequency((uint32_t)USARTx);
+ uint32_t div = DIV_ROUND_CLOSEST(pclk, CONFIG_SERIAL_BAUD);
+ USARTx->BRR = (((div / 16) << USART_BRR_DIV_MANTISSA_Pos)
+ | ((div % 16) << USART_BRR_DIV_FRACTION_Pos));
+ USARTx->CR1 = CR1_FLAGS;
+ armcm_enable_irq(USARTx_IRQHandler, USARTx_IRQn, 0);
+
+ gpio_peripheral(GPIO_Rx, GPIO_FUNCTION(7), 1);
+ gpio_peripheral(GPIO_Tx, GPIO_FUNCTION(7), 0);
+}
+DECL_INIT(serial_init);
diff --git a/src/stm32/watchdog.c b/src/stm32/watchdog.c
index 696d64d6..47c6b35c 100644
--- a/src/stm32/watchdog.c
+++ b/src/stm32/watchdog.c
@@ -4,9 +4,14 @@
//
// This file may be distributed under the terms of the GNU GPLv3 license.
+#include "autoconf.h" // CONFIG_MACH_STM32H7
#include "internal.h" // IWDG
#include "sched.h" // DECL_TASK
+#if CONFIG_MACH_STM32H7 // stm32h7 libraries only define IWDG1 and IWDG2
+#define IWDG IWDG1
+#endif
+
void
watchdog_reset(void)
{