cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 How to start SPI DMA transfer by event

wqw.1
Associate II

I have a ADC chip connected via SPI to STM32H7x MCU. MCU is master spi, and ADC sends "Data ready" signal whenever new values are ready to transfer. I need sampling rate above 100000 samples per second。If i listen to external interrupt (Data Ok) and calls SPI transfer to read valus, the cpu is getting interrupted too many times and i can’t do anything else。

CSTART is set by software to start spi communication. Is there a way to start tranferring when external event or other event? 

wqw1_1-1688799515425.png

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

Reading and sampling frequency are the same, synchronous. But I was mistaken that SPI alone could synthesize all required signals, erroneously thinking that CONVST and _CS could be jumped. There is t-quiet 50 nsec spacing required by AD, so it leaves dual timer (1-st option) only the way. And it 'd complicate things more than I was doing with AD7984, main Timer has to be configured in dual combine PWM to synthesize _CS and CONVST with correct phase shift. I can't do testing since I don't have such adc around, but in theory it looks quite possible /realistic.

View solution in original post

11 REPLIES 11
MasterT
Senior III

What kind of ADC?

The best option not to read DR at all, have SPI-DMA preconfigured and driven by Timer synchronously with ADC clock, it's easy if uCPU provides master clock to ADC. Knowing how many cycles it takes to adc to complete conversion.

Thanks for your reply!

The ADC chip is ADS7616.

I wrote a demo, triggering DMA transfer by TIM2_UP event, DMA transfers data to SPI5 TXDR, the problem is that STM32H743 as SPI Master cannot generate CLK clock.

 

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();

/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI5_Init();
MX_TIM2_Init();
MX_TIM5_Init();
/* USER CODE BEGIN 2 */
__HAL_TIM_CLEAR_IT(&htim2, TIM_DMA_UPDATE);
// HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
const unsigned char s_ucSize = 36;
HAL_DMA_Start(&hdma_spi5_rx, (uint32_t )&hspi5.Instance->RXDR, (uint32_t)g_aucSpiRx, s_ucSize);
SET_BIT(hspi5.Instance->CFG1, SPI_CFG1_RXDMAEN);
__HAL_SPI_ENABLE(&hspi5);

HAL_DMA_Start(&hdma_tim2_up, (uint32_t)g_aucSpiTx, (uint32_t )&hspi5.Instance->TXDR, s_ucSize);

__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);

__HAL_TIM_ENABLE(&htim2);

HAL_TIM_Base_Start_IT(&htim5);

// HAL_TIM_Base_Start_IT(&htim2);

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

 

static void MX_TIM2_Init(void)
{

/* USER CODE BEGIN TIM2_Init 0 */

/* USER CODE END TIM2_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM2_Init 1 */

/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 5000;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */

}

 

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
if(htim_base->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */

/* USER CODE END TIM2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();

/* TIM2 DMA Init */
/* TIM2_UP Init */
hdma_tim2_up.Instance = DMA1_Stream0;
hdma_tim2_up.Init.Request = DMA_REQUEST_TIM2_UP;
hdma_tim2_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim2_up.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim2_up.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim2_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim2_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim2_up.Init.Mode = DMA_CIRCULAR;
hdma_tim2_up.Init.Priority = DMA_PRIORITY_LOW;
hdma_tim2_up.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_tim2_up) != HAL_OK)
{
Error_Handler();
}

__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_UPDATE],hdma_tim2_up);

/* TIM2 interrupt Init */
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspInit 1 */

/* USER CODE END TIM2_MspInit 1 */
}
else if(htim_base->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspInit 0 */

/* USER CODE END TIM5_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM5_CLK_ENABLE();
/* TIM5 interrupt Init */
HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM5_IRQn);
/* USER CODE BEGIN TIM5_MspInit 1 */

/* USER CODE END TIM5_MspInit 1 */
}

}

 

MasterT
Senior III

Probably AD-7616 , no "s".  I see that adc works with internal oscillator, than conversion clock cycles unknown as frequency of the oscillator.

Well, I see 3 options to interface adc:

 

  1. Two timers, one generates for CS, another (gated mode driven by CS event) - SPI clock.
  2. I2S, PCM mode. Provide higher frequency (PWM) clock to ADC that is synchronous to I2S frame rate, than you don't need to read DR pin.
  3. stm32H7 has NSSP - nice feature I discovered recently. Pity other stm32 uCPU product lines doesn't have.

Option #1 explained here:

https://community.st.com/t5/stm32-mcu-products/spi-with-dma-add-delay-between-bytes/m-p/227885#M50764

Universal method, for any uCPU even w/o I2S.

For H7 I 'd recommend option 3 - NSSP, read Reference Manual RM0433 for details.

Examples related to H7 SPI:

https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/NUCLEO-H743ZI/Examples/SPI

 

Very pleased with you reply! 

Yes,The Adc is AD-7616.

Option #1 is very creative solutions.

Option #3,I saw the example. I need to use HAL_SPI_TransmitReceive_DMA to start SPI-DMA transmission in interrupt sevice routine, which is not good choice when using a relatively high frequency. I was hoping that the SPI DMA transfer could be triggered automatically by some event.

 

just - why not using onchip adc ? has also 16 bits and not sooo bad, 80dB snr , AD7616 has 92 dB SNR (need this 12db at so much effort ?)

maybe this way: (from MasterT idea )

1. a timer, to start adc , conversion rate

2. a triggered timer, by DR from adc, doing 16 pulses

3. a spi in slave mode, 16 clks shift in the adc result

4. a circular dma, writing spi rx data to a block.

then get an callback INT from dma, when a block is loaded with new data - eh ?

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

I always start SPI->DMA only ones, circular mode, sample array as big as possible up to 32768 - for FFT compatibility.

In the interrupt subroutine generated by DMA (actually 2 - half complete and full complete ) copy samples to another array quickly, somekind of double buffering. Doing so I was able to get sample rate 1.5 Msps with ad7982-83-84 (18-bits)

The project requires an external ADC for data sampling.

What I am confused about is that in circular mode, SPI->DMA cyclically sends data repeatedly.In this way, how to associate SPI->DMA transfer with the ad conversion is done,and perform sampling according to the speicified sampling rate.My ideal is to trigger circular SPI->DMA transfer after the ad conversion is done.

 In circular mode you control SPI via clock preselector,  in essence setting baudrate defines read periodicity. Fine tuning possible adjusting NSSP clock cycles, so it may not be necessary. I'm talking option 3 specifically to H7 (not available on any other F3/F4/G4 series). Option 1 using timers would works in any case.

I see in DS AD7616 has sequencer & oversampling

"Conversion Start Input for Channel Group A and Channel Group B. This logic input initiates
conversions on the analog input channels.
A conversion is initiated when CONVST transitions from low to high for the selected analog
input pair. When burst mode and oversampling mode are disabled, every CONVST transition
from low to high converts one channel pair. In sequencer mode, when burst mode or
oversampling is enabled, a single CONVST transition from low to high is necessary to perform
the required number of conversions."

but it change nothing compare to to AD7984 - conversion times strictly specified in DS and since master on SPI bus set start conversion than time when Busy (Ready) lines goes low also known, simply multiplies T of single conversion x number of channels x oversampling.  Idea is the same, don't read indication from adc when data is ready, just calculate when it must be ready