# Digital Tachometer VHDL

Discussion in 'General Electronics Discussion' started by tachometer, Mar 25, 2012.

1. ### tachometer

14
0
Mar 25, 2012
Hi,

I have this project where I have to design a tachometer using VHDL. The thing is I m pretty new to the field of electronic projects and I m very new to VHDL.
The project asks me to count the Rotations/per Minute from a motor.
It has to be in the range 19-98 RPM, the measurement time is 1.1s, and the display resolution is 0.1 .

The good part is that I dont have to create a system to get this rotations, they are given to me thru a generator. I just have to count the pulses and display the result.
http://imageshack.us/photo/my-images/546/blockvk.jpg
So, I tought I'll use an AND gate with the signal from the general as one input and the clock divided so I'll obtain the period of 1.1s .
http://imageshack.us/photo/my-images/839/clockbg.jpg/

What I have to do next is when i press a button it will count the pulses for 1.1s and then display the result. [During counting the display will be OFF].

Sorry for the long post but the first problem resumes to this. How do i design a clock divider so I can get a period of 1.1s.
I have a basic clock divider code where i count the rising edges and then i change the state of clock. But how many edges i have to count?

Code:
```ENTITY CLKDIV IS
PORT(
CLK: IN STD_LOGIC;
CLKOUT: OUT STD_LOGIC
);
END CLKDIV;

ARCHITECTURE DIV OF CLKDIV IS
BEGIN
PROCESS (CLK)
VARIABLE COUNT: INTEGER RANGE 0 TO ????  :=0;
VARIABLE STATE: STD_LOGIC := '1';
BEGIN
IF(RISING_EDGE (CLK)) THEN
IF(COUNT=  )THEN
COUNT:=0;
STATE:= '1';
ELSE
COUNT := COUNT+1;
END IF;
IF(COUNT=)
STATE:= NOT STATE;
END IF;
END IF;
CLKOUT <= STATE;
END PROCESS;
END DIV;
```

2. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
A clock of frequency xxx Hz has xxx cycles per second. So in 1.1 seconds this clock will have No_of_cycles=xxxHz*1.1s. Now that you know the number of clock cycles, you know the denominator (or divisor) for your counter (or clock scaler).

Although it's been some time I last programmed VHDL, may I suggest you change the architecture of your divider slightly? I would include the state of the pushbutton as one process variable. Then I would look for the state of this button and keep the counter in reset (output of the preocess = "0") until the button is pressed. If the button is pressed, I would change the output of the process to "1" and start counting until 1.1s have passed. During counting the state of the button is ignored, so once the counter starts running, you can release the button or press it without affecting the counting operation. If the counter reaches the llimit as calculated above, bring it back to the reset state, set the process output to "0" and start looking for the state of the button again.
You can then directly use the output of the counter as one input of the AND gate for counting the RPMs in a second counter.

Harald

3. ### tachometer

14
0
Mar 25, 2012
I thought that if 50MHZ means a 0.2us period,then because 1.s/0.2us =5.5*10^7 I should count 5.5*10^7 rising edges and then change the state to 0. Am I right? Is this gonna get a 0.909 Hertz signal?

I was about to include the button but in the main process.. now I m just doing the individual blocks.
CLKDIV, AND, COUNTER (I m gonna use 2 bcd counters one for units and the other for above 9 connected to some MUX ), and the 7 segments Display with Common Anode.

Last edited: Mar 27, 2012
4. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
1/50 MHz = 20 ns, not 200 ns as you write. But 55*10^6 counts will give you 1.1 s, that's right.

Harald

5. ### tachometer

14
0
Mar 25, 2012
Thank you! I will keep posting my progress. I m pretty sure I am going to have some problems with those BCDs

6. ### tachometer

14
0
Mar 25, 2012
Is there any faster way to do this?
Cause the simulation for CLKDIV takes forever. Obviously it has to count to 55 000 000 but i thought it will be a lot faster and I m afraid this will affect my entire system. After i press the button in 1.1 sec I should have a result..

7. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
You shouldn't expect a simulation to run in real time.
You could use a much slower clock, e.g. 50 kHz and count to 55 000, or 50 Hz and count to 55, as long as this doesn't affect other aspects/functions of your simulation.

Harald

8. ### tachometer

14
0
Mar 25, 2012
It s me again.
So i've done most of my project. The only problem i have to figure out now it s this.
So i have a button. When i press the button the system should start and display the result in 1.1s. During this time the display should be off.
What buggs me its this button. I know i have to use it in my clock divider and to reset my counters but i`m not quite sure how to do so.
Here is the code i have for the 1.1s high generator.

entity clkdiv is
port (
CLK: in std_logic;
CLKOUT: out std_logic;
PB: in std_logic -- state of the button
);
end clkdiv;

architecture DIV of clkdiv is
begin
process (CLK, PB)
variable MAXCOUNT: integer :=5500;
variable COUNT: integer range 0 to MAXCOUNT :=0 ;
begin
if (PB='1') then
if(rising_edge (CLK)) then
if(COUNT < MAXCOUNT ) then
COUNT :=COUNT+1;
CLKOUT <= '1';
end if;
if (COUNT=MAXCOUNT) then
PUSHBUTTON:= 0;
COUNT :=0;
CLKOUT <= '0';
end if;
end if;
else
COUNT :=0;
CLKOUT <= '0';
end if;
end process;
end DIV;

Should the button be an entity itself?

Thanks.

9. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
As I said, it's been soooome time I last programmed VHDL. But a few things strike me as illogical:

1) There is PB as an input to your process, but within the process you use PUSHBUTTON and PB without declaring PUSHBUTTON.

2) The statement if (PB='1') then looks for PB (presumedly the PUSHBUTTON) to be 1. This will work only as long as PB is pressed (1). I don't think it is your intention that the user has to keep the button pressed until 1.1 s are over. I suggest you use an internal state variable, say BUTTON_PRESSED and set this variable initially 0. Then on detecting a rising edge of PB you set BUTTON_PRESSED:=1. The above statement in italics is modified to look for the state variable, not for PB: if (BUTTON_PRESSED='1') then . At the end of the count cycles (1.1 s) you reset BUTTON_PRESSED:=0.
I think this is the statement
if (COUNT=MAXCOUNT) then
PUSHBUTTON:= 0;

which becomes
if (COUNT=MAXCOUNT) then
BUTTON_PRESSED:= 0;

Don't forget to declare the state variable BUTTON_PRESSED.

This may not be all you need but it should help you get along. I'm no VHDL expert - I think I repeat myself

Harald

10. ### tachometer

14
0
Mar 25, 2012
Yeah, sorry. Thats a mistake from me. I've copied the wrong code.. I've tried different ways that s why there is a PB and a PUSHBUTTON also, in the final code instead of pushbutton it s PB.

I guess what I'm trying to figure out is that if the state of the button is a signal or a variable. You solution seems excelent. I dont understand yet ". Then on detecting a rising edge of PB " of button but i`ll look into it From what you re saying it looks like the button acts as a clock or something

Thanks.

11. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
is essentially the same as detecting a clock edge:
Harald

12. ### tachometer

14
0
Mar 25, 2012
Ok, keep in mind that I m very new to this
I m trying to create the system entity and to connect everything togeter.
Now, i have this:

http://imageshack.us/photo/my-images/859/schematic.jpg/

The counters are described in the same entity. Can i instantiate the same entity multiple times? My question is how can i connect all the counters to each MUX?

Thanks for the help again.

What I'm thinking is this:
architecture SYS of System is
...

component BCD_Counter
port(
clock: in std_logic;
reset: in std_logic;
carry: out std_logic;
output: out std_logic_vector(0 to 3));
end component;

signal IES_01 : std_logic_vector(0 to 3);
signal IES_1 : std_logic_vector(0 to 3);
signal IES_10 : std_logic_vector(0 to 3);
signal Scarry: std_logic;
signal Scarry1: std_logic;

counter01: BCD_Counter portmap (clock<=CLK, reset<=RST, carry<=Scarry, out<=IES_01 );
counter1 : BCD_Counter portmap (clock<=Scarry, reset<=RST, carry<=Scarry1, out<=IES_1 );
counter10 : BCD_Counter portmap (clock<=Scarry1, reset<=RST, carry<=Carry, out<=IES_10 );

component mux4 is
port ( INS: in std_logic_vector (0 to 3);
EN: in std_logic;
Sel: in std_logic_vector (0 to 1);
outmux: out std_logic
);
end component;

signal INS0 : std_logic_vector(0 to 3):= ??? IES_01(0) IES_1(0) IES_10(0) 1 ???
signal INS1 : std_logic_vector(0 to 3):= ??? IES_01(1) IES_1(1) IES_10(1) 1 ???
signal INS2 : std_logic_vector(0 to 3):= ??? IES_01(2) IES_1(2) IES_10(2) 1 ???
signal INS3 : std_logic_vector(0 to 3):= ??? IES_01(3) IES_1(3) IES_10(3) 1 ???

signal OUTMUXES : std_logic_vector(0 to 3);

mux1 : mux4 portmap (INS0, EN, Sel, OUTMUXES(0) );
mux2 : mux4 portmap (INS1, EN, Sel, OUTMUXES(1) );

..........

I hope you understand what I m trying to ask..

Last edited: May 7, 2012

14
0
Mar 25, 2012

14
0
Mar 25, 2012
15. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
I have no real idea.
Does this happen at the code loacations you show? Or maybe somewhere else and you have a collision between more than one statement trying to assign conflicting values to the variables?

Harald

16. ### tachometer

14
0
Mar 25, 2012
It happens for every output of this entity.. it gets the '1' s right but instead of '0' it s X.
I m going to ask one of my teachers today.

17. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
Sorry for not being able to help.
Would you mind posting the result?

Harald

18. ### tachometer

14
0
Mar 25, 2012
I found what the problem was:
My code was something like this:

display: sevseg port map (INM=>sig6, nodisp=>sig1, AtoG=>sigout);
....
if(rotatii<19 or rotatii>98 or (sig5='1')) then
sigout<= "1111111";
end if;
....

From what i can tell it didnt like the fact that i was using the same signal sigout and that I was forcing it to '1111111' when the range condition wasnt satisfied.
so i changed the signals and now it is fine... well almost fine.

So i have to count the rotations, which it does just fine but if they go out of a range (<19 or >98), then I should close the display. and the problem is that i dont know how to do that.

process(CLOCK)
begin

if(Scarry2='1')then
sig5<='1';
end if;
if(rotatii<19 or rotatii>98 or (sig5='1')) then
sigout<= "1111111";
else
sigout<=sig7;
end if;

end process;
end sys;

I tried this, where sig7 its the signal I get from the seven segments display and sigout is the signal which controles the leds for display, but this is not a solution because even if I'm in the range sigout it s delayed compared to sig7 because the process it s CLOCK sensitive.

I really dont know how I m gonna do this I ve attached my system.vhd if that s any help.

Thank you.

File size:
3.5 KB
Views:
233
19. ### Harald KappModeratorModerator

11,803
2,749
Nov 17, 2011
Your process is clock sensitive but not edge sensitive:
There is no reference to the rising clock edge.
As a guess you may try to insert something like
into this process and into this process
This may help, because both processes are now synchronyzed to the rising edge of the clock.
Harald

20. ### tachometer

14
0
Mar 25, 2012
What I did is this: process(CLOCK,RESET, sig7)

And now, finally my project seems to be working Starting tomorrow I m going to do the fpga implementation. My deadline is next friday.

Thank you Harald for all the help, you made me continue in times when I was about to give up. Hope you`ll win the lottery soon as a reward for your kindness .