PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : VHDL unerklährliche Wartezeit nach Tastendruck



todo
17.09.2009, 10:22
Hallo Leute,

ich habe hier meine ersten Schritte in VHDL gepostet. Das Programm lässt eine LED mit verschiedenen Frequenzen (je nach Tastereingabe blinken).
Drückt man Taster 1 (sBtn0_i), Taster 2 (sBtn1_i) oder Taster 3 (sBtn2_i), dann blinkt die LED entweder mit 0,125Hz, 0,25Hz, oder 0,5 Hz.

Haupt-Entity mit eigentlichem Blink-Prozess ganz am Ende ist:



library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;



entity blinky_tast_top is

generic (
iTWaitDebounce : integer range 0 to 99999999 := 50000
);

port (
sClk_i : in std_logic;
sBtn0_i, sBtn1_i, sBtn2_i : in std_logic;
sLed_o : out std_logic
);

end;



architecture rtl of blinky_tast_top is


-- components -----------------------------------------------------------------------

component debouncer

generic (
iTWait : integer range 0 to 99999999
);

port (
sClk_i, sRst_i : in std_logic;
sPb_i : in std_logic;
sPb_debounced_o : out std_logic
);

end component;


-- signals -------------------------------------------------------------------------

signal sCnt_s : std_logic_vector(27 downto 0) := x"0000000";
signal sLed_s : std_logic :='0';
signal iTimeCnt_s : integer range 0 to 99999999 := 50000000;
signal sBtn0_s, sBtn1_s, sBtn2_s : std_logic := '1';



begin




---===============================Debounce Button 1===============================---

DebounceBtn0: debouncer
generic map (
iTWait => iTWaitDebounce
)
port map (
sClk_i => sClk_i,
sRst_i => 'L', -- Reset not required -> always "low"
sPb_i => sBtn0_i,
sPb_debounced_o => sBtn0_s
);




---===============================Debounce Button 2===============================---

DebounceBtn1: debouncer
generic map (
iTWait => iTWaitDebounce
)
port map (
sClk_i => sClk_i,
sRst_i => 'L', -- Reset not required -> always "low"
sPb_i => sBtn1_i,
sPb_debounced_o => sBtn1_s
);




---===============================Debounce Button 3===============================---

DebounceBtn2: debouncer
generic map (
iTWait => iTWaitDebounce
)
port map (
sClk_i => sClk_i,
sRst_i => 'L', -- Reset not required -> always "low"
sPb_i => sBtn2_i,
sPb_debounced_o => sBtn2_s
);




---================Change the blinking frequency by Button input==================---

DetectSpeed: process(sClk_i)
begin

if (sBtn0_s = '0') then

iTimeCnt_s <= 12500000;

elsif (sBtn1_s = '0') then

iTimeCnt_s <= 25000000;

elsif (sBtn2_s = '0') then

iTimeCnt_s <= 50000000;

end if;
end process;




---===============================Toggle the LEDs=================================---

Toggle: process(sClk_i) -- sClk_i -> 50Mhz
begin
if rising_edge(sClk_i) then

sCnt_s <= sCnt_s + 1; -- every clock cycle -> increment

if sCnt_s = iTimeCnt_s then -- increment until count = 12500000 (Btn0)or 25000000 (Btn1) or 50000000 (Btn2)

sCnt_s <= (others=>'0'); -- reset counter
sLed_s <= not sLed_s; -- toggle LED

end if;
end if;
end process;

sLed_o <= sLed_s;

end;


Und die Entity der Entprell-Routine:



library ieee;
use ieee.std_logic_1164.all;



entity debouncer is

generic (
iTWait : integer range 0 to 99999999
);

port (
sClk_i, sRst_i : in std_logic; -- clk frequency = 50Mhz
sPb_i : in std_logic;
sPb_debounced_o : out std_logic
);

end entity debouncer;



architecture rtl of debouncer is


-- signals ------------------------------------------------------------------------

signal iCntWait_s : integer range 0 to 99999999;
signal sClkDiv_s : std_logic;
signal sPb_sampled_s : std_logic;


begin



---===============================Divide Frequency===============================---

DivideFrequency: process(sClk_i, sRst_i)
begin

if sRst_i = '1' then

sClkDiv_s <= '0';
iCntWait_s <= 0;

elsif rising_edge(sClk_i) then

if iCntWait_s = iTWait then
iCntWait_s <= 0;
sClkDiv_s <= '1';
else
iCntWait_s <= iCntWait_s + 1;
sClkDiv_s <= '0';
end if;

end if;
end process DivideFrequency;



---============================Debounce Push Button===============================---

debounce_pb: process(sClk_i) is
begin
if rising_edge(sClk_i) then

if sClkDiv_s='1' then
if sPb_i = sPb_sampled_s then
sPb_debounced_o <= sPb_i;
end if;
end if;

sPb_sampled_s <= sPb_i;

end if;
end process debounce_pb;

end architecture rtl;


Wenn ich den Button 3 (sBtn2_i) drücke, verharrt die LED 5 s (also eine Ewigkeit, wenn man bedenkt, dass der Eingangstakt 50 MHz beträgt) in ihrem Zustand, bevor sie die Dem Knopfdruck entsprechende Frequenz annimmt. Dies passiert aber nur wenn ich gerade diesen Button drücke. Bei den anderen kommt dies nicht vor und ich kann mir dieses Verhalten nicht erklären.

Weiß jemand, woran dies liegen könnte, oder wo ich nach dem Fehler suchen kann.

Ich würde mich freuen, wenn vielleicht ein erfahrener VHDL-Programmierer prinzipiell seine Meinung zum Code abgeben könnte. Wie gesagt, es ist mein erster!

todo
18.09.2009, 11:32
Ich habe gestern den Code nochmal etwas geändert:

- Ich habe die Entprell-Routine rausgeworfen, da ich gesehen habe, dass bei den Tastern RC-Glieder sind, mit einer Zeitkonstante von 100ms.

- Ich habe die Sensitivity List den Prozess geändert, der die Tasten abfragt. Zum einen habe ich die Taktfrequenz aus der Sensitivity-List herausgenommen, und stattdessen die Tastersignale reingepackt. Es ist ja sinnvoller den Prozess aufzurufen, wenn sich ein Taster ändert. Ausserdem habe ich einen 4-ten Taster eingebaut. Bei dessen Betätigung wird eine Subtraktion zur Reduktion des Zählersignals (iTimeCnt_s) ausgeführt. Dieses Signal legt fest wie lange der Zähler hochzählt bis die LED getoggelt wird und bestimmt damit die Blinkfrequenz festlegt. Ziel ist es also damit die Blinkfrequenz durch Tastendruck stetig zu erhöhen. Aufgrund dieser Subtraktionsoperation hab ich alle Variablen des Zählers (iCnt_s iTimeCnt), zu Integer gemacht.

Hier der entsprechende Code des geänderten Prozesses:



---================Change the blinking frequency by Button input==================---

DetectSpeed: process(sBtn0_i, sBtn1_i, sBtn2_i, sBtn3_i)
begin

if (sBtn0_i = '0') then

iTimeCnt_s <= 12500000;

elsif (sBtn1_i = '0') then

iTimeCnt_s <= 25000000;

elsif (sBtn2_i = '0') then

iTimeCnt_s <= 50000000;

elsif (sBtn3_i = '0') and (iTimeCnt_s > 12500000) then

iTimeCnt_s <= iTimeCnt_s - 12500000;

end if;
end process;


Und der gesamte Code des Projektes, das jetzt nur noch aus einer Entity besteht, da die Entprellung rausgeflogen ist:



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



entity blinky_tast_top is

port (
sClk_i : in std_logic;
sBtn0_i, sBtn1_i, sBtn2_i, sBtn3_i : in std_logic;
sLed_o : out std_logic
);

end;



architecture rtl of blinky_tast_top is


-- signals -------------------------------------------------------------------------

signal svCnt_s : integer range 0 to 99999999 := 0;
signal iTimeCnt_s : integer range 0 to 99999999 := 50000000;
signal sLed_s : std_logic :='0';
signal sBtn0_s, sBtn1_s, sBtn2_s, sBtn3_s : std_logic := '1';



begin



---================Change the blinking frequency by Button input==================---

DetectSpeed: process(sBtn0_i, sBtn1_i, sBtn2_i, sBtn3_i)
begin

if (sBtn0_i = '0') then

iTimeCnt_s <= 12500000;

elsif (sBtn1_i = '0') then

iTimeCnt_s <= 25000000;

elsif (sBtn2_i = '0') then

iTimeCnt_s <= 50000000;

elsif (sBtn3_i = '0') and (iTimeCnt_s > 12500000) then

iTimeCnt_s <= iTimeCnt_s - 12500000;

end if;
end process;




---===============================Toggle the LEDs=================================---

Toggle: process(sClk_i, iTimeCnt_s) -- sClk_i -> 50Mhz
begin
if rising_edge(sClk_i) then

svCnt_s <= svCnt_s + 1; -- every clock cycle -> increment

if svCnt_s = iTimeCnt_s then -- After several Time (iTimeCnt_s):
--
svCnt_s <= 0; -- -> reset counter
sLed_s <= not sLed_s; -- -> toggle LED

end if;
end if;
end process;

sLed_o <= sLed_s;

end;


Leider funktioniert das mit der Subtraktion noch nicht. Und wieder einmal weiß ich nicht, was ich falsch gemacht habe. Wenn ich beispielsweise die zweite Taste betätige (sBtn1_s) habe ich Zählt der Counter bis 25000000. Wenn ich danach die neu hinzugefügte Taste betätige, sollte der counter (25000000-12500000=12500000) die Blinkfrequenz verdoppeln. Tut er aber nicht. Stattdessen blinkt die LED langsamer. Ich habe aber nirgends einen Überlauf oder ähnliches

Kann jemand mit Erfahrung die Subtraktion mal beurteielen? Ich kann doch mit Integer addieren und subtrahieren ...

_werwurm_
23.09.2009, 19:48
Hallo!

vorab: ich bin leider jemand der _nicht_ viel Erfahrung hat...

So ganz verstehe ich daher das Programm noch nicht. Soweit ich das überblicke wird iTimeCnt_s geändert wenn du eine Taste drückst. Aber was ist wenn zu dem Zeitpunkt svCnt_s schon größer ist als der neue iTimeCnt_s Wert?

Kann es außerdem nicht passieren, daß an der Stelle

elsif (sBtn3_i = '0') and (iTimeCnt_s > 12500000) then ....

die Subtraktion mehrfach ausgeführt wird (so lange wie man den Knopf gedrückt hält)? Muß man da nicht noch auf eine Flanke triggern oder so was?

Warum die LED langsamer blinkt erschließt sich mir im Augenblick aber auch nicht. Ist sicher was ganz einfaches, offensichtliches .. wie immer ;)

_werwurm_
24.09.2009, 00:25
Hallo nochmal!

Ich habe deinen Code ausprobiert. Folgende Beobachtungen:

- beim 1. Einschalten ist das Blinken besonders langsam (ungefähr nur halb so schnell wie die mit sBtn2_i einstellbare Frequenz) - irgendwas mit der initialisierung der Werte?!?
- durch drücken von Taste 4 wird immer gleich auf die schnellste Blinkrate geschaltet - es wird also vermutlich mehrfach subtrahiert
- manchmal dauert es nach Druck einer Taste extra lange bevor die gewünschte Operation ausgeführt wird - vermutlich Überlauf weil svCnt_s schon weiter ist als der neue gewünschte Endwert

Ich vermute die Sache ist vielleicht nicht so synthetisierbar wie beabsichtigt. Der Prozess DetectSpeed ist als kombinatorischer Prozess angegeben aber der Ausdruck
iTimeCnt_s <= iTimeCnt_s - 12500000;
ist wohl nicht rein kombinatorisch sondern bezieht sich auf den eigenen früheren Wert. Wenn ich das richtig verstehe darf sowas nur in getakteten Prozessen vorkommen da nur dann das vorher und nachher eindeutig geregelt ist.

Quelle: Grundregeln für synthetisierbaren VHDL-Code (http://www.mikrocontroller.net/articles/VHDL#Grundregeln_f.C3.BCr_synthetisierbaren_VHDL-Code)

Wenn man das Ändern der Blinkfrequenz mit in den Clock Prozess stopft wird es zwar unübersichtlich - aber es scheint zu funktionieren..





library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;



entity blinky_tast_top is

port (
sClk_i : in std_logic;
sBtn0_i, sBtn1_i, sBtn2_i, sBtn3_i : in std_logic;
sLed_o : out std_logic
);

end;



architecture rtl of blinky_tast_top is

-- signals -------------------------------------------------------------------------

signal svCnt_s : integer range 0 to 99999999 := 0;
signal iTimeCnt_s : integer range 0 to 99999999 := 50000000;
signal sLed_s : std_logic :='0';
signal sBtn3_s : std_logic :='1'; -- remember last state of sBtn3_i in sBtn3_s

begin



---==Toggle the LEDs and change the blinking frequency by Button input==---

Toggle: process(sClk_i) -- sClk_i -> 50Mhz
begin
if rising_edge(sClk_i) then

svCnt_s <= svCnt_s + 1; -- every clock cycle -> increment

if svCnt_s = iTimeCnt_s then -- After several Time (iTimeCnt_s):

svCnt_s <= 0; -- -> reset counter
sLed_s <= not sLed_s; -- -> toggle LED

end if;




-- change the blinking frequency by Button input:

if (sBtn0_i = '0') then

iTimeCnt_s <= 12500000;

elsif (sBtn1_i = '0') then

iTimeCnt_s <= 25000000;

elsif (sBtn2_i = '0') then

iTimeCnt_s <= 50000000;

elsif (sBtn3_i = '0') and (iTimeCnt_s > 12500000) and (sBtn3_s = '1') then

iTimeCnt_s <= iTimeCnt_s - 12500000;


end if;



sBtn3_s <= sBtn3_i; -- remember last state of sBtn3_i




-- fix the issue with casual counter overflow:

if (sBtn3_i and sBtn2_i and sBtn1_i and sBtn0_i) = '0' then
svCnt_s <= 0; -- -> reset counter on any button input
end if;


end if;
end process;


sLed_o <= sLed_s;


end;