2025-10-27 5:06 PM - last edited on 2025-11-03 10:33 PM by Imen.D
Hi, I'm having really a hard time setting up my ADCs to work with DMA using the STM32F429I-DISC1. My objective:
have a clock source from a Timer at a given frequency as trigger to the ADCs, each ADC would then read a specific pin (let's say ADC1-> pin_x; ADC2->pin_y and ADC3->pin_z). Since nothing is working regardless of the configuration I set, I'm trying with very big time intervals to avoid busy bus and so on. So I've set the TIMER8 to have a period of 5s and tested it individually with the interrupt enabled to make sure it is triggering and having the correct interval. Now the idea is to trigger the ADCs to perform a few conversions of the pins in order to do some oversampling.
Here is my ADCs configurations:
The ADC3 is identical to ADC2. Timer8 Trigger Event Selection is set to "Update Event".
Here is my main function:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_CRC_Init();
MX_I2C3_Init();
MX_SPI5_Init();
MX_FMC_Init();
MX_LTDC_Init();
MX_DMA2D_Init();
MX_ADC2_Init();
MX_ADC3_Init();
MX_ADC1_Init();
MX_USB_OTG_HS_HCD_Init();
MX_TIM8_Init();
MX_TouchGFX_Init();
/* Call PreOsInit function */
MX_TouchGFX_PreOSInit();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of GUI_Task */
GUI_TaskHandle = osThreadNew(TouchGFX_Task, NULL, &GUI_Task_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
My ADC initializing functions:
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_TRIPLEMODE_REGSIMULT;
multimode.DMAAccessMode = ADC_DMAACCESSMODE_1;
multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, test, 3) != HAL_OK)
//if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, test, 5) != HAL_OK)
//if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&(adc_L.val), 1) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
/* USER CODE END ADC1_Init 2 */
}
/**
* @brief ADC2 Initialization Function
* None
* @retval None
*/
static void MX_ADC2_Init(void)
{
/* USER CODE BEGIN ADC2_Init 0 */
/* USER CODE END ADC2_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC2_Init 1 */
/* USER CODE END ADC2_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC2_Init 2 */
//if(HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&(adc_R.val), 1) != HAL_OK)
if(HAL_ADC_Start(&hadc2) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
/* USER CODE END ADC2_Init 2 */
}
/**
* @brief ADC3 Initialization Function
* None
* @retval None
*/
static void MX_ADC3_Init(void)
{
/* USER CODE BEGIN ADC3_Init 0 */
/* USER CODE END ADC3_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC3_Init 1 */
/* USER CODE END ADC3_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC3_Init 2 */
//if(HAL_ADC_Start_DMA(&hadc3, (uint32_t*)&(adc_M.val), 1) != HAL_OK)
if(HAL_ADC_Start(&hadc3) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
/* USER CODE END ADC3_Init 2 */
}
Timer8 initializing function:
static void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM8_Init 1 */
/* USER CODE END TIM8_Init 1 */
htim8.Instance = TIM8;
htim8.Init.Prescaler = 14399;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 49999;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
HAL_TIM_Base_Start(&htim8);
/* USER CODE END TIM8_Init 2 */
}
Problem is: it only fires HAL_ADC_ConvCpltCallback once at the beginning of the debugging and not anymore. My "test" variable is defined as "uint32_t test[3];".
I've already tried almost all combinations possible... tried changing EOC flag to "end of all conversions", tried circular and normal DMA modes, tried also some other ADC modes (continuous, injected, scan etc) but I only get one of the following results:
- either the callback keeps getting inifintely called blocking the rest of the application and leading to ADC overrun or;
- it triggers once and never more
Also pausing the debug session shows me that the results stored on the test variable are the ones from the beginning of the session, regardless if I change the voltage level ate the ADC pins. What am I missing here?
2025-11-05 12:05 AM
As I understand it, each individual result in a dual/triple sequence overwrites the previous one in CDR.
> (besides maybe explicitely mentioning ADC_CDR[31:16] = ADC_CDR[15:0])? The way it is written seems it overwrites the whole 32bit ADC_CDR with the new ADCx_DR, reseting the upper 32:16 bits.
Read the statement for Triple Mode nearby. No way the 3 x 12 = 36 bit from the individual ADCs fit into one 32-bit register.
I would think the RM is not explicit enough here.
OTOH, as I said, I never used dual or triple mode.
2025-11-05 8:09 AM
Hi @feliperibas;
After some tinkering here I think I have a working example. I have a spare STM32f44 Nucleo board, and I wanted to try to set up a simple isolated test apart from my main project to see if I could understand how this works.
(forgive me if I overexplain anything here, this is me trying to verify what I think I've learned more than anything else)
For me, there are two things to wrap my head around: there's the triggering of the ADC by the timer as we want, then there's the issue how to properly read the data we want.
I found that configuring my ADC1/2/3 as follows produces the right timing. There are two parameters that were a bit confusing for me here:
I think the EOC flag in this case might be a little irrelevant, as I am only performing a single conversion per ADC anyway.
I didn't see the actual code you're using to start/read the ADCs (those seem to be in a separate task function somewhere for you?), but here is what I am doing to actually start the ADCs:
// Start the two slave ADCs first
HAL_ADC_Start(&hadc2);
HAL_ADC_Start(&hadc3);
// Then we start the master thusly:
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *) adc_vals, 3);
// The above "arms" all of the ADCs. To begin triggering
// conversions, we start our timer channel:
HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_4);
Next, we can turn our attention to how to read the results. Here's what I have:
// These three variable will hold our raw ADC values
volatile uint16_t adc_1_val;
volatile uint16_t adc_2_val;
volatile uint16_t adc_3_val;
// This array will be 'fed' by the DMA owned by ADC1:
volatile uint32_t adc_vals[3];
// Here we implement our conversion complete callback. We
// anticipate this will be called once per timer trigger.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1) {
// What do we do when we enter this callback?
// The answer here is a little funky, and dependent
// on how many ADCs we expect to be reading from.
//
// In Dual Simultaneous mode, this seems to work:
//
// uint32_t packed_value = HAL_ADCEx_MultiModeGetValue(&hadc1);
// adc_1_val = (uint16_t)( packed_value & 0xFFFF );
// adc_2_val = (uint16_t)((packed_value >> 16) & 0xFFFF );
//
// This at first seems a little confusing. In Dual mode, we would have
// started ADC1 like:
//
// HAL_ADC_Start(&hadc2);
// HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *) adc_vals, 1);
//
// Where adc_vals is:
//
// volatile uint32_t adc_vals[1];
//
// It's odd to me that we need to create and pass this array
// to the HAL_ADCEx_MultiModeStart_DMA() command, but then we
// never seem to need to use it directly. Instead, we have to
// use this HAL_ADCEx_MultiModeGetValue() function to retrieve
// these values and "unpack" them.
//
// However, I find things work quite differently in Triple mode.
// For Triple mode, we do:
//
// HAL_ADC_Start(&hadc2);
// HAL_ADC_Start(&hadc3);
// HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *) adc_vals, 3);
//
// Where adc_vals is:
//
// volatile uint32_t adc_vals[3];
//
// We can then successfully read our three simultaneous ADC values
// like this:
adc_1_val = adc_vals[0];
adc_2_val = adc_vals[1];
adc_3_val = adc_vals[2];
// The reason for this discrepency is in the "DMA Mode" that's
// used in Dual mode vs. Triple mode. In Dual Mode, we're forced
// to use DMA Mode 2, which - from the reference manual, is:
// DMA mode 2: On each DMA request (two data items are available) two half-
// words representing two ADC-converted data items are transferred as a word.
// In Dual ADC mode, both ADC2 and ADC1 data are transferred on the first request
// (ADC2 data take the upper half-word and ADC1 data take the lower half-word) and
// so on.
// In Triple ADC mode, three DMA requests are generated. On the first request, both
// ADC2 and ADC1 data are transferred (ADC2 data take the upper half-word and
// ADC1 data take the lower half-word). On the second request, both ADC1 and
// ADC3 data are transferred (ADC1 data take the upper half-word and ADC3 data
// take the lower half-word).On the third request, both ADC3 and ADC2 data are
// transferred (ADC3 data take the upper half-word and ADC2 data take the lower
// half-word) and so on.
// DMA mode 2 is used in interleaved mode and in regular simultaneous mode (for
// Dual ADC mode only).
// Note the end there - it says this DMA Mode 2 is only used in DUAL ADC MODE.
// For Triple mode, we're forced by cubeMX to choose DMA Mode 1:
// DMA mode 1: On each DMA request (one data item is available), a half-word
// representing an ADC-converted data item is transferred.
// In Triple ADC mode, ADC1 data are transferred on the first request, ADC2 data
// are transferred on the second request and ADC3 data are transferred on the third
// request; the sequence is repeated. So the DMA first transfers ADC1 data followed
// by ADC2 data followed by ADC3 data and so on.
// DMA mode 1 is used in regular simultaneous triple mode only.
// So, in the end, the RM is telling us that the way we need to read
// these values is quite different depending on whether we're doing dual
// or triple mode conversions.
// Whew, ok. Anyway, let's toggle a pin to let ourselves know
// we're here. We can scope this to verify the timing of this callback.
HAL_GPIO_TogglePin( TEST_PIN_GPIO_Port,
TEST_PIN_Pin);
HAL_GPIO_TogglePin( LD2_GPIO_Port,
LD2_Pin);
}
}Unfortunately, the example projects mentioned by @TDK don't make this "how to read the data" part super clear - those examples are useful to understand how to configure the ADCs, but understanding how to get at the data they are converting is a little cloudier. And as you say, the comments/documentation within the ADCex HAL files also make understanding this a bit convoluted.
Next step is for me to try to take this back to my original project to verify it works there. But, I wanted to leave all of this in case it's helpful.
2025-11-23 1:00 PM
So, I had given up using DMA and was just using Regular Simultaneous Triple Mode but manually starting ADC2 and ADC3 with HAL_ADC_Start() and ADC1 after them with HAL_ADC_Start_IT() and having one callback for ADC1 where I would read all ADCs at once. ADC1 was being triggered by a Timer. This used to work until i had some unexpected behavior: pulling ADC3 pin to 3v crashed my application. I noticed that both ADC1 and ADC2 behaved like open-drain without pull-up or pull-down and I could connect to anything between 0v and 3v without problems. ADC3 however, despite all the same configuration (no pull-ups or downs, open-drain, analog mode etc), was behaving as if it would have a pull-down, giving always 0 when not connected and measuring 3v difference to the 3v pin. When pulling it to 3v, the ADC would capture this 3v for a few miliseconds and then the application would break or exhibit some unexpected behavior. I thought it was a HW problem but I think it was due to the fact i was accessing ADC3 registers when using Triple Simultaneous mode, where the user is supposed to read stuff from the common register. So I went back to the Regular Simultaneous Triple Mode with DMA, and now it works! Finally. So here is what I figured it out:
complementing to some stuff pointer out by @sb_st :
- Continuous conversion mode is an ADC setting which decouples the ADC from the timmer, starting the next conversion immediately after the last one. As I was using the timer to make sure my intervals were constant, this must be disabled;
- DMA Continuous requests: this is also an ADC setting and it tells the ADC module to keep sending DMA requests uninterruptly, which is in my case also not desired as I only want to transfer the data once at end of conversion and wait for the next timer trigger. So also disabled
- DMA Circular mode: this one fills up the number of conversions to be made after finishing the last sequence. I left it disabled.
Now the important things:
1) quote from the manual RM0090 Page 403 talking about DMA Continuous Requests: "... the DMA bit is
not cleared by hardware. It must be written to 0, then to 1 to start a new transfer.". I am not sure if this is the DMA bit the manual is talking about but I put these two lines on my ADC Conversion Callback:
ADC123_COMMON->CCR &= 0xFFFF3FFF;
ADC123_COMMON->CCR |= 0x00004000;Which disables and enables DMA Mode from Common ADC Register. Commenting out these lines makes my code not work again.
2) Besides re-setting the DMA mode on the common ADC register, I also appended the following lines afterwards (still inside the ADC conv callback):
DMA2_Stream0->CR |= 0x10;
DMA2_Stream0->CR |= 0x01;Which re-enables the DMA Interrupt on my stream as well as enabling it again.
3) I enabled the ADCs global interrupts (NVIC) so the auto-generated code from STM32CubeIDE/MX calls the ADC interrupt handlers which check for OVR flag. In case overrun occurs, this also blocks further readings. This can be done directly on the registers as well but I used the callbacks so I only go through the handlers if an overrun occrus.
4) I've set up my global variable as an array of 3 elements of uint32_t. Then DMA set as "word" for peripheral and memory, only increment on memory. This setting is also probably better using "half-word" with different parameters, since ADC results are 16 bit but I decided to use 32bit as ADC_CDR is 32bit and it is working, so I havent tested with "half-word" yet.
Now my code works as expected, but I confess I still dont understand who re-fills my DMA->NDTR, since i am clearly not doing that manually after triggering the conversions during intialization (only once). But it does work somehow.
Here is the rest of my code.
Initialization:
int main(void)
{
...
MX_GPIO_Init();
MX_DMA_Init();
...
MX_ADC2_Init();
MX_ADC3_Init();
MX_ADC1_Init();
MX_TIM8_Init(); // <- used for trigger
...
}void MX_ADC2_Init(void)
{
/* USER CODE BEGIN ADC2_Init 0 */
/* USER CODE END ADC2_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC2_Init 1 */
/* USER CODE END ADC2_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC2_Init 2 */
if(HAL_ADC_Start(&hadc2) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
__HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_OVR);
/* USER CODE END ADC2_Init 2 */
}void MX_ADC3_Init(void)
{
/* USER CODE BEGIN ADC3_Init 0 */
/* USER CODE END ADC3_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC3_Init 1 */
/* USER CODE END ADC3_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC3_Init 2 */
if(HAL_ADC_Start(&hadc3) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_OVR);
/* USER CODE END ADC3_Init 2 */
}void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_TRIPLEMODE_REGSIMULT;
multimode.DMAAccessMode = ADC_DMAACCESSMODE_1;
multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, adc_val, 3) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler();
}
/* USER CODE END ADC1_Init 2 */
}
uint32_t adc_val[3];
void HAL_ADC_ConvCpltCallback (ADC_HandleTypeDef * hadc)
{
...
//USE adc_val[3] HERE
...
ADC123_COMMON->CCR &= 0xFFFF3FFF;
ADC123_COMMON->CCR |= 0x00004000;
DMA2_Stream0->CR |= 0x10;
DMA2_Stream0->CR |= 0x01;
}
I hope this helps, but I would still be glad if someone could explain who is filling up my NDTR every round, as I havent found this info in the manual.
Cheers