Hệ thống đèn giao thông

Bài trước chúng ta đã làm quen với Led 7 thanh và lập trình hiển thị nó trên FPGA, trong bài này mình sẽ làm một ứng dụng nho nhỏ với Led 7 thanh nhé👌. Nào bắt tay xây dựng hệ thống đèn giao thông cơ bản với FPGA thôi🚀

1. Nhắc lại một chút về module hiển thị Led 7 thanh nhé!

Bảng giá trị 0 -> 9 hiển thị cho Led 7 thanh cathode(-) chung:

Chữ số a b c d e f g
0 1 1 1 1 1 1 0
1 0 1 1 0 0 0 0
2 1 1 0 1 1 0 1
3 1 1 1 1 0 0 1
4 0 1 1 0 0 1 1
5 1 0 1 1 0 1 1
6 1 0 1 1 1 1 1
7 1 1 1 0 0 0 0
8 1 1 1 1 1 1 1
9 1 1 1 1 0 1 1


2. Viết module xử lý chính cho hệ thống đèn giao thông

module bcdto7segment_cathode(bcd, out);
input [3:0] bcd;
output reg[6:0] out;

always @*
case(bcd)
	4'b0000: out = 7'b1111110; // 0
	4'b0001: out = 7'b0110000; // 1
	4'b0010: out = 7'b1101101; // 2
	4'b0011: out = 7'b1111001; // 3
	4'b0100: out = 7'b0110011; // 4
	4'b0101: out = 7'b1011011; // 5
	4'b0110: out = 7'b1011111; // 6
	4'b0111: out = 7'b1110000; // 7
	4'b1000: out = 7'b1111111; // 8
	4'b1001: out = 7'b1111011; // 9
	default: out = 7'b1111110; // default = 0
endcase
endmodule

Mổ xẻ bài toán: Hệ thống hoạt động ở tần số 40MHz, đèn xanh sáng 40s, đèn vàng sáng 10s, đèn đỏ sáng 50s hiển thị thời gian đếm ngược lên 2 Led 7 thanh và 3 led đơn chỉ trạng thái đèn: xanh, đỏ, vàng.

  • Trước tiên cần xác định đâu là input và output của bài toán: input (clock 40MHz, reset mức 0), output (7 segment 1, 7 segment 2 và led).
  • Tần số 40MHz gấp 40000000 lần tần số 1Hz (thời gian thực) => dùng biến đếm từ 0 đến 39999999 để đánh dấu trạng thái hết 1s (lưu ý: biến đếm này độ dài 26 bits vì [log2(39999999)] làm tròn lên là 26).
  • Tổng thời gian chạy của 3 đèn là 100s nên mình sẽ dùng một biến đếm cnt giảm dần về 0.
  • Đầu ra gồm 3 Led xanh, đỏ, vàng nên mình sẽ dùng biến led gồm 3 bits.
module traffic_light(clk40M, reset, seg1, seg2, led);
input clk40M;
input reset;
output wire [6:0] seg1;
output wire [6:0] seg2;
output reg [2:0] led; //greenLed, yellowLed, redLed
reg[3:0] a;
reg[3:0] b;
reg[25:0] one_second_counter; // 26 bits => 1s
wire one_second_enable;
reg [6:0] cnt;

always @(posedge(clk40M) or negedge(reset))
begin
	if(reset == 0)
		one_second_counter <= 0;
	else begin
		one_second_counter <= one_second_counter + 1;
		if(one_second_counter >= 39999999)
			one_second_counter <= 0;
	end
			
end
assign one_second_enable = (one_second_counter == 39999999) ? 1 : 0;

always @(posedge(clk40M) or negedge(reset))
begin
	if(reset == 0)
		cnt <= 7'b1100100;
	else if(one_second_enable == 1) begin
		cnt <= cnt - 1;
		if(cnt == 0)
			cnt <= 7'b1100100;
	end
end

always @*
begin
	if(cnt >= 60) begin
		a <= (cnt - 60) / 10;
		b <= (cnt - 60) % 10;
		led <= 3'b100;
	end
	if(cnt >= 50 && cnt <= 60) begin
		a <= (cnt - 50) / 10;
		b <= (cnt - 50) % 10;
		led <= 3'b010;
	end
	if(cnt >= 0 && cnt <= 50) begin
		a <= cnt / 10;
		b <= cnt % 10;
		led <= 3'b001;
	end
end	
bcdto7segment_cathode s1(.bcd(a), .out(seg1));
bcdto7segment_cathode s2(.bcd(b), .out(seg2));
endmodule

3. Viết code Test bench để xem kết quả thôi, hồi hộp quá rùi

Ở đây mình để timescale là 1ns, để tạo tần số 40MHz (chu kỳ 25ns) => nửa chu kỳ 12.5ns, nên cứ hết nửa chu kỳ mọi người cho clock = ~clock là được nhé! 

`timescale 1ns / 1ns
module simulation;
reg reset, clk40M;
wire [6:0] seg1;
wire [6:0] seg2;
wire [2:0] led;

traffic_light uut(.clk40M(clk40M), .reset(reset), .seg1(seg1), .seg2(seg2), .led(led));
initial begin
	reset = 0;
	clk40M = 1;
	#10 reset = 1;
	forever #12.5 clk40M = ~clk40M;
end
initial begin
	$monitor("seg1 = %7b, seg2 = %7b, led = %3b", seg1, seg2, led);
end

endmodule

 

Web hosting by Somee.com