1. 简介
增量式编码器(正交编码器),广泛应用于工业自动化和伺服控制系统的位置检测,主要功能是检测转机的旋转位置和速度,实现精确控制。
一般在检测过程中可能会受到工作环境影响,导致编码器输出的脉冲信号产生毛刺,影响计数,进而影响加工、定位精度。
A、B、Z脉冲的关系:正向旋转A和B存在90°的相位差,反向旋转A和B存在-90°的相位差,Z脉冲为0位置。
2. FPGA实现步骤
通过Verilog语言实现信号的滤波,再对信号进行计数,可以提升信号精度。
步骤如下:
- 先对输入信号,两级打拍消除抖动;
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
positiona_r <= 'd0;
positionb_r <= 'd0;
positionz_r <= 'd0;
end
else begin
positiona_r <= {positiona_r[0], positiona};
positionb_r <= {positionb_r[0], positionb};
positionz_r <= {positionz_r[0], positionz};
end
end
2. 然后通过高频采样,滤除毛刺信号;
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
a_pos <= 'd0;
end
else if(~positiona_r[1] && positiona_r[0])begin // pos
a_pos <= 1'b1;
end
else if(positiona_r[1] && ~positiona_r[0])begin // neg
a_pos <= 1'b0;
end
else begin
a_pos <= a_pos;
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
cnt_a <= 'd0;
end
else if(~positiona_r[1] && positiona_r[0])begin // pos
cnt_a <= 'b0;
end
else if(cnt_a >= CNT_DELAY)begin
cnt_a <= cnt_a ;
end
else if(positiona_r[1])begin
cnt_a <= cnt_a + 1'b1;
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
a_flag <= 'd0;
end
else if((cnt_a >= CNT_DELAY) && (~positiona_r[1]))begin // neg
a_flag <= 'b1;
end
else begin
a_flag <= 'b0;
end
end
3. 通过比较滤波后A、B的高电平位置,确定转动方向;
//对a b的HIGH计数,在同时为高时比较 cnt_a > cnt_b 则顺时钟,反之
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
frount <= 'd1;
end
else if(positiona_r[1] && positionb_r[1] && cnt_a > cnt_b)begin // 顺时针
frount <= 'b1;
end
else if(positiona_r[1] && positionb_r[1] && cnt_a < cnt_b)begin // 逆时针
frount <= 'b0;
end
else begin
frount <= frount;
end
end
- 通过Z信号脉冲确定0位置,对信号计数,确定一周的总脉冲数量;
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
sys_all <= 'd0;
end
else if(z_flag && ~z_flag_r )begin // 复位
sys_all <= 'd0;
end
else if((a_flag && ~a_flag_r) | (b_flag && ~b_flag_r))begin // 复位
sys_all <= sys_all + 1'b1;
end
else begin
sys_all <= sys_all;
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
sys_all_r <= 'd1;
end
else if(z_flag && ~z_flag_r )begin // 寄存
sys_all_r <= (sys_all >> 1);
end
else begin
sys_all_r <= sys_all_r;
end
end
- 根据滤波后A、B、Z的脉冲来进行位置计算和速度计算。
3. 位置计算
[当前角度]° = [相对0位置转过的脉冲数量]° x 360.00° / [转一周的总脉冲数量]°
对360.00° 放大100倍,来提高转动角度的精确计算,即
degree_r = (sys_cnt<<'d5) + (sys_cnt<<'d7) + (sys_cnt<<'d10) + (sys_cnt<<'d11) + (sys_cnt<<'d15);
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
degree_r0 <= 'd0;
end
else if(z_flag && ~z_flag_r)begin // clr
degree_r0 <= 'd0;
end
else if(frount_r)begin
degree_r0 <= (sys_cnta<<'d5) + (sys_cnta<<'d15);
end
else if(~frount_r)begin
degree_r0 <= (sys_cntb<<'d5) + (sys_cntb<<'d15);
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
degree_r1 <= 'd0;
end
else if(z_flag && ~z_flag_r)begin // clr
degree_r1 <= 'd0;
end
else if(frount_r)begin
degree_r1 <= (sys_cnta<<'d7) + (sys_cnta<<'d10) + (sys_cnta<<'d11);
end
else if(~frount_r)begin
degree_r1 <= (sys_cntb<<'d7) + (sys_cntb<<'d10) + (sys_cntb<<'d11);
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
degree_r <= 'd0;
end
else if(z_flag && ~z_flag_r)begin // clr
degree_r <= 'd0;
end
else begin
degree_r <= degree_r0 + degree_r1;
end
end
由于除数是定值,可以通过移位求和,也可以使用IP来除法运算;这里转动一周的总脉冲数量是由转动一周统计出来的,目前不确定总脉冲数量,所以我使用整数除法IP直接得到计算结果。由于,除法一般是DSP处理,所以,可以对除数和被除数先寄存,确保其周期满足除法IP的时序要求。
4. 速度计算
由于速度可能存在加速度,其算法处理过于复杂,且实际场景使用不到,所以博主的速度是单位时间的平均速度,如果转机速度不是匀速,可能计算不准确。
4.1 高速模式
通过计算转动一周用的时间,根据滤波后Z的脉冲上升沿到下一次上升沿,计数转动所用的时间,转速公式:
[当前转速] (r/s) = [一秒的拍数] (拍/s) / [转一周的拍数] (拍/r)
注: 一秒的拍数,即晶振的频率通过FPGA采样后使用的实际频率;
// 速度 (转)r/s 换方向速度清零 奇数圈和偶数圈分开计数,确保实时性
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
frount_r <= 'd0;
end
else begin
frount_r <= frount;
end
end
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
clr_speed <= 'd0;
end
else if((~frount_r && frount) || (~frount && frount_r))begin
clr_speed <= 'd1;
end
else begin
clr_speed <= 'd0;
end
end
// 方式一(高速): 转1周,用的时间 sf_cmd[3:0] == 5(HIGH_SPEED) : 6(LOW_SPEED)
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
one_cycle <= 'd0;
end
else if(clr_speed)begin // clr
one_cycle <= 'd0;
end
else if(z_flag && ~z_flag_r)begin // z pos
one_cycle <= one_cycle + 1'b1;
end
else begin
one_cycle <= one_cycle;
end
end
//one_cycle 拉低计数
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
one_time_n <= 'd1;
end
else if(clr_speed)begin
one_time_n <= 'd1;
end
else if(one_cycle)begin
one_time_n <= 'd1;
end
else begin
one_time_n <= one_time_n + 1'b1;
end
end
//one_cycle 拉高计数
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
one_time_p <= 'd1;
end
else if(clr_speed)begin
one_time_p <= 'd1;
end
else if(~one_cycle)begin
one_time_p <= 'd1;
end
else begin
one_time_p <= one_time_p + 1'b1;
end
end
4.2 低速模式
单位时间转动的周数,这里参考时间为50ms,可根据实际需求调整,转速公式:
[当前转速] (r/s) = ([50msA、B转动的总脉冲数量 ](个/50ms) x 20) (个/s) / [总脉冲数量] (个/r)
//cnt_50ms
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
cnt_50ms <= 'd0;
end
else if(clr_speed)begin // clr
cnt_50ms <= 'd0;
end
else if(cnt_50ms == 'd10_000_000)begin // clr
cnt_50ms <= 'd0;
end
else begin
cnt_50ms <= cnt_50ms + 1'b1;
end
end
//circle
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
circle <= 'd0;
end
else if(clr_speed)begin // clr
circle <= 'd0;
end
else if(cnt_50ms == 'd9_999_999 )begin // 复位
circle <= 'd0;
end
else if((a_flag && ~a_flag_r) | (b_flag && ~b_flag_r))begin //
circle <= circle + 1'b1;
end
else begin
circle <= circle;
end
end
由于转速太低时,转动一周时间太长,使用方式一,速度计算的反馈延时太长;在速度过高时,单位时间采样数量较大,通过计算一周的转动时间稳定性更高,所以转速>=5r/s使用方式一,转速<5r/s使用方式二。
//速度选择, >=5.00r/s:高速 speed_rh, <5.00r/s:低速 speed_rl
always@(posedge clk_200m or negedge rst_n)begin
if(!rst_n)begin
speed_tmp <= 'd0;
end
else if(speed_tmp < 'd500)begin
speed_tmp <= speed_rl ;
end
else begin
speed_tmp <= speed_rh ;
end
end
黄色为速度放大了100倍,即实际为49.99(r/s),紫色为位置角度也放大了100倍,即当前位置为356.40°。
5. Modelsim仿真
最后,测试时可通过将数据每隔500ms寄存一次位置和速度信息,通过串口发送到上位机,观察其准确度。
注:完整工程和仿真程序放资源里面了。