一 项目需求
图1.1实现设备:小脚丫STEP-MXO2-LPC
板卡简介:STEP小脚丫FPGA学习平台是苏州思得普信息科技公司专门针对FPGA初学者(尤其是学习数字电路的在校同学)打造的一系列性价比最高、学习门槛最低的学习模块系列。板上选用的芯片兼具了FPGA和CPLD的优点,瞬时上电启动,无需外部重新配置FPGA,是学习数字逻辑绝佳的选择。系列中所有板子的大小兼容标准的DIP40封装,尺寸只有52mm x 18mm,非常便于携带,而且能够直接插在面包板上或以模块的方式放置在其它电路板上以即插即用的方式,大大简化系统的设计。
在小脚丫FPGA核心板上,利用8个单色LED实现不同的LED显示效果,通过4个轻触按键控制不同的LED显示模式,通过4个拨动开关控制每种显示模式的显示周期,并在数码管上通过数值显示出相应的模式和周期——第一个数码管显示LED的显示模式,第二个数码管显示周期。轻触开关K1~K4,用于切换LED的显示模式:
1. K1:循环心跳灯——8个LED轮流心跳,每一个LED心跳2个周期(半个周期亮、半个周期灭、半个周期亮、半个周期灭),然后该LED灭掉,下一个LED开始跳动,从第一个LED开始,到第8个LED,然后再从第一个开始,周而复始,其心跳的周期由拨动开关SW1~SW4控制。
2. K2:呼吸灯——8个LED轮流呼吸,每一个LED呼吸2个周期(半个周期从灭到亮、半个周期从亮到灭、半个周期从灭到亮、半个周期从亮到灭),然后该LED灭掉,下一个LED开始呼吸,从第一个LED开始,到第8个LED,然后再从第一个开始,周而复始,其呼吸的周期由拨动开关SW1~SW4控制。
3. K3:带渐灭功能的流水灯——8个LED构成流动显示的效果,且下面的灯亮度逐渐变暗。
4. K4:自定义模式——此处选择计时器,使用拨动开关SW1~SW4不同组合控制计时开始、暂停、继续、清零、结束操作。数码每管满10s即清零并在右侧LED亮起一个灯,最终计时时长等于右侧LED亮灯数×10加上数码管显示数值。
拨动开关,用于控制显示的周期:
SW1: 1秒
SW2: 2秒
SW3: 3秒
SW4: 4秒
二 需求分析
(1)LED显示需求
1. K1:循环心跳灯
8个LED轮流心跳,每一个LED心跳2个周期。心跳周期由拨动开关控制。
2. K 2:呼吸灯
8个LED轮流呼吸,每一个LED呼吸2个周期。呼吸周期由拨动开关控制。
3. K3:带渐灭功能的流水灯
8个LED构成流动显示的效果,下面的灯亮度逐渐变暗。流水灯速度由拨动开关控制。
4. K4:自定义计时器模式
用户能够通过不同拨动开关组合实现计时开始、暂停、继续、清零、结束操作。
(2)数码管界面需求
K1~K3模式下左侧数码管显示当前工作模式,右侧数码管显示周期。K4计时器模式下在计时期间显示计时数值,清零后显示工作模式。
三 功能框图
图3.1整体实现框图
整体实现框图如上图3.1所示。
四 实现方式
(1)按键读取
按键读取模块中每个时钟上升沿读取按键以及开关SW1~SW4的状态,按键使用了if else语句判断,开关使用了具有优先级的casez语句并分别用二位变量key_choose和sw_choose存储各自四个状态。
module get(
clk,
key,
sw,
key_choose,
sw_choose
);
input clk;
input [3:0] sw;
input [3:0] key;
output reg [1:0] sw_choose;
output reg [1:0] key_choose;
//Determine the status of keys and switches
always@(posedge clk)
begin
if (key[0]==1'b0) key_choose <= 2'b00;
else if(key[1]==1'b0) key_choose <= 2'b01;
else if(key[2]==1'b0) key_choose <= 2'b10;
else if(key[3]==1'b0) key_choose <= 2'b11;
//selector with priority
casez(sw)
4'b1???:sw_choose <= 2'b11;//4s
4'b01??:sw_choose <= 2'b10;//3s
4'b001?:sw_choose <= 2'b01;//2s
4'b0001:sw_choose <= 2'b00;//1s
default: sw_choose <= 2'b00;//1s
endcase
end
endmodule
(2)K1循环心跳灯模块
首先定义了四个分频系数分别对应了1~4s周期,然后通过sw开关选择分频系数,其次定义了变量two以及state,state用于控制LED闪烁位置,two用于记录闪烁次数,每亮或者灭一定间隔就加一,闪烁两下即计数至2’b11则state加一,闪烁位置向前移动一格。
module task_1 (
clk,
sw_choose,
key_choose,
led
);
input clk;
input [1:0] sw_choose ;
input [1:0] key_choose;
output reg [7:0] led;
//Different crossover coefficients
parameter div_1s = 3000000;
parameter div_2s = 6000000;
parameter div_3s = 9000000;
parameter div_4s = 12000000;
reg [31:0] counts,div;
reg [2:0] state ;//Record the current state
reg div_clk ;//Clock after frequency division
reg [1:0] two ;//Record the number of flashes
//Selection of crossover coefficients
always@(posedge clk)begin
case(sw_choose)
2'b11:div=div_4s;
2'b10:div=div_3s;
2'b01:div=div_2s;
2'b00:div=div_1s;
default:div=div_1s;
endcase
end
//generation of split frequency clocks
always@(posedge clk)
begin
if(counts==div)
begin
counts<=0;
div_clk<=~div_clk;
end
else if(counts>div)
begin
counts<=0;
end
else
begin
counts<=counts+1'b1;
end
end
//Record the status and assign values to each
always@(posedge div_clk)
begin
if(key_choose==2'b00)
begin
case(state)
3'b000 : led<=8'b11111110;
3'b001 : led<=8'b11111101;
3'b010 : led<=8'b11111011;
3'b011 : led<=8'b11110111;
3'b100 : led<=8'b11101111;
3'b101 : led<=8'b11011111;
3'b110 : led<=8'b10111111;
3'b111 : led<=8'b01111111;
default : led<=8'b11111111;
endcase
if(two==2'b11)
begin
state<=state+1'b1;
two<=1'b0;
end
else if(two==2'b10)
begin
two<=two+1'b1;
led<=8'b11111111;
end
else if(two==2'b01)
begin
two<=two+1'b1;
end
else
begin
two<=two+1'b1;
led<=8'b11111111;
end
end
else
begin
two<=2'b00;
state<=3'b000;
led<=8'b11111111;
end
end
endmodule
(3)K2呼吸灯
呼吸灯的关键是产生占空比不同的PWM波,即在K1的基础上将亮的步骤变为占空比从0逐渐变大至1的过程,而熄灭则变为占空比从1逐渐变为0的过程。为了产生PWM波这里两个计数器,他们的计数最大值同为CNT_NUM,因此有如下公式计算不同周期下的CNT_NUM值
t表示周期1~4s。计数器cnt1在每个时钟上升沿计数一次,计满CNT_NUM则计数器2加一,由于需要闪烁两次,因此使用flag记录闪烁次数,flag为00或者10时cnt2递增,此时PWM占空比将会变大,LED灯将会变亮,计满CNT_NUM则清零且flag加1,此时将从最大值开始递减,LED将会逐渐变暗。flag满11则用于记录LED闪烁位置的state将会加一使下一个LED开始闪烁。最终PWM输出使用cnt2与cnt1相比较即可得到。开始时cnt2为0,LED灭,cnt2逐渐增加的过程中了的渐渐变亮,当两个计数器都计到最大值时LED最亮且此时时间间隔等于开关sw所选择的时间。熄灭过程相反。
module task_2(
clk,
sw_choose,
key_choose,
led
);
input clk;
input [1:0] sw_choose ;
input [1:0] key_choose;
output reg [7:0] led;
reg [24:0] cnt1; //Counter 1
reg [24:0] cnt2; //Counter 2
reg [1:0] flag; //Record the status of the breathing light
reg [1:0] pre_flag;
reg [2:0] state; //Record the position of the breathing light
reg [31:0] CNT_NUM; //Record different crossover coefficients
//Different crossover coefficients
parameter CNT_NUM_1 = 2450;
parameter CNT_NUM_2 = 3464;
parameter CNT_NUM_3 = 4242;
parameter CNT_NUM_4 = 4898;
//selector
always@(posedge clk)
begin
casez(sw_choose)
2'b11:CNT_NUM=CNT_NUM_4;
2'b10:CNT_NUM=CNT_NUM_3;
2'b01:CNT_NUM=CNT_NUM_2;
2'b00:CNT_NUM=CNT_NUM_1;
default:CNT_NUM=CNT_NUM_1;
endcase
end
//Generate counter 1
always@(posedge clk)
begin
if(key_choose==2'b01)
begin
if(cnt1>=CNT_NUM-1)
cnt1<=1'b0;
else
cnt1<=cnt1+1'b1;
end
else
begin
cnt1<=1'b0;
end
end
/*Generate counter 2
The light turns on when the variable flag is 00 or 10,
The light dims when the variable flag is 01 or 11.*/
always@(posedge clk)
begin
if(key_choose==2'b01)
begin
if(cnt1==CNT_NUM-1)
begin
if(flag==2'b00 || flag==2'b10)
begin
if(cnt2>=CNT_NUM-1)
flag<=flag + 1'b1;
else
cnt2<=cnt2+1'b1;
end
else
begin
if(cnt2<=0)
flag<=flag + 1'b1;
else
cnt2<=cnt2-1'b1;
end
end
else
begin
cnt2<=cnt2;
end
end
else
begin
cnt2<=0;
flag<=0;
end
end
//Compare the values of Counter 1 and Counter 2 to generate PWM waves with different duty cycles.
always@(posedge clk)
begin
if(key_choose==2'b01)
begin
if(cnt2>cnt1)
begin
case(state)
3'b000 : led<=8'b11111110;
3'b001 : led<=8'b11111101;
3'b010 : led<=8'b11111011;
3'b011 : led<=8'b11110111;
3'b100 : led<=8'b11101111;
3'b101 : led<=8'b11011111;
3'b110 : led<=8'b10111111;
3'b111 : led<=8'b01111111;
default : led<=8'b11111111;
endcase
end
else
begin
led<=8'b11111111;
end
end
else
begin
led<=8'b11111111;
end
end
//Record and move the blinking position
always@(posedge clk)
begin
if(key_choose==2'b01)
begin
if(flag==2'b00 && pre_flag!=2'b00)
begin
state<=state+1'b1;
pre_flag<=flag;
end
else
begin
pre_flag<=flag;
end
end
else
begin
state<=3'b000;
pre_flag<=0;
end
end
endmodule
(4)K3带渐灭功能的流水灯
在K2中已实现渐灭的效果,这时只需要将8位LED不断右移1位即可得到亮度为前一半的输出,用此方法得到8个不同亮度的LED输出。而此处的关键为要使得在状态state加1时每个LED的占空比前后一致,这样才能保证不出现LED有闪烁的情况。为了产生1~4s的流水灯效果,首先计算不同的分频系数div,利用下述公式
其中t为1~4。为了使得LED状态切换前后占空比一致,通过调整每一个分频系数div对应的CNT_NUM可以使其满足要求,计算公式如下
由此可以计算出四个分频系数所以对应的最大值CNT_NUM。这里同样通过sw选择div以及CNT_NUM的值,计数器cnt1每个时钟上升沿加一直至CNT_NUM,而由于需要渐灭,因此计数器cnt2将会从最大值CNT_NUM开始每次cnt1满时递减实现渐灭,同时输出的LED通过不同位数的移位组合达到不同亮度的效果,最终通过分频后的时钟上升沿触发state变化,每次加1并将cnt2复位为最大值即可实现渐灭流水灯效果。
module task_3(
clk ,
sw_choose,
key_choose,
led
);
input clk ;
input [1:0] sw_choose ;
input [1:0] key_choose;
output reg [7:0] led ;
reg [31:0] cnt1; //counter1
reg [31:0] cnt2; //counter2
reg [31:0] counts;
reg [31:0] CNT_NUM; //Record different crossover coefficients
reg [31:0] div;
reg flag; //Record the breathing light brightening or dimming
reg pre_flag;
reg [2:0] state;
reg clk_led,temp;
wire [7:0] led_temp;
parameter CNT_NUM_1 = 1732;//Period 1s
parameter CNT_NUM_2 = 2450;//Period 2s
parameter CNT_NUM_3 = 3000;//Period 3s
parameter CNT_NUM_4 = 3464;//Period 4s
parameter div_500ms = 750000-1;
parameter div_1000ms = 1500000-1;
parameter div_1500ms = 2250000-1;
parameter div_2000ms = 3000000-1;
//selector
always@(posedge clk)
begin
case(sw_choose)
2'b11:begin CNT_NUM<=CNT_NUM_4;div<=div_2000ms; end
2'b10:begin CNT_NUM<=CNT_NUM_3;div<=div_1500ms; end
2'b01:begin CNT_NUM<=CNT_NUM_2;div<=div_1000ms; end
2'b00:begin CNT_NUM<=CNT_NUM_1;div<=div_500ms; end
default:begin CNT_NUM<=CNT_NUM_1;div<=div_500ms; end
endcase
end
//Generation of split frequency clocks
always@(posedge clk)
begin
if(counts>=div)
begin
clk_led<=~clk_led;
counts<=0;
end
else
begin
counts<=counts+1'b1;
end
end
//Generate counter cnt1
always@(posedge clk)
begin
if(cnt1>=CNT_NUM-1)
cnt1<=1'b0;
else
cnt1<=cnt1+1'b1;
end
//Generate counter cnt2
//brightness starts to decrease after the maximum setting
always@(posedge clk)
begin
if(cnt1==CNT_NUM-1)
begin
if(flag==0)
begin
cnt2<=CNT_NUM-1;
flag<=1'b1;
end
else
begin
if(cnt2<=0)
begin
flag<=1'b0;
end
else
begin
cnt2<=cnt2-1'b1;
end
end
if(clk_led==1&&temp==0)
begin
flag<=0;
temp<=clk_led;
end
else
begin
temp<=clk_led;
end
end
end
//Compare the values of Counter 1 and Counter 2 to generate PWM waves with different duty cycles.
assign led_temp[7] = (cnt1<cnt2)?1'b0:1'b1;
assign led_temp[6] = (cnt1<(cnt2>>1))?1'b0:1'b1;
assign led_temp[5] = (cnt1<(cnt2>>2))?1'b0:1'b1;
assign led_temp[4] = (cnt1<(cnt2>>3))?1'b0:1'b1;
assign led_temp[3] = (cnt1<(cnt2>>4))?1'b0:1'b1;
assign led_temp[2] = (cnt1<(cnt2>>5))?1'b0:1'b1;
assign led_temp[1] = (cnt1<(cnt2>>6))?1'b0:1'b1;
assign led_temp[0] = (cnt1<(cnt2>>7))?1'b0:1'b1;
//Splicing signals and generating outputs
always@(posedge clk)
begin
case(state)
3'b000 : led<=led_temp;
3'b001 : led<={led_temp[6:0],led_temp[7]};
3'b010 : led<={led_temp[5:0],led_temp[7:6]};
3'b011 : led<={led_temp[4:0],led_temp[7:5]};
3'b100 : led<={led_temp[3:0],led_temp[7:4]};
3'b101 : led<={led_temp[2:0],led_temp[7:3]};
3'b110 : led<={led_temp[1:0],led_temp[7:2]};
3'b111 : led<={led_temp[0],led_temp[7:1]};
default : led<=led_temp;
endcase
end
//Record and move the blinking position
always@(posedge clk_led)
begin
if(key_choose==2'b10)
begin
state<=state+1'b1;
end
else
begin
state<=0;
end
end
endmodule
(5)K4自定义模式——计时器
当按下第四个按键时进入K4模式。首先进行分频产生10Hz时钟,并通过开关SW1~SW4控制计数情况,当拨到?000时LED熄灭,且数码管左侧显示模式为4,右侧为一横线表示无需显示拨码。当拨到?100时计时开始,数码管显示一位小数并每满10s点亮一个LED,直至最多8个LED全部点亮。当拨到??10时计时暂停,若此时重新拨为?100计时可继续,当拨为???1时则计数清零LED熄灭,数码管显示0.0。该模块需要输出数码管的值display_task4_left和display_task4_right给display模块。
module task_4(
clk,
key_choose,
sw_choose,
display_task4_left,
display_task4_right,
led
);
input clk;
input [1:0] key_choose;
input [1:0] sw_choose;
output reg [8:0] display_task4_left;
output reg [8:0] display_task4_right;
output reg [7:0] led;
parameter div_100ms = 600000-1;
reg [31:0] counts;//crossover counter
reg clk_100ms;//10Hz clock
reg [3:0] num_left;//left digital tube counts
reg [3:0] num_right;//right digital tube counts
reg [3:0] state;//record status
reg num_state;
reg [8:0] seg [10:0];
initial
begin
seg[0] = 9'h3f; //0
seg[1] = 9'h06; //1
seg[2] = 9'h5b; //2
seg[3] = 9'h4f; //3
seg[4] = 9'h66; //4
seg[5] = 9'h6d; //5
seg[6] = 9'h7d; //6
seg[7] = 9'h07; //7
seg[8] = 9'h7f; //8
seg[9] = 9'h6f; //9
seg[10]= 9'h40; //empty
end
//Generate 10Hz clock
always@(posedge clk)
begin
if(counts>=div_100ms)
begin
clk_100ms <= ~clk_100ms;
counts<=0;
end
else
begin
counts<=counts+1'b1;
end
end
//Add 1 for every 0.1s and add 1 to the number of lighted lamps when full 10s.
always@(posedge clk_100ms)
begin
if(key_choose==2'b11)
begin
if(sw_choose==2'b01)//sw=z100,start counting
begin
if(num_state==1'b1)
begin
if(state==4'b1000&&num_left==4'd9&&num_right==4'd9)
begin
state<=state;
num_left<=num_left;
num_right<=num_right;
end
else
begin
if(num_right==4'd9)//Right digital tube counts full 9
begin
num_left<=num_left+1'b1;
num_right<=0;
end
else
begin
num_right<=num_right+1'b1;
end
if(num_left==4'd10)//Left digital tube counts full 10
begin
num_left<=0;
state<=state+1'b1;
end
end
end
else
begin
num_state<=1'b1;
num_left<= 0 ;
num_right<= 0 ;
end
end
else if(sw_choose==2'b10)//sw=zzz1,stop counting
begin
num_left<=num_left;
num_right<=num_right;
state<=state;
end
else if(sw_choose==2'b11)//sw=1000,zero counting
begin
num_state <= 1'b0;
num_left<=4'd0;
num_right<=4'd0;
state<=4'd0;
end
else
begin if(sw_choose==2'b00)//display mode
num_left<=4'd4;
num_right<=4'd15;
num_state <= 1'b0;
end
end
else
begin
num_left <= 4'd4;
num_right <= 4'd15;
end
end
//left display
always@(posedge clk_100ms)
begin
case(num_left)
4'd0:display_task4_left<=seg[0];
4'd1:display_task4_left<=seg[1];
4'd2:display_task4_left<=seg[2];
4'd3:display_task4_left<=seg[3];
4'd4:display_task4_left<=seg[4];
4'd5:display_task4_left<=seg[5];
4'd6:display_task4_left<=seg[6];
4'd7:display_task4_left<=seg[7];
4'd8:display_task4_left<=seg[8];
4'd9:display_task4_left<=seg[9];
default:display_task4_left<=seg[0];
endcase
//Light up the decimal point
if(num_right!=4'b1111)
begin
display_task4_left[7]<=1'b1
end
//right display
case(num_right)
4'd0:display_task4_right<=seg[0];
4'd1:display_task4_right<=seg[1];
4'd2:display_task4_right<=seg[2];
4'd3:display_task4_right<=seg[3];
4'd4:display_task4_right<=seg[4];
4'd5:display_task4_right<=seg[5];
4'd6:display_task4_right<=seg[6];
4'd7:display_task4_right<=seg[7];
4'd8:display_task4_right<=seg[8];
4'd9:display_task4_right<=seg[9];
4'd15:display_task4_right<=seg[10];
default:display_task4_right<=seg[0];
endcase
//state switching
case(state)
4'b0000:led<=8'b1111_1111;
4'b0001:led<=8'b1111_1110;
4'b0010:led<=8'b1111_1100;
4'b0011:led<=8'b1111_1000;
4'b0100:led<=8'b1111_0000;
4'b0101:led<=8'b1110_0000;
4'b0110:led<=8'b1100_0000;
4'b0111:led<=8'b1000_0000;
4'b1000:led<=8'b0000_0000;
default:led<=8'b1111_1111;
endcase
if(sw_choose==2'b11)
begin
led<=8'b1111_1111;
end
end
endmodule
(6)显示模块
在模式1~3下,先定义不同数字对应的二进制,然后通过不同的按键以及开关分别给左右数码管赋不同的值达到显示效果,无需模块1~3的输出。但在模式四条件下由于计数需要显示不同的数字,因此数码管将直接赋值为来自task_4的输出display_task4_left和display_task4_right,左右输出的内容直接在task_4中完成而非display模块中。
module display(
clk,
key_choose,
sw_choose,
display_task4_left,
display_task4_right,
display_left,
display_right
);
input clk;
input [1:0] key_choose;
input [1:0] sw_choose;
input [8:0] display_task4_left;
input [8:0] display_task4_right;
output reg [8:0] display_left;
output reg [8:0] display_right;
reg [8:0] seg [10:0];//0~9 data
initial
begin
seg[0] = 9'h3f; //0
seg[1] = 9'h06; //1
seg[2] = 9'h5b; //2
seg[3] = 9'h4f; //3
seg[4] = 9'h66; //4
seg[5] = 9'h6d; //5
seg[6] = 9'h7d; //6
seg[7] = 9'h07; //7
seg[8] = 9'h7f; //8
seg[9] = 9'h6f; //9
seg[10]= 9'h40; //empty
end
always@(posedge clk)
begin
if(key_choose!=2'b 11)//Mode 1~3
begin
case(key_choose)
2'b 00:display_left <= seg[1];
2'b 01:display_left <= seg[2];
2'b 10:display_left <= seg[3];
2'b 11:display_left <= seg[4];
default:display_left<= seg[1];
endcase
end
else//Mode 4
begin
display_left<= display_task4_left;
end
if(key_choose!=2'b 11)//Mode 1~3
begin
casez(sw_choose)
2'b 00:display_right <= seg[1];
2'b 01:display_right <= seg[2];
2'b 10:display_right <= seg[3];
2'b 11:display_right <= seg[4];
default:display_right<= seg[1];
endcase
end
else if(key_choose==2'b 11)//Mode 4
begin
display_right <= display_task4_right;
end
end
endmodule
五 模块仿真
仿真使用了Diamond的仿真功能进行仿真,为了仿真方便这里将仿真时钟设置为了500MHz,小脚丫的12MHz下的1s对应该仿真的24ms,大大压缩了仿真时间。
(1)K1仿真
如下图仿真在开关SW设置为1s时亮到灭共48ms,执行两次后换到下一个LED灯,仿真符合要求。
图5.1 SW设置为1s时仿真结果
开关SW设置为2s时亮到灭共96ms,执行两次后换到下一个LED灯,仿真符合要求。
图5.2 SW设置为2s时仿真结果
开关SW设置为3s时亮到灭共144ms,执行两次后换到下一个LED灯,仿真符合要求。
图5.3 SW设置为3s时仿真结果
开关SW设置为3s时亮到灭共192ms,执行两次后换到下一个LED灯,仿真符合要求。
图5.4 SW设置为4s时仿真结果
(2)K2仿真
如下图5.5仿真在开关SW设置为1s时亮到灭共48ms,执行两次后换到下一个LED灯,且在48s时间间隔内占空比从大到小,占空比越大则LED灯越暗,因此是由暗(图5.6)→亮(图5.7)→暗(图5.8)→亮(图5.9)→暗(图5.10)→切换(图5.11)。
图5.5 K2模式SW为1s仿真结果
图5.6第一次由暗到亮 图5.7第一次由亮到暗
图5.8第二次由暗到亮 图5.9第二次由亮到暗
图5.10第二次熄灭 图5.11切换
下面对SW分别设置为2s、3s、4s时进行仿真,由于PWM波类似,这里只展示下图5.12~5.14 LED亮灭及切换过程。
图5.12 K2模式SW为2s仿真结果
图5.13 K2模式SW为3s仿真结果
图5.14 K2模式SW为4s仿真结果
(3)K3仿真
SW设置为1s时进行仿真,对于每一个LED输出,PWM占空比都会越来越大,即从亮到灭,下图5.15可以得到对于LED[7]占空比很小,此时为最亮,然后图5.16中LED[7]占空比变小,灯变暗,最后图5.17中占空比约等于0,灯熄灭,此外图5.16的3ms处对应了LED灯的占空比前后切换的情况,但可看出肉眼无法分辨出区别,前后衔接较好,不出现切换过程中的突然闪烁的情况。
图5.15 K3模式SW为1s仿真结果
图5.16 K3模式SW为1s仿真结果
图5.17占空比变化图
(4)K4仿真
在显示模式状态下,数码管输出如下图5.18所示
图5.18 K4显示模式4
开始计数后波形如下图5.19右侧显示每0.1s加一,左侧每1s加一,即24ms变化一次,计满第一个10s后LED[0]变为低电平,表示满10s,此时对应了时间轴为24ms,在48ms处LED[1]也变为低电平,表示此时计时器计满了20s。符合设计情况。
图5.19 K4计时模式
然后对暂停、继续及清零功能进行测试,如下图5.20,仿真设置为1s计时后1s暂停,再开始1s计时再暂停1s,最后清零。由下图可见24ms时右侧计满,然后接下来24ms内波形不变,再开始计数时左侧秒数变为1,最后清零时左侧因为有小数点显示所以输出为10111111,右侧无小数点输出为00111111,所有功能符合设计要求。
图5.20 K4暂停、开始、清零仿真结果
六 资源利用说明
整体所用资源如下图
图6.1整体所需资源
K1~K4所需资源如下图6.2~6.5
图6.2 K1所需资源
图6.3 K2所需资源
图6.4 K3所需资源
图6.5 K4所需资源
图6.6 get模块所需资源
图6.7 display显示模块所需资源
七 遇到的主要困难及解决方法
(1)按键检测,原来使用的是按键按下一次即重置并切换为对应模式,但实际上按键消抖较为复杂,因此我选择了较为直接的只要检测到按下就切换为对应模式,后续无论此按钮按下多少次都没有影响,效果较好。
(2)带渐灭功能的流水灯使用了直接移位的方法产生了不同占空比的PWM波,但难点在于如何在移位前后每个LED灯的占空比能够衔接,因此我通过计算+仿真+实践的方法使得占空比在切换前后近似相等,看起来不会产生闪烁(即突然变亮或变暗)的效果。
(3)模式K1~3的数码管显示可以独立于LED部分,但模式4由于需要不断刷新显示数码管,因此我将K4产生的LED信号单独判断输出。
八 未来的计划或建议
(1)该项目已经实现循环心跳灯、呼吸灯、带渐灭功能的流水灯以及计时器的功能,开关SW1~4实际上可以有16种不同的组合,调节产生更多的周期。
(2)计时由于硬件限制上限为89.9s,可使用三色LED进行额外拓展时间以达到更长计时的目的。
(3)在程序方面不同的任务实际上有部分使用了相同的功能,为了简便我将它们按照任务划分进行编写程序,实际上其中一些分频时钟、计数器等功能可以另写一个模块进行共用减少资源浪费。代码中我也用到了很多if-else语句以及case语句,实际上他们可以相互转化,若要求资源使用较少时可以多使用if-else语句来代替case语句节约资源。