Skip to main content
Associate II
May 12, 2024
Solved

200kHz ADC Communication

  • May 12, 2024
  • 12 replies
  • 6818 views

Hello, 

 

I have been using the STM32F4 chip for a while but I ran into an issue when trying to communicate with an ADC to sample at 200kHz. So I have started to work with the STM32U5 chip. My goal is to use the SPI RDY feature or the GPDMA triggered from an external interrupt in order to handle the spi communication and fill a buffer. Unfortunately I have been struggling to find good resources on how to use these features or examples that I can try to follow through. I am able to do spi communication that are called from the main process but I can't get a message to trigger from an external interrupt. 

 

Any advice on how I can learn about these features would be greatly appreciated,

 

I have also included the code as I have it thus far.

 

Thanks!

This topic has been closed for replies.
Best answer by Saket_Om

Hello @UL_it 

 

To trigger an SPI Master communication using an external interrupt, you need to connect the DRDY pin from the ADC to an EXTI pin (EXTI4 or EXTI9, please refer to the reference manual for more details) and perform the following settings:

 

 

 /* EXTI Configuration **********************************************/
 /* Enable the GPIO clock */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 /* Configure pin as the EXTI input event line in interrupt mode */
 GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Pin = GPIO_PIN_4;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 /* Configure pin as Output now to generate automatically the event */
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_PULLDOWN;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
 /* EXTI interrupt init*/
 HAL_NVIC_SetPriority(EXTI4_IRQn, 3, 0);
 HAL_NVIC_EnableIRQ(EXTI4_IRQn);

 /* Init the SPI peripheral */

 hspi1.Instance = SPI1;
 hspi1.Init.Mode = SPI_MODE_MASTER;
 hspi1.Init.Direction = SPI_DIRECTION_2LINES;
 hspi1.Init.DataSize = SPI_DATASIZE_4BIT;
 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
 hspi1.Init.NSS = SPI_NSS_SOFT;
 hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
 hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
 hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
 hspi1.Init.CRCPolynomial = 0x7;
 hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
 hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
 hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
 hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
 hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
 hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
 hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
 hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
 hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
 HAL_SPI_Init(&hspi1);
 /* SPI Trigger Configuration ****************************************/
 AutonomousConfig.TriggerState = SPI_AUTO_MODE_ENABLE;
 AutonomousConfig.TriggerSelection = SPI_GRP1_EXTI4_TRG;
 AutonomousConfig.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
 HAL_SPIEx_SetConfigAutonomousMode(&hSPI1, &AutonomousConfig);

 

 

 

12 replies

ST Technical Moderator
May 14, 2024

Hello @UL_it 

 

In order to initiate SPI communication via an external interrupt, you need to connect the slave device's RDY pin to the master device's external interrupt pin.

Additionally, it is important to configure the ReadyMasterManagement and ReadyPolarity parameters correctly. Here is an example code snippet for these settings:

 

 hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_EXTERNALLY;
 hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;

Please ensure that the polarity setting is suitable for your application.

 

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Saket_Om
UL_itAuthor
Associate II
May 14, 2024

Hi OSAKE,

So the ready pin is only used to note that the SPI is ready to recieve a message. Can you use the RDY pin to trigger a message on the slave device?

 

 

What I am trying to do is trigger a message from the Master device (STM32U5) when a DRDY signal goes high on the ADC. I am trying to off load this task to an autonomous function that won't require the main loop. Reading your message, it seems like the RDY is not meant for this type of functionality.

 

thanks,

Spencer

ST Technical Moderator
May 14, 2024

Hello @UL_it 

 

The RDY signal is featured in some SPI versions, which performs the slave's FIFOs occupancy status,
and so its capability to continue at operation without any risk of data underrun or overrun. If the master disregards
RDY and continues the transfer, the slave risks an underrun or overrun condition. On STM32 devices, the master
communication is temporarily frozen when the slave provides a not-ready status. 

 

For your application I understand that you are looking to streamline it by offloading tasks from the main loop. To achieve this, you can indeed utilize the HAL_ADC_ConvCpltCallback() function. This callback function is executed when the ADC conversion is complete and can be used to initiate SPI communication autonomously.

Here's a step-by-step guide on how you can implement this:

  1. Enable ADC EOC (End Of Conversion) interrupt: Make sure that the ADC interrupt is enabled to trigger the callback function upon completion of the conversion.

  2. Implement the HAL_ADC_ConvCpltCallback(): Within this function, you can place your code to start the SPI communication. This function will be called automatically when the ADC conversion is complete.

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Saket_Om
Uwe Bonnes
Chief
May 14, 2024

Tell us what external ADC you use. Probably the ADC will complete the conversion in some maximum time guaranteed in the datasheet. Set up a timer to ensure this maximum time is over and read out without looking at DRDY.

UL_itAuthor
Associate II
May 14, 2024

Hi Uwe,

 

I am using the ADS127L11. I could do that but I still need to get more of the messaging off the main loop. In pervious tests I didn't have enough time to move the data around after the messaging even when using the SPI interrupt mode. 

 

I have been trying to work through the messaging similar to the STM32U5 workshop: STM32U5 Workshop (rristm.github.io).

 

I have been able to get the USART working as they show but I cannot get the SPI to work in a similar manor. Do you know if there is something special about the SPI protocol or controller that prevents this type of architecture?

Uwe Bonnes
Chief
May 14, 2024

Do you want to read out like Figure 8-35. SDO/DRDY and DRDY Function?

Have you tried to use EXTI(DRDY) as trigger for a SPI transfer of 24 bit  and SPI_RX_DMA to write the result to a block of memory?

UL_itAuthor
Associate II
May 14, 2024

In Order to make the transfer I have done the following:

Starting point LL Node:

pNodeConfig.Init.Request = GPDMA1_REQUEST_USART1_TX;

pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;

pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;

pNodeConfig.Init.DestInc = DMA_DINC_FIXED;

pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;

pNodeConfig.SrcAddress = data;

pNodeConfig.DstAddress = (uint32_t)&(USART1->TDR);

pNodeConfig.DataSize = 64*2;

 

After Change LL Node:

pNodeConfig.Init.Request = GPDMA1_REQUEST_SPI1_TX;

pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;

pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;

pNodeConfig.Init.DestInc = DMA_DINC_FIXED;

pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;

pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;

pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;

pNodeConfig.SrcAddress = data;

pNodeConfig.DstAddress = (uint32_t)&(SPI1->TXDR);

pNodeConfig.DataSize = 64*2;

 

 

main.c Uart Setup:

 

MX_YourQueueName_Config();

HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel15, &YourQueueName);

ATOMIC_SET_BIT(huart1.Instance->CR3, USART_CR3_DMAT);

__HAL_UART_ENABLE(&huart1);

HAL_ADC_Start(&hadc1);

HAL_DMAEx_List_Start(&handle_GPDMA1_Channel15);

HAL_TIM_Base_Start(&htim15);

 

 

main.c SPi Setup:

MX_YourQueueName_Config();

HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel15, &YourQueueName);

SET_BIT(hspi1.Instance->CFG1, SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN);

SPI_2LINES_TX(&hspi1);

__HAL_SPI_ENABLE(&hspi1);

HAL_SPI_Transmit_DMA(&hspi1, data, 4);

HAL_ADC_Start(&hadc1);

HAL_DMAEx_List_Start(&handle_GPDMA1_Channel15);

HAL_TIM_Base_Start(&htim15);

 

 

SPI Config:

 

hspi1.Instance = SPI1;

hspi1.Init.Mode = SPI_MODE_MASTER;

hspi1.Init.Direction = SPI_DIRECTION_2LINES;

hspi1.Init.DataSize = SPI_DATASIZE_16BIT;

hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;

hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

hspi1.Init.NSS = SPI_NSS_SOFT;

hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;

hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

hspi1.Init.TIMode = SPI_TIMODE_DISABLE;

hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi1.Init.CRCPolynomial = 0x7;

hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;

hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;

hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;

hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;

hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;

hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;

hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;

hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;

hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;

hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;

if (HAL_SPI_Init(&hspi1) != HAL_OK)

{

Error_Handler();

}

HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;

HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;

HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;

if (HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct) != HAL_OK)

{

Error_Handler();

}