You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.2 KiB
C

#include <stm32l476xx.h>
// 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)
{
while ((RCC->CR & 0x00000002) == 0) {;} // wait till MSI is ready
FLASH->ACR |= 2; // Add two wait-states for 48MHz operation
RCC->CR = (RCC->CR & ~0x000000F0) | 0x000000B0 | RCC_CR_MSIRGSEL; // Switch to 48 MHz clock
while ((RCC->CR & 0x00000002) == 0) {;} // wait till MSI is ready
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
}