CID aplicatii 10 : Aplicatii cu numaratoare

De la WikiLabs

Teorie

Numaratoarele au extrem de multe aplicatii in lumea reala, cateva dintre acestea fiind descrise mai jos.


Exemplu: Numaratul apasarilor unui buton

Un prim sistem simplu cu numarator poate fi cel care contorizeaza de cate ori a fost apasat un buton sau, echivalent, cate persoane/masini au trecut printr-un senzor, de cate ori a fost deschisa o usa etc. Acest sistem se poate apoi dezvolta cu usurinta pentru a numara si oamenii care trec invers prin respectivul set de senzori prin adaugarea functionalitatii de up/down la numarator.

Un astfel de sistem simplu (varianta doar cu numarat intrari) ar arata in felul urmator:

Aplicatii numarator exemplu contor senzor.png

Circuitul de debounce are rolul de a "curata" semnalul de intrare, astfel incat chiar si in prezenta zgomotului sau a unei activari indelungate a senzorului/butonului, sa se contorizeze un singur eveniment. O explicatie mai detaliata puteti gasi aici.

Numaratorul numara evenimentele.

Transcodorul pentru display cu 7 segmente este apoi folosit pentru o mai usoara vizualizare a numarului curent.

Observatie: transcodorul pentru 7seg din exemplul de mai jos este in logica negativa. Daca circuitul fizic este in logica pozitiva trebuie adaugat un inversor la iesirea din acesta. De asemnea consider segmentul "a" pe bitul "0" si segmentul "h" pe bitul "6".


Descrierea debouncer-ului (fisierul debounce.v):

module debounce
	#(
		parameter limit = 20'd650000
	) (
		input wire clock,
		input wire in,
		output wire out
	);

reg [19:0] counter; // observatie: si circuitul de debounce foloseste un numarator
reg hit;

assign out = (counter == limit);

always@(posedge clock) 
begin
	if(in == 0) 
		begin
		counter <= 0;
		hit <= 0;        
		end 
	else 
		begin
		if(counter == limit) 
			begin
			hit <= 1;
			counter <= counter + 1;
			end 
		else 
			begin
			if(in == 1 & hit == 0) 
				begin
				counter <= counter + 1;
				end
			end
		end
end

endmodule


Descrierea numaratorului pe 4b(fisierul counter_4b.v):

module counter_4b
	(
		input wire clock,
		input wire reset,
		input wire en,
		output reg [3:0] out
    );
    
always@(posedge clock)
begin
	if(reset == 1)
		begin
		out <=0;
		end
	else
		begin
		if(en == 1)
			begin
			out <= out + 1;
			end
		else
			begin
			out <= out;
			end
		end	
end    
    
endmodule


Descrierea transcodorului pentru afisaj cu 7seg(fisierul transcodor_7seg.v):

module transcodor_7seg				// logica negativa 
	(
		input [3:0] in,
  		output reg [6:0] out
    );
    
always@(*)
begin
	case(in)
		4'd0: out = 7'b1000000;// h....a
		4'd1: out = 7'b1111001;
		4'd2: out = 7'b0100100;
		4'd3: out = 7'b0110000;
		4'd4: out = 7'b0011001;
		4'd5: out = 7'b0010010;
		4'd6: out = 7'b0000010;
		4'd7: out = 7'b1111000;
		4'd8: out = 7'b0000000;
		4'd9: out = 7'b0010000;
		4'd10: out = 7'b0001000;
		4'd11: out = 7'b0000011;
		4'd12: out = 7'b1000110;
		4'd13: out = 7'b0100001;
		4'd14: out = 7'b0000110;
		4'd15: out = 7'b0001110;
		default: out = 7'b1111111;//tot stins 
	endcase 
end  
    
endmodule


Descrierea sistemului, modulul de top (fisierul top.v):

module top
	(
		input wire clock,
		input wire reset,
		input wire button,
		output wire [6:0] out    // logica negativa 
    );
    
wire debounce_0_X_out;
wire [3:0] counter_4b_0_X_out;   
    
debounce
	#(
		.limit(20'd50_000)
	) debounce_0 (
		.clock(clock),
		.in(button),
		.out(debounce_0_X_out)
	);
	    
counter_4b counter_4b_0
	(
		.clock(clock),
		.reset(reset),
		.en(debounce_0_X_out),
		.out(counter_4b_0_X_out)
    );    
    
transcodor_7seg	transcodor_7seg_0			// logica negativa 
	(
		.in(counter_4b_0_X_out),
  		.out(out)
    );    
 
endmodule


Descrierea test bench-ului(fisierul tb.v):

`timescale 1ns / 1ps

module tb();

reg clock_tb;
reg reset_tb;
reg button_tb;
wire [6:0] out_tb;

top dut
	(
		.clock(clock_tb),
		.reset(reset_tb),
		.button(button_tb),
		.out(out_tb)    // logica negativa 
    );

initial
begin
	clock_tb = 0;
	forever
		begin
		#5 clock_tb = ~clock_tb;
		end
end

initial
begin
	reset_tb = 0;
	button_tb = 0;
	
	#50;
	reset_tb = 1;
	#50;
	reset_tb = 0;
	#100;
	
	repeat(5) 	// vreau 5 apasari de buton
		begin
		button_tb = 1;
		repeat(70_000)				
			begin
			@(posedge clock_tb);
			end
		button_tb = 0;
		repeat(70_000)
			begin
			@(posedge clock_tb);
			end
		end
		
	#1000 $stop();
end

endmodule

Proiectul complet se poate descarca de aici: Exemplu_aplicatii_numarator.zip

Exercitii

Exercitiul 1: Divizor de frecventa cu puteri ale lui 2

Divizorul de frecventa are rolul de a genera un semnal de ceas mai lent din semnalul de ceas principal. El este alcatuit doar dintr-un numarator, iar fiecare bit al acestuia va fi practic un semnal de ceas cu frecventa din ce in ce mai mica astfel (la jumate):

bit[0] - frecventa de 2 ori mai mica decat semnalul de ceas

bit[1] - frecventa de 2 ori mai mica decat bit[0], deci de 4 ori mai mica decat semnalul de ceas

bit[2] - frecventa de 2 ori mai mica decat bit[1], deci de 8 ori mai mica decat semnalul de ceas


Acest lucru se poate observa si in poza ce urmeaza:

Freq divider output.png

Implementati acest circuit, si folosind ledurile prezente pe placa, incercati sa conectati bitul corespunzator la leduri astfel incat cel mai din dreapta led sa clipeasca cu o frecventa cat mai apropiata de 1s.

Observatie: Printr-un astfel de divizor de frecventa minimal, un numar foarte mic de frecvente poate fi generat. Daca se doreste generarea unui semnal periodic cu o perioada exacta, diferita de cele posibile prin acest mecanism, se poate construi urmatorul circuit:


Exercitiul 2: Divizor de frecventa ce poate genera orice perioada

Un astfel de divizor de frecventa este construit ca in figura de mai jos:

Freq div orice freq.png

Prin adaugarea constantei "limit", perioada semnalului de iesire poate sa fie orice multiplu a perioadei semnalului de ceas.

Bistabilul de tip t, se modifica atunci cand limita este atinsa si practic iesirea lui este semnalul periodic dorit (cu observatia ca o perioada a semnalului de iesire necesita 2 numarari pana la limita).

Sa se calculeze valoarea necesara pentru "limit" astfel incat pe leduri sa se genereze semnal cu perioada exact 1s.


Observatie: In FPGA exista fizic trasee speciale pentru reset si ca sinteza sa le foloseasca pe acestea in mod optim nu este recomandat sa se puna logica (cum e aici poarta sau) pe acestea. Modificati schema de mai sus astfel incat sa eliminati respectiva poarta de pe reset. (Sfat: numaratorul va avea acum si intrari de load: semnalul de comanda si data propriu zisa)

Exercitiul 3: PWM (Pulse Width Modulation)

PWM este un concept folosit foarte des in multiple ramuri ale ingineriei, de la transmisiunea informatiei pana la controlul motoarelor sau al intensitatii ledurilor din instalatii de Craciun. El se refera la a avea un semnal periodic cu factor de umplere variabil, asa cum este aratat in poza de mai jos :

Pwm.png


Generarea unui astfel de semnal periodic se face printr-un numarator si un comparator, ca in figura de mai jos:

Pwm schematic.png


Raportul dintre limita pusa si valoarea la care numaratorul se reseteaza este practic factorul de umplere selectat.

Pentru testare pe placa, cei 6b ai limitei provin de la switch-uri si butoane. Afisarea se face pe led[0].

Testati acest circuit in simulare si apoi implementati pe placa pentru o valoare fixa a limitei. Implementati in paralel mai multe generatoare de semnal PWM cu limite diferite, astfel incat sa observati diferentele de intentistate dintre ledurile comandate de acestea.


Exercitiul 4: PWM cu limita variabila

Un exemplu foarte uzual de folosire a PWM este aprinderea ledurilor cu diverse pattern-uri. Factorul de umplere determina intensitatea cu care ledul este aprins. Un PWM cu o limita variabila automata va face ledul sa para din ce in ce mai stins sau din ce in ce mai luminos (exemplu: instalatii de Craciun). Combinand asta cu leduri RGB se obtin efecte de schimbare a culorii ledului aparent la intamplare.

Un astfel de circuit se realizeaza punand inca un numarator in locul limitei, ca in poza de mai jos:

Pwm duty cycle up schematic.png


Pentru o functionalitate suplimentara, anume a pastra un anumit factor de umplere mai multe perioade, am adaugat inca un numarator.

Pentru claritatea desenului, nu am mai tras efectiv firul de ceas catre intrarile unde acesta se duce.


Rezultatul este:

Pwm duty cycle up output.png


Testati circuitul propus prin simulare si apoi vizualizati implementarea sa pe placa. Alegeti limite potrivite astfel incat sa puteti observa usor rezultatul.


Bonus: Incercati sa adaugati si parametri pentru LIMIT_DUTY_CYCLE_LOW si LIMIT_DUTY_CYCLE_HIGH, care sa permita factorului de umplere sa varieze doar intre ele (numaratorul va avea nevoie de intrari pentru comanda de load si data load, ca sa poata incepe de la orice valoare).

Bonus: Modificati circuitul astfel incat semnalul de iesire sa isi schimbe sensul de variatie al factorului de umplere cand ajunge cu acesta la capat. In forma curenta factorul de umplere creste de la 0% la 100% si apoi se reseteaza brusc la 0%, repetand acest ciclu. Se vrea ca la ajungerea la 100% sa inceapa o scadere treptata catre 0%, urmata apoi de o urcare s.a.m.d.

Exercitiul 5: Ceas

Un ceas poate fi construit cifra cu cifra, folosind numaratoare si comparatoare (si transcodoare pentru display cu 7seg ca sa se vada totul mai bine pe placa).

Incercati sa implementati un ceas cu milisecunde, secunde si minute in varianta comportamentala.

Observatie: Va fi nevoie fie separat, fie in modul de un numarator cu frecventa rezolutiei dorite (aici 1 ms).

Observatie: Codul va contine multe "if" de tipul "daca cifra unitatilor secundelor a ajuns la 10, se face 0 si cresc cifra zecilor", repetate pentru fiecare cifra.


Incercati si o implementare structurala a ceasului, mergand pe aceeasi idee. Fiecare cifra va contine numaratorul ei. Cand numaratorul unei cifre ajunge ajunge la limita sa, va da enable pentru numaratorul cifrei urmatoare. Daca este nevoie se pot folosi si porti aditionale (ex: ca sa creasca minutul cifra zecilor secundelor trebuie sa fie 5 si cifra unitatilor secundelor trebuie sa fie 9).

Observatie: Va fi nevoie de un numarator cu frecventa rezolutiei dorite (aici 1 ms sau 1s).

Observatie: Pentru a scrie mai putin puteti face un numarator doar cu secunde si minute. Principiul de baza este acelasi si daca aveati milisecunde.

Observatie: Simularea a cateva milisecunde sau chiar secunde este un proces indelungat (dureaza minute-ore fizice), astfel strict pentru simulari se recomanda ceasul sa functioneze cu microsecunde sau milisecunde maxim.