一、原理
(一)实现步骤:
确定加速参数:包括起始频率、最大频率、加速时间(或加速段步数)等。
生成正弦加速曲线:在加速过程中,我们期望频率按照正弦曲线从起始频率增加到最大频率。注意,这里我们通常使用正弦曲线的一半(0到π/2)或者一个完整的周期(0到π)来作为加速曲线,具体取决于需求。
使用FPGA生成步进电机脉冲:根据当前频率输出脉冲,同时按照一定的时间间隔更新频率值,使得频率遵循正弦加速曲线。
(二)具体实现:
假设我们使用0到π/2的正弦曲线来加速,那么频率的变化可以表示为:
f(t) = f0 + (f_max - f0) * sin(π/2 * t / T)
其中,f0是起始频率,f_max是最大频率,T是加速总时间,t是当前时间。
但是,在数字系统中,我们通常使用离散的时间点,因此我们可以将加速过程分为N个步骤,每个步骤的时间为Δt,那么第n个步骤的频率为:
f(n) = f0 + (f_max - f0) * sin(π/2 * n / N)
然而,步进电机的控制是通过脉冲周期(或频率)来控制的,所以我们实际上需要控制每个脉冲的周期。因此,我们可以将加速过程分为M个脉冲,每个脉冲的周期随着正弦曲线变化。
另一种方法是:我们并不每个脉冲都改变频率,而是将加速过程分为多个阶段,每个阶段内输出一定数量的脉冲,且每个阶段的频率按照正弦曲线变化。
但是,为了更精细的控制,我们可以每个脉冲都更新频率(即每个脉冲的周期都不同)。这样,我们需要一个实时计算正弦函数的能力,或者预先计算好正弦表(Sine Table)存储在FPGA的ROM中。
(三)步骤:
预先计算正弦表:将0到π/2的正弦函数离散化,存储在一个ROM中。例如,将0到π/2分为1024个点,计算每个点的正弦值。
确定加速过程中的频率变化范围:起始频率f0和最大频率f_max,以及加速过程的总步数(即脉冲数)M。
每个脉冲周期T(n) = 1 / f(n) = 1 / [ f0 + (f_max - f0) * sin(π/2 * n / M) ]
但是,由于我们使用正弦表,索引index = n * (表大小-1) / M,然后查表得到正弦值,再计算频率。
然而,在FPGA中,我们通常使用计数器来生成脉冲。具体来说,我们可以使用一个计数器,其计数上限由当前频率决定。例如,我们有一个基准时钟clk,频率为f_clk,那么生成脉冲的频率f_out = f_clk / (count_max)。因此,count_max = f_clk / (f_out)。
所以,我们需要根据当前频率f(n)计算count_max(n) = f_clk / (f(n))。
但是,由于f(n)是变化的,且我们使用正弦曲线,所以count_max(n) = f_clk / ( [f0 + (f_max - f0) * sin(π/2 * n / M)])
(四)实现结构:
状态机:控制加速、匀速、减速等状态。
脉冲生成:使用一个计数器,当计数达到count_max时,翻转脉冲信号,并重新加载计数器。同时,在每次脉冲后更新频率(即更新count_max值)。
频率更新:在加速状态下,每产生一个脉冲,就更新一次频率。我们使用一个相位累加器(或者步进计数器)n,从0到M-1,然后通过查表得到当前的正弦值,计算出当前的频率,再转换为count_max。
注意:由于正弦函数在0到π/2是单调递增的,所以加速过程是平滑的。
步骤:
在空闲状态,等待start信号。
进入加速状态后,每产生一个脉冲,step_counter加1,直到达到M。
每个脉冲后,根据step_counter计算当前在正弦表中的位置,查表得到正弦值,然后计算当前频率,再转换为count_max。
脉冲生成:pulse_counter从0计数到current_count_max,然后输出一个脉冲,并重置pulse_counter,同时更新频率(即更新current_count_max)。
注意:由于计算频率和count_max涉及除法和乘法,可能需要使用FPGA内的DSP资源,或者采用近似计算。
另一种简化方法:预先计算count_max表,存储在ROM中,这样就不需要实时计算。
这里我们采用实时计算,但为了简化,我们可以假设f0和f_max的值是固定的,并且M也是固定的,这样我们可以预先计算好count_max表,存储在ROM中。这样在加速过程中,我们只需要根据step_counter从ROM中读取count_max值即可。
考虑到灵活性,我们也可以使用实时计算,但这样会消耗较多的逻辑资源。
这里我们给出一个使用预计算ROM的方法:
步骤:
使用MATLAB或其他工具计算count_max表,并生成ROM初始化文件(.coe或.mif)。
在FPGA中实例化一个ROM,深度为M,宽度为count_max值的位宽。
在加速过程中,step_counter作为ROM的地址,读取count_max值。
这种方法的好处是节省了实时计算所需的逻辑资源,而且速度更快。
二、FPGA实现
verilog加速过程核心代码如下:
cpp
always@(posedge clk)
begin
if (rst)
begin
state <= IDLE;
cnt_prt <= 0;
romen <= 0;
romaddr <= 14'd16383;
addrswfg_up <= 0;
end
else
begin
case(state)
IDLE:
begin
if(work_en_po | addrswfg_up)
begin
state <= DELAY;
cnt_prt <= 0;
romen <= 1;
romaddr <= romaddr + speed_mult;
addrswfg_up <= 0;
end
else
begin
state <= IDLE;
cnt_prt <= 0;
romen <= 0;
romaddr <= 14'd16383;
addrswfg_up <= 0;
end
end
DELAY:
begin
if(cnt_prt == dly_value - 1)
begin
state <= CNT;
cnt_prt <= cnt_prt + 1;
romen <= 0;//////////////////////
romaddr <= romaddr;
addrswfg_up <= 0;
end
else
begin
state <= DELAY;
cnt_prt <= cnt_prt + 1;
romen <= 1;//////////////////////
romaddr <= romaddr;
addrswfg_up <= 0;
end
end
CNT:
begin
if(cnt_prt == quoeitnt - 1)
begin
state <= IDLE;
cnt_prt <= 0;
romen <= 0;
romaddr <= romaddr;
if(romaddr == 14'd16383)
begin
addrswfg_up <= 0;
end
else
begin
addrswfg_up <= 1;
end
end
else
begin
state <= CNT;
cnt_prt <= cnt_prt + 1;
romen <= 0;
romaddr <= romaddr;
addrswfg_up <= 0;
end
end
default:
begin
state <= IDLE;
cnt_prt <= 0;
romen <= 0;
romaddr <= 14'd16383;
addrswfg_up <= 0;
end
endcase
end
end
三、实现效果
加速如下:

"加速---匀速---减速"如下:

四、注意
(一)减速
ROM地址反着读即可。
(二)除法器IP核
商的位宽和被除数的位宽一致,位于高位,余数的位宽和除数的位宽一致,位于低位。如下:
cpp
div_gen_0 u_div_gen_0(
.aclk ( clk ),
.s_axis_divisor_tvalid ( div_vin ),
.s_axis_divisor_tdata ( divisor ),//16bit
.s_axis_dividend_tvalid ( div_vin ),
.s_axis_dividend_tdata ( dividend ),//32bit
.m_axis_dout_tvalid ( div_vout ),
.m_axis_dout_tdata ( div_out ) //48bit
);
assign quoeitnt = div_out[47:16];
assign intrmd = div_out[15:0];
(三)步进脉冲形式
(1)步进电机的每个步进脉冲需要两个边沿(上升沿和下降沿)来构成一个完整的方波周期。但是,在步进电机驱动中,我们通常只关心脉冲的上升沿(或者下降沿)来触发步进电机走一步。
(2)脉宽
1)典型脉冲宽度范围
最小脉冲宽度:通常 1-5μs(微秒)
常用脉冲宽度:2-10μs
最大脉冲宽度:一般不超过 50μs
2)具体驱动器要求示例
