cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743ZI ADC + DMA sampling data acting quite strange

CLeo.1
Senior II

Hi everyone, hope all of you are doing well.

Posting here again about the ADC. I finally got the ADC out of that over run situation and now ready to acquire data!

The way the ADC is setup is by having 4 channels using Pins setup as continuous scanning

PC2 = Channel 0 for ADC 3 Rank 0

PC3 = Channel 1 for ADC 3 Rank 1

PF9 = Channel 2 for ADC 3 Rank 2

PF7 = Channel 3 for ADC 3 Rank 3

and internal temperature sensor = Channel 12 for ADC 3

I am essentially polling for the DMA1 Stream 4 Transfer Complete 4 flag, once this is done I know that the ADC has done all 5 sequential reads, however when I get into the array or even the data there's either nonsense data or zeros. The channels that read zero I have tried hooking up those pins to VDD and it still reads zero. I notice in the ADC3->DR there's a lot of activity going.

What I have tried thus far is, changing DMA to one shot mode, behavior is the same.

I am not sure what's going as it seems to look like the DMA is not transferring from ADC3->DR properly into its memory buffer. The buffer being used is in the x2400 region of memory and there's no error flag set for either DMA1 or ADC3.

ADC, GPIO, Low Level driver initialization

LL_DMA_InitTypeDef       ADC_Config_DMA;
	LL_GPIO_InitTypeDef      ADC_Config_GPIO;
	LL_ADC_InitTypeDef       ADC_Config_Init;
	LL_ADC_CommonInitTypeDef ADC_Config_CommonInit;
	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_CommonStructInit(&ADC_Config_CommonInit);
	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_DOWN;
	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_DOWN;
	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_HALFWORD;
	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)&ADC3->DR;
	ADC_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_MDATAALIGN_HALFWORD;
	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);
 
	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_CommonInit.CommonClock = LL_ADC_CLOCK_ASYNC_DIV1;
	ADC_Config_CommonInit.MultiDMATransfer = LL_ADC_MULTI_REG_DMA_EACH_ADC;
	ADC_Config_CommonInit.Multimode = LL_ADC_MULTI_INDEPENDENT;
	LL_ADC_CommonInit(ADC3_COMMON, &ADC_Config_CommonInit);
 
	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_PRESERVED;
	ADC_Config_REG.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
	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_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_0);
 
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_810CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_810CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_810CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_810CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_810CYCLES_5);
 
	LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_0, LL_ADC_SINGLE_ENDED);
	LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_1, LL_ADC_SINGLE_ENDED);
	LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_2, LL_ADC_SINGLE_ENDED);
	LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_3, LL_ADC_SINGLE_ENDED);
 
	LL_ADC_SetCommonPathInternalCh(ADC3_COMMON, LL_ADC_PATH_INTERNAL_TEMPSENSOR);
	LL_ADC_SetBoostMode(ADC3, LL_ADC_BOOST_MODE_50MHZ);
	LL_ADC_SetLowPowerMode(ADC3, LL_ADC_LP_MODE_NONE);
	//LL_ADC_SetOffsetSignedSaturation(ADC3, LL_ADC_OFFSET_4, LL_ADC_OFFSET_SIGNED_SATURATION_ENABLE);
	//LL_ADC_SetDataRightShift(ADC3, LL_ADC_OFFSET_4, LL_ADC_OFFSET_RSHIFT_ENABLE);
 
	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();
 
	LL_ADC_StartCalibration(ADC3, LL_ADC_CALIB_OFFSET, LL_ADC_SINGLE_ENDED);
	while (LL_ADC_IsCalibrationOnGoing(ADC3));
 
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_4);
	LL_ADC_DisableDeepPowerDown(ADC3);
	LL_ADC_EnableInternalRegulator(ADC3);
	LL_ADC_ClearFlag_ADRDY(ADC3);
	LL_ADC_Enable(ADC3);
	while (LL_ADC_IsActiveFlag_ADRDY(ADC3) == 0);
	LL_ADC_ClearFlag_ADRDY(ADC3);
	LL_ADC_REG_StartConversion(ADC3);

ADC Task to read 5 channels

void ADC::ADC_Daemon(void * parameter) {
 
	int tempSensor = 0;
	int inputVoltage = 0;
	int inputCurrent = 0;
	int outputVoltage = 0;
	int outputCurrent = 0;
 
	while(1) {
		vTaskDelay(ADC_Checkin_Interval / portTICK_PERIOD_MS);
 
		if (LL_DMA_IsActiveFlag_TC4(DMA1)) {
 
			inputVoltage = dataStructure->getRxBuffer()[0];
			inputCurrent = dataStructure->getRxBuffer()[1];
 
			outputVoltage = dataStructure->getRxBuffer()[2];
			outputCurrent = dataStructure->getRxBuffer()[3];
 
			tempSensor = (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP)/(*TEMPSENSOR_CAL2_ADDR - *TEMPSENSOR_CAL1_ADDR) * dataStructure->getRxBuffer()[4] + 30;
			LL_DMA_ClearFlag_TC4(DMA1);
		}
	}
}

Data within the buffer once Transfer Completed flag 4 from the DMA1 is triggered. Just pure nonsense data. The pins are pull down and not in used but here there's some kind of voltage.

0693W00000GW7jBQAT.jpg

1 ACCEPTED SOLUTION

Accepted Solutions
CLeo.1
Senior II

Fixed the issue for the analog pins for not reading the voltage properly.

Fix:

LL_ADC_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_0);

LL_ADC_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_1);

LL_ADC_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_2);

LL_ADC_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_3);

LL_ADC_SetChannelPreSelection(ADC3, LL_ADC_CHANNEL_TEMPSENSOR);

I dont know why this is the way to initialize the ADC as I thought the ranks you pick them will decide which channel to sample through. I also thought setChannelPreSelection was to choose which ADC channel gets sampled first. Anyways adding all the ADC channels you are going to use into setChannelPreSelection works.

View solution in original post

20 REPLIES 20

ADC can output wrong data if clock is set is too high, or differential inputs provided with non-differential signal (common mode voltage is not vref/2 and/or there is no 180deg phase difference between inputs). In your situation I just make separate project using full hall drivers, find difference and then search for explanation in reference manual. Just debug through both of your projects using F5 and compare differences. I have project on H7 with three ADC running in parallel, and a lot of other peripheral is running. It uses full HAL drivers though:

// ... I use BDMA for ADC3
hdma_adc3.Instance = BDMA_Channel0;
// ...
HAL_ADC_Start_DMA(&hadc3,(uint32_t *)adcBuffer2,ADC_BUFFER_SIZE);
// ... buffer is located at   RAM_D3 (xrw)   : ORIGIN = 0x38000000, LENGTH = 64K
uint16_t adcBuffer2[ADC_BUFFER_SIZE] __attribute__((section(".ram_d3_sram4")));
// ...
.ram_d3_sram4 :
{
  . = ALIGN(4);
  *(.ram_d3_sram4)
} >RAM_D3

I've used BDMA because DMA1/2 is already used heavily with ADC1/2, DAC and some timers with input capture.

Interesting, I heard about the clock speed causing errors as well. I lowered from 39MHz to 1MHz. Do I need to go lower?

I meant if you overclock it. For example, when using RevV firmware on RevY chip (AN5312, page 10: "On RevV the ADC kernel clock must be twice the frequency needed for the sampling (as an example, if a 25 MHz clock is needed, ADC kernel clock must be set to 50 MHz"). I would still recommend to start from working example (LL example from other series, F4 or maybe other), or making your own test project using full HAL driver. Make ADC/DMA register snapshots, find description in reference manual, see if there are some big differences.

jiangfan
ST Employee

data seen in buffer, this shows ADC_DR => buffer should work. it is suggested to simplify the project, for example, to convert one channel only per time. for LL_ADC_CHANNEL_TEMPSENSOR, I see 625~630mV at room temperature. for LL_ADC_CHANNEL_VREFINT, I see 1221~1224mV.

Yup, just converted the ADC3 to a 1 channel scan with the temperature sensor enabled only. I am seeing now 0.0076539251V on the channel

equation used (ADC->DR)/(2^16-1/3.3). Is this normal operation?

@jiangfan​ Figured out the issue but not the solution yet. Its the DMA transferring the data. I see the data within the ADC-DR register correctly however when the data is transferred in to the array buffer its completely different.

For example ADC3->DR = 0x30B0, and in Buffer = 0xA5. No idea whats going on here

if you worry about the issue during DMA transfer, you may have a look at ADC DMA example, for example, STM32Cube_FW_H7_V1.9.0 -> Projects\NUCLEO-H743ZI\Examples\ADC\ADC_DMA_Transfer. compare the difference.

 DmaHandle.Init.PeriphInc      = DMA_PINC_DISABLE;

 DmaHandle.Init.MemInc       = DMA_MINC_ENABLE;

 DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

 DmaHandle.Init.MemDataAlignment  = DMA_MDATAALIGN_HALFWORD;

Thanks to you I found the error.

Problem line:

ADC_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_MDATAALIGN_HALFWORD;

Fix:

ADC_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;

Is it normal to see an offset on those pins when they are pull down?