差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 后一修订版 两侧同时换到之后的修订记录 | ||
旋转编码器模块 [2017/06/06 10:34] anran [硬件说明] |
旋转编码器模块 [2017/06/06 11:23] anran [相关资料] |
||
---|---|---|---|
行 7: | 行 7: | ||
------- | ------- | ||
旋转编码器是用来测量转速的装置,因其人性化的操作被用于越来越多的电子设备中,旋转编码器有多种分类: | 旋转编码器是用来测量转速的装置,因其人性化的操作被用于越来越多的电子设备中,旋转编码器有多种分类: | ||
- | \\ | + | |
- | 以编码器工作原理可分为:光电式、磁电式和触点电刷式。 | + | * 以编码器工作原理可分为:光电式、磁电式和触点电刷式。 |
- | \\ | + | * 以码盘刻孔方式不同分为:增量式和绝对式两类。 |
- | 以码盘刻孔方式不同分为:增量式和绝对式两类。 | + | |
- | \\ | + | |
关于以上各类编码器的区别,大家自行查阅资料,这里就不多做介绍了。 | 关于以上各类编码器的区别,大家自行查阅资料,这里就不多做介绍了。 | ||
\\ | \\ | ||
我们[[STEP-BaseBoard]]底板上集成的EC11的旋转编码器就属于增量式触电电刷编码器,其工作原理如下: | 我们[[STEP-BaseBoard]]底板上集成的EC11的旋转编码器就属于增量式触电电刷编码器,其工作原理如下: | ||
\\ | \\ | ||
- | {{:增量式触电点刷编码器.jpg?300 |}} | + | {{ :增量式触电点刷编码器.jpg?400 |}} |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
- | \\ | + | |
\\ | \\ | ||
+ | 如上图所示,当顺时针旋转时A信号提前B信号90度相位,当逆时针旋转时B信号提前A信号90度相位,FPGA接收到旋转编码器的A、B信号时,可以根据A、B的状态组合判定编码器的旋转方向。 | ||
\\ | \\ | ||
+ | 程序设计中我们可以对A、B信号检测,检测A信号的边沿及B信号的状态, | ||
+ | * 当A信号上升沿时B信号为低电平,或当A信号下降沿时B信号为高电平,证明当前编码器为顺时针转动 | ||
+ | * 当A信号上升沿时B信号为高电平,或当A信号下降沿时B信号为低电平,证明当前编码器为逆时针转动 | ||
- | + | 本设计实际电路连接如下: | |
- | + | ||
- | + | ||
- | + | ||
- | 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,使用行线和列线分别连接到按键开关的两端,这样我们就可以通过4根行线和4根列线(共8个I/O口)连接16个按键,而且按键数量越多优势越明显。 | + | |
- | + | ||
- | FPGA驱动矩阵按键模块,首先我们来了解矩阵按键的硬件连接: | + | |
- | \\ | + | |
- | {{ :矩阵按键.jpg?800 |}} | + | |
- | \\ | + | |
- | 上图为4x4矩阵按键的硬件电路图,可以看到4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4),同时列线通过上拉电阻连接到VCC电压(3.3V),对于矩阵按键来讲: | + | |
- | - 4根行线是输入的,是由FPGA控制拉高或拉低, | + | |
- | - 4根列线数输出的,是由4根行线的输入及按键的状态决定,输出给FPGA | + | |
- | 当某一时刻,FPGA控制4根行线分别为ROW1=0、ROW2=1、ROW3=1、ROW4=1时, | + | |
- | * 对于K1、K2、K3、K4按键:按下时对应4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,不按时对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, | + | |
- | * 对于K5~~~K16之间的按键:无论按下与否,对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, | + | |
- | 通过上面的描述:在这一时刻只有K1、K2、K3、K4按键被按下,才会导致4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,否则COL1=1、COL2=1、COL3=1、COL4=1,反之当FPGA检测到列线(COL1、COL2、COL3、COL4)中有低电平信号时,对应的K1、K2、K3、K4按键应该是被按下了。 | + | |
- | + | ||
- | 按照扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测,我们在程序中以这4个时刻对应状态机的4个状态。 | + | |
- | 至于循环的周期,根据我们基础教程里可知,按键抖动的不稳定时间在10ms以内,所以对同一个按键采样的周期大于10ms,这同样取20ms时间。20ms时间对应4个状态,每5分钟进行一次状态转换。 | + | |
- | \\ | + | |
- | {{ :矩阵按键程序框图.jpg?800 |}} | + | |
\\ | \\ | ||
- | {{ :矩阵按键扫描法原理.jpg |}} | + | {{ :旋转编码器硬件连接.jpg?800 |}} |
\\ | \\ | ||
====Verilog代码==== | ====Verilog代码==== | ||
行 65: | 行 34: | ||
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
- | // Module: Array_KeyBoard | + | // Module: Encoder |
// | // | ||
// Author: Step | // Author: Step | ||
// | // | ||
- | // Description: Array_KeyBoard | + | // Description: Driver for rotary encoder |
// | // | ||
// Web: www.stepfapga.com | // Web: www.stepfapga.com | ||
- | // | + | // |
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Code Revision History : | // Code Revision History : | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Version: |Mod. Date: |Changes Made: | // Version: |Mod. Date: |Changes Made: | ||
- | // V1.0 |2015/11/11 |Initial ver | + | // V1.0 |2016/04/20 |Initial ver |
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
- | module Array_KeyBoard # | + | module Encoder |
( | ( | ||
- | parameter NUM_FOR_200HZ = 60000 //定义计数器cnt的计数范围,例化时可更改 | + | input clk_in, //系统时钟 |
- | ) | + | input rst_n_in, //系统复位,低有效 |
- | ( | + | input key_a, //旋转编码器A管脚 |
- | input clk_in, //系统时钟 | + | input key_b, //旋转编码器B管脚 |
- | input rst_n_in, //系统复位,低有效 | + | input key_ok, //旋转编码器D管脚 |
- | input [3:0] col, //矩阵按键列接口 | + | output reg Left_pulse, //左旋转脉冲输出 |
- | output reg [3:0] row, //矩阵按键行接口 | + | output reg Right_pulse, //右旋转脉冲输出 |
- | output reg [15:0] key_out //消抖后的信号 | + | output OK_pulse //按动脉冲输出 |
); | ); | ||
- | /* | + | |
- | 因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态 | + | localparam NUM_500US = 6_000; |
- | 在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样 | + | |
- | 周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间 | + | |
- | 对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理 | + | |
- | */ | + | |
- | localparam STATE0 = 2'b00; | + | |
- | localparam STATE1 = 2'b01; | + | |
- | localparam STATE2 = 2'b10; | + | |
- | localparam STATE3 = 2'b11; | + | |
- | //计数器计数分频实现5ms周期信号clk_200hz | + | reg [12:0] cnt; |
- | reg [15:0] cnt; | + | //计数器周期为500us,控制键值采样频率 |
- | reg clk_200hz; | + | always@(posedge clk_in or negedge rst_n_in) begin |
- | always@(posedge clk_in or negedge rst_n_in) begin | + | if(!rst_n_in) cnt <= 0; |
- | if(!rst_n_in) begin //复位时计数器cnt清零,clk_200hz信号起始电平为低电平 | + | else if(cnt >= NUM_500US-1) cnt <= 1'b0; |
- | cnt <= 16'd0; | + | else cnt <= cnt + 1'b1; |
- | clk_200hz <= 1'b0; | + | end |
- | end else begin | + | |
- | if(cnt >= ((NUM_FOR_200HZ>>1) - 1)) begin //数字逻辑中右移1位相当于除2 | + | reg [5:0] cnt_20ms; |
- | cnt <= 16'd0; | + | reg key_a_r,key_a_r1; |
- | clk_200hz <= ~clk_200hz; //clk_200hz信号取反 | + | reg key_b_r,key_b_r1; |
- | end else begin | + | reg key_ok_r; |
- | cnt <= cnt + 1'b1; | + | |
- | clk_200hz <= clk_200hz; | + | //针对A、B、D管脚分别做简单去抖操作, |
- | end | + | //如果对旋转编码器的要求比较高,建议现对旋转编码器的输出做严格的消抖处理后再来做旋转编码器的驱动 |
+ | //对旋转编码器的输入缓存,消除亚稳态同时延时锁存 | ||
+ | always@(posedge clk_in or negedge rst_n_in) begin | ||
+ | if(!rst_n_in) begin | ||
+ | key_a_r <= 1'b1; | ||
+ | key_a_r1 <= 1'b1; | ||
+ | key_b_r <= 1'b1; | ||
+ | key_b_r1 <= 1'b1; | ||
+ | cnt_20ms <= 1'b1; | ||
+ | key_ok_r <= 1'b1; | ||
+ | end else if(cnt == NUM_500US-1) begin | ||
+ | key_a_r <= key_a; | ||
+ | key_a_r1 <= key_a_r; | ||
+ | key_b_r <= key_b; | ||
+ | key_b_r1 <= key_b_r; | ||
+ | if(cnt_20ms >= 6'd40) begin //对于按键D信号还是采用20ms周期采样的方法,40*500us = 20ms | ||
+ | cnt_20ms <= 6'd0; | ||
+ | key_ok_r <= key_ok; | ||
+ | end else begin | ||
+ | cnt_20ms <= cnt_20ms + 1'b1; | ||
+ | key_ok_r <= key_ok_r; | ||
end | end | ||
end | end | ||
+ | end | ||
+ | |||
+ | reg key_ok_r1; | ||
+ | //对按键D信号进行延时锁存 | ||
+ | always@(posedge clk_in or negedge rst_n_in) begin | ||
+ | if(!rst_n_in) key_ok_r1 <= 1'b1; | ||
+ | else key_ok_r1 <= key_ok_r; | ||
+ | end | ||
+ | |||
+ | wire A_state = key_a_r1 && key_a_r && key_a; //旋转编码器A信号高电平状态检测 | ||
+ | wire B_state = key_b_r1 && key_b_r && key_b; //旋转编码器B信号高电平状态检测 | ||
+ | assign OK_pulse = key_ok_r1 && (!key_ok_r); //旋转编码器D信号下降沿检测 | ||
- | reg [1:0] c_state; | + | reg A_state_reg; |
- | //状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效 | + | //延时锁存 |
- | always@(posedge clk_200hz or negedge rst_n_in) begin | + | always@(posedge clk_in or negedge rst_n_in) begin |
- | if(!rst_n_in) begin | + | if(!rst_n_in) A_state_reg <= 1'b1; |
- | c_state <= STATE0; | + | else A_state_reg <= A_state; |
- | row <= 4'b1110; | + | end |
- | end else begin | + | |
- | case(c_state) | + | //旋转编码器A信号的上升沿和下降沿检测 |
- | STATE0: begin c_state <= STATE1; row <= 4'b1101; end //状态c_state跳转及对应状态下矩阵按键的row输出 | + | wire A_pos = (!A_state_reg) && A_state; |
- | STATE1: begin c_state <= STATE2; row <= 4'b1011; end | + | wire A_neg = A_state_reg && (!A_state); |
- | STATE2: begin c_state <= STATE3; row <= 4'b0111; end | + | |
- | STATE3: begin c_state <= STATE0; row <= 4'b1110; end | + | //通过旋转编码器A信号的边沿和B信号的电平状态的组合判断旋转编码器的操作,并输出对应的脉冲信号 |
- | default:begin c_state <= STATE0; row <= 4'b1110; end | + | always@(posedge clk_in or negedge rst_n_in)begin |
- | endcase | + | if(!rst_n_in)begin |
+ | Right_pulse <= 1'b0; | ||
+ | Left_pulse <= 1'b0; | ||
+ | end else begin | ||
+ | if(A_pos && B_state) Left_pulse <= 1'b1; | ||
+ | else if(A_neg && B_state) Right_pulse <= 1'b1; | ||
+ | else begin | ||
+ | Right_pulse <= 1'b0; | ||
+ | Left_pulse <= 1'b0; | ||
end | end | ||
end | end | ||
+ | end | ||
- | //因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环 | ||
- | always@(negedge clk_200hz or negedge rst_n_in) begin | ||
- | if(!rst_n_in) begin | ||
- | key_out <= 16'hffff; | ||
- | end else begin | ||
- | case(c_state) | ||
- | STATE0:key_out[3:0] <= col; //采集当前状态的列数据赋值给对应的寄存器位 | ||
- | STATE1:key_out[7:4] <= col; | ||
- | STATE2:key_out[11:8] <= col; | ||
- | STATE3:key_out[15:12] <= col; | ||
- | default:key_out <= 16'hffff; | ||
- | endcase | ||
- | end | ||
- | end | ||
- | |||
endmodule | endmodule | ||
行 161: | 行 148: | ||
====小结==== | ====小结==== | ||
------ | ------ | ||
- | 本节主要为大家讲解了矩阵按键的工作原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 | + | 本节主要为大家讲解了旋转编码器的工作原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 |
\\ | \\ | ||
如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 | 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 | ||
行 168: | 行 155: | ||
------ | ------ | ||
\\ | \\ | ||
- | 使用[[STEP-MXO2第二代]]的矩阵按键程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MXO2第二代]]的旋转编码器应用程序: 后续会有下载连接 待更新 |
\\ | \\ | ||
- | 使用[[STEP-MAX10]]的矩阵按键程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MAX10]]的旋转编码器应用程序: 后续会有下载连接 待更新 |
\\ | \\ |