Skip to main content
wqw.1
Associate II
July 8, 2023
Solved

STM32H743 How to start SPI DMA transfer by event

  • July 8, 2023
  • 2 replies
  • 13458 views

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

 

 

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

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.

2 replies

MasterT
Lead II
July 9, 2023

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.

wqw.1
wqw.1Author
Associate II
July 10, 2023

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
Lead II
July 10, 2023

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

 

wqw.1
wqw.1Author
Associate II
July 11, 2023

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.

 

AScha.3
Super User
July 11, 2023

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""."