一. 简介
想要知道直流电机的转速,就需要用到编码器,常用的编码器有霍尔和光电两种,但是光电编码器比较贵(性能好于霍尔),所以平常的时候使用最多的是霍尔编码器了。
霍尔编码器一般有AB两相信号输出,默认的时候为低电平,当电机转动的时候,AB两相会输出具有一定相位差的脉冲,根据相位差可以确定电机旋转的方向,另外根据单位时间内,脉冲的数量可以确定电机的转速(线数X(编码器参数):电机旋转一圈产生的脉冲数,减速电机有一个减速比Y(Y越大,扭矩越大,负载转速越小),也就是说负载(一般为轮胎)旋转一圈所产生的脉冲数为: X * Y)。AB输出的脉冲波形如下
编码器的使用方法如上,非常简单,下面就用FPGA来写一个编码器模块吧!
二. 编码器模块架构
首先,需要明确一下,编码器模块需要实现那些功能,经过简单的思考,可以确定要实现的功能如下,其中加速度不是必须的。
功能确定了之后,就可以简单设计一下框图如下,根据框图可以很清晰来编写各个模块了。
三.定时器模块
定时器模块的功能非常简单。就是每隔一段时间产生一个触发信号,用来清除脉冲计数 和 计算一次速度与加速度。我这里是设置的5ms触发一次,具体多少时间触发一次合适,可以根据实际的测试效果而定。
四. 编码器脉冲检测模块
该模块的主要功能是用来检测AB两相的脉冲并且判断脉冲的方向。一共有两种方法来检测:
- 单边沿检测 : 检测AB某一相的A沿,然后判断另外一相的电平。例如检测A相的上升沿为有效脉冲,然后判断B相的电平为电机旋转方向。该方法非常简单,但是抗干扰能力并没有那么强,容易误判。
- 四边沿检测 : 依次检测每一个边沿,如果四个边沿均满足要求,则该脉冲为有效脉冲,否则为无效脉冲。该方法抗干扰能力比较强,但逻辑上稍微有点点复杂相较于第一种方法来说。
两种方法的实现,对于FPGA来说,不是什么难事,第一种就不提了;第二种方法的话,一个状态机就可以了,在对应的状态里面检测一相的边沿信号和另外一相的电平,两相都满足的话,就跳到下一个状态,四个状态均满足的话,则输出一个有效电平,否则的话,均回到初始状态。本次使用的是一段式状态机,代码片段如下(两个状态)。
always@(posedge sys_clk_i or negedge sys_rst_n_i) beginif( sys_rst_n_i == 1'b0 ) beginstep <= 6'b00_0000;encoder_AB_detect_dir_reg <= 1'b0;endelse begincase (step)6'b00_0000: begin //处理第一个边沿if( (encoder_A_posedge | encoder_B_posedge) && encoder_AB_detect_en_i == 1'b1) step <= 6'b00_0001;elsestep <= step;end6'b00_0001: begin //处理第二个边沿if( encoder_A_posedge == 1'b1)if( encoder_B_d0 == 1'b1) beginstep <= 6'b00_0100;encoder_AB_detect_dir_reg <= 1'b0; //在第二个边沿的时候,确定旋转方向endelsestep <= 6'b00_0000;else if( encoder_B_posedge == 1'b1)if( encoder_A_d0 == 1'b1) beginstep <= 6'b00_0010;encoder_AB_detect_dir_reg <= 1'b1; //在第二个边沿的时候,确定旋转方向endelsestep <= 6'b00_0000;else if( encoder_A_negedge | encoder_B_negedge) //检测到下降沿 表示错误 step <= 6'b00_0000;else step <= step; //需进行超时处理end
然后模块的信号如下,
module encoder_AB_detect(input sys_clk_i ,input sys_rst_n_i ,input encoder_AB_detect_en_i , //编码器检测使能output encoder_AB_detect_valid_o, //编码器检测有效输出output encoder_AB_detect_dir_o , //编码器检测方向输出//硬件编码器输入input motor_encoder_A_i , //编码器A相输入input motor_encoder_B_i //编码器B相输入
);
五. 脉冲计数模块
计算脉冲数,由于电机在朝一个方向转动的时候,可能会由于不确定因数的影响,产生另外一个方向的脉冲,所以在计算的时候需要对其进行处理。
分别计数两个方向的脉冲数,以脉冲数多的方向为当前方向,多的脉冲数 减去 少的脉冲数 为当前方向的脉冲数。
assign encoder_Phase_count_dir_o = ( encoder_Phase_Cnt_dir0 > encoder_Phase_Cnt_dir1 ) ? 1'b0 : 1'b1;
assign encoder_Phase_count_o = ( encoder_Phase_Cnt_dir0 > encoder_Phase_Cnt_dir1 ) ? (encoder_Phase_Cnt_dir0 - encoder_Phase_Cnt_dir1): (encoder_Phase_Cnt_dir1 - encoder_Phase_Cnt_dir0);//编码器脉冲计数
always@(posedge sys_clk_i or negedge sys_rst_n_i) beginif( sys_rst_n_i == 1'b0) beginencoder_Phase_Cnt_dir0 <= 16'd0;encoder_Phase_Cnt_dir1 <= 16'd0;endelse if( encoder_Phase_count_clear_i == 1'b1 ) begin //定时器输入,重新开始计数encoder_Phase_Cnt_dir0 <= 16'd0;encoder_Phase_Cnt_dir1 <= 16'd0;endelse if( encoder_AB_detect_valid_i == 1'b1 ) //对两个方向的脉冲分别计数if( encoder_AB_detect_dir_i == 1'b0 ) beginencoder_Phase_Cnt_dir0 <= encoder_Phase_Cnt_dir0 + 1'b1;encoder_Phase_Cnt_dir1 <= encoder_Phase_Cnt_dir1;endelse beginencoder_Phase_Cnt_dir0 <= encoder_Phase_Cnt_dir0;encoder_Phase_Cnt_dir1 <= encoder_Phase_Cnt_dir1 + 1'b1; endelse beginencoder_Phase_Cnt_dir0 <= encoder_Phase_Cnt_dir0;encoder_Phase_Cnt_dir1 <= encoder_Phase_Cnt_dir1;end
end
六. 速度与加速度检测模块
当定时器信号输入时,根据脉冲计数模块输入的脉冲数来计算。速度的单位为 圈数/分钟,而检测周期为5ms,所以RPM = 圈数 *( 1分钟 / 5ms ),然后这里使用的编码器为500线,电机的减速比为20,所以一圈对应的编码器脉冲数为500 * 20 = 10000 ,所以最终RPM = ( 脉冲数 / 10000) * ( 1分钟 / 5ms) = 1.2 * 脉冲数。 为了便于计算将RMP扩大10倍, 即 RPM = 12 * 脉冲数 = ( 8 + 4) * 脉冲数。将脉冲数左移3位 加速 脉冲数左移2位 即可。
加速度就更加简单了,只需要将当前速度 减去 上一时刻的速度 即可,加速度有方向,还需要判断一下加速度方向。
assign encoder_speed = ( encoder_Phase_count_i << 3 ) + ( encoder_Phase_count_i << 2 );
always@( posedge sys_clk_i or negedge sys_rst_n_i ) beginif( sys_rst_n_i == 1'b0 ) beginencoder_speed_d0 <= 16'd0;encoder_speed_o <= 16'd0;encoder_acc_o <= 16'd0;encoder_acc_dir_o<= 1'b0;endelse if( cal_en_i == 1'b1 ) beginencoder_speed_d0 <= encoder_speed_o;encoder_speed_o <= encoder_speed;encoder_acc_o <= (encoder_speed > encoder_speed_d0) ? encoder_speed - encoder_speed_d0 : encoder_speed_d0 - encoder_speed;encoder_acc_dir_o<= (encoder_speed > encoder_speed_d0) ? 1'b1 : 1'b0;endelse beginencoder_speed_d0 <= encoder_speed_d0;encoder_speed_o <= encoder_speed_o;encoder_acc_o <= encoder_acc_o;encoder_acc_dir_o<= encoder_acc_dir_o;end
end
七. 角度检测模块
当需要旋转对应角度的时候,就需要对其旋转角度进行检测。旋转一圈,对应的脉冲数为10000,所以每个脉冲数对应的角度为0.036°,为了便于计算,这里将角度扩大10倍,每个脉冲数对应的角度就变成了0.36°,每5个脉冲数对应的角度就变成了1.8°,这样以5个脉冲数为一个脉冲单位,所以每一个脉冲单位角度就变化了1°,先不管0.8°,而0.8 * 5 = 4°,所以每5个脉冲单位角度就变化了4°,这样就非常方便计算了,当是会有一定的误差,最大误差角度约为: 3.2° + 1° = 4.2°,即实际误差角度为0.42°,还是可以接受的。
always@(posedge sys_clk_i or negedge sys_rst_n_i ) beginif( sys_rst_n_i == 1'b0 )encoder_num_angle_o <= 20'd0;else if( encoder_accumulate_clr_i == 1'b1)encoder_num_angle_o <= 20'd0;else if( encoder_accumulate_en_i == 1'b1 && encoder_AB_detect_valid_i == 1'b1 )if( encoder_AB_Angle_Cnt1 == 'd4 )encoder_num_angle_o <= encoder_num_angle_o + 'd4 + 'd1;else if( encoder_AB_Angle_Cnt == 'd4)encoder_num_angle_o <= encoder_num_angle_o + 1'b1;else encoder_num_angle_o <= encoder_num_angle_o;elseencoder_num_angle_o <= encoder_num_angle_o;
end
八. 小结
现在一个简单的编码器模块,就写完啦,算是一个基础版本吧,后面会根据需求进行完善。
需要完整模块文件的话关注gzh FPGA之旅 可以回复 编码器1.0 获取