diff --git a/src/application/main.c b/src/application/main.c index 17fbe9f..c48d234 100644 --- a/src/application/main.c +++ b/src/application/main.c @@ -1,10 +1,12 @@ - #include "protocol.h" +#include "system/bus/spi.h" #include "system/led.h" #include "system/system.h" #include "util/error.h" #include "util/logging.h" +typedef enum { TX_BUFFER_DATA = 0x9F, RX_BUFFER_LENGTH = 38 } BufferConstants; + int main(void) { SYSTEM_init(); @@ -16,6 +18,12 @@ int main(void) return -1; } + // Initialize the SPI bus + SPI_Handle *spi_handle = SPI_init(0); + if (spi_handle == NULL) { + LOG_ERROR("Failed to initialize SPI bus"); + } + // Main application loop while (1) { // Process protocol tasks @@ -29,6 +37,19 @@ int main(void) LED_toggle(LED_GREEN); last_toggle = SYSTEM_get_tick(); } + + // SPI communication example + uint8_t tx_data = TX_BUFFER_DATA; + uint8_t rx_data[RX_BUFFER_LENGTH] = { 0 }; + SPI_transmit_receive(spi_handle, &tx_data, rx_data, sizeof(rx_data)); + LOG_INFO( + "SPI Received: %02X %02X %02X %02X %02X", + rx_data[0], + rx_data[1], + rx_data[2], + rx_data[3], + rx_data[4] + ); } __builtin_unreachable(); diff --git a/src/platform/h563xx/CMakeLists.txt b/src/platform/h563xx/CMakeLists.txt index 5a81e83..d19075b 100644 --- a/src/platform/h563xx/CMakeLists.txt +++ b/src/platform/h563xx/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(pslab-platform esp_ll.c led_ll.c platform.c + spi_ll.c tim_ll.c uart_ll.c usb_ll.c @@ -32,6 +33,7 @@ target_link_libraries(pslab-platform HAL::STM32::H5::ICACHE HAL::STM32::H5::GPIO HAL::STM32::H5::RCCEx + HAL::STM32::H5::SPIEx HAL::STM32::H5::PWREx HAL::STM32::H5::TIMEx HAL::STM32::H5::UARTEx diff --git a/src/platform/h563xx/platform.c b/src/platform/h563xx/platform.c index 9404903..d9a6f9a 100644 --- a/src/platform/h563xx/platform.c +++ b/src/platform/h563xx/platform.c @@ -138,8 +138,10 @@ static void system_clock_config(void) /* Configure peripheral clocks - Set ADC clock source to PLL2R (75 MHz) */ RCC_PeriphCLKInitTypeDef periph_clk_init = { 0 }; - periph_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC; + periph_clk_init.PeriphClockSelection = + RCC_PERIPHCLK_ADCDAC | RCC_PERIPHCLK_SPI3; periph_clk_init.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_PLL2R; + periph_clk_init.Spi3ClockSelection = RCC_SPI3CLKSOURCE_PLL2P; // Configure PLL2 structure to match our desired configuration // This is required because HAL_RCCEx_PeriphCLKConfig calls @@ -148,7 +150,7 @@ static void system_clock_config(void) // NOLINTBEGIN: readability-magic-numbers periph_clk_init.PLL2.PLL2M = 5; // 25 MHz / 5 = 5 MHz periph_clk_init.PLL2.PLL2N = 60; // 5 MHz * 60 = 300 MHz VCO - periph_clk_init.PLL2.PLL2P = 2; // 300 MHz / 2 = 150 MHz + periph_clk_init.PLL2.PLL2P = 3; // 300 MHz / 3 = 100 MHz (for SPI3) periph_clk_init.PLL2.PLL2Q = 2; // 300 MHz / 2 = 150 MHz periph_clk_init.PLL2.PLL2R = 4; // 300 MHz / 4 = 75 MHz (for ADC) // NOLINTEND: readability-magic-numbers @@ -156,8 +158,10 @@ static void system_clock_config(void) periph_clk_init.PLL2.PLL2VCOSEL = RCC_PLL2_VCORANGE_MEDIUM; // 150-420 MHz VCO periph_clk_init.PLL2.PLL2FRACN = 0; + // Enable PLL2R outputs periph_clk_init.PLL2.PLL2ClockOut = - RCC_PLL2_DIVR; // Enable PLL2R output for ADC + RCC_PLL2_DIVR | // Enable PLL2R output for ADC + RCC_PLL2_DIVP; // Enable PLL2P output for SPI3 if (HAL_RCCEx_PeriphCLKConfig(&periph_clk_init) != HAL_OK) { /* ADC clock configuration failed */ diff --git a/src/platform/h563xx/spi_ll.c b/src/platform/h563xx/spi_ll.c new file mode 100644 index 0000000..3fd1afe --- /dev/null +++ b/src/platform/h563xx/spi_ll.c @@ -0,0 +1,199 @@ +/** + * @file spi_ll.c + * @brief SPI hardware implementation for STM32H563xx + * + * This module handles initialization and operation of the SPI peripheral of + * the STM32H5 microcontroller. It configures the hardware and dispatches SPI + * interrupts to the hardware-independent SPI implementation. + * + * Implementation Details: + * - Supports single SPI instance (SPI1) + * - Configured for 8-bit data size + * - DMA-based transmission and reception + * + * @author Tejas Garg + * @date 2025-10-31 + */ + +#include +#include +#include + +#include "stm32h5xx_hal.h" + +#include "util/error.h" + +#include "spi_ll.h" + +/* SPI instance structure */ +typedef struct { + SPI_HandleTypeDef *hspi; + bool initialized; +} SPIInstance; + +/* HAL SPI handle */ +static SPI_HandleTypeDef g_hspi1 = { nullptr }; + +/* SPI instance configuration */ +static SPIInstance g_spi_instances[1] = { + [SPI_BUS_0] = { + .hspi = &g_hspi1, + }, +}; + +void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) +{ + GPIO_InitTypeDef gpio_init = { 0 }; + + if (hspi->Instance == SPI3) { + /* SPI3 clock enable */ + __HAL_RCC_SPI3_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + + /* SPI1 GPIO Configuration: PC10=SCK, PC11=MISO, PC12=MOSI */ + gpio_init.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + gpio_init.Alternate = GPIO_AF6_SPI3; + HAL_GPIO_Init(GPIOC, &gpio_init); + + } else { + THROW(ERROR_INVALID_ARGUMENT); + } +} + +/** + * @brief Initialize the SPI peripheral. + * + * @param bus SPI bus instance to initialize + */ +void SPI_LL_init(SPI_Bus bus) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + + SPIInstance *instance = &g_spi_instances[bus]; + if (instance->initialized) { + return; + } + SPI_TypeDef *spi_instance[SPI_BUS_COUNT] = { + [SPI_BUS_0] = SPI3, + }; + + /* Initialize SPI1 */ + instance->hspi->Instance = spi_instance[bus]; + instance->hspi->Init.Mode = SPI_MODE_MASTER; + instance->hspi->Init.Direction = SPI_DIRECTION_2LINES; + instance->hspi->Init.DataSize = SPI_DATASIZE_8BIT; + instance->hspi->Init.CLKPolarity = SPI_POLARITY_LOW; + instance->hspi->Init.CLKPhase = SPI_PHASE_1EDGE; + instance->hspi->Init.NSS = SPI_NSS_SOFT; + instance->hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; + instance->hspi->Init.FirstBit = SPI_FIRSTBIT_MSB; + instance->hspi->Init.TIMode = SPI_TIMODE_DISABLE; + instance->hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + + if (HAL_SPI_Init(instance->hspi) != HAL_OK) { + THROW(ERROR_HARDWARE_FAULT); + } + instance->initialized = true; +} + +/** + * @brief Deinitialize the SPI peripheral. + * + * @param bus SPI bus instance to deinitialize + */ +void SPI_LL_deinit(SPI_Bus bus) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + + SPIInstance *instance = &g_spi_instances[bus]; + if (!instance->initialized) { + return; + } + + /* Deinitialize SPI */ + if (HAL_SPI_DeInit(instance->hspi) != HAL_OK) { + THROW(ERROR_HARDWARE_FAULT); + } + instance->initialized = false; +} + +/** + * @brief Transmit data over SPI. + * + * @param bus SPI bus instance to use + * @param data Pointer to the data buffer to transmit + * @param size Size of the data buffer + */ +void SPI_LL_transmit(SPI_Bus bus, uint8_t const *txdata, size_t size) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + + SPIInstance *instance = &g_spi_instances[bus]; + if (!instance->initialized) { + THROW(ERROR_INVALID_ARGUMENT); + } + + // Start the transmission + HAL_SPI_Transmit(instance->hspi, (uint8_t *)txdata, size, HAL_MAX_DELAY); +} +/** + * @brief Receive data over SPI. + * + * @param bus SPI bus instance to use + * @param data Pointer to the buffer to store received data + * @param size Size of the data buffer + */ + +void SPI_LL_receive(SPI_Bus bus, uint8_t *rxdata, size_t size) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + + SPIInstance *instance = &g_spi_instances[bus]; + if (!instance->initialized) { + THROW(ERROR_INVALID_ARGUMENT); + } + + // Start the reception + HAL_SPI_Receive(instance->hspi, rxdata, size, HAL_MAX_DELAY); +} + +/** + * @brief Transmit and receive data over SPI. + * + * @param bus SPI bus instance to use + * @param tx_data Pointer to the data buffer to transmit + * @param rx_data Pointer to the buffer to store received data + * @param size Size of the data buffer + */ +void SPI_LL_transmit_receive( + SPI_Bus bus, + uint8_t const *tx_data, + uint8_t *rx_data, + size_t size +) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + + SPIInstance *instance = &g_spi_instances[bus]; + if (!instance->initialized) { + THROW(ERROR_INVALID_ARGUMENT); + } + + // Start the transmission and reception + HAL_SPI_TransmitReceive( + instance->hspi, (uint8_t *)tx_data, rx_data, size, HAL_MAX_DELAY + ); +} \ No newline at end of file diff --git a/src/platform/h563xx/stm32h5xx_hal_conf.h b/src/platform/h563xx/stm32h5xx_hal_conf.h index 1961bb0..36a4e46 100644 --- a/src/platform/h563xx/stm32h5xx_hal_conf.h +++ b/src/platform/h563xx/stm32h5xx_hal_conf.h @@ -85,7 +85,7 @@ extern "C" { // #define HAL_SDRAM_MODULE_ENABLED // #define HAL_SMARTCARD_MODULE_ENABLED // #define HAL_SMBUS_MODULE_ENABLED -// #define HAL_SPI_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED // #define HAL_SRAM_MODULE_ENABLED #define HAL_TIM_MODULE_ENABLED #define HAL_UART_MODULE_ENABLED diff --git a/src/platform/spi_ll.h b/src/platform/spi_ll.h new file mode 100644 index 0000000..a3003ed --- /dev/null +++ b/src/platform/spi_ll.h @@ -0,0 +1,55 @@ +/** + * @file spi_ll.h + * @brief SPI low-level hardware interface for STM32H563xx + */ + +#ifndef SPI_LL_H +#define SPI_LL_H + +#include +#include + +/** + * @brief SPI bus instance enumeration + */ +typedef enum { SPI_BUS_0 = 0, SPI_BUS_COUNT = 1 } SPI_Bus; + +/** + * @brief Initialize the SPI peripheral. + */ +void SPI_LL_init(SPI_Bus bus); + +/** + * @brief Deinitialize the SPI peripheral. + * @param bus SPI bus instance to deinitialize. + */ +void SPI_LL_deinit(SPI_Bus bus); + +/** + * @brief Transmit data over SPI. + * @param txdata Pointer to the data buffer to transmit. + * @param size Size of the data buffer. + */ +void SPI_LL_transmit(SPI_Bus bus, uint8_t const *txdata, size_t size); + +/** + * @brief Receive data over SPI. + * @param rxdata Pointer to the buffer to store received data. + * @param size Size of the data buffer. + */ +void SPI_LL_receive(SPI_Bus bus, uint8_t *rxdata, size_t size); + +/** + * @brief Transmit and receive data over SPI. + * @param tx_data Pointer to the data buffer to transmit. + * @param rx_data Pointer to the buffer to store received data. + * @param size Size of the data buffer. + */ +void SPI_LL_transmit_receive( + SPI_Bus bus, + uint8_t const *tx_data, + uint8_t *rx_data, + size_t size +); + +#endif /* SPI_LL_H */ \ No newline at end of file diff --git a/src/system/bus/CMakeLists.target.txt b/src/system/bus/CMakeLists.target.txt index 8b9529c..f1d89a7 100644 --- a/src/system/bus/CMakeLists.target.txt +++ b/src/system/bus/CMakeLists.target.txt @@ -7,6 +7,7 @@ target_include_directories(pslab-system target_sources(pslab-system PRIVATE + spi.c uart.c usb.c ) diff --git a/src/system/bus/spi.c b/src/system/bus/spi.c new file mode 100644 index 0000000..c1c8b68 --- /dev/null +++ b/src/system/bus/spi.c @@ -0,0 +1,145 @@ +/** + * @file spi_ll.c + * @brief SPI system level implementation + * + * + * @author Tejas Garg + * @date 2024-10-31 + */ + +#include "platform/spi_ll.h" +#include "util/error.h" + +#include "spi.h" + +#include +#include +#include +#include + +struct SPI_Handle { + SPI_Bus bus_id; + bool initialized; +}; + +static SPI_Handle *g_spi_instances[SPI_BUS_COUNT] = { nullptr }; + +/** + * @brief Initialize the SPI peripheral. + * + * @param bus SPI bus instance to initialize + */ + +SPI_Handle *SPI_init(size_t bus) +{ + if (bus >= SPI_BUS_COUNT) { + THROW(ERROR_INVALID_ARGUMENT); + } + SPI_Bus bus_id = (SPI_Bus)bus; + if (g_spi_instances[bus_id]->initialized) { + THROW(ERROR_RESOURCE_BUSY); + } + /* Allocate handle */ + SPI_Handle *handle = malloc(sizeof(SPI_Handle)); + if (!handle) { + THROW(ERROR_OUT_OF_MEMORY); + } + + handle->bus_id = bus_id; + /* Initialize SPI */ + SPI_LL_init(bus); + handle->initialized = true; + g_spi_instances[bus_id] = handle; + + return handle; +} + +/** + * @brief Deinitialize the SPI peripheral. + * + * @param bus SPI bus instance to deinitialize + */ + +void SPI_deinit(SPI_Handle *handle) +{ + if (handle == NULL) { + THROW(ERROR_INVALID_ARGUMENT); + } + + if (!handle->initialized) { + return; + } + + /* Deinitialize SPI */ + SPI_LL_deinit(handle->bus_id); + handle->initialized = false; + + /* Free handle */ + free(handle); +} + +/** + * @brief Transmit data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param txbuf Pointer to the data buffer to transmit + * @param sz Size of the data buffer + */ +void SPI_transmit(SPI_Handle *handle, uint8_t const *txbuf, size_t sz) +{ + if (handle == NULL || txbuf == NULL || sz == 0) { + THROW(ERROR_INVALID_ARGUMENT); + } + + if (!handle->initialized) { + THROW(ERROR_DEVICE_NOT_READY); + } + + SPI_LL_transmit(handle->bus_id, txbuf, sz); +} + +/** + * @brief Receive data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param rxbuf Pointer to the buffer to store received data + * @param sz Size of the data buffer + */ +void SPI_receive(SPI_Handle *handle, uint8_t *rxbuf, size_t sz) +{ + if (handle == NULL || rxbuf == NULL || sz == 0) { + THROW(ERROR_INVALID_ARGUMENT); + } + + if (!handle->initialized) { + THROW(ERROR_DEVICE_NOT_READY); + } + + SPI_LL_receive(handle->bus_id, rxbuf, sz); +} + +/** + * @brief Transmit and receive data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param tx_data Pointer to the data buffer to transmit. + * @param rx_data Pointer to the buffer to store received data. + * @param size Size of the data buffer. + */ +void SPI_transmit_receive( + SPI_Handle *handle, + uint8_t const *tx_data, + uint8_t *rx_data, + size_t size +) +{ + if (handle == NULL || tx_data == NULL || rx_data == NULL || size == 0) { + THROW(ERROR_INVALID_ARGUMENT); + } + + if (!handle->initialized) { + THROW(ERROR_DEVICE_NOT_READY); + } + + SPI_LL_transmit_receive(handle->bus_id, tx_data, rx_data, size); +} \ No newline at end of file diff --git a/src/system/bus/spi.h b/src/system/bus/spi.h new file mode 100644 index 0000000..e2f8234 --- /dev/null +++ b/src/system/bus/spi.h @@ -0,0 +1,77 @@ +/** + * @file spi_ll.c + * @brief SPI system level implementation + * + * + * @author Tejas Garg + * @date 2024-10-31 + */ +#ifndef SPI_H +#define SPI_H + +#include +#include + +#include "util/error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SPI bus handle structure + */ +typedef struct SPI_Handle SPI_Handle; + +/** + * @brief Initialize the SPI peripheral. + * + * @param bus SPI bus instance to initialize + */ +SPI_Handle *SPI_init(size_t bus); + +/** + * @brief Deinitialize the SPI peripheral. + * + * @param bus SPI bus instance to deinitialize + */ +void SPI_deinit(SPI_Handle *handle); + +/** + * @brief Transmit data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param txbuf Pointer to the data buffer to transmit + * @param sz Size of the data buffer + */ +void SPI_transmit(SPI_Handle *handle, uint8_t const *txbuf, size_t sz); + +/** + * @brief Receive data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param rxbuf Pointer to the buffer to store received data + * @param sz Size of the data buffer + */ +void SPI_receive(SPI_Handle *handle, uint8_t *rxbuf, size_t sz); + +/** + * @brief Transmit and receive data over SPI. + * + * @param SPI_Handle SPI Handle Pointer + * @param tx_data Pointer to the data buffer to transmit. + * @param rx_data Pointer to the buffer to store received data. + * @param size Size of the data buffer. + */ +void SPI_transmit_receive( + SPI_Handle *handle, + uint8_t const *tx_data, + uint8_t *rx_data, + size_t size +); + +#ifdef __cplusplus +} +#endif + +#endif /* SPI_H */ \ No newline at end of file