FPGA驱动旋转编码器(Verilog)

简述

遇到问题:不知道怎么使用旋转编码器;不知道判断左旋右旋;编码器硬件消抖后还是抖动、乱跳,不符合编程预想结果。
编程思路:通过信号打拍进行信号跳变检测;当一个端口的跳变时,判断另一个端口的电平状态,来判断是左旋还是右旋;通过计数器计数来消除编码器的抖动。

正文

最近学习FPGA的时候有用到旋转编码器,学习过程也遇到许多的困难,也有看许多关于旋转编码器的文章,不过许多的文章都是关于C语言的,关于Verilog的非常少,因此准备写一篇FPGA驱动旋转编码器的文章。代码亲测可用。

遇到的问题

1、如何判断左旋右旋;
2、编码器加了硬件消抖后仍然有抖动、乱跳。

编码器介绍

目前我所遇到的编码器有两种,一种是拧动一下,端口电平跳变一次;另一种是拧动一下,端口产生一个高电平脉冲。不过两种编码器都有共同特点,左旋旋钮左端口电平先跳变,右旋旋钮右端口先跳变。(图片是自己画的有点简陋,凑合一下吧)
第一种编码器

第二种编码器

硬件电路

这里附上我的硬件电路图 

 代码编写

思路:
通过打拍,再进行逻辑判断,检测信号的上升沿和下降沿。
一个端口跳变的时候,判断另一个端口的电平。(如左端口出现上升沿时右端口是低电平代表左拧一次,以此类推。)
通过设置计数器计数1ms用于消除信号抖动。(计数时长根据自己需求修改)

代码:(亲测可用)

module  rotary_encoder
(
    input   wire            sys_clk     ,//时钟信号
    input   wire            sys_rst_n   ,//复位信号
    
    input   wire            left_io     ,//左/A端口
    input   wire            right_io    ,//右/B端口
//    input   wire            button_io ,
    
    output  reg             left_flag   ,//左旋单脉冲信号
    output  reg             right_flag   //右旋单脉冲信号
//    output  reg             button_flag 

);

parameter   CNT_MAX = 16'd49_999;//计数1ms

//用于打拍
reg     left_io1 ;
reg     right_io1;

//计数延迟消抖
reg     [15:0]  cnt_left ;
reg     [15:0]  cnt_right;

//稳定的电平信号
reg     left_deb ;
reg     right_deb;

//用于打拍
reg     left_deb1 ;
reg     right_deb1;

//各端口上升、下降沿信号
wire    left_pose ;
wire    right_pose;
wire    left_nege ;
wire    right_nege;


//左端口计数消抖
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_left    <= 16'd0;
    else    if(left_io != left_io1)
        cnt_left    <= 16'd0;
    else    if(cnt_left == CNT_MAX)
        cnt_left    <= CNT_MAX;
    else
        cnt_left    <= cnt_left + 16'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        left_deb    <= 1'b0;
    else    if(cnt_left == (CNT_MAX - 16'd1))
        left_deb    <= left_io1;
    else
        left_deb    <= left_deb;


//右端口计数消抖
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_right    <= 16'd0;
    else    if(right_io != right_io1)
        cnt_right    <= 16'd0;
    else    if(cnt_right == CNT_MAX)
        cnt_right    <= CNT_MAX;
    else
        cnt_right    <= cnt_right + 16'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        right_deb    <= 1'b0;
    else    if(cnt_right == (CNT_MAX - 16'd1))
        right_deb    <= right_io1;
    else
        right_deb    <= right_deb;


//打拍
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
        left_io1  <= 1'b0;
        right_io1 <= 1'b0;
        left_deb1  <= 1'b0;
        right_deb1 <= 1'b0;
        end
    else
        begin
        left_io1  <= left_io  ;
        right_io1 <= right_io ;
        left_deb1  <= left_deb ;
        right_deb1 <= right_deb;
        end


//检测跳变
assign  left_pose  = left_deb & (!left_deb1);
assign  right_pose = right_deb & (!right_deb1);
assign  left_nege  = left_deb1 & (!left_deb);
assign  right_nege = right_deb1 & (!right_deb);



// 左端口上升沿时,右端口低电平||左端口下降沿时,右端口高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        left_flag  <= 1'b0;
    else    if((left_pose==1'b1) && (right_deb==1'b0))
        left_flag  <= 1'b1;
    else    if((left_nege==1'b1) && (right_deb==1'b1))
        left_flag  <= 1'b1;
    else
        left_flag  <= 1'b0;

// 右端口上升沿时,左端口低电平||右端口下降沿时,左端口高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        right_flag  <= 1'b0;
    else    if((right_pose==1'b1) && (left_deb==1'b0))
        right_flag  <= 1'b1;
    else    if((right_nege==1'b1) && (left_deb==1'b1))
        right_flag  <= 1'b1;
    else
        right_flag  <= 1'b0;

endmodule

我使用的是第一种旋转编码器,最后的判断能实现每拧一次,获得一个对应信号脉冲,当然也可以根据自己的需求拆解开。
注意!如果使用的是第二种旋转编码器,最后的判断会输出两个对应信号脉冲,需要自己拆开。
这段代码已实现左旋和右旋的判断,还缺少按下的判断,如有需求,可自行仿照左旋和右旋的代码逻辑编写。

希望这篇文章能够帮助到你,如有不足欢迎各位大佬指正。

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值