分享,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
/////////////////////////////////////////////////////////// 咋没人响应呢?
即使说“这个太简单了,早就晓得了”也可以回复下嘛~~
最后实测表面,未加入滤波前,传感器碰到金属机床时会产生误操作,加入之后,即使每秒钟25600个脉冲都没有一个丢步或者干扰了 ~~ 顶下大牛,绝对的经典 我通常采用硬消抖,RC + 施密特触发器 回复【3楼】h2feo4 无机酸
我通常采用硬消抖,rc + 施密特触发器
-----------------------------------------------------------------------
用CPLD编程,其实也是硬件消抖了,我这个设计在综合出来就是增加了一串寄存器 ,还有组合逻辑。
这是单片机所无法比拟的优点~~ FPga CPLD 有点冷清 用到时候研究下。 记下 回复【楼主位】Ian11122840 船儿
-----------------------------------------------------------------------
楼主 看起来的你的程序比较有新意啊,而且比较简便。但可以把完整的程序贴下吗,而且可以注释多一点就好了。
谢谢啦 这方法很有内涵,不错。 应该把你的思想给出来,程序不贴没有什么关系? 回复【8楼】xulinlinux
回复【楼主位】ian11122840 船儿
-----------------------------------------------------------------------
楼主 看起来的你的程序比较有新意啊,而且比较简便。但可以把完整的程序贴下吗,而且可以注释多一点就好了。
谢谢啦
-----------------------------------------------------------------------
这个是给别人开发的东东,源程序就免了吧~~精华思路都已经说清楚了,照着写就差不多了 mark
学习了。。 学习了 CPLD软件消抖,用得着mark一下 好。 恩 ~· 这个是FPGA的吧
cpld的话这样使用太浪费了,
消抖的话还是软件定时查询处理最经典 mark mark一下 帮顶一下愿意分享的坛友! 回复【19楼】AG17 阿甘
mark一下
----------------------------------------------------------------------- 看了一会才懂,呵呵,有用 回复【楼主位】Ian11122840 船儿
-----------------------------------------------------------------------
不用的应用场合,自然有不同的处理方式。20ms延迟消抖对于单片机来说是一种简单实用的方式,但楼主的工业应用,而且还是用的CPLD,自然方式是不一样的。因此实在无法推论出20ms的消抖方式哪里不好了。
对于楼主的应用,没有更具体的信号说明,以及为什么以前的实现不好。只是告诉大家了一种方式,至于为什么好实在没看出来。
对于楼主说的崩溃问题,我理解很可能是亚稳态没处理好。
比如FPGA使用CLK对一个异步的CSN信号进行采样,使用2级寄存器级联,可以处理好亚稳态问题。
但是,我实际遇到的一个情况时,一般情况下,用CLK采样CSN,出现的低电平是5个CLK周期,但是实测有百万分之1的机会,出现了只有4个CLK周期的低电平。 如果没有处理好这一点,那代码还是会出现问题。
虽然信号通过了2级寄存器之后,值已经确定了,不会出现不定态,但究竟是1还是0,完全是随机概率,而且某些小概率很容易让人忽视掉。 感谢楼主的分享,谢谢了… mark先 还看不懂 mark 谢谢楼主无私分享 mark,这样应用层面上有关细节的讨论很重要. 简单明了 这种做法可行,前提是clk的频率远高于输入信号rin! 抖动基本原理就是刚刚接触的时候,有些不稳定状态,这时候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, 这只是一种思路的一种实现方法,还有其他思路和其他实现方法。相同的思路不同方法,比方说使用状态机检测,模拟那个软件消抖的过程。不同的思路可以考虑考虑异步方式进行边缘检测等等,不多说。 mark mark 谢谢 mark mark mark,对按键消抖又有了新的理解。 MARK下 mark! mark学习了 学习了··· 贴一段类似功能的代码,以前项目上用的,应该可以直接用
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; 回复【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; 回复【42楼】qwic
-----------------------------------------------------------------------
解释一下:
CLOCK_FREQUENCY:时钟频率
SAMPLING_RATE:采样频率
SAMPLE_SIZE:采样数据数量
THRESHOLD_LOW:输出为低门限
THRESHOLD_HIGH:integer:输出为高门限
基本思路是按SAMPLING_RATE速率采样,记录SAMPLE_SIZE个最新的采样值,当采样值里面'1'的数量超过高门限时,输出高,'1'的数量低于低门限时,输出低,在两个门限之间时输出保持不变,类似于迟滞比较器的效果。 回复【44楼】qwic
-----------------------------------------------------------------------
也是个好办法~~ 在几行代码很巧妙 mark 还可以用 状态机 检测序列 1111111 000000
时间可以设定
很多IO都是这样消除干扰的 mark! //rin 信号滤波
always @(posedge clk)
begin
Rin_buf <= {Rin_buf,rin};
end
assign rfilter = (Rin_buf == 8'hFF)? 1'b1 : 1'b0; //
不錯,精典 31楼很详细,我们项目上思路也是这样的 很看好cpld,fpga 我也做过一点,学习了 VHDL怎么这么少......
页:
[1]