#include // Initializes the ADC for reading from channel one. void adc_init(void); // Does an ADC conversion on channel one and returns the result. unsigned int adc_read(void); int main(void) { adc_init(); // DAC initialization: // Enables DAC channel one on PA4 // Does not configure a hardware or software trigger, so DAC updates // when its data register is written. RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; RCC->APB1ENR1 |= RCC_APB1ENR1_DAC1EN; DAC1->CR = DAC_CR_EN1; // Execute a first-order IIR filter in a loop: y[n] = a*y[n-1] + b*x[n] const float a = 0.5f; const float b = 0.5f; unsigned int yprev = 0; // y[n-1] while (1) { unsigned int x = adc_read(); unsigned int y = a * yprev + b * x; DAC1->DHR12R1 = y; yprev = y; // Add some delay between samples for (int i = 0; i < 100; ++i); } } void adc_init(void) { // Use the CCIPR register to select the system clock as a source for the // ADC clock, then enable the ADC clock through the AHB. RCC->CCIPR &= ~(RCC_CCIPR_ADCSEL_Msk); RCC->CCIPR |= 3 << RCC_CCIPR_ADCSEL_Pos; RCC->AHB2ENR |= RCC_AHB2ENR_ADCEN; // Enable the ADC's internal voltage regulator, with a delay for its // startup time. // ADVREGEN requires some bits in the CR register to be zero before being // set, so we clear CR to zero first. ADC1->CR = 0; ADC1->CR = ADC_CR_ADVREGEN; for (int i = 0; i < 100; ++i); // Clear DIFSEL to do single-ended conversions. ADC1->DIFSEL = 0; // Begin a single-ended calibration and wait for it to complete. // If the clock source for the ADC is not correctly configured, the // processor may get stuck in the while loop. ADC1->CR &= ~(ADC_CR_ADCALDIF); // Select single-ended calibration ADC1->CR |= ADC_CR_ADCAL; while ((ADC1->CR & ADC_CR_ADCAL) != 0); // Enable the ADC and wait for it to be ready. ADC1->CR |= ADC_CR_ADEN; while ((ADC1->ISR & ADC_ISR_ADRDY) == 0); // GPIO pin PC3 will be used as our ADC input. // In order: enable the GPIOC clock, set PC3 to analog input mode, // connect ADC channel one to its GPIO pin (PC3). RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN; GPIOC->MODER |= 3 << GPIO_MODER_MODE0_Pos; GPIOC->ASCR |= 1 << 0; // Make channel one the first in the ADC's read sequence. // Using '=' here also clears the L field of the register, giving the // sequence a length of one. ADC1->SQR1 = 1 << ADC_SQR1_SQ1_Pos; } unsigned int adc_read(void) { ADC1->CFGR &= ~(ADC_CFGR_CONT); // Single conversion ADC1->CR |= ADC_CR_ADSTART; // Start converting while ((ADC1->ISR & ADC_ISR_EOC) == 0); // Wait for end-of-conversion ADC1->ISR &= ~(ADC_ISR_EOC); // Clear the status flag return ADC1->DR; // Read the result }