引自强大而免费的微信小程序:皆我百晓生。
以下是使用Verilog语言编写的一个实现上述要求的数电实验的模块示例:
module VisionTest(
input wire clk, //时钟信号
input wire btn0, //启动键
input wire [3:0] btn6to3, //方向键
output wire [7:0] ledMatrix, //8x8点阵
output wire [3:0] segDisp //数码管
);
// 定义常量
reg [7:0] pattern0 = 8'b00000001; //上方向视标图案
reg [7:0] pattern1 = 8'b00000010; //下方向视标图案
reg [7:0] pattern2 = 8'b00000100; //左方向视标图案
reg [7:0] pattern3 = 8'b00001000; //右方向视标图案
reg [7:0] patternX = 8'b11111111; //错误显示图案
reg [3:0] segDispValue; //数码管显示值
reg [2:0] currentPattern; //当前显示的视标图案
reg [3:0] currentLevel; //当前显示的视力级别,默认为0.1
reg [7:0] expressionHappy = 8'b11111001; //高视力时的表情图案
reg [7:0] expressionSad = 8'b00011111; //低视力时的表情图案
reg [7:0] display; //点阵需要显示的图案
wire [7:0] level01 = 8'b00000001; //视力为0.1对应的数码管显示值
wire [7:0] level02 = 8'b00000010; //视力为0.2对应的数码管显示值
wire [7:0] level04 = 8'b00000100; //视力为0.4对应的数码管显示值
wire [7:0] level06 = 8'b00000110; //视力为0.6对应的数码管显示值
wire [7:0] level08 = 8'b00001000; //视力为0.8对应的数码管显示值
wire [7:0] level10 = 8'b00010000; //视力为1.0对应的数码管显示值
wire [7:0] level12 = 8'b00011000; //视力为1.2对应的数码管显示值
wire [7:0] level15 = 8'b00100000; //视力为1.5对应的数码管显示值
reg [3:0] nextLevel; //记录下一个要显示的视力级别
reg [2:0] nextPattern; //记录下一个要显示的视标图案
reg updateMatrix; //标记是否需要更新点阵显示
// 定义状态机
// 初始状态为显示0.8视力的随机方向视标
parameter S_INIT = 2'b00;
parameter S_WAIT_BTN0_RELEASE = 2'b01;
parameter S_WAIT_BTN_PRESS = 2'b10;
reg [1:0] state;
always @(posedge clk) begin
if (btn0 && ~state[1]) state <= S_WAIT_BTN0_RELEASE; //等待启动键释放
else if (state == S_WAIT_BTN0_RELEASE && ~btn0) state <= S_WAIT_BTN_PRESS; //等待启动键按下
else if (state == S_WAIT_BTN_PRESS && btn0) state <= S_INIT; //按下启动键
else state <= state; //其他情况保持当前状态
end
// 逻辑处理
always @(state or btn6to3 or currentPattern or currentLevel or btn0) begin
case (state)
S_INIT: begin
currentLevel <= 8;
nextLevel <= currentLevel;
currentPattern <= 0;
nextPattern <= currentPattern;
segDispValue <= level08; //数码管显示0.8
updateMatrix <= 0; //不需要更新点阵显示
if (btn6to3 == 4'b0001) nextLevel <= 4; //方向键按下"上",切换到较低视力
else if (btn6to3 == 4'b0010) nextLevel <= 12; //方向键按下"下",切换到较高视力
else if (btn6to3 == 4'b0100) nextPattern <= (currentPattern + 1) % 4; //方向键按下"左",切换到前一个视标图案
else if (btn6to3 == 4'b1000) nextPattern <= (currentPattern + 3) % 4; //方向键按下"右",切换到后一个视标图案
end
S_WAIT_BTN0_RELEASE: begin
currentLevel <= currentLevel;
nextLevel <= nextLevel;
currentPattern <= currentPattern;
nextPattern <= nextPattern;
segDispValue <= segDispValue;
updateMatrix <= updateMatrix;
end
S_WAIT_BTN_PRESS: begin
currentLevel <= nextLevel;
nextLevel <= currentLevel;
currentPattern <= nextPattern;
nextPattern <= currentPattern;
segDispValue <= eval(segDispValue, currentLevel, nextLevel);
updateMatrix <= 1; //需要更新点阵显示
if (btn6to3 == 4'b0001) nextLevel <= eval(currentLevel, 4, currentLevel - 4); //方向键按下"上",切换到较低视力
else if (btn6to3 == 4'b0010) nextLevel <= eval(currentLevel, 12, currentLevel + 4); //方向键按下"下",切换到较高视力
else if (btn6to3 == 4'b0100) nextPattern <= (currentPattern + 1) % 4; //方向键按下"左",切换到前一个视标图案
else if (btn6to3 == 4'b1000) nextPattern <= (currentPattern + 3) % 4; //方向键按下"右",切换到后一个视标图案
end
endcase
// 根据视力级别和视标图案设置点阵显示值
case (currentPattern)
2'b00: display = eval(display, pattern0, patternX);
2'b01: display = eval(display, pattern1, patternX);
2'b10: display = eval(display, pattern2, patternX);
2'b11: display = eval(display, pattern3, patternX);
endcase
end
// 逻辑运算函数,用于根据条件进行取值判断
function [7:0] eval;
input [7:0] input, trueValue, falseValue;
input wire condition;
reg [7:0] result;
begin
if (condition) result = trueValue;
else result = falseValue;
eval = result;
end
endfunction
// 时钟分频模块
reg [18:0] count;
reg clk_1hz;
always @(posedge clk) begin
count <= count + 1;
if (count >= 50000000) begin
count <= 0;
clk_1hz <= ~clk_1hz;
end
end
// 1Hz时钟信号驱动的计时器
reg [22:0] timer;
initial timer = 0;
always @(posedge clk_1hz) begin
if (timer < 7) timer <= timer + 1;
else if (updateMatrix) timer <= 0;
end
// 1Hz时钟信号驱动的点阵显示切换
always @(posedge clk_1hz) begin
if (timer == 0) ledMatrix <= display; //显示当前视标图案
else if (timer == 1) ledMatrix <= 8'b00000000; //熄灭点阵显示
else ledMatrix <= ledMatrix; //保持当前点阵显示状态
end
// 数码管显示控制
always @(posedge clk) begin
case (segDispValue)
8'b00000001: segDisp <= level01;
8'b00000010: segDisp <= level02;
8'b00000100: segDisp <= level04;
8'b00000110: segDisp <= level06;
8'b00001000: segDisp <= level08;
8'b00010000: segDisp <= level10;
8'b00011000: segDisp <= level12;
8'b00100000: segDisp <= level15;
endcase
end
// 定义表情图案在测试结束时的显示
always @(state) begin
if (state == S_WAIT_BTN_PRESS && currentLevel == 1'b0)
ledMatrix <= expressionSad; //低视力时显示悲伤表情
else if (state == S_WAIT_BTN0_RELEASE && segDispValue == level15)
ledMatrix <= expressionHappy; //测试结束时显示高视力时的笑脸表情
else
ledMatrix <= ledMatrix;
end
endmodule
在Quartus软件中,将上述代码保存为.v文件后可以进行编译和仿真,生成对应的电路文件。之后,将电路文件下载到MaxⅡ实验板上,连接好输入输出接口,即可进行实验运行。