FPGA学习笔记(5)——步进电机梯形加减速算法

使用加减速的目的是:防止步进电机的启动频率过快而无法正常启动,避免控制脉冲频率变化过大造成电机丢步或过冲。

空载启动频率,即步进电机在空载情况下能够正常启动的脉冲频率,如果脉冲频率高于该值,电机不能正常启动,可能发生丢步或堵转。在有负载的情况下,启动频率应更低。如果要使电机达到高速转动,脉冲频率应该有加速过程,即启动频率较低,然后按一定加速度升到所希望的高频(电机转速从低速升到高速)。

步进电机的基本概念及控制模式参考如下链接:

https://www.elecfans.com/d/1294049.html


通过梯形加减速要达到的效果:

  • 控制脉冲频率从0到期望速度有直线加减速的过程;
  • 电机运动结束能够到达指定的位置,即经过加减速曲线后能够准确在预期位置处停止。

梯形加减速的算法实现

梯形加减速的位移、速度、加速度曲线如图1所示,速度曲线分为加速、匀速、减速三个部分。

图1 梯形加减速示意图

    图2所示,对直线加速段进行离散,在0-ta之间插入若干中间速度值,控制速度从小到大取值,即可实现直线加速。本次设计采用对0-ta进行n等分,每一份的时间为t_s=ta/n,也是将期望速度(speed)进行n等分,即在加速段均匀插入n-1个中间速度值,每经过时间t_s,对当前速度加 speed/n ,直到达到期望速度speed。减速过程与之相反。

本次设计只实现图2所示的加减速情况,n=100。

图2 离散化后的直线加减速速度变化曲线


梯形加减速模块框图如图3所示

图3

功能说明:上位机传送电机目标位置、目标速度、加速时间、减速时间,经过加减速模块后,能够在目标位置准确停下。

引脚说明

Clk

系统时钟50MHz

Rst_n

复位信号,低电平有效

Step

电机目标位置,输入脉冲个数。示例代码中为64000

Speed

电机目标速度,输入脉冲频率(hz)。示例代码中为100khz

T_accel

加速时间(ms)。示例代码中为300ms

T_decel

减速时间(ms)。示例代码中为400ms

Pul

输出脉冲信号


各个模块定义

1. 定时计数模块

在加速或减速控制开启状态下启动该计数器,加速计数的时间为t_s=ta/n,减速计数的时间为

t_s=(tc-tb)/n

代码:

assign  T_clk = 26'd50_000_000;
assign	CNT_up = (t_accel*T_clk)/100000;	//acc time;计数器的计数值
assign	CNT_dn = (t_decel*T_clk)/100000;	//dec time


always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_up <= 'b0;
	else if(cnt_up == CNT_up - 1'b1)
		cnt_up <= 'b0;
	else if(up_en)                       
 //加速使能有效时才开始计数,所以speed_current赋值条件为cnt_up == 0
		cnt_up <= cnt_up + 1'b1;
end
always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_dn <= 'b0;
	else if(cnt_dn == CNT_dn - 1'b1)
		cnt_dn <= 'b0;
	else if(down_en)
		cnt_dn <= cnt_dn + 1'b1;
end

2. 加减速使能模块

通过比较当前脉冲数与加速阶段的总脉冲数和需要减速时的脉冲数,确定加速使能信号up_en和减速使能信号down_en。

图2所示,加速阶段的总脉冲数为红线下方的面积,即CNT_sup=1/2×Vt×(ta-ta/n)

注意单位转换:hz X s,得到的是脉冲数,减速阶段同理。

代码:

assign  CNT_con= step_target - speed_target*(t_decel-t_decel/100)/2000; 
//when dec time ,减速的时间点,总脉冲数减去加减速总共所需的脉冲数,hz x s,得出的才是脉冲个数
assign  CNT_sup= speed_target*(t_accel-t_accel/100)/2000;
//加速阶段的脉冲总数

always@(posedge clk or negedge rst_n)    //加减速的使能条件要合理布置,用速度作为判断条件时容                
                                         //易出错
begin
	if(!rst_n) begin
		up_en   <= 'b0;
		down_en <= 'b0;
	end
	else if (cnt_con <= CNT_sup)    //当输出脉冲个数小于等于加速阶段的总脉冲数时,置一
		up_en	<= 1'b1;	
	else if  (cnt_con > CNT_con && speed_current > 'b0) 
//when dec,当到达减速点时,给减速使能置一,同时条件还必须包括speed_current > 'b0,
//减速到0时,该条件会永远满足,一直减速下去
		down_en <= 1'b1;
	else begin
		up_en	<= 1'b0;
		down_en <= 1'b0;
	end
end

3. 加减速变化模块

当加速使能信号up_en为真时,对当前速度加speed/n,当减速使能信号down_en为真时,对当前速度减speed/n

代码:

always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		speed_current <= 'b0;
	else if(up_en && cnt_up == 0)    //必须是在up_en刚刚有效时就进行加减速
		speed_current <= speed_current + speed_target/100;
	else if(down_en && cnt_dn == 0)
		speed_current <= speed_current - speed_target/100;
	else 
		speed_current <= speed_current ;
end

4. 脉冲输出模块

计算出当前速度对应的脉冲周期,进而计算出计数器计数到周期值所对应的计数值。

CNT_per= T_clk/speed_current;

代码:

assign  CNT_per= T_clk/speed_current;	//pulse out,当前速度对应的脉冲周期的计数值


always@(posedge clk or negedge rst_n)    //输出脉冲的周期计数器
begin
	if (!rst_n)
		cnt_per <= 'b0;
	else if(cnt_per == CNT_per - 1'b1 || cnt_up == CNT_up - 1'b1
		|| cnt_dn == CNT_dn - 1'b1)     //清零条件要加上 cnt_up和cnt_dn计数到最大值减一时,
                                        //因为下一段的速度值与前一段不同,速度变化后要重新开始                        
                                        //对输出脉冲周期的计数
		cnt_per <= 'b0;
	else 	cnt_per <= cnt_per + 1'b1;
end
//pulse out
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		pul <= 'b0;
	else if(cnt_per == CNT_per/2-1 || cnt_per == CNT_per-1) //脉冲输出,中间结束各翻转一次
		pul <= ~pul;
	else	pul <= pul;
end

5. 脉冲计数模块

记录输出脉冲的个数,且只能计数到目标位置。

always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_con <= 'b0;
	else if(cnt_per == CNT_per - 1'b1 && cnt_con != step_target)    
// 脉冲周期计数器计满且输出脉冲个数不等于要求个数时才加1,
// 计数器只计数一次的写法
		cnt_con <= cnt_con + 1'b1;
	else	cnt_con <= cnt_con;
end


仿真结果

  • Speed_current的波形满足直线加减速曲线;
  • 实际加速时间是297ms,减速时间是396ms;
  • 在输出脉冲数为64000时,Speed_current正好减速到0,电机停止。

满足预期的功能要求。

图4 直线加减速仿真结果图


完整代码如下:

Rtl:

module	linear_acc_dec(
	input		clk,
	input		rst_n,
	input	[15:0]	step_target,	// pulse_count
	input	[16:0]	speed_target,	// hz
	input	[8:0]	t_accel,	// ms
	input	[8:0]	t_decel,
	output	reg	pul		
);
//0-speed_target Insert 100 intermediate values
	wire	[33:0]	CNT_up;     //counter 3ms/20ns
	wire	[34:0]	CNT_dn;     //声明位宽时,不能只看结果值,要能容下计算过程中的最大值
	reg	[17:0]	cnt_up;
	reg	[17:0]	cnt_dn;
	reg	[16:0]	speed_current;
	reg		up_en;
	reg		down_en;
	wire	[15:0]	CNT_per;
	reg	[15:0]	cnt_per;	//T-pul period
	wire	[15:0]	CNT_con;
	reg	[15:0]	cnt_con;	//pulse count
	wire	[15:0]  CNT_sup;
	wire	[25:0]	T_clk  ;

assign  T_clk = 26'd50_000_000;
assign	CNT_up = (t_accel*T_clk)/100000;	//acc time
assign	CNT_dn = (t_decel*T_clk)/100000;	//dec time
assign  CNT_per= T_clk/speed_current;	//pulse out
assign  CNT_con= step_target - speed_target*(t_decel-t_decel/100)/2000; 
//when dec time
assign  CNT_sup= speed_target*(t_accel-t_accel/100)/2000;

always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_up <= 'b0;
	else if(cnt_up == CNT_up - 1'b1)
		cnt_up <= 'b0;
	else if(up_en)
		cnt_up <= cnt_up + 1'b1;
end
always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_dn <= 'b0;
	else if(cnt_dn == CNT_dn - 1'b1)
		cnt_dn <= 'b0;
	else if(down_en)
		cnt_dn <= cnt_dn + 1'b1;
end
//100 intermediate values
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		speed_current <= 'b0;
	else if(up_en && cnt_up == 0)
		speed_current <= speed_current + speed_target/100;
	else if(down_en && cnt_dn == 0)
		speed_current <= speed_current - speed_target/100;
	else 
		speed_current <= speed_current ;
end
//up_en,down_en
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n) begin
		up_en   <= 'b0;
		down_en <= 'b0;
	end
	else if (cnt_con <= CNT_sup)
		up_en	<= 1'b1;	
	else if  (cnt_con > CNT_con && speed_current > 'b0)//when dec
		down_en <= 1'b1;
	else begin
		up_en	<= 1'b0;
		down_en <= 1'b0;
	end
end
//pulse count
always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_con <= 'b0;
	else if(cnt_per == CNT_per - 1'b1 && cnt_con != step_target)
		cnt_con <= cnt_con + 1'b1;
	else	cnt_con <= cnt_con;
end

//cnt_pulse out
always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		cnt_per <= 'b0;
	else if(cnt_per == CNT_per - 1'b1 || cnt_up == CNT_up - 1'b1
		|| cnt_dn == CNT_dn - 1'b1)
		cnt_per <= 'b0;
	else 	cnt_per <= cnt_per + 1'b1;
end
//pulse out
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		pul <= 'b0;
	else if(cnt_per == CNT_per/2-1 || cnt_per == CNT_per-1)
		pul <= ~pul;
	else	pul <= pul;
end

endmodule

Tb:

`timescale	1ns/1ns
`define	clk_period 20
module tb();
reg		clk		;
reg		rst_n		;
reg	[15:0]	step_target	;	
reg	[16:0]	speed_target	;	
reg	[8:0]	t_accel		;	
reg	[8:0]	t_decel		;
wire		pul		;
initial begin
	clk <= 'b0;
	rst_n <= 'b0;
	#(`clk_period)
	rst_n <= 'b1;
	step_target <= 64000;
	speed_target<=100000;
	t_accel <= 300;
	t_decel <= 400;
	#(`clk_period*50000000)
	$stop ;
end

always #(`clk_period/2) clk<=~clk;

linear_acc_dec
u1
(
	.clk		(clk		),
	.rst_n		(rst_n		),
	.step_target	(step_target	),
	.speed_target	(speed_target	),
	.t_accel	(t_accel	),	
	.t_decel	(t_decel	),	
	.pul		(pul		)
);
endmodule

  • 15
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值