集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 1729|回复: 6

紫龙Fpga笔记之计算器 (上)

[复制链接]
宇宙第一帅 发表于 2016-8-26 00:28:51 | 显示全部楼层 |阅读模式
      紫龙FPGA笔记之
                计算器 (上)
                                紫龙

一点闲话:  咳咳 ,最近这两周弄得计算器简直要发疯,自己每天做的都能及时弄完,但是到最后一综合,问题百出,好多自己找了很久都找不到,在此感谢寇老师的细心解答哈哈,不然本宝宝今天得熬到很晚了~~~

前言:  首先,当我们要用fpga实现一个简易计算器的时候,我们不妨将手离开键盘,先别急着敲代码,先静下来想一想······一个计算器到底包含哪几个部分呢?   我们平时都用过计算器,回顾一下,当我们拿到计算器
进行运算的时候,我们第一步是用键盘按下自己想要的数,然后按下运算符,再按下等于号  ,他的运算结果将会通过显示屏显示出来,这些都是我们能够用眼睛所能看到的。而看不到的部分,就是我们输入数据的运算
处理了。====================就上面而言,我们所需要的部分,是不是简而言之就是 按键部分 ,显示部分 还有运算部分了~
        FPGA从根本上说,我们只是对01进行编码,让他实现我们所想要的功能。那么如何对01进行编码实现一个简易计算器呢?  嘿嘿~没我说的这么复杂,其实我们懂原理很简单就做出来了~~~

原理图:  这个是我本人手写的,因为实在不会用这个发帖栏编辑这么高端的东西哈哈哈,图我也不知道传到哪一行了,到时候传完看看~~

原理图小结:  由图我们可以更加直观的看到这个计算器模块的框架,显然,计算器的设计 分为三个等级 :第一个等级,也就是最高级,我们叫做计算器总模块,本质上来说,他就是一个top文件,将第二级的分top文件
进行连线例化; 第二个等级,包块三个小模块 :按键驱动模块 ,数码管驱动模块,计算模块。 之所以前面两个模块我称之为驱动,我认为,他们将所对应的按键,数码管等小模块联系起来,让其有条不紊的进行工作,这也算
是一种驱动了吧!  但是计算模块只是一个单独的小模块 ,他不包含小程序 ,我也没把他称为驱动;  第三个等级:也就是各种小模块了 ,分属于二级之下 ,二级相对于三级而言,就是他们的顶层文件,将三级模块进行连线例
化。  按键驱动模块之下的三级模块包括 按键扫描和按键消抖 , 数码管驱动模块之下的三级模块包括 BCD转换,亮灭控制以及显示控制 。

由于计算器这几个模块 一次发帖占用篇幅太长 ,我分三次发吧 ,第一次讲的的是 二级模块中的第一个 :按键模块 。

按键模块: 要实现按键的输入,首先 ,我们得对按键按下这个状态进行采样,这也是我们所说的按键扫描 ,但是 ,由于机械性误差,我们每次按下按键的时候, 他并不是马上就能准确的接上,毕竟我们所采用的按键内部其实就是
一个金属弹片,两个金属弹片按下相撞一起,他们或多或少都会经过一个“缓冲期” ,由开始接触,再到弹开,再接触·····周而复始,一直到最后稳定下来,接触到一起。这就涉及到物理中的机械能守恒了,这里就不多了,不然我可以
和你得得三天三夜哈哈~   话说回来 ,我们对按键工作的分析,很清楚就能知道 要实现一个完整的按键输入,一般而言我们要有三个模块:一个模块对按键进行扫描,一个模块对按键进行消抖,另一个模块对前两个模块进行例化,让他们有
条不紊的工作,充当驱动部分。

按键扫描:我们所用的ZX2开发板上的外设 为4 x 4的矩阵键盘 ,他包括四行 (row)和四列 (col),其中,他的行(col)与fpga相连,我们所能控制的就是列(col)的输入,当行一定,我们对列进行扫描,扫到一个值,由已知的行列相对应
我们便能得到唯一的数值knum,由于数值knum有16个,我们用四个位宽来进行定义。下面直接上程序吧 ,我将对各个步骤进行解说。

module keyscan (
                          input         clk        ,      // 系统时钟
                          input         rst_n      ,    //复位键
                          input         [3:0] row  ,  //输入行
                          output         [3:0]        col  ,  //输出列
                          output reg  [4:0]  knum  //扫描得到的按键码
                                         );
//==========middle_siganal====================
reg         [3:0]        col_r;                                          //输出列电位
wire         [7:0]        scancode;                                   //扫描码, 用八位的,因为我们扫描矩阵键盘,行和列各占用四位
reg         [7:0] scancode_r1,scancode_r2;             //两个寄存器 ,register

//==========寄存扫描码======================
assign col=col_r;                                            //首先 ,我们定义将扫描的列寄存到col_r这个寄存器之中
always @(posedge clk )begin                            //这个模块 ,是将scancode值存到寄存器中两次,这样,咱们的扫描码将慢两拍,这个操作很多程序中都会使用,其意义在于使数据更加稳定,消除亚稳态
                                scancode_r1<=scancode;
                                scancode_r2<=scancode_r1;end
assign scancode={col_r[3:0],row[3:0]};            //定义扫描码由行和列组成
always @(posedge clk or negedge rst_n)           
           if(~rst_n)                           
                                col_r<=4'b0000;              //复位时,我们要扫描的列清0
           else case(col_r)                                     //  否则   我们让列从 4‘b1011 开始扫描,0代表列选中,default语句表明0开始左移。这样可以实现列的扫描
                                        4'b1011:col_r<=4'b0000;
                                        4'b0000:col_r<=4'b0111;
                                        default:col_r<={col_r[2:0],col_r[3]};
                endcase
// =======扫描=================
always @(posedge clk or negedge rst_n)  
                if(~rst_n)
                        knum<=5'd0;
                else case(scancode_r2)               //若我们的列都为0,行再怎么变 ,扫描结果还是0 ,表明未进行列扫描         
                        8'b0000_1111:knum<={1'b0,knum[3:0]};
                        8'b0000_1110:knum<={1'b1,knum[3:0]};
                        8'b0000_1101:knum<={1'b1,knum[3:0]};
                        8'b0000_1011:knum<={1'b1,knum[3:0]};
                        8'b0000_0111:knum<={1'b1,knum[3:0]};
//=====后四位代表行,低电平有效,当行为1时============               
                        8'b1110_1110:knum<=5'h10;              //========knum为扫描结果,扫描的为16进制 ,结果为 0
                        8'b1101_1110:knum<=5'h11;
                        8'b1011_1110:knum<=5'h12;
                        8'b0111_1110:knum<=5'h13;
//==========当行为2时=========================                       
                        8'b1110_1101:knum<=5'h14;
                        8'b1101_1101:knum<=5'h15;
                        8'b1011_1101:knum<=5'h16;
                        8'b0111_1101:knum<=5'h17;
//=========当行为3时==========================                       
                        8'b1110_1011:knum<=5'h18;
                        8'b1101_1011:knum<=5'h19;
                        8'b1011_1011:knum<=5'h1A;
                        8'b0111_1011:knum<=5'h1B;
//=========当行为4时==========================                       
                        8'b1110_0111:knum<=5'h1C;
                        8'b1101_0111:knum<=5'h1D;
                        8'b1011_0111:knum<=5'h1E;
                        8'b0111_0111:knum<=5'h1F;
                        default:knum<=knum;            //default语句意义在于当出现不是事件值得情况,扫描出的值保持不变
                endcase
               
endmodule


按键消抖模块: 我们对按键扫描多次,但是,一次扫描7个结果,我们想想,如果,这个按键并没有抖动,那么这扫描的七个结果是不是相同的,于是我们对这七个结果进行比较,如果相同,让这个结果保存到一个寄存器之中,如果不同,
那么这个采样短周期能采样的结果无效。这样一直判断6次,其中,如果后来数据一样,那么将顶替掉前一个采集结果,当他值不改变时,我们得到的按键值换句话说也就稳定了,没有抖动的效果,那么我们就达到了按键消抖的效果。
程序如下:


module keyshake(
                                        input clk                                        ,
                                        input rst_n                                        ,
                                        input [4:0]knum                                ,                    //由按键扫描模块扫描而来,作为消抖的输入
                                        output reg [3:0]kreanum             ,                    //键盘标志
                                        output reg kreanum_vld                                  //按键使能,这个模块不必看这个,为以后的模块做的准备
                                        );
//========定义状态==========================
parameter S1 = 7'b000_0001 ;
parameter S2 = 7'b000_0010 ;                                       
parameter S3 = 7'b000_0100 ;                                       
parameter S4 = 7'b000_1000 ;                                       
parameter S5 = 7'b001_0000 ;                                       
parameter S6 = 7'b010_0000 ;                                       
parameter S7 = 7'b100_0000 ;                                       
//=======middle_signal========================
reg [5:0]keyf        ;
reg [3:0]knum_r;
reg [6:0]state        ;
//=======扫描按键按下,并将结果给寄存器knum_r=======
always @(posedge clk or negedge rst_n)
                begin
                        if(!rst_n)                // 复位时对寄存器清0
                        knum_r <= 4'd0 ;
                        else case(state)
                        S1:if(knum[4])        //=========knum第五位,代表的是有无按键摁下,有的话值为1,这里为检测到按键摁下
                                        begin
                                state<=S2;                    //跳转到第二个状态
                                knum_r<=knum[3:0];    //  按键值赋给寄存器  
                                        end
                        S2:begin
                                state<=S3;
                                knum_r<=knum[3:0];
                                        end
                        S3:begin
                                state<=S4;
                                knum_r<=knum[3:0];
                                        end
                        S4: begin
                                state<=S5;
                                knum_r<=knum[3:0];
                                        end
                        S5: begin
                                state<=S6;
                                knum_r<=knum[3:0];
                                        end
                        S6: begin
                                state<=S7;
                                knum_r<=knum[3:0];
                                        end
                        S7:state<=S1;            //跳转到状态1  也就是让这扫描赋值过程循环多次
                                default:begin
                                                state<=S1 ;
                                                knum_r<=knum_r;
                                                end
                        endcase
                end
//=========判断是否抖动===================
always @(posedge clk or negedge rst_n)       
                        begin
                                if(!rst_n)
                                        keyf<=6'b000_0000;
                                else case(state)
                                S1:begin
                                        keyf<=6'd0;end
                                S2:begin
                                        if(knum[3:0]==knum_r)                 // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第1位赋值为1
                                        keyf <={5'd0,1'b1};
                                        else
                                        keyf <={5'd0,1'b0};      //否则为0
                                        end
                                S3:begin
                                        if(knum[3:0]==knum_r)                  // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第2位赋值为1
                                        keyf <={4'd0,1'b1,keyf[0]};  
                                        else
                                        keyf <={4'd0,1'b0,keyf[0]};
                                        end
                                S4:begin
                                        if(knum[3:0]==knum_r)                 // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第3位赋值为1
                                        keyf <={3'd0,1'b1,keyf[1:0]};
                                        else
                                        keyf <={3'd0,1'b0,keyf[1:0]};
                                        end
                                S5:begin
                                        if(knum[3:0]==knum_r)                // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第4位赋值为1
                                        keyf <={2'd0,1'b1,keyf[2:0]};
                                        else
                                        keyf <={2'd0,1'b0,keyf[2:0]};
                                        end
                                S6:begin
                                        if(knum[3:0]==knum_r)                // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第5位赋值为1
                                        keyf <={1'd0,1'b1,keyf[3:0]};
                                        else
                                        keyf <={1'd0,1'b0,keyf[3:0]};
                                        end
                                S7:begin
                                        if(knum[3:0]==knum_r)                        // 如果寄存器knum_r的值与输入的按键后三位值一样  那么  我们将寄存器keyf 第6位赋值为1
                                        keyf <={1'b1,keyf[4:0]};
                                        else
                                        keyf <={1'b0,keyf[4:0]};
                                        end
                                endcase
                        end
//============判断寄存器keyf中值是否一样================                                       
always @(posedge clk or negedge rst_n)
                        if(!rst_n)
                                kreanum<=4'd0;
                        else if(keyf[5]& keyf[4] &keyf[3]&keyf[2]&keyf[1]&keyf[0])       //如果其中六个值一样
                                kreanum<=knum_r ;                                                    //我们将结果赋值给输出kreanum
                        else
                                begin
                                kreanum<=kreanum ;                                           // 如果不一样 ,我们让输出值保持原样
                                end
//============今天可以不看=========================
always @(posedge clk or negedge rst_n)
                if(~rst_n)
                                kreanum_vld<=1'b0;
                else if(~knum[4])
                                kreanum_vld<=1'b0;
                else begin
                                if(keyf[5]& keyf[4]& keyf[3]& keyf[2]& keyf[1]& keyf[0])
                                                kreanum_vld<=1'b1;
                                else
                                                kreanum_vld<=kreanum_vld;
                end
endmodule


按键驱动模块:  就是将按键扫描模块和按键消抖模块例化相连,这个就不细说了

module key (
                                input clk                           ,
                                input rst_n                          ,
                                input [3:0]row                  ,
                               
                                output kreanum_vld           ,
                                output [3:0]col                    ,
                                output [3:0]kreanum
                           );
//========middle_signal================                       
reg [31:0]cnt                 ;
reg clk_1khz                  ;
wire [4:0]knum                ;
//=========fre_1000hz==================
always @(posedge clk or negedge rst_n)       
        begin
                if(!rst_n)
                        begin
                        cnt<= 0;
                        clk_1khz<=0;
                        end
                else if (cnt==49999)
                        begin
                        cnt<= 0;
                        clk_1khz<=~clk_1khz;
                        end
                   else
                        cnt<=cnt+1 ;
        end
//========inst======================
keyscan keyscan(
                          . clk                (clk_1khz),
                          .rst_n        (rst_n     ),
                          . row        (row       ),
                          .col                (col        ),
                          .knum        (knum    )
                );               
                       
keyshake keyshake(
                                        .clk                         (clk_1khz                  )        ,
                                        .rst_n                 (rst_n                 )        ,
                                        .knum                 (knum                 )        ,
                                        .kreanum            (kreanum                   )         ,
                                        .kreanum_vld          (kreanum_vld         )   
                                        );               
                                       
endmodule
 楼主| 宇宙第一帅 发表于 2016-8-26 00:31:59 | 显示全部楼层
    这个是前面的图     自己太菜 ,这个编辑发贴不熟练啊啊啊啊   。纯手打 ,弄到现在好累  ,准备休息了 ,明天还要做cpu    = = !
 楼主| 宇宙第一帅 发表于 2016-8-26 00:33:44 | 显示全部楼层
 楼主| 宇宙第一帅 发表于 2016-8-26 00:35:18 | 显示全部楼层
为什么我这一直显示不了图啊 ,,,,心疼 ,,,算了 睡觉
 楼主| 宇宙第一帅 发表于 2016-8-26 09:04:41 | 显示全部楼层
Blondie 发表于 2016-8-26 09:17:26 | 显示全部楼层
                                 加油
zhiweiqiang33 发表于 2016-8-27 09:48:23 | 显示全部楼层
把自己做的每一个实验整理成笔记,这样也方便你结束之后复习,加油,努力;
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2025-5-6 22:55 , Processed in 0.082230 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表