feat: add markdown

This commit is contained in:
alivender
2025-05-19 21:40:55 +08:00
parent 068576b60b
commit 0bd3b42840
17 changed files with 436 additions and 146 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 KiB

View File

@@ -0,0 +1,175 @@
# 基础-1-流水灯
## 1.1 章节导读
流水灯实验作为基础实验的第一个实验是非常合适的本章我们利用试验箱中的LED进行点亮LED并实现流水灯的功能。
## 1.2 理论学习
相信大家之前肯定接触过单片机等设备而学习这些设备的第一个实验例程往往都是点亮一个LED。本次实验在点亮LED的基础上另LED灯依次闪亮循环不止实现“流水”的功能。其原理是依次控制连接到LED的IO口的电平高低让LED的闪亮间隔为0.5s,以实现流水灯的效果。
## 1.3 实战演练
### 1.3.1实验目标
依次点亮实验板中的8个LED灯两灯点亮间隔为0.5s每次点亮持续0.5s,实现流水灯效果。
### 1.3.2硬件资源
实验板上有0~31共32个LED灯的资源每4个LED灯为一组分别是绿黄四种颜色本次实验使用8个LED进行验证。
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/1.png"
alt="无法显示图片时显示的文字"
style="zoom:30%"/>
<br> <!--换行-->
图1.LED扩展板 <!--标题-->
</center>
</div>
通过原理图可以得知本试验箱的LED灯为高电平时点亮。
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/2.png"
alt="无法显示图片时显示的文字"
style="zoom:40%"/>
<br> <!--换行-->
图2.LED扩展板原理图 <!--标题-->
</center>
</div>
### 1.3.3程序设计
流水灯的设计与分频器计数器的逻辑相似只是多了LED灯的点亮部分。为了实现计数器肯定需要时钟信号sysclk也需要一个复位信号rstn同时为了驱动LED需要8个IO口。所以模块的端口如下表所示
| 端口名称 | 端口位宽 | 端口类型 |功能描述
|:----------:|:----:|:----:|:--------------------:|
| sysclk | 1Bit | Input | 输入时钟频率27M |
| rstn | 1Bit | Input | 复位信号,低电平有效 |
| led | 8Bit | Output | LED控制信号 |
为了使灯点亮0.5s我们应该设计一个计数器或者是分频器先将板载27M高频时钟降速。在27M时钟下计数0.5s需要计数器计数13_500_000个数也就是计数器从0开始计数到13_499_999。所以我们定义一个寄存器cnt每一次时钟上升沿cnt就加1当计数到13_499_999时led的状态改变同时cnt归零重新开始计数。
为了实现8个led流水的效果我们将0定义为led灭1表示亮初始状态led = 8b0000_0001当经过0.5s后也就是cnt等于13_499_999的时候第一个led灭第二个led亮起也就是led = 8b0000_0010。同理再过0.5sled = 8b0000_0100再过0.5sled = 8b0000_1000以此类推。
根据上面的规律我们很容易发现led的流水是靠1的移位来实现的也就是最基本的左移(<<)和右移(>>)运算符去实现。在这里我们需要向左移位并且每次只需要移动1位。模块的参考代码waterled_top.v如下所示
```verilog
module waterled_top(
input sysclk, //27MHz system clock
input rstn, //active low reset
output [7:0] led
);
parameter CNT_MAX = 32'd13_499_999;
reg [7:0] led_reg;
reg [31:0] cnt;
//cnt 当cnt == CNT_MAX时变为0计数0.5秒
always @(posedge sysclk) begin
if (!rstn)
cnt <= 0;
else if (cnt < CNT_MAX)
cnt <= cnt + 1;
else
cnt <= 0;
end
//led_reg 当cnt == CNT_MAX时左移一位。
always @(posedge sysclk) begin
if (!rstn)
led_reg <= 8'b0000_0001;
else if (led_reg == 8'b1000_0000 && cnt == CNT_MAX)//led7亮0.5s后重回led0
led_reg <= 8'b0000_0001;
else if (cnt == CNT_MAX) //0.5s后左移
led_reg <= led_reg << 1;
else
led_reg <= led_reg;
end
//led
assign led = led_reg;
endmodule
```
### 1.3.4仿真验证
为上述模块编写仿真模块参考代码waterled_top_tb.v如下
```verilog
`timescale 1ns/1ns
module waterled_top_tb;
reg sysclk;
reg rstn;
wire [7:0] led;
// 实例化待测试模块
waterled_top #(
.CNT_MAX(32'd100)//为了加快仿真速度将模块内部CNT_MAX由13_499_999变为1000
)uut (
.sysclk(sysclk),
.rstn(rstn),
.led(led)
);
// 产生系统时钟:周期约为 27Mhz
initial begin
sysclk = 0;
forever #(500/27) sysclk = ~sysclk;
end
// 初始化和复位过程
initial begin
// 初始化
rstn = 0;
#100; // 保持复位100ns
rstn = 1; // 释放复位
end
endmodule
```
为了加速仿真我们在仿真文件中另CNT_MAX的值为100。同时为了便于仿真可以直接点击sim文件夹下hebav文件夹中的do.bat文件即可利用ModuleSim对模块进行仿真仿真波形如下
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/3.png"
alt="无法显示图片时显示的文字"
style="zoom:70%"/>
<br> <!--换行-->
图3.流水灯仿真波形(一) <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/4.png"
alt="无法显示图片时显示的文字"
style="zoom:70%"/>
<br> <!--换行-->
图4.流水灯仿真波形(二) <!--标题-->
</center>
</div>
从图3我们可以看到端口信号led的值经过一定时间之后就进行了左移并且在图4中我们也可以发现当cnt的值等于CNT_MAX的时候led进行左移与我们设计的目标相符合可以进行下一步上板验证了。
### 1.3.5上板验证
仿真已经通过,可以进行上板验证,上板前要先进行管脚约束。端口与对应管脚如下表所示:
| 端口名称 |信号类型| 对应管脚|功能
|:----:|:----:|:----:|:----:|
| sysclk | Input | | 时钟 |
| rstn | Input | | 复位 |
| led[0] | Output | | LED |
| led[1] | Output | | LED |
| led[2] | Output | | LED |
| led[3] | Output | | LED |
| led[4] | Output | | LED |
| led[5] | Output | | LED |
| led[6] | Output | | LED |
| led[7] | Output | | LED |
管脚分配可以直接编写.fdc文件也可以使用PDS内置的工具进行分配。
完成管脚分配之后就可以生成sbit文件将文件提交到网站后点击烧录即可将sbit下载到实验板中在摄像头页面即可观察到流水灯的现象。
## 1.4 章末总结
本次实验主要学习使用左移(<<)和右移(>>)运算符实现移位,但实际应用中也可以使用位拼接({})进行更加复杂的移位操作,各位同学可以尝试学习。

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

258
public/doc/02_key/key.md Normal file
View File

@@ -0,0 +1,258 @@
# 基础-2-按键检测与消抖
## 2.1 章节导读
在数字电路中,按键是最常用的人机交互输入方式。然而,机械式按键在按下或释放过程中会产生抖动信号,直接读取会引起误触发。本章我们将实现一个可靠的按键检测模块,完成信号的消抖和下降沿检测,以便为更复杂的模块如状态机切换、模式转换等提供稳定的触发信号。
## 2.2 理论学习
由于机械结构的限制按键在触发的一瞬间其接触点会发生数次抖动导致输出信号在0和1之间反复跳变。这种现象称为“抖动”。为避免系统错误响应需要对按键信号进行“消抖”处理。
常见的软件消抖方法包括定时器延时,而在软件中通常使用计数器。在本实验中,采用对输入信号进行采样判断,当其状态发生变化时开始计数,若持续稳定一定时长后,才认为按键真正改变。
在此基础上,若需检测按键的“按下事件”,则还需进一步提取其上升沿(或下降沿)作为一个单周期的“有效触发”信号。
## 2.3 实战演练
### 2.3.1 实验目标
实现一个具有消抖功能的按键检测模块,并进一步提取其下降沿触发信号,输出一个单时钟周期宽度的 `btn_flag` 信号用于后级逻辑判断。同时为了使实验现象更加明显设置8位的IO输出连接led当检测到 `btn_flag` 信号后8位信号`led`会自加1。
### 2.3.2 硬件资源
本实验使用试验箱上普通按键输入资源,输入信号经过电平转换后进入 FPGA 芯片,输出信号可连接状态指示灯以观察效果。
根据原理图可知实验板的按键按下是低电平,不按为高电平。
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/1.png"
alt="无法显示图片时显示的文字"
style="zoom:30%"/>
<br> <!--换行-->
图1.实验板的按键资源 <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/2.png"
alt="实验板按键原理图"
style="zoom:60%"/>
<br> <!--换行-->
图2.实验板按键原理图 <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/xx.png"
alt="数字孪生"
style="zoom:60%"/>
<br> <!--换行-->
图3.远程实验界面按键 <!--标题-->
</center>
</div>
### 2.3.3 程序设计
为了实现稳定的按键检测逻辑,设计流程如下:
1. 对输入 `btn` 进行采样,形成 `btn_temp`
2. 若检测到 `btn_temp` 与当前 `btn` 状态不一致,则开始计数;
3. 若计数器 `cnt` 达到设定阈值如255则认为按键状态稳定更新 `btn_ggle`
4. 实验板的按键按下是低电平,不按为高电平。所以对 `btn_ggle` 打两拍形成 `btn_flag_d0``btn_flag_d1`,再判断其下降沿,输出一个时钟周期的`btn_flag`
5. 检测到信号`btn_flag`后,信号`led <= led + 1`
该模块的参考代码如下(`btn_ggle.v`
```verilog
module btn_ggle(
input wire clk,
input wire rstn,
input wire btn,
output wire btn_flag,
output reg [7:0] led
);
reg btn_ggle;
reg btn_flag_d0,btn_flag_d1;
reg [7:0] cnt;
reg btn_temp;
//检测按键状态
always @(posedge clk) btn_temp <= btn;
//按键状态改变时开始计数
always @(posedge clk) begin
if(~rstn) cnt <= 0;
else if(btn_temp != btn) cnt <= 1;
else if(cnt != 0) cnt <= cnt + 1;
else cnt <= 0;
end
//计数到255时认为按键值稳定
always @(posedge clk) begin
if(~rstn) btn_ggle <= btn;
else if(cnt == 8'hFF) btn_ggle <= btn_temp;
else btn_ggle <= btn_ggle;
end
//对btn_ggle信号延迟打拍
always @(posedge clk) begin
if(~rstn) begin
btn_flag_d0 <= 0;
btn_flag_d1 <= 0;
end
else begin
btn_flag_d0 <= btn_ggle;
btn_flag_d1 <= btn_flag_d0;
end
end
//btn_flag检测btn_ggle的下降沿
assign btn_flag = ~btn_flag_d0 && btn_flag_d1;
//检测到按键按下的标志位btn_flagled会加1
always @(posedge clk) begin
if(~rstn) led <= 0;
else if(btn_flag) led <= led + 1;
else led <= led;
end
endmodule
```
### 2.3.4 仿真验证
为验证功能的正确性,设计测试平台(`btn_ggle_tb.v`),代码如下:
```verilog
`timescale 1ns/1ns
module btn_ggle_tb;
reg clk;
reg rstn;
reg btn;
wire btn_flag;
wire [7:0] led;
btn_ggle btn_ggle_inst (
.clk(clk),
.rstn(rstn),
.btn(btn),
.btn_flag(btn_flag),
.led(led)
);
// 27MHz 时钟周期约为 37.037ns取37ns近似
always #(500/27) clk = ~clk; // 半周期18.5ns ≈ 27MHz
initial begin
// 初始化
clk = 0;
rstn = 0;
btn = 1; // 按键默认未按下,高电平有效
// 释放复位
#200;
rstn = 1;
// 模拟带抖动的按下过程
#1000 btn = 0;
#100 btn = 1; // 抖动
#100 btn = 0;
#100 btn = 1;
#100 btn = 0;
// 稳定按下
#100000 btn = 0;
// 模拟抖动松开过程
#300000 btn = 1;
#100 btn = 0;
#100 btn = 1;
#100 btn = 0;
#100 btn = 1;
// 稳定松开
#100000 btn = 1;
// 第二次按下
#300000 btn = 0;
#100000 btn = 0;
#300000 $finish;
end
endmodule
```
利用ModuleSim进行仿真部分仿真波形如下图所示
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/3.png"
alt="仿真波形(一)"
style="zoom:60%"/>
<br> <!--换行-->
图4.仿真波形(一) <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/4.png"
alt="仿真波形(二)"
style="zoom:60%"/>
<br> <!--换行-->
图5.仿真波形(二) <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/5.png"
alt="仿真波形(三)"
style="zoom:60%"/>
<br> <!--换行-->
图6.仿真波形(三) <!--标题-->
</center>
</div>
从仿真波形二和三中我们可以看到当我们模拟按键按下1 ----> 0当按键抖动`btn`在0和1之间来回跳转`cnt`的值会变回1重新开始计数直到按键稳定按下`btn`的值稳定不变为0`cnt`稳定增加,当`cnt`的值增加到`8hFF`时,认为按键按下,`btn_ggle`存储此时的按键状态,同时`btn_flag`检测到下降沿,拉高一个时钟周期。`led`信号也加一。
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/6.png"
alt="仿真波形(四)"
style="zoom:60%"/>
<br> <!--换行-->
图7.仿真波形(四) <!--标题-->
</center>
</div>
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="./images/7.png"
alt="仿真波形(五)"
style="zoom:60%"/>
<br> <!--换行-->
图8.仿真波形(五) <!--标题-->
</center>
</div>
从波形三和四中我们可以看到当模拟按键抬起时0 ----> 1按键的抖动也会使`cnt`重新计数,直到稳定,`cnt`计数到`8hFF`时,更新`btn_ggle`,由于按键是抬起,`btn_flag`不变,`led`不变。
### 2.3.5 上板验证
完成仿真后,可进行上板验证。端口连接如下表所示:
| 端口名称 | 类型 | 管脚 |说明 |
| -------- | ------ | ------ | ---------- |
| clk | Input | | 27MHz 时钟 |
| rstn | Input | | 低电平复位 |
| btn | Input | | 外部按钮 |
| btn_flag | Output | | 上升沿标志 |
| led[0] | Output | | 驱动led |
| led[1] | Output | | 驱动led |
| led[2] | Output | | 驱动led |
| led[3] | Output | | 驱动led |
| led[4] | Output | | 驱动led |
| led[5] | Output | | 驱动led |
| led[6] | Output | | 驱动led |
| led[7] | Output | | 驱动led |
`.sbit`文件上传至平台并下载到实验板多次按下按键观察led灯跳转如果按下1次按键led只跳转一次那么说明达成实验目标。
## 2.4 章末总结
本实验通过一个典型的按键检测例子,介绍了数字系统中常用的消抖和边沿检测方法,掌握了如何利用计数器和触发器组合进行抖动抑制与事件捕捉。在更复杂的设计中,这类基础模块可作为控制逻辑的可靠触发信号源,具有广泛应用价值。