呼吸灯
呼吸灯:我们将通过脉宽调制技术来实现“呼吸灯”,实现LED的亮度由最暗逐渐增加到最亮,再逐渐变暗的过程。 脉冲宽度调制。它是利用微控制器的数字输出调制实现,是对模拟电路进行控制的一种非常有效的技术,广泛应用于测量、通信、功率控制与变换等众多领域。
硬件设计
实现原理:如上图所示,脉冲信号的周期为T,高电平脉冲宽度为t,占空比为t/T。为了实现PWM脉宽调制,我们需要保持周期T不变,调整高电平脉宽t的时间,从而改变占空比。
当t = 0时,占空比为0%,因为我们的LED硬件为低电平点亮,所以为最亮的状态。
当t = T时,占空比为100%,LED灯为最暗(熄灭)的状态。
结合呼吸灯的原理,整个呼吸的周期为最亮→最暗→最亮的时间,即t的值的变化:0→T→0逐渐变化,这个时间应该为2s。
呼吸灯设计要求呼吸的周期为2s,也就是说LED灯从最亮的状态开始,第一秒时间内逐渐变暗,第二秒的时间内再逐渐变亮,依次进行。
本设计中需要两个计数器cnt1和cnt2,cnt1随系统时钟同步计数(系统时钟上升沿时cnt1自加1)范围为0-T,cnt2随cnt1的周期同步计数(cnt1等于T时,cnt2自加1)范围也是0-T,这样每次cnt1在0-T的计数时,cnt2为一个固定值,相邻cnt1计数周期对应的cnt2的值逐渐增大,我们将cnt1计数0~T的时间作为脉冲周期,cnt2的值作为脉冲宽度,则占空比 = cnt2/T,占空比从0%到100%的时间 = cnt2*cnt1 = T^2 = 1s = 12M个系统时钟,T = 2400,我们定义CNT_NUM = 2400作为两个计数器的计数最大值。
2.verilog程序设计
module breath_led(clk,rst,led);
input clk; //系统时钟输入
input rst; //复位输出
output led; //led输出
reg [24:0] cnt1; //计数器1
reg [24:0] cnt2; //计数器2
reg flag; //呼吸灯变亮和变暗的标志位
parameter CNT_NUM = 2400; //计数器的最大值 period = (2400^2)*2 = 24000000 = 2s
//产生计数器cnt1
always@(posedge clk or negedge rst) begin
if(!rst) begin
cnt1<=13'd0;
end
else if(cnt1>=CNT_NUM-1)
cnt1<=1'b0;
else
cnt1<=cnt1+1'b1;
end
//产生计数器cnt2
always@(posedge clk or negedge rst) begin
if(!rst) begin
cnt2<=13'd0;
flag<=1'b0;
end
else if(cnt1==CNT_NUM-1) begin //当计数器1计满时计数器2开始计数加一或减一
if(!flag) begin //当标志位为0时计数器2递增计数,表示呼吸灯效果由暗变亮
if(cnt2>=CNT_NUM-1) //计数器2计满时,表示亮度已最大,标志位变高,之后计数器2开始递减
flag<=1'b1;
else
cnt2<=cnt2+1'b1;
end
else begin
if(cnt2<=0) //当标志位为高时计数器2递减计数
flag<=1'b0; //计数器2级到0,表示亮度已最小,标志位变低,之后计数器2开始递增
else
cnt2<=cnt2-1'b1;
end
end
else
cnt2<=cnt2; //计数器1在计数过程中计数器2保持不变
end
//比较计数器1和计数器2的值产生自动调整占空比输出的信号,输出到led产生呼吸灯效果
assign led = (cnt1<cnt2)?1'b0:1'b1;
endmodule
交通灯
1.有限状态机
FSM:一种由寄存器组和组合逻辑构成硬件时序电路,堪称FPGA硬件设计的灵魂。
在同一时钟跳变沿由当前状态(现态)转移到下一状态(次态)
究竟转向哪一状态不但取决于各个输入值,还取决于当前状态。
在时钟跳变沿时刻进行复杂组合逻辑控制
包含时序、组合逻辑电路
非常有用模型,可以模拟大部分事物。如按键命令、自动门控制、通信时序等
1)状态总数state是有限的
2)任何一个时刻,只能处于一个状态
3)在条件满足时,由一个状态转变到另一个状态
分类:1)moore型:输出逻辑只由当前状态决定
下一个状态 = F(当前状态,输入信号);
输出信号 = G(当前状态);
2)mealy型:输出逻辑不但与当前状态有关还与当前输入值有关
下一个状态 = F(当前状态,输入信号);
输出信号 = G(当前状态,输入信号);
有限状态机要素
状态:当前状态、次态
输入:触发状态转移的输入条件
输出:输入所触发的动作
有限状态机的样式
一段式
整个状态机在一个always模块中,模块中既包含状态转移也含有组合逻辑输入输出。
二段式
状态机使用两个always描述,一个采用同步时序实现状态转移,一个采用组合逻辑判断转移条件和逻辑输出。需要定义两个状态——现态和次态
三段式
状态机使用三个always描述,一个采用同步时序实现状态转移,一个采用组合逻辑判断转移条件,一个实现状态同步输出
2.交通灯的设计
设计流程:
上图为十字路口交通示意图分之路与主路,要求如下: * 交通灯主路上绿灯持续15s的时间,黄灯3s的时间,红灯10s的时间; * 交通灯支路上绿灯持续7s的时间, 黄灯持续3秒的时间,红灯18秒的时间;
根据上述要求,状态机设计框架分析如下: * S1:主路绿灯点亮,支路红灯点亮,持续15s的时间; * S2:主路黄灯点亮,支路红灯点亮,持续3s的时间; * S3:主路红灯点亮,支路绿灯点亮,持续10s的时间; * S4:主路红灯点亮,支路黄灯点亮,持续3s的时间;
程序设计
时钟分频:
module divide
(
//INPUT
clk ,
rst_n ,
//OUTPUT
clkout
);
//*******************
//DEFINE PARAMETER
//*******************
parameter WIDTH = 3;
parameter N = 5;
//*******************
//DEFINE INPUT
//*******************
input clk,rst_n;
//*******************
//DEFINE OUTPUT
//*******************
output clkout;
//********************
//OUTPUT ATTRIBUTE
//********************
//REGS
reg [WIDTH-1:0] cnt_p,cnt_n;
reg clk_p,clk_n;
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;
//Sequential logic style
always @ (posedge clk)
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1;
end
always @ (negedge clk)
begin
if(!rst_n)
cnt_n<=0;
else if (cnt_n==(N-1))
cnt_n<=0;
else cnt_n<=cnt_n+1;
end
always @ (posedge clk)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1))
clk_p<=0;
else
clk_p<=1;
end
always @ (negedge clk)
begin
if(!rst_n)
clk_n<=0;
else if (cnt_n<(N>>1))
clk_n<=0;
else
clk_n<=1;
end
endmodule
交通灯通过利用有限状态机的三段式模型的程序设计:
module traffic
(
clk , //时钟
rst_n , //复位
out //三色led代表交通灯
);
input clk,rst_n;
output reg[5:0] out;
parameter S1 = 4'b00, //状态机状态编码
S2 = 4'b01,
S3 = 4'b10,
S4 = 4'b11;
parameter time_s1 = 4'd15, //计时参数
time_s2 = 4'd3,
time_s3 = 4'd10,
time_s4 = 4'd3;
//交通灯的控制
parameter led_s1 = 6'b101011, // LED2 绿色 LED1 红色
led_s2 = 6'b110011, // LED2 蓝色 LED1 红色
led_s3 = 6'b011101, // LED2 红色 LED1 绿色
led_s4 = 6'b011110; // LED2 红色 LED1 蓝色
reg [3:0] timecont;
reg [1:0] cur_state,next_state; //现态、次态
wire clk1h; //1Hz时钟
//产生1秒的时钟周期
divide #(.WIDTH(32),.N(12000000)) CLK1H (
.clk(clk),
.rst_n(rst_n),
.clkout(clk1h));
//第一段 同步逻辑 描述次态到现态的转移
always @ (posedge clk1h or negedge rst_n)
begin
if(!rst_n)
cur_state <= S1;
else
cur_state <= next_state;
end
//第二段 组合逻辑描述状态转移的判断
always @ (cur_state or rst_n or timecont)
begin
if(!rst_n) begin
next_state = S1;
end
else begin
case(cur_state)
S1:begin
if(timecont==1)
next_state = S2;
else
next_state = S1;
end
S2:begin
if(timecont==1)
next_state = S3;
else
next_state = S2;
end
S3:begin
if(timecont==1)
next_state = S4;
else
next_state = S3;
end
S4:begin
if(timecont==1)
next_state = S1;
else
next_state = S4;
end
default: next_state = S1;
endcase
end
end
//第三段 同步逻辑 描述次态的输出动作
always @ (posedge clk1h or negedge rst_n)
begin
if(!rst_n==1) begin
out <= led_s1;
timecont <= time_s1;
end
else begin
case(next_state)
S1:begin
out <= led_s1;
if(timecont == 1)
timecont <= time_s1;
else
timecont <= timecont - 1;
end
S2:begin
out <= led_s2;
if(timecont == 1)
timecont <= time_s2;
else
timecont <= timecont - 1;
end
S3:begin
out <= led_s3;
if(timecont == 1)
timecont <= time_s3;
else
timecont <= timecont - 1;
end
S4:begin
out <= led_s4;
if(timecont == 1)
timecont <= time_s4;
else
timecont <= timecont - 1;
end
default:begin
out <= led_s1;
end
endcase
end
end
endmodule