from machine import Pin, I2C
import utime
import math
from ssd1306 import SSD1306_I2C
# Configuración de pines GPIO para salida R2R
pines = [2, 3, 4, 5, 6, 7, 8, 9, 10]
# Configuración OLED
WIDTH = 128
HEIGHT = 64
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)
# Función para actualizar los LEDs según el valor del contador
def GPIO_SALP(valor, listagpio):
pines_gpio = [Pin(pin, Pin.OUT) for pin in listagpio]
saltxt = ""
for i in range(8):
if valor & (1 << i):
pines_gpio[i].value(1)
saltxt = saltxt + "1"
else:
pines_gpio[i].value(0)
saltxt = saltxt + "0"
return saltxt
# Función para convertir coordenadas matemáticas a coordenadas de pantalla
def convertir_coordenadas(x_math, y_math, x_min, x_max, y_min, y_max):
x_pixel = int((x_math - x_min) / (x_max - x_min) * (WIDTH - 1))
y_pixel = HEIGHT - 1 - int((y_math - y_min) / (y_max - y_min) * (HEIGHT - 1))
return x_pixel, y_pixel
# NUEVA FUNCIÓN ORIGINAL - Pulso Rectangular
def funcion_original(x):
# Normalizar x al rango [-0.5, 0.5] (un período)
periodo = 1.0
x_norm = ((x + 0.5) % periodo) - 0.5
if -0.5 < x_norm < -0.25:
return 0.0
elif -0.25 < x_norm < 0.25:
return 1.0
else: # 0.25 < x_norm < 0.5
return 0.0
# NUEVA SERIE DE FOURIER para Pulso Rectangular
def SerieFourier_pulso(nmax, x):
T = 1.0 # Período
w0 = 2 * math.pi / T # Frecuencia fundamental = 2π
# Cálculo de a0 (valor promedio)
# ∫f(x)dx desde -0.5 a 0.5 = ∫1 dx desde -0.25 a 0.25 = 0.5
a0 = 0.5 / T # = 0.5
suma = 0.0
for n in range(1, nmax + 1):
# Para función par, solo términos coseno (bn = 0)
# an = (2/T) * ∫f(x)*cos(nω₀x)dx desde -T/2 a T/2
# an = 2 * ∫cos(2πnx)dx desde -0.25 a 0.25
an = 2 * (math.sin(n * math.pi * 0.5) - math.sin(-n * math.pi * 0.5)) / (n * math.pi)
an = (2 / (n * math.pi)) * math.sin(n * math.pi * 0.5)
bn = 0 # Función par → no hay términos seno
# Suma de la serie
suma += an * math.cos(n * w0 * x)
return a0 + suma
# Versión optimizada para mejor rendimiento
def SerieFourier_pulso_optimizada(nmax, x):
a0 = 0.5 # Componente DC
suma = 0.0
for n in range(1, nmax + 1):
# Fórmula simplificada: an = (2/(nπ)) * sin(nπ/2)
an = (2 / (n * math.pi)) * math.sin(n * math.pi * 0.5)
suma += an * math.cos(2 * math.pi * n * x)
return a0 + suma
def graficar_funcion_pulso_OLED(nmax, mostrar_original=True):
x_min = -1.0 # Mostrar 2 períodos completos
x_max = 1.0
y_min = -0.2
y_max = 1.5
# Limpiar pantalla
oled.fill(0)
# Dibujar ejes coordenados
oled.hline(0, HEIGHT-1, WIDTH, 1) # Eje X en la parte inferior
oled.vline(WIDTH//2, 0, HEIGHT, 1) # Eje Y en el centro
# Graficar función de Fourier (línea continua)
puntos_fourier = []
for i in range(WIDTH):
x_math = x_min + (i / WIDTH) * (x_max - x_min)
y_math = SerieFourier_pulso_optimizada(nmax, x_math)
x_plot, y_plot = convertir_coordenadas(x_math, y_math, x_min, x_max, y_min, y_max)
puntos_fourier.append((x_plot, y_plot))
# Dibujar línea de Fourier
for i in range(len(puntos_fourier) - 1):
x1, y1 = puntos_fourier[i]
x2, y2 = puntos_fourier[i + 1]
if 0 <= y1 < HEIGHT and 0 <= y2 < HEIGHT:
oled.line(x1, y1, x2, y2, 1)
# Graficar función original si se solicita
if mostrar_original:
puntos_original = []
for i in range(WIDTH):
x_math = x_min + (i / WIDTH) * (x_max - x_min)
y_math = funcion_original(x_math)
x_plot, y_plot = convertir_coordenadas(x_math, y_math, x_min, x_max, y_min, y_max)
puntos_original.append((x_plot, y_plot))
# Dibujar función original como línea discontinua
for i in range(0, len(puntos_original) - 1, 2):
x1, y1 = puntos_original[i]
x2, y2 = puntos_original[i + 1]
if 0 <= y1 < HEIGHT and 0 <= y2 < HEIGHT:
oled.line(x1, y1, x2, y2, 1)
# Mostrar información en OLED
oled.text(f"N={nmax}", 0, 10, 1)
oled.text("Pulso Rectangular", 0, 0, 1)
#oled.text("T=1, ω₀=2π", 0, 20, 1)
#oled.text("Linea=Fourier", 0, 50, 1)
oled.show()
# Función principal para el pulso rectangular
def main_fourier_pulso():
x_min = -1.5 # Tres períodos
x_max = 1.5
delta = 0.02 # Más puntos para mejor resolución
print("Visualizando Pulso Rectangular Periodico")
print("f(x) = 0 para -0.5<x<-0.25")
print("f(x) = 1 para -0.25<x<0.25")
print("f(x) = 0 para 0.25<x<0.5")
print("T = 1, ω₀ = 2π")
harmonicos = 1
max_harmonicos = 20
while True:
print(f"\n--- Aproximacion con {harmonicos} armonicos ---")
# Graficar en OLED
graficar_funcion_pulso_OLED(harmonicos, True)
# Generar salida por terminal y R2R
x = x_min
punto_count = 0
while x <= x_max:
# Calcular ambos valores
fx_fourier = SerieFourier_pulso_optimizada(harmonicos, x)
fx_original = funcion_original(x)
# Salida por terminal (mostrar puntos clave)
if punto_count % 50 == 0:
print(f"x={x:6.3f} Fourier={fx_fourier:6.3f} Original={fx_original:6.3f}")
# Salida analógica R2R
valor_r2r = int(fx_fourier * 255) # Escalar a 0-255
valor_r2r = max(0, min(255, valor_r2r))
GPIO_SALP(valor_r2r, pines)
x += delta
punto_count += 1
utime.sleep(0.01)
# Cambiar número de armónicos
harmonicos += 1
if harmonicos > max_harmonicos:
harmonicos = 1
utime.sleep(2)
# Demo de la función original del pulso
def demo_pulso_exacto():
"""Muestra solo la función pulso rectangular exacta"""
print("Mostrando Pulso Rectangular EXACTO")
x_min = -1.0
x_max = 1.0
y_min = -0.2
y_max = 1.5
while True:
oled.fill(0)
# Dibujar ejes
oled.hline(0, HEIGHT-1, WIDTH, 1)
oled.vline(WIDTH//2, 0, HEIGHT, 1)
# Graficar función exacta
puntos = []
for i in range(WIDTH):
x_math = x_min + (i / WIDTH) * (x_max - x_min)
y_math = funcion_original(x_math)
x_plot, y_plot = convertir_coordenadas(x_math, y_math, x_min, x_max, y_min, y_max)
puntos.append((x_plot, y_plot))
# Dibujar línea escalonada
for i in range(len(puntos) - 1):
x1, y1 = puntos[i]
x2, y2 = puntos[i + 1]
if 0 <= y1 < HEIGHT and 0 <= y2 < HEIGHT:
# Para transiciones bruscas, dibujar líneas verticales
if abs(y2 - y1) > 0.5: # Detectar transición
oled.vline(x1, min(y1, y2), abs(y2 - y1), 1)
else:
oled.line(x1, y1, x2, y2, 1)
#oled.text("PULSO RECTANGULAR", 0, 0, 1)
oled.text("f(x) Escalonada", 0, 0, 1)
#oled.text("T=1, Ancho=0.5", 0, 20, 1)
#oled.text("ω₀=2π", 0, 30, 1)
oled.show()
utime.sleep(4)
if __name__ == '__main__':
# Mensaje inicial
oled.fill(0)
oled.text("PULSO RECTANGULAR", 0, 0)
oled.text("Serie de Fourier", 0, 15)
oled.text("T=1, ω₀=2π", 0, 30)
oled.text("Ancho=0.5", 0, 45)
oled.show()
utime.sleep(2)
try:
# Elegir qué función ejecutar:
#demo_pulso_exacto() # Muestra solo la función exacta
main_fourier_pulso() # Muestra la aproximación de Fourier
except Exception as e:
print(f"Error: {e}")
demo_pulso_exacto()