cancel
Showing results for 
Search instead for 
Did you mean: 

SPI full duplex slave does not send data

sde c.1
Senior II

Hi all,

I have a setup with Pi and STM board. The Pi acts like an SPI master, the STM is setup as SPI full duplex slave.

The SPI input and outputbuffer are the same size, what i want is that when i receive x bytes from the pi, i send back the first x bytes of the SPI output buffer back .

I can receive data from the Pi with no issue's, but when i put data into the DMA SPI outputbuffer , this data is not send by the slave(STM) the next time the Pi initiate an SPI communication.

I use HAL library and setup the STM like this :

in main(void) ->

uint8_t cnt=0;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
  spi_buf_out[cnt]=cnt++;
 
HAL_SPI_TransmitReceive_DMA(&hspi2,spi_buf_out,spi_buf_in,SPI_SIZE);

At the logic analyser i see correct data at MOSI and MISO lines when the pi sends the first SPI package.

But at the next SPI packages from the master (Pi), there is no data anymore at MISO (spi_buf_out), this line stays 0.

How do i instruct the HAL library to send back the contents of spi_buf_out every time the Pi starts a SPI communication?

Thank you

Steve

1 ACCEPTED SOLUTION

Accepted Solutions

it finally works, your tip lead to the solution. Thank you . I summarize the solution for other people .

The HAL lib is not ideal for this setup, i would recommend other engineers to write their own code to acces the registers , getting this right took at least as much time then starting from 0.

the setup :

The Pi is SPI master , the STM SPI slave , each SPI package is forwarded to the UART, some external device answers. This answer is given back the next SPI packet initiated by the Pi.

0693W00000DnVB5QAN.pngDe code :

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SPI2_Init();
  
  HAL_SPI_TransmitReceive_DMA(&hspi2,spi_buf_out,spi_buf_in,SPI_SIZE);
}
 
// 1) SPI enable line will have positive flank once the data is transferred from PI to STM, this will be used as interrupt to
// copy the received SPI data into the uart output buffer and initiate a DMA transfer to the uart peripheral
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
	if(GPIO_Pin == GPIO_PIN_12){
                WritePin(PA10,1); //DE
		memcpy(uart_buf_out,spi_buf_in,SPI_SIZE);
		// reset uart to flush FIFO
		HAL_UART_DeInit(&huart1);
		__HAL_RCC_USART1_FORCE_RESET();
		__HAL_RCC_USART1_RELEASE_RESET();
		HAL_UART_Init(&huart1);
 
		HAL_UART_Transmit_DMA(&huart1,(uint8_t *)(uart_buf_out+1),*(uint8_t *)(uart_buf_out));
	}
}
 
// 2) after the DMA transfer at the UART peripheral, this callback will be called
// we use this to clear the RS485 DE line and start the DMA to start receiving data to the uart peripheral
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
	if(huart == &huart1){
		WritePin(PA10,0); //DE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart1,(uint8_t*)uart_buf_in,UART_SIZE);
		__HAL_DMA_DISABLE_IT(&hdma_usart1_rx,DMA_IT_HT);
	}
}
 
// 3) once data is received this callback is called
// we use this to copy the data in the uart inbuffer to the spi outbuffer, so the data will be available at the SPI bus at the next POLL
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
	if(huart == &huart1){
		// process received data
		// copy data to SPI outbuffer for next SPI poll
		memcpy(spi_buf_out,uart_buf_in,UART_SIZE);
 
		// reset spi to flush FIFO
		HAL_SPI_DeInit(&hspi2);
		__HAL_RCC_SPI2_FORCE_RESET();
		__HAL_RCC_SPI2_RELEASE_RESET();
		HAL_SPI_Init(&hspi2);
 
		HAL_SPI_TransmitReceive_DMA(&hspi2,spi_buf_out,spi_buf_in,SPI_SIZE);
	}
}
 

View solution in original post

15 REPLIES 15
TDK
Guru

You need to call HAL_SPI_TransmitReceive_DMA for every transaction. Or set it up in circular mode.

If you feel a post has answered your question, please click "Accept as Solution".
S.Ma
Principal

It did answer while receiving, transmit and receive simultaneously....

call HAL_SPI_TransmitReceive_DMA for every transaction -> does not work

circular mode. -> then the output shifts all the time,

Every time the pointer of the SPI outputbuffer is shifted with the number of bytes received by the PI. So for the first communication the pi send 5 bytes and receives the first 5 bytes from SPI outputbuffer, the next communication the pi sends 5 bytes and receives byte 5 till 9 from SPI outputbuffer.

How do i instruct DMA to start every time from byte 0 ?

yes

Which STM32?

> How do i instruct DMA to start every time from byte 0 ?

By setting the Tx DMA transfer size to 5.

HAL_SPI_TransmitReceive_DMA() does not support separate Tx and Rx size, that's the limitation of Cube/HAL. You'd need to accept it (e.g. copy the 5 bytes of data manually to your positions in buffer); work around it somehow; or not use it and write register-access based programs.

JW

I cant find any DMA transfer size members in the TxDMA struct.

i did find a work arround but its still not working 100%.

// reset the SPI tx DMA position to index 0
		__HAL_DMA_DISABLE(&hdma_spi2_tx);
		hdma_spi2_tx.Instance->CNDTR = SPI_SIZE;
		__HAL_DMA_ENABLE(&hdma_spi2_tx);

When i update the CNDTR register , then the bytes in the SPI output buffer sends with every SPI communication, but the issue i have is a problem with index.

In the SPI answer is see the first 3 bytes are from somewhere at the middle of the hdma_spi2_tx array , the 4 bytes is hdma_spi2_tx[0], and from there on it works and follow the hdma_spi2_tx index correctly.

There is no shifing anymore, but the first 3 returned bytes are still wrong.

So how do i make sure the first byte starts at hdma_spi2_tx[0] ?

Thank you

TDK
Guru

SPI sends as many bytes as it receives, whether you want it to or not. There is one clock for the two data signals. Send 9 bytes at a time instead of 5 if you're receiving 9.

If you feel a post has answered your question, please click "Accept as Solution".

You did not tell us which STM32 are you using.

When you set up and enable DMA for Tx for the first time - presumably calling HAL_SPI_TransmitReceive_DMA() - the SPI Tx triggers the DMA as it has empty buffer/FIFO, and DMA fills up that buffer/FIFO (depending on the STM32 model and the SPI settings). So, if you afterwards disable DMA, the first frames are already in the SPI and are transmitted when master starts clocking. If master is clocking while DMA is disabled in your "workaround", and the SPI runs out of transmittable characters, it keeps repeating the last one - that's what you are probably observing.

You have also probably incorrectly synchronized master and slave. Toggle a GPIO output when you enable the SPI/DMA in STM32 and observe it together with the SPI signals.

JW

S.Ma
Principal

First prepare a TX and RX slave buffer. As slave doesn't control the SCK, set the buffer in cyclic mode, no spi data transfer interrupt. Now, to synchronise the start stop of messages, use NSS as EXTI interrupt, say rising edge for the slave to process incoming.message and prepare next to go outgoing message.

If your SPI has fifo, reset (not disable) the SPI and reconfigure it to flush the clogged fifos. Once you reach this point, you are out of the woods....