【一生一芯】数字实验七:状态机及键盘输入

实验目标

  • 七段数码管低两位显示当前按键的键码,中间两位显示对应的ASCII码(转换可以考虑自行设计一个ROM并初始化)。只需完成字符和数字键的输入,不需要实现组合键和小键盘。

  • 当按键松开时,七段数码管的低四位全灭。

  • 七段数码管的高两位显示按键的总次数。按住不放只算一次按键。只考虑顺序按下和放开的情况,不考虑同时按多个键的情况。

实验收获

一开始对实验目标没啥思路,第七个实验难度突然陡增。一开始没能把整体都思考清楚。就想着用分步走的思路,先实现一个功能框架,再实现它的逻辑部分。

第一版,模块设计和功能实现:

  1. 捕获键值模块,输出键值

  2. 统计按键次数模块,基于F0键值进行判断次数

  3. SEG显示模块,

第二版,添加状态机

  1. 捕获键值模块,输出键值和状态

  2. 统计按键次数模块,根据状态信息进行计数

  3. SEG显示模块,根据状态信息进行显示

实验过程

  1. 参考nvboard的demo ,实现键值捕获模块

    module ps2_keyboard_ctl(clk,resetn,ps2_clk,ps2_data,ps2_code,ps2_count);
    input clk,resetn,ps2_clk,ps2_data;
    output [7:0] ps2_code;
    reg [9:0] buffer; // ps2_data bits
    reg [3:0] count; // count ps2_data bits
    reg [2:0] ps2_clk_sync;
    output reg [7:0] ps2_count;

    复制代码
     always @(posedge clk) begin
         ps2_clk_sync <=  {ps2_clk_sync[1:0],ps2_clk};
     end
    
     wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1];
    
     always @(posedge clk) begin
         if (resetn == 0) begin // reset
             count <= 0;
             ps2_count <=0;
         end
         else begin
             if (sampling) begin
               if (count == 4'd10) begin
                 if ((buffer[0] == 0) &&  // start bit
                     (ps2_data)       &&  // stop bit
                     (^buffer[9:1])) begin      // odd  parity
                       $display("receive  %x ", buffer[8:1]);
                     if (buffer[8:1] == 8'hF0) begin
                     ps2_count <= ps2_count + 8'b1;

    // $display("ps2_count %x, ", ps2_count);
    end
    end
    count <= 0; // for next
    end else begin
    buffer[count] <= ps2_data; // store ps2_data
    count <= count + 3'b1;
    end
    end
    end
    end
    assign ps2_code = buffer[8:1]; //always set output data

    endmodule

  2. 实现键值统计模块

    module ps2_keyboard_to_count (
    input wire clk, // 系统时钟
    input wire rst_n, // 复位信号(低有效)
    input wire [7:0] ps2_code, // 8位输入数据
    input wire sampling,
    output wire [7:0] key_count // F0出现的累计次数
    );

    复制代码
     reg [7:0] count  ;  // F0出现的累计次数
     always @(posedge clk) begin
     //always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
             count <= 8'd0;    // 复位时计数器清零
         end else if ( (ps2_code == 8'hF0 ) && sampling ) begin
             count <= count + 1; // 检测到F0且数据有效时计数加1
             $display("ps2code %x,count: %x", ps2_code,count);
         end
     end
    
     assign key_count = count;

    endmodule

  3. 实现SEG显示模块

    module ps2_keyboard_ctl_seg(
    input wire clk, // 时钟输入
    input wire rst, // 复位信号(低电平有效)
    input wire [7:0] data, // 8位数据输入
    input wire [7:0] ascii_code, // 8位数据输入
    input wire [7:0] data_count, // 8位数据输入
    output [7:0] o_seg0,
    output [7:0] o_seg1,
    output [7:0] o_seg2,
    output [7:0] o_seg3,
    output [7:0] o_seg4,
    output [7:0] o_seg5,
    output [7:0] o_seg6,
    output [7:0] o_seg7
    );

    复制代码
     // 七段数码管编码表(共阳极,段顺序:dp,g,f,e,d,c,b,a)
     reg [7:0] seg_table [0:15];
     reg [7:0] seg0, seg1,seg2,seg3,seg4,seg5,seg6,seg7; 
    
     
     // 初始化七段编码表
     initial begin
         seg_table[0]  = 8'b11111100; // 0 → abcdef亮 [1](@ref)
         seg_table[1]  = 8'b01100000; // 1 → bc亮
         seg_table[2]  = 8'b11011010; // 2 → abdeg亮
         seg_table[3]  = 8'b11110010; // 3 → abcdg亮
         seg_table[4]  = 8'b01100110; // 4 → bcfg亮
         seg_table[5]  = 8'b10110110; // 5 → acdfg亮
         seg_table[6]  = 8'b10111110; // 6 → acdefg亮
         seg_table[7]  = 8'b11100000; // 7 → abc亮
         seg_table[8]  = 8'b11111110; // 8 → abcdefg全亮(缺dp)[1,
         seg_table[9]  = 8'b11110110; // 9 → abcdfg亮(缺e段)
         seg_table[10] = 8'b11101110; // A(10) → abcefg亮(缺d段)
         seg_table[11] = 8'b00111110; // B(11) → cdefg亮(缺ab段)
         seg_table[12] = 8'b10011100; // C(12) → adef亮(缺bcg段)
         seg_table[13] = 8'b01111010; // D(13) → bcdeg亮(缺af段)
         seg_table[14] = 8'b11011110; // E(14) → adefg亮(缺bc段)
         seg_table[15] = 8'b11101110; // F(15) → aefg亮(缺bcd段)
    
     end
    
    
    
    
     
     // 仅使用前两位数码管显示8位数据(高4位和低4位)
     always @(posedge clk ) begin
         if (rst) begin
             seg0 <= 8'b11111111; // 复位时关闭所有段
             seg1 <= 8'b11111111; // 复位时关闭所有段
         end else begin
             if (data == 8'b11110000 ) begin 
                seg0 <= 8'b11111111;
                seg1 <= 8'b11111111;
                seg2 <= 8'b11111111;
                seg3 <= 8'b11111111;
                seg6 <= ~ seg_table[data_count[3:0]];
                seg7 <= ~ seg_table[data_count[7:4]];
    
             end else begin
                 
             // 使用低4位和高4位数据来显示
             // 注意:这里假设data是8位二进制数,seg_table的索引范围是0-15
             // 因此data[3:0]和data[7:4]分别对应低4位和高4位
                seg0 <= ~ seg_table[data[3:0]];
                seg1 <= ~ seg_table[data[7:4]];
                seg3 <= ~ seg_table[ascii_code[3:0]];
                seg4 <= ~ seg_table[ascii_code[7:4]];
                seg6 <= ~ seg_table[data_count[3:0]];
                seg7 <= ~ seg_table[data_count[7:4]];
                 end
             end
         end
    
    
      assign   o_seg0 = seg0 ;
      assign   o_seg1 = seg1 ;
      assign   o_seg2 = 8'b11111111; // dp段关闭
      assign   o_seg3 = seg3; 
      assign   o_seg4 = seg4; 
      assign   o_seg5 = 8'b11111111; // dp段关闭     
      assign   o_seg6 = seg6;
      assign   o_seg7 = seg7; 

    endmodule

  4. 第一版运行结果遗留多个问题,有的需要引入状态机机制去解决

  5. 统计按键次数统计就不准。由于只简单的采用了F0作为统计机制,导致了有了多次的冗余统计。

  1. 改进了判断逻辑

但也遗留两个问题,状态机没能实现,键盘收起后要求能灭灯,也还没实现。目前没想出好的办法。刚觉是要同时判断结束码(F0)和键码才行,这样的话就要加入 fifo机制,保存两个状态。

相关推荐
sucool_lb6 天前
《一生一芯》数字实验三:加法器与ALU
一生一芯
sucool_lb7 天前
《一生一芯》数字实验六:实现随机数发生器
一生一芯
农民真快落9 个月前
【IC设计】跨时钟异步处理系列——单比特跨时钟
fpga开发·verilog·ic设计·数字ic设计·一生一芯
农民真快落1 年前
【IC设计】牛客网-序列检测习题总结
fpga开发·verilog·数字ic设计·一生一芯·秋招面试
农民真快落1 年前
【IC设计】任意倍数占空比为50%的奇数分频和偶数分频(Verilog源码、仿真波形、讲解)
fpga开发·riscv·chisel·一生一芯·cpu设计
农民真快落1 年前
【程序人生】研二下学期快结束了~~~~随便写写
fpga开发·riscv·chisel·一生一芯·cpu设计
农民真快落1 年前
【IC设计】Verilog线性序列机点灯案例(四)(小梅哥课程)
fpga开发·verilog·ic设计·数字ic设计·一生一芯
农民真快落1 年前
【异常处理】sbt构建Chisel库时出现extracting structure failed:build status:error的解决办法
scala·ic设计·chisel·noc·一生一芯
农民真快落1 年前
【IC设计】Windows下基于IDEA的Chisel环境安装教程(图文并茂)
scala·ic设计·risc-v·chisel·一生一芯