基於FPGA的交通燈設計

疫情無情,人間有情,武漢加油,

在家裏,無所事事,把以前的課設給放到網上吧,也順便複習一下。

就貼了一下代碼,不是太詳細,你們都是聰明人,一下子肯定看懂了!

題目是這樣子的

信號控制系統設計

內容及要求:

    設計一個十字路口交通控制系統,具體設計要求如下:

(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

差不多就這樣了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章