Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> VHDL

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

Pulsbreedtemodulatie (PWM) verandert een digitale FPGA-pin in een krachtige analoge controller. Door de voeding op hoge snelheid aan en uit te schakelen, levert PWM de exacte gemiddelde stroom die een apparaat nodig heeft, zonder ooit een echte analoge spanning te genereren.

Typische PWM-toepassingen omvatten audio-uitgang voor speakers, LED-dimming, en het sturen van het koppel van inductiemotoren zoals servomotoren, computerventilatoren, pompen en brushless DC-motoren voor elektrische voertuigen.

Zie ook: RC servo-controller met PWM vanaf een FPGA-pin

Hoe PWM Werkt

Wanneer een voeding getoggled wordt tussen 100% en 0% op een hoge frequentie, is de tijdsgemiddelde stroom die de belasting ziet, direct evenredig met de fractie van de tijd dat de voeding aanstaat. Het volgende diagram illustreert het principe:

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

Duty Cycle

De duty cycle definieert hoe lang de PWM-uitgang hoog blijft tijdens elke periode. Uitgedrukt als percentage, wordt het berekend uit de binaire tellerwaarde:

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

In een VHDL-implementatie behandelen we de duty cycle als een unsigned integer, wat ons de volledige resolutie van de PWM-teller geeft. Een 0% duty cycle zet de uitgang permanent uit, terwijl een 100% duty cycle hem continu aan houdt. De bereikbare resolutie wordt bepaald door de breedte van de teller.

Om de binaire duty-cycle waarde om te zetten naar een percentage, gebruik:

\mathit{duty\_cycle\_percentage} = \frac{\mathit{commanded\_duty\_cycle} * 100}{2^\mathit{pwm\_bits} - 1}

PWM Frequentie

De PWM-frequentie is de inverse van de periode:

\mathit{pwm\_freq} = \frac{1}{\mathit{pwm\_period}}

De juiste frequentie kiezen hangt af van het doelapparaat. Voor LED's is een paar honderd hertz voldoende om zichtbaar knipperen te voorkomen. Brushless DC-motoren werken daarentegen typisch in de tientallen kilohertz; te lage frequentie kan mechanische trillingen produceren, terwijl te hoge frequentie energie verspilt.

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

Omdat MOSFET-schakelaars langzamer zijn dan de FPGA-logica, kan het bedrijven van de PWM te snel ertoe leiden dat de transistor tijd doorbrengt in een gedeeltelijk geleidende toestand, wat extra warmte dissipeert.

PWM Generator Module

Hieronder is een generieke VHDL-implementatie van een PWM-controller die geïnstantieerd kan worden met elke tellerbreedte en klokdelfactor.

Entiteitsdeclaratie

 entity pwm is generic ( pwm_bits : integer; clk_cnt_len : positive := 1 ); port ( clk : in std_logic; rst : in std_logic; duty_cycle : in unsigned(pwm_bits - 1 downto 0); pwm_out : out std_logic ); end pwm; 
clk en rst poorten bieden synchrone controle. De duty_cycle invoer heeft een breedte gedefinieerd door pwm_bits, dus de tellerresolutie is direct configureerbaar. Het pwm_out signaal wordt gerouteerd naar de FPGA-pin die de MOSFET-gate aanstuurt.

Interne Signalen

 signal pwm_cnt : unsigned(pwm_bits - 1 downto 0); signal clk_cnt : integer range 0 to clk_cnt_len - 1; 

De pwm_cnt teller spiegelt de duty-cycle breedte. De clk_cnt teller implementeert een optionele klokdeler; clk_cnt_len op 1 instellen schakelt de deler uit en verwijdert de bijbehorende logica.

Klokdeler-proces

 CLK_CNT_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then clk_cnt <= 0; else if clk_cnt < clk_cnt_len - 1 then clk_cnt <= clk_cnt + 1; else clk_cnt <= 0; end if; end if; end if; end process; 

Met de standaard clk_cnt_len = 1 zullen synthese-tools dit proces automatisch elimineren.

PWM Uitgang Proces

 PWM_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then pwm_cnt <= (others => '0'); pwm_out <= '0'; else if clk_cnt_len = 1 or clk_cnt = 0 then pwm_cnt <= pwm_cnt + 1; pwm_out <= '0'; if pwm_cnt = unsigned(to_signed(-2, pwm_cnt'length)) then pwm_cnt <= (others => '0'); end if; if pwm_cnt < duty_cycle then pwm_out <= '1'; end if; end if; end if; end if; end process; 

De teller incrementeert alleen wanneer clk_cnt nul is, wat de PWM-frequentie effectief dempt. De voorwaarde die pwm_cnt reset op de tweede-hoogste waarde garandeert dat een 100% duty cycle bereikbaar is.

\mathit{pwm\_hz} = \frac{\mathit{clk\_hz}}{(2^\mathit{pwm\_bits} - 1) * \mathit{clk\_cnt\_len}}

Top-Level Ontwerp

Om de PWM-controller te demonstreren, stuurt het ontwerp de power-on LED op een Lattice iCEstick FPGA. De board's 12 MHz oscillator voedt een teller die de LED-intensiteit in een zaagtandpatroon stuurt.

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

Entiteitsdeclaratie

 entity pwm_led is generic ( pwm_bits : integer := 8; cnt_bits : integer := 25; clk_cnt_len : positive := 47 ); port ( clk : in std_logic; rst_n : in std_logic; led_1 : out std_logic; led_2 : out std_logic; led_3 : out std_logic; led_4 : out std_logic; led_5 : out std_logic ); end pwm_led; 

De 12 MHz klok en de gekozen generics produceren een PWM-frequentie van ongeveer 1 kHz:
\frac{12e6}{(2^8 - 1) * 47} \approx 1 \text{ kHz}

De cnt_bits generiek stuurt een 25-bit zaagtandteller die de LED-duty cycle controleert. De periode is ongeveer 2.8 Hz:

\frac{2^{\mathit{cnt\_bits}}}{\mathit{clk\_hz}} = \frac{2^{25}}{12e6} \approx 2.8 \text{ Hz}

Alleen LED 5 wordt gebruikt; de andere LED's zijn laag getrokken om streulicht te voorkomen.

Interne Signalen en Bekabeling

 architecture str of pwm_led is signal rst : std_logic; signal cnt : unsigned(cnt_bits - 1 downto 0); signal pwm_out : std_logic; alias duty_cycle is cnt(cnt'high downto cnt'length - pwm_bits); begin led_1 <= '0'; led_2 <= '0'; led_3 <= '0'; led_4 <= '0'; led_5 <= pwm_out; 

Het rst signaal is een gesynchroniseerde, niet-geïnverteerde versie van de externe reset. De alias duty_cycle map de meest significante bits van cnt naar de PWM-controller.

Module Instantiations

 RESET : entity work.reset(rtl) port map ( clk => clk, rst_n => rst_n, rst => rst ); PWM : entity work.pwm(rtl) generic map ( pwm_bits => pwm_bits, clk_cnt_len => clk_cnt_len ) port map ( clk => clk, rst => rst, duty_cycle => duty_cycle, pwm_out => pwm_out ); COUNTER : entity work.counter(rtl) generic map ( counter_bits => cnt'length ) port map ( clk => clk, rst => rst, count_enable => '1', counter => cnt ); 

Simulatie

Generics maken het gemakkelijk om simulatie te versnellen. Door cnt_bits en clk_cnt_len te reduceren kunnen we volledige PWM-cycli in een paar microseconden observeren in plaats van seconden.

Testbench instantiatie:

 DUT : entity work.pwm_led(str) generic map ( pwm_bits => 8, cnt_bits => 16, clk_cnt_len => 1 ); 

De waveform in ModelSim toont een zaagtandteller die de LED-helderheid stuurt. Een korte "aan" puls verschijnt aan de top van elke cyclus wanneer de duty cycle zijn maximum bereikt.

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

FPGA Implementatie

Na synthese met iCEcube2 verbruikt het ontwerp slechts 5% van de 64 beschikbare LUTs op het ice40hx1ktq144 deel:

 Resource Usage Report for pwm_led Mapping to part: ice40hx1ktq144 Cell usage: GND 3 uses SB_CARRY 31 uses SB_DFF 5 uses SB_DFFSR 39 uses SB_GB 1 use VCC 3 uses SB_LUT4 64 uses I/O ports: 7 I/O primitives: 7 SB_GB_IO 1 use SB_IO 6 uses I/O Register bits: 0 Register bits not including I/Os: 44 (3%) Total load per clock: pwm_led|clk: 1 @S |Mapping Summary: Total LUTs: 64 (5%) 

Het programmeren van de FPGA via USB met de Diamond standalone programmer was eenvoudig. De resulterende LED-animatie toont een soepel helderheidsverloop gevolgd door een korte verdoning voordat de cyclus herhaalt.

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

Breathing LED met een Sinusgolf Duty Cycle

Het vervangen van de zaagtandteller door een sinusgolf-lookuptabel produceert een natuurlijk "ademend" effect, vaak gezien op notificatie-LED's.

Een PWM-controller implementeren in VHDL: Ontwerp, Simulatie en FPGA-demo

In een komend artikel laten we zien hoe je een sinusgolf-generator bouwt met block RAM en integreert in het pwm_led ontwerp.

Lees het volgende bericht: Hoe een breathing LED-effect te maken met een sinusgolf opgeslagen in block RAM

Zie ook: RC servo-controller met PWM vanaf een FPGA-pin

VHDL

  1. PWM Vermogenscontroller: Bouw een Pulsbreedte Gemoduleerde Lampstuur
  2. Stapsgewijze Gids voor Het Veilig en Efficiënt Laden van Condensatoren
  3. Dynamische string-lijsten in VHDL: Best Practices & Voorbeeld
  4. Een Tcl-gedreven testbench maken voor een VHDL code-lock module
  5. Een VHDL-testbench netjes beëindigen: methoden en best practices
  6. Willekeurige getallen genereren in VHDL: Van Uniform naar OSVVM
  7. Ontwerpen van een robuuste VHDL-ringbuffer FIFO in blok-RAM
  8. Een self-checking testbench in VHDL bouwen – Een praktische gids
  9. Een dynamische gelinkte lijst in VHDL implementeren met protected types en access pointers
  10. In-process procedures benutten voor schonere VHDL FSM-ontwerpen
  11. VHDL-functies onder de knie krijgen: een praktische gids voor efficiënt ontwerp