2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。。
标签
FPGA
秒表
参加活动/培训
Ling_da_jin
更新2024-04-01
北京邮电大学
109

项目需求

  • 通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
  • 使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。
  • 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。

需求分析

  • 需要用到小脚丫的两个数码管及按键,实现秒表功能。
  • 由于有计时功能,需要用到小脚丫的12M晶振作为时钟源,要求0.1s准确更新一次,即10Hz更新一次,理论实现方式为在12MHz的每个时钟上升沿计数一次,计到12*10^5后更新一次即为10Hz更新。
  • 用到四个按钮输入,由于秒表的高精度性,需要对按键进行消抖。
  • 计划利用三段式状态机进行编写

实现方式

  • 使用三段式状态机编写主模块,子模块分为seg数码管模块、debounce消抖模块(也使用了三段式状态机)、color_breath彩虹呼吸灯模块。
  • 给出三段式状态机基本写法:
always @(posedge clk or negedge rst) begin
if(!rst) begin //异步复位
STATE_C <= IDLE;
end
else begin
STATE_C <= STATE_N; //每个时钟上升沿到来时更新状态
end
end

always @(*) begin
case(STATE_C)
STATE_1: begin

end
STATE_2: begin

end



endcase
end

always @(posedge clk or negedge rst) begin
if(!rst) begin

end
else if(STATE_C == STATE_1) begin

end
else if() begin

end
end
  • 此程序可分为6个状态,STOP暂停状态、STOP_PRESS按下暂停键后一直不放手的状态,COUNT计数状态、COUNT_PRESS按下开始键后一直不放手状态、INCRE增一键、S0按下增一键后不放手状态,将程序分好状态后续的实现方式可以很简单。
  • seg数码管模块直接定义每个数字对应的的数值,直接赋值即可。
  • color_breath彩虹呼吸灯模块,首先定义pwm发生器模块,再动态调整pwm的占空比,使其由低到高再由高到低循环,RGB端口调用pwm模块实现彩虹呼吸灯。
  • 彩虹呼吸灯为原创功能,其具体功能为,增加了三种模式,普通模式为0.1s更新,模式2为0.01s更新一次,模式3为1s更新一次,不同的模式闪烁不同的呼吸灯。
  • 原创增加了红色LED灯来扩展显示的秒数,以普通模式为例,显示时间从0 ~ 9.9s后会亮起一个LED灯,同时再次从0 ~ 9.9s循环,之后再次亮起一个LED灯,在8个LED灯亮满后且再次达到9.9s才算一次完整循环,即由基本要求的9.9s计时扩展到了89.9s计时,且保证精度不变。
  • 使用拨码开关控制模式的切换,且只有在STOP状态下可以进行模式切换,注意!switch为4’b0000时为普通模式,4’b0001时为高精度模式,精确到0.01s,4’b0011时为高范围模式,精确到1s,拨码为该三种情况外的其他情况时均为普通模式。

功能框图

状态转移关系如下图:

代码及说明

1.1顶层模块参数定义

//参数、状态定义
parameter STATE_WID = 6; //状态位宽
parameter STOP = 6'b000_001; //松开后暂停状态(独热码)
parameter COUNT = 6'b000_010; //松开后计数状态
parameter INCRE = 6'b000_100; //增一状态
parameter S0 = 6'b001_000; //增一后一直按住的状态(不计数)
parameter COUNT_PRESS = 6'b010_000; //开始计数后一直按住的状态
parameter STOP_PRESS = 6'b100_000; //暂停后一直按住的状态

parameter COUNT_CLK_1 = 1199999; //计数1200000次,即0.1s
parameter COUNT_CLK_2 = 119999; //计数120000次,即0.01s
parameter COUNT_CLK_3 = 11999999; //计数12000000次,即1s

parameter PRESS = 1'b1; //按钮按下
parameter UP = 1'b0; //按钮抬起

1.2状态转换

always @(*) begin
case(STATE_C)
STOP: begin
if(btn_de == PRESS) begin //按下按键,状态转换
STATE_N = COUNT_PRESS;
end
else if(increase_de == PRESS) begin //按下加一键,状态转换
STATE_N = INCRE;
end
else begin //保持状态
STATE_N = STOP;
end
end
COUNT_PRESS: begin
if(btn_de == PRESS) begin
STATE_N = COUNT_PRESS; //一直按住就不会变动,也为计数状态
end
else if(btn_de == UP) begin
STATE_N = COUNT; //松开后进入计数状态
end
else begin
STATE_N = COUNT_PRESS;
end
end
COUNT: begin
if(btn_de == PRESS) begin
STATE_N = STOP_PRESS;
end
else begin
STATE_N = COUNT;
end
end
STOP_PRESS: begin
if(btn_de == PRESS) begin
STATE_N = STOP_PRESS;
end
else if(btn_de == UP) begin
STATE_N = STOP; //松开后进入暂停状态
end
else begin
STATE_N = STOP_PRESS;
end
end
INCRE: begin
if(increase_de == PRESS) begin
STATE_N = S0;
end
else if(increase_de == UP) begin
STATE_N = STOP;
end
else begin
STATE_N = INCRE;
end
end
S0: begin
if(increase_de == PRESS) begin
STATE_N = S0;
end
else if(increase_de == UP) begin
STATE_N = STOP;
end
else begin
STATE_N = S0;
end
end
default : begin //状态要完善
STATE_N = STATE_C;
end
endcase
end

1.3定义计时器,定义每个状态下应当做些什么

always @(posedge clk or negedge rst) begin
if(!rst) begin //复位
count <= 0;
y1 <= 0;
y0 <= 0;
led_reg <= 0;
end
else if(STATE_C == COUNT || STATE_C == COUNT_PRESS) begin //在COUNT、COUNT_PRESS启动计时
if(count == count_choose) begin
count <= 0; //计时器清零
if(y0 == 9) begin
y0 <= 0; //数字指示器个位清零
if(y1 == 9) begin
y1 <= 0; //计满,数字指示器十位清零
if(led_reg == 255) begin
led_reg <= 0; //进位计满,进位指示器清零
end
else begin
led_reg <= (led_reg << 1) + 1; //进位,左移一位并加一
end
end
else begin
y1 <= y1 + 1; //数字指示器十位进一
end
end
else begin
y0 <= y0 + 1; //每0.1s数字指示器个位加一
end
end
else begin
count <= count + 1; //在计时状态启动计时器
end
end
else if(STATE_C == INCRE) begin //在INCRE状态数字指示器加一
count <= 0; //计时器清零
if(y0 == 9) begin
y0 <= 0; //数字指示器个位清零
if(y1 == 9) begin
y1 <= 0; //数字指示器十位清零
if(led_reg == 255) begin
led_reg <= 0; //进位计满,进位指示器清零
end
else begin
led_reg <= (led_reg << 1) + 1; //进位,左移一位并加一
end
end
else begin
y1 <= y1 + 1; //数字指示器十位进一
end
end
else begin
y0 <= y0 + 1; //数字指示器个位加一
end
end
else begin
count <= 0; //在STOP、STOP_PRESS和S0状态计时器清零
y1 <= y1;
y0 <= y0; //在STOP、STOP_PRESS和S0状态数字指示器不变
led_reg <= led_reg;
case(switch) //仅可在STOP、STOP_PRESS和S0状态下改变模式
4'b0000: begin
count_choose <= COUNT_CLK_1; //普通秒表模式,精确到0.1s
mod_reg <= 0;
end
4'b0001: begin
count_choose <= COUNT_CLK_2; //模式2,精确到0.01s
mod_reg <= 1;
end
4'b0011: begin
count_choose <= COUNT_CLK_3; //模式3,精确到1s
mod_reg <= 2;
end
default: begin
count_choose <= COUNT_CLK_1;
mod_reg <= 0;
end
endcase
end
end

2.数码管控制

module seg(y1,y0,seg0,seg1);
input [3:0]y1;
input [3:0]y0;
output [8:0]seg1;
output [8:0]seg0;
reg [8:0]seg_reg [9:0];

initial begin
seg_reg[0] = 9'h3f;
seg_reg[1] = 9'h06;
seg_reg[2] = 9'h5b;
seg_reg[3] = 9'h4f;
seg_reg[4] = 9'h66;
seg_reg[5] = 9'h6d;
seg_reg[6] = 9'h7d;
seg_reg[7] = 9'h07;
seg_reg[8] = 9'h7f;
seg_reg[9] = 9'h6f;
end

assign seg1 = seg_reg[y1];
assign seg0 = seg_reg[y0];


endmodule

3.1PWM发生器

module pwm(out,duty,clk);
input [7:0] duty; // 输入,表示占空比,范围从0到255
input clk; // 输入,时钟信号用于同步
output reg out; // 输出信号,表示PWM波形
reg [7:0] buffer; // 8位寄存器,用于存储当前计数值

always @ (posedge clk) begin
buffer <= buffer + 1;// 在每个时钟上升沿递增缓冲区值
if (buffer < duty)// 将缓冲区值与占空比进行比较:如果缓冲区小于占空比,则将输出设置为0;否则,设置为1。
begin
out <= 0;
end
else begin
out <= 1;
end
end
endmodule

3.2通过不断改变占空比实现呼吸效果

always @(posedge divide_clk) begin  //使RGB_buffer在0~255递增再从255~0递减
if(wheel_position < 510 ) wheel_position <= wheel_position + 1;
else wheel_position <= 0;
if(wheel_position < 255) begin
RGB_buffer <= wheel_position;
end
else begin
RGB_buffer <= 510 - wheel_position;
end
end

4.1消抖模块状态转换关系

//参数定义
localparam KEY_W = 1;
localparam TIME_20MS = 24_0000;

localparam IDLE = 4'b0001;//初始状态
localparam DOWN = 4'b0010;//按键按下抖动
localparam HOLD = 4'b0100;//按键按下后稳定
localparam UP = 4'b1000;//按键上升抖动
//状态转移条件定义
wire idle2down; //初始转移到按下抖动
wire down2idle; //按下抖动转移到初始
wire down2hold; //按下抖动转移到按下稳定
wire hold2up ; //按下稳定转移到上升抖动
wire up2idle ; //上升抖动转移到初始

always@(*)begin
case(state_c)
IDLE:begin
if(idle2down)begin
state_n = DOWN;
end
else begin
state_n = state_c;
end
end
DOWN:begin
if(down2idle)begin
state_n = IDLE;
end
else if(down2hold)begin
state_n = HOLD;
end
else begin
state_n = state_c;
end
end
HOLD:begin
if(hold2up)begin
state_n = UP;
end
else begin
state_n = state_c;
end
end
UP:begin
if(up2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default:state_n = state_c;
endcase
end

4.2检测按键抖动来定义状态转换

assign idle2down = (state_c == IDLE) && nedge;//检测到下降沿
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//计时到20ms时没有出现上升沿标志按键按下后保持稳定
assign hold2up = (state_c == HOLD) && (pedge);//检测到上升沿跳转到上升态
assign up2idle = (state_c == UP) && end_cnt_20ms;//计数器计数到20ms跳转到初始态

4.3按键赋值

//按键赋值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out_r <= {KEY_W{1'b0}};
end
else if(state_c == HOLD)begin
key_out_r <= {KEY_W{1'b1}};
end
else begin
key_out_r <= {KEY_W{1'b0}};
end
end
assign key_out = key_out_r;

使用大模型生成模板

1.大模型帮助生成有限状态机模板

与我自己编写的状态机相比,大模型告诉我这是摩尔型状态机,还有mealy型状态机,此种状态机代码更为复杂,输出同时取决于当前状态与输入信号,而摩尔型输出只取决于当前状态,于是尝试询问mealy型状态机模板:

2.大模型帮助生成彩虹呼吸灯模板

大模型生成的彩色呼吸灯与我自己写的相比,语言更加简洁,他将PWM模块与呼吸灯相结合了,而我是先定义了PWM生成函数模块,再通过调用PWM实现彩虹呼吸灯。

仿真波形图

1.编写总体测试文件

`timescale 1ns/1ns
module counter_tb();
reg clk;
reg btn;
reg increase;
reg rst;
wire [8:0]seg1;
wire [8:0]seg0;

counter uut(
.clk(clk),
.btn(btn),
.rst(rst),
.increase(increase),
.seg0(seg0),
.seg1(seg1)
);

always #(125/3) clk = ~clk;
// always #5 clk = ~clk;

initial begin
rst = 1;
btn = 1;
increase = 1;
clk = 1;

rst = 0;
#5
rst = 1; //初始复位
#9000000

//==============按下按钮开始计时=============
#100
btn = 0;
#100
btn = 1;
// #900000000
#9000000

//==============按下按钮暂停====================
btn = 0;
#100
btn = 1;
#9000000

//=================测试加一键========================
increase = 0;
#1000
increase = 1;
#9000000

//====================测试复位键==================
rst = 0;
#100
rst = 1;
#4000000
$stop;

end

endmodule

输出波形

2.测试消抖

`timescale 1ns/1ns
module debounce_tb();
reg clk;
reg rst;
reg key_in;
wire key_out;

debounce uut(
.clk(clk),
.rst_n(rst),
.key_in(key_in),
.key_out(key_out)
);
always #(125/3) clk = ~clk;

initial begin
rst = 1;
key_in = 1;
clk = 1;

rst = 0;
#5
rst = 1; //初始复位
#1000000

key_in = 0;
#200000
key_in = 1; //按下按键抖动
#200000
key_in = 0;
#25000000

key_in = 1;
#200000
key_in = 0; //抬起按键抖动
#200000
key_in = 1;
#30000000
$stop;
end

endmodule

输出波形:

可见,在输入模拟按下抖动信号大约20ms后才会输出消抖后信号,表明进入了稳定按下的状态,在输入模拟抬起抖动信号时,在上升沿出现一瞬间就输出了消抖后信号,表明已经不在按下状态。且由于小脚丫特性,输入信号中高电平代表未按下,低电平代表按下,而输出信号经过修正,高电平代表按下,低电平代表未按下。

FPGA资源利用说明

Design Summary

Number of registers: 241 out of 4635 (5%)

PFU registers: 241 out of 4320 (6%)

PIO registers: 0 out of 315 (0%)

Number of SLICEs: 212 out of 2160 (10%)

SLICEs as Logic/ROM: 212 out of 2160 (10%)

SLICEs as RAM: 0 out of 1620 (0%)

SLICEs as Carry: 116 out of 2160 (5%)

Number of LUT4s: 421 out of 4320 (10%)

Number used as logic LUTs: 189

Number used as distributed RAM: 0

Number used as ripple logic: 232

Number used as shift registers: 0

Number of PIO sites used: 40 + 4(JTAG) out of 105 (42%)

Number of block RAMs: 0 out of 10 (0%)

Number of GSRs: 1 out of 1 (100%)


Diamond MAP分析界面截图:


附件下载
file.zip
源文件、仿真文件及编译后的jed文件
团队介绍
田晁旭 北京邮电大学
团队成员
Ling_da_jin
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表该项目使用了WebIDE平台、Verilog HDL语言,实现了秒表的设计,它的主要功能为:启动和停止计时,递增和复位。
Glass
304
2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表该项目使用了基于Lattice MXO2的小脚丫FPGA核心板,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
电小麦
233
2024寒假练——基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,基于stepfpga网页版编译器,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:创建一个2位数码管秒表 ,秒表从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次,通过按键可以控制开始、停止,递增和清零。。
MSCu
209
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号