cancel
Showing results for 
Search instead for 
Did you mean: 

Having trouble with ADC sequencing STM32H7

CLeo.1
Senior II

Hi everyone, hope everyone is doing well

I am trying to setup the STM32H743ZI's ADC for a 5 channel Continuous scan. The 5 scans will be used for

The Input voltage - Channel 0

The Input Current - Channel 1

The output Voltage - Chanel 2

The output Current - Channel 3

Internal Temperature Sensor - Channel 18

Essentially I just want the ADC to continuously scan these 5 channels and have the DMA transfer it into a buffer so it can be read by software. The way I want to read the buffers is via Polling as I am using a RTOS.

The problem I am having is that the EOS flag for the ADC and the DMA1 TC4 flag is setting but the data is not transferring into the DMA buffer. I see the data shifting around in the DR register in the ADC3 however.

I double checked the DMA, the memory address is within the right region of memory for the DMA to see. I have no idea what's happening has I never really use the ADC for a sequential scan before.

I was hopping if anyone can see if I mis-setup or if I have to do something. For example I am not using interrupts as I mention I am using polling, however I am using the DMA. Do I need to still enable the DMA4 TC interrupt still? Etc etc. I just want the ADC to scan forever along side the DMA transferring data and I poll in whenever I want.

Any suggestions are appreciated

The Low Level register that setups GPIO, DMA, ADC

void ADC_helper_function::enable(int * RxBuffer) {
 
	LL_DMA_InitTypeDef     ADC_Config_DMA;
	LL_GPIO_InitTypeDef    ADC_Config_GPIO;
	LL_ADC_InitTypeDef     ADC_Config_init;
	LL_ADC_REG_InitTypeDef ADC_Config_REG;
 
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOC);
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOF);
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_ADC3);
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
	LL_ADC_StructInit(&ADC_Config_init);
	LL_ADC_REG_StructInit(&ADC_Config_REG);
	LL_GPIO_StructInit(&ADC_Config_GPIO);
	LL_DMA_StructInit(&ADC_Config_DMA);
 
	//Configure GPIOA
	ADC_Config_GPIO.Mode       = LL_GPIO_MODE_ANALOG;
	ADC_Config_GPIO.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	ADC_Config_GPIO.Pin        = LL_GPIO_PIN_2 | LL_GPIO_PIN_3;
	ADC_Config_GPIO.Pull       = LL_GPIO_PULL_UP;
	ADC_Config_GPIO.Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	LL_GPIO_Init(GPIOC, &ADC_Config_GPIO);
 
	//Configure GPIOC
	ADC_Config_GPIO.Mode       = LL_GPIO_MODE_ANALOG;
	ADC_Config_GPIO.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	ADC_Config_GPIO.Pin        = LL_GPIO_PIN_9 | LL_GPIO_PIN_7;
	ADC_Config_GPIO.Pull       = LL_GPIO_PULL_UP;
	ADC_Config_GPIO.Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	LL_GPIO_Init(GPIOF, &ADC_Config_GPIO);
 
	ADC_Config_DMA.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
	ADC_Config_DMA.FIFOMode = LL_DMA_FIFOMODE_DISABLE;
	ADC_Config_DMA.MemBurst = LL_DMA_MBURST_SINGLE;
	ADC_Config_DMA.MemoryOrM2MDstAddress = (uint32_t)RxBuffer;
	ADC_Config_DMA.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
	ADC_Config_DMA.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
	ADC_Config_DMA.Mode = LL_DMA_MODE_CIRCULAR;
	ADC_Config_DMA.NbData = 5;
	ADC_Config_DMA.PeriphBurst = LL_DMA_PBURST_SINGLE;
	ADC_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&ADC1->DR;
	ADC_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_MDATAALIGN_WORD;
	ADC_Config_DMA.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
	ADC_Config_DMA.PeriphRequest = LL_DMAMUX1_REQ_ADC3;
	ADC_Config_DMA.Priority = LL_DMA_PRIORITY_LOW;
 
	//Interrupt: DMA
	LL_DMA_Init(DMA1, LL_DMA_STREAM_4, &ADC_Config_DMA);
 
	//Configure ADC
	ADC_Config_init.LeftBitShift    = LL_ADC_LEFT_BIT_SHIFT_NONE;
	ADC_Config_init.LowPowerMode    = LL_ADC_LP_MODE_NONE;
	ADC_Config_init.Resolution      = LL_ADC_RESOLUTION_16B;
	LL_ADC_Init(ADC3, &ADC_Config_init);
 
	ADC_Config_REG.ContinuousMode   = LL_ADC_REG_CONV_CONTINUOUS;
	ADC_Config_REG.DataTransferMode = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
	ADC_Config_REG.Overrun          = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
	ADC_Config_REG.SequencerLength  =  LL_ADC_REG_SEQ_SCAN_ENABLE_5RANKS;
	ADC_Config_REG.TriggerSource    =  LL_ADC_REG_TRIG_SOFTWARE;
	LL_ADC_REG_Init(ADC3, &ADC_Config_REG);
 
	LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
	LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_1);
	LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_2);
	LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_3);
	LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_5, LL_ADC_CHANNEL_TEMPSENSOR);
 
	LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_1 | LL_ADC_CHANNEL_2 | LL_ADC_CHANNEL_3, LL_ADC_SINGLE_ENDED);
 
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_0 | LL_ADC_CHANNEL_1 | LL_ADC_CHANNEL_2 | LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_1CYCLE_5);
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_TEMPSENSOR, LL_ADC_SAMPLINGTIME_387CYCLES_5);
 
	LL_ADC_SetCommonPathInternalCh(ADC3_COMMON, LL_ADC_PATH_INTERNAL_TEMPSENSOR);
 
	LL_ADC_DisableDeepPowerDown(ADC3);
 
	LL_RCC_PLL2_SetM(4);
	LL_RCC_PLL2_SetN(9);
	LL_RCC_PLL2_SetP(4);
	LL_RCC_PLL2_SetFRACN(6144);
	LL_RCC_PLL2_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_8_16);
	LL_RCC_PLL2_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
	LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLL2P);
	LL_RCC_PLL2FRACN_Enable();
	LL_RCC_PLL2P_Enable();
	LL_RCC_PLL2_Enable();
	while ((LL_RCC_PLL2_IsReady == 0)) {
 
	}
 
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_4);
	LL_ADC_EnableInternalRegulator(ADC3);
	LL_ADC_Enable(ADC3);
	LL_ADC_REG_StartConversion(ADC3);
}

The task that polls for the ADC buffer

void ADC::ADC_Daemon(void * parameter) {
 
	int tempSensor = 0;
 
	while(1) {
		vTaskDelay(ADC_Checkin_Interval/portTICK_PERIOD_MS);
 
		if ((LL_ADC_IsActiveFlag_EOS(ADC3)) && (LL_DMA_IsActiveFlag_TC4(DMA1))) {
 
			tempSensor = dataStructure->getRxBuffer()[3];
			LL_ADC_ClearFlag_EOS(ADC3);
			LL_DMA_ClearFlag_TC4(DMA1);
		}
 
	}
}

1 ACCEPTED SOLUTION

Accepted Solutions
CLeo.1
Senior II

Found the error. ADC is no longer OVR and DMA is transferring data. However the values of temp sensor is completely wrong

Issue:

ADC_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&ADC1->DR;

FIx:

ADC_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&ADC3->DR;

View solution in original post

13 REPLIES 13
jiangfan
ST Employee

the first suggestion (easy to do) is, to check if STM32H7 Cube FW lib has similar example, and do some update following the example.

Thank you for your reply. I have tried every example it didnt work for me. I have also tried using interrupts and didnt work. However I noticed that the Over run flag is being triggered on the ADC3 side. This could be the cause as to why it stops running. But why does it trigger?

I've tested H7s ADC back and forth and it works seamlessly with multiple channels and injected conversions, though I did not used LL drivers. 1) Ensure that DMA is initialized before ADC; 2) Ensure that DMA can access particular RAM address. If it still does not work, create simple project using full HAL and find the difference between LL one. It is probably that RxBuffer located in RAM which is not accessible by DMA, or DMA/ADC initialization somewhere performed in wrong order.

I agree with this idea, to check DMA operation, ADC_DR => buffer_RAM should be checked. still, I suggest to compare with DMA example(s) in​ STM32H7 Cube FW lib.

Thank you for the response.

  1. DMA is ensured that its initiated before the ADC peripheral.
  2. The address memory is accessible by the ADC as address region is 0x240000ac *D1 domain, AXI SRAM:*
  3. I have tried using examples and everytime my ADC ready flag never sets.
CLeo.1
Senior II

Found the error. ADC is no longer OVR and DMA is transferring data. However the values of temp sensor is completely wrong

Issue:

ADC_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&ADC1->DR;

FIx:

ADC_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&ADC3->DR;

in case the values of temp sensor is completely wrong, 2 points may be checked:

  • the sequence may not be the expected one. that is, input is channel A,B,C,D,E, but output (to buffer RAM) may be B,C,D,E,A
  • better to use longest sample time

Issue is actually rooting more than just the temperature sensor. I've made a new thread as this was more geared towards the over run issue with the ADC.

I want to understand point 1 more, as I am having troubles piecing it together.

Point 2, I have tried the slowest cycle and lowering the ADC clock as well, no avail.

I just try example in STM32Cube_FW_H7_V1.9.0 -> Projects\NUCLEO-H743ZI\Examples\ADC\ADC_InternalChannelConversion, and it (ADC_CHANNEL_VREFINT) works for me, I get 1221~1224mV for VREFINT. I modify to ADC_CHANNEL_TEMPSENSOR, and 624~629mV seen at room temperature 25~30 degree C.