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.
88 lines
2.9 KiB
C
88 lines
2.9 KiB
C
2 years ago
|
#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)
|
||
|
{
|
||
|
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
|
||
|
}
|
||
|
|