diff --git a/meson.build b/meson.build
index f6a813bb..48b44034 100644
--- a/meson.build
+++ b/meson.build
@@ -222,7 +222,7 @@ stm32f405_src = ['platform/mcu/STM32F4xx/boot/startup.cpp',
'platform/mcu/STM32F4xx/drivers/rcc.c',
'platform/mcu/STM32F4xx/drivers/i2c_stm32.c',
'platform/drivers/ADC/adc_stm32f4.c',
- 'platform/drivers/SPI/spi_stm32.c',
+ 'platform/drivers/SPI/spi_stm32f4.c',
'platform/drivers/GPIO/gpio_stm32.c',
'platform/drivers/audio/stm32_dac.cpp',
'platform/drivers/audio/stm32_adc.cpp',
@@ -285,6 +285,7 @@ stm32h743_src = ['platform/mcu/STM32H7xx/boot/startup.cpp',
'platform/mcu/STM32H7xx/drivers/delays.cpp',
'platform/drivers/GPIO/gpio_stm32.c',
'platform/drivers/ADC/adc_stm32h7.c',
+ 'platform/drivers/SPI/spi_stm32h7.c',
'platform/mcu/CMSIS/Device/ST/STM32H7xx/Source/system_stm32h7xx.c']
stm32h743_inc = ['platform/mcu/CMSIS/Include',
diff --git a/platform/drivers/SPI/spi_stm32.c b/platform/drivers/SPI/spi_stm32f4.c
similarity index 100%
rename from platform/drivers/SPI/spi_stm32.c
rename to platform/drivers/SPI/spi_stm32f4.c
diff --git a/platform/drivers/SPI/spi_stm32h7.c b/platform/drivers/SPI/spi_stm32h7.c
new file mode 100644
index 00000000..901f8ab5
--- /dev/null
+++ b/platform/drivers/SPI/spi_stm32h7.c
@@ -0,0 +1,206 @@
+/***************************************************************************
+ * Copyright (C) 2024 by Silvano Seva IU2KWO *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include "spi_stm32.h"
+
+
+static inline uint8_t spi_sendRecv(SPI_TypeDef *spi, const uint8_t val)
+{
+ // Of course, setting the SPI frame size is not enough: to actually send
+ // 8 bits instead of 32, we have to access the TXDR register with an 8 bit
+ // write operation. Damn ST...
+ *((volatile uint8_t *) &spi->TXDR) = val;
+ while((spi->SR & SPI_SR_TXC) == 0) ;
+ return spi->RXDR;
+}
+
+int spiStm32_init(const struct spiDevice *dev, const uint32_t speed, const uint8_t flags)
+{
+ SPI_TypeDef *spi = (SPI_TypeDef *) dev->priv;
+ uint32_t busClk;
+
+ // On STM32H7 the clock tree is a bit more complicated than on STM32F4:
+ // - SPI1/2/3 use PLL1_Q clock for SCK generation and APB clock for reg access
+ // - SPI4/5/6 use APB clock both for SCK generation and reg access
+ // From PLL config PLL1_Q output is set at 100MHz
+ switch((uint32_t) spi)
+ {
+ case SPI1_BASE:
+ busClk = 100000000;
+ RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
+ __DSB();
+ break;
+
+ case SPI2_BASE:
+ busClk = 100000000;
+ RCC->APB1LENR |= RCC_APB1LENR_SPI2EN;
+ __DSB();
+ break;
+
+ case SPI3_BASE:
+ busClk = 100000000;
+ RCC->APB1LENR |= RCC_APB1LENR_SPI3EN;
+ __DSB();
+ break;
+
+ case SPI4_BASE:
+ busClk = getBusClock(PERIPH_BUS_APB2);
+ RCC->APB2ENR |= RCC_APB2ENR_SPI4EN;
+ __DSB();
+ break;
+
+ case SPI5_BASE:
+ busClk = getBusClock(PERIPH_BUS_APB2);
+ RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
+ __DSB();
+ break;
+
+ case SPI6_BASE:
+ busClk = getBusClock(PERIPH_BUS_APB4);
+ RCC->APB4ENR |= RCC_APB4ENR_SPI6EN;
+ __DSB();
+ break;
+
+ default:
+ return -ENODEV;
+ break;
+ }
+
+ uint8_t spiDiv;
+ uint32_t spiClk;
+
+ // Find nearest clock frequency, round down
+ for(spiDiv = 0; spiDiv < 7; spiDiv += 1)
+ {
+ spiClk = busClk / (1 << (spiDiv + 1));
+ if(spiClk <= speed)
+ break;
+ }
+
+ if(spiClk > speed)
+ return -EINVAL;
+
+ spi->I2SCFGR = 0;
+ spi->CR2 = 0;
+ spi->CFG1 = (spiDiv << SPI_CFG1_MBR_Pos) // Baud rate
+ | SPI_CFG1_DSIZE_2 // SPI frame size 8-bit
+ | SPI_CFG1_DSIZE_1
+ | SPI_CFG1_DSIZE_0;
+
+ spi->CR1 = SPI_CR1_SSI; // Force nCS state to active
+ spi->CFG2 = SPI_CFG2_AFCNTR // Peripheral keeps control of gpio AF mode
+ | SPI_CFG2_SSM // Software management of nCS
+ | SPI_CFG2_MASTER;
+
+ if((flags & SPI_FLAG_CPOL) != 0)
+ spi->CFG2 |= SPI_CFG2_CPOL;
+
+ if((flags & SPI_FLAG_CPHA) != 0)
+ spi->CFG2 |= SPI_CFG2_CPHA;
+
+ if((flags & SPI_LSB_FIRST) != 0)
+ spi->CFG2 |= SPI_CFG2_LSBFRST;
+
+ if(dev->mutex != NULL)
+ pthread_mutex_init((pthread_mutex_t *) dev->mutex, NULL);
+
+ return 0;
+}
+
+void spiStm32_terminate(const struct spiDevice *dev)
+{
+ SPI_TypeDef *spi = (SPI_TypeDef *) dev->priv;
+
+ switch((uint32_t) spi)
+ {
+ case SPI1_BASE:
+ RCC->APB2ENR &= ~RCC_APB2ENR_SPI1EN;
+ __DSB();
+ break;
+
+ case SPI2_BASE:
+ RCC->APB1LENR &= ~RCC_APB1LENR_SPI2EN;
+ __DSB();
+ break;
+
+ case SPI3_BASE:
+ RCC->APB1LENR &= ~RCC_APB1LENR_SPI3EN;
+ __DSB();
+ break;
+
+ case SPI4_BASE:
+ RCC->APB2ENR &= ~RCC_APB2ENR_SPI4EN;
+ __DSB();
+ break;
+
+ case SPI5_BASE:
+ RCC->APB2ENR &= ~RCC_APB2ENR_SPI5EN;
+ __DSB();
+ break;
+
+ case SPI6_BASE:
+ RCC->APB4ENR &= ~RCC_APB4ENR_SPI6EN;
+ __DSB();
+ break;
+
+ default:
+ break;
+ }
+
+ if(dev->mutex != NULL)
+ pthread_mutex_destroy((pthread_mutex_t *) dev->mutex);
+}
+
+int spiStm32_transfer(const struct spiDevice *dev, const void *txBuf,
+ void *rxBuf, const size_t size)
+{
+ SPI_TypeDef *spi = (SPI_TypeDef *) dev->priv;
+ uint8_t *rxData = (uint8_t *) rxBuf;
+ const uint8_t *txData = (const uint8_t *) txBuf;
+
+ spi->CR1 |= SPI_CR1_SPE;
+ spi->CR1 |= SPI_CR1_CSTART;
+
+ // Send only
+ if(rxBuf == NULL)
+ {
+ for(size_t i = 0; i < size; i++)
+ spi_sendRecv(spi, txData[i]);
+
+ return 0;
+ }
+
+ // Receive only
+ if(txBuf == NULL)
+ {
+ for(size_t i = 0; i < size; i++)
+ rxData[i] = spi_sendRecv(spi, 0x00);
+
+ return 0;
+ }
+
+ // Transmit and receive
+ for(size_t i = 0; i < size; i++)
+ rxData[i] = spi_sendRecv(spi, txData[i]);
+
+ spi->CR1 &= ~SPI_CR1_SPE;
+
+ return 0;
+}
diff --git a/platform/mcu/STM32H7xx/drivers/pll.cpp b/platform/mcu/STM32H7xx/drivers/pll.cpp
index 5d4311f7..c3f16231 100644
--- a/platform/mcu/STM32H7xx/drivers/pll.cpp
+++ b/platform/mcu/STM32H7xx/drivers/pll.cpp
@@ -97,3 +97,12 @@ void startPll()
RCC->CR |= RCC_CR_PLL2ON; // Start PLL2
while((RCC->CR & RCC_CR_PLL2RDY)==0) ; // Wait until ready
}
+
+uint32_t getBusClock(const uint8_t bus)
+{
+ if(bus >= PERIPH_BUS_NUM)
+ return 0;
+
+ // All busses run at 200MHz
+ return 200000000;
+}
diff --git a/platform/mcu/STM32H7xx/drivers/pll.h b/platform/mcu/STM32H7xx/drivers/pll.h
index 80a91ee9..c9a235df 100644
--- a/platform/mcu/STM32H7xx/drivers/pll.h
+++ b/platform/mcu/STM32H7xx/drivers/pll.h
@@ -29,6 +29,40 @@
#ifndef PLL_H
#define PLL_H
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Enumeration type for STM32 internal busses.
+ */
+enum PeriphBus
+{
+ PERIPH_BUS_AHB = 0,
+ PERIPH_BUS_APB1 = 1,
+ PERIPH_BUS_APB2 = 2,
+ PERIPH_BUS_APB4 = 3,
+
+ PERIPH_BUS_NUM
+};
+
+/**
+ * Configure and start the PLL.
+ */
void startPll();
+/**
+ * Get the clock frequency of a given peripheral bus.
+ *
+ * @param bus: bus identifier.
+ * @return bus clock frequency in Hz or zero in case of errors.
+ */
+uint32_t getBusClock(const uint8_t bus);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif //PLL_H