cancel
Showing results for 
Search instead for 
Did you mean: 

Interface with external ADC via SPI at 1MS/s

CPurc.1
Associate II

Hi all,

I am using the NUCLEO-F446RE with the STM32F446RE. I am trying to interface with an external dual sampling differential ADC: ADS9224R.

ADC Timing Diagram

0693W00000KZlwCQAT.pngThe ADC can provide samples at up to 3M/s. I would be happy to achieve ~0.8M/s sampling.

I have been struggling to meet the timing that I require and can only reach ~180kHz with the STM32. My problem is that initiating separate SPI receive calls takes too long between commands.

Running the following code to initiate back to back SPI reads:

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); //CS LOW
  HAL_SPI_Receive(&hspi1, (uint8_t *)spi_buffer, 1,40);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); //CS high
 
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); //CS LOW
  HAL_SPI_Receive(&hspi1, (uint8_t *)spi_buffer, 1,40);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); //CS high

Produces the following timing:

0693W00000KZlxPQAT.png 

The STM32 is operating at 180MHz, and the SPI CLK is at ~30MHz. You can see that once the SPI initiates it is very fast, but the time to initiate the next SPI read is taking too long (~6us) which is limiting the rate at which I can read from the ADC.

I understand that this approach will only read ADC-0A, and not ADC-0B simultaneously. I have an implementation that uses QSPI but suffers from the same problem. I have simplified to single line SPI in hope that more people can provide help and I can transfer this to a dual line implementation.

Thank you all for your help!

27 REPLIES 27

That's great. I have the CS line working now!

I am not seeing anything on the SPI SCLK however, so something is incorrect with my SPI-DMA-timer setup.

Below is how the DMA is configured for the timer (to receive from SPI):

[In DMA request, I can only select TIM2_UP/CH3, instead of TIM2/CH3 in your screenshot]

0693W00000KZyv7QAD.png0693W00000KZyx3QAD.pngMy SPI is configured as below:

0693W00000KZyvRQAT.png0693W00000KZywZQAT.pngThe following code:

[I swapped the SPI2->DR and buffer around (line 3-4) since I want to receive, not transmit, I believe this is correct. Am I perhaps not correctly setting a register in the SPI module to initialise it for receiving?]

uint16_t buffer[100];
 
  HAL_DMA_Start(&hdma_tim2_up_ch3, (uint32_t)&(SPI2->DR), (uint32_t)buffer, 1); //SPI to DMA
  //HAL_DMA_Start(&hdma_tim2_up_ch3,  (uint32_t)buffer, (uint32_t)&(SPI2->DR), 1); //DMA to SPI
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  //HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
  HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_4);
  __HAL_SPI_ENABLE(&hspi2);
  //SET_BIT(SPI2->CR2, SPI_CR2_RXDMAEN);
  __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC3);

I am probing PB13 and expecting to see a clock when channel 3 timer triggers, but it is stuck low.

Do I need to change the order of operation around to:

  1. Timer triggers SPI RX
  2. SPI RX completed triggers DMA (DMA configured within SPI instead of the timer)

Thanks again for your help.

Works for me. Found my Nucleo F446RE board and setup as you did. Have attached my .ioc file, copied your code.

Only difference: DMA should transfer half-words (16-bit), not bytes.

You may set CH3 to output its pulse on a pin and probe that too.

hth

KnarfB

Thanks! It appears that the .ioc target is the NUCLEO-L432KC, did you upload the wrong file? I'm transferring it now across to a Nucleo F446RE project. Fingers crossed.

sorry, see below

Unfortunately my PB13 (SPI SCLK) is still staying low. Are you sure that you uploaded the correct .ioc file? The "Counter Period" parameter for TIM2 was incorrectly set to default with the most recent file that you uploaded.

When you say that it works, do you mean that you can see 16 clock cycles on PB13, every time channel 1 triggers (every 2us)?

Thanks!

Caught me again. I tested the F446RE board first with periodically pushing bytes to SPI2->DR in the while loop. When I saw the waveforms on my LA, I was happy, inserted your code compiled... but somehow still looked at the old waveforms :-(. Porting such stuff between series is not 1:1 and posiibly different HALs behave differently.

Let me think about it, but not today, its getting late here. Maybe you're first.

hth

KnarfB

No worries! I'm not sure swapping the "buffer" and "SPI2->DR" around like I did is sufficient. From my understanding, writing to "SPI2->DR" will initiate an SPI transmit transfer. However now that I am trying to receive, we need the timer to initiate the SPI read, not send the contents of "SPI2->DIR" (which would be empty?) to the buffer.

Or do you plan for this to work by the timer triggering the write of "SPI2->DR" to memory, which contains the previous cycle's SPI read data? I just don't see what is triggering an SPI read.

I really appreciate your help here, thank you.

Ok, next plan, no code yet. If the 2nd SPI is slave, why not put both in slave mode (with circular RX DMA)? Then, we need another component, generating the clock pulses. This could be Timer TIM1 in one pulse mode and using a repetition counter of 7 (for 8 pulses). The pulse train (clock) is output in PWM GenerationCH1 mode.

How to trigger TIM1? Manually, for testing, worked.

Next: Automatically from TIM2. (Put TIM into slave trigger mode and route the TIM2 event to TIM1 ITR.

Don't have that ADC for testing.

Soon more...

KnarfB

Hi KnarfB, I'm not able to get the one pulse mode working on TIM1. I have tried many settings but can't seem to get anything out.

I have got TIM1 triggering on TIM2 working (when I set TIM1 channel 1 output to something that isn't using the OnePulse mode). From the reference manual, TIM2 can trigger TIM1 using ITR1.

I am using line 1 of the code below to try to start the one pulse functionality on TIM1:

HAL_TIM_OnePulse_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);

Below is an example of the type of setup I am trying for TIM1 (I have verified that the trigger mode is working, but also tried one pulse with trigger disabled):0693W00000Ka5iKQAR.png0693W00000Ka5j3QAB.pngThank you!

Looks promising. For TIM1 Clock Source set Internal Clock. Counter Period to 2 (something small) and CH1 PWM Pulse to 1 (not 0).

TIM2 is as yesterday with TRGO Trigger Event Selection to OC3REF.

In main() just start both timers.

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);

I get the clock pulses.

hth

KnarfB