疫情无情,人间有情,武汉加油,
在家里,无所事事,把以前的课设给放到网上吧,也顺便复习一下。
就贴了一下代码,不是太详细,你们都是聪明人,一下子肯定看懂了!
题目是这样子的
信号控制系统设计
内容及要求:
设计一个十字路口交通控制系统,具体设计要求如下:
(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
差不多就这样了