FPGA与Vince步进电机驱动器

一、工程简介

1、 本工程实现的功能:用FPGA通过两个按键控制步进电机的启动和停止,硬件包括:黑金开发板、步进电机和Vince步进电机驱动器和电脑。
2、FPGA通过控制电机驱动器来控制步进电机的旋转,通过控制脉冲个数来控制电机角位移量,控制发送脉冲频率来控制电机的速度和加速度。
3、FPGA与Vince步进电机驱动器之间的通讯协议采用RS232电平标准的串口通信(UART),FPGA发送给驱动器的命令是32位数据或64位的,因此串口模块还实现了32转8位数据的功能。
4.我的电机用在下图中:
在这里插入图片描述

二、顶层设计

motor顶层包括3个子模块:2个按键模块(key_ctrl)和一个电机模块(motor_ctrl)。顶层如图一所示。
这块模块要特别注意:motor模块复位端口rst_n的接线,如果不接,那么key_ctrl模块的rst_n必须接高电平,否则电机无法启动,原因是直接接motor模块的外部rst_n,电平不稳定,会导致这个key_ctrl模块一直处于复位状态,可用SignalTap II抓取一下rst_n信号就一目了然了。

module motor (

	input		wire				clk,
	input		wire				rst_n,
	
	input		wire				key1,
	input		wire				key2,
	
	output 	wire				TXD
); 
	
	wire				key_flag1;
	wire				key_flag2;			//	key_flag2 = 0 in general 

//================ key1 control motor stop =============================

	key_ctrl key_ctrl_inst1 (

		.clk					(clk			),
		.rst_n				(1'b1			),
                         
		.key					(key1			),
		                   
		.key_flag			(key_flag1	)
	);

//=========================================================================

//================ key2 control motor start/reset =========================

	key_ctrl key_ctrl_inst2 (

		.clk					(clk			),
		.rst_n				(1'b1			),		//	if rst_n connected to port rst_n,rst_n must be allocated pins.
                         
		.key					(key2			),
		                   
		.key_flag			(key_flag2	)
	);

//========================================================================

	motor_ctrl motor_ctrl_inst(

		.clk						(clk			),
		.rst_n					(~ key_flag2), 
		
		.motor_end				(key_flag1	), 
		
		.TXD						(TXD			)
	);
	
endmodule 

三、架构设计

1、key_ctrl模块

该模块分为两个部分:按键消抖和边缘检测。其中中间信号key_wave的脉宽由按键按下的时间长短来决定,key_ctrl模块顶层如下:

module key_ctrl (

	input		wire					clk,
	input		wire					rst_n,

	input		wire					key,
	
	output	wire					key_flag
);

	wire					key_wave;
	
	key_filter key_filter_inst(

				.clk				(clk			),
				.rst_n			(rst_n		),
				
				.key				(key			),
				
				.key_wave		(key_wave	)
	);

	edge_check edge_check_inst(

				.clk				(clk			),
				.rst_n			(rst_n		),
				
				.key				(key_wave	),
				
				.flag_pos		(),
				.flag_neg		(key_flag	)
	);

endmodule 

(1)按键消抖模块(key_filter)

按键按下的标准:按下时间超过5ms。按键的状态可以分为4个:松开(KEY_OFF)、闭合抖动(ON_SHAKE)、闭合(KEY_ON)、松开抖动(OFF_SHAKE)。key_filter模块代码如下:

module key_filter (

	input		wire				clk,
	input		wire				key,
	input		wire				rst_n,
	
	output	reg				key_wave
);

	parameter		T_5ms			=	250_000;
	
	localparam		KEY_OFF			=	4'b0001;
	localparam		ON_SHAKE			=	4'b0010;
	localparam		KEY_ON			=	4'b0100;
	localparam		OFF_SHAKE		=	4'b1000;

	reg		[17:0]				cnt_5ms;
	reg		[3:0]				c_state;
	reg		[3:0]				n_state;
	
	reg		[1:0]				buf_key;
	wire							skey;
	
	always @ (posedge clk) buf_key <= {buf_key[0],key};
	
	assign skey = buf_key[1];
	
	always @ (posedge clk, negedge rst_n) begin
		if (rst_n == 0)
			c_state <= KEY_OFF;
		else
			c_state <= n_state;
	end

	always @ (*) begin
		case (c_state)
			KEY_OFF		:	if (skey)
									n_state = KEY_OFF;
								else
									n_state = ON_SHAKE;
			ON_SHAKE		:	if (skey)
									n_state = KEY_OFF;
								else
									if (cnt_5ms < T_5ms - 1)
										n_state = ON_SHAKE;
									else
										n_state = KEY_ON;
	
			KEY_ON		:	if (skey)
									n_state = OFF_SHAKE;
								else
									n_state = KEY_ON;
	
			OFF_SHAKE	:	if (!skey)
									n_state = KEY_ON;
								else
									if (cnt_5ms < T_5ms - 1)
										n_state = OFF_SHAKE;
									else
										n_state = KEY_OFF;
			default		:	n_state = KEY_OFF;
		endcase
	end
	
	always @ (posedge clk, negedge rst_n) begin
		if (rst_n == 0)
			cnt_5ms <= 0;
		else
			case (c_state)
				KEY_OFF		:	cnt_5ms <= 0;
				
				ON_SHAKE		:	if (skey)
										cnt_5ms <= 0;
									else
										if (cnt_5ms < T_5ms - 1)
											cnt_5ms <= cnt_5ms + 1'b1;
										else
											cnt_5ms <= 0;
											
				KEY_ON		:	cnt_5ms <= 0;
				
				OFF_SHAKE	:	if (!skey)
										cnt_5ms <= 0;
									else 
										if (cnt_5ms < T_5ms - 1)
											cnt_5ms <= cnt_5ms + 1'b1;
										else
											cnt_5ms <= 0;
			endcase
	end
	
	always @ (posedge clk, negedge rst_n) begin
		if (rst_n == 0)
			key_wave <= 1;
		else
			if (c_state == KEY_ON || c_state == OFF_SHAKE)
				key_wave <= 0;
			else
				key_wave <= 1;
	end
	
endmodule 

(2)边沿检测模块(edge_check)

该模块可以检测一个外部信号的上升沿和下降沿。edge_check模块代码如下:

module edge_check (

	input		wire				clk,
	input		wire				rst_n,
		
	input		wire				key,
	
	output	wire				flag_pos,
	output	wire				flag_neg
);

	reg							syn_reg;
	reg							check_reg;
	
	always @ (posedge clk, negedge rst_n) begin
		if (rst_n == 0)
			syn_reg <= 1;
		else
			syn_reg <= key;
	end
	
	always @ (posedge clk, negedge rst_n) begin
		if (rst_n == 0)
			check_reg <= 1;
		else
			check_reg <= syn_reg;
	end
	
	assign flag_neg = check_reg & (~syn_reg);
	assign flag_pos = (~check_reg) & syn_reg;
	
endmodule 

2、motor_ctrl模块

模块输入:clk(时钟)、rst_n(电机启动)、motor_end(电机停止)
模块输出:TXD(串口发送)
实现的功能:
上电复位,FPGA给Vince步进电机驱动器发送如下指令的16进制码值:握手指令dev\n(32’h6465760a)、电机使能ena\n(32’h656e610a)、旋转指令mov\n(32’h6d6f760a)和停止指令off(32’h6f66660a)。电机接收到上述指令,开始按照预设的参数(速度、加速度等)旋转,直到按键key1按下,motor_end信号被拉高,停止旋转。
tx_demo模块是32位数据转8位数据模块,因为电机的指令至少是32位的,所以将其拆分成4次通用的8位UART串口发送。
uart_tx模块是8位数据的串口发送模块,发送波特率为115200bps,每帧数据包括10位:起始位、停止位各一位,中间8位数据位。各模块的代码如下:

(1)motor_ctrl模块代码:

//=========== control motor rotate, rs232 communicaton protocols ================

module motor_ctrl(

   input		wire				clk,
	input		wire				rst_n, 
	input		wire				motor_end,
	
	output	wire				TXD
);

	reg	[31:0]	order;                                			//232
	reg 	[2:0]		state;                                			//232
	reg				rst_n_t;                              			//232  
	
	wire				txd_done;                              		//232

	tx_demo tx_demo_inst(

		.clk					(clk			),
		.rst_n				(rst_n_t		),
                         
		.order				(order		),
		                   
		.TXD					(TXD			),
		.txd_done			(txd_done	)
	);
	 
	always @ ( posedge clk or negedge rst_n )
		if(!rst_n)
			begin
				rst_n_t <= 0;
				order <= 32'h0;
				state <= 3'd0;
			end
		else
			begin
				case(state)
				
					 0:
						begin 
							order <= 32'h6465760a;				//dev= 646576(HEX) ,发送指令,指令是小写   
							if(txd_done)								//换行"\n"的ASCII的十六进制表示是0a
								begin
									state <= 2'd1;
									rst_n_t <= 0;
								end
							else
								begin
									state <= state;
									rst_n_t <= 1;
								end
						end
						
					1:
						begin 
							order <= 32'h656e610a;			//ena= 656E61(HEX)
							if(txd_done)
								begin
									state <= 2'd2;
									rst_n_t <= 0;
								end
							else
								begin
									state <= state;
									rst_n_t <= 1;
								end
						end

					2:
						begin 
							order <= 32'h6d6f760a;			//mov= 6d6f76(HEX)
							if(txd_done && motor_end)
								begin
									state <= 2'd3;
									rst_n_t <= 0;
								end
							else
								begin
									state <= state;
									rst_n_t <= 1;
								end
						end
						
					3:
						begin 
							order <= 32'h6f66660a;			//off= 6f6666(HEX)
							if(txd_done)
								begin 
									state <= 3'd4;
									rst_n_t <= 0;
								end
							else
								begin
									state <= state;
									rst_n_t <= 1;
								end
						end
						
//					3:
//						begin 
//							order <= 32'h63666720;			//cfg = 3666720(HEX)  注意空格
//							if(txd_done)
//								begin 
//									state <= 3'd4;
//									rst_n_t <= 0;
//								end
//							else
//								begin
//									state <= state;
//									rst_n_t <= 1;
//								end
//						end						
//						
//					4:
//						begin 
//							order <= 32'h7370643d;			//spd= = 7370643d(HEX)
//							if(txd_done)
//								begin 
//									state <= 3'd5;
//									rst_n_t <= 0;
//								end
//							else
//								begin
//									state <= state;
//									rst_n_t <= 1;
//								end
//						end						
//						
//					5:
//						begin 
//							order <= 32'h3231300a;			//210= 323130(HEX)
//							if(txd_done)
//								begin 
//									state <= 3'd6;
//									rst_n_t <= 0;
//								end
//							else
//								begin
//									state <= state;
//									rst_n_t <= 1;
//								end
//						end						
						
					default:	rst_n_t <= 0;
				endcase
			end

endmodule

(2)tx_demo模块代码:

module tx_demo(

	input		wire					clk,
	input		wire					rst_n,

	input		wire	[31:0]		order,
	
	output	wire					TXD,
	output	reg					txd_done
);
	
	reg					icall;
	reg		[2:0]		state;
	reg		[7:0]		idata;

	uart_tx uart_tx_inst(

		.clk						(clk			),
		.rst_n					(rst_n		),
										
		.icall					(icall		),
		.idata					(idata		),
                            
		.TXD						(TXD			),
		.odone					(odone		)
	);

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				begin
					icall <= 0;
					txd_done <= 0;
					idata <= 0;
					state <= 0;
				end
			else
				case (state)
					3'd0				:	begin
												if (odone)
													begin
														icall <= 1'b0;
														state <= 3'd1;
													end
												else
													begin
														icall <= 1'b1;
														idata <= order[31:24];
														state <= state;
													end
											end

					3'd1				:	begin
												if (odone)
													begin
														icall <= 1'b0;
														state <= 3'd2;
													end
												else
													begin
														icall <= 1'b1;
														idata <= order[23:16];
														state <= state;
													end
											end
											
					3'd2				:	begin
												if (odone)
													begin
														icall <= 1'b0;
														state <= 3'd3;
													end
												else
													begin
														icall <= 1'b1;
														idata <= order[15:8];
														state <= state;
													end
											end
											
					3'd3				:	begin
												if (odone)
													begin
														icall <= 1'b0;
														state <= 3'd4;
													end
												else
													begin
														icall <= 1'b1;
														idata <= order[7:0];
														state <= state;
													end
											end
											
					3'd4				:	begin
												txd_done <= 1'b1;
												state <= state;
											end			
				endcase
		end

endmodule 

(3)uart_tx模块代码:

module uart_tx(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire					icall,
	input		wire	[7:0]			idata,

	output	reg					TXD,
	output	reg					odone
);

	parameter			BAUDRATE = 13'd434;			// 50_000_000/115200 = 434;
																// 50_000_000/9600 = 5208;
	reg		[12:0]	cnt;
	reg		[3:0]		state;
	
	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				begin
					odone <= 0;
					cnt <= 0;
					TXD <= 1'b1;
					state <= 0;
				end
			else if (icall)
				case (state)
					4'd0				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= 1'b0;					// UART start bit
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd1;
													end
											end
											
					4'd1				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[0];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd2;
													end
											end
						
					4'd2				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[1];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd3;
													end
											end

					4'd3				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[2];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd4;
													end
											end

					4'd4				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[3];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd5;
													end
											end

					4'd5				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[4];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd6;
													end
											end

					4'd6				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[5];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd7;
													end
											end

					4'd7				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[6];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd8;
													end
											end
											
					4'd8				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= idata[7];
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd9;
													end
											end
											
					4'd9				:	begin
												if (cnt < BAUDRATE - 1)
													begin
														cnt <= cnt + 1'b1;
														TXD <= 1'b1;				// UART stop bit
														state <= state;
													end
												else
													begin
														cnt <= 0;
														state <= 4'd10;
													end
											end

					4'd10				:	begin
												odone <= 1'b1;
												state <= 4'd11;
											end

					4'd11				:	begin
												odone <= 1'b0;
												state <= 4'd0;
											end
					default			:	;
				endcase
		end

endmodule 

四、个人总结

本人花了相当长的时间在和这个Vince步进电机驱动器打交道,遇到的问题主要有以下几个:
(1)
刚开始想用Vince驱动器产品配套的软件设定电机运转参数,但是驱动器一直无法和PC机通讯成功,奇怪的是用相同的电路用FPGA确可以控制电机旋转,最后找出原因是电路中某处连线的焊锡不足,导致用PC机来驱动电机旋转时电压驱动能力不够,所以导致通讯不成功,用电烙铁加强焊点就可以了。
(2)
关于motor顶层模块复位rst_n的接线问题,把我折腾的快怀疑人生了,好在最后用SignalTap II 找出了原因,详情可看前文。
学FPGA得慢慢来啊,你今天的日积月累,会带来明天别人的望尘莫及

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值