宋桓公

【红色飓风Nano二代测评】Verilog实现直线插补

0
阅读(6197)

 

既然要用Nano2打造一个数控平台,那么插补就是必不可少的,那么我们就从“插补”开始。

wps_clip_image-11610

首先,任意一条直线都是X和Y分量组成的,而我们的X,Y分量是有两个步进电机控制的。如上图所示,原点O和点A构成直线。

想象一下,现在我们的XY平台已经搭建好了。

平台的控制端点上有一支画笔,X轴的步进电机转一下,画笔就会在X方向上走一步;Y轴的步进电机转一下,画笔就会在Y方向上走一步。

wps_clip_image-16590

(要是能有个这样的数控平台就爽了,哈哈~)

假设,现在假设画笔就在原点O,A的坐标是(4,3),现在我们要求控制步进电机,使画笔从原点O移到A。如果不考虑插补的话,我可以控制X电机先移动4个单位,再让Y电机移动3个单位。那么画笔移动到的A点没错,再从宏观上看画笔的运动轨迹:

wps_clip_image-3701

它是由两条相互垂直的直线组成的折线。但是如果我们要求画笔的运动轨迹看上去就是直线OA,那这个如何实现呢?

先抛开上面这个问题不管,我们继续假设,画笔在原点O,A的坐标是(3,3),我们要求控制步进电机,使画笔从原点O移到A。当然我们可以效仿上面的方法,可以先控制X电机移动3个单位,再让Y电机移动3个单位。紧接着,提高要求:要求画笔的运动轨迹看上去就是直线OA。

那似乎也不难,只要我们要求X轴和Y轴步骤电机“步伐保持一致”同时移动3个单位,那么画笔的运动轨迹就是直线OA。回头再之前的问题,看似否能解决:画笔就在原点O,A的坐标是(4,3),要求画笔的运动轨迹看上去就是直线OA。感觉依然没有办法。原因是应为之前A点的坐标比较特殊(3,3)导致直线OA的X和Y轴的分量是相等的,很容易保证在相同时间内XY轴步进电机同时完成各种的任务。

但是如果直线OA的X和Y轴的分量不等,如何保证画笔的运动轨迹看上去就是直线OA呢?这就要借助插补的力量了~~

插补的思路就是,让X和Y电机“同时工作”,在适当的时机走出自己的一步。

在理想的数学是世界里,直线是由无数细化的点构成的,但是在真实的的世界,没有什么是无限的,就拿步进电机驱动的器的而言它的细分数是有限的,也就是说步进电机每一步的距离不可能是无穷小的。所以我们数控世界的“直线”往往是由很多细小直线段构成的“折线”如下图:

wps_clip_image-1510wps_clip_image-21779

左图的,直线经过局部放大后,我们发现它是一条类似楼梯的折线,用插补的思想去解读它,就感觉是;“X电机走一步,紧接着Y电机走一步,然后X电机走2步,紧接着Y电机走一步”循环往复,一条“直线就出来了”现在思路出来了,如何将一条直线细化,并且将任务分配给X,Y电机,它们之间如何做出配合,在适当的时间走出自己的一步,就是问题的关键。

现在就介绍一种,十分简单的直线插补方法:

还是以画直线(4,3)为例,我们需要建立一个定时器,用来控制画直线的数度,还需要建立两个计数器来匹配XY轴的速度。很明显计数器的大小是有“讲究”的。首先直线(4,3)X轴的分量是4,Y轴的分量是3,而计数器的大小正好反过来,X轴的计数器大小是3,Y轴的计数器大小是4。为什么要这么做,看着下图,想必您也猜的八九不离十了,那我就说说具体实现过程:

当数度计时器溢出时,同通知X,Y计数器计数加一,当某个计数器计满后,就通知所对应的电机走一步,并且清空该计数器。如此往复,当走到合适的长度就让其停下来。

我们可先不让其停止,让其一直这么循环下去,看看斜率对不对即可。

wps_clip_image-769

wps_clip_image-13062

从仿真图上看当X走完4步,Y轴正好完成3步,正好满足(4,3)这条直线的分量,如果对应到实际再来看看。

那就是X走一步,Y走一步,X走一步,Y走一步,XY同时走一步:

wps_clip_image-4345这个就是循环的最小单位。

如果多叠加几次,看看是什么效果:

wps_clip_image-6865

有没有发现,我们走的这条折线,已近非常的接近OA这条直线了,也就是斜边“弦五”,说明一下,真正的数控中,步进电机加上机械结构,轻松达到了每步0.1mm的精度,人眼更本看不出来,这是一条折线~~

  最后奉上代码:


module Pulse
(
	input CLK,
	input RSTn,
	input Pu_Sig,//该信号为高时,表示要发脉冲~~
	output reg pulse
);

	parameter ON = 1'b1;
	parameter OFF = 1'b0;

	reg[7:0] C0;
	reg [3:0]i;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn) 
		begin
			C0 <= 8'd0;
			pulse <= OFF;
			i <= 4'd0;
		end
		else 
			case(i)
				0://判断是否发脉冲
				begin
					if(Pu_Sig)begin i <= i + 1'b1;end
					else begin pulse <= OFF; i <= i;end
				end
				1:
				begin
					if(C0 == 10)//脉冲宽度控制
					begin 
						pulse <= OFF;
						C0 <= 8'd0;
						i <= 4'd0;
					end
					else
					begin
						pulse <= ON;
						C0 <= C0 + 1'b1;
						i <= i;
					end
				end
				
			endcase


endmodule


上面这段代码是实现,如何发送一个有一定宽度的脉冲,是实现驱动步进电机的关键,这里一个脉冲代表电机走一步。

wps_clip_image-13062这个就是宽度为10的脉冲


module test
(
	input CLK,
	input RSTn,
	
	output X_pulse,
	output Y_pulse
	
);
	//速度控制器
	reg [9:0]Cnt;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)
		begin
			Cnt <= 10'd0;
		end
		else 
		begin
			if(Cnt == 10'd1000) Cnt <= 10'd0;
			else Cnt <= Cnt + 1'b1;
		end

	//X轴计数器
	reg [9:0]Cx;
	reg X_Pu_Sig;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)
		begin
			Cx <= 10'd0;
			X_Pu_Sig <= 1'b0;
		end
		else 
		begin
		     if(Cx == 3) begin Cx <= 10'd0; X_Pu_Sig <= 1'b1;end
		     else if(Cnt == 10'd1000) begin Cx <= Cx + 1'b1; X_Pu_Sig <= 1'b0;end
		     else begin Cx <= Cx; X_Pu_Sig <= 1'b0;end
		end
		
	//Y轴计数器	
	reg [9:0]Cy;
	reg Y_Pu_Sig;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)
		begin
			Cy <= 10'd0;
			Y_Pu_Sig <= 1'b0;
		end
		else 
		begin
			if(Cy == 4) begin Cy <= 10'd0; Y_Pu_Sig <= 1'b1; end
			else if(Cnt == 10'd1000) begin Cy <= Cy + 1'b1; Y_Pu_Sig <= 1'b0;end
			else begin Cy <= Cy; Y_Pu_Sig <= 1'b0;end
		end	


//--发脉冲模块例化					
	Pulse	 PulseX
				(
					.CLK(CLK),
					.RSTn(RSTn),
					.Pu_Sig(X_Pu_Sig),//该信号为高时,表示要发脉冲~~
					.pulse(X_pulse)
				);
				
	Pulse	 PulseY
				(
					.CLK(CLK),
					.RSTn(RSTn),
					.Pu_Sig(Y_Pu_Sig),//该信号为高时,表示要发脉冲~~
					.pulse(Y_pulse)
				);	


endmodule
这是顶层模块,实现一个定时器,两个计数器,并且例化两个Pulse模块,一个X轴,一个Y轴。



TestBeach如下:


module test_vlg();

reg CLK;
reg RSTn;
                                              
wire X_pulse;
wire Y_pulse;

                        
test i1 ( 
	.CLK(CLK),
	.RSTn(RSTn),
	.X_pulse(X_pulse),
	.Y_pulse(Y_pulse)
);

initial                                                
begin                                                  
	RSTn = 0; #10 RSTn = 1; 
	CLK = 1; forever #5 CLK = ~CLK;
end  


接下来,我将用Nano2实现——通过插补在TFT上画任意直线,并且同步驱动电机。敬请期待~~


技术讨论欢迎加群~~电子技术协会   362584474