2025-01-23 1:30 AM - last edited on 2025-02-12 3:34 AM by Amel NASRI
Issue Description:
I am trying to configure ADC1 on PA0 (Channel 16) in continuous mode on the STM32H750 microcontroller. However, the code gets stuck at:
while (!(ADC1->ISR & ISR_EOC) && --timeout); // Wait for End-of-Conversion
This suggests that EOC (End-of-Conversion) is never set, meaning the ADC is not completing a conversion.
*What I Have Done:
-Enabled clocks for GPIOA and ADC1 (RCC->AHB4ENR and RCC->AHB1ENR)
-Configured PA0 as Analog Mode (GPIOA->MODER |= (3U << 0);)
-Set the correct ADC clock source in CCR
-Exited deep power-down mode and enabled the ADC voltage regulator
-Performed ADC calibration and waited for completion
-Enabled ADC and waited for ISR_ADRDY (ADC Ready flag)
-Enabled PA0 in PCSEL
-Set CFGR_CONT for continuous mode
-Configured SQR1 for a single-channel sequence (Channel 16, Rank 1)
-Set maximum sampling time for PA0 (SMPR1)
-Started ADC conversion (CR_ADSTART)
-Full Code:
#include "stm32h7xx.h"
#include <stdint.h>
// Define Register Bit Masks
#define GPIOAEN (1U << 0) // Enable GPIOA Clock
#define ADC1EN (1U << 5) // Enable ADC1 Clock
#define CR_ADVREGEN (1U << 28) // Voltage Regulator Enable
#define CR_DEEPPWD (1U << 29) // Deep Power-Down Disable
#define ADC_CALIBRATION (1U << 31) // ADC Calibration
#define CFGR_CONT (1U << 13) // Continuous Mode Enable
// Define ADC Control Register Bits
#define CR_ADEN (1U << 0) // ADC Enable Bit
#define CR_ADSTART (1U << 2) // ADC Start Conversion
#define CR_ADDIS (1U << 1) // ADC Disable Bit
// Define ADC Status Register Bits
#define ISR_EOC (1U << 2) // End of Conversion Flag
#define ISR_ADRDY (1U << 0) // ADC Ready Flag
#define ISR_LDORDY (1U << 12) // ADC LDO Ready Flag
// Define ADC Channel Configuration
#define CH16_RANK1 (16U << 6) // Channel 16 (PA0) as Rank 1
volatile uint32_t adc_value; // Store ADC result
// Function to Initialize ADC for PA0 (Channel 16) in Continuous Mode
void adc1_init(void) {
// 1-Enable Clocks for GPIOA and ADC1
RCC->AHB4ENR |= GPIOAEN; // Enable GPIOA Clock
RCC->AHB1ENR |= ADC1EN; // Enable ADC1 Clock
// Ensure ADC is using the correct clock
ADC12_COMMON->CCR &= ~(0b11 << 16); // Clear clock source
ADC12_COMMON->CCR |= (0b01 << 16); // Select AHB clock for ADC
//2-Configure PA0 as Analog Mode
GPIOA->MODER |= (3U << 0); // Set PA0 to Analog Mode
GPIOA->PUPDR &= ~(3U << 0); // No Pull-up, No Pull-down
// 3-Exit Deep Power Down & Enable Voltage Regulator
ADC1->CR &= ~CR_DEEPPWD; // Exit deep power-down
ADC1->CR |= CR_ADVREGEN; // Enable voltage regulator
while (!(ADC1->ISR & ISR_LDORDY)); // Wait for LDO Ready
// 4-Ensure ADC is Disabled Before Calibration
if (ADC1->CR & CR_ADEN) {
ADC1->CR |= CR_ADDIS; // ADC Disable
while (ADC1->CR & CR_ADEN);
}
// 5-Start Calibration
ADC1->CR |= ADC_CALIBRATION;
while (ADC1->CR & ADC_CALIBRATION); // Wait for calibration
// 6-Enable ADC
ADC1->CR |= CR_ADEN;
while (!(ADC1->ISR & ISR_ADRDY)); // Wait for ADC Ready
// 7-Enable Channel 16 (PA0) in PCSEL
ADC1->PCSEL |= (1U << 16); // Must enable the channel in PCSEL
// 8-Configure ADC for Continuous Mode
ADC1->CFGR |= CFGR_CONT; // Enable Continuous Mode
ADC1->SQR1 &= ~(0xF << 0); // Clear sequence length
ADC1->SQR1 |= CH16_RANK1; // Set Channel 16 (PA0) as Rank 1
// 9-Set Correct Sampling Time for Channel 16
ADC1->SMPR1 |= (0b111 << 18); // Max sampling time for PA0
// 10-Start ADC Conversion (Only if ADC is Ready)
ADC1->ISR |= ISR_EOC; // Clear any previous EOC flag
ADC1->CR |= CR_ADSTART;
}
// Function to Read ADC Value
uint32_t adc_read(void) {
uint32_t timeout = 1000000; // Prevent infinite loop
while (!(ADC1->ISR & ISR_EOC) && --timeout); // Wait for End-of-Conversion
if (timeout == 0) {
return 0xFFFFFFFF; // Error: Timeout occurred
}
ADC1->ISR |= ISR_EOC; // Clear EOC flag to allow next conversion
return ADC1->DR; // Read ADC value
}
int main(void) {
adc1_init(); // Initialize ADC1 for Continuous Conversion on PA0
while (1) {
adc_value = adc_read(); // Continuously Read ADC
}
}
*****What I Need Help With***
Why does the ADC get stuck at while (!(ADC1->ISR & ISR_EOC));?
Is ADSTART getting cleared automatically after the first conversion?
Should I manually restart ADSTART?
Is PCSEL being set correctly before conversion starts?
I am enabling PCSEL (ADC1->PCSEL |= (1U << 16);) before configuring the sequence.
Are there any additional settings required in CCR, CFGR, or SMPR?
Should I explicitly configure DIFSEL or LTR registers?
Are there better debugging methods to check if the ADC is running?
Should I use an oscilloscope to check PA0 input?
Are there any status bits I should read to confirm ADC is running?
******System Information*****
Microcontroller: STM32H750
IDE: STM32CubeIDE
Debugger: ST-LINK V2
Edit: code formatting
2025-03-07 6:01 AM
Hello,
To speed up the process, I suggest you to use HAL: create your example with CubeMx and generate the code. Then inspire from that implementation to do the direct access to the registers. It's difficult to follow each line and check each shift from your code.