//ENTRADA ANALOGICA:
//- Potenciometro (SIG) -> PA0 (conector A0) -> ADC_IN0
//
//SAIDAS - barra de 8 LEDs (nivel = quantidade de LEDs acesos):
//- L0 -> PA12 (D2) L4 -> PB1 (D6)
//- L1 -> PB0 (D3) L5 -> PA8 (D7)
//- L2 -> PB7 (D4) L6 -> PA11 (D8)
//- L3 -> PB6 (D5) L7 -> PB4 (D12)
//
//EXTENSAO: leitura em percentual pela UART (USART2, TXapenas), inicializada em baixo nivel:
//- TX -> PA2 (conector A7)
#include <stdint.h>
#define BIT(x) (1UL << (x))
//Faixa e niveis do ADC (usados tambem no relatorio)
#define ADC_MIN 0u
#define ADC_MAX 4095u // ADC de 12 bits
#define NUM_NIVEIS 8u // 8 LEDs na barra
#define MEDIA_AMOSTRAS 8u // tamanho da janela da media movel
//Mapeamento da barra de LEDs
typedef struct {
GPIO_TypeDef *porta;
uint32_t pino;
} pino_t;
static const pino_t barraLeds[NUM_NIVEIS] = {
{ GPIOA, 12 }, // L0 - D2
{ GPIOB, 0 }, // L1 - D3
{ GPIOB, 7 }, // L2 - D4
{ GPIOB, 6 }, // L3 - D5
{ GPIOB, 1 }, // L4 - D6
{ GPIOA, 8 }, // L5 - D7
{ GPIOA, 11 }, // L6 - D8
{ GPIOB, 4 }, // L7 - D12
};
static uint16_t buffer[MEDIA_AMOSTRAS];
static uint8_t idxBuffer = 0;
static uint32_t somaBuffer = 0;
static uint8_t amostrasValidas = 0;
// Configura o clock do sistema para HSI16 (16 MHz), garantindo uma base
// de tempo conhecida para o ADC e para o calculo do BRR da UART.
static void clock_init(void) {
RCC->CR |= RCC_CR_HSION;
while (!(RCC->CR & RCC_CR_HSIRDY)) { }
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) { }
}
//Configura os pinos GPIO usados: PA0 analogico (ADC), 8 saidas da
//barra de LEDs e PA2 em modo alternativo (USART2_TX).
static void gpio_init(void) {
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;
//PA0 em modo analogico (MODER = 11) para entrada do ADC
GPIOA->MODER |= (3UL << (0 * 2));
//Saidas da barra de LEDs
for (uint8_t i = 0; i < NUM_NIVEIS; i++) {
GPIO_TypeDef *p = barraLeds[i].porta;
uint32_t n = barraLeds[i].pino;
p->MODER &= ~(3UL << (n * 2));
p->MODER |= (1UL << (n * 2)); //01 = saida push-pull
p->BSRR = (1UL << n) << 16; // inicia apagado
}
//PA2 em modo alternativo (AF4 = USART2_TX), usado apenas na extensao
GPIOA->MODER &= ~(3UL << (2 * 2));
GPIOA->MODER |= (2UL << (2 * 2)); //10 = funcao alternativa
GPIOA->AFR[0] &= ~(0xFUL << (2 * 4));
GPIOA->AFR[0] |= (4UL << (2 * 4)); //AF4
}
//Configura o ADC1: regulador interno, calibracao, clock sincrono
//(PCLK/2), canal 0 (PA0) e habilitacao.
static void adc_init(void) {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
//Clock sincrono do ADC = PCLK / 2
ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE;
ADC1->CFGR2 |= ADC_CFGR2_CKMODE_0; //01 = PCLK/2
//Tempo de amostragem intermediario (compromisso velocidade/precisao)
ADC1->SMPR = (4UL << ADC_SMPR_SMP_Pos);
//Habilita o regulador interno do ADC e aguarda estabilizacao
ADC1->CR |= ADC_CR_ADVREGEN;
for (volatile uint32_t i = 0; i < 3000; i++) { }
//Calibracao (ADC precisa estar desabilitado, ADEN = 0)
ADC1->CR |= ADC_CR_ADCAL;
while (ADC1->CR & ADC_CR_ADCAL) { }
//Seleciona o canal 0 (PA0 = ADC_IN0)
ADC1->CHSELR = ADC_CHSELR_CHSEL0;
//Habilita o ADC e aguarda ficar pronto (ADRDY)
ADC1->ISR |= ADC_ISR_ADRDY;
ADC1->CR |= ADC_CR_ADEN;
while (!(ADC1->ISR & ADC_ISR_ADRDY)) { }
}
// Extensao: inicializa a USART2 (apenas TX) em baixo nivel: 115200 8N1,
// assumindo PCLK = 16 MHz (HSI16 configurado em clock_init()).
static void uart2_init(void) {
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
USART2->BRR = 16000000UL / 115200UL; //divisor para 115200 bps @ 16 MHz
USART2->CR1 = USART_CR1_TE | USART_CR1_UE; //8N1 (M=00), TX + USART habilitados
}
static void uart2_send_char(char c) {
while (!(USART2->ISR & USART_ISR_TXE)) { }
USART2->TDR = (uint8_t)c;
}
static void uart2_send_string(const char *s) {
while (*s) { uart2_send_char(*s++); }
}
static void uart2_send_uint(uint32_t v) {
char buf[6];
int8_t n = 0;
if (v == 0) { uart2_send_char('0'); return; }
while (v > 0 && n < 6) { buf[n++] = (char)('0' + (v % 10)); v /= 10; }
while (n > 0) { uart2_send_char(buf[--n]); }
}
// Dispara uma conversao e retorna o valor bruto de 12 bits (0-4095)
static uint16_t adc_read(void) {
ADC1->CR |= ADC_CR_ADSTART;
while (!(ADC1->ISR & ADC_ISR_EOC)) { }
return (uint16_t)(ADC1->DR);
}
// Atualiza a barra de LEDs de acordo com o nivel (0 a NUM_NIVEIS)
static void barra_atualiza(uint8_t nivel) {
for (uint8_t i = 0; i < NUM_NIVEIS; i++) {
GPIO_TypeDef *p = barraLeds[i].porta;
uint32_t n = barraLeds[i].pino;
if (i < nivel) {
p->BSRR = (1UL << n); //acende
} else {
p->BSRR = (1UL << n) << 16; //apaga
}
}
}
void setup() {
clock_init();
gpio_init();
adc_init();
uart2_init();
uart2_send_string("Experimento 2 - ADC (registradores) - Nucleo L031K6\r\n");
}
static uint16_t contadorEnvio = 0;
void loop() {
//Leitura bruta do ADC
uint16_t bruto = adc_read();
//Estabilizacao por media movel (janela de MEDIA_AMOSTRAS amostras)
somaBuffer -= buffer[idxBuffer];
buffer[idxBuffer] = bruto;
somaBuffer += bruto;
idxBuffer = (uint8_t)((idxBuffer + 1) % MEDIA_AMOSTRAS);
if (amostrasValidas < MEDIA_AMOSTRAS) amostrasValidas++;
uint16_t media = (uint16_t)(somaBuffer / amostrasValidas);
//Conversao da leitura filtrada em nivel para a barra de LEDs
//Divide-se por ADC_MAX (4095), e nao por ADC_MAX+1, para que o
//Maximo do ADC (4095) resulte no nivel maximo (8 LEDs).
uint8_t nivel = (uint8_t)(((uint32_t)media * NUM_NIVEIS) / ADC_MAX);
if (nivel > NUM_NIVEIS) nivel = NUM_NIVEIS;
//Atualiza a barra de LEDs
barra_atualiza(nivel);
//Extensao: a cada ~20 ciclos, envia o percentual pela UART
contadorEnvio++;
if (contadorEnvio >= 20) {
contadorEnvio = 0;
uint32_t percentual = ((uint32_t)media * 100UL) / ADC_MAX;
uart2_send_string("ADC=");
uart2_send_uint(media);
uart2_send_string(" (");
uart2_send_uint(percentual);
uart2_send_string("%) nivel=");
uart2_send_uint(nivel);
uart2_send_string("\r\n");
}
//Pausa apenas para definir a taxa de amostragem (nao e delay() do Arduino)
for (volatile uint32_t i = 0; i < 8000; i++) { }
}