cancel
Showing results for 
Search instead for 
Did you mean: 

LL Low Layer SPI interrupt

DAlvarez
Associate II

Hi,

I´m trying to transmit (send only) 3 bytes with the SPI using interrupts, and deassert the chip select line from an interrupt routine that should be triggered when the transmission ends.

The line 11 (// more stuff here ...), doesn't run immediately after, it runs when the whole transaction ends and the interrupt routine finish (takes about 3us)

am I doing something wrong? It seems like the interrupt routine is reentering constantly and that blocks the main execution. What interrupt should I enable to make sure it only triggers after the whole transaction is complete?

Thanks in advance

MCU STM32F722ZETx (Nucleo F722ZE)

main

LL_SPI_SetRxFIFOThreshold(SPI1, LL_SPI_RX_FIFO_TH_QUARTER);  // Quarter threshold for 1 byte / 8 bits
LL_SPI_Enable(SPI1); 
 
while(1){
  uint8_t pData[] = {0xAA, 0xBB, 0xCC};
  fastOff(CS_GPIO_Port, CS_Pin);  // CS assert now
  for (unsigned int i=0; i<3; i++){
    LL_SPI_TransmitData8(SPI1, pData[i]);  // load bytes into transmit buffer
  }
  LL_SPI_EnableIT_TXE(SPI1);  
  // more stuff here
}

Interrupt routine in Core\Src\stm32f7xx_it.c

void SPI1_IRQHandler(void){
  /* USER CODE BEGIN SPI1_IRQn 0 */
  if((LL_SPI_IsActiveFlag_TXE(SPI1)) && !LL_SPI_IsActiveFlag_BSY(SPI1)) {  
    fastOn(CS_GPIO_Port, CS_Pin);  // CS de-assert now
    LL_SPI_DisableIT_TXE(SPI1);
  }
  /* USER CODE END SPI1_IRQn 0 */
  /* USER CODE BEGIN SPI1_IRQn 1 */
  /* USER CODE END SPI1_IRQn 1 */
}
 
 
  /* USER CODE END SPI1_IRQn 0 */
  /* USER CODE BEGIN SPI1_IRQn 1 */
 
  /* USER CODE END SPI1_IRQn 1 */
}

SPI initialization generated by CubeMX

/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI1_Init(void)
{
 
  /* USER CODE BEGIN SPI1_Init 0 */
 
  /* USER CODE END SPI1_Init 0 */
 
  LL_SPI_InitTypeDef SPI_InitStruct = {0};
 
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
 
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  /**SPI1 GPIO Configuration
  PA5   ------> SPI1_SCK
  PA6   ------> SPI1_MISO
  PA7   ------> SPI1_MOSI
  PA15   ------> SPI1_NSS
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* SPI1 interrupt Init */
  NVIC_SetPriority(SPI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
  NVIC_EnableIRQ(SPI1_IRQn);
 
  /* USER CODE BEGIN SPI1_Init 1 */
 
  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
  SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_HARD_OUTPUT;
  SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV4;
  SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 7;
  LL_SPI_Init(SPI1, &SPI_InitStruct);
  LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
  LL_SPI_DisableNSSPulseMgt(SPI1);
  /* USER CODE BEGIN SPI1_Init 2 */
 
  /* USER CODE END SPI1_Init 2 */
 
}

4 REPLIES 4
S.Ma
Principal

In SPI master mode, rely on RX flags based interrupt. Remember that TXNE will flag just after write to DR, before even a single clock bit goes through the pin....

can you please elaborate? The only RX related flag I can find is RXNE (Rx buffer not empty) and I don´t see how to detect the end of the transaction based on that.

> I don´t see how to detect the end of the transaction based on that.

The question is what do you call transaction.

There are two kinds of SPI devices: one uses CS (NSS) to delimit "transactions", another uses CS just to enable the interface (to connect multiple devices to one host, etc.). For the latter kind, CS can go on and off multiple times during a "transaction".

STM32H7 has special mode where the count of SPI "words" is set before transaction and automatically keeps NSS active until all the data is moved.

And it has a simple mode when NSS is driven manually, or automatically, but NSS can go up/down several times as long as the host sends any data.

I don't have STM32F722, don't know whether its SPI is like 'H7.

To clarify, my configuration is STM32 as master with several slaves and by "end of transaction" I mean just after the last clock pulse. I was expecting an interrupt there, when is safe to deassert the CS for that slave and the SPI is ready to be used with another slave.

There is a busy flag in the SPI status register, but it doesn't generate an interrupt, and I want to avoid polling.