cancel
Showing results for 
Search instead for 
Did you mean: 

GPDMA with ADC with multiple channels

JuliaK
Associate II

Hi,

I am trying to setup GPDMA with ADC with multiple channels on the Nucleo-H563ZI. The DMA should fill a buffer of the length of number of channels in a circular fashion, such that one entry in the buffer array always contains data of the same ADC channel. Unfortunately, I ran into several issues where I would be happy to get some input. 

1. How do I enable ADC requests? Is that only possible with the function HAL_ADC_Start_DMA? If I use this function, it fills only the first entry in my buffer and does not circle through it.

2. When implementing GPDMA, I am able to get a circular buffer filling to work by setting it up in linked list mode. I create a node for every "transaction", with both the source and destination fixed. The source is the same for all nodes and I define one entry of my destination array as a destination for each node. I have set it up like this because according to the documentation it seems like a request can only trigger the execution of a full node or the whole linked list and not individual "transactions" within a linked list node. However, this does not work in link step mode (LinkStepMode = DMA_LSM_1LINK_EXECUTION), only in run-to-completion mode. I would like to set it up such that the ADC conversion requests a DMA transfer of only one value.
Could you maybe give me some pointers as to what I am doing wrong?

Thanks and best regards,

Julia

7 REPLIES 7
Pierre_Paris
ST Employee

Hello Julia @JuliaK

Thank you for your question !

1/ "How do I enable ADC requests?" 

  • Configuration of ADC conversions
    • Parameters using HAL_ADC_Init() -> resolution, data alignment, mode normal or circular, ...
    • Channels using HAL_ADC_ConfigChannel() -> channel number, rank into sequencer, ...
  • Execution of ADC conversions with transfer by DMA
    • Activate ADC peripheral and start conversions using HAL_ADC_Start_DMA()
    • Wait for ADC conversion completion by call of function HAL_ADC_CponvCpltCallback() (to implement in user program)
    • Conversion results are automatically transferred by DMA into destination variable address
    • Stop conversion and disable ADC peripheral using HAL_ADC_Stop_DMA()

2/ "Could you maybe give me some pointers as to what I am doing wrong?"

  • Have you try to use the full Linked-List and LinkStepMode=DMA_LSM_FULL_EXECUTION. Can you share your node config (DMA_NodeConfTypeDef struct) please ?

You can download the STM32CubeH5 here and see this example to inspire you (under the root STM32Cube_FW_H5_V1.1.0\Projects\NUCLEOH563ZI\Examples\

  • ADC\ADC_SingleConversion_TriggerSW_DMA) -> For the question on ADC and DMA.
  • \DMA\DMA_LinkedList) -> For the question on LinkedList.

Best Regards,

Pierre

 

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Dear Pierre,

 

Please find attached my main.c and linked_list.c files. I tried different combinations and have seen the example code. The ADC_SingleConversion_TriggerSW_DMA example only has one ADC channel, whereas I would like to automatically read several channels and write the results to a buffer in a way that buffer[0] has the data from channel 0, buffer[1] has the data from channel 1, etc.

When implementing my linked list, it at least executes the code when I set LinkStepMode=DMA_LSM_FULL_EXECUTION, but the buffer is then not filled correctly with the different ADC channel results. I would like to trigger the execution of one list linked item as soon as an ADC conversion is done in order to fill my buffer as described above (buffer[0] has data from channel 0, etc).  This is why I am trying to get it to work with LinkStepMode=DMA_LSM_1LINK_EXECUTION, but no success.

Just to avoid any misunderstandings, my questions were all related to the same goal of getting a multi channel ADC to work with DMA. It does not really matter to me if I set it up with a linked-list or if I just set up the ADC and then use the HAL_ADC_Start_DMA() function. 

Thank you for your help and kind regards,

Julia

 

Pierre_Paris
ST Employee

Hello @JuliaK,

I took a quick look and here are some remarks to investigate more precisely :

  • Change the size of aDST_Buffer1 (eg: 20) to check if you are writing after in a unauthorized memory area.
  • I didn't find any HAL_ADCEx_Calibration_Start() to perform an ADC automatic self-calibration. That may be the reason why the buffer is not filled correctly.
  • I think the HAL_DMA_RegisterCallback() may be called before HAL_DMA_Init() in HAL_DMA_STATE_RESET.
  • I don't see any GPDMA1 interrupt initialization. (eg : HAL_NVIC_EnableIRQ())

Best Regards,

Pierre

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hi Pierre,

Thanks for your answer. Unfortunately, that did not resolve my issues.

  • Changing size of buffer: My buffer is not filled at all if LinkStepMode=DMA_LSM_1LINK_EXECUTION. The first entry in my buffer is filled if LinkStepMode=DMA_LSM_FULL_EXECUTION, implying that only the first node of my linked list is executed. Changing the buffer length did not change this behaviour.
  • Adding HAL_ADCEx_Calibration_Start() did not change the behaviour.
  • I now call HAL_DMA_RegisterCallback() before HAL_DMA_Init(), but that also did not change the behaviour.
  • If I add a GPDMA1 interrupt initialization, my program gets stuck in an infinite loop, which I assume is because there is no callback function for this interrupt in stm32h5xx_it.c. It is not automatically created with STM32CubeMX. How should this callback function be called and what should it do?

Kind regards,

Julia

Pierre_Paris
ST Employee

Hello @JuliaK,

I am sorry to hear that, here some support about this callback function :

  • You can add these lines in your MX_GPDMA1_Init() with the appropriate channel

 

 

  /* GPDMA1 interrupt Init */
    HAL_NVIC_SetPriority(GPDMA1_ChannelXX_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_ChannelXX_IRQn);​

 

 

  • In stm32h5xx_it.c file, you should have this :

 

 

/**
  * @brief This function handles GPDMA1 Channel 10 global interrupt.
  */
void GPDMA1_ChannelXX_IRQHandler(void)
{
  /* USER CODE BEGIN GPDMA1_ChannelXX_IRQn 0 */

  /* USER CODE END GPDMA1_ChannelXX_IRQn 0 */
  HAL_DMA_IRQHandler(&handle_GPDMA1_ChannelXX);
  /* USER CODE BEGIN GPDMA1_ChannelXX_IRQn 1 */

  /* USER CODE END GPDMA1_ChannelXX_IRQn 1 */
}​

 

 

  •  Are you entering in the TransferComplete callback ? (add a breakpoint in debug)

Best Regards,

Pierre

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Dear Pierre,

Thank you, that helped. I also saw that quite a lot of the setup of the ADC_SingleConversion_TriggerSW_DMA example is done in the stm32h5xx_hal_msp.c file. I now have an implementation of a continuously converting ADC together with DMA, which writes the data to the buffer correctly. However, the CPU seems to get stuck in executing the transfer callback functions and is not executing anything else. Can I disable these callback functions? Or can I lower the acquisition frequency somewhere?

Thank you and kind regards,

Julia

Pierre_Paris
ST Employee

Hello @JuliaK,

Your welcome, I am glad to hear that.

Here some additional support :

  • Try to change the priority settings to DMA_LOW_PRIORITY_LOW_WEIGHT.
  • Also, you can change the parameter TransferEventMode (specifies the transfer event mode for the DMA channel). You set it to DMA_TCEM_EACH_LL_ITEM_TRANSFER (TC event is generated at the end of each linked-list item and the HT event is generated at the half of each linked-list item). I advise you to set to  DMA_TCEM_LAST_LL_ITEM_TRANSFER (TC event is generated at the end of the last linked-list item and the HT event is generated at the half of the last linked-list item).

Best Regards,
Pierre

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.