赞
踩
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
该模块分为两个部分:按键消抖和边缘检测。其中中间信号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
按键按下的标准:按下时间超过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
该模块可以检测一个外部信号的上升沿和下降沿。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
模块输入: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位数据位。各模块的代码如下:
//=========== 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
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
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得慢慢来啊,你今天的日积月累,会带来明天别人的望尘莫及。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。