/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file spi_port.c * @author ST67 Application Team * @brief SPI bus interface porting layer implementation ****************************************************************************** * @attention * * Copyright (c) 2026 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 */ /** * This file is based on QCC74xSDK provided by Qualcomm. * See https://git.codelinaro.org/clo/qcc7xx/QCCSDK-QCC74x for more information. * * Reference source: examples/stm32_spi_host/QCC743_SPI_HOST/Core/Src/spi_iface.c */ /* Includes ------------------------------------------------------------------*/ #include #include #include #include #include "bsp_conf.h" #include "spi_port.h" #include "logging.h" #include "main.h" /* USER CODE BEGIN Includes */ /* Noms de broches : les etiquettes CubeMX du projet sont ST67_CHIP_EN, SPI4_CS ; le driver attend * CHIP_EN et SPI_CS (SPI_RDY est deja identique). Ces alias survivent a la regeneration. */ #ifndef CHIP_EN_Pin #define CHIP_EN_Pin ST67_CHIP_EN_Pin #define CHIP_EN_GPIO_Port ST67_CHIP_EN_GPIO_Port #endif #ifndef SPI_CS_Pin #define SPI_CS_Pin SPI4_CS_Pin #define SPI_CS_GPIO_Port SPI4_CS_GPIO_Port #endif /* USER CODE END Includes */ /* Global variables ----------------------------------------------------------*/ /* USER CODE BEGIN GV */ /* Le DMA du SPI ne sait ni lire ni ecrire la DTCM, or les tampons du driver viennent du tas FreeRTOS * place en DTCM (cf. Appli/stm32h7rsxx_RAMxspi1_ROMxspi2.ld) : le canal DMA sortait en erreur de * transfert (HAL_DMA_ERROR_TE) des que le module envoyait plus que l'en-tete. Les transferts DMA * passent donc par deux tampons de rebond en RAM_CMD (non cachee, region MPU 6), recopies a la * volee. Taille : W61_MAX_SPI_XFER + en-tete SPI + alignement. */ #define DMA_BOUNCE_SIZE 1536U static uint8_t dma_tx_bounce[DMA_BOUNCE_SIZE] __attribute__((section("noncacheable_buffer"), aligned(32))); static uint8_t dma_rx_bounce[DMA_BOUNCE_SIZE] __attribute__((section("noncacheable_buffer"), aligned(32))); static void * volatile dma_rx_dest; /* tampon final du transfert de reception en cours */ static volatile uint16_t dma_rx_len; /* USER CODE END GV */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private defines -----------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macros ------------------------------------------------------------*/ /** SysTick Reload Value Register */ #define SYSTICK_LOAD ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) /** SysTick Current Value Register */ #define SYSTICK_VALUE ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) /** * @brief Wait function to ensure a minimum wait time (in CPU cycles) * @param _start_tick Tick value when the wait period started * @param _nb_tick Number of ticks to wait * @retval None */ #define WAIT_FROM(_start_tick, _nb_tick) do \ {\ int32_t _end_tick = (_start_tick) - (_nb_tick); \ if (_end_tick < 0) /* Wrap */ \ { \ _end_tick += SYSTICK_LOAD; \ } \ while( SYSTICK_VALUE> _end_tick) {} \ }while(0) /** Convert microseconds to ticks */ #define MICROSECOND_TO_TICK( _us ) ( (_us) * (SystemCoreClock>>20) ) /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /** SPI transaction complete callback */ static spi_transaction_complete_t spi_port_transaction_complete_cb = NULL; static const char spi_port_error_str[] = "SPI not initialized\n"; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Functions Definition ------------------------------------------------------*/ void *spi_port_memcpy(void *dest, const void *src, unsigned int len) { /* USER CODE BEGIN memcpy_1 */ /* USER CODE END memcpy_1 */ uint8_t *d = (uint8_t *)dest; const uint8_t *s = (const uint8_t *)src; /* Copy bytes until the destination address is aligned to 4 bytes */ while (((uint32_t) d % 4 != 0) && len > 0) { *d++ = *s++; len--; } /* Copy 4-byte blocks */ uint32_t *d32 = (uint32_t *)d; const uint32_t *s32 = (const uint32_t *)s; while (len >= 4U) { *d32++ = *s32++; len -= 4U; } /* Copy remaining bytes */ d = (uint8_t *)d32; s = (const uint8_t *)s32; while (len > 0U) { *d++ = *s++; len--; } /* USER CODE BEGIN memcpy_2 */ /* USER CODE END memcpy_2 */ return dest; /* USER CODE BEGIN memcpy_End */ /* USER CODE END memcpy_End */ } int32_t spi_port_init(spi_transaction_complete_t transaction_complete_cb) { /* USER CODE BEGIN spi_port_init_1 */ /* Le driver traite CS comme actif a l'etat haut (spi_port_set_cs(1) = niveau haut). Au repos il * doit donc etre bas, avant meme d'alimenter le module (CubeMX le laisse haut par defaut). */ HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET); /* CubeMX configure SCK/MOSI du SPI4 en vitesse LOW (front lent) alors que le bus tourne a * ~19 Mbit/s : les fronts deformes corrompent l'en-tete (magic 0x5501 au lieu de 0x55AA). * Vitesse tres haute pour PE12 (SCK), PE13 (MISO) et PE14 (MOSI). */ GPIO_InitTypeDef spi_gpio = {0}; spi_gpio.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14; spi_gpio.Mode = GPIO_MODE_AF_PP; spi_gpio.Pull = GPIO_NOPULL; spi_gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; spi_gpio.Alternate = GPIO_AF5_SPI4; HAL_GPIO_Init(GPIOE, &spi_gpio); /* CubeMX genere les DMA de SPI4 avec adresse memoire FIXE (RX: DestInc, TX: SrcInc) : tous les * octets recus sont ecrits au meme endroit. A cocher dans CubeMX (SPI4 > DMA Settings > * "Increment Address" cote memoire) ; en attendant on corrige ici. */ extern DMA_HandleTypeDef handle_GPDMA1_Channel4, handle_GPDMA1_Channel5; handle_GPDMA1_Channel4.Init.DestInc = DMA_DINC_INCREMENTED; /* SPI4_RX */ handle_GPDMA1_Channel5.Init.SrcInc = DMA_SINC_INCREMENTED; /* SPI4_TX */ if ((HAL_DMA_Init(&handle_GPDMA1_Channel4) != HAL_OK) || (HAL_DMA_Init(&handle_GPDMA1_Channel5) != HAL_OK)) { Error_Handler(); } /* Le bus est instable a ~19 Mbit/s (en-tetes decales d'un bit, donnees nulles) : on ralentit. * Reglage equivalent dans CubeMX : SPI4 > Baud Rate Prescaler (regenere ensuite sans effet ici). */ __HAL_SPI_DISABLE(&NCP_SPI_HANDLE); MODIFY_REG(NCP_SPI_HANDLE.Instance->CFG1, SPI_CFG1_MBR, SPI_BAUDRATEPRESCALER_16); /* USER CODE END spi_port_init_1 */ if (NCP_SPI_HANDLE.State == HAL_SPI_STATE_RESET) { LogError(spi_port_error_str); return -1; } /* Powering up the NCP using GPIO CHIP_EN */ HAL_GPIO_WritePin(CHIP_EN_GPIO_Port, CHIP_EN_Pin, GPIO_PIN_SET); if (transaction_complete_cb != NULL) { spi_port_transaction_complete_cb = transaction_complete_cb; } /* USER CODE BEGIN spi_port_init_2 */ /* USER CODE END spi_port_init_2 */ return 0; /* USER CODE BEGIN spi_port_init_End */ /* USER CODE END spi_port_init_End */ } int32_t spi_port_deinit(void) { /* USER CODE BEGIN spi_port_deinit_1 */ /* USER CODE END spi_port_deinit_1 */ /* Switch off the NCP using GPIO CHIP_EN */ HAL_GPIO_WritePin(CHIP_EN_GPIO_Port, CHIP_EN_Pin, GPIO_PIN_RESET); spi_port_transaction_complete_cb = NULL; /* USER CODE BEGIN spi_port_deinit_2 */ /* USER CODE END spi_port_deinit_2 */ return 0; /* USER CODE BEGIN spi_port_deinit_End */ /* USER CODE END spi_port_deinit_End */ } int32_t spi_port_transfer(void *tx_buf, void *rx_buf, uint16_t len, uint32_t timeout) { /* USER CODE BEGIN spi_port_transfer_1 */ /* USER CODE END spi_port_transfer_1 */ HAL_StatusTypeDef status; if (NCP_SPI_HANDLE.State == HAL_SPI_STATE_RESET) { LogError(spi_port_error_str); return -1; } #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) SCB_CleanInvalidateDCache_by_Addr(rx_buf, len); #endif /* __DCACHE_PRESENT */ /* Check whether host data is to be transmitted to the NCP, otherwise read only data from the NCP */ if (tx_buf != NULL) { #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) SCB_CleanDCache_by_Addr(tx_buf, len); #endif /* __DCACHE_PRESENT */ status = HAL_SPI_TransmitReceive(&NCP_SPI_HANDLE, tx_buf, rx_buf, len, timeout); } else { assert_param(len <= SPI_DMA_XFER_SIZE_THRESHOLD); uint8_t tx_dummy[SPI_DMA_XFER_SIZE_THRESHOLD] = {0}; status = HAL_SPI_TransmitReceive(&NCP_SPI_HANDLE, tx_dummy, rx_buf, len, timeout); } /* USER CODE BEGIN spi_port_transfer_2 */ /* USER CODE END spi_port_transfer_2 */ return ((status == HAL_OK) ? 0 : -1); /* USER CODE BEGIN spi_port_transfer_End */ /* USER CODE END spi_port_transfer_End */ } int32_t spi_port_transfer_dma(void *tx_buf, void *rx_buf, uint16_t len) { /* USER CODE BEGIN spi_port_transfer_dma_1 */ /* USER CODE END spi_port_transfer_dma_1 */ HAL_StatusTypeDef status; if (NCP_SPI_HANDLE.State == HAL_SPI_STATE_RESET) { LogError(spi_port_error_str); return -1; } #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) SCB_CleanInvalidateDCache_by_Addr(rx_buf, len); #endif /* __DCACHE_PRESENT */ /* Check whether host data is to be transmitted to the NCP, otherwise read only data from the NCP */ if (tx_buf != NULL) { #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) SCB_CleanDCache_by_Addr(tx_buf, len); #endif /* __DCACHE_PRESENT */ /* USER CODE BEGIN spi_port_transfer_dma_trx */ if (len > DMA_BOUNCE_SIZE) { return -1; } (void)memcpy(dma_tx_bounce, tx_buf, len); dma_rx_dest = rx_buf; dma_rx_len = len; status = HAL_SPI_TransmitReceive_DMA(&NCP_SPI_HANDLE, dma_tx_bounce, dma_rx_bounce, len); /* USER CODE END spi_port_transfer_dma_trx */ } else { /* USER CODE BEGIN spi_port_transfer_dma_rx */ if (len > DMA_BOUNCE_SIZE) { return -1; } dma_rx_dest = rx_buf; dma_rx_len = len; status = HAL_SPI_Receive_DMA(&NCP_SPI_HANDLE, dma_rx_bounce, len); /* USER CODE END spi_port_transfer_dma_rx */ } /* USER CODE BEGIN spi_port_transfer_dma_2 */ /* USER CODE END spi_port_transfer_dma_2 */ return ((status == HAL_OK) ? 0 : -1); /* USER CODE BEGIN spi_port_transfer_dma_End */ /* USER CODE END spi_port_transfer_dma_End */ } int32_t spi_port_is_ready(void) { /* USER CODE BEGIN spi_port_is_ready_1 */ /* USER CODE END spi_port_is_ready_1 */ /* Check whether NCP data are available on the SPI bus */ return (int32_t)HAL_GPIO_ReadPin(SPI_RDY_GPIO_Port, SPI_RDY_Pin); /* USER CODE BEGIN spi_port_is_ready_End */ /* USER CODE END spi_port_is_ready_End */ } int32_t spi_port_set_cs(int32_t state) { static int32_t last_falling_tick; /* USER CODE BEGIN spi_port_set_cs_1 */ /* USER CODE END spi_port_set_cs_1 */ if (state == 1) { WAIT_FROM(last_falling_tick, MICROSECOND_TO_TICK(2)); /* Activate Chip Select before starting transfer */ HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_SET); } else { /* Disable Chip Select when transfer is complete */ HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET); last_falling_tick = SYSTICK_VALUE; } /* USER CODE BEGIN spi_port_set_cs_2 */ /* USER CODE END spi_port_set_cs_2 */ return 0; /* USER CODE BEGIN spi_port_set_cs_End */ /* USER CODE END spi_port_set_cs_End */ } /* USER CODE BEGIN FD */ /* Recopie la reception DMA du tampon de rebond vers le tampon du driver, avant de signaler la fin. */ static void spi_port_dma_rx_copyback(SPI_HandleTypeDef *hspi) { if (hspi == &NCP_SPI_HANDLE && dma_rx_dest != NULL) { (void)memcpy(dma_rx_dest, dma_rx_bounce, dma_rx_len); dma_rx_dest = NULL; } } /* USER CODE END FD */ /* Weak functions redefinition -----------------------------------------------*/ void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { /* USER CODE BEGIN HAL_SPI_TxCpltCallback_1 */ /* USER CODE END HAL_SPI_TxCpltCallback_1 */ if (hspi == &NCP_SPI_HANDLE) { spi_port_transaction_complete_cb(); } /* USER CODE BEGIN HAL_SPI_TxCpltCallback_End */ /* USER CODE END HAL_SPI_TxCpltCallback_End */ } void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { /* USER CODE BEGIN HAL_SPI_RxCpltCallback_1 */ spi_port_dma_rx_copyback(hspi); /* USER CODE END HAL_SPI_RxCpltCallback_1 */ if (hspi == &NCP_SPI_HANDLE) { spi_port_transaction_complete_cb(); } /* USER CODE BEGIN HAL_SPI_RxCpltCallback_End */ /* USER CODE END HAL_SPI_RxCpltCallback_End */ } void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { /* USER CODE BEGIN HAL_SPI_TxRxCpltCallback_1 */ spi_port_dma_rx_copyback(hspi); /* USER CODE END HAL_SPI_TxRxCpltCallback_1 */ if (hspi == &NCP_SPI_HANDLE) { spi_port_transaction_complete_cb(); } /* USER CODE BEGIN HAL_SPI_TxRxCpltCallback_End */ /* USER CODE END HAL_SPI_TxRxCpltCallback_End */ } void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) { /* USER CODE BEGIN HAL_SPI_ErrorCallback_1 */ /* USER CODE END HAL_SPI_ErrorCallback_1 */ if (hspi == &NCP_SPI_HANDLE) { Error_Handler(); } /* USER CODE BEGIN HAL_SPI_ErrorCallback_End */ /* USER CODE END HAL_SPI_ErrorCallback_End */ } /* USER CODE BEGIN WFR */ /* USER CODE END WFR */