步进电机是一种能够将电脉冲信号转换成角位移或线位移的机电元件,它实际上是一种单相或多相同步电动机。单相步进电动机有单路电脉冲驱动,输出功率一般很小,其用途为微小功率驱动。多相步进电动机有多相方波脉冲驱动,用途很广。使用多相步进电动机时,单路电脉冲信号可先通过脉冲分配器转换为多相脉冲信号,在经功率放大后分别送入步进电动机各相绕组。每输入一个脉冲到脉冲分配器,电动机各相的通电状态就发生变化,转子会转过一定的角度(称为步距角)。正常情况下,步进电机转过的总角度和输入的脉冲数成正比;连续输入一定频率的脉冲时,电动机的转速与输入脉冲的频率保持严格的对应关系,不受电压波动和负载变化的影响。由于步进电动机能直接接收数字量的输入,所以特别适合于微机控制。

    本实验是采用双四拍来控制四相步进电机的运行:AB-BC-CD-DA-AB;利用PWM信号控制电机,电机的各相电流大小取决于PWM信号的占空比,所以通过改变PWM信号的占空比来控制电机各相的电流。

//程序实现的功能:使用PWM方法来控制步进电机细分旋转,实现1/4细分(4.5°/步)控制和不细分控制(18°/步)。Key控制电机的正、反转,key2控制电机的正常运行和细分运行
module step_motor
     (
       clk,
       rst_n,
       key1,
       key2,
       motor
        
     );

input       clk;        //系统时钟50MHZ;
input       rst_n;      //复位信号,低电平有效;
input       key1;       //控制电机正、反转;
input       key2;       //控制电机的运行模式;
output[3:0] motor;      //步进电机的A、B、C、D四相;



//键盘消抖
reg[1:0]  key_r1;
always @ (posedge clk or negedge rst_n)
  begin
         if(!rst_n)
           key_r1<=2'b11;
         else 
           key_r1<={key1,key2};  
  end  

reg[1:0]  key_r2; 
 always @ (posedge clk or negedge rst_n)
  begin
         if(!rst_n)
           key_r2<=2'b11;
         else 
           key_r2<=key_r1;  
  end  
  
wire[1:0]  neg_key1;
assign  neg_key1=(~key_r1)&(key_r2);

reg[19:0] cnt;

always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
           cnt<=20'd0;
         else if(neg_key1)
           cnt<=20'd0;
         else  
           cnt<=cnt+1'b1;      
  end
  
reg[1:0]   key_r3;
always @ (posedge clk or negedge rst_n)
  begin
         if(!rst_n)
           key_r3<=2'b11;
         else if(cnt==20'hfffff)     //当有按键按下时,延时20ms,再次读入按键值;
           key_r3<={key1,key2};
           
  end  

reg[1:0]  key_r4;
always @ (posedge clk or negedge rst_n)
 begin
      if(!rst_n)
        key_r4<=2'b11;
       else
        key_r4<=key_r3; 
 end 

wire[1:0] neg_key2;
assign neg_key2=(~key_r3)&key_r4 ;

reg [23:0] delay;     
always @ (posedge clk or negedge rst_n)
   begin
        if(!rst_n)
           delay<=24'd0;
        else
           delay<=delay+1'b1;
   end




reg dir;       //电机正、反转
always @ (posedge clk or negedge rst_n)
   begin
         if(!rst_n)
            dir<=1'b0;
         else if(neg_key2[1]) 
            dir<=~dir;  
   end

reg mode ;   //电机模式选择;
always @ (posedge clk or negedge rst_n)
   begin
         if(!rst_n)
            mode<=1'b0;
         else if(neg_key2[0]) 
            mode<=~mode;  
   end   

wire speed_clk; // 电机转动速度控制时钟;
assign  speed_clk=(delay[23:0]==24'hffffff);    //大约3HZ; 
  
reg[3:0] num;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
           num<=4'b0000;
        else if(speed_clk)
          begin
           if(dir)
              num<=num+1'b1; 
           else
             num<=num-1'b1;     
          end 
  end 

reg[3:0] motor_r ; 
always @ (posedge clk or negedge rst_n)
  begin
      if(!rst_n)
         motor_r<=4'd0;
      else if(speed_clk)   
        begin
          case(num[1:0])
            2'b00 :  motor_r<=4'b1100; 
            2'b01 :  motor_r<=4'b0110;
            2'b10 :  motor_r<=4'b0011;
            2'b11 :  motor_r<=4'b1001;
            default :motor_r<=4'b0000;
          endcase 
        end    
       
  end    

wire pwm_clk;   //pwm计数时钟;
assign  pwm_clk=(delay[6:0]==7'h7f);

reg [3:0] pwm_cnt;     //pwm计数器;
always @(posedge clk or negedge rst_n)
  begin
        if(!rst_n)
           pwm_cnt<=4'd0;
        else if(pwm_clk)
           pwm_cnt<=pwm_cnt+1'b1;
            
  end
  
reg[15:0] duty_ratio;    //占空比;
reg[3:0] pwm_out;
always @ (posedge clk or negedge rst_n)           //PWM  A相通道;
  begin
        if(!rst_n)
          pwm_out[3]<=1'b0;
        else if(pwm_cnt[3:0]<duty_ratio[15:12])
          pwm_out[3]<=1'b1;
        else 
          pwm_out[3]<=1'b0;    
  end  
  
always @ (posedge clk or negedge rst_n)          //PWM  B相通道;
  begin
        if(!rst_n)
          pwm_out[2]<=1'b0;
        else if(pwm_cnt[3:0]<duty_ratio[11:8])
          pwm_out[2]<=1'b1;
        else 
          pwm_out[2]<=1'b0;    
  end  
  
always @ (posedge clk or negedge rst_n)          //PWM  C相通道;
  begin
        if(!rst_n)
          pwm_out[1]<=1'b0;
        else if(pwm_cnt[3:0]<duty_ratio[7:4])
          pwm_out[1]<=1'b1;
        else 
          pwm_out[1]<=1'b0;    
  end      

  
always @ (posedge clk or negedge rst_n)          //PWM  D相通道;
  begin
        if(!rst_n)
          pwm_out[0]<=1'b0;
        else if(pwm_cnt[3:0]<duty_ratio[3:0])
          pwm_out[0]<=1'b1;
        else 
          pwm_out[0]<=1'b0;    
  end      

always @ (*)                                    //四项步进电机pwm 细分参数表;
   begin
         case(num)
             4'h0   :   duty_ratio=16'hf000;
             4'h1   :   duty_ratio=16'he600;
             4'h2   :   duty_ratio=16'hbb00;
             4'h3   :   duty_ratio=16'h6e00;
             4'h4   :   duty_ratio=16'h0f00;
             4'h5   :   duty_ratio=16'h0e60;
             4'h6   :   duty_ratio=16'h0bb0;
             4'h7   :   duty_ratio=16'h06e0;
             4'h8   :   duty_ratio=16'h00f0;
             4'h9   :   duty_ratio=16'h00e6;
             4'ha   :   duty_ratio=16'h00bb;
             4'hb   :   duty_ratio=16'h006e;
             4'hc   :   duty_ratio=16'h000f;
             4'hd   :   duty_ratio=16'h600e;
             4'he   :   duty_ratio=16'hb00b;
             4'hf   :   duty_ratio=16'he006;
             default :  duty_ratio=16'h0000;
          endcase    
   end

assign  motor=mode? pwm_out :motor_r;         //输出模块选择(细分/正常)

endmodule