Prellende Tasten bzw. deren Entprellung - das Thema hat sooooooo nen Bart. Jeder Elektroniker weiss was gemeint ist - das Gezappel an den Signalflanken, wenn eine Taste gedrückt oder losgelassen wird. Übelster Urschleim, Problem seit ewig bekannt, Lösungen ebenso. Trotzdem soll es immer mal wieder dem einen oder anderen Bastler passieren, dass er zu optimistisch an das Thema herangeht und bei der Inbetriebnahme einer Schaltung feststellen muss dass die Dinge nicht so machen wie sie sollen. Kommt gelegentlich auch mal bei Konsumgütern vor - ich hatte mal eine Digitalkamera, deren Ein/Aus-Taster ein Alptraum war...
Lösungen gibt es als Hardware (wird selten so gemacht) und als Software - ein Mikrocontroller erledigt sowas ganz locker nebenbei, so wird's dann meist auch gemacht, aus Kostengründen. Bei der hier vorgestellten Lösung kann man sich streiten - sieht wie Software aus, ist aber dann im FPGA eigentlich Hardware. Darüber ob FPGA Zeugs nun Hardware oder Software ist, streiten sich sowieso die Geister, ich mag mich an diesem Streit mangels klarer Definitionen nicht beteiligen.
Das Modul zum Entprellen habe ich von der Website
http://www.fpga4fun.com gemopst. Ich sag immer "besser gut geklaut als schlecht selber ausgedacht". Hier ist erst mal der neue Code, Erklärungen folgen:
Code: Alles auswählen
// Beispiel 3: Ein/Aus-Taster,
// diesmal entprellt
module main (
input wire CLK_in,
input wire Key_in,
output reg LED_out
);
wire Key1;
PushButton_Debouncer Entpreller1 (.clk(CLK_in), .PB(Key_in), .PB_state(Key1));
always @ (posedge Key1) LED_out <= ~LED_out;
endmodule
// ***** Ende Hauptmodul *****
// folgender Code wurde aus einem Beispiel auf www.fpga4fun.com entnommen:
module PushButton_Debouncer(
input clk,
input PB, // "PB" is the glitchy, asynchronous to clk, active low push-button signal
// from which we make three outputs, all synchronous to the clock
output reg PB_state, // 1 as long as the push-button is active (down)
output PB_down, // 1 for one clock cycle when the push-button goes down (i.e. just pushed)
output PB_up // 1 for one clock cycle when the push-button goes up (i.e. just released)
);
// First use two flip-flops to synchronize the PB signal the "clk" clock domain
reg PB_sync_0; always @(posedge clk) PB_sync_0 <= ~PB; // invert PB to make PB_sync_0 active high
reg PB_sync_1; always @(posedge clk) PB_sync_1 <= PB_sync_0;
// Next declare a 16-bits counter
reg [15:0] PB_cnt;
// When the push-button is pushed or released, we increment the counter
// The counter has to be maxed out before we decide that the push-button state has changed
wire PB_idle = (PB_state==PB_sync_1);
wire PB_cnt_max = &PB_cnt; // true when all bits of PB_cnt are 1's
always @(posedge clk)
if(PB_idle)
PB_cnt <= 0; // nothing's going on
else
begin
PB_cnt <= PB_cnt + 16'd1; // something's going on, increment the counter
if(PB_cnt_max) PB_state <= ~PB_state; // if the counter is maxed out, PB changed!
end
assign PB_down = ~PB_idle & PB_cnt_max & ~PB_state;
assign PB_up = ~PB_idle & PB_cnt_max & PB_state;
endmodule
Falls der Inhalt des Moduls
PushButton_Debouncer derzeit noch rätselhaft erscheint, am besten zunächst ignoriern. Erklärungen und Beispiele zu den dort verwendeten Techniken folgen später. Hier geht es zunächst mal um das Einbinden fertiger Module.
Im Hauptmodul wird eine Instanz namens
Entpreller1 des Moduls
PushButton_Debouncer angelegt. Daran erkennt der Compiler übrigens, wer der Chef ist: dasjenige Modul, von dem keine Instanzen erzeugt werden wird als Hauptmodul, als "oberste Ebene" des Projekts angesehen. Der hier verwendete Modulname
main hat (im Gegensatz zu C) keine deklarative Bedeutung. Ich hab den rein willkürlich bzw. aus Gewohnheit gewählt.
Die Portübergabe zwischen Hauptmodul und der Instanz des Debouncers erfolgt im Beispiel durch benannte Ports. Wenn man es so macht ist die Reihenfolge dann egal, man kann sogar Ports weglassen falls diese nicht benötigt werden. Im Beipiel werden die beiden Ports PB_down und PB_up nicht verwertet.
Alternativ zur Portübergabe per Name ist in Verilog auch die Portübergabe per Reihenfolge möglich, so wie man das von "klassischen" Programmiersprachen kennt. Davon wird allerdings abgeraten, da Module bei "echten" Projekten schnell unüberschaubar viele Ports haben können.
Das Debouncer-Modul kann man als eine Art Filter sehen, das aus dem "schmutzigen" Signal Key das "saubere" Signal Key1 macht zur weiteren Verwendung. Gelernte Programmierer der alten Schule werden sich vielleicht fragen - wieviel Performance nimmt das weg? Die Antwort ist erfreulich: keine! Da es hier um Hardware geht, wühlt jenes Modul selbstständig vor sich hin, ohne unserem super-Lichtumschalter Performance zu stehlen. Die Freunde des Hive bzw. des Propeller haben da sicher eher ein Verständnis
Vielleicht ist jemandem aufgefallen, dass in diesem Beispiel zum ersten Mal eine "Clock" auftaucht. Die ersten zwei Beispiele haben gezeigt, dass es im Prinzip auch ohne geht. Allerdings ist bei komplexeren FPGA Designs fast immer eine (manchmal sogar mehrere) Clocks mit im Spiel. Auf altdeutsch Taktgeber.
Eventuell fällt auf, dass im Hauptmodul in der always Anweisung jetzt auf die positive Flanke "posedge" des Tasters reagiert wird. Das rühert daher, dass die direkte Hardware-Taste am FPGA (in Beispiel 2 verwendet) low-aktiv ist. Der Debouncer dreht allerdings die Polarität um, so dass das gefilterte Taster-Signal high-aktiv ist. Während man es bei konventioneller Logik oft mit low-aktiven Signalen zu tun hat gibt es im Innern von FPGA keinen triftigen Grund, so zu designen. Modelle mit high aktiven Signalen sind besser zu lesen und deshalb üblich.