Luz de fogata para el belén con CPLD 
Un año más, decorando el belén con nuevos inventos. En esta ocasión he vuelto a los orígenes y he implementado una luz, pero esta vez una luz especial que simule un fuego encendido utilizando un CPLD.

Introducción

A la hora de simular el crepitar de una llamas se ha optado por hacer que un led varíe de luminosidad de forma aleatoria varias veces por segundo.



Con una luz que varíe su intensidad lumínica varias veces por segundo (entre 5 y 6 veces por segundo, por ejemplo) más una buena escenografía (color de la luz, decorados, etc.) se consigue un razonable efecto de fuego encendido.

Diseño técnico

Como vamos a hacer el diseño utilizando un circuito totalmente digital, la intensidad lumínica se modulará utilizando una señal cuadrada modulada en anchura (PWM). Cuanto mayor sea el semiciclo a 1 y menor el semiciclo a 0 más brillará el led y a la inversa: cuanto mayor sea el semiciclo a 0 y menor el semiciclo a 1 menos brillará.



Si hacemos que la frecuencia sea lo suficientemente alta no se apreciará ningún tipo de parpadeo y la luz se percibirá como que brilla de forma continuada pero con diferente intensidad. El CPLD utilizado tiene conectado un reloj a 50 MHz, por tanto usando un contador de 10 bits estándar conseguiremos un desbordamiento a una frecuencia de

$${50000000 \over {2^{10}}} = 48828.125 Hz$$

Si el valor de este contador lo comparamos con un valor determinado, el resultado de esta comparación será la salida PWM que necesitamos para el led de nuestra fogata:



El valor de intensidad lo generaremos mediante un LFSR maximal de 10 bits. Dicho LFSR ha sido utilizado en otros montajes anteriores:



Genera una secuencia maximal de 1023 valores pseudoaleatorios (el valor 0 no aparece en la secuencia) que se puede usar como valor de intensidad lumínica:



En este circuito el bloque combinacional "OP" es el que implementa el polinomio maximal de 10 bits. A continuación sólo falta implementar la temporización. Como queremos el que valor de intensidad lumínica cambie unas 5 ó 6 veces por segundo, bastará con poner un contador estándar de:

$$\lceil log_2\left({50000000 \over 6}\right) \rceil = 23 bits$$

Con un contador de 23 bits a 50 MHz tendremos una frecuencia de desbordamiento de casi 6 veces por segundo:

$${50000000 \over {2^{23}}} = 5.96 Hz$$

A continuación puede verse cómo quedaría el diagrama completo:



El bloque combinacional "CM" (Control del Multiplexor) se encarga de controlar la selección del multiplexor del LFSR en función del timer contador de 23 bits y del valor del propio LFSR. La tabla de verdad de este bloque sería la siguiente:

EntradasSalidas
Timer == 0LFSR == 0MUX
X1"1"
10Salida de OP
00Salida del LFSR


Cuando el valor del LFSR es 0, lo que hace es seleccionarla entrada "1" del multiplexor para que el LFSR se cargue con un valor distinto de cero (el 1) y poder así arrancar la generación de números aleatorios. Cuando el timer (contador de 23 bits) se desborda (pasa por cero) el LFSR se carga con el siguiente valor de la secuencia de números pseudoaleatorios y el resto del tiempo (valor del contador de 23 bits diferente de 0) el registro LFSR permanece inalterado.

Como se puede apreciar, se ha prescindido de circuitería de reset. Teniendo en cuenta que el objetivo es minimizar la circuitería y que los fabricantes siempre te garantizan que en el arranque, todos los biestables están a 0, se puede "abusar" de esta característica y ahorrar así parte de la circuitería de reset.

Posibles mejoras

Como se puede apreciar, del contador que hace de timer (el de 23 bits) sólo nos interesa cuando pasa por un valor concreto (el 0), no es como el contador que se utiliza para comparar con el LFSR y generar la señal PWM. Teniendo esto presente, dicho contador de 23 bits podría implementarse utilizando también un LFSR maximal de 23 bits: en lugar de un sumador, se puede implementar un bloque combinacionar consistente tan solo en una única puerta xor (ver polinomio a aplicar aquí), lo que supone un ahorro considerable en circuitería.

Hay que tener presente que los LFSRs no pasan nunca por cero, por lo que habría que elegir cualquier otro valor (cualquiera) como valor de "desbordamiento" (el valor 1, por ejemplo).

Implementación

A continuación puede verse la implementación de este diseño en VHDL.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ChristmasFire is
    port (
        Clk : in std_logic;
        Led : out std_logic
    );
end entity;

architecture RTL of ChristmasFire is
    signal CounterDBus : std_logic_vector(9 downto 0);
    signal CounterQBus : std_logic_vector(9 downto 0);
    signal LFSRDBus : std_logic_vector(9 downto 0);
    signal LFSRQBus : std_logic_vector(9 downto 0);
    signal TimerDBus : std_logic_vector(22 downto 0);
    signal TimerQBus : std_logic_vector(22 downto 0);
    signal TimerOverflow : std_logic;
begin
    -- pwm counter
    process (Clk)
    begin
        if (Clk'event and (Clk = '1')) then
            CounterQBus <= CounterDBus;
        end if;
    end process;
    
    CounterDBus <= std_logic_vector(signed(CounterQBus) + to_signed(1, 10));
    
    -- lfsr
    process (Clk)
    begin
        if (Clk'event and (Clk = '1')) then
            LFSRQBus <= LFSRDBus;
        end if;
    end process;
    
    LFSRDBus <= std_logic_vector(to_signed(1, 10)) when (signed(LFSRQBus) = to_signed(0, 10)) else
                ((LFSRQBus(3) xor LFSRQBus(0)) & LFSRQBus(9 downto 1)) when (TimerOverflow = '1') else
                LFSRQBus;
    
    -- timer
    process (Clk)
    begin
        if (Clk'event and (Clk = '1')) then
            TimerQBus <= TimerDBus;
        end if;
    end process;
    
    TimerDBus <= std_logic_vector(signed(TimerQBus) + to_signed(1, 23));
    TimerOverflow <= '1' when (signed(TimerQBus) = to_signed(0, 23)) else
                     '0';
    
    -- output
    Led <= '1' when (signed(CounterQBus) > signed(LFSRQBus)) else
           '0';
end architecture;


Se trata de un único fichero que puede descargarse desde la sección soft.



¡Feliz Navidad a todos!

Comentarios 
Lo sentimos. No se permiten nuevos comentarios después de 90 días.