yngufeng 发表于 2010-8-12 22:53:19

增量式光电编码器计数器程序

似乎坛子里做光电编码器相关项目的兄弟还不少,发个FPGA实现的90度相位差A、B两相脉冲计数的程序上来(Z相被54了).
看了几个兄弟的写法,似乎都有点问题,乱说几点:
1、四倍频是必须的,这个跟编码器的结构有关
2、初始状态可能不是 A=0,B=0,(对于伺服电机尤其如此)
3、适当的隔离和滤波也是必须的
刚开始的时候接手摇编码器,计数器值老是有差。接伺服电机就更夸张,计数都计到天上去了.
纠结了两天才弄成,在伺服电机上和手摇编码器上测了一段时间没发现问题(阿弥陀佛).

FSM实现,个人觉得不是最优的方法,请高手指点.
FSM主状态转换图:
http://cache.amobbs.com/bbs_upload782111/files_32/ourdev_574663.JPG
总共13个状态,全画出来烦死,知道意思就好:) (原文件名:coderFSM.JPG)

源程序:

/*******************************************************************   
** Company                :
** Project Name      :               
** Author            :
** Creation Date       :2010.07                              
** Version Number      :1.0                              
**                                                            
** Description         :                                    
**                                                      
**
**
** Revision History    :                     
**                                                      
** Date          Initials         Modification                     
**
**                                                                     
**********************************************************************/
module coder_counter(
                                clk,        //system clock
                                en,        //enable(low active)
                                load,         //asynchronous load signal(high active)
                                diff_a,         //A phasic pulse input
                                diff_b,        //B phasic pulse input
                                load_data,        //asynchronous load data
                                counter,        //counter data
                                err        //counter error signal(high active)
                                );
/*****************************************************************************
*                      Parameter Declarations                     *
*****************************************************************************/
parameter DATA_WIDTH = 32;

parameter IDLE='d0,ALBL_ADD='d1,ALBL_SUB='d2,ALBL='d3,AHBL_ADD='d4,AHBL_SUB='d5,AHBL='d6,
AHBH_ADD='d7,AHBH_SUB='d8,AHBH='d9,ALBH_ADD='d10,ALBH_SUB='d11,ALBH='d12,ERR='d13;
/*****************************************************************************
*                         Port Declarations                                 *
*****************************************************************************/
input clk,en,load,diff_a,diff_b;
input load_data;
output regcounter;
output reg err;
/*****************************************************************************
*      Internal wires and registers Declarations                         *
*****************************************************************************/
reg state,n_state;
//wire reset_n = ~load;
wire signal = {diff_b,diff_a};
/*****************************************************************************
*                        Finite State Machine                                  *
*****************************************************************************/
always@(posedge clk or posedge load)
begin
        if(load)
        state <= IDLE;
        else
        state <= n_state;
end

always@(en or signal or state)
begin
        case(state)
                IDLE:
                begin
                        if(en == 1)
                        n_state <= IDLE;
                        else
                        begin
                                case(signal)
                                'b00:n_state <= ALBL;
                                'b01:n_state <= AHBL;
                                'b10:n_state <= ALBH;
                                'b11:n_state <= AHBH;
                                endcase
                        end
                end
                ALBL_ADD:n_state <= ALBL;
                ALBL_SUB:n_state <= ALBL;
                ALBL://00
                begin
                        case(signal)
                        'b00:n_state <= ALBL;
                        'b01:n_state <= AHBL_ADD;
                        'b10:n_state <= ALBH_SUB;
                        'b11:n_state <= ERR;
                        endcase
                end
                AHBL_ADD:n_state <= AHBL;
                AHBL_SUB:n_state <= AHBL;
                AHBL://01
                begin
                        case(signal)
                        'b00:n_state <= ALBL_SUB;
                        'b01:n_state <= AHBL;
                        'b10:n_state <= ERR;
                        'b11:n_state <= AHBH_ADD;
                        endcase
                end
                AHBH_ADD:n_state <= AHBH;
                AHBH_SUB:n_state <= AHBH;
                AHBH://11
                begin
                        case(signal)
                        'b00:n_state <= ERR;
                        'b01:n_state <= AHBL_SUB;
                        'b10:n_state <= ALBH_ADD;
                        'b11:n_state <= AHBH;
                        endcase
                end
                ALBH_ADD:n_state <= ALBH;
                ALBH_SUB:n_state <= ALBH;
                ALBH://10
                begin
                        case(signal)
                        'b00:n_state <= ALBL_ADD;
                        'b01:n_state <= ERR;
                        'b10:n_state <= ALBH;
                        'b11:n_state <= AHBH_SUB;
                        endcase
                end
                ERR:n_state <= ERR;
                default:n_state <= IDLE;
        endcase
end

always@(posedge clk)
begin
        case(state)
        IDLE:
        begin
                counter <= load_data;
                err <= 0;
        end
        ALBL_ADD:
        begin
                counter <= counter + 1'b1;
                err <= 1'b0;
        end
        ALBL_SUB:
        begin
                counter <= counter - 1'b1;
                err <= 1'b0;
        end
        ALBL:
        begin
                counter <= counter;
                err <= 1'b0;
        end
        AHBL_ADD:
        begin
                counter <= counter + 1'b1;
                err <= 1'b0;
        end
        AHBL_SUB:
        begin
                counter <= counter - 1'b1;
                err <= 1'b0;
        end
        AHBL:
        begin
                counter <= counter;
                err <= 1'b0;
        end
        AHBH_ADD:
        begin
                counter <= counter + 1'b1;
                err <= 1'b0;
        end
        AHBH_SUB:
        begin
                counter <= counter - 1'b1;
                err <= 1'b0;
        end
        AHBH:
        begin
                counter <= counter;
                err <= 1'b0;
        end
        ALBH_ADD:
        begin
                counter <= counter + 1'b1;
                err <= 1'b0;
        end
        ALBH_SUB:
        begin       
                counter <= counter - 1'b1;
                err <= 1'b0;
        end
        ALBH:
        begin
                counter <= counter;
                err <= 1'b0;
        end
        ERR:
        begin
                counter <= counter;
                err <= 1'b1;
        end
        default:
        begin
                counter <= counter;
                err <= err;
        end
        endcase
end
endmodule

程序文件:
增量式光电编码器计数器verilog程序ourdev_574664.rar(文件大小:1K) (原文件名:coder_counter.rar)

semonpic 发表于 2010-8-12 23:57:08

楼主辛苦了,强烈支持

281229961 发表于 2010-8-13 00:52:18

楼主搞的不错

wanwzy 发表于 2010-8-13 07:24:56

学习了,希望多加点注释,谢谢

millwood0 发表于 2010-8-13 07:38:37

here is my code, using the same thought process, in C.

//determine increment / decrement of the encoder
unsigned char encoder_read(PORT_TYPE port, PORT_TYPE pins) {
const signed char ABs_states[]={0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
static unsigned char encoder_output=0x00;
static unsigned char ABs=0x00; //AB key read out, Previous in the high 2 bits and current in the low two bits;
unsigned char tmp;

ABs <<=2; //left 2 bits now contain the previous AB key read-out;
tmp=IO_GET(port, pins); //read ab pins
if (tmp & KEY_A) ABs |= 0x02; //set the 1st bit if A is high now;
if (tmp & KEY_B) ABs |= 0x01; //set the 0th bit if B is high;
ABs &= 0x0f; //only retain ABs' last 4 bits (A_previous, B_previous, A_current, B_current)
encoder_output += ABs_states;
return encoder_output;
//return ABs;
}

much simpler thanyours.

semonpic 发表于 2010-8-16 14:28:37

ERR:n_state <= ERR;

一旦出现错误,是不是就在ERR状态中打转了。要重新使能才能继续运行吗

yngufeng 发表于 2010-8-17 20:22:42

回复【5楼】semonpic
err:n_state &lt;= err;
一旦出现错误,是不是就在err状态中打转了。要重新使能才能继续运行吗
-----------------------------------------------------------------------

是的,根据编码器的结构分析,正常情况下是不会出现跳转到ERR的条件的,除非信号线上有大的干扰,所以才要对信号进行隔离和滤波的处理.目前实现的是:如果出现ERR状态,相应位会被置位,必须重新用load信号加载.

maxsuntech 发表于 2010-8-19 11:24:16

mark

zlfxia 发表于 2010-8-19 12:12:38

没有这么复杂的,请看附件

点击此处下载 ourdev_576113.rar(文件大小:59K) (原文件名:Quadarture Decoder.rar)

gxy508 发表于 2010-8-19 13:08:01

mark

zyw19987 发表于 2010-10-22 19:50:57

图画的很好啊
mark

fengzheng 发表于 2010-10-27 10:49:24

回复【楼主位】yngufeng
----------------------------------------------------------------------
你好 楼主 我现在在做光电编码器,可以请教下,你的程序的意思吗。我学的是VHDL 可否注释写下啊。13个状态不懂,怎么就13个状态啊,是四个状态啊

SZSBS 发表于 2010-10-27 12:04:37

呵呵,我卖ACTEL的FPGA或CPLD,我见别人做的这东西CPLD这一块还好吧

yngufeng 发表于 2010-10-28 22:16:07

回复【11楼】fengzheng
-----------------------------------------------------------------------

- -||...那么老的贴都被你挖出来了.按你的要求加了中文注释.图上画的状态转换只是示意,还有一些用来加减运算的中间状态没有画出来.
带注释文件:
点击此处下载 ourdev_593594BEPWMO.rar(文件大小:1K) (原文件名:coder_counter.rar)

fengzheng 发表于 2010-10-29 10:46:14

回复【13楼】yngufeng
-----------------------------------------------------------------------
非常感谢楼主,我还有个问题啊,因为我想重改你的这个程序用VHDL ,不知道你说的中间的状态是不是和你画出来的是一样的那,
还有就是你这个实现了 正转和反转的不同计数吗?再次感谢楼主

fengzheng 发表于 2010-10-29 11:52:35

回复【13楼】yngufeng
-----------------------------------------------------------------------

楼主你把你的设计思想大致说下可以不,我看了程序大概明白了,但是signal 好像你在这里是用到了状态

fengzheng 发表于 2010-11-8 15:07:30

回复【楼主位】yngufeng
-----------------------------------------------------------------------
谢谢你楼主啊 ,想问下为什么我仿真出来的值counter始终就记住了一次的光电编码器的个数那。你也出现这种情况吗。请指导:如果可以的话加我QQ576394421万分感激附图
http://cache.amobbs.com/bbs_upload782111/files_34/ourdev_596150OLC8PM.jpg
附图 (原文件名:截图.jpg)

fengzheng 发表于 2010-11-8 15:14:14

回复【17楼】fengzheng
-----------------------------------------------------只能记录一个回复【17楼】fengzheng
-----------------------------------------------------------------------

楼主我搞出来了 谢谢啊

gallop020142 发表于 2010-11-9 12:06:50

mark 增量式编码器

fengzheng 发表于 2010-12-13 09:41:21

回复【楼主位】yngufeng
-----------------------------------------------------------------------
请问楼主,为什么我得出的用singaltap看的那个counter的值都是1那 不改变

ndt2000 发表于 2010-12-13 09:58:37

mark 增量式编码器

arthur2010 发表于 2010-12-13 16:10:02

感谢楼主分享

fengzheng 发表于 2010-12-17 15:39:07

回复【楼主位】yngufeng
-----------------------------------------------------------------------
楼主你这个程序有问题计数不正确,状态机设计的有问题 ,不知道你发现没有,请赐教,为什么计数不对 啊

yngufeng 发表于 2010-12-18 15:56:50

回复【23楼】fengzheng
回复【楼主位】yngufeng
-----------------------------------------------------------------------
楼主你这个程序有问题计数不正确,状态机设计的有问题 ,不知道你发现没有,请赐教,为什么计数不对 啊
-----------------------------------------------------------------------

./emotion/em031.gif 呃~~~没遇到过这种情况,你把状态添加进singaltap里观察下看看,为什么会跳到ERROR状态。

fengzheng 发表于 2010-12-20 20:49:17

回复【24楼】yngufeng
-----------------------------------------------------------------------

不知道为什么计数不对,我用你的程序来计数但是不对,

wwwdege 发表于 2010-12-20 20:59:33

mark

czhxp 发表于 2010-12-20 22:13:03

mark

sanji 发表于 2011-7-14 08:13:11

mark

Steal 发表于 2011-7-15 16:30:53

学习中...

zhangalex88 发表于 2011-7-19 08:30:26

mark 一下,以后学习。

scfor 发表于 2011-7-21 11:03:07

回复【14楼】fengzheng
-----------------------------------------------------------------------

回复【14楼】fengzheng
回复【13楼】yngufeng
-----------------------------------------------------------------------
非常感谢楼主,我还有个问题啊,因为我想重改你的这个程序用vhdl ,不知道你说的中间的状态是不是和你画出来的是一样的那,
还有就是你这个实现了 正转和反转的不同计数吗?再次感谢楼主
-----------------------------------------------------------------------

你用VHDL弄出来了没,能否分享一下,我现在也在弄这个

yanzaihong 发表于 2011-11-25 21:38:57

好资料!正在学习中

yuyanlzh 发表于 2011-11-25 21:44:00

mark

caizijian 发表于 2011-11-25 22:00:02

经常见有人发码盘的,这个有什么诀窍吗?
我以前用C语言的思想写了一个,还是一样的用得好好的啊....

reg cnt11;
reg cnt12;
reg cnt13;
reg cnt14;

/**************************CODER1************************/
always@(posedge coder1a or negedge clear1)//Line a input rising edge
begin
if(clear1==1'b0)cnt11<=0;
else
begin
        if(coder1b==1'b1)cnt11<=cnt11+1;
        else cnt11<=cnt11-1;                               
end
end
//---------------------------------------------
always@(negedge coder1a or negedge clear1)//Line a input rising edge
begin
if(clear1==1'b0)cnt12<=0;
else
begin
        if(coder1b==1'b0)cnt12<=cnt12+1;
        else cnt12<=cnt12-1;       
end       
end
//---------------------------------------------
always@(posedge coder1b or negedge clear1)//Line b input rising edge
begin
if(clear1==1'b0)cnt13<=0;
else
begin
        if(coder1a==1'b0)cnt13<=cnt13+1;
        else cnt13<=cnt13-1;               
end       
end
//---------------------------------------------
always@(negedge coder1b or negedge clear1)//Line b input rising edge
begin
if(clear1==1'b0)cnt14<=0;
else
begin
        if(coder1a==1'b1)cnt14<=cnt14+1;
        else cnt14<=cnt14-1;               
end       
end

assign cnt10= cnt11+cnt12+cnt13+cnt14;

vermon 发表于 2011-11-28 08:16:41

很好啊

89712568 发表于 2011-12-2 15:22:52

有用

Ca_guo 发表于 2011-12-2 16:29:31

学习了,以后用得上。

win100 发表于 2011-12-7 13:55:56

增量式编码器

blueice_net 发表于 2011-12-27 11:05:04

过来学习一下~

MCU678 发表于 2012-2-24 00:41:17

mark

beck_ck 发表于 2012-2-24 02:07:38

回复【楼主位】yngufeng
-----------------------------------------------------------------------

状态怎么13个?看不出?不知道是用来干什么的

hymeng98 发表于 2012-2-29 12:32:19

收藏!!

schlang 发表于 2012-7-21 18:17:36

正在入门学习,收藏了

xzf962 发表于 2012-7-21 19:31:31

我也来MARK一下

whjforever 发表于 2013-1-17 21:38:24

mark学习一下~~~

kenson 发表于 2013-1-17 22:13:56

记一下! 谢谢

steven_ye 发表于 2013-1-22 20:15:36

学习一下,编码器也是计数不对。。

dory_m 发表于 2013-12-11 12:50:58

学习,谢谢!!!

coolamber1 发表于 2014-7-27 21:38:14

不错。。。。。。
页: [1]
查看完整版本: 增量式光电编码器计数器程序