STM32F4 SPI interrupt and task communication

anonymous wrote on Tuesday, April 16, 2013:

Hi,

I am working on a project with STM32F4 and FreeRTOS. I have a SPI ISR for receiving data from SPI in the speed of about 5Mbps, I also have a task which handles those data. The Queue is used to be between the interrupt and task.

xQueue=xQueueCreate(10,sizeof(uint8_t)*BufferSize);

The problem is that the task stops working after some time of normal running. But the SPI receive seems to be still continuing because I can still see the waveforms in the oscillopscope. The implementation of SPI receive ISR is as follows:

void SPI1_IRQHandler(void) 
{ 	
        static portBASE_TYPE xHigherPriorityTaskWoken;
	portBASE_TYPE xStatus;
	xHigherPriorityTaskWoken = pdFALSE; 
	
	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
	 if(index_spi<512)                
        {        
			lValueSend[index_spi]=SPI_I2S_ReceiveData(SPI1);
                        index_spi++; 
			SPI_I2S_SendData(SPI1,0x22);
	}
	else{
			index_spi=0;
			xStatus=xQueueSendToBackFromISR(xQueue,&lValueSend,&xHigherPriorityTaskWoken);	
			portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
               }
}

And inside the task infinite loop:

xStatus=xQueueReceive(xQueue,&lValueReceive,xTicksToWait);
					
					if(xStatus==pdPASS){
							dataProcess(lValueReceive);
				
					}

After some debugging, I found out that the queue becomes full very easily, and the context switch doesn’t happen. So it seems that my task just never gets a chance of running again. But if I understand correctly, after the ISR, either we switch the context to the task waiting for queue  if this task waiting for the queue has a higher priority than the current one which was interrupted, either we return to the task which was interrupted. My Datahandling task is already set to configMAX_PRIORITIES, so even if we return to the privious task, my task should have been given control to run.  Someone can help to analyse and understand?

And also the interrupt priority thing makes me confused. My SPI IRQ settings is:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
  NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn; 
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 

In the FreeRTOS document, there are two which are absolutely not my settings at all. One is http://www.freertos.org/RTOS-Cortex-M3-M4.html, 
“If you are using an STM32 with the STM32 driver library then ensure all the priority bits are assigned to be preempt priority bits by calling NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); before the RTOS is started.”

Another one is that the interrupt with priority between configMAX_SYSCALL_INTERRUPT_PRIRITY and configMAX_SYSCALL_INTERRUPT_PRIORITY can use the freeRTOS API functions. My settings are

#define configKERNEL_INTERRUPT_PRIORITY       255
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */

With the mapping of STM32, the priority between 11 and 15 can use FreeRTOS API. But my settings are completely out of these two rules, but after test, this is the best config. If I put the NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ) and set the PreemptionPriority to 15. The task stops even more quickly. Someone may also help to understand with this?

rtel wrote on Tuesday, April 16, 2013:

First, you have in part answered your own question when (paraphrasing) you say “the documentation says not to do this because it won’t work correctly, but I have done it anyway”.  So the first piece of advise is to configure your system in accordance with the documentation.

That done, you should not find that a queue becoming full would in any way prevent a task from executing, you would just miss characters that could not be placed into the queue as they were received.

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

I’m not sure what this line of code is doing, but if it is polling a bit in a register then it is not the sort of code I would necessarily expect to see in an interrupt handler.  Generally the interrupt handler should only be entered when a character is actually available, otherwise *all* your tasks are going to be prevented from executing while the interrupt handler is polling.

On the other hand if the line of code is somehow resetting a bit in a register and just reading it back to ensure it is reset before continuing then it should be ok.

Similar comments for calling functions that send data from inside the receive handler.

As more general comments - normally when attempting to received data at that rate it is not a good idea to use an RTOS queue to send the data to a task.  Instead it is better to either use a RAM circular buffer, or ideally a DMA.  The DMA would receive the data and the DMA end Rx interrupt would be used to unblock a task only when there was a break in the reception.

Regards.

anonymous wrote on Tuesday, April 16, 2013:

Thanks for your reply, richardbarry. I’ve already tried to use the recommanded configuration, like I said with NVIC_group4 and Preemption priority 15(lowest),  the task stops anyway.  And it’s like stopping immediately as I start to send something from SPI. With my current wrong setting, it can still run for several seconds and then stops.

Then I tried to slow down my SPI to about 1Mbps, it works very stable with my wrong settings, the task didn’t stop at all, but with the config which is supposed to be the right one, the task stops at some point in the same test.  So that’s why it’s so strange.

You are right about the flag staff,  I just comment it now but it doesn’t seem to change anything. I’ve done another test which just doesn’t process the data anymore and only keep the queue receive there.

xStatus=xQueueReceive(xQueue,&lValueReceive,xTicksToWait);
					
if(xStatus==pdPASS){
//dataProcess(lValueReceive);
				
}

Then my task still stops, so it seems that it’s not my processing which is slow but the queue just doesn’t catch up.

anonymous wrote on Tuesday, April 16, 2013:

And also I found something similar in the STM32 examples which seemed to be wrong. In the STM32F4  FreeRTOS  httpserver_netconn example, the Ethernet interrupt is used with FreeRTOS API functions.

void ETH_IRQHandler(void)
{
  portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  /* Frame received */
  if ( ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R) == SET) {
    /* Give the semaphore to wakeup LwIP task */
    xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );   
  }
	
  /* Clear the interrupt flags. */
  /* Clear the Eth DMA Rx IT pending bits */
  ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
  ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
	
  /* Switch tasks if necessary. */	
  if ( xHigherPriorityTaskWoken != pdFALSE ) {
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
  }
}

However, its setting is also not like the recommandations:

void ETH_NVIC_Config(void)
{
  NVIC_InitTypeDef   NVIC_InitStructure;
  /* 2 bit for pre-emption priority, 2 bits for subpriority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
  
  /* Enable the Ethernet global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);    
}

This is becoming more and more confusing, can anyone give a clear explanation?

rtel wrote on Tuesday, April 16, 2013:

The ETH_NVIC_Config() function is also wrong, but I can’t find it in the FreeRTOS download.  Which file is it in?

Regards.

anonymous wrote on Tuesday, April 16, 2013:

It’s in the STM32F4DIS-BB examples. You can find a download here: http://www.element14.com/community/docs/DOC-51670?ICID=knode-STM32F4-space
The httpserver_netconn project  in /STM32F4xx_Ethernet_Example/project/FreeRTOS/httpserver_netconn, and then this ETH_NVIC_Config() function is defined in the stm32f4x7_eth_bsp.c, the ETH_IRQHandler is in the stm32f4xx_it.c.