#define RCC_BASE 0x40021000
#define ADC_BASE 0x40012400

#define RCC_APBENR2 (*((volatile uint32_t*)(RCC_BASE + 0x40)))
#define ADC_ISR (*((volatile uint32_t*)(ADC_BASE + 0x00)))
#define ADC_CR (*((volatile uint32_t*)(ADC_BASE + 0x08)))
#define ADC_CFGR2 (*((volatile uint32_t*)(ADC_BASE + 0x10)))
#define ADC_SMPR (*((volatile uint32_t*)(ADC_BASE + 0x14)))
#define ADC_CHSELR (*((volatile uint32_t*)(ADC_BASE + 0x28)))
#define ADC_DR (*((volatile uint32_t*)(ADC_BASE + 0x40)))
#define ADC_CFGR1 (*((volatile uint32_t*)(ADC_BASE + 0x0C)))

#define RCC_APBENR2_ADCEN (1<<20)
#define ADC_CR_ADEN       (1<<0)
#define ADC_CR_ADCAL      (1<<31)
#define ADC_CFGR2_CKMODE  (1<<30)
#define ADC_CHSELR_CHSEL0 (1<<0)
#define ADC_CHSELR_CHSEL1 (1<<1)
#define ADC_CHSELR_CHSEL2 (1<<2)
#define ADC_SMPR_SMP_1_5  0       //(0<<0);
#define ADC_CR_ADSTART    (1<<2)
#define ADC_ISR_ADRDY     (1<<0)
#define ADC_ISR_EOC       (1<<2)
#define AWD1CH0            (0x0<<26)
#define AWD1CH1            (0x1<<26)
#define AWD1CH2            (0x2<<26)
#define AWD1EN            (1<<23)
#define AWD1SGL           (1<<22)
#define CHSELRMOD         (1<<21)
#define CONT              (1<<13)
#define EXTEN             (3<<10)
#define SCANDIR           (1<<2)
#define CCRDY             (1<<13)
#define SMPSEL0           (1<<8)
#define SMPSEL1           (1<<9)
#define SMPSEL2           (1<<10)
#define RES               (3<<3)
#define ALIGN               (1<<5)

void ADC_Init(void) {
    // Enable ADC clock
    RCC_APBENR2 |= RCC_APBENR2_ADCEN;
    ADC_CR |= ADC_CR_ADDIS;//disable ADC
    while (ADC_CR & ADC_CR_ADDIS);
    ADC_CR |= ADC_CR_ADCAL;         // Start calibration
    while (ADC_CR & ADC_CR_ADCAL);  // Wait for calibration to complete

    // Configure ADC clock mode (HCLK/1)
    ADC_CFGR2 &= ~ADC_CFGR2_CKMODE;
    // Configure sampling time for all channels (use SMP1 setting)
    ADC_SMPR = SMPSEL0 | SMPSEL1 | SMPSEL2;  // Use SMP1 for all channels
    ADC_SMPR |= (0x0 << 0);                  // Sampling time: 1.5 cycles

    // Disable continuous conversion, enable single conversion
    ADC_CFGR1 &= ~CONT;        // Single conversion mode
    ADC_CFGR1 &= ~SCANDIR;     // Forward scan direction
    ADC_CFGR1 &= ~CHSELRMOD;   // Non-configurable sequencer

    // Enable ADC
    ADC_CR |= ADC_CR_ADEN;
    while (!(ADC_ISR & ADC_ISR_ADRDY)); // Wait until ADC is ready
}


uint16_t ADC_Read(uint8_t channel) {
    // Ensure ADC is not running a conversion
    if (ADC_CR & ADC_CR_ADSTART) {
        ADC_CR |= ADC_CR_ADSTP; // Stop any ongoing conversion
        while (ADC_CR & ADC_CR_ADSTART); // Wait for stop
    }
    ADC_CHSELR = (1 << channel);
    while (!(ADC_ISR & CCRDY));
    ADC_ISR |= CCRDY; // Clear CCRDY flag
    // Start conversion
    ADC_CR |= ADC_CR_ADSTART;
    while (!(ADC_ISR & ADC_ISR_EOC));
    return ADC_DR;
}


int main(void) {
    Serial.begin(115200);
    ADC_Init();

    while (1) {
        uint16_t adc_value_ch0 = ADC_Read(0); // Channel 0
        uint16_t adc_value_ch1 = ADC_Read(1); // Channel 1
        uint16_t adc_value_ch2 = ADC_Read(2); // Channel 2

        Serial.print("value1:");
        Serial.println(adc_value_ch0);
        Serial.print("value2:");
        Serial.println(adc_value_ch1);
        Serial.print("value3:");
        Serial.println(adc_value_ch2);

        for (volatile int i = 0; i < 100000; i++);
    }
}