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 09ef3569..31a64290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,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 ba619672..7df2ab42 100644 --- a/G4PERTESTING/Core/Src/main.c +++ b/G4PERTESTING/Core/Src/main.c @@ -24,13 +24,16 @@ #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 +#include "Logomatic.h" +#include "spi.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -61,6 +64,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 +87,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 +103,7 @@ int main(void) HAL_Init(); /* USER CODE BEGIN Init */ + ITM_Enable(); /* USER CODE END Init */ @@ -101,10 +122,122 @@ 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_FULL_DUPLEX; + 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_OUTPUT; + 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"); + + LOGOMATIC("Starting message transaction...\n"); + + GR_SPI_Message msg; + msg.data = (uint8_t *)malloc(4 * sizeof(uint8_t)); + msg.size = 4; + + msg.data[0] = 'a'; + msg.data[1] = '0'; + msg.data[2] = '0'; + msg.data[3] = '0'; + + GR_SPI_Send(&ex_handler, &msg); + + LOGOMATIC("Sent message, now receiving...\n"); + + GR_SPI_Message recv_msg; + + while (GR_SPI_IsRxEmpty(&ex_handler)) {} + + GR_SPI_Receive(&ex_handler, &recv_msg); + + char str[5]; + strcpy(str, (char *)recv_msg.data); + str[4] = '\0'; + + LOGOMATIC("Received: %s\n", str); + + GR_SPI_Close(&ex_handler); /* USER CODE END 2 */ /* Infinite loop */ @@ -117,11 +250,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 @@ -131,12 +269,12 @@ void SystemClock_Config(void) LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); 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 */ @@ -155,14 +293,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 */ @@ -178,7 +316,6 @@ void Error_Handler(void) * state */ __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..c4dcad14 --- /dev/null +++ b/Lib/FancyLayers-RENAME/SPI/Inc/spi.h @@ -0,0 +1,158 @@ +#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 Deallocate the memory in a GR_SPI_Message + * + * @param handler + */ +void GR_SPI_Msg_Free(GR_SPI_Message *msg); + +/** + * @brief Handles SPI interrupts + * + * @param + * @return + */ +void GR_SPI_Interrupt_Handler(GR_SPI_Handler *handle); + +// Map SPI1-3 IRQHandlers to custom interrupt handler +void SPI1_IRQHandler(void); +void SPI2_IRQHandler(void); +void SPI3_IRQHandler(void); + +// ============================= 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 + */ +void GR_SPI_Receive(GR_SPI_Handler *handle, GR_SPI_Message *dest_msg); + +/** + * @brief Returns whether a message was received and is waiting in the Rx buffer + * + * @param handler + * @return Same as GR_CircularBuffer_IsEmpty + */ +bool GR_SPI_IsRxEmpty(GR_SPI_Handler *handle); + +// ============================= 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 handle + * @param + */ +void GR_SPI_Transfer_Tx_Bytes(GR_SPI_Handler *handle); + +/** + * @brief Pops off the next Tx message and initiates its transaction + * + * @param handle + * @param + */ +void GR_SPI_Begin_New_Tx(GR_SPI_Handler *handle); + +#endif // SPI_H diff --git a/Lib/FancyLayers-RENAME/SPI/Src/spi.c b/Lib/FancyLayers-RENAME/SPI/Src/spi.c new file mode 100644 index 00000000..99fd62e9 --- /dev/null +++ b/Lib/FancyLayers-RENAME/SPI/Src/spi.c @@ -0,0 +1,385 @@ +// Wonderful SPI Abstraction Layer courtesy of Bailey +#include "spi.h" + +#include + +// Transfer sizes +#define GR_SPI_TRANSFER_SIZE_8 8 +#define GR_SPI_TRANSFER_SIZE_16 16 + +// Current message status codes +#define GR_SPI_MSG_IN_PROGRESS 1 +#define GR_SPI_MSG_IDLE 0 +#define GR_SPI_INVALID_TX_SIZE 257 // test value - change later + +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; + + // Set current message variables + handle->current_msg = NULL; + handle->current_rx_msg_index = 0; + handle->current_tx_msg_index = 0; + handle->msg_status = GR_SPI_MSG_IDLE; + + // Store handler in lookup table for interrupts + if (handle->pins->SPIx == SPI1) { + GR_SPI_HANDLER_LUT[0] = handle; + } else if (handle->pins->SPIx == SPI2) { + GR_SPI_HANDLER_LUT[1] = handle; + } else if (handle->pins->SPIx == SPI3) { + GR_SPI_HANDLER_LUT[2] = handle; + } + /* else: do nothing */ + + // 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 <= (SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2)) { + handle->transfer_size = GR_SPI_TRANSFER_SIZE_8; + // Make the RXNE trigger when >= 8 bits are received + handle->pins->SPIx->CR2 |= SPI_CR2_FRXTH; + } + // Transaction size is 16-bits + else { + handle->transfer_size = GR_SPI_TRANSFER_SIZE_16; + // Make the RXNE trigger when >= 16 bits are received + handle->pins->SPIx->CR2 &= ~SPI_CR2_FRXTH; + } + + // Enable SPI peripheral after BSY flag clears + while (LL_SPI_IsActiveFlag_BSY(handle->pins->SPIx)) {} + LL_SPI_Enable(handle->pins->SPIx); + + // 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 (TXE is conditionally enabled during GR_SPI_Transfer_Tx_Bytes) + LL_SPI_EnableIT_ERR(handle->pins->SPIx); // Error interrupt + LL_SPI_EnableIT_RXNE(handle->pins->SPIx); // Not empty Rx buffer +} + +void SPI1_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[0]); } + +void SPI2_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[1]); } + +void SPI3_IRQHandler(void) { GR_SPI_Interrupt_Handler(GR_SPI_HANDLER_LUT[2]); } + +void GR_SPI_Interrupt_Handler(GR_SPI_Handler *handle) +{ + // 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); + handle->current_rx_msg_index += 2; + } 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; + handle->current_rx_msg_index += 1; + } else { + // ERROR: Current message is full + } + + // Push current message into Rx circular buffer to mark completion + if (handle->current_rx_msg_index == msg_size) { + handle->current_rx_msg_index = 0; + GR_CircularBuffer_Push(handle->rx_buffer, (void *)handle->current_msg, sizeof(GR_SPI_Message)); + GR_SPI_Msg_Free(handle->current_msg); + handle->current_msg = NULL; + // 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_IsEmpty(handle->tx_buffer)) { + handle->msg_status = GR_SPI_MSG_IDLE; + } else { + GR_SPI_Begin_New_Tx(handle); + } + } + } + // Check if Tx is empty + if (LL_SPI_IsActiveFlag_TXE(handle->pins->SPIx)) { + // Continue sending bytes in transaction + if (handle->current_tx_msg_index != GR_SPI_INVALID_TX_SIZE) { + GR_SPI_Transfer_Tx_Bytes(handle); + } + } +} + +// SPIx_IRQn is defined in stm32 libraries +uint32_t GR_SPI_Get_IRQn(SPI_TypeDef *SPIx) +{ + if (SPIx == SPI1) { + return SPI1_IRQn; // 35 + } else if (SPIx == SPI2) { + return SPI2_IRQn; // 36 + } else if (SPIx == SPI3) { + return SPI3_IRQn; // 51 + } else { + return GR_SPI_UNKNOWN_IRQN; + } +} + +void GR_SPI_Enable_Clocks(GR_SPI_Handler *handle) +{ + uint32_t GPIOx_Port; + + for (uint32_t i = 0; i < handle->pins->num_pins; i++) { + GPIO_TypeDef *gpio = handle->pins->GPIOx[i]; + + if (gpio == GPIOA) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOA; + } else if (gpio == GPIOB) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOB; + } else if (gpio == GPIOC) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOC; + } else if (gpio == GPIOD) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOD; + } else if (gpio == GPIOE) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOE; + } else if (gpio == GPIOF) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOF; + } else if (gpio == GPIOG) { + GPIOx_Port = LL_AHB2_GRP1_PERIPH_GPIOG; + } + // GPIOH does not exist on G4 board + else { + continue; // unknown GPIOx + } + + LL_AHB2_GRP1_EnableClock(GPIOx_Port); + } + + // Enable SPI clock + if (handle->pins->SPIx == SPI1) { + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); + } else if (handle->pins->SPIx == SPI2) { + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); + } else if (handle->pins->SPIx == SPI3) { + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3); + } else { + // ERROR: Unexpected SPI address + } +} + +void GR_SPI_Send(GR_SPI_Handler *handle, GR_SPI_Message *msg) +{ + // Push the new message (copy) onto the Tx circular buffer + GR_CircularBuffer_Push(handle->tx_buffer, msg, sizeof(GR_SPI_Message)); + + // Check if there is no message in progress + if (handle->msg_status != GR_SPI_MSG_IN_PROGRESS) { + GR_SPI_Begin_New_Tx(handle); + } +} + +void GR_SPI_Receive(GR_SPI_Handler *handle, GR_SPI_Message *dest_msg) +{ + GR_SPI_Message *rx_msg = GR_CircularBuffer_Pop(handle->rx_buffer); + + // Check if there was a message returned by buffer pop + if (rx_msg) { + // If sizes don't match, re-malloc correct size inside destination message + if (dest_msg->size != rx_msg->size) { + free(dest_msg->data); + dest_msg->size = rx_msg->size; + dest_msg->data = malloc(rx_msg->size * sizeof(uint8_t)); + } + + // Copy over data into the destination message + for (int i = 0; i < dest_msg->size; i++) { + dest_msg->data[i] = rx_msg->data[i]; + } + + // Deallocate rx_msg + GR_SPI_Msg_Free(rx_msg); + } +} + +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 + 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) +{ + 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 == GR_SPI_TRANSFER_SIZE_16 && 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 (handle->current_tx_msg_index == msg_size) { + handle->current_tx_msg_index = 0; + // Queue up next message to be sent + if (!GR_CircularBuffer_IsEmpty(handle->tx_buffer)) { + handle->current_msg = GR_CircularBuffer_Pop(handle->tx_buffer); + } + // No more messages to load into transfer buffer + else { + handle->current_tx_msg_index = GR_SPI_INVALID_TX_SIZE; + LL_SPI_DisableIT_TXE(handle->pins->SPIx); + } + } +} + +void GR_SPI_Close(GR_SPI_Handler *handler) +{ + // 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); + } + GR_SPI_Msg_Free(handler->current_msg); + GR_CircularBuffer_Free(&handler->rx_buffer); + GR_CircularBuffer_Free(&handler->tx_buffer); +} + +void GR_SPI_Msg_Free(GR_SPI_Message *msg) +{ + if (msg) { + if (msg->data) { + free(msg->data); + } + free(msg); + } +} + +bool GR_SPI_IsRxEmpty(GR_SPI_Handler *handle) { return GR_CircularBuffer_IsEmpty(handle->rx_buffer); } + +void GR_SPI_Begin_New_Tx(GR_SPI_Handler *handle) +{ + // 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); + + // Pull chip select to active low + LL_GPIO_ResetOutputPin(handle->pins->GPIOx[3], handle->pins->pin_nums[3]); + + // Note: This will trigger a TXE flag eventually (and maybe execute the handler below) + GR_SPI_Transfer_Tx_Bytes(handle); + + // Enable TXE interrupts for loading bytes into TX buffer + // ---Warning: Without an if-statement conditional, this statement could take over register buses quite often + // LL_SPI_EnableIT_TXE(handle->pins->SPIx); // Empty Tx buffer +} 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 +)