疫情無情,人間有情,武漢加油,
在家裏,無所事事,把以前的課設給放到網上吧,也順便複習一下。
就貼了一下代碼,不是太詳細,你們都是聰明人,一下子肯定看懂了!
題目是這樣子的
信號控制系統設計
內容及要求:
設計一個十字路口交通控制系統,具體設計要求如下:
(1)東西(用A表示)、南北(用B表示)方向均有綠燈、黃燈、紅燈指示,其持續時間分別是40秒、5秒和45秒, 交通燈運行的切換示意圖和時序圖分別如圖1、圖2所示。
(2)系統設有時鐘,以倒計時方式顯示每一路允許通行的時間。
(3)當東西或南北兩路中任一路出現特殊情況時,系統可由交警手動控制立即進入特殊運行狀態,即紅燈全亮,時鐘停止計時,東西、南北兩路所有車輛停止通行;當特殊運行狀態結束後,系統恢復工作,繼續正常運行。
圖2 交通燈時序圖
(4)完成全部流程:設計規範文檔、模塊設計、代碼輸入、下載驗證等,最後就課程設計本身提交一篇課程設計報告。
這是交通燈的時序模塊
主要的是完成交通燈的時序模塊,註釋很少,忘見諒!
module traffic_led(
input sys_clk,
input sys_rst_n,
input en, //使能標記位
output reg greenA, //LED燈
output reg greenB,
output reg redA,
output reg redB,
output reg yellowA,
output reg yellowB,
output reg [5:0] timeA,
output reg [5:0] timeB
);
//parameter NUM_MAX=26'd50_000_000; //1s延遲
parameter NUM_MAX=26'd50_000; //1ms,仿真時用的,1s太長了
parameter state0=4'b0001;
parameter state1=4'b0010;
parameter state2=4'b0100;
parameter state3=4'b1000;
reg [25:0] count; //延時1s 20ns 1000_000_000/20ns=50_000_000次
reg s_flag;//秒標記位
reg [5:0] count45; //45s延時
reg flag45; //45s標記位
reg [3:0] current_state;
reg [3:0] next_state;
//1s延時函數
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
count <=26'd0;
else if(en) begin
if(count < NUM_MAX-1'b1) begin
count <= count + 1'b1;
s_flag<=1'b0;
end
else begin
s_flag<=1'b1;
count<=26'd0;
end
end
else
count <= count;
end
//45s延時
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
count45 <=6'd45;
flag45 <=1'b0;
end
else if(en) begin
if(s_flag) begin
if(count45 == 6'b1) begin
count45 <=6'd45;
flag45 <=1'b1;
end
else begin
count45<=count45 - 1'b1;
flag45 <=1'b0;
end
end
else begin
count45 <= count45;
flag45 <= flag45;
end
end
else begin
count45 <=count45;
flag45 <=flag45;
end
end
/*這是用狀態機實現的,我使用的是兩段式*/
//狀態的跳轉(時序邏輯)
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
current_state<=state0;
else
current_state <= next_state;
end
//各個狀態的下動作
always @(posedge sys_clk) begin
if(en)begin
case(current_state)
state0:begin
if(count45 == 6'd5)
next_state=state1;
else begin
greenA <=1'b0;
greenB <=1'b1;
redA <=1'b1;
redB <=1'b0;
yellowA<= 1'b1;
yellowB <=1'b1;
timeA <=count45;
timeB <=6'd0;
end
end
state1:begin
if(flag45)
next_state=state2;
else begin
greenA <=1'b1;
greenB <=1'b1;
redA <=1'b1;
redB <=1'b0;
yellowA<= 1'b0;
yellowB <=1'b1;
timeA <=count45;
timeB <=6'd0;
end
end
state2:begin
if(count45 == 6'd5)
next_state=state3;
else begin
greenA <=1'b1;
greenB <=1'b0;
redA <=1'b0;
redB <=1'b1;
yellowA<= 1'b1;
yellowB <=1'b1;
timeA <= 6'd0;
timeB <=count45;
end
end
state3:begin
if(flag45)
next_state=state0;
else begin
greenA <=1'b1;
greenB <=1'b1;
redA <=1'b0;
redB <=1'b1;
yellowA<= 1'b1;
yellowB <=1'b0;
timeA <= 6'd0;
timeB <=count45;
end
end
default:next_state=state0;
endcase
end
else begin
greenA <=1'b1;
greenB <=1'b1;
redA <=1'b0;
redB <=1'b0;
yellowA<= 1'b1;
yellowB <=1'b1;
end
end
endmodule
數碼管顯示模塊
這個代碼我是採用正點原子的數碼管顯示模塊的代碼,只是進行了一下簡單的修改。
/*數碼管顯示模塊*/
module seg_led(
input sys_clk , // 時鐘信號
input sys_rst_n , // 復位信號
input [5:0] timeA,
input [5:0] timeB,
input [3:0] point , // 小數點具體顯示的位置,從高到低,高電平有效
input en , // 數碼管使能信號
input sign , // 符號位(高電平顯示“-”號)
output reg [3:0] seg_sel, // 數碼管位選,最左側數碼管爲最高位
output reg [7:0] seg_led // 數碼管段選
);
//分頻參數
localparam CLK_DIVIDE=4'd10;
localparam MAX_NUM =13'd5000; //10分頻,用於1ms計時
reg [3:0] clk_cnt ; // 時鐘分頻計數器,十分頻,計數5個系統週期
reg dri_clk ; // 數碼管的驅動時鐘,5MHz 200ns
reg [15:0] num; //要顯示的BCD數字
reg [12:0] cnt0; // 數碼管驅動時鐘計數器,1000_000ns/200ns=5000 13bit
reg flag; // 標誌信號(標誌着cnt0計數達1ms)
reg [1:0] cnt_sel ; // 數碼管位選計數器
reg [3:0] num_disp ; // 當前數碼管顯示的數據
reg dot_disp ; // 當前數碼管顯示的小數點
//拆分顯示
wire [3:0] data0 ; // 個位數9 4bit
wire [3:0] data1 ; // 十位數
wire [3:0] data2 ; // 百位數
wire [3:0] data3 ; // 千位數
assign data0 = timeA % 4'd10;
assign data1 = timeA /4'd10;
assign data2 = timeB % 4'd10;
assign data3 = timeB / 4'd10;
//對系統時鐘10分頻(1 5個系統週期 0 5個系統週期),得到的頻率爲5Mhz的數碼管驅動時鐘 200ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
clk_cnt <= 4'b0;
dri_clk <= 1'b1; //信號開始爲1
end
else if(clk_cnt==CLK_DIVIDE/2-1'b1) begin
clk_cnt <= 4'b0;
dri_clk <= ~dri_clk;
end
else begin
clk_cnt <=clk_cnt + 1'b1;
dri_clk <= dri_clk;
end
end
//將14位2進制數轉換爲8421bcd碼(即使用4位二進制數表示1位十進制數)
always @(posedge dri_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
num <= 16'b0;
else begin
if(!point[3]) begin
num[15:12] <= data3;
num[11:8] <= data2;
num[ 7:4] <= data1;
num[ 3:0] <= data0;
end
else begin
if(data2 || point[2]) begin
num[11:0]<={data2,data1,data0};
if(sign)
num[15:12] <= 4'd11;//d11表示負號
else
num[15:12] <= 4'd0;
end
else begin
if(data1 || point[1]) begin
num[15:12] <= 4'd10;
num[7:0]<={data1,data0};
if(sign)
num[11:8] <= 4'd11;//d11表示負號
else
num[11:8] <= 4'd0;
end
else begin
num[15:12] <= 4'd10;
num[11:8] <= 4'd10;
num[3:0]<= data0;
if(sign)
num[7:4] <= 4'd11;//d11表示負號
else
num[7:4] <= 4'd0;
end
end
end
end
end
//數碼管驅動時鐘,計時1ms,輸出一個時鐘週期的脈衝信號flag
always @ (posedge dri_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
cnt0 <=13'b0;
flag <=1'b0;
end
else if(cnt0 < MAX_NUM -1'b1) begin
cnt0 <=cnt0 + 1'b1;
flag <= 1'b0;
end
else begin
flag <= 1'b1;
cnt0 <= 1'b0;
end
end
//cnt_sel從0計數到3,用於選擇當前處於顯示狀態的數碼管
always @ (posedge dri_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_sel <= 2'b0;
else if(flag) begin
if(cnt_sel < 3)
cnt_sel=cnt_sel+1'b1;
else
cnt_sel <= 2'b0;
end
else
cnt_sel <=cnt_sel;
end
//進行循環位選
always @ (posedge dri_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
seg_sel <= 4'b1111; //位選信號低電平有效
num_disp <= 4'd10; //默認不顯示
dot_disp <= 1'b1; //默認不顯示小數點
end
else begin
if(en) begin
case (cnt_sel)
2'd0 :begin
seg_sel <= 4'b1110; //顯示數碼管最低位
num_disp <= num[3:0] ; //顯示的數據
dot_disp <= ~point[0]; //顯示的小數點
end
2'd1 :begin
seg_sel <= 'b1101; //顯示數碼管第1位
num_disp <= num[7:4] ;
dot_disp <= ~point[1];
end
2'd2 :begin
seg_sel <= 4'b1011; //顯示數碼管第2位
num_disp <= num[11:8];
dot_disp <= ~point[2];
end
2'd3 :begin
seg_sel <= 4'b0111; //顯示數碼管第3位
num_disp <= num[15:12];
dot_disp <= ~point[3];
end
default: begin
seg_sel <= 4'b1111; //位選信號低電平有效
num_disp <= 4'd10; //默認
dot_disp <= 1'b1; //默認不顯示小數點
end
endcase
end
else begin
seg_sel <= 4'b1111; //位選信號低電平有效
num_disp <= 4'd10; //默認顯示
dot_disp <= 1'b1; //默認不顯示小數點
end
end
end
always @ (posedge dri_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
seg_led <= 8'hc0;
else begin
case(num_disp)
4'd0 : seg_led <= {dot_disp,7'b1000000}; //顯示數字 0
4'd1 : seg_led <= {dot_disp,7'b1111001}; //顯示數字 1
4'd2 : seg_led <= {dot_disp,7'b0100100}; //顯示數字 2
4'd3 : seg_led <= {dot_disp,7'b0110000}; //顯示數字 3
4'd4 : seg_led <= {dot_disp,7'b0011001}; //顯示數字 4
4'd5 : seg_led <= {dot_disp,7'b0010010}; //顯示數字 5
4'd6 : seg_led <= {dot_disp,7'b0000010}; //顯示數字 6
4'd7 : seg_led <= {dot_disp,7'b1111000}; //顯示數字 7
4'd8 : seg_led <= {dot_disp,7'b0000000}; //顯示數字 8
4'd9 : seg_led <= {dot_disp,7'b0010000}; //顯示數字 9
4'd10: seg_led <= 8'b11111111; //不顯示任何字符
4'd11: seg_led <= 8'b10111111; //顯示負號(-)
default:
seg_led <={dot_disp,7'b11111111};//其他不顯示
endcase
end
end
endmodule
按鍵的延遲模塊
主要是用於開始或停止按鈕的消抖
module key_debounce(
input sys_clk, //外部50M時鐘
input sys_rst_n, //外部復位信號,低有效
input key, //外部按鍵輸入
output reg key_flag, //按鍵數據有效信號
output reg key_value //按鍵消抖後的數據
);
//reg define
reg [31:0] delay_cnt;
reg key_reg;
//*****************************************************
//** main code
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
delay_cnt <= 32'd0;
end
else begin
key_reg <= key;
if(key_reg != key) //一旦檢測到按鍵狀態發生變化(有按鍵被按下或釋放)
delay_cnt <= 32'd1000000; //給延時計數器重新裝載初始值(計數時間爲20ms)
else if(key_reg == key) begin //在按鍵狀態穩定時,計數器遞減,開始20ms倒計時
if(delay_cnt > 32'd0)
delay_cnt <= delay_cnt - 1'b1;
else
delay_cnt <= delay_cnt;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_flag <= 1'b0;
key_value <= 1'b1;
end
else begin
if(delay_cnt == 32'd1) begin //當計數器遞減到1時,說明按鍵穩定狀態維持了20ms
key_flag <= 1'b1; //此時消抖過程結束,給出一個時鐘週期的標誌信號
key_value <= key; //並寄存此時按鍵的值
end
else begin
key_flag <= 1'b0;
key_value <= key_value;
end
end
end
endmodule
頂層模塊
這個模塊主要實現的是實例化三個模塊,設置一個開始或停止按鈕,就是緊急接管按鈕。
module trafficlight(
input sys_clk,
input sys_rst_n,
input key, //開始或停止交通燈
output greenA,
output greenB,
output redA,
output redB,
output yellowA,
output yellowB,
output [3:0] seg_sel, // 數碼管位選,最左側數碼管爲最高位
output [7:0] seg_led // 數碼管段選
);
parameter POINT=4'd0000;
parameter SIGN=1'b0;
wire [5:0] timeA;
wire [5:0] timeB;
wire key_flag;
wire key_value;
reg en;
//開始或停止交通燈
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
en<=1'b1;
else begin
if(key_flag && (~key_value)) //判斷按鍵是否有效按下
en<=~en;
else
en<=en;
end
end
traffic_led u_traffic_led(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.greenA(greenA),
.greenB(greenB),
.redA(redA),
.redB(redB),
.yellowA(yellowA),
.yellowB(yellowB),
.en(en),
.timeA(timeA),
.timeB(timeB)
);
seg_led u_seg_led(
.sys_clk(sys_clk) , // 時鐘信號
.sys_rst_n(sys_rst_n) , // 復位信號
.timeA(timeA),
.timeB(timeB), // 6位數碼管要顯示的數值
.point( POINT) , // 小數點具體顯示的位置,從高到低,高電平有效
.en(en) , // 數碼管使能信號
.sign(SIGN) , // 符號位(高電平顯示“-”號)
.seg_sel(seg_sel), // 數碼管位選,最左側數碼管爲最高位
.seg_led(seg_led) // 數碼管段選
);
key_debounce u_key_debounce(
.sys_clk(sys_clk), //外部50M時鐘
.sys_rst_n(sys_rst_n), //外部復位信號,低有效
.key(key), //外部按鍵輸入
.key_flag(key_flag), //按鍵數據有效信號
.key_value(key_value) //按鍵消抖後的數據
);
endmodule
這樣就ok了,簡單吧!
仿真圖
下面是仿真圖,仿真的時間我使用的是毫秒爲單位,一秒太長了,仿真太麻煩
這是綠燈仿真時間,40s
這是紅燈的仿真時間,45s
這是綠燈仿真時間,5s
差不多就這樣了