446 lines
14 KiB
C
446 lines
14 KiB
C
/* 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 <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#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 */
|