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
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;
|
|
}
|
|
}
|
|
|