Ian11122840 发表于 2010-11-19 12:55:30

分享,CPLD消抖动的办法

键盘扫描加消抖的代码,众多的入门书、参考书、教课书上都给出的采用软件延时20ms消抖,连32位系统的教课书上也是这样。如果这样的例子代码做为一个库函数,那它的效率就不要谈了

实际做产品的时候才有体会,我之前设计一个工业产品,来捕捉周期为1us的信号的跳变沿,用EPM3256的CPLD、40M有源时钟

工业环境下,信号线上偶尔会有小小的跳变干扰,这点干扰足以让强逻辑的CPLD系统运算错误直到崩溃!~~

于是,对外接信号做软件滤波,牺牲了一点时钟,但再也没有干扰了。分享给大家看看吧,这就是经验~~

         input rin;

        reg Rin_buf=0;
        trireg rfilter; //filtered signal of R
..............

////////////////////////////////////////////////////////////////////
        //rin 信号滤波
always @(posedge clk)
                begin
                Rin_buf <= {Rin_buf,rin};
                end
        assign rfilter = (Rin_buf == 8'hFF)? 1'b1 : 1'b0; //

always@(negedge rst or posedge rfilter)//每个rin到来时将计数值储存
begin
if(!rst)
    begin
   cnumberrin = 14969;// 0x3A77
    end
else
   cnumberrin = cnumber;
end       

///////////////////////////////////////////////////////////

Ian11122840 发表于 2010-11-19 13:57:56

咋没人响应呢?

即使说“这个太简单了,早就晓得了”也可以回复下嘛~~

最后实测表面,未加入滤波前,传感器碰到金属机床时会产生误操作,加入之后,即使每秒钟25600个脉冲都没有一个丢步或者干扰了 ~~

yclover789 发表于 2010-11-19 14:21:07

顶下大牛,绝对的经典

h2feo4 发表于 2010-11-19 14:53:20

我通常采用硬消抖,RC + 施密特触发器

Ian11122840 发表于 2010-11-19 15:05:27

回复【3楼】h2feo4 无机酸
我通常采用硬消抖,rc + 施密特触发器
-----------------------------------------------------------------------

用CPLD编程,其实也是硬件消抖了,我这个设计在综合出来就是增加了一串寄存器 ,还有组合逻辑。

这是单片机所无法比拟的优点~~

jeffwei 发表于 2010-11-19 15:41:03

FPga CPLD 有点冷清

Adrian 发表于 2010-11-19 15:48:41

用到时候研究下。

ndt2000 发表于 2010-11-19 16:06:36

记下

xulinlinux 发表于 2010-11-21 13:58:57

回复【楼主位】Ian11122840 船儿
-----------------------------------------------------------------------

楼主 看起来的你的程序比较有新意啊,而且比较简便。但可以把完整的程序贴下吗,而且可以注释多一点就好了。
谢谢啦

semonpic 发表于 2010-11-22 20:57:25

这方法很有内涵,不错。

tianya342201 发表于 2010-11-23 08:11:33

应该把你的思想给出来,程序不贴没有什么关系?

Ian11122840 发表于 2010-11-23 08:36:34

回复【8楼】xulinlinux
回复【楼主位】ian11122840 船儿
-----------------------------------------------------------------------
楼主 看起来的你的程序比较有新意啊,而且比较简便。但可以把完整的程序贴下吗,而且可以注释多一点就好了。
谢谢啦
-----------------------------------------------------------------------

这个是给别人开发的东东,源程序就免了吧~~精华思路都已经说清楚了,照着写就差不多了

sytu_xww 发表于 2010-11-23 10:46:52

mark
学习了。。

guke 发表于 2010-11-23 11:14:02

学习了

zhaoghsea 发表于 2010-11-23 11:29:48

CPLD软件消抖,用得着mark一下

jianchangd 发表于 2010-11-23 13:05:56

好。

power_peng 发表于 2010-12-1 13:41:57

恩 ~·

xiaoking 发表于 2010-12-1 14:55:23

这个是FPGA的吧
cpld的话这样使用太浪费了,

消抖的话还是软件定时查询处理最经典

yuzr 发表于 2010-12-1 17:04:09

mark

AG17 发表于 2010-12-1 17:18:17

mark一下

cooleaf 发表于 2010-12-1 17:32:36

帮顶一下愿意分享的坛友!

ljt8015 发表于 2010-12-1 17:41:51

回复【19楼】AG17 阿甘
mark一下
-----------------------------------------------------------------------

fhh20061362 发表于 2010-12-8 15:54:15

看了一会才懂,呵呵,有用

ishock 发表于 2010-12-17 14:44:27

回复【楼主位】Ian11122840 船儿
-----------------------------------------------------------------------

不用的应用场合,自然有不同的处理方式。20ms延迟消抖对于单片机来说是一种简单实用的方式,但楼主的工业应用,而且还是用的CPLD,自然方式是不一样的。因此实在无法推论出20ms的消抖方式哪里不好了。

对于楼主的应用,没有更具体的信号说明,以及为什么以前的实现不好。只是告诉大家了一种方式,至于为什么好实在没看出来。

对于楼主说的崩溃问题,我理解很可能是亚稳态没处理好。

比如FPGA使用CLK对一个异步的CSN信号进行采样,使用2级寄存器级联,可以处理好亚稳态问题。
但是,我实际遇到的一个情况时,一般情况下,用CLK采样CSN,出现的低电平是5个CLK周期,但是实测有百万分之1的机会,出现了只有4个CLK周期的低电平。 如果没有处理好这一点,那代码还是会出现问题。

虽然信号通过了2级寄存器之后,值已经确定了,不会出现不定态,但究竟是1还是0,完全是随机概率,而且某些小概率很容易让人忽视掉。

redbat_228 发表于 2010-12-17 14:48:16

感谢楼主的分享,谢谢了…

morion 发表于 2010-12-17 15:57:45

mark先 还看不懂

yuzr 发表于 2010-12-17 16:44:19

mark

sangreal 发表于 2010-12-17 17:13:56

谢谢楼主无私分享

HYFAVR 发表于 2010-12-18 22:31:28

mark,这样应用层面上有关细节的讨论很重要.

fy024 发表于 2010-12-18 22:46:42

简单明了

minfudianzi 发表于 2010-12-19 12:10:34

这种做法可行,前提是clk的频率远高于输入信号rin!

mcupro 发表于 2010-12-20 00:56:36

抖动基本原理就是刚刚接触的时候,有些不稳定状态,这时候01跳变,大家和我顺序思考以下几个问题,并最后写出代码,代码没有实际编译,要进行编译的时候可能有点小小笔误要修改,简单一改就行。
1, 正如楼上所说,不一样的场合有不一样的抖动时间,咱们就以最最常见的按键为例子,使用典型的20MS消除抖动时间,一下讨论假设正电平为按键按下。
2, 假设我们用一个大约20MS的周期,每周期采集一次,按键按下我们假设至少停留100MS,采集三次以上都是采集到正电平那就说明这是真的按下了对吧。因为就是之前的波动被采集成0,那之后的连续三次已经证明按键至少按下了60MS。
3, 那我们用什么方式实现找出连续的三次的高电平呢,也就是说连续的1。 我们可以使用三个寄存器连成移位寄器的形式,也就好像那个74HC595芯片。每间隔20MS就实现一个类似逻辑左(右)移的操作,将新采集到的电平数放在最左(右)位,将以前最右(左)位的数值抛弃,这样当三个数值都是1的时候,我们可以断定是按键有意识的按下。
4, 有了3的思考,我们再假设系统有一个20MS的时钟信号(20MS的周期这么慢的频率也算不上时钟,暂且这么说吧),我们就可以得到以下代码,用VERILOG2001语法,因为简洁:
module key_detect1(input clk_20ms,key_in,output pushed);//clk_20ms为50HZ
reg shift_reg ;
always @ (posedge clk_20ms)shift_reg <={key_in,shift_reg};//新近来的按键值放在最左边位置。
assign pushed = shift_reg == 3'b111 ;//连续到连续三次数值为1,则断定按键是按下。
endmodule
5,实际上我们很难看到有周期为20MS的时钟,因此上面代码直接用的场合可能受限制(别告诉我直接用交流电产生50HZ信号,虽然那很准确并且早年的数字钟就是这么做的,呵呵)。假设我们使用的时钟是10MHZ,这个数量级是比较常见的,那以上代码要如何修改呢。保持基本原理不变,我们只要保证20MS采集一次数据,并进行一次移位就行。我们可以设置定时器,当定时器数数到(10*1000)/(1/0.020) = 200,000的时候,采集进行一次采集和移位。于是有了一下代码:
module key_detect2(input clk_10mhz,key_in,output pushed ) ;//clk 用10MHZ
reg shift_reg ;
reg cntr ;//一个能数到200,000的寄存器,简单这样得到:200,1000 <=256*1024 =2的(8+10)次方,所以18位足够。
always @ (posedge clk)if (cntr==200*1000)cntr<=0;else cntr<=cntr+1;
always @ (posedge clk)if (cntr==200*1000)shift_reg <={key_in,shift_reg};//只有在计数器OVERFLOW时候采集移位,达到20MS采集一次的效果。
assign pushed = shift_reg == 3'b111 ;//连续到连续三次数值为1,则断定按键是按下。
endmodule

6,实现这种周期差异很大的情况的采集,用到了大约20个BIT的寄存器,这在FPGA里面是微小不足道的,在CPLD里应该也应该不算多。如果你要用这段代码做实验的话,建议你尝试修改下 regcntr 将这句里面的17换成30,40,之类大于17的数字,你会得到和17一样的资源使用量,因为综合器算出了你就从0数到200,000只给你安排一定位数的寄存器,防止了你的浪费。

7, 刚才是分析原理,一步一步得到了代码,这个代码可以修改完善,将进来的KEY_IN 延迟两个拍子,就能有效防止搂上说的所谓亚稳态(输入信号同一时刻达到片内的不同区域数值不同叫做亚稳态,主要原因是时钟建立保持时间不满足)。另外为了输出信号pushed 也延迟一个周期出去比较好。
8, 另外从节约逻辑角度出发审查这个代码,我们发现(cntr==200*1000)这是一个大大的比较器,肯定用不少逻辑,可以将200,000的代码直接等于256*1024 这样定时器溢出时间就大于了20MS一点,也是没有关系的。但是我们做的代码就不一样了,使用的FPGA/CPLD资源也会有所减少。
9,综合考虑到7,8点最后得到如下代码:
module key_detect3(input clk,key_in,output reg pushed ) ;//clk用10MHZ
reg key_inr ,key_inrr ;
reg shift_reg ;
reg cntr ; //这里寄存器位数直接相关溢出时间!
always @ (posedge clk)begin key_inr<=key_in;key_inrr<=key_inr ;end // “<=” 不要写成 “=”
always @ (posedge clk)cntr<=cntr+1;//自由加吧,到达256*1024-1的时候下一拍自动归0,因为只有18个BIT
always @ (posedge clk)if (~|cntr)shift_reg <={key_inrr,shift_reg};//(~|cntr) 等效于(cntr==0)
always @ (posedge clk) pushed <= shift_reg == 3'b111 ;//指示此时键已经按下。
endmodule
10, 这只是一种思路的一种实现方法,还有其他思路和其他实现方法。相同的思路不同方法,比方说使用状态机检测,模拟那个软件消抖的过程。不同的思路可以考虑考虑异步方式进行边缘检测等等,不多说。

wxx116zh 发表于 2010-12-20 08:29:34

mark

geniusjia 发表于 2010-12-20 09:52:59

mark

2006lc 发表于 2010-12-20 11:13:53

谢谢

pygh 发表于 2010-12-20 11:43:00

mark

nightmara 发表于 2010-12-20 15:33:55

mark

mogong 发表于 2011-6-7 17:27:00

mark,对按键消抖又有了新的理解。

beiguoqishi 发表于 2011-6-8 00:39:17

MARK下

fwt11 发表于 2011-6-19 11:49:23

mark!

whjforever 发表于 2011-6-21 19:26:39

mark学习了

jielove2003 发表于 2011-6-21 21:17:10

学习了···

qwic 发表于 2011-6-24 19:33:55

贴一段类似功能的代码,以前项目上用的,应该可以直接用

entity syn_filter is
        generic(
                CLOCK_FREQUENCY:integer;
                SAMPLING_RATE:integer;
                SAMPLE_SIZE:integer:=16;
                THRESHOLD_LOW:integer:=3;
                THRESHOLD_HIGH:integer:=13
        );
        port(
                clk:in std_logic;
                reset:in std_logic:='0';
                sync_in:in std_logic;
                sync_out:out std_logic
        );
end syn_filter;

architecture behavior of syn_filter is
begin
        process(clk)
        variable sample_shift:std_logic_vector(SAMPLE_SIZE-1 downto 0);
        variable count_clkdiv:std_logic_vector(log2(CLOCK_FREQUENCY/SAMPLING_RATE)-1 downto 0);
        variable count_filter:std_logic_vector(log2(SAMPLE_SIZE+1)-1 downto 0);
        begin
                if(rising_edge(clk))then
                        if(reset='1')then
                                if(sync_in='1')then
                                        sync_out<='1';
                                        sample_shift:=(others=>'1');
                                        count_filter:=conv_std_logic_vector(SAMPLE_SIZE,log2(SAMPLE_SIZE+1));
                                else
                                        sync_out<='0';
                                        sample_shift:=(others=>'0');
                                        count_filter:=conv_std_logic_vector(0,log2(SAMPLE_SIZE+1));
                                end if;       
                                count_clkdiv:=conv_std_logic_vector(CLOCK_FREQUENCY/SAMPLING_RATE-1,log2(CLOCK_FREQUENCY/SAMPLING_RATE));
                        else
                                if(count_clkdiv=conv_std_logic_vector(0,log2(CLOCK_FREQUENCY/SAMPLING_RATE)))then
                                        if(sample_shift(SAMPLE_SIZE-1)='0' and sync_in='1')then
                                                count_filter:=count_filter+1;
                                        elsif(sample_shift(SAMPLE_SIZE-1)='1' and sync_in='0')then
                                                count_filter:=count_filter-1;
                                        end if;
                                        if(count_filter>=conv_std_logic_vector(THRESHOLD_HIGH,log2(SAMPLE_SIZE+1)))then
                                                sync_out<='1';
                                        elsif(count_filter<=conv_std_logic_vector(THRESHOLD_LOW,log2(SAMPLE_SIZE+1)))then
                                                sync_out<='0';
                                        end if;
                                        sample_shift:=sample_shift(SAMPLE_SIZE-2 downto 0)&sync_in;
                                        count_clkdiv:=conv_std_logic_vector(CLOCK_FREQUENCY/SAMPLING_RATE-1,log2(CLOCK_FREQUENCY/SAMPLING_RATE));
                                else
                                        count_clkdiv:=count_clkdiv-1;
                                end if;
                        end if;
                end if;
        end process;
end behavior;

qwic 发表于 2011-6-24 19:34:59

回复【42楼】qwic
-----------------------------------------------------------------------
漏了一个自定义的函数,补上

        function log2(n:natural) return natural is
        begin
                for i in 1 to n loop
                        if(2**i>=n)then
                                return i;
                        end if;
                end loop;
                return n;
        end log2;

qwic 发表于 2011-6-24 19:44:19

回复【42楼】qwic
-----------------------------------------------------------------------

解释一下:
CLOCK_FREQUENCY:时钟频率
SAMPLING_RATE:采样频率
SAMPLE_SIZE:采样数据数量
THRESHOLD_LOW:输出为低门限
THRESHOLD_HIGH:integer:输出为高门限
基本思路是按SAMPLING_RATE速率采样,记录SAMPLE_SIZE个最新的采样值,当采样值里面'1'的数量超过高门限时,输出高,'1'的数量低于低门限时,输出低,在两个门限之间时输出保持不变,类似于迟滞比较器的效果。

Ian11122840 发表于 2011-6-25 20:21:25

回复【44楼】qwic
-----------------------------------------------------------------------

也是个好办法~~

kofkyok 发表于 2011-9-13 20:12:23

在几行代码很巧妙

rkfch 发表于 2011-9-13 22:10:52

mark

gongxd 发表于 2011-9-14 09:20:05

还可以用 状态机 检测序列 1111111 000000
时间可以设定

很多IO都是这样消除干扰的

liwboy 发表于 2011-9-14 10:17:10

mark!

yuyu87 发表于 2011-9-15 09:59:54

//rin 信号滤波
always @(posedge clk)
begin
Rin_buf <= {Rin_buf,rin};
end
assign rfilter = (Rin_buf == 8'hFF)? 1'b1 : 1'b0; //


不錯,精典

luok 发表于 2011-9-15 10:52:45

31楼很详细,我们项目上思路也是这样的

stoppeddream 发表于 2011-9-19 09:06:03

很看好cpld,fpga

zwm279 发表于 2011-9-20 10:41:29

我也做过一点,学习了

tianpengyu 发表于 2011-9-24 21:39:03

VHDL怎么这么少......
页: [1]
查看完整版本: 分享,CPLD消抖动的办法