cancel
Showing results for 
Search instead for 
Did you mean: 

Reading both ADS8350 ADC serial channels with SPI or DMA

physicist
Associate

Hello,

I have the TI ADS8350 and want to read both channels in parallel. They support SPI, but I can not figure out how to setup the dual SPI for this. Another way would be to use DMA, could someone point me in the right direction. I am using SMT32F412.

Thanks.

 

1 ACCEPTED SOLUTION

Accepted Solutions
MasterT
Lead

I interfaced ads8354 to stm32h743, using one spi as master, and another as a slave 2-line rx only. The beauty of H7 is that it can drive CS line by spi in master /dma mode.  F4 & F7 don't have this future, so if you can't switch project to h7m than I'd recommend config two spi as slaves driven by two linked timers. First timer -gated generates 16-clock for SCLK, and second drives CS and defines sampling rate.

View solution in original post

7 REPLIES 7

So this: https://www.ti.com/product/ADS8350 

Do TI have any suggestions/recommendations on this?

https://e2e.ti.com/support/data-converters-group/data-converters/f/data-converters-forum

In SPI terms, Looks like it has a single SCLK, two MISOs, and no MOSI:

AndrewNeil_0-1744278592636.png

So the start of the read is triggered purely by /CS:

AndrewNeil_1-1744278659133.png

So maybe try:

  • Use one SPI as Master, to generate the clock
  • Use another SPI as Slave, clocked by the Master

You'll need to manually control /CS anyhow to get the tCONV timing

AndrewNeil_3-1744279194208.png

 

PS:

You may also need to feed the /CS back into the Slave...

 

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

And if I want to have a specific acquisition time should I use one /CS for slave SPI and second for ADC because with this ADC the acquisition time is controlled by the /CS pin.

I would think not: there should be just one /CS - that's what synchronises everything.

 

Looking at it again, it seems that  tCONV isn't (just) a time - it's a number of clock cycles.

You should be able to get that by the Master clocking-out a 16-bit value (just a dummy that gets ignored).

Again, this is all about  the TI chip; so TI would be experts and they would be the ones to ask.

https://e2e.ti.com/support/data-converters-group/data-converters/f/data-converters-forum 

None of this is specific to STM32.

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
MasterT
Lead

I interfaced ads8354 to stm32h743, using one spi as master, and another as a slave 2-line rx only. The beauty of H7 is that it can drive CS line by spi in master /dma mode.  F4 & F7 don't have this future, so if you can't switch project to h7m than I'd recommend config two spi as slaves driven by two linked timers. First timer -gated generates 16-clock for SCLK, and second drives CS and defines sampling rate.

Thanks, I am forced to use the F4. So I should confing 2 spi slaves that will just read with first gated timer being trigered by some ITR, and second timer for CS, and I get the ITR from its output compare? 

You could still try the Master + Slave, and get the Master to give the 16 clocks as suggested?

Should be easy enough to try ...

 

PS:

The F4 does have a kind of hardware /CS control:

AndrewNeil_0-1744298203668.png

https://www.st.com/resource/en/reference_manual/rm0402-stm32f412-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=809

Maybe that could be made to work?

 

There's also QUADSPI ...

AndrewNeil_1-1744298388183.png

 

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

Here a code that link Tim2 Tim-3 and SPI-3 on F446re.

void SPI3_DMA_Init(void) 
{
  __HAL_RCC_DMA1_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}

void SPI3_Init(void)
{

  hspi3.Instance                = SPI3;
  hspi3.Init.Mode               = SPI_MODE_SLAVE;
  hspi3.Init.Direction          = SPI_DIRECTION_2LINES_RXONLY;
  hspi3.Init.DataSize           = SPI_DATASIZE_16BIT;
  hspi3.Init.CLKPolarity        = SPI_POLARITY_HIGH;
  hspi3.Init.CLKPhase           = SPI_PHASE_1EDGE;
  hspi3.Init.NSS                = SPI_NSS_HARD_INPUT;
  hspi3.Init.FirstBit           = SPI_FIRSTBIT_MSB;
  hspi3.Init.TIMode             = SPI_TIMODE_DISABLE;
  hspi3.Init.CRCCalculation     = SPI_CRCCALCULATION_DISABLE;
  hspi3.Init.CRCPolynomial      = 10;

  if (HAL_SPI_Init(&hspi3) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = { 0};
  if(hspi->Instance == SPI3) {
    __HAL_RCC_SPI3_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**SPI3 GPIO Configuration    
    PA15     ------> SPI3_NSS
    PC10     ------> SPI3_SCK
    PC11     ------> SPI3_MISO 
    */
    GPIO_InitStruct.Pin       = GPIO_PIN_15;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin       = GPIO_PIN_10|GPIO_PIN_12;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* SPI3 DMA Init */
    /* SPI3_RX Init */
    hdma_spi3_rx.Instance                 = DMA1_Stream0;
    hdma_spi3_rx.Init.Channel             = DMA_CHANNEL_0;
    hdma_spi3_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    hdma_spi3_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_spi3_rx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi3_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
    hdma_spi3_rx.Init.Mode                = DMA_CIRCULAR;
    hdma_spi3_rx.Init.Priority            = DMA_PRIORITY_HIGH;
    hdma_spi3_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;

    if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hspi,hdmarx,hdma_spi3_rx);
  }
}

void TIM2_Config(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
  TIM_MasterConfigTypeDef sMasterConfig     = { 0};
  TIM_OC_InitTypeDef sConfigOC              = { 0};

  __HAL_RCC_TIM2_CLK_ENABLE();

  htim2.Instance                = TIM2;
  htim2.Init.Prescaler          = 0;
  htim2.Init.CounterMode        = TIM_COUNTERMODE_UP;
  htim2.Init.Period             = 219;//409kHz
  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_OC3REF;//TIM_TRGO_OC4REF;
  sMasterConfig.MasterSlaveMode       = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 32;
//  sConfigOC.OCPolarity  = TIM_OCPOLARITY_HIGH;//TIM_OCPOLARITY_LOW;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;

  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
//
  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 50;//36;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;

  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  
  HAL_TIM_Base_Start(&htim2);

  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
}

void TIM3_Config(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
  TIM_SlaveConfigTypeDef sSlaveConfig       = { 0};
  TIM_MasterConfigTypeDef sMasterConfig     = { 0};
  TIM_OC_InitTypeDef sConfigOC              = { 0};

  __HAL_RCC_TIM3_CLK_ENABLE();

  htim3.Instance                = TIM3;
  htim3.Init.Prescaler          = 0;
  htim3.Init.CounterMode        = TIM_COUNTERMODE_UP;
  htim3.Init.Period             = 1;
  htim3.Init.ClockDivision      = TIM_CLOCKDIVISION_DIV1;

  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  
  sClockSourceConfig.ClockSource  = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sSlaveConfig.SlaveMode          = TIM_SLAVEMODE_GATED;
  sSlaveConfig.InputTrigger       = TIM_TS_ITR1;
  if (HAL_TIM_SlaveConfigSynchronization(&htim3, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  
  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 1;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_TIM_Base_Start(&htim3);

  if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
}