// Experimento 2 - Conversão AD
// Indicador analógico em barra a partir de potenciômetro
// Placa: STM32 Nucleo-32 L031K6
// Configuração 100% por registradores (RCC, GPIO, ADC), sem analogRead/delay.
// Pinos de saída da barra de LED (8 segmentos), na ordem visual 1..8
const uint8_t pinos_barra[8] = {1, 3, 4, 5, 6, 7, 8, 9};
// PA1, PA3, PA4, PA5, PA6, PA7, PA8, PA9 (serigrafia A1, A2, A3, A4, A5, A6, D9, D1)
// Entrada analógica: PA0 (serigrafia A0) = canal ADC_IN0
#define N_AMOSTRAS 8 // tamanho da janela da média móvel (estabilização)
#define CICLOS_ENTRE_AMOSTRAS 20000 // laço de espera = "temporizador" simples de amostragem periódica
uint16_t buffer_amostras[N_AMOSTRAS];
uint8_t indice_amostra = 0;
uint8_t amostras_validas = 0;
uint32_t soma_amostras = 0;
void configurarGPIO() {
RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
// PA0 em modo analógico (11), sem pull-up/pull-down
GPIOA->MODER |= (0x3UL << (0 * 2));
GPIOA->PUPDR &= ~(0x3UL << (0 * 2));
// Pinos da barra de LED como saída digital push-pull
for (uint8_t i = 0; i < 8; i++) {
uint8_t pin = pinos_barra[i];
GPIOA->MODER &= ~(0x3UL << (pin * 2));
GPIOA->MODER |= (0x1UL << (pin * 2)); // 01 = saída
GPIOA->OTYPER &= ~(1UL << pin); // push-pull
GPIOA->PUPDR &= ~(0x3UL << (pin * 2));
}
}
void configurarADC() {
// 1) Habilita o clock do periférico ADC
RCC->APB2ENR |= RCC_APB2ENR_ADCEN;
// 2) Clock do ADC = PCLK/2
ADC1->CFGR2 |= ADC_CFGR2_CKMODE_0;
// 3) Tempo de amostragem máximo disponível (mais estável, menos sensível a ruído)
ADC1->SMPR |= ADC_SMPR_SMP;
// 4) Calibração (recomendada pelo fabricante antes de habilitar o ADC)
ADC1->CR |= ADC_CR_ADCAL;
while ((ADC1->CR & ADC_CR_ADCAL) != 0) { }
// 5) Seleciona o canal 0 (PA0)
ADC1->CHSELR = ADC_CHSELR_CHSEL0;
// 6) Habilita o ADC e aguarda ficar pronto para converter
ADC1->ISR |= ADC_ISR_ADRDY; // limpa a flag de "pronto"
ADC1->CR |= ADC_CR_ADEN;
while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) { }
}
// Dispara uma conversão única e espera o resultado (resolução padrão: 12 bits, 0-4095)
uint16_t lerADC() {
ADC1->CR |= ADC_CR_ADSTART;
while ((ADC1->ISR & ADC_ISR_EOC) == 0) { }
return ADC1->DR;
}
void setup() {
configurarGPIO();
configurarADC();
for (uint8_t i = 0; i < N_AMOSTRAS; i++) buffer_amostras[i] = 0;
}
void loop() {
// "Temporizador" simples por contagem de laço, para amostrar periodicamente
// sem usar delay() nem configurar um timer de hardware.
static uint32_t contador = 0;
contador++;
if (contador < CICLOS_ENTRE_AMOSTRAS) return;
contador = 0;
// ---- Amostragem ----
uint16_t leitura = lerADC();
// ---- Estabilização: média móvel curta (janela de N_AMOSTRAS) ----
soma_amostras -= buffer_amostras[indice_amostra];
buffer_amostras[indice_amostra] = leitura;
soma_amostras += leitura;
indice_amostra = (indice_amostra + 1) % N_AMOSTRAS;
if (amostras_validas < N_AMOSTRAS) amostras_validas++;
uint16_t media = soma_amostras / amostras_validas;
// ---- Conversão de escala: 0-4095 (12 bits) -> 0-8 níveis ----
uint8_t nivel = (uint32_t)media * 8 / 4095;
if (nivel > 8) nivel = 8;
// ---- Atualiza a barra de LEDs de forma cumulativa ----
uint32_t odr = GPIOA->ODR;
for (uint8_t i = 0; i < 8; i++) {
if (i < nivel) odr |= (1UL << pinos_barra[i]);
else odr &= ~(1UL << pinos_barra[i]);
}
GPIOA->ODR = odr;
}