2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表
该项目使用了Verilog语言、webIDE,实现了9.9正计时秒表的设计,它的主要功能为:从0.0以10hz计时到9.9并翻转循环计时,具有启动、停止、递增和清除功能键。
标签
FPGA
verilog
参加活动/培训
xhj
更新2024-04-01
哈尔滨工业大学
88

1.项目需求

通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。

使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9 秒,然后翻转,计数值每0.1秒精确更新一次。

秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10 Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增, 但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。


2.需求分析

2.1时钟频率


由原理图可知,时钟频率为12 MHz,而秒表以10 Hz时钟速率递增(即每0.1秒计数一次),故设置一个寄存器变量 cnt 以存储计数值,即时钟上升沿到来的次数,当cnt从0计数到12_000_00-1时,即完成了0.1s的计时。

2.2数码管显示

image.png

本项目需要使用两个数码管进行计数显示。由原理图可知,核心板上两个数码管皆为共阴极数码管,每个数码管的控制信号有九位,MSB~LSB=DIG、DP、G、F、E、D、C、B、A,其中DIG对应位选码,共阴极故接地;DP对应小数点,十位需要点亮置一;其余七位段选码根据需要显示的数字0~9确定。数码管从0.0开始显示,每0.1s刷新显示值,当显示值达到9.9时,翻转回0.0,重新开始计时。

2.3按键

image.png

本项目一共需要使用四个独立按键进行功能切换,分别实现开始、停止、增量和清除(重置)四个功能。由原理图可知,按键默认为高电平,按下为低电平。

由四个按键的功能可知,计时器有两种状态:启动或者暂停。按下开始键使得计时器转向启动状态,按下停止或重置键使得计时器转向暂停状态,而增量键不改变计时器运行状态。除了运行状态的切换,增量键使得显示值加一,而清除键使得显示值归零,而开始键和暂停键不额外改变显示值。

每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间,故需要对按键进行消抖。

3.实现的方式

3.1时钟频率

已知时钟频率为12 MHz,而秒表以10 Hz时钟速率递增,故设置一个reg型变量cnt作为计数器,用于检测时钟上升沿到来的数量。当计时器处于启动状态时,cnt从0开始计数,每次检测到时钟上升沿,cnt加一,当cnt=12_000_00-1时,恰好经过0.1s,更新数码管显示值,cnt清零,重新开始计数。

3.2数码管显示

3.2.1移位加三法

数码管显示值最大为9.9,设置一个reg型变量cnt_seg用于存储数码管的显示值。为了分离cnt_seg的十位和个位,将两个数赋给不同的数码管,需要用到移位加三法。该方法可以将二进制数转换为bcd码,其中bcd[7:4]表示十位,bcd[3:0]表示个位。

3.2.2查表法

为了方便根据数码管的显示数得到其控制信号的输入,设置一个深度为10的reg型变量数组seg,存储显示数为0~9对应的数码管输入,在initial模块里进行赋值,由此得到一张对应表。每次需要更新数码管显示值时,只需取数组seg对应的元素即可。

此外,由于十位需要显示小数点,故其输入信号需要额外加上9'h80。

3.3按键

3.3.1按键消抖

检测到下降沿后延时20ms,再判断是否为低电平,是则输出一个时钟的高电平脉冲,如果20ms内电平没有一直维持低电平认为这是一种抖动,不输出信号。

3.3.2按键功能实现

设置了一个reg型变量start_flag作为启停标志位,存储秒表的运行状态:启动或者停止。判断开始、停止和重置键按下与否,对启停标志位进行更新。

根据按键按下情况和启停标志位进行判断,更新数码管的显示值cnt_seg。按下重置键,cnt_seg清零;按下增量键,cnt_seg加一,若cnt_seg为99,则翻转至00;启停标志位处于启动状态且经过了0.1s时,cnt_seg加一,若cnt_seg为99,则翻转至00。

4.功能框图

image.png

5.代码及说明

编写Verilog时,借助ChatGPT进行学习,并利用ChatGPT设计模块,在此基础上进行修改。

询问ChatGPT的记录

5.1顶层文件

5.1.1输入、输出信号
    input clk, rst;                     // Clock, Reset
input [3:0] key; // 0 for start, 1 for pause, 2 for increment, 3 for reset

output [8:0] seg_led_1, seg_led_2; // Segmented LED display 1, Segmented LED display 2
5.1.2信号标志位的产生
		// Debouncing keys to get high pulse on key press
debounce #(
.N(4) // Number of keys to debounce
) U1 (
.clk (clk), // Clock input
.rst (rst), // Reset input
.key (key[3:0]), // Key inputs
.key_pulse(keyi[3:0]) // Debounced key pulses
);

assign add_flag = keyi[2]; // Assigning increment flag
assign clear_flag = keyi[3]; // Assigning clear flag

// Start/pause flag control
always @(posedge clk or negedge rst)
begin
if (!rst)
start_flag <= 0; // Reset on reset
else if (keyi[3]) // Reset if clear flag is set
start_flag <= 0;
else if (keyi[1]) // Pause if pause flag is set
start_flag <= 0;
else if (keyi[0]) // Start if start flag is set
start_flag <= 1;
else
start_flag <= start_flag; // Otherwise maintain the previous state
end

对按键进行消抖后,由开始/暂停/重置键的按下判断,决定启停标志位;增量和重置标志位由对应按键直接决定。

5.1.3计数器分频
    // Counter module
always @(posedge clk or negedge rst)
begin
if (!rst) // Reset on reset
cnt <= 0;
else if (clear_flag) // Reset if clear flag is set
cnt <= 0;
else if (cnt >= 1200000-1) // Reset if 0.1s elapsed
cnt <= 0;
else if (start_flag==1) // Increment if start flag is set
cnt <= cnt + 1;
else
cnt <= cnt; // Otherwise maintain the previous state
end

实现0.1s计时。

5.1.4 9.9计时控制
 // Control for 9.9s positive timing
always @(posedge clk or negedge rst)
begin
if (!rst) // Reset on reset
cnt_seg <= 0;
else if(clear_flag) // Reset if clear flag is set
cnt_seg <= 0;
else if(add_flag) // Increment if increment flag is set
begin
if(cnt_seg >= 99) // Reset if 99 is reached
cnt_seg <= 0;
else
cnt_seg <= cnt_seg + 1;
end
else if(start_flag && cnt >= 1200000-1) // Increment if 0.1s elapsed in start state
begin
if(cnt_seg >= 99) // Reset if 99 is reached
cnt_seg <= 0;
else
cnt_seg <= cnt_seg + 1;
end
else
cnt_seg <= cnt_seg; // Otherwise maintain the previous state
end

更新数码管显示值。

5.1.5数码管显示
    reg [8:0] seg [9:0];                // Array to store segment LED display patterns

// Initialization: Assigning segment LED display patterns
initial
begin
seg[0] = 9'h3f; // Display pattern for 0
seg[1] = 9'h06; // Display pattern for 1
seg[2] = 9'h5b; // Display pattern for 2
seg[3] = 9'h4f; // Display pattern for 3
seg[4] = 9'h66; // Display pattern for 4
seg[5] = 9'h6d; // Display pattern for 5
seg[6] = 9'h7d; // Display pattern for 6
seg[7] = 9'h07; // Display pattern for 7
seg[8] = 9'h7f; // Display pattern for 8
seg[9] = 9'h6f; // Display pattern for 9
end

// Binary to BCD conversion
bin2bcd display_val(
.bin (cnt_seg), // Input binary value
.bcd (cnt_seg_bcd) // Output BCD value
);

// Assigning segment LED patterns based on BCD value
assign seg_led_2[8:0] = {seg[cnt_seg_bcd[3:0]]}; // Units place
assign seg_led_1[8:0] = {seg[cnt_seg_bcd[7:4]]} + 9'h80; // Tens place

用reg变量存储数码管显示值对应表,需要注意的是显示十位的数码管的dp需点亮,额外加上9'h80。

5.2子模块

5.2.1按键消抖
module debounce (
clk,
rst,
key,
key_pulse
);

parameter N = 1; // Number of keys to debounce

input clk; // Clock input
input rst; // Reset input
input [N-1:0] key; // Input keys
output [N-1:0] key_pulse; // Output key pulse signal

reg [N-1:0] key_rst_pre; // Register storing previous key state
reg [N-1:0] key_rst; // Register storing current key state
wire [N-1:0] key_edge; // Signal indicating positive edge of key signal

// Store current and previous key states on clock edge or reset
always @(posedge clk or negedge rst) begin
if (!rst) begin
key_rst <= {N{1'b1}}; // Initialize key_rst to all ones
key_rst_pre <= {N{1'b1}}; // Initialize key_rst_pre to all ones
end
else begin
key_rst <= key; // Store current key state
key_rst_pre <= key_rst; // Store previous key state
end
end

// Detect positive edge of key signal
assign key_edge = key_rst_pre & (~key_rst);

reg [17:0] cnt; // Counter for generating delay (for debounce)

// Generate delay of around 20ms on detecting key edge
always @(posedge clk or negedge rst) begin
if (!rst)
cnt <= 18'h0; // Reset counter on reset
else if (key_edge)
cnt <= 18'h0; // Reset counter on key edge
else
cnt <= cnt + 1'h1; // Increment counter otherwise
end

reg [N-1:0] key_sec_pre; // Register storing previous key state after delay
reg [N-1:0] key_sec; // Register storing current key state after delay

// Delayed key detection: check key state after delay
always @(posedge clk or negedge rst) begin
if (!rst)
key_sec <= {N{1'b1}}; // Initialize key_sec to all ones
else if (cnt == 18'h3ffff)
key_sec <= key; // Store current key state after delay
end

// Store previous key state after delay on clock edge or reset
always @(posedge clk or negedge rst) begin
if (!rst)
key_sec_pre <= {N{1'b1}}; // Initialize key_sec_pre to all ones
else
key_sec_pre <= key_sec; // Store previous key state after delay
end

// Generate key pulse on positive edge of key signal after delay
assign key_pulse = key_sec_pre & (~key_sec);

endmodule

检测到下降沿后延时20ms,再判断是否为低电平,是则输出一个时钟的高电平脉冲,如果20ms内电平没有一直维持低电平认为这是一种抖动,不输出信号。

(此处引用了webIDE里已有的消抖模块)

5.2.2移位加三法
module bin2bcd(bin, bcd);
input wire [7:0] bin; // Input eight-bit binary number
output wire [11:0] bcd; // Output twelve-bit BCD code

wire [19:0] w_0; // Intermediate variable for storing data during conversion
wire [19:0] w_1;
wire [19:0] w_2;
wire [19:0] w_3;
wire [19:0] w_4;
wire [19:0] w_5;
wire [19:0] w_6;
wire [19:0] w_7;

// Instantiate eight shift modules to convert the input binary number into eight segments of BCD code
shift shift_ins0 (.i_data({12'd0, bin}), .o_data(w_0));
shift shift_ins1 (.i_data(w_0), .o_data(w_1));
shift shift_ins2 (.i_data(w_1), .o_data(w_2));
shift shift_ins3 (.i_data(w_2), .o_data(w_3));
shift shift_ins4 (.i_data(w_3), .o_data(w_4));
shift shift_ins5 (.i_data(w_4), .o_data(w_5));
shift shift_ins6 (.i_data(w_5), .o_data(w_6));
shift shift_ins7 (.i_data(w_6), .o_data(w_7));

// Concatenate the converted BCD segments into a twelve-bit output
assign bcd = w_7[19:8];
endmodule

module shift(i_data, o_data);
input wire [19:0] i_data; // Input twenty-bit data
output wire [19:0] o_data; // Output twenty-bit data

wire [19:0] shift_data; // Intermediate variable for storing shifted data

// Perform shifting operations on different segments of the input data and increment values based on conditions
assign shift_data[19:16] = i_data[19:16] > 4'd4 ? i_data[19:16] + 4'd3 : i_data[19:16];
assign shift_data[15:12] = i_data[15:12] > 4'd4 ? i_data[15:12] + 4'd3 : i_data[15:12];
assign shift_data[11:8] = i_data[11:8] > 4'd4 ? i_data[11:8] + 4'd3 : i_data[11:8];
assign shift_data[7:0] = i_data[7:0];

// Left-shift the processed data by one bit and assign it as the output data
assign o_data = shift_data << 1'b1;
endmodule

将二进制码转换为bcd码,bcd[3:0]为个位,bcd[7:4]为十位。

6.仿真波形图

由于webIDE仿真界面只有1600ns选项,故使用ModelSim仿真。

6.1按键消抖

image.png

为了方便仿真,将顶层文件的延时暂时修改为200ns。

如图所示,在按键按下一段时间后,才产生一个时钟周期的高电平,实现了延时消抖。

6.2正计时

image.png

由于十位的Dp点被点亮,所以十位和个位的输入信号在数码管显示表的基础上不一致。

6.3开始、停止、增量、复位功能

image.png

复位后开始,计时一段时间后暂停,然后按下增量键,之后重置,计时显示值清零,又按下开始键继续计数。

7.FPGA的资源利用说明

Design Summary:
Number of registers: 67 out of 4635 (1%)
PFU registers: 67 out of 4320 (2%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 69 out of 2160 (3%)
SLICEs as Logic/ROM: 69 out of 2160 (3%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 28 out of 2160 (1%)
Number of LUT4s: 138 out of 4320 (3%)
Number used as logic LUTs: 82
Number used as distributed RAM: 0
Number used as ripple logic: 56
Number used as shift registers: 0
Number of PIO sites used: 24 + 4(JTAG) out of 105 (27%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)


附件下载
stopwatch.zip
团队介绍
谢惠娟 哈尔滨工业大学(威海)
评论
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秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
电小麦
234
2024寒假练——基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,基于stepfpga网页版编译器,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:创建一个2位数码管秒表 ,秒表从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次,通过按键可以控制开始、停止,递增和清零。。
MSCu
212
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号