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.

90 lines
2.9 KiB
C

#include <stm32l476xx.h>
// Reads the configured ADC channel and returns its value.
unsigned int adc_read(void);
// Delays for approximately the given amount of microseconds.
void delay_us(int);
int main(void)
{
// Disable interrupts
asm("cpsid i");
// 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;
delay_us(20);
// 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;
// Configure the Nucleo's LED (pin PA5) for output.
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
GPIOA->MODER &= ~(GPIO_MODER_MODE5_Msk);
GPIOA->MODER |= 1 << GPIO_MODER_MODE5_Pos;
// In a loop, read from the ADC channel and set the LED according to
// the result.
while (1) {
unsigned int reading = adc_read();
if (reading > 2048)
GPIOA->BSRR |= 1 << 5;
else
GPIOA->BRR |= 1 << 5;
}
}
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
}
void delay_us(int amount)
{
// This loop should take around a microsecond to execute per iteration
// when running at the default speed of 4MHz.
while (amount > 0) {
asm volatile("nop");
--amount;
}
}