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

实验目标

  • 七段数码管低两位显示当前按键的键码,中间两位显示对应的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. 第一版运行结果遗留多个问题,有的需要引入状态机机制去解决

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

5. 改进了判断逻辑

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值