cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F446 DMA Indexing

joesnyder
Associate II
Posted on March 22, 2017 at 22:54

I ultimately need 'instant on' ADC conversions.  For instance, when a timing gate goes high, I need to start taking ADC values immediately (the signal is only 100us wide),  The problem I'm running into is the lag: firing up the ADC in DMA mode from a 'cold start' takes 50+ usec to start getting values.  I tried leaving the ADC/DMA running, and just enabling/disabling interrupts, but even that takes ~25us from the point of enabling interrupts to getting values out.

The attached picture shows the dilemma: blue is the timing gate, yellow/purple is the signal I need to sample, and green is toggling when samples are being taken.  You can see that from the rising edge (when DMA interrupts are enabled) to getting samples takes ~25us. 

Now I've got the ADC/DMA dumping values into a 1000 element array (circular, continuous conversion).  That's working fine, but I need to be able to either 1: see what 'index' the DMA is pointing to in the 1000 element array, or 2: be able to reset the pointer back to the beginning of the array.  i.e., I need to know where I'm at in the circular buffer at the rising edge of the timing gate.  I'm searching for examples, but everything seems to point to stopping the ADC/DMA and restarting it, which takes too long.  And I've found nothing on how to read out what index it is currently pointing at.

Ideally, I'd like to have 2 arrays, where the DMA dumps data to a 'garbage' array, and at the rising edge of my timing gate, switch on the fly to the address of a 'good' array.  However, section 9.5.8 of the STM32F446 reference manual says that the DMA stream x memory 0 address register (DMA_SxM0AR) can only be written if the stream is disabled. 

Any insight would be helpful.  Thanks in advance. 

1 ACCEPTED SOLUTION

Accepted Solutions
Posted on March 23, 2017 at 01:31

/**

  * @brief  Enables or disables the ADC continuous conversion mode

  * @param  ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.

  * @param  NewState: new state of the selected ADC continuous conversion mode

  *          This parameter can be: ENABLE or DISABLE.

  * @retval None

  */

void ADC_ContinuousModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState)

{

  /* Check the parameters */

  assert_param(IS_ADC_ALL_PERIPH(ADCx));

  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)

  {

    /* Enable the selected ADC continuous conversion mode */

    ADCx->CR2 |= (uint32_t)ADC_CR2_CONT;

  }

  else

  {

    /* Disable the selected ADC continuous conversion mode */

    ADCx->CR2 &= (uint32_t)(~ADC_CR2_CONT);

  }

}
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

6 REPLIES 6
Posted on March 22, 2017 at 23:40

Getting it in/out of continuous mode should be one bit.

If the samples are 12-bit wide one could certainly use a fill pattern in the buffer that could be recognized at HT/TC events.

If the DMA pointer is readable, a second DMA+TIM could sample via a TIMx_CCx edge

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
joesnyder
Associate II
Posted on March 22, 2017 at 23:58

Clive,

For direct bit manipulation, from the data sheet, I know it's ADC1, register CR2, bit CONT...how would I raw bit-bang that?

ADC1->CR2 <?????> = 0

Thanks in advance.

Posted on March 23, 2017 at 01:31

/**

  * @brief  Enables or disables the ADC continuous conversion mode

  * @param  ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.

  * @param  NewState: new state of the selected ADC continuous conversion mode

  *          This parameter can be: ENABLE or DISABLE.

  * @retval None

  */

void ADC_ContinuousModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState)

{

  /* Check the parameters */

  assert_param(IS_ADC_ALL_PERIPH(ADCx));

  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)

  {

    /* Enable the selected ADC continuous conversion mode */

    ADCx->CR2 |= (uint32_t)ADC_CR2_CONT;

  }

  else

  {

    /* Disable the selected ADC continuous conversion mode */

    ADCx->CR2 &= (uint32_t)(~ADC_CR2_CONT);

  }

}
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
joesnyder
Associate II
Posted on March 23, 2017 at 17:11

I'm using EWARM and CUBE generated code.  I'm using the following to try and enable/disable the continuous conversion:

   ADC1->CR2 &= (~ADC_CR2_CONT);  //DISABLES CONTINUOUS (clear CONT bit)

   HAL_Delay(1);

   ADC1->CR2 |= ADC_CR2_CONT;   //ENABLES CONTINUOUS (set CONT bit)

   HAL_Delay(1);     

In ST-LINK, I can see the CONT bit changing, and disabling continuous definitely disables continuous conversions, but then it will not start back up once I enable continuous conversions (by setting the CONT bit).  If I use the built in function:

I have tried adding the following to start the conversions:

   ADC1->CR2 |= 0x00040000;   //ENABLE SOFTWARE START BIT (STM32F446 ref manual, p. 346)

...but the conversions still will not start back up.  The reference manual says setting that bit will start conversion of regular channels when ADON=1 (which it is).  What am I missing to resume continuous conversions? 

joesnyder
Associate II
Posted on March 23, 2017 at 17:51

Standby, I was setting the JSWSTART bit, not the SWSTART bit.  I'll post if/when I figure this out. 

joesnyder
Associate II
Posted on March 24, 2017 at 14:05

That was it.  You have to enable the SWSTRT bit (Bit 30 of ADC_CR2) to coerce it back into continuous conversions.  Final code snippet:

   ADC1->CR2 &= (~ADC_CR2_CONT);  //DISABLES CONTINUOUS (clear CONT bit)

   ....do other stuff

   ADC1->CR2 |= ADC_CR2_CONT;   //ENABLES CONTINUOUS (set CONT bit)

   ADC1->CR2 |= 0x40000000;   //ENABLE SOFTWARE START (SWSTART bit) (STM32F446 ref manual, p. 387)

This also has the added benefit of synchronizing the ADC sampling sequence to whenever you re-enable continuous conversions.  By just letting it free-run and enabling/disable interrupts, you are at the mercy of where it happens to be in the continuous conversion sequence.