diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0e2fd381..414498df 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -72,6 +72,15 @@ ], "command": "cmake --build build/${command:cmake.activeBuildPresetName} --target G4BLINKY" }, + { + "label": "CMake: configure and build G4PERTESTING", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "CMake: configure" + ], + "command": "cmake --build build/${command:cmake.activeBuildPresetName} --target G4PERTESTING" + }, { "label": "CMake: configure and build U5BLINKY", "type": "shell", diff --git a/CMakeLists.txt b/CMakeLists.txt index bb3126aa..668a2e3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ include("${platform_path}/STM32L476xG/chip.cmake") include("${lib_path}/GlobalShare/common.cmake") include("${lib_path}/Utils/CircularBuffer/circular-buffer-lib.cmake") include("${lib_path}/cmake/gr-lib.cmake") +include("${lib_path}/FancyLayers-RENAME/SPI/spi.cmake") include("${lib_path}/Utils/BitManipulations/bit-utils.cmake") # Peripherals diff --git a/G4PERTESTING/CMakeLists.txt b/G4PERTESTING/CMakeLists.txt index fe543a8d..da19cea8 100644 --- a/G4PERTESTING/CMakeLists.txt +++ b/G4PERTESTING/CMakeLists.txt @@ -36,7 +36,6 @@ target_sources( Core/Src/gpio.c Core/Src/i2c.c Core/Src/main.c - Core/Src/spi.c Core/Src/stm32g4xx_hal_msp.c Core/Src/stm32g4xx_it.c Core/Src/syscalls.c @@ -46,6 +45,6 @@ target_sources( Core/Src/usart.c ) -target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE) +target_link_libraries(${PROJECT_NAME}_USER_CODE INTERFACE SPI_Lib) target_include_directories(${PROJECT_NAME}_USER_CODE INTERFACE Core/Inc) diff --git a/G4PERTESTING/Core/Inc/main.h b/G4PERTESTING/Core/Inc/main.h index 23d7ffce..9ff00d05 100644 --- a/G4PERTESTING/Core/Inc/main.h +++ b/G4PERTESTING/Core/Inc/main.h @@ -47,7 +47,8 @@ extern "C" { /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include +#include /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ diff --git a/G4PERTESTING/Core/Inc/spi.h b/G4PERTESTING/Core/Inc/spi.h deleted file mode 100644 index 74bed003..00000000 --- a/G4PERTESTING/Core/Inc/spi.h +++ /dev/null @@ -1,49 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file spi.h - * @brief This file contains all the function prototypes for - * the spi.c file - ****************************************************************************** - * @attention - * - * Copyright (c) 2024 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __SPI_H__ -#define __SPI_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "main.h" - -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -/* USER CODE BEGIN Private defines */ - -/* USER CODE END Private defines */ - -void MX_SPI3_Init(void); - -/* USER CODE BEGIN Prototypes */ - -/* USER CODE END Prototypes */ - -#ifdef __cplusplus -} -#endif - -#endif /* __SPI_H__ */ diff --git a/G4PERTESTING/Core/Src/main.c b/G4PERTESTING/Core/Src/main.c index d0c98d24..72c1f940 100644 --- a/G4PERTESTING/Core/Src/main.c +++ b/G4PERTESTING/Core/Src/main.c @@ -24,13 +24,14 @@ #include "fdcan.h" #include "gpio.h" #include "i2c.h" -#include "spi.h" +// #include "stm32g4xx_hal_ospi.h" #include "tim.h" #include "usart.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include "Logomatic.h" +#include "spi.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -61,6 +62,18 @@ void SystemClock_Config(void); /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +/* Enable ITM for SWO output */ +static void ITM_Enable(void) +{ + /* Enable TRC (Trace) */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + + /* Enable stimulus port 0 */ + ITM->TER |= (1UL << 0); + + /* Set trace control register */ + ITM->TCR |= ITM_TCR_ITMENA_Msk; +} /* USER CODE END 0 */ @@ -72,6 +85,11 @@ int main(void) { /* USER CODE BEGIN 1 */ + static GR_SPI_Handler ex_handler; + static LL_SPI_InitTypeDef ex_config; + static GR_SPI_Pins ex_pins; + /*HAL_OSPI_HandleTypeDef hospi; + HAL_StatusTypeDef status;*/ /* USER CODE END 1 */ @@ -83,6 +101,7 @@ int main(void) HAL_Init(); /* USER CODE BEGIN Init */ + ITM_Enable(); /* USER CODE END Init */ @@ -101,10 +120,95 @@ int main(void) MX_LPUART1_UART_Init(); MX_I2C2_Init(); MX_USART1_UART_Init(); - MX_SPI3_Init(); + // MX_SPI3_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ + LOGOMATIC("Booted!\n"); + + ex_config.TransferDirection = LL_SPI_HALF_DUPLEX_TX; + ex_config.Mode = LL_SPI_MODE_MASTER; + ex_config.DataWidth = LL_SPI_DATAWIDTH_8BIT; + ex_config.ClockPolarity = LL_SPI_POLARITY_LOW; + ex_config.ClockPhase = LL_SPI_PHASE_1EDGE; + ex_config.NSS = LL_SPI_NSS_HARD_INPUT; + ex_config.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2; + ex_config.BitOrder = LL_SPI_LSB_FIRST; + ex_config.CRCCalculation = LL_SPI_CRCCALCULATION_ENABLE; + ex_config.CRCPoly = 0x1D; + + ex_pins.SPIx = SPI1; + ex_pins.GPIOx = (GPIO_TypeDef **)(malloc(4 * sizeof(GPIO_TypeDef *))); + // All pins are in the A clock port + for (int i = 0; i < 4; i++) { + *(ex_pins.GPIOx + i) = GPIOA; + } + ex_pins.num_pins = 4; + ex_pins.pin_nums = (uint32_t *)malloc(4 * sizeof(int)); + ex_pins.pin_nums[0] = LL_GPIO_PIN_7; // COPI + ex_pins.pin_nums[1] = LL_GPIO_PIN_6; // CIPO + ex_pins.pin_nums[2] = LL_GPIO_PIN_5; // SCK + ex_pins.pin_nums[3] = LL_GPIO_PIN_4; // NSS + ex_pins.alternate_function_number = 5; + + GR_SPI_Initialize(&ex_handler, &ex_config, &ex_pins); + + LOGOMATIC("-= SPI + GPIO Init Verification (Measured | Expected) =-\n"); + /* ---------------- SPI ---------------- */ + LOGOMATIC("TransferDirection = %lu | %lu\n", LL_SPI_GetTransferDirection(ex_pins.SPIx), ex_config.TransferDirection); + LOGOMATIC("Mode = %lu | %lu\n", LL_SPI_GetMode(ex_pins.SPIx), ex_config.Mode); + LOGOMATIC("DataWidth = %lu | %lu\n", LL_SPI_GetDataWidth(ex_pins.SPIx), ex_config.DataWidth); + LOGOMATIC("ClockPolarity = %lu | %lu\n", LL_SPI_GetClockPolarity(ex_pins.SPIx), ex_config.ClockPolarity); + LOGOMATIC("ClockPhase = %lu | %lu\n", LL_SPI_GetClockPhase(ex_pins.SPIx), ex_config.ClockPhase); + LOGOMATIC("NSS = %lu | %lu\n", LL_SPI_GetNSSMode(ex_pins.SPIx), ex_config.NSS); + LOGOMATIC("BaudRate = %lu | %lu\n", LL_SPI_GetBaudRatePrescaler(ex_pins.SPIx), ex_config.BaudRate); + LOGOMATIC("BitOrder = %lu | %lu\n", LL_SPI_GetTransferBitOrder(ex_pins.SPIx), ex_config.BitOrder); + LOGOMATIC("CRC Enable = %lu | 1\n", LL_SPI_IsEnabledCRC(ex_pins.SPIx)); + LOGOMATIC("CRC Polynomial = 0x%lx | 0x%lx\n", ex_pins.SPIx->CRCPR, ex_config.CRCPoly); + LOGOMATIC("SPI Enable = %lu | 1\n", LL_SPI_IsEnabled(ex_pins.SPIx)); + + uint32_t spi_clk_en = 0; + if (ex_handler.pins->SPIx == SPI1) { + spi_clk_en = LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_SPI1); + } else if (ex_handler.pins->SPIx == SPI2) { + spi_clk_en = LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_SPI2); + } else if (ex_handler.pins->SPIx == SPI3) { + spi_clk_en = LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_SPI3); + } + LOGOMATIC("SPI Clock Enable = %lu | 1\n", spi_clk_en); + /* ---------------- GPIO CLOCKS ---------------- */ + for (int i = 0; i < ex_pins.num_pins; i++) { + uint32_t clk_en = 0; + + if (ex_pins.GPIOx[i] == GPIOA) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOA); + } else if (ex_pins.GPIOx[i] == GPIOB) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOB); + } else if (ex_pins.GPIOx[i] == GPIOC) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOC); + } else if (ex_pins.GPIOx[i] == GPIOD) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOD); + } else if (ex_pins.GPIOx[i] == GPIOE) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOE); + } else if (ex_pins.GPIOx[i] == GPIOF) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOF); + } else if (ex_pins.GPIOx[i] == GPIOG) { + clk_en = LL_AHB2_GRP1_IsEnabledClock(LL_AHB2_GRP1_PERIPH_GPIOG); + } + + LOGOMATIC("GPIO Clock [%p] = %lu | 1\n", (void *)ex_pins.GPIOx[i], clk_en); + } + /* ---------------- GPIO MODE + AF ---------------- */ + for (int i = 0; i < ex_pins.num_pins; i++) { + uint32_t pin = ex_pins.pin_nums[i]; + LOGOMATIC("GPIO[%d] Mode = %lu | %lu\n", pin, LL_GPIO_GetPinMode(ex_pins.GPIOx[i], pin), LL_GPIO_MODE_ALTERNATE); + + LOGOMATIC("GPIO[%d] AF = %lu | %lu\n", pin, (ex_pins.pin_nums[i] < LL_GPIO_PIN_8) ? LL_GPIO_GetAFPin_0_7(ex_pins.GPIOx[i], pin) : LL_GPIO_GetAFPin_8_15(ex_pins.GPIOx[i], pin), + ex_pins.alternate_function_number); + } + LOGOMATIC("-= End Verification =-\n"); + + GR_SPI_Close(&ex_handler); /* USER CODE END 2 */ /* Infinite loop */ @@ -117,11 +221,16 @@ int main(void) HAL_Delay(1000); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); HAL_Delay(1000); + LOGOMATIC("Blinking!\n"); /* USER CODE BEGIN 3 */ } } +/* void test_spi_initialize(GR_SPI_Handler* handle, LL_SPI_InitTypeDef* config, +GR_SPI_Pins* pin_config){ if(GR_SPI_Initialize(&handle, &config, &pin_config) +} */ + /** * @brief System Clock Configuration * @retval None @@ -132,13 +241,13 @@ void SystemClock_Config(void) while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) { } LL_PWR_EnableRange1BoostMode(); - LL_RCC_HSE_Enable(); - /* Wait till HSE is ready */ - while (LL_RCC_HSE_IsReady() != 1) { + LL_RCC_HSI_Enable(); + /* Wait till HSI is ready */ + while (LL_RCC_HSI_IsReady() != 1) { } - LL_RCC_HSE_EnableCSS(); - LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_1, 20, LL_RCC_PLLR_DIV_2); + LL_RCC_HSI_SetCalibTrimming(64); + LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_4, 85, LL_RCC_PLLR_DIV_2); LL_RCC_PLL_EnableDomain_SYS(); LL_RCC_PLL_Enable(); /* Wait till PLL is ready */ @@ -159,14 +268,14 @@ void SystemClock_Config(void) LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); - LL_SetSystemCoreClock(160000000); + LL_SetSystemCoreClock(170000000); /* Update the time base */ if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { + // status = ERROR; Error_Handler(); } } - /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ @@ -183,7 +292,6 @@ void Error_Handler(void) __disable_irq(); while (1) { } - /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** diff --git a/G4PERTESTING/Core/Src/spi.c b/G4PERTESTING/Core/Src/spi.c deleted file mode 100644 index 66459f2e..00000000 --- a/G4PERTESTING/Core/Src/spi.c +++ /dev/null @@ -1,95 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file spi.c - * @brief This file provides code for the configuration - * of the SPI instances. - ****************************************************************************** - * @attention - * - * Copyright (c) 2024 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Includes ------------------------------------------------------------------*/ -#include "spi.h" - -/* USER CODE BEGIN 0 */ - -/* USER CODE END 0 */ - -/* SPI3 init function */ -void MX_SPI3_Init(void) -{ - - /* USER CODE BEGIN SPI3_Init 0 */ - - /* USER CODE END SPI3_Init 0 */ - - LL_SPI_InitTypeDef SPI_InitStruct = {0}; - - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - - /* Peripheral clock enable */ - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3); - - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - /**SPI3 GPIO Configuration - PC10 ------> SPI3_SCK - PC11 ------> SPI3_MISO - PC12 ------> SPI3_MOSI - */ - GPIO_InitStruct.Pin = LL_GPIO_PIN_10; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_6; - LL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = LL_GPIO_PIN_11; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_6; - LL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = LL_GPIO_PIN_12; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_6; - LL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /* USER CODE BEGIN SPI3_Init 1 */ - - /* USER CODE END SPI3_Init 1 */ - SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX; - SPI_InitStruct.Mode = LL_SPI_MODE_MASTER; - SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT; - SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW; - SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE; - SPI_InitStruct.NSS = LL_SPI_NSS_SOFT; - SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32; - SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST; - SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE; - SPI_InitStruct.CRCPoly = 7; - LL_SPI_Init(SPI3, &SPI_InitStruct); - LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA); - LL_SPI_EnableNSSPulseMgt(SPI3); - /* USER CODE BEGIN SPI3_Init 2 */ - - /* USER CODE END SPI3_Init 2 */ -} - -/* USER CODE BEGIN 1 */ - -/* USER CODE END 1 */ diff --git a/Lib/FancyLayers-RENAME/SPI/Inc/spi.h b/Lib/FancyLayers-RENAME/SPI/Inc/spi.h new file mode 100644 index 00000000..d19ec23a --- /dev/null +++ b/Lib/FancyLayers-RENAME/SPI/Inc/spi.h @@ -0,0 +1,143 @@ +#ifndef SPI_H +#define SPI_H + +#include "circularBuffer.h" +#include "stm32g4xx_ll_bus.h" +#include "stm32g4xx_ll_gpio.h" +#include "stm32g4xx_ll_rcc.h" +#include "stm32g4xx_ll_spi.h" + +#define GR_SPI_UNKNOWN_IRQN -64 +#define GR_SPI_BUFFER_MESSAGE_CAPACITY 16 + +// Generic type +typedef struct { + uint8_t *data; // byte array + uint16_t size; +} GR_SPI_Message; + +typedef struct { + SPI_TypeDef *SPIx; // Pointer to SPI register wrapper (e.g. SPI1, SPI2, + // SPI3 macro defines) + // COPI, CIPO, SCLK, CS + GPIO_TypeDef **GPIOx; // Pointer to GPIO port register wrapper (e.g. + // GPIOA, GPIOB, GPIOC, etc. macro defines) + // COPI, CIPO, SCLK, CS + uint32_t *pin_nums; // SPI pin numbers (e.g. LL_GPIO_PIN_0, + // LL_GPIO_PIN_1, LL_GPIO_PIN_2 macro defines) + uint32_t num_pins; + uint32_t alternate_function_number; +} GR_SPI_Pins; + +// Generic type +typedef struct { + // Contains all configuration information + LL_SPI_InitTypeDef *spi_config; + GR_SPI_Pins *pins; + // GR structs + CircularBuffer *rx_buffer; + CircularBuffer *tx_buffer; + // Tx-Rx parameters + uint8_t transfer_size; + // Tx-Rx current messages + GR_SPI_Message *current_msg; + volatile uint16_t current_tx_msg_index, current_rx_msg_index; + volatile uint8_t msg_status; +} GR_SPI_Handler; + +static GR_SPI_Handler *GR_SPI_HANDLER_LUT[3]; // Stores pointer to the handler structs for SPI1 + // (0), SPI2 (1), & SPI3 (2) + +// ============================= Handler Functions ============================= + +/** + * @brief Initializes SPI with config values and alternate function number. + * Creates circular buffer structs. + * + * @param handle + * @param config + * @param alternate_function_num + * @return + */ +void GR_SPI_Initialize(GR_SPI_Handler *handle, LL_SPI_InitTypeDef *config, GR_SPI_Pins *pin_config); + +/** + * @brief Tear down the SPI handler + * + * @param handler + */ +void GR_SPI_Close(GR_SPI_Handler *handler); + +/** + * @brief Handles SPI interrupts + * + * @param + * @return + */ +void GR_SPI_Interrupt_Handler(GR_SPI_Handler *handle); + +// Map SPI1-3 IRQHandlers to custom interrupt handler +inline void SPI1_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[0]); } +inline void SPI2_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[1]); } +inline void SPI3_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[2]); } + +// ============================= Tx/Rx ============================= + +/** + * @brief Send data through SPI + * + * @param handle + * @param data + */ +void GR_SPI_Send(GR_SPI_Handler *handle, GR_SPI_Message *data); + +/** + * @brief Read from the SPI buffer; primarily intended for polling. Rely on the + * passed in handler when setup() is called + * + * @param handle + * @return SPI_Message + */ + +GR_SPI_Message *GR_SPI_Receive(GR_SPI_Handler *handle); + +/** + * @brief Checks if the Rx circular buffer is empty + * + * @param handle + */ +inline bool GR_SPI_Is_RXE(GR_SPI_Handler *handle) { return GR_CircularBuffer_IsEmpty(handle->rx_buffer); } + +// ============================= Helper Functions ============================= + +/** + * @brief Returns the interrupt request number for a given SPIx peripheral + * + * @param handle + */ +uint32_t GR_SPI_Get_IRQn(SPI_TypeDef *SPIx); + +/** + * @brief Enables the GPIO port clocks and SPI clock needed for the given pins + * + * @param pins + */ +void GR_SPI_Enable_Clocks(GR_SPI_Handler *handle); + +/** + * @brief Configures GPIO pins for SPI + * + * @param config + * @param pins + */ +void GR_SPI_Configure_Pins(GR_SPI_Handler *handle, LL_GPIO_InitTypeDef *pin_config); + +/** + * @brief Continues sending the next byte(s) within an SPI message + * + * @param config + * @param pins + */ +void GR_SPI_Transfer_Tx_Bytes(GR_SPI_Handler *handle); + +#endif // SPI_H \ No newline at end of file diff --git a/Lib/FancyLayers-RENAME/SPI/Src/spi.c b/Lib/FancyLayers-RENAME/SPI/Src/spi.c new file mode 100644 index 00000000..2fa3cf3d --- /dev/null +++ b/Lib/FancyLayers-RENAME/SPI/Src/spi.c @@ -0,0 +1,384 @@ +// Wonderful SPI Abstraction Layer courtesy of Bailey +#include "spi.h" + +#include + +// Defines hidden from user space +#define GR_SPI_TRANSFER_SIZE_8 8 +#define GR_SPI_TRANSFER_SIZE_16 16 +#define GR_SPI_STATUS 0 +#define GR_SPI_MSG_IN_PROGRESS -1 +#define GR_SPI_MSG_IDLE -1 + +void GR_SPI_Initialize(GR_SPI_Handler *handle, LL_SPI_InitTypeDef *config, GR_SPI_Pins *pin_config) +{ + // Create Circular Buffers + CircularBuffer *circular_buffer_ptr; + circular_buffer_ptr = GR_CircularBuffer_Create(GR_SPI_BUFFER_MESSAGE_CAPACITY); + if (circular_buffer_ptr == NULL) { + // Attempt to Create Rx Buffer Error + } else { + handle->rx_buffer = circular_buffer_ptr; + } + circular_buffer_ptr = GR_CircularBuffer_Create(GR_SPI_BUFFER_MESSAGE_CAPACITY); + if (circular_buffer_ptr == NULL) { + // Attempt to Create Tx Buffer Error + } else { + handle->tx_buffer = circular_buffer_ptr; + } + + // Copy over config values + handle->spi_config = (LL_SPI_InitTypeDef *)malloc(sizeof(LL_SPI_InitTypeDef)); // Make memory for LL_SPI_InitTypeDef + // config struct + *handle->spi_config = *config; + + // Deep copy of pins struct + handle->pins = (GR_SPI_Pins *)malloc(sizeof(GR_SPI_Pins)); // Make memory for GR_SPI_Pins struct + handle->pins->pin_nums = (uint32_t *)malloc(pin_config->num_pins * sizeof(uint32_t)); // Make memory for pin_nums[num_pins] + handle->pins->GPIOx = (GPIO_TypeDef **)malloc(pin_config->num_pins * sizeof(GPIO_TypeDef)); // Make memory for GPIOx[num_pins] + for (uint32_t i = 0; i < pin_config->num_pins; i++) { + handle->pins->pin_nums[i] = pin_config->pin_nums[i]; + handle->pins->GPIOx[i] = pin_config->GPIOx[i]; + } + handle->pins->SPIx = pin_config->SPIx; + handle->pins->num_pins = pin_config->num_pins; + handle->pins->alternate_function_number = pin_config->alternate_function_number; + + // Store handler in lookup table for interrupts + switch ((uint32_t)handle->pins->SPIx) { + case (uint32_t)SPI1: { + GR_SPI_HANDLER_LUT[0] = handle; + break; + } + case (uint32_t)SPI2: { + GR_SPI_HANDLER_LUT[1] = handle; + break; + } + case (uint32_t)SPI3: { + GR_SPI_HANDLER_LUT[2] = handle; + break; + } + } + + // Disable SPI + LL_SPI_Disable(handle->pins->SPIx); + + // Enable GPIO and SPI clocks + GR_SPI_Enable_Clocks(handle); + + // Configure GPIOs + LL_GPIO_InitTypeDef gpio_pin_config; + GR_SPI_Configure_Pins(handle, &gpio_pin_config); + + // Configure SPI protocol with config values + LL_SPI_Init(handle->pins->SPIx, config); + // Transaction size is 8-bits + if (config->DataWidth <= 8) { + handle->transfer_size = GR_SPI_TRANSFER_SIZE_8; + } + // Transaction size is 16-bits + else { + handle->transfer_size = GR_SPI_TRANSFER_SIZE_16; + } + + // Enable interrupts in NVIC + int SPI_IRQn = GR_SPI_Get_IRQn(handle->pins->SPIx); + if (SPI_IRQn != GR_SPI_UNKNOWN_IRQN) { + NVIC_SetPriority(SPI_IRQn, 1); + NVIC_EnableIRQ(SPI_IRQn); + } else { + return; // Throw an error + } + + // Enable interrupts at peripheral level + /*LL_SPI_EnableIT_ERR(handle->pins->SPIx); // Error interrupt + LL_SPI_EnableIT_RXNE(handle->pins->SPIx); // Not empty Rx buffer + LL_SPI_EnableIT_TXE(handle->pins->SPIx); // Empty Tx buffer*/ + + // Enable SPI peripheral after BSY flag clears + while (LL_SPI_IsActiveFlag_BSY(handle->pins->SPIx)) { + } + LL_SPI_Enable(handle->pins->SPIx); +} + +void GR_SPI_Interrupt_Handler(GR_SPI_Handler *handle) +{ + // Check if valid handle + if (handle == NULL) { + // Throw an error + return; + } + + // Check if called by error interrupt + // Frame format error + if (LL_SPI_IsActiveFlag_FRE(handle->pins->SPIx)) { + // Log an error + return; + } + // Overrun error + else if (LL_SPI_IsActiveFlag_OVR(handle->pins->SPIx)) { + // Log an error + return; + } + // Fault mode error + else if (LL_SPI_IsActiveFlag_MODF(handle->pins->SPIx)) { + // Log an error + return; + } + // CRC error + else if (LL_SPI_IsActiveFlag_CRCERR(handle->pins->SPIx)) { + // Log an error + return; + } + + // No errors detected... + + // Transfer modes for simple send/receive only + // #define LL_SPI_SIMPLEX_TX (SPI_CFG2_COMM_0) + // #define LL_SPI_SIMPLEX_RX (SPI_CFG2_COMM_1) + + // Check if Rx circular buffer is not empty + if (LL_SPI_IsActiveFlag_RXNE(handle->pins->SPIx)) { + uint16_t rx_index = handle->current_rx_msg_index, msg_size = handle->current_msg->size; + // Queue the message into the circular buffer + if (handle->transfer_size == GR_SPI_TRANSFER_SIZE_16 && rx_index <= msg_size - 2) { + uint16_t data = LL_SPI_ReceiveData16(handle->pins->SPIx); + handle->current_msg->data[rx_index + 1] = (uint8_t)(data & 0xFF); + handle->current_msg->data[rx_index] = (uint8_t)(data >> 8); + } else if (handle->transfer_size == GR_SPI_TRANSFER_SIZE_8 && rx_index <= msg_size - 1) { + uint8_t data = LL_SPI_ReceiveData8(handle->pins->SPIx); + handle->current_msg->data[rx_index] = data; + } else { + // ERROR: Current message is full + } + + // Push current message into Rx circular buffer to mark + // completion + if (rx_index == msg_size) { + GR_CircularBuffer_Push(handle->rx_buffer, (void *)handle->current_msg, sizeof(GR_SPI_Message *)); + handle->current_msg = NULL; + handle->current_rx_msg_index = 0; + // Finish transaction + LL_GPIO_SetOutputPin(handle->pins->GPIOx[3], handle->pins->pin_nums[3]); + // Only go to IDLE when no additional messages are in + // pipeline + if (!GR_CircularBuffer_Peek(handle->tx_buffer) && !handle->current_msg) { + handle->msg_status = GR_SPI_MSG_IDLE; + } else { + // Re-initiate a transaction + handle->msg_status = GR_SPI_MSG_IN_PROGRESS; + handle->current_tx_msg_index = 0; + handle->current_rx_msg_index = 0; + handle->current_msg = GR_CircularBuffer_Pop(handle->tx_buffer); + + // PIN mask needs to be fixed + LL_GPIO_ResetOutputPin(handle->pins->GPIOx[3], handle->pins->pin_nums[3]); + + GR_SPI_Transfer_Tx_Bytes(handle); + } + } + } + // Check if Tx is empty + if (LL_SPI_IsActiveFlag_TXE(handle->pins->SPIx)) { + // Check if there is no ongoing message + if (handle->msg_status != GR_SPI_MSG_IN_PROGRESS) { + // Check if there is a message in the Tx circular buffer + if (GR_CircularBuffer_Peek(handle->tx_buffer) != NULL) { + // Pop off the message + handle->current_msg = GR_CircularBuffer_Pop(handle->tx_buffer); + handle->msg_status = GR_SPI_MSG_IN_PROGRESS; + handle->current_tx_msg_index = 0; + } + } + // Now check if there is an ongoing message + if (handle->msg_status == GR_SPI_MSG_IN_PROGRESS) { + GR_SPI_Transfer_Tx_Bytes(handle); + } + } +} + +// SPIx_IRQn is defined in stm32 libraries +uint32_t GR_SPI_Get_IRQn(SPI_TypeDef *SPIx) +{ + switch ((uint32_t)SPIx) { + case (uint32_t)SPI1: + return SPI1_IRQn; // 35 + case (uint32_t)SPI2: + return SPI2_IRQn; // 36 + case (uint32_t)SPI3: + return SPI3_IRQn; // 51 + default: + return GR_SPI_UNKNOWN_IRQN; + } +} + +void GR_SPI_Enable_Clocks(GR_SPI_Handler *handle) +{ + // Enable GPIO clock + uint32_t GPIOx_Port; + for (uint32_t i = 0; i < handle->pins->num_pins; i++) { + switch ((uint32_t)handle->pins->GPIOx[i]) { + case (uint32_t)GPIOA: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOA; + break; + case (uint32_t)GPIOB: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOB; + break; + case (uint32_t)GPIOC: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOC; + break; + case (uint32_t)GPIOD: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOD; + break; + case (uint32_t)GPIOE: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOE; + break; + case (uint32_t)GPIOF: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOF; + break; + case (uint32_t)GPIOG: + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOG; + break; + // Doesn't exist on G4 board + // case (uint32_t) GPIOH: + // GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOH; + // break; + default: // Do nothing (unknown GPIOx) + continue; + } + LL_AHB2_GRP1_EnableClock(GPIOx_Port); + } + + // Enable SPI clock + switch ((uint32_t)handle->pins->SPIx) { + case (uint32_t)SPI1: + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); + break; + case (uint32_t)SPI2: + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); + break; + case (uint32_t)SPI3: + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3); + break; + default: // Do nothing (unknown SPIx) + } +} + +void GR_SPI_Send(GR_SPI_Handler *handle, GR_SPI_Message *msg) +{ + // Check if a message is currently in progress + if (handle->msg_status == GR_SPI_MSG_IN_PROGRESS) { + // Push the new message onto the Tx circular buffer + GR_CircularBuffer_Push(handle->tx_buffer, msg, msg->size); + } else { + handle->msg_status = GR_SPI_MSG_IN_PROGRESS; + handle->current_tx_msg_index = 0; + handle->current_rx_msg_index = 0; + handle->current_msg = msg; + + LL_GPIO_ResetOutputPin(handle->pins->GPIOx[3], handle->pins->pin_nums[3]); + + GR_SPI_Transfer_Tx_Bytes(handle); + } +} + +GR_SPI_Message *GR_SPI_Receive(GR_SPI_Handler *handle) +{ + // Returns NULL if there is no message to receive + GR_SPI_Message *data_ptr = GR_CircularBuffer_Pop(handle->rx_buffer); + return data_ptr; +} + +void GR_SPI_Configure_Pins(GR_SPI_Handler *handle, LL_GPIO_InitTypeDef *pin_config) +{ + LL_GPIO_StructInit(pin_config); // Default config values + pin_config->Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; // Very high output speed + pin_config->Pull = LL_GPIO_PULL_NO; // No pull-up or pull-down + // resistance + pin_config->OutputType = LL_GPIO_OUTPUT_PUSHPULL; // Push-pull output (not open-drain) + pin_config->Mode = LL_GPIO_MODE_ALTERNATE; // Alternate pin function mode + pin_config->Alternate = handle->pins->alternate_function_number; // Alternate function number + for (uint32_t i = 0; i < handle->pins->num_pins; i++) { + pin_config->Pin = handle->pins->pin_nums[i]; + LL_GPIO_Init(handle->pins->GPIOx[i], pin_config); + } +} + +void GR_SPI_Transfer_Tx_Bytes(GR_SPI_Handler *handle) +{ + // Make this function atomic + LL_SPI_DisableIT_TXE(handle->pins->SPIx); // Empty Tx buffer + + uint16_t tx_index = handle->current_tx_msg_index, msg_size = handle->current_msg->size; + // Send two bytes if transferring 16 bits + if (handle->transfer_size && tx_index <= msg_size - 2) { + uint16_t data = (((uint16_t)handle->current_msg->data[tx_index]) << 8) + handle->current_msg->data[tx_index + 1]; + LL_SPI_TransmitData16(handle->pins->SPIx, data); + handle->current_tx_msg_index += 2; + } + // Send one byte if transferring 8 bits or transferring 16 bits with + // only 8 bits left + else if (handle->transfer_size == GR_SPI_TRANSFER_SIZE_8 && tx_index <= msg_size - 1) { + uint8_t data = handle->current_msg->data[tx_index]; + LL_SPI_TransmitData8(handle->pins->SPIx, data); + handle->current_tx_msg_index += 1; + } else { + // ERROR: Message was already fully transmitted + } + + // Mark message send complete + if (tx_index == msg_size) { + handle->current_tx_msg_index = 0; + // Queue up next message to be sent + if (GR_CircularBuffer_Peek(handle->tx_buffer)) { + handle->current_msg = GR_CircularBuffer_Pop(handle->tx_buffer); + } + } + + // Resume TXE interrupts + LL_SPI_EnableIT_TXE(handle->pins->SPIx); // Empty Tx buffer +} + +void GR_SPI_Close(GR_SPI_Handler *handler) +{ + + // BTW I asked Gemini for help on this and what it says seems right + // but shoot me if it's wrong + + // Safety Checks + LL_GPIO_SetOutputPin(handler->pins->GPIOx[3], handler->pins->pin_nums[3]); // Set CS high + + // Set all the pins analog + for (int i = 0; i < 3; i++) { + LL_GPIO_SetPinMode(handler->pins->GPIOx[i], handler->pins->pin_nums[i], LL_GPIO_MODE_ANALOG); + } + + // Disable and De-init + LL_SPI_Disable(handler->pins->SPIx); + LL_SPI_DeInit(handler->pins->SPIx); + // IDK man check the error codes if it doesn't work + + // Deallocate memory + if (handler->spi_config) { + free(handler->spi_config); + } + if (handler->pins->GPIOx) { + free(handler->pins->GPIOx); + } + if (handler->pins->pin_nums) { + free(handler->pins->pin_nums); + } + if (handler->pins) { + free(handler->pins); + } + if (handler->current_msg && handler->current_msg->data) { + free(handler->current_msg->data); + } + if (handler->current_msg) { + free(handler->current_msg); + } + GR_CircularBuffer_Free(&handler->rx_buffer); + GR_CircularBuffer_Free(&handler->tx_buffer); + + return; +} \ No newline at end of file diff --git a/Lib/FancyLayers-RENAME/SPI/spi.cmake b/Lib/FancyLayers-RENAME/SPI/spi.cmake new file mode 100644 index 00000000..f1c8a00e --- /dev/null +++ b/Lib/FancyLayers-RENAME/SPI/spi.cmake @@ -0,0 +1,11 @@ +add_library(SPI_Lib INTERFACE) + +target_sources(SPI_Lib INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Src/spi.c) +target_include_directories(SPI_Lib INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Inc) + +target_link_libraries( + SPI_Lib + INTERFACE + GLOBALSHARE_LIB + CircularBuffer_Lib +)