2025-01-23 01:30 AM - edited 2025-01-23 01:37 AM
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); // :white_heavy_check_mark: 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
// :white_heavy_check_mark: 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)); // :white_heavy_check_mark: 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); // :white_heavy_check_mark: Wait for calibration
// 6-Enable ADC
ADC1->CR |= CR_ADEN;
while (!(ADC1->ISR & ISR_ADRDY)); // :white_heavy_check_mark: Wait for ADC Ready
// 7-Enable Channel 16 (PA0) in PCSEL
ADC1->PCSEL |= (1U << 16); // :white_heavy_check_mark: Must enable the channel in PCSEL
// 8-Configure ADC for Continuous Mode
ADC1->CFGR |= CFGR_CONT; // :white_heavy_check_mark: 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; // :white_heavy_check_mark: 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); // :white_heavy_check_mark: Wait for End-of-Conversion
if (timeout == 0) {
return 0xFFFFFFFF; // Error: Timeout occurred
}
ADC1->ISR |= ISR_EOC; // :white_heavy_check_mark: 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