Compare commits
6 Commits
d754a881d7
...
487e7c114a
Author | SHA1 | Date |
---|---|---|
|
487e7c114a | |
|
7f37514dfa | |
|
0b0b4acb17 | |
|
0bd3b42840 | |
|
068576b60b | |
|
7dd5e2189f |
|
@ -13,6 +13,7 @@
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"log-symbols": "^7.0.0",
|
"log-symbols": "^7.0.0",
|
||||||
|
"marked": "^12.0.0",
|
||||||
"mathjs": "^14.4.0",
|
"mathjs": "^14.4.0",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.0.2",
|
||||||
|
@ -3071,6 +3072,18 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "12.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz",
|
||||||
|
"integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mathjs": {
|
"node_modules/mathjs": {
|
||||||
"version": "14.4.0",
|
"version": "14.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.4.0.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"log-symbols": "^7.0.0",
|
"log-symbols": "^7.0.0",
|
||||||
|
"marked": "^12.0.0",
|
||||||
"mathjs": "^14.4.0",
|
"mathjs": "^14.4.0",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.0.2",
|
||||||
|
|
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 2.9 MiB |
After Width: | Height: | Size: 620 KiB |
After Width: | Height: | Size: 635 KiB |
|
@ -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 = 8’b0000_0001,当经过0.5s后,也就是cnt等于13_499_999的时候,第一个led灭,第二个led亮起,也就是led = 8‘b0000_0010。同理,再过0.5s,led = 8’b0000_0100,再过0.5s,led = 8‘b0000_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 章末总结
|
||||||
|
|
||||||
|
本次实验主要学习使用左移(<<)和右移(>>)运算符实现移位,但实际应用中也可以使用位拼接({})进行更加复杂的移位操作,各位同学可以尝试学习。
|
After Width: | Height: | Size: 821 KiB |
After Width: | Height: | Size: 434 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 1.6 MiB |
|
@ -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_flag),led会加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`的值增加到`8‘hFF`时,认为按键按下,`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`计数到`8’hFF`时,更新`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 章末总结
|
||||||
|
|
||||||
|
本实验通过一个典型的按键检测例子,介绍了数字系统中常用的消抖和边沿检测方法,掌握了如何利用计数器和触发器组合进行抖动抑制与事件捕捉。在更复杂的设计中,这类基础模块可作为控制逻辑的可靠触发信号源,具有广泛应用价值。
|
|
@ -100,12 +100,6 @@ try
|
||||||
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "assets")),
|
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "assets")),
|
||||||
RequestPath = "/assets"
|
RequestPath = "/assets"
|
||||||
});
|
});
|
||||||
// Public Files
|
|
||||||
app.UseStaticFiles(new StaticFileOptions
|
|
||||||
{
|
|
||||||
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "EquipmentTemplates")),
|
|
||||||
RequestPath = "/public/EquipmentTemplates"
|
|
||||||
});
|
|
||||||
// Log Files
|
// Log Files
|
||||||
if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "log")))
|
if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "log")))
|
||||||
{
|
{
|
||||||
|
|
|
@ -230,7 +230,7 @@ const availableTemplates = ref([
|
||||||
name: "PG2L100H 基础开发板",
|
name: "PG2L100H 基础开发板",
|
||||||
id: "PG2L100H_Pango100pro",
|
id: "PG2L100H_Pango100pro",
|
||||||
description: "包含主板和两个LED的基本设置",
|
description: "包含主板和两个LED的基本设置",
|
||||||
path: "/public/EquipmentTemplates/PG2L100H_Pango100pro.json",
|
path: "/EquipmentTemplates/PG2L100H_Pango100pro.json",
|
||||||
thumbnailUrl: motherboardSvg,
|
thumbnailUrl: motherboardSvg,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-1 h-full w-full bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
|
<div class="flex-1 h-full w-full bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
|
||||||
@mousedown="handleCanvasMouseDown" @mousedown.middle.prevent="startMiddleDrag" @wheel.prevent="onZoom"
|
@mousedown="handleCanvasMouseDown" @mousedown.middle.prevent="startMiddleDrag" @wheel.prevent="onZoom"
|
||||||
@contextmenu.prevent="handleContextMenu">
|
@contextmenu.prevent="handleContextMenu"> <!-- 工具栏 -->
|
||||||
<!-- 工具栏 -->
|
|
||||||
<div class="absolute top-2 right-2 flex gap-2 z-30">
|
<div class="absolute top-2 right-2 flex gap-2 z-30">
|
||||||
<button class="btn btn-sm btn-primary" @click="openDiagramFileSelector">
|
<button class="btn btn-sm btn-primary" @click="openDiagramFileSelector">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24"
|
||||||
|
@ -27,6 +26,14 @@
|
||||||
</svg>
|
</svg>
|
||||||
添加组件
|
添加组件
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-sm btn-primary" @click="emit('toggle-doc-panel')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
{{ props.showDocPanel ? '属性面板' : '文档' }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 隐藏的文件输入 -->
|
<!-- 隐藏的文件输入 -->
|
||||||
|
@ -156,6 +163,7 @@ const emit = defineEmits([
|
||||||
"component-selected",
|
"component-selected",
|
||||||
"component-moved",
|
"component-moved",
|
||||||
"component-delete",
|
"component-delete",
|
||||||
|
"toggle-doc-panel",
|
||||||
"wire-created",
|
"wire-created",
|
||||||
"wire-deleted",
|
"wire-deleted",
|
||||||
"load-component-module",
|
"load-component-module",
|
||||||
|
@ -165,6 +173,7 @@ const emit = defineEmits([
|
||||||
// 定义组件接受的属性
|
// 定义组件接受的属性
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
componentModules: Record<string, any>;
|
componentModules: Record<string, any>;
|
||||||
|
showDocPanel?: boolean; // 添加属性接收文档面板的显示状态
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// --- 画布状态 ---
|
// --- 画布状态 ---
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderedContent = computed(() => {
|
||||||
|
if (!props.content) return '<p>没有内容</p>';
|
||||||
|
let processedContent = props.content;
|
||||||
|
// 设置 marked 选项
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
marked.setOptions({
|
||||||
|
renderer: renderer,
|
||||||
|
gfm: true,
|
||||||
|
breaks: true
|
||||||
|
});
|
||||||
|
return marked(processedContent);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="markdown-content" v-html="renderedContent"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.markdown-content {
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(img) {
|
||||||
|
max-width: 60%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 1rem auto;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(h1) {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid hsl(var(--b2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(h2) {
|
||||||
|
margin-top: 1.8rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
border-left: 4px solid hsl(var(--p));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(h3) {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(h4),
|
||||||
|
.markdown-content :deep(h5),
|
||||||
|
.markdown-content :deep(h6) {
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(p) {
|
||||||
|
text-indent: 2em;
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: hsl(var(--bc) / 0.8);
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul),
|
||||||
|
.markdown-content :deep(ol) {
|
||||||
|
padding-left: 2em;
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
color: hsl(var(--bc) / 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(li) {
|
||||||
|
margin: 0.4rem 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul ul),
|
||||||
|
.markdown-content :deep(ul ol),
|
||||||
|
.markdown-content :deep(ol ul),
|
||||||
|
.markdown-content :deep(ol ol) {
|
||||||
|
margin: 0.4rem 0 0.4rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul) {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ol) {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul ul) {
|
||||||
|
list-style-type: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul ul ul) {
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(ul li::marker) {
|
||||||
|
color: hsl(var(--p));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(pre) {
|
||||||
|
background-color: hsl(var(--b3));
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid hsl(var(--b2));
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(code) {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
background-color: hsl(var(--b3));
|
||||||
|
padding: 2px 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: hsl(var(--p));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(table) {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin: 1rem 0;
|
||||||
|
background-color: hsl(var(--b1));
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(th),
|
||||||
|
.markdown-content :deep(td) {
|
||||||
|
border: 1px solid hsl(var(--b2));
|
||||||
|
padding: 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(th) {
|
||||||
|
background-color: hsl(var(--b2));
|
||||||
|
font-weight: 500;
|
||||||
|
color: hsl(var(--bc));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(td) {
|
||||||
|
color: hsl(var(--bc) / 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(blockquote) {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-left: 4px solid hsl(var(--p));
|
||||||
|
background-color: hsl(var(--b2));
|
||||||
|
color: hsl(var(--bc) / 0.8);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(hr) {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid hsl(var(--b2));
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(a) {
|
||||||
|
color: hsl(var(--p));
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content :deep(a:hover) {
|
||||||
|
color: hsl(var(--pf));
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,30 +2,63 @@
|
||||||
<div class="h-screen flex flex-col overflow-hidden">
|
<div class="h-screen flex flex-col overflow-hidden">
|
||||||
<div class="flex flex-1 overflow-hidden relative">
|
<div class="flex flex-1 overflow-hidden relative">
|
||||||
<!-- 左侧图形化区域 -->
|
<!-- 左侧图形化区域 -->
|
||||||
<div class="relative bg-base-200 overflow-hidden h-full" :style="{ width: leftPanelWidth + '%' }">
|
<div
|
||||||
<DiagramCanvas ref="diagramCanvas" :componentModules="componentModules"
|
class="relative bg-base-200 overflow-hidden h-full"
|
||||||
@component-selected="handleComponentSelected" @component-moved="handleComponentMoved"
|
:style="{ width: leftPanelWidth + '%' }"
|
||||||
@component-delete="handleComponentDelete" @wire-created="handleWireCreated" @wire-deleted="handleWireDeleted"
|
>
|
||||||
@diagram-updated="handleDiagramUpdated" @open-components="openComponentsMenu"
|
<DiagramCanvas
|
||||||
@load-component-module="handleLoadComponentModule" />
|
ref="diagramCanvas"
|
||||||
|
:componentModules="componentModules"
|
||||||
|
:showDocPanel="showDocPanel"
|
||||||
|
@component-selected="handleComponentSelected"
|
||||||
|
@component-moved="handleComponentMoved"
|
||||||
|
@component-delete="handleComponentDelete"
|
||||||
|
@wire-created="handleWireCreated"
|
||||||
|
@wire-deleted="handleWireDeleted"
|
||||||
|
@diagram-updated="handleDiagramUpdated"
|
||||||
|
@open-components="openComponentsMenu"
|
||||||
|
@load-component-module="handleLoadComponentModule"
|
||||||
|
@toggle-doc-panel="toggleDocPanel"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 拖拽分割线 -->
|
<!-- 拖拽分割线 -->
|
||||||
<div
|
<div
|
||||||
class="resizer bg-base-100 hover:bg-primary hover:opacity-70 active:bg-primary active:opacity-90 transition-colors"
|
class="resizer bg-base-100 hover:bg-primary hover:opacity-70 active:bg-primary active:opacity-90 transition-colors"
|
||||||
@mousedown="startResize"></div>
|
@mousedown="startResize"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- 右侧编辑区域 -->
|
<!-- 右侧编辑区域 -->
|
||||||
<div class="bg-base-200 h-full overflow-hidden flex flex-col" :style="{ width: 100 - leftPanelWidth + '%' }">
|
<div
|
||||||
|
class="bg-base-200 h-full overflow-hidden flex flex-col"
|
||||||
|
:style="{ width: 100 - leftPanelWidth + '%' }"
|
||||||
|
>
|
||||||
<div class="overflow-y-auto flex-1">
|
<div class="overflow-y-auto flex-1">
|
||||||
<PropertyPanel :componentData="selectedComponentData" :componentConfig="selectedComponentConfig"
|
<!-- 使用条件渲染显示不同的面板 -->
|
||||||
@updateProp="updateComponentProp" @updateDirectProp="updateComponentDirectProp" />
|
<PropertyPanel
|
||||||
|
v-if="!showDocPanel"
|
||||||
|
:componentData="selectedComponentData"
|
||||||
|
:componentConfig="selectedComponentConfig"
|
||||||
|
@updateProp="updateComponentProp"
|
||||||
|
@updateDirectProp="updateComponentDirectProp"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="doc-panel overflow-y-auto bg-base-100 rounded-md h-full"
|
||||||
|
>
|
||||||
|
<MarkdownRenderer :content="documentContent" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 元器件选择组件 -->
|
<!-- 元器件选择组件 -->
|
||||||
<ComponentSelector :open="showComponentsMenu" @update:open="showComponentsMenu = $event"
|
<ComponentSelector
|
||||||
@add-component="handleAddComponent" @add-template="handleAddTemplate" @close="showComponentsMenu = false" />
|
:open="showComponentsMenu"
|
||||||
|
@update:open="showComponentsMenu = $event"
|
||||||
|
@add-component="handleAddComponent"
|
||||||
|
@add-template="handleAddTemplate"
|
||||||
|
@close="showComponentsMenu = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -36,6 +69,7 @@ import { ref, computed, onMounted, onUnmounted, shallowRef } from "vue"; // 引
|
||||||
import DiagramCanvas from "@/components/DiagramCanvas.vue";
|
import DiagramCanvas from "@/components/DiagramCanvas.vue";
|
||||||
import ComponentSelector from "@/components/ComponentSelector.vue";
|
import ComponentSelector from "@/components/ComponentSelector.vue";
|
||||||
import PropertyPanel from "@/components/PropertyPanel.vue";
|
import PropertyPanel from "@/components/PropertyPanel.vue";
|
||||||
|
import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
|
||||||
import type { DiagramData, DiagramPart } from "@/components/diagramManager";
|
import type { DiagramData, DiagramPart } from "@/components/diagramManager";
|
||||||
import {
|
import {
|
||||||
type PropertyConfig,
|
type PropertyConfig,
|
||||||
|
@ -44,6 +78,24 @@ import {
|
||||||
generatePropsFromAttrs,
|
generatePropsFromAttrs,
|
||||||
} from "@/components/equipments/componentConfig"; // 引入组件配置工具
|
} from "@/components/equipments/componentConfig"; // 引入组件配置工具
|
||||||
|
|
||||||
|
// --- 文档面板控制 ---
|
||||||
|
const showDocPanel = ref(false);
|
||||||
|
const documentContent = ref("");
|
||||||
|
|
||||||
|
// 切换文档面板和属性面板
|
||||||
|
async function toggleDocPanel() {
|
||||||
|
showDocPanel.value = !showDocPanel.value;
|
||||||
|
|
||||||
|
// 如果切换到文档面板,则获取文档内容
|
||||||
|
if (showDocPanel.value) {
|
||||||
|
const response = await fetch("/doc/01_water_led/water_led.md");
|
||||||
|
documentContent.value = (await response.text()).replace(
|
||||||
|
/.\/images/gi,
|
||||||
|
"/doc/01_water_led/images",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- 元器件管理 ---
|
// --- 元器件管理 ---
|
||||||
const showComponentsMenu = ref(false);
|
const showComponentsMenu = ref(false);
|
||||||
const diagramData = ref<DiagramData>({
|
const diagramData = ref<DiagramData>({
|
||||||
|
@ -751,4 +803,27 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 文档面板样式 */
|
||||||
|
.doc-panel {
|
||||||
|
padding: 1.5rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文档切换按钮样式 */
|
||||||
|
.doc-toggle-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markdown渲染样式调整 */
|
||||||
|
:deep(.markdown-content) {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: hsl(var(--b1));
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|