2025-04-24 2:24 AM
Good morning everyone,
I'm currently trying to implement a SPI slave in full duplex mode on a STM32F469IDISCOVERY in order to interface with an external Master that is working at 10Mbit/s (the master cannot be modified). What I need to do is a full duplex communication by receiving a packet of 32 bits and at the same time responding with 32 bits that correspond the answer to the previous packet.
Those transaction happens in a "burst" of 6 transaction each every 25us , then some idle time in the order of ms and the transactions restart. During each transaction, 3 messages are sent by the master (0xA3000007,0xB3000006 and 0x93000004).
My idea was to use SPI in full duplex with Interrupt mode by relying on the HAL function HAL_SPI_TransmitReceive_IT(), since later I will be needing to add some elaboration in the meantime and I cannot keep the microcontroller halted for communications. I first started testing only the reception and everything works fine. Once I started trasmitting, I encountered some issue. My intent was to reply everytime to the master with a known sequence 0x12345678, but the result was shifted of 11 bits to the right in circular way as it can be shown in the oscilloscope trace below. If I'm using TransmitReceive the same thing is sent, but I'm not more able to read the result.HAL_SPI_Transmit_IT() waveforms
I tried the same with the DMA mode, for which I used the same communication settings and I was successfully able to receive the data. Again, when coming to transmission I encountered the same problem, but only the 0x5678 sequence is trasmitted and circularly left shifted by 5 bits, resulting in the sequence 0xCF0A repetead two times, as shown below for HAL_SPI_TransmitReceive_DMA(). In that case also the reading are shifted, and in particular I'm reading the end of the packet before (0xA3000007) with the start of the subsequent packet(0xB3000006) combined in 0x0007b300, which then is shifted to the left.HAL_SPI_TransmitReceive_DMA() oscilloscope trace
I'm trying to use SPI in 16bit mode with SW NSS, and I setted DMA in normal mode by relying on Half Words. The code that I used was generated by the .ioc and it's the following
#define BUFFER_SIZE 2
SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi2_tx;
TIM_HandleTypeDef htim2;
//Communication buffers
uint16_t TxBuffer[BUFFER_SIZE] = {0x1234,0x5678};
uint16_t RxBuffer[BUFFER_SIZE] = {0x0000};
//Variable for control
uint32_t singleResult = 0;
uint32_t spi_counter = 0;
HAL_StatusTypeDef txrxStatus = HAL_OK;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_SPI2_Init(void);
static void MX_TIM2_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI2_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start(&htim2);
if (HAL_SPI_TransmitReceive_DMA(&hspi2, TxBuffer, RxBuffer, BUFFER_SIZE) != HAL_OK)
{
Error_Handler(); // Handle error
}
while (1)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); // DEBUG
HAL_Delay(100);
}
}
static void MX_SPI2_Init(void)
{
/* SPI2 parameter configuration*/
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_SLAVE;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
/* DMA1_Stream4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == SPI2)
{
spi_counter++; // Update the counter for the next data pattern
delay_us(20); //20 us delay implemented customly
// Indicate transfer completion
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); // DEBUG
//Elaborate the result
singleResult = ((uint32_t)RxBuffer[0] << 16) | (uint32_t)RxBuffer[1];
txrxStatus = HAL_SPI_TransmitReceive_DMA(&hspi2, TxBuffer, RxBuffer, BUFFER_SIZE);
// Start the next DMA transfer with the updated buffer
if (txrxStatus != HAL_OK)
{
Error_Handler(); // Handle error
}
}
}
void delay_us(uint32_t us) {
uint32_t start = TIM2->CNT; // Capture the starting count
// Wait until the desired number of microseconds has elapsed
while ((TIM2->CNT - start) < us);
}
Also the handlers of the DMA IRQ are included
void DMA1_Stream3_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream3_IRQn 0 */
/* USER CODE END DMA1_Stream3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_rx);
/* USER CODE BEGIN DMA1_Stream3_IRQn 1 */
/* USER CODE END DMA1_Stream3_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream4 global interrupt.
*/
void DMA1_Stream4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream4_IRQn 0 */
/* USER CODE END DMA1_Stream4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_tx);
/* USER CODE BEGIN DMA1_Stream4_IRQn 1 */
/* USER CODE END DMA1_Stream4_IRQn 1 */
}
If someone can help me in what I'm missing or misconfiguring I will be so grateful, thank you so much in advance. If needing more information I will keep you posted!
P.S. I considered also implementing hardware NSS but in this way the SPI controller was dumping out the bits before I was actually able to reload it, however even if in my code I'm not checking for the NSS since the master is only sending the clock when it want the slave to transmit, I need to insert this in a multi slave environment, so if you have also considerations on the most efficient way to implement it let me know