Diseño de semáforo basado en FPGA

La epidemia es despiadada, el mundo es sentimental, vamos Wuhan,

En casa, no hagas nada, pon los cursos anteriores en Internet, y repasa por cierto.

Acabo de publicar el código, no demasiado detallado, todos ustedes son personas inteligentes, ¡definitivamente lo entenderán todo a la vez!

El titulo es asi

Diseño del sistema de control de señales

Contenido y requisitos:

    Para diseñar un sistema de control de tráfico en una intersección, los requisitos de diseño específicos son los siguientes:

(1) Las direcciones este-oeste (indicadas por A) y norte-sur (indicadas por B) tienen luces verdes, amarillas y rojas, cuya duración es de 40 segundos, 5 segundos y 45 segundos, respectivamente. Diagrama de cambio de semáforo y sincronización Los diagramas se muestran en la Figura 1 y la Figura 2 respectivamente.

(2) El sistema está equipado con un reloj para mostrar el tiempo permitido de cada camino en forma de cuenta regresiva.

(3) Cuando ocurre una situación especial en cualquiera de las carreteras este-oeste o norte-sur, el sistema puede ser controlado manualmente por la policía de tránsito para ingresar inmediatamente a un estado operativo especial, es decir, la luz roja está encendida, el reloj detiene el cronometraje y todos los vehículos en las carreteras este-oeste, sur-norte dejan de pasar; una vez finalizado el estado de funcionamiento, el sistema reanuda el trabajo y continúa con el funcionamiento normal.

 

Figura 2 Diagrama de tiempos de los semáforos

(4) Complete todos los procesos: documentos de especificación de diseño, diseño de módulo, entrada de código, verificación de descarga, etc., y finalmente envíe un informe de diseño del curso sobre el diseño del curso en sí.

 

Este es el módulo de cronometraje para semáforos.

Lo principal es completar el módulo de cronometraje del semáforo, con pocos comentarios, ¡perdóname!

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

Módulo de pantalla de tubo digital

Este código es el código del módulo de visualización del tubo nixie que usa el átomo puntual, solo una simple modificación.

/*数码管显示模块*/
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


Módulo de retardo de botón

Se utiliza principalmente para eliminar el rebote del botón de inicio o parada.

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 

 

Módulo de nivel superior

La principal realización de este módulo es crear una instancia de tres módulos y establecer un botón de inicio o parada, que es el botón de toma de control de emergencia.

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

¡Está bien, simple!

 

Diagrama de simulación

El siguiente es el diagrama de simulación. El tiempo de simulación está en milisegundos. Un segundo es demasiado largo y la simulación es demasiado problemática.

Este es el tiempo de simulación de luz verde, 40 s.

Este es el tiempo de simulación de la luz roja, 45 s.

Este es el tiempo de simulación de luz verde, 5 s.

Casi eso es todo

Supongo que te gusta

Origin blog.csdn.net/weixin_40943540/article/details/104765890
Recomendado
Clasificación