2024年寒假练 - 基于小脚丫FPGA实现不同的LED显示效果
该项目使用了Lattice MXO2小脚丫FPGA,实现了不同的LED显示效果的设计,它的主要功能为:心跳循环灯、呼吸灯、带渐灭功能的流水灯、计时器。
标签
FPGA
USB
参加活动/培训
Super
更新2024-04-16
南京理工大学
534

一 项目需求

image.png

图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计时器模式下在计时期间显示计时数值,清零后显示工作模式。

三 功能框图

image.png

图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值

image.png

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,利用下述公式

image.png

其中t为1~4。为了使得LED状态切换前后占空比一致,通过调整每一个分频系数div对应的CNT_NUM可以使其满足要求,计算公式如下

image.png

由此可以计算出四个分频系数所以对应的最大值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灯,仿真符合要求。

image.png

图5.1 SW设置为1s时仿真结果

开关SW设置为2s时亮到灭共96ms,执行两次后换到下一个LED灯,仿真符合要求。

image.png

图5.2 SW设置为2s时仿真结果

开关SW设置为3s时亮到灭共144ms,执行两次后换到下一个LED灯,仿真符合要求。

image.png

图5.3 SW设置为3s时仿真结果

开关SW设置为3s时亮到灭共192ms,执行两次后换到下一个LED灯,仿真符合要求。

image.png

图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)。

image.png

图5.5 K2模式SW为1s仿真结果

image.pngimage.png

图5.6第一次由暗到亮                  图5.7第一次由亮到暗

image.pngimage.png

图5.8第二次由暗到亮                 图5.9第二次由亮到暗

image.pngimage.png

图5.10第二次熄灭                    图5.11切换

下面对SW分别设置为2s、3s、4s时进行仿真,由于PWM波类似,这里只展示下图5.12~5.14 LED亮灭及切换过程。

image.png

图5.12 K2模式SW为2s仿真结果

image.png

图5.13 K2模式SW为3s仿真结果

image.png

图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灯的占空比前后切换的情况,但可看出肉眼无法分辨出区别,前后衔接较好,不出现切换过程中的突然闪烁的情况。

image.png

图5.15 K3模式SW为1s仿真结果

image.png

图5.16 K3模式SW为1s仿真结果

image.png

图5.17占空比变化图

(4)K4仿真

在显示模式状态下,数码管输出如下图5.18所示

image.png

图5.18 K4显示模式4

开始计数后波形如下图5.19右侧显示每0.1s加一,左侧每1s加一,即24ms变化一次,计满第一个10s后LED[0]变为低电平,表示满10s,此时对应了时间轴为24ms,在48ms处LED[1]也变为低电平,表示此时计时器计满了20s。符合设计情况。

image.png

图5.19 K4计时模式

然后对暂停、继续及清零功能进行测试,如下图5.20,仿真设置为1s计时后1s暂停,再开始1s计时再暂停1s,最后清零。由下图可见24ms时右侧计满,然后接下来24ms内波形不变,再开始计数时左侧秒数变为1,最后清零时左侧因为有小数点显示所以输出为10111111,右侧无小数点输出为00111111,所有功能符合设计要求。

image.png

图5.20 K4暂停、开始、清零仿真结果

六 资源利用说明

整体所用资源如下图

image.png

图6.1整体所需资源

K1~K4所需资源如下图6.2~6.5

image.png

图6.2 K1所需资源

image.png

图6.3 K2所需资源

image.png

图6.4 K3所需资源

image.png

图6.5 K4所需资源

image.png

图6.6 get模块所需资源

image.png

图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语句节约资源。

附件下载
TOP.jed
可直接使用的jed文件
archive.zip
整个工程文件
团队介绍
南京理工大学-黄懿杰
团队成员
Super
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号