import machine
from machine import Pin, Timer
import time
import math
import micropython
# ==============================================================================
# IMPORTACIÓN DE MÓDULOS DEL USUARIO
# ==============================================================================
import fourier
import multimetro
import oled_utils
# Reserva de memoria para errores dentro de las ISR
micropython.alloc_emergency_exception_buf(100)
# ==============================================================================
# 1. INICIALIZACIÓN DE PERIFÉRICOS
# ==============================================================================
# Inicializar Pantalla
oled = oled_utils.inicializar_oled()
# Botones de navegación (Conectados a GND en común)
btn_modo0 = Pin(18, Pin.IN, Pin.PULL_UP)
btn_modo1 = Pin(19, Pin.IN, Pin.PULL_UP)
btn_modo2 = Pin(20, Pin.IN, Pin.PULL_UP)
btn_modo3 = Pin(21, Pin.IN, Pin.PULL_UP)
# ==============================================================================
# 2. MÁQUINA DE ESTADOS Y VARIABLES GLOBALES
# ==============================================================================
modo_actual = 0
n_armonicos = 1
x = 0.0 # Tiempo global
# Variables del Reloj (RTC Virtual)
segundos = 50
minutos = 11
horas = 20
# Variables para Control de Rebotes
ultima_interrupcion = 0
TIEMPO_REBOTE_MS = 250
# Bandera de Exclusión Mutua para la pantalla
actualizar_pantalla_flag = True
# ==============================================================================
# 3. RUTINAS DE SERVICIO DE INTERRUPCIÓN (ISR)
# ==============================================================================
def isr_reloj_tick(timer):
"""ISR del Timer: Actualiza el reloj sincrónico."""
global segundos, minutos, horas, actualizar_pantalla_flag
segundos += 1
if segundos >= 60:
segundos = 0
minutos += 1
if minutos >= 60:
minutos = 0
horas += 1
if horas >= 24:
horas = 0
actualizar_pantalla_flag = True
def cambiar_modo_0(pin):
global modo_actual, ultima_interrupcion, actualizar_pantalla_flag, n_armonicos, x
tiempo_actual = time.ticks_ms()
if time.ticks_diff(tiempo_actual, ultima_interrupcion) > TIEMPO_REBOTE_MS:
modo_actual = 0
n_armonicos = 1
x = -999.0 # Fuerza reinicio del periodo en el main loop
ultima_interrupcion = tiempo_actual
actualizar_pantalla_flag = True
def cambiar_modo_1(pin):
global modo_actual, ultima_interrupcion, actualizar_pantalla_flag, n_armonicos, x
tiempo_actual = time.ticks_ms()
if time.ticks_diff(tiempo_actual, ultima_interrupcion) > TIEMPO_REBOTE_MS:
modo_actual = 1
n_armonicos = 1
x = -999.0
ultima_interrupcion = tiempo_actual
actualizar_pantalla_flag = True
def cambiar_modo_2(pin):
global modo_actual, ultima_interrupcion, actualizar_pantalla_flag, n_armonicos, x
tiempo_actual = time.ticks_ms()
if time.ticks_diff(tiempo_actual, ultima_interrupcion) > TIEMPO_REBOTE_MS:
modo_actual = 2
n_armonicos = 1
x = -999.0
ultima_interrupcion = tiempo_actual
actualizar_pantalla_flag = True
def cambiar_modo_3(pin):
global modo_actual, ultima_interrupcion, actualizar_pantalla_flag, n_armonicos, x
tiempo_actual = time.ticks_ms()
if time.ticks_diff(tiempo_actual, ultima_interrupcion) > TIEMPO_REBOTE_MS:
modo_actual = 3
n_armonicos = 1
x = -999.0
ultima_interrupcion = tiempo_actual
actualizar_pantalla_flag = True
# ==============================================================================
# 4. CONFIGURACIÓN Y ASIGNACIÓN DE INTERRUPCIONES AL HARDWARE
# ==============================================================================
btn_modo0.irq(trigger=Pin.IRQ_FALLING, handler=cambiar_modo_0)
btn_modo1.irq(trigger=Pin.IRQ_FALLING, handler=cambiar_modo_1)
btn_modo2.irq(trigger=Pin.IRQ_FALLING, handler=cambiar_modo_2)
btn_modo3.irq(trigger=Pin.IRQ_FALLING, handler=cambiar_modo_3)
reloj_hw = Timer()
reloj_hw.init(period=1000, mode=Timer.PERIODIC, callback=isr_reloj_tick)
# ==============================================================================
# 5. BUCLE PRINCIPAL (MAIN LOOP)
# ==============================================================================
print("LABORATORIO 12 - GRUPO L4")
print("LOPEZ RODRIGUEZ MIGUEL ANGEL")
print("CHINO QUISPE ALEJANDRO")
try:
while True:
# TAREA A: PROCESAMIENTO DIGITAL AL DAC Y CÁLCULO DE LA SEÑAL
if modo_actual == 0:
fx = fourier.fourier_ejercicio_1(x, n_armonicos)
rango_min, rango_max = -1.0, 4.5
limite_x, inicio_x, delta_x = 4 * math.pi, 0.0, 0.1
elif modo_actual == 1:
fx = fourier.calcular_fourier_ej2(x, n_armonicos)
rango_min, rango_max = -2.5, 1.5
limite_x, inicio_x, delta_x = 4.0, 0.0, 0.05
elif modo_actual == 2:
fx = fourier.fourier_ejercicio_3(x, n_armonicos)
rango_min, rango_max = -0.5, 1.5
limite_x, inicio_x, delta_x = 0.5, -0.5, 0.01
elif modo_actual == 3:
fx = fourier.fourier_ejercicio_4_ecg(x, n_armonicos)
rango_min, rango_max = 0.0, 1.5
limite_x, inicio_x, delta_x = 0.5, 0.0, 0.005
# Seguridad: Si el 'x' está fuera de rango al cambiar de modo, lo reiniciamos
if x < inicio_x or x > (limite_x + delta_x):
x = inicio_x
# Enviar valor mapeado al DAC de 10 bits
valor_dac = ((fx - rango_min) / (rango_max - rango_min)) * 1023
multimetro.escribir_dac(valor_dac)
# Avance del tiempo
x += delta_x
# Cuando termina un ciclo completo, reiniciamos el tiempo e incrementamos armónicos
if x >= limite_x:
x = inicio_x
n_armonicos += 1
if n_armonicos > 50:
n_armonicos = 1
# TAREA B: ACTUALIZACIÓN ASÍNCRONA DE PANTALLA
if actualizar_pantalla_flag:
bateria_pct = multimetro.leer_bateria()
# Llamamos a nuestra función modular pasándole los 7 parámetros
oled_utils.dibujar_interfaz(
oled, horas, minutos, segundos, bateria_pct, modo_actual, n_armonicos
)
actualizar_pantalla_flag = False
except KeyboardInterrupt:
reloj_hw.deinit()
multimetro.escribir_dac(0)
print("\nSistema Detenido")