4
\$\begingroup\$

Cause for writing this question:

I think I found a bug. This happens when I try to enable DMA for SDADC and Input Capture.

Introduction:

I'm using STM32F373VBTx processor and it working great! Except when I try to enable DMA for TIM16 and TIM17 as Input Capture and SDADC1, SDADC2 and SDADC3. What happens is that the values from these peripherals only call DMA request 1 time and never more.

Method - Input Capture:

I enable IC1 (Input Capture 1)

enter image description here enter image description here

I enable IC0 (Input Capture 0)

enter image description here enter image description here

Then I start IC with DMA.

volatile static uint16_t input_capture0[2] = {0};
volatile static uint16_t input_capture1[2] = {0};

void STM32_PLC_Start_Input_Capture(TIM_HandleTypeDef* htim17, TIM_HandleTypeDef* htim16) {
    /*
     * Input capture for measuring frequency
     * For TIM17 and TIM16
     * Timer clock: 48 Mhz
     * Prescaler: 4799
     * Counter: 65535 (0xffff)
     * Update frequency: 0.1526 Hz (1/0.1526 = 6.5535 seconds)
     * Example: For every second, it will count 10000
     * Lowest frequency measurement: 1/(0xFFFF*0.0001) = 0.1526 Hz
     * Highest frequency measurement: 1/(1*0.0001) = 10000 Hz
     */
    if(HAL_TIM_IC_Start_DMA(htim16, TIM_CHANNEL_1, (uint32_t*)input_capture1, 2) != HAL_OK)
        Error_Handler();
    if(HAL_TIM_IC_Start_DMA(htim17, TIM_CHANNEL_1, (uint32_t*)input_capture0, 2) != HAL_OK)
        Error_Handler();
}

Method - Sigma Delta ADC

I enable Sigma Delta ADC 1

enter image description here enter image description here

Sigma Delta ADC 2

enter image description here enter image description here

Sigma Delta ADC 3

enter image description here enter image description here

And for timer 12 and timer 13

enter image description here enter image description here

Results - Input Capture

When I call to read the arrays input_capture0, input_capture1 then the DMA has only filled the arrays 1 time only. Only the first element has a value, the rest of the elements has 0 value. I try to pulse 3.3V onto the Input Capture pin, but it only react once.

static float compute_frequency(uint16_t input_capture[]) {
    /*
     * Typical worst case scenarios:
     * T1: 0xFFFF - T0: 0x0
     * T1: 0x0    - T0: 0xFFFF
     * T1: 0x7FFF - T0: 0x7FFF
     * T1: 0x0    - T0: 0x0
     */
    uint16_t difference;
    if(input_capture[1] > input_capture[0]) {
        difference = input_capture[1] - input_capture[0];
        return (float) 1/(difference*0.0001f);
    } else if(input_capture[1] < input_capture[0]) {
        difference = 0xFFFF - input_capture[1] + input_capture[0] + 1;
        return (float) 1/(difference*0.0001f);
    } else {
        return 1/(65335*0.0001f);
    }
}

float STM32_PLC_Input_Capture_Get(uint8_t i) {
    if(i == 0)
        return compute_frequency((uint16_t*)input_capture0);
    else
        return compute_frequency((uint16_t*)input_capture1);
}

Result - Sigma Delta ADC

When I try to read the arrays SDADC1_Single, SDADC2_Single, SDADC3_Differential, only the first element has a value, the rest of the elements has 0 values.

/* Get raw ADC0 to ADC11 */
uint16_t STM32_PLC_Analog_Input_ADC_Get_Raw(uint8_t i) {
    if(i < 9)
        return SDADC1_Single[i] + 0x8000;
    else
        return SDADC2_Single[i-9] + 0x8000;
}

/* Get raw DADC0 to DADC4 */
int16_t STM32_PLC_Analog_Input_DADC_Get_Raw(uint8_t i) {
    return SDADC3_Differential[i];
}

Discussion:

If I enable change Circular Mode to

enter image description here

Normal Mode

enter image description here

It will give me the same results. The first element get a value, the rest of the elements are zero.

Question:

What do you think causing this issue? Why does not the whole array completes?

enter image description here

enter image description here

Here is my main.c configuration file.

Update 1:

I tried to change the gain and offset at SDADC1 and this is how it goes. To change the gain, you need to call

HAL_StatusTypeDef HAL_SDADC_InjectedStop_DMA(SDADC_HandleTypeDef *hsdadc)
{
  HAL_StatusTypeDef status;

  /* Check parameters */
  assert_param(IS_SDADC_ALL_INSTANCE(hsdadc->Instance));

  /* Check SDADC state */
  if((hsdadc->State != HAL_SDADC_STATE_INJ) && \
     (hsdadc->State != HAL_SDADC_STATE_REG_INJ))
  {
    /* Return error status */
    status = HAL_ERROR;
  }
  else
  {
    /* Clear JDMAEN bit in SDADC_CR1 register */
    hsdadc->Instance->CR1 &= ~(SDADC_CR1_JDMAEN);

    /* Stop current DMA transfer */
    if(HAL_DMA_Abort(hsdadc->hdma) != HAL_OK)
    {
      /* Set SDADC in error state */
      hsdadc->State = HAL_SDADC_STATE_ERROR;
      status = HAL_ERROR;
    }
    else
    {
      /* Stop injected conversion */
      status = SDADC_InjConvStop(hsdadc);
    }
  }
  /* Return function status */
  return status;
}

Then it will call this function to abort DMA.

HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma)
{
  if(hdma->State != HAL_DMA_STATE_BUSY)
  {
    /* no transfer ongoing */
    hdma->ErrorCode = HAL_DMA_ERROR_NO_XFER;
    
    /* Process Unlocked */
    __HAL_UNLOCK(hdma);
    
    return HAL_ERROR;
  }
  else
  {
    /* Disable DMA IT */
     hdma->Instance->CCR &= ~(DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);
    
    /* Disable the channel */
    hdma->Instance->CCR &= ~DMA_CCR_EN;
    
    /* Clear all flags */
    hdma->DmaBaseAddress->IFCR = (DMA_FLAG_GL1 << hdma->ChannelIndex);
  }
  /* Change the DMA state*/
  hdma->State = HAL_DMA_STATE_READY; 
  
  /* Process Unlocked */
  __HAL_UNLOCK(hdma);
  
  return HAL_OK;
}

Here we can clearly see that the DMA wasn't started, even if I got values from DMA once.

enter image description here

Update 2:

Here is the complete project. Import it with STM32CubeIDE.

\$\endgroup\$
18
  • 1
    \$\begingroup\$ What have you done to debug the problem? I’d be looking at the dma registers to see what is really happening. You’ll only get so far relying on cubemx configuration. \$\endgroup\$
    – Kartman
    Commented Aug 18, 2021 at 13:14
  • 1
    \$\begingroup\$ I think its about time you grab the reference manual and start reading. The dma controller is not that difficult. At some point you’ll need to escape the grip of cubemx and get your hands dirty. Assuming you’ve got a debugger, you can look at the hardware registers. You can look at how cubemx configures the dma and the state of the dma after 1 transfer. Does the dma think it is finished? Or is it the timer not playing ball. ST is usually quite simple compared to some of the TI nightmares I’ve had to contend with. \$\endgroup\$
    – Kartman
    Commented Aug 18, 2021 at 13:23
  • 2
    \$\begingroup\$ Try not using HAL or CubeMX if you run into such problems. This is why I don't even bother to look at questions trying to debug using HAL or CubeMX. Not enough people read the Reference Manual which says what the details are and on top of that you don't know exactly what HAL or CubeMX is actually doing. \$\endgroup\$
    – DKNguyen
    Commented Aug 18, 2021 at 13:23
  • 1
    \$\begingroup\$ @MrYui You would only think the STM32 manual is complex if you never actually tried to read the manual for other microcontrollers. Most are...worse unless you are comparing to PICs which are simpler processors in general. \$\endgroup\$
    – DKNguyen
    Commented Aug 18, 2021 at 13:31
  • 2
    \$\begingroup\$ There really is no API. You're not coding an application. You're generating c-code and header files using a GUI provided by the manufacturer. Sometimes it gets it right, sometimes there are subtle issues with the generated code. When this happens, you need to go through the generated headers, line by line, until you understand them and find the issues. \$\endgroup\$ Commented Aug 18, 2021 at 13:48

1 Answer 1

3
\$\begingroup\$

I found the solution! It's a BUG in CubeMX. And it's very easy to solve!

Just move MX_DMA_Init(); to the beginning.

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init(); <--- To here
  MX_DAC1_Init();
  MX_DAC2_Init();
  MX_RTC_Init();
  MX_SPI2_Init();
  MX_TIM2_Init();
  MX_TIM5_Init();
  MX_SPI1_Init();
  MX_TIM4_Init();
  MX_SDADC1_Init();
  MX_SDADC2_Init();
  MX_SDADC3_Init();
  MX_CAN_Init();
  MX_USART1_UART_Init();
  MX_TIM6_Init();
  MX_TIM12_Init();
  MX_TIM13_Init();
  MX_TIM16_Init();
  MX_USB_DEVICE_Init();
  MX_FATFS_Init();
  MX_TIM17_Init();
  <--- From here
  MX_TIM19_Init();
  /* USER CODE BEGIN 2 */

This results:

  • Input Capture + DMA works
  • SDADC + DMA works, but I only get -0x8000 = -32768 (int16_t) because all my inputs are attached to GND. It seems that even if I have circular buffer, it still acts like it's Normal Mode. I can't change these values if I apply higher voltage onto the SDADC inputs. Hm.....

enter image description here

Update 1:

I removed the volatile keyword for SDADC arrays.

static int16_t SDADC1_Single[9];
static int16_t SDADC2_Single[3];
static int16_t SDADC3_Differential[5];

And now something happens, as it should, when I apply voltage onto the SDADC inputs. It's not correct index, but I'm happy that I can see values.

Solution:

  • Don't use volatile with the SDADC arrays. Perhaps a bug as well?

enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.