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