cancel
Showing results for 
Search instead for 
Did you mean: 

how to let SPI_fullDuplex_DMA_Slave runs continously using the HAL example

Ping1
Associate II

Dear all

I am trying to follow the exmaple https://github.com/STMicroelectronics/STM32CubeH5/tree/e6985112c824fce355638ef4e7113b1bc671f999/Projects/NUCLEO-H503RB/Examples/SPI/SPI_FullDuplex_ComDMA_Slave project with STM32H563 on my own target board, I copied most of the code/DMA setup form the example and got it working, however, my requirement is more complicated than the example, the example only receives and reply 1 message after a button press, but my application needs it to be able to receive/reply messages periodically, i.e. master keep sending fixed length of message and expect slave to reply with same length of message at a fixed period. For now, after I copied example, it runs receives/replies 1 message, which I see the data on bus , but then it ends up with busy state and never recovers even wait for a long time, could anyone help please? Also, the about the DMA setting, does it needs to be in circular mode? I am sort of new to STM32 and DMA. Any suggestion is appreciated.

Ping

5 REPLIES 5
TDK
Super User

If you want a packet sent continuously, yes DMA will need to be in circular mode.

The slave will need to be ready to send before the master clocks out data.

 

If data is being sent, it is expected that the peripheral will be in busy state.

If you are having trouble, show your code (both master and slave).

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

Thanks for reply TDK!

First packet receives and send successfully, when re-arm for next receive by calling HAL_SPI_TransmitReceive_DMA(), it returns BUSYor ERROR although interrupt is already fired in HAL_SPI_TxRxCpltCallback(). also it ends up in HAL_SPI_ErrorCallback(), which often has UDR error combine with others maybe. I am not sure my error handling is correct as shown below:

void Start_SPI_DMA(void)
{
	//check error before re-arm?
	if(hspi2.ErrorCode != HAL_SPI_ERROR_NONE) {
		errorState = hspi2.ErrorCode;
		if(hspi2.ErrorCode & HAL_SPI_ERROR_OVR ) {
			__HAL_SPI_CLEAR_OVRFLAG(&hspi2);
		}
		if(hspi2.ErrorCode & HAL_SPI_ERROR_UDR) {
			__HAL_SPI_CLEAR_UDRFLAG(&hspi2);
		}
		if(hspi2.ErrorCode & HAL_SPI_ERROR_ABORT) {
			//__HAL_SPI_CLEAR_UDRFLAG(&hspi2);
		}
		if(hspi2.ErrorCode & HAL_SPI_ERROR_FRE) {
			__HAL_SPI_CLEAR_FREFLAG(&hspi2);
		}
	}
	hspi2.State = HAL_SPI_STATE_READY;

	HAL_StatusTypeDef status = HAL_SPI_TransmitReceive_DMA(&hspi2,
				&(spi_tx_manager.spi_tx[txBuff].aTxBuffer[0]), &aRxBuffer[0], sizeof(aRxBuffer));
	if(status != HAL_OK)//send a message to the thread.
	{
		errorState = status;
	}
}
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
 {
	 if(hspi->Instance == SPI2) { 
//	    //re-arm with
	Start_SPI_DMA();
	 }
 }
//Callback
 void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi->Instance == SPI2)
	{
		tx_event_flags_set(&R_Pi_SPI_Data_EventFlag, DMX_RX_DONE, TX_OR);
		SPIRxTxCnt++;
	}
	else if(hspi->Instance == SPI4)
	{

	}
}
//In process thread
void tx_spi_proc_thread_entry(ULONG thread_input)
{
  /* USER CODE BEGIN tx_app_thread_entry */
	ULONG	actual_events;
	bool resp_timer_running = false;
	while(TX_LOOP_FOREVER)
	{
		UINT STATUS = tx_event_flags_get(&R_Pi_SPI_Data_EventFlag, DMX_RX_DONE,
				TX_OR_CLEAR, &actual_events, TX_WAIT_FOREVER);
		if( STATUS== TX_SUCCESS)
		{
			uint16_t crc = calculate_crc16(&aRxBuffer[0], (uint16_t)sizeof(aRxBuffer) - 2);
			//comingCrc = *(uint16_t*)&aRxBuffer[518];
			if(crc == *(uint16_t*)&aRxBuffer[518])
			{
				switch(aRxBuffer[0])
				{
					case CMD_DMX_INTENSITY:
					{
						//mutex ensures that there is no data access whiles copying  data from the receive buffer.
							//data received successfully, therefore process it.
						 memcpy(&IntensityData, aRxBuffer, sizeof(DMXPkt));  //endianess need swap at source
						 tx_event_flags_set(&R_Pi_SPI_Data_EventFlag, NEW_DMX_DATA, TX_OR);
						  //here process the data
						 if(!resp_timer_running)
						 {
							 run_resp_timer();
						 }
						break;
					}
					case CMD_SETTINGS:
					{
						memcpy(&ConfigData, aRxBuffer, sizeof(ConfigPkt));
						 //set a flag to warn of the presence of unprocessed data?
						tx_event_flags_set(&R_Pi_SPI_Data_EventFlag, NEW_CONFIG_DATA, TX_OR);
						break;
					}
					case CMD_BOOTLOADER:
					{
						EnterBL();
						//Unreachable code!
						//uint32_t SectorError;
						bool isEdataReady = false;
						HAL_StatusTypeDef erase_result = CheckSetEraseEDATA(true, &isEdataReady);
						if(erase_result == HAL_OK)
						{
							/*BootCommand_Type cmd;
							cmd.cmd_data = COMMAND_DATA;
							cmd.command = CMD_BOOTLOADER;
							cmd.crc = calculate_crc16((uint8_t*)&cmd, sizeof(BootCommand_Type) - 4);
							uint32_t dataStartAddress = (uint32_t )(&cmd);*/
							/*WriteBootCommand((uint32_t)dataStartAddress, 8);
							BootCommand_Type cmdchk;
							ReadBootCommand(&cmdchk);
							uint16_t crc = calculate_crc16((uint8_t*)&cmdchk, sizeof(BootCommand_Type) - 4);*/

						}
						break;
					}
				}
			}
		}
		if(tx_mutex_get(spi_tx_manager.spi_tx[txBuff].spi_mutex, TX_SPI_BUF_MUTEX_WAIT) == TX_SUCCESS) //won't come here if event flag is not set
		{

//			if(hspi2.ErrorCode != HAL_SPI_ERROR_NONE) {//check error first before sending
//				errorBeforeTx = hspi2.ErrorCode;
//			}
		    while(HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
		    {}
			HAL_StatusTypeDef status = HAL_SPI_TransmitReceive_DMA(&hspi2,
					&(spi_tx_manager.spi_tx[txBuff].aTxBuffer[0]), &aRxBuffer[0], sizeof(aRxBuffer));  //TX and Rx data with Pi
			//HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA(&hspi2, &(spi_tx_manager.spi_tx[txBuff].aTxBuffer[0]), sizeof(aRxBuffer));
			if(status != HAL_OK)
			{
					//retry?
					switch(hspi2.ErrorCode)  //bug here - when more than 1 error is there
					{
						case  HAL_SPI_ERROR_MODF:
						{
							__HAL_SPI_CLEAR_MODFFLAG(&hspi2);
							break;
						}
						case  HAL_SPI_ERROR_CRC:
						{
							__HAL_SPI_CLEAR_CRCERRFLAG(&hspi2);
							break;
						}
						case  HAL_SPI_ERROR_OVR:
						{
							__HAL_SPI_CLEAR_OVRFLAG(&hspi2);
							break;
						}
						case  HAL_SPI_ERROR_UDR:
						{
							__HAL_SPI_CLEAR_UDRFLAG(&hspi2);
							break;
						}
						case  HAL_SPI_ERROR_FRE:
						{
							__HAL_SPI_CLEAR_FREFLAG(&hspi2);
							break;
						}
						case  HAL_SPI_ERROR_DMA:
						{
//							__HAL_DMA_CLEAR_FLAG(hspi2.hdmarx, DMA_FLAG_TE);   // Transfer error
//							__HAL_DMA_CLEAR_FLAG(hspi2.hdmatx, DMA_FLAG_TE);
							break;
						}
						case  HAL_SPI_ERROR_FLAG:
						{
							break;
						}
						case  HAL_SPI_ERROR_ABORT:
						{
//							__HAL_DMA_CLEAR_FLAG(hspi2.hdmarx, DMA_FLAG_ABORT);
//							__HAL_DMA_CLEAR_FLAG(hspi2.hdmatx, DMA_FLAG_ABORT);
							break;
						}

					}
				}
				hspi2.State = HAL_SPI_STATE_READY;
				tx_mutex_put((spi_tx_manager.spi_tx[txBuff].spi_mutex));  //release the mutex here - for access aTxBuffer
		}
		// Copy data from aRxBuffer to IntensityData
		tx_thread_sleep(10);
	}
  /* USER CODE END tx_app_thread_entry */
}

 Sorry, I shared some of the main code snips here. Master is done in another MCU from Ti Tiva. And it is sends fine as can be seen from bus on logical analyser.

Sorry to bother you with all of these details. But i am really stuck, also enabled circular buffer.

Regards!

Ping  

With circular DMA, the transmission never stops so you will be unable to call it more than once. It sounds like that's not what you want, but you want to send a packet on request, exactly like the example. So DMA should be set to normal, not circular.

If that's the case, I'm not sure what about the example isn't working for you. Instead of sending a packet when a button is pressed, send it when your program requires it (periodically, based on a trigger or whatever else is the requirement). You will need to wait for the previous transmission is complete. Replace the (if button) check with (if xxx) based on your trigger.

> it ends up with busy state and never recovers

Are global SPI and DMA interrupts enabled?

 

The only place your posted code starts a transmission is in Start_SPI_DMA which is only called from within HAL_SPI_ErrorCallback. That makes no sense.

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

Hi, RDK

Thanks for looking at it.

It was not circular buffer before, and result seems no different.

SPI2 global interrupt and GPDMA1 DMA Channel interrupt are all enabled, instead of Channel 6&7 as DMA channel, I am using Channel 0 & 1 in my application as Channel 6 is used already in my application, hope this is OK.

I first call HAL_SPI_TransmitReceive_DMA at App_Thread_Init(), which is not posted above, sorry, forgot to show that.

In Start_SPI_DMA(), it is called again after error recover, so re-arm with next transmission in case it ends up with error. Probably could take it out, I am not sure. It is normally called in the thread (line 119) after processing the packet and prepare for another transmission. 

The example only runs once, and make me wonder whether it is able to run periodically if it is triggered.

Regards!

Ping

 

Ping1
Associate II

Dear all

AI said the example runs for H503 MCU only, not for H563, they sue different DMA engine, which i am not sure. That probably explains why the example doesn't exist for H563? And also, why not use normal DMA mode? will a normal DMA mode work for a typical SPI full duplex with DMA? a simple working example is more helpful than a show case one which only runs once. Some feedback to STM probably.

I am still struggling here...

Regards!

Ping