Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> VHDL

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

Pulsweitenmodulation (PWM) verwandelt einen digitalen FPGA-Pin in einen leistungsstarken analogen Controller. Durch schnelles Ein- und Ausschalten der Versorgung liefert PWM den exakten durchschnittlichen Strom, den ein Gerät benötigt, ohne je eine echte analoge Spannung zu erzeugen.

Typische PWM-Anwendungen umfassen Audioausgang für Lautsprecher, LED-Dimming und Drehmomentsteuerung von Induktionsmotoren wie Servomotoren, Computerlüftern, Pumpen und bürstenlosen DC-Motoren für Elektrofahrzeuge.

Siehe auch: RC-Servo-Controller mit PWM vom FPGA-Pin

Wie PWM funktioniert

Wenn eine Stromversorgung mit hoher Frequenz zwischen 100 % und 0 % umgeschaltet wird, ist der zeitgemittelte Strom, den die Last sieht, direkt proportional zum Anteil der Zeit, in der die Versorgung eingeschaltet ist. Das folgende Diagramm veranschaulicht das Prinzip:

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

Duty Cycle

Der Duty Cycle definiert, wie lange der PWM-Ausgang während jeder Periode high bleibt. Als Prozentwert ausgedrückt, wird er aus dem Binärzählerwert berechnet:

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

In einer VHDL-Implementierung behandeln wir den Duty Cycle als vorzeichenlose Ganzzahl, was uns die volle Auflösung des PWM-Zählers gibt. Ein 0 % Duty Cycle schaltet den Ausgang permanent aus, während 100 % ihn dauerhaft an hält. Die erreichbare Auflösung wird durch die Zählerbreite bestimmt.

Um den binären Duty-Cycle-Wert in einen Prozentsatz umzurechnen, nutzen Sie:

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

PWM-Frequenz

Die PWM-Frequenz ist der Kehrwert der Periode:

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

Die Wahl der richtigen Frequenz hängt vom Zielgerät ab. Für LEDs reichen einige hundert Hertz, um sichtbares Flimmern zu vermeiden. Bürstenlose DC-Motoren arbeiten typischerweise im Zehner-kilohertz-Bereich; zu niedrige Frequenzen erzeugen mechanische Vibrationen, zu hohe verschwenden Leistung.

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

Da MOSFET-Schalter langsamer sind als FPGA-Logik, kann ein zu schnelles PWM den Transistor in einen teilweise leitenden Zustand versetzen, was zusätzliche Wärme erzeugt.

PWM-Generator-Modul

Im Folgenden eine generische VHDL-Implementierung eines PWM-Controllers, der mit beliebiger Zählerbreite und Taktteiler-Faktor instanziiert werden kann.

Entity-Deklaration

 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; 

Die clk- und rst-Ports bieten synchrone Steuerung. Der duty_cycle-Eingang hat eine Breite, die durch pwm_bits definiert ist, sodass die Zählerauflösung direkt konfigurierbar ist. Das pwm_out-Signal wird an den FPGA-Pin geroutet, der das MOSFET-Gate ansteuert.

Interne Signale

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

Der pwm_cnt-Zähler spiegelt die Duty-Cycle-Breite wider. Der clk_cnt-Zähler implementiert einen optionalen Taktteiler; setzt man clk_cnt_len auf 1, wird der Teiler deaktiviert und die zugehörige Logik entfernt.

Taktteiler-Prozess

 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; 

Mit dem Standard clk_cnt_len = 1 eliminieren Synthese-Tools diesen Prozess automatisch.

PWM-Ausgabe-Prozess

 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; 

Der Zähler inkrementiert nur, wenn clk_cnt null ist, was die PWM-Frequenz effektiv drosselt. Die Bedingung, die pwm_cnt an seinem zweithöchsten Wert zurücksetzt, garantiert, dass 100 % Duty Cycle erreichbar ist.

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

Top-Level-Design

Zur Demonstration des PWM-Controllers steuert das Design die Power-On-LED auf einem Lattice iCEstick FPGA. Der 12-MHz-Oszillator des Boards speist einen Zähler, der die LED-Intensität in einem Sägezahnmuster steuert.

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

Entity-Deklaration

 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; 

Der 12-MHz-Takt und die gewählten Generics erzeugen eine PWM-Frequenz von ca. 1 kHz:
\frac{12e6}{(2^8 - 1) * 47} \approx 1 \text{ kHz}

Der cnt_bits-Generic treibt einen 25-Bit-Sägezahnzähler, der den LED-Duty-Cycle steuert. Seine Periode beträgt ca. 2,8 Hz:

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

Nur LED 5 wird genutzt; die anderen LEDs werden auf low gebunden, um Streulicht zu vermeiden.

Interne Signale und Verdrahtung

 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; 

Das rst-Signal ist eine synchronisierte, nicht-invertierte Version des externen Resets. Der Alias duty_cycle mappt die meistsignifikanten Bits von cnt auf den PWM-Controller.

Modul-Instanziierungen

 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 ); 

Simulation

Generics erleichtern die Beschleunigung der Simulation. Durch Reduzieren von cnt_bits und clk_cnt_len können wir vollständige PWM-Zyklen in wenigen Mikrosekunden statt mehrerer Sekunden beobachten.

Testbench-Instanziierung:

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

Das Wellenform in ModelSim zeigt einen Sägezahnzähler, der die LED-Helligkeit steuert. Ein kurzer „An“-Puls erscheint an der Spitze jedes Zyklus, wenn der Duty Cycle seinen Maximalwert erreicht.

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

FPGA-Implementierung

Nach Synthese mit iCEcube2 verbraucht das Design nur 5 % der 64 verfügbaren LUTs auf dem ice40hx1ktq144-Chip:

 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%) 

Das Programmieren des FPGA über USB mit dem Diamond Standalone Programmer war unkompliziert. Die resultierende LED-Animation zeigt einen sanften Helligkeitsanstieg gefolgt von kurzer Abdunkelung, bevor der Zyklus sich wiederholt.

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

Atmende LED mit Sinuswellen-Duty-Cycle

Ersetzt man den Sägezahnzähler durch eine Sinuswellen-Lookup-Tabelle, entsteht ein natürlicher „Atmungs“-Effekt, wie man ihn von Benachrichtigungs-LEDs kennt.

PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo

In einem kommenden Artikel zeigen wir, wie man einen Sinuswellengenerator mit Block RAM baut und in das pwm_led-Design integriert.

Nächsten Beitrag lesen: Atmende LED mit Sinuswelle im Block RAM erzeugen

Siehe auch: RC-Servo-Controller mit PWM vom FPGA-Pin

VHDL

  1. PWM-Leistungssteller: Pulsweitenmodulierter Lampentreiber bauen
  2. String-Listen in VHDL erstellen: Best Practices & Beispiel
  3. Tcl-gesteuerter Testbench für ein VHDL Code-Lock-Modul erstellen
  4. VHDL-Testbench-Simulation sauber beenden
  5. Zufallszahlen in VHDL generieren: Von Uniform zu OSVVM
  6. Ein robuster VHDL-Ringpuffer-FIFO im Block-RAM entwerfen
  7. Selbstprüfenden VHDL-Testbench erstellen – Praxisleitfaden
  8. Dynamische verkettete Liste in VHDL mit Protected Types und Access-Pointern
  9. In-Prozess-Prozeduren für saubere VHDL-FSM-Entwürfe nutzen
  10. Kondensatoren sicher und effizient laden: Schritt-für-Schritt-Anleitung