**
设计说明:
**
此信号灯主要应用在主路与次路相交的交叉口处,信号灯共分为4个不同的状态,利用Moore型状态机实现,用六个灯代表主路与次路的六个信号灯(红、绿、黄),高电平时信号灯亮,低电平时灭。同时用4个数码管分别显示主路与次路当前信号状态下的剩余时间,并设置复位信号按键rst。
**
设计原理图:
**
整个电路可以分为4个部分,分别是时钟模块、BCD码与二进制转换模块、状态机模块、数码管显示模块。Zero1为复位按键,cp为输入的100MHZ时钟信号,red1、yellow1、green1、red2、yellow2、green2为输出的信号灯状态,1代表亮,0代表灭。a_to_g则分别对应于数码管的显示,an则是数码管的位选端,下面分别介绍这几个模块的具体功能。
- 时钟模块:
此模块的输入为EGO1开发板上的100MHZ时钟,对应着P17管脚,可将时钟信号进行分频,数码管显示需要200HZ的时钟信号,状态机需要1HZ的时钟信号,没有时钟信号,整个系统也将无法正常工作,时钟模块的核心代码如下所示:(时钟模块代码不需要这么长,这里粘贴的是100MHZ的大部分分频,方便后续复习)
module clk_div(input CP_100MHz, output clk_100MHz,output clk_1kHz,output clk_200Hz,output clk_100Hz,output clk_50Hz,output clk_20Hz,output clk_10Hz,output clk_5Hz,output clk_2Hz,output clk_1Hz);reg [16 : 0] cnt_1kHz;reg [2 : 0] cnt1_200Hz;reg [2: 0] cnt2_200Hz;reg [2 : 0] cnt_100Hz;reg [3 : 0] cnt_50Hz;reg [4 : 0] cnt_20Hz;reg [5 : 0] cnt_10Hz;reg [6 : 0] cnt_5Hz;reg [7 : 0] cnt_2Hz;reg [8 : 0] cnt_1Hz;reg clk_1kHz_reg;//reg clk_200Hz_reg;reg clk_200Hz_reg1;reg clk_200Hz_reg2;reg clk_100Hz_reg;reg clk_50Hz_reg;reg clk_20Hz_reg;reg clk_10Hz_reg;reg clk_5Hz_reg;reg clk_2Hz_reg;reg clk_1Hz_reg;initialbegincnt_1kHz=0;cnt_100Hz=0;cnt_50Hz=0;cnt_20Hz=0;cnt_10Hz=0;cnt_5Hz=0;cnt_2Hz=0;cnt_1Hz=0; clk_1kHz_reg=0;clk_200Hz_reg1=0;clk_200Hz_reg2=0;clk_100Hz_reg=0;clk_50Hz_reg=0;clk_20Hz_reg=0;clk_10Hz_reg=0;clk_5Hz_reg=0;clk_2Hz_reg=0;clk_1Hz_reg=0;end always @ (posedge CP_100MHz) begin if (cnt_1kHz < 17'd100000/2-1)//17'h186A0=100000begincnt_1kHz <= cnt_1kHz + 1;clk_1kHz_reg <= clk_1kHz_reg;endelsebegincnt_1kHz = 0;clk_1kHz_reg <= ~clk_1kHz_reg; endend//200Hzalways @ (posedge clk_1kHz) begin if (cnt1_200Hz < 3'd5-1)cnt1_200Hz <= cnt1_200Hz + 1;elsecnt1_200Hz <= 0; end always @ (posedge clk_1kHz) begin if (cnt1_200Hz < 3'd5/2)clk_200Hz_reg1 <= 1;elseclk_200Hz_reg1 <= 0;end always @ (negedge clk_1kHz) begin if (cnt2_200Hz < 3'd5-1)cnt2_200Hz <= cnt2_200Hz + 1;elsecnt2_200Hz <= 0; end always @ (negedge clk_1kHz) begin if (cnt2_200Hz < 3'd5/2)clk_200Hz_reg2 <= 1;elseclk_200Hz_reg2 = 0;end assign clk_200Hz=clk_200Hz_reg1|clk_200Hz_reg2;//100Hzalways @ (posedge clk_1kHz) begin if (cnt_100Hz < 4'd10/2-1)begincnt_100Hz <= cnt_100Hz + 1;clk_100Hz_reg <= clk_100Hz_reg;endelsebegincnt_100Hz = 0;clk_100Hz_reg <= ~clk_100Hz_reg; endend //50Hzalways @ (posedge clk_1kHz) begin if (cnt_50Hz < 5'd20/2-1)begincnt_50Hz <= cnt_50Hz + 1;clk_50Hz_reg <= clk_50Hz_reg;endelsebegincnt_50Hz = 0;clk_50Hz_reg <= ~clk_50Hz_reg; endend//20Hzalways @ (posedge clk_1kHz) begin if (cnt_20Hz < 6'd50/2-1)begincnt_20Hz <= cnt_20Hz + 1;clk_20Hz_reg <= clk_20Hz_reg;endelsebegincnt_20Hz = 0;clk_20Hz_reg <= ~clk_20Hz_reg; endend//10Hzalways @ (posedge clk_1kHz) begin if (cnt_10Hz < 7'd100/2-1)begincnt_10Hz <= cnt_10Hz + 1;clk_10Hz_reg <= clk_10Hz_reg;endelsebegincnt_10Hz = 0;clk_10Hz_reg <= ~clk_10Hz_reg; endend //5Hzalways @ (posedge clk_1kHz) begin if (cnt_5Hz < 8'd200/2-1)begincnt_5Hz <= cnt_5Hz + 1;clk_5Hz_reg <= clk_5Hz_reg;endelsebegincnt_5Hz = 0;clk_5Hz_reg <= ~clk_5Hz_reg; endend //2Hzalways @ (posedge clk_1kHz) begin if (cnt_2Hz < 9'd500/2-1)begincnt_2Hz <= cnt_2Hz + 1;clk_2Hz_reg <= clk_2Hz_reg;endelsebegincnt_2Hz = 0;clk_2Hz_reg <= ~clk_2Hz_reg; endend //1Hzalways @ (posedge clk_1kHz) begin if (cnt_1Hz < 10'd1000/2-1)begincnt_1Hz <= cnt_1Hz + 1;clk_1Hz_reg <= clk_1Hz_reg;endelsebegincnt_1Hz = 0;clk_1Hz_reg <= ~clk_1Hz_reg; endend assign clk_100MHz = CP_100MHz; assign clk_1kHz = clk_1kHz_reg;assign clk_100Hz = clk_100Hz_reg;assign clk_50Hz = clk_50Hz_reg;assign clk_20Hz = clk_20Hz_reg;assign clk_10Hz = clk_10Hz_reg;assign clk_5Hz = clk_5Hz_reg;assign clk_2Hz = clk_2Hz_reg;assign clk_1Hz = clk_1Hz_reg;endmodule
**
- 状态机模块:*
**
此模块的输入为复位信号rst以及时钟信号clk,输出为red1,yellow1,green1;red2,
yellow2,green2。高电平时亮,低电平时灭,利用Moore型状态机实现,整个信号灯周期共分为4个状态,状态表如下图所示:
利用Verilog语言实现过程如下:
module state( input rst,clk,output red1,yellow1,green1,red2,yellow2,green2,output [7:0] counter_x,counter_y);reg red1,yellow1,green1,red2,yellow2,green2;reg [3:0] current_state,next_state;reg [7:0] counter,counter_x,counter_y,timeover,timeover_x,timeover_y;parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 2'b11;parameter time1 = 'd24, time2 = 'd4;//状态转换always @(posedge clk or negedge rst)if(!rst)begincurrent_state <= S0;counter <= time1;counter_x <= 'd24;counter_y <= 'd29;endelsebeginif(counter == 0)begincurrent_state <= next_state;counter <= timeover;counter_x <= timeover_x;counter_y <= timeover_y;endelsebegincounter <= counter - 1;counter_x <= counter_x - 1;counter_y <= counter_y -1;endend//状态改变always @(current_state)begincase(current_state)S0:begin //横向绿灯,纵向红灯red1 = 0; yellow1 = 0; green1 = 1;red2 = 1; yellow2 = 0; green2 = 0;next_state <= S1; timeover <= time2;timeover_x <= 'd4;timeover_y <= 'd4;endS1:begin //横向黄灯,纵向红灯red1 = 0; yellow1 = 1; green1 = 0;red2 = 1; yellow2 = 0; green2 = 0;next_state <= S2; timeover <= time1;timeover_x <= 'd29;timeover_y <= 'd24;endS2:begin //横向红灯,纵向绿灯red1 = 1; yellow1 = 0; green1 = 0;red2 = 0; yellow2 = 0; green2 = 1;next_state <= S3; timeover <= time2;timeover_x <= 'd4;timeover_y <= 'd4;endS3:begin //横向红灯,纵向黄灯red1 = 1; yellow1 = 0; green1 = 0;red2 = 0; yellow2 = 1; green2 = 0;next_state <= S1; timeover <= time1;timeover_x <= 'd24;timeover_y <= 'd29;enddefault:begin //横向绿灯,纵向红灯red1 = 0; yellow1 = 0; green1 = 1;red2 = 1; yellow2 = 0; green2 = 0;next_state <= S1; timeover <= time2;timeover_x <= 'd4;timeover_y <= 'd4;endendcaseend
endmodule
- 二进制与BCD码转换模块:
此模块的输入为状态机模块的counter_x与counter_y输出,分别表示主路与次路当前状态下所剩余的时间,但它们均是由二进制数表示的,为了让数码管可以正常显示,需要借助BCD码来实现,也就是将二进制转换成BCD码,此次设计采用大4加3移位算法,算法原理如下:
- 4位二进制数大于15则进位,4位BCD码大于9则进位,若使4位二进制数变为BCD码,则其大于9时加6,得到的便是其BCD码(由真值表规律可知),例如:12的二进制数为1100,它是大于9的,则1100+0110=1_0010,即为BCD码的12。
- 对于5位二进制,先判断高四位是否大于9,若大于9,则加6并向左移位;若小于9,则直接向左移位。例如30的二进制数为11110,高4位1111的BCD码为10101,因为其大于9,所以加6,变为1_0101,左移一位之后,得到0010_1010,新低四位的值1010大于9,所以新低4位加6得到0011_0000,即为30的BCD码。所以只要通过左移及大9加6(大于等于10加6),则可转换任意位2进制数为BCD码。
但是该算法是移位加6,即加0110。而大4加3(0011)算法比此法占用资源少,但在算法上两者是等价的,即判断4位二进制数是否大于等于5,若大于等于5则加3,再移位;否则直接移位。例如10的二进制数0_1010 - 按照大9加6算法,5的二进制数为0101,左移一位之后变为1010(10),此时需要加6并左移一位,变为BCD码1_0000。即10+6=16(进1)
- 按照大4加3算法,5的二进制数为0101,大于4,此时需要加3并左移一位,变为BCD码1_0000。即(5+3)*2 = 16(进1),左移一位(低位补0)相当于乘以2。
- 8位2进制数最大能表示的数字为255,用BCD码表示,需要10位来表示。因此此模块的输入为8位二进制数,输出为10位二进制数。
a.把二进制数左移一位;
b.如果共移了8位,那么BCD数就在百位、十位和个位列,转换完成;
c.如果在BCD列中,任何一个二进制数是5或比5更大,那么就在BCD列的数值加 3;
d.返回步骤a。
例如二进制数1111_1111到255的转换过程如下所示:
利用Verilog语言实现的过程如下:
module binbcd8 (input wire [7:0] b,output reg [9:0] p
);// 中间变量 reg [17:0] z;//8位输入,10位输出,共18个数integer i;always @ ( * ) //always模块中的任何一个输入信号或电平发生变化时,该语句下方的模块将被执行begin for (i = 0; i <=17; i = i + 1)z[i] = 0;z[10:3] = b; // 向左移3位,8421码,左移两位均不会大于等于5repeat (5) // 重复5次begin if (z[11:8] > 4) // 如果个位大于4z[11:8] = z[11:8] +3; // 加3if (z[15:12] > 4) // 如果十位大于4 z[15:12] = z[15:12] +3; // 加3z[17:1] = z[16:0]; // 左移一位endp = z[17:8]; // BCD end
endmodule
- 数码管显示模块:
此模块的输入为二进制转换为BCD码模块的输出,用来使数码管正确的显示十进制数,也就是此次实验设计中的各个信号灯所剩余的时间,利用Verilog语言实现的过程如下:
module hx7seg(input wire [11:0] x,//4的倍数,12位input wire cclk,//保证有一个足够的频率。一般工作在200HZinput wire clr,//清零端output reg [6:0] a_to_g,output reg [1:0] an//位选端,控制数码管的选择
);reg s;reg [3:0] digit;// 2-位计数器always @ (posedge cclk or posedge clr)begin if (clr ==1)//高电平清零s <= 0;elses <= s + 1;end// Quad 4-to-1 MUX: mux44always @ ( * )//always模块中的任何一个输入信号或电平发生变化时,该语句下方的模块将被执行。case (s)//本项目中的所有时间均是两位数,因此s有两种状态即可0: digit = x[3:0];1: digit = x[7:4];default: digit = x[11:8];endcase// 选择数码管always @ ( * )begin an = 2'b00; an[s] = 1;end// 7段解码器:hex7segalways @ ( * )case (digit)0: a_to_g = 7'b1111110;1: a_to_g = 7'b0110000;2: a_to_g = 7'b1101101;3: a_to_g = 7'b1111001;4: a_to_g = 7'b0110011;5: a_to_g = 7'b1011011;6: a_to_g = 7'b1011111;7: a_to_g = 7'b1110000;8: a_to_g = 7'b1111111;9: a_to_g = 7'b1111011;'hA: a_to_g = 7'b1110111;'hB: a_to_g = 7'b0011111;'hC: a_to_g = 7'b1001110;'hD: a_to_g = 7'b0111101;'hE: a_to_g = 7'b1001111;'hF: a_to_g = 7'b1000111;default: a_to_g = 7'b0000000; // 空白endcase
endmodule
- 引脚约束:
set_property IOSTANDARD LVCMOS33 [get_ports zero1]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an1[0]}]
set_property PACKAGE_PIN P5 [get_ports zero1]
set_property PACKAGE_PIN P4 [get_ports zero2]
set_property IOSTANDARD LVCMOS33 [get_ports cp]
set_property IOSTANDARD LVCMOS33 [get_ports green1]
set_property IOSTANDARD LVCMOS33 [get_ports red1]
set_property IOSTANDARD LVCMOS33 [get_ports green2]
set_property IOSTANDARD LVCMOS33 [get_ports red2]
set_property IOSTANDARD LVCMOS33 [get_ports yellow2]
set_property IOSTANDARD LVCMOS33 [get_ports yellow1]
set_property IOSTANDARD LVCMOS33 [get_ports zero2]
set_property PACKAGE_PIN P17 [get_ports cp]
set_property PACKAGE_PIN J3 [get_ports red1]
set_property PACKAGE_PIN J2 [get_ports yellow1]
set_property PACKAGE_PIN K2 [get_ports green1]
set_property PACKAGE_PIN L1 [get_ports red2]
set_property PACKAGE_PIN M1 [get_ports yellow2]
set_property PACKAGE_PIN K3 [get_ports green2]
set_property PACKAGE_PIN C1 [get_ports {an0[1]}]
set_property PACKAGE_PIN H1 [get_ports {an0[0]}]
set_property PACKAGE_PIN E1 [get_ports {an1[1]}]
set_property PACKAGE_PIN G6 [get_ports {an1[0]}]
set_property PACKAGE_PIN B4 [get_ports {a_to_g0[6]}]
set_property PACKAGE_PIN A4 [get_ports {a_to_g0[5]}]
set_property PACKAGE_PIN A3 [get_ports {a_to_g0[4]}]
set_property PACKAGE_PIN B1 [get_ports {a_to_g0[3]}]
set_property PACKAGE_PIN A1 [get_ports {a_to_g0[2]}]
set_property PACKAGE_PIN B3 [get_ports {a_to_g0[1]}]
set_property PACKAGE_PIN B2 [get_ports {a_to_g0[0]}]
set_property PACKAGE_PIN D4 [get_ports {a_to_g1[6]}]
set_property PACKAGE_PIN E3 [get_ports {a_to_g1[5]}]
set_property PACKAGE_PIN D3 [get_ports {a_to_g1[4]}]
set_property PACKAGE_PIN F4 [get_ports {a_to_g1[3]}]
set_property PACKAGE_PIN F3 [get_ports {a_to_g1[2]}]
set_property PACKAGE_PIN E2 [get_ports {a_to_g1[1]}]
set_property PACKAGE_PIN D2 [get_ports {a_to_g1[0]}]
- 效果图展示:
- 视频展示:
视频链接如下所示:
视频展示
项目在Vivado运行一切正常,实验所用设备为EGO1开发板,源文件链接如下:(bit流文件与记忆文件均已生成,在traffic文件中,下载后可正常测试)
- 链接
百度网盘链接分享
提取码:0000
–来自百度网盘超级会员V4的分享
首次blog,多多关照!!!