#include <stdint.h>
// --- DIRECCIONES DE MEMORIA (STM32F103) ---
#define RCC_BASE 0x40021000
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define AFIO_BASE 0x40010000
#define EXTI_BASE 0x40010400
#define NVIC_BASE 0xE000E100
#define ADC1_BASE 0x40012400
// --- REGISTROS DE RELOJ (RCC) ---
#define RCC_APB2ENR *(volatile uint32_t *)(RCC_BASE + 0x18)
#define RCC_CFGR *(volatile uint32_t *)(RCC_BASE + 0x04)
#define RCC_IOPAEN (1 << 2)
#define RCC_IOPBEN (1 << 3)
#define RCC_IOPCEN (1 << 4)
#define RCC_AFIOEN (1 << 0)
#define RCC_ADC1EN (1 << 9)
// --- REGISTROS GPIO ---
#define GPIOA_CRL *(volatile uint32_t *)(GPIOA_BASE + 0x00)
#define GPIOA_ODR *(volatile uint32_t *)(GPIOA_BASE + 0x0C)
#define GPIOB_CRL *(volatile uint32_t *)(GPIOB_BASE + 0x00)
#define GPIOB_ODR *(volatile uint32_t *)(GPIOB_BASE + 0x0C)
#define GPIOC_CRH *(volatile uint32_t *)(GPIOC_BASE + 0x04)
#define GPIOC_ODR *(volatile uint32_t *)(GPIOC_BASE + 0x0C)
#define GPIOC13 (1UL << 13) // LED Parpadeante
#define GPIOC14 (1UL << 14) // LED Indicador de Modo
// --- REGISTROS EXTI Y NVIC ---
#define AFIO_EXTICR1 *(volatile uint32_t *)(AFIO_BASE + 0x08)
#define EXTI_IMR *(volatile uint32_t *)(EXTI_BASE + 0x00)
#define EXTI_FTSR *(volatile uint32_t *)(EXTI_BASE + 0x0C)
#define EXTI_PR *(volatile uint32_t *)(EXTI_BASE + 0x14)
#define NVIC_ISER0 *(volatile uint32_t *)(NVIC_BASE + 0x00)
// --- REGISTROS DEL ADC1 ---
#define ADC1_SR *(volatile uint32_t *)(ADC1_BASE + 0x00)
#define ADC1_CR2 *(volatile uint32_t *)(ADC1_BASE + 0x08)
#define ADC1_SMPR1 *(volatile uint32_t *)(ADC1_BASE + 0x0C)
#define ADC1_SMPR2 *(volatile uint32_t *)(ADC1_BASE + 0x10)
#define ADC1_SQR3 *(volatile uint32_t *)(ADC1_BASE + 0x34)
#define ADC1_DR *(volatile uint32_t *)(ADC1_BASE + 0x4C)
#define ADC_CR2_ADON (1 << 0)
#define ADC_CR2_CAL (1 << 2)
#define ADC_CR2_TSVREFE (1 << 23)
#define ADC_SR_EOC (1 << 1)
// --- SYSTICK ---
#define SysTick_BASE 0xE000E010
#define SysTick_CTRL *(volatile uint32_t *)(SysTick_BASE + 0x00)
#define SysTick_LOAD *(volatile uint32_t *)(SysTick_BASE + 0x04)
#define SysTick_VAL *(volatile uint32_t *)(SysTick_BASE + 0x08)
#define SysTick_CTRL_ENABLE (1 << 0)
#define SysTick_CTRL_TICKINT (1 << 1)
#define SysTick_CTRL_CLKSOURCE (1 << 2)
// --- VARIABLES GLOBALES ---
volatile uint32_t tick = 0;
volatile uint8_t modo_medicion = 0;
void SysTick_Handler(void) {
tick++;
}
// ISR con enlace explícito
void EXTI0_IRQHandler(void) {
static uint32_t ultimo_tiempo = 0;
if (EXTI_PR & (1 << 0)) {
uint32_t tiempo_actual = tick;
// Debounce de 200 ms
if ((tiempo_actual - ultimo_tiempo) > 200) {
modo_medicion ^= 1;
if (modo_medicion) {
GPIOC_ODR &= ~GPIOC14; // Encender PC14 (Lógica invertida/Anodo)
} else {
GPIOC_ODR |= GPIOC14; // Apagar PC14
}
ultimo_tiempo = tiempo_actual;
}
EXTI_PR |= (1 << 0); // Limpiar bandera de interrupción
}
}
void systick_init_ms(void) {
tick = 0;
SysTick_CTRL = 0;
SysTick_LOAD = 7999; // 8 MHz / 8000 = 1 kHz (1 ms)
SysTick_VAL = 0;
SysTick_CTRL = SysTick_CTRL_CLKSOURCE | SysTick_CTRL_TICKINT | SysTick_CTRL_ENABLE;
}
void delay_ms(uint32_t ms) {
uint32_t inicio = tick;
while ((tick - inicio) < ms);
}
void exti0_init_pa0(void) {
RCC_APB2ENR |= (RCC_IOPAEN | RCC_AFIOEN);
// PA0 como Entrada con Pull-Up/Pull-Down (CNF=10, MODE=00)
GPIOA_CRL &= ~0x0000000F;
GPIOA_CRL |= 0x00000008;
GPIOA_ODR |= (1 << 0); // Habilitar Pull-Up
AFIO_EXTICR1 &= ~0x000F; // Mapear PA0 a EXTI0
EXTI_IMR |= (1 << 0);
EXTI_FTSR |= (1 << 0); // Flanco de bajada
NVIC_ISER0 |= (1 << 6); // Habilitar vector EXTI0 en NVIC
}
void adc1_init(void) {
RCC_APB2ENR |= (RCC_IOPAEN | RCC_ADC1EN);
// Prescaler ADC = PCLK2 / 6 (8MHz / 6 = 1.33MHz)
RCC_CFGR &= ~(0x3 << 14);
RCC_CFGR |= (0x2 << 14);
GPIOA_CRL &= ~(0xF << 4); // PA1 Entrada Analógica (MODE=00, CNF=00)
// Activar ADC y Sensor Interno
ADC1_CR2 |= ADC_CR2_TSVREFE | ADC_CR2_ADON;
ADC1_SMPR1 |= (0x7 << 18); // Canal 16 (239.5 ciclos)
ADC1_SMPR2 |= (0x7 << 3); // Canal 1 (239.5 ciclos)
delay_ms(2);
// Calibración
ADC1_CR2 |= ADC_CR2_CAL;
while (ADC1_CR2 & ADC_CR2_CAL);
}
uint16_t adc_leer_canal(uint8_t canal) {
ADC1_SQR3 = canal;
ADC1_CR2 |= ADC_CR2_ADON;
uint32_t timeout = 5000;
while (!(ADC1_SR & ADC_SR_EOC) && --timeout);
return (uint16_t)ADC1_DR;
}
void actualizar_leds_5_rangos(uint16_t valor) {
// Apagar PB0, PB1, PB5, PB6, PB7
GPIOB_ODR &= ~((1 << 0) | (1 << 1) | (1 << 5) | (1 << 6) | (1 << 7));
if (modo_medicion == 1) {
// Escala para el Sensor Interno
if (valor > 1200) GPIOB_ODR |= (1 << 0);
if (valor > 1400) GPIOB_ODR |= (1 << 1);
if (valor > 1600) GPIOB_ODR |= (1 << 5);
if (valor > 1700) GPIOB_ODR |= (1 << 6);
if (valor > 1800) GPIOB_ODR |= (1 << 7);
} else {
// Escala Potenciómetro (0 - 4095)
if (valor > 400) GPIOB_ODR |= (1 << 0);
if (valor > 1200) GPIOB_ODR |= (1 << 1);
if (valor > 2000) GPIOB_ODR |= (1 << 5);
if (valor > 2800) GPIOB_ODR |= (1 << 6);
if (valor > 3600) GPIOB_ODR |= (1 << 7);
}
}
int main(void) {
// Habilitar Relojes de GPIO A, B y C
RCC_APB2ENR |= RCC_IOPAEN | RCC_IOPBEN | RCC_IOPCEN;
// Configurar PC13 y PC14 como Salidas Push-Pull 2MHz (MODE=10, CNF=00)
GPIOC_CRH &= ~0x0FF00000;
GPIOC_CRH |= 0x02200000;
// CONFIGURACIÓN CORREGIDA DE PB0, PB1, PB5, PB6, PB7:
// PB0 y PB1 están en los nibbles 0 y 1 de GPIOB_CRL.
// PB5, PB6 y PB7 están en los nibbles 5, 6 y 7 de GPIOB_CRL.
GPIOB_CRL &= ~(0xFF0000FF); // Limpiar PB0, PB1, PB5, PB6, PB7
GPIOB_CRL |= (0x22000022); // Configurar como Output Push-Pull 2MHz (0x2)
// Inicializaciones
exti0_init_pa0();
systick_init_ms();
adc1_init();
// Habilitar Interrupciones Globales
__asm__ volatile ("cpsie i" : : : "memory");
uint32_t ultimo_parpadeo = 0;
uint32_t ultima_lectura = 0;
uint16_t lectura_adc = 0;
while (1) {
// 1. Parpadeo de LED PC13 (500 ms)
if ((tick - ultimo_parpadeo) >= 500) {
GPIOC_ODR ^= GPIOC13;
ultimo_parpadeo = tick;
}
// 2. Muestreo de ADC (100 ms)
if ((tick - ultima_lectura) >= 100) {
ultima_lectura = tick;
if (modo_medicion == 0) {
lectura_adc = adc_leer_canal(1); // Potenciómetro en PA1
} else {
lectura_adc = adc_leer_canal(16); // Sensor Temp Interno
}
actualizar_leds_5_rangos(lectura_adc);
}
}
}