单片机控制步进电机_单片机控制输出的pwm脉冲数控制步进电机 - CSDN
  • 基于51单片机步进电机控制

    万次阅读 多人点赞 2018-05-10 18:23:41
    前面笔者分享过基于51单片机的两种小车制作,我们利用的是L298N驱动控制电机转动,那么接下来,笔者给大家介绍两种利用51单片机控制步进电机的小程序。 首先我们要如何使电机转动呢,源程序如下: #include &...

    前面笔者分享过基于51单片机的两种小车制作,我们利用的是L298N驱动控制电机转动,那么接下来,笔者给大家介绍两种利用51单片机控制步进电机的小程序。
    首先我们要如何使电机转动呢,源程序如下:

    #include <reg52.h>
    
    
    unsigned char code F_Rotation[4]={0x02,0x04,0x08,0x10}; //正转表格,换算成二进制 0000 0010,0000 0100,0000 1000,0001 0000
    unsigned char code B_Rotation[4]={0x10,0x08,0x04,0x02}; //反转表格,换算成二进制 0001 0000,0000 1000,0000 0100,0000 0010
    /******************************************************************/
    /*                    延时函数                                    */
    /******************************************************************/
    void Delay(unsigned int i)//延时
    {
     while(--i);
    }
    /******************************************************************/
    /*                   主函数                                       */
    /******************************************************************/
    main()
    {
    
     unsigned char i;
    
     while(1)
     {
      for(i=0;i<4;i++)      //4相
         {
         P1=F_Rotation[i];  //输出对应的相 可以自行换成反转表格
         Delay(500);        //改变这个参数可以调整电机转速 ,数字越小,转速越大
         }
      }
    }

    接下来,我们要控制电机的正反转,这个程序主要用于4相步进电机的常规驱动,速度不可调的过快,不然就没有力矩的转动了,按s4可控制电机正反转:

    #include <reg52.h>
    
    bit Flag;//定义正反转标志位
    
    
    unsigned char code F_Rotation[4]={0xf1,0xf2,0xf4,0xf8}; //正转表格
    unsigned char code B_Rotation[4]={0xf8,0xf4,0xf2,0xf1}; //反转表格
    /******************************************************************/
    /*                    延时函数                                    */
    /******************************************************************/
    void Delay(unsigned int i)//延时
    {
     while(--i);
    }
    /******************************************************************/
    /*                   主函数                                       */
    /******************************************************************/
    main()
    {
    
     unsigned char i;
    
    
      EX1=1;         //外部中断0开
      IT1=1;         //边沿触发
      EA=1;          //全局中断开
    
     while(!Flag)            
     {
      P0=0x71;//显示 F 标示正转
      for(i=0;i<4;i++)      //4相
         {
         P1=F_Rotation[i];  //输出对应的相 可以自行换成反转表格
         Delay(500);        //改变这个参数可以调整电机转速 ,数字越小,转速越大
         }
      }
    while(Flag)
     { 
      P0=0x7C;//显示 b 标示反转
      for(i=0;i<4;i++)      //4相
         {
         P1=B_Rotation[i];  //输出对应的相 
         Delay(500);        //改变这个参数可以调整电机转速 ,数字越小,转速越大
         }
      }
    }
    /******************************************************************/
    /*                   中断入口函数                                 */
    /******************************************************************/
    void ISR_Key(void) interrupt 2 using 1
    {
     Delay(300);
    
     Flag=!Flag;         //s3按下触发一次,标志位取反
    
     }

    最后介绍一下带停机的步进电机正反转的方法:

    #include <reg52.h>
    
    unsigned char Flag;//定义正反转和停止标志位
    sbit KEY = P3^3;
    
    
    unsigned char code F_Rotation[4]={0xf1,0xf2,0xf4,0xf8}; //正转表格
    unsigned char code B_Rotation[4]={0xf8,0xf4,0xf2,0xf1}; //反转表格
    /******************************************************************/
    /*                    延时函数                                    */
    /******************************************************************/
    void Delay(unsigned int i)//延时
    {
     while(--i);
    }
    /******************************************************************/
    /*                   主函数                                       */
    /******************************************************************/
    main()
    {
    
     unsigned char i;
    
    
      EX1=1;         //外部中断0开
      IT1=1;         //边沿触发
      EA=1;          //全局中断开
    
     while(Flag==0)            
     {
      P0=0x71;//显示 F 标示正转
      for(i=0;i<4;i++)      //4相
         {
         P1=F_Rotation[i];  //输出对应的相 可以自行换成反转表格
         Delay(500);        //改变这个参数可以调整电机转速 ,数字越小,转速越大
         }
      }
    while(Flag==1)
     { 
      P0=0x7C;//显示 b 标示反转
      for(i=0;i<4;i++)      //4相
         {
         P1=B_Rotation[i];  //输出对应的相 
         Delay(500);        //改变这个参数可以调整电机转速 ,数字越小,转速越大
         }
      }
    while(Flag==2)  //停止
     { 
      P0=0x6D;//   显示 S
      P1=0;
      }
    }
    /******************************************************************/
    /*                   中断入口函数                                 */
    /******************************************************************/
    void ISR_Key(void) interrupt 2 using 1
    {
     Delay(500);
     if(!KEY)
     {
    
     Flag++;         //s3按下触发一次
     if(Flag==3)
       Flag=0;
      }
     }
    展开全文
  • 单片机控制步进电机

    万次阅读 多人点赞 2019-06-06 19:07:38
    单片机控制步进电机正转 反转 加速 减速;由LCD1602实时显示步进电机的状态;F-正转,B-反转;数字越大,转速越大; 仿真原理图如下: MCU和LCD1602显示模块: ULN2803驱动和步进电机模块: C语言代码如下...

    简介:
    用单片机控制步进电机正转 反转 加速 减速;
    由LCD1602实时显示步进电机的状态;F-正转 B-反转;数字越大,转速越大;
    仿真原理图如下:
    MCU和LCD1602显示模块:
    在这里插入图片描述
    ULN2803驱动和步进电机模块:
    在这里插入图片描述
    C语言代码如下:

    /*-----------------------------
    FileName: StepperMotor.h
    Function: 函数头文件
    Author: Zhang Kaizhou
    Date: 2019-6-6 17:59:39
    ------------------------------*/
    #include <reg52.h>
    #include <string.h>
    #define uchar unsigned char
    #define uint unsigned int
    #define Factor 5 // 转速控制常数	
    
    /*LCD1602端口设置*/
    sbit lcdrs = P1^0;
    sbit lcdrw = P1^1;
    sbit lcden = P1^2;
    
    /*步进电机驱动器端口设置*/
    sbit direcChange = P1^3; // 方向翻转
    sbit speedUp = P1^4; // 加速
    sbit slowDown = P1^5; // 减速
    
    /*主函数声明*/
    void keyScan();
    void execute();
    
    /*LCD1602显示相关函数声明*/
    void LCDInit();
    void displayInit();
    void display(uchar oper, uchar dat);
    void writeCommand(uchar command);
    void writeData(uchar dat);
    void delay(uchar xms);
    
    /*-------------------------------------------
    FileName:main.c
    Function: MCU控制步进电机
    Description:控制步进电机正转 反转 加速 减速;
    由LCD1602实时显示步进电机的状态;
    F-正转 B-反转;数字越大,转速越大;
    ---------------------------------------------
    Author: Zhang Kaizhou
    Date: 2019-6-6 17:56:41
    -------------------------------------------*/
    #include "StepperMotor.h"
    
    uchar code pulseTable0[] = {0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x04, 0x08}; // 一相励磁(同一时刻只有一个线圈通电,旋转角1.8度)
    uchar code pulseTable1[] = {0x0c, 0x06, 0x03, 0x09, 0x09, 0x03, 0x06, 0x0c}; // 二相励磁(同一时刻有两个线圈通电,旋转角1.8度)
    uchar code pulseTable2[] = {0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09,
    						    0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08}; // 一-二相励磁场(一二相交替励磁,旋转角0.9度)
    uchar speed = 0, startPos = 0; // 默认正转
    bit oper = 0/*操作数*/, direcFlag = 0; // 初始状态为正向
    
    void main(){
    	LCDInit(); // LCD1602显示初始化
    	displayInit();
    	while(1){
    		keyScan(); // 按键扫描
    		display(0, direcFlag);
    		display(1, speed);
    		execute();
    	}
    }
    
    /*按键扫描*/
    void keyScan(){
    	if(!speedUp){ // 加速
    		delay(5);
    		if(!speedUp){
    			if(speed < 4){
    				while(!speedUp);
    				speed++;
    			}else{
    				while(!speedUp);
    				speed = 3;
    			}
    		}
    	}
    	if(!slowDown){ // 减速
    		delay(5);
    		if(!slowDown){
    			if(speed != 0){
    				speed--;
    			}else{
    				while(!slowDown);
    				speed = 0;
    			}
    		}
    	}
    	if(!direcChange){ // 方向翻转
    		delay(5);
    		if(!direcChange){
    			while(!direcChange);
    			direcFlag = ~direcFlag;
    		}
    	}
    }
    
    /*步进电机控制执行函数*/
    void execute(){
    	uchar i, j;
    	startPos = (direcFlag == 0) ? 0 : 4; // 方向控制
    	for(i = startPos; i <= (startPos + 4); i++){
    		P2 = pulseTable0[i];
    		for(j = 0; j < (speed + 1) * Factor; j++){ // 用延时来控制脉冲输出的频率,从而控制步进电机转速
    			delay(10);
    		} 
    	}
    }
    
    /*-----------------------------
    FileName:display.c
    Function: LCD1602显示函数
    Author: Zhang Kaizhou
    Date: 2019-6-6 17:58:42
    ------------------------------*/
    #include "StepperMotor.h"
    
    uchar code table0[] = {"Direction:"}; // 每行的字符数据
    uchar code table1[] = {"Speed:"};
    uchar code table2[] = {"1234"};
    uchar code table3[] = {"FB"}; // F-正向 B-反向
    
    /*初始化LCD1602的设置*/
    void LCDInit(){
    	lcden = 0; // 拉低使能端,准备产生使能高脉冲信号
    	writeCommand(0x38); // 显示模式设置(16x2, 5x7点阵,8位数据接口)
    	writeCommand(0x0c); // 开显示,不显示光标
    	writeCommand(0x06); // 写一个字符后地址指针自动加1
    	writeCommand(0x01); // 显示清零,数据指针清零
    }
    
    /*LCD上电界面*/
    void displayInit(){
    	uchar i;
    	writeCommand(0x80); // 将数据指针定位到第一行首
    	for(i = 0; i < strlen(table0); i++){
    		writeData(table0[i]);
    		delay(5);
    	}
    	
    	writeCommand(0x80 + 0x40); // 将数据指针定位到第二行首
    	for(i = 0; i < strlen(table1); i++){
    		writeData(table1[i]);
    		delay(5);
    	}
    }
    
    /*LCD显示函数*/
    void display(uchar oper, uchar dat){
    	if(oper == 0){ // 方向显示
    		if(dat == 0){ // 正向
    			writeCommand(0x80 + strlen(table0)); // 数据指针定位到第一行空闲处
    			writeData(table3[0]);
    		}else if(dat == 1){ // 反向
    			writeCommand(0x80 + strlen(table0)); // 数据指针定位到第一行空闲处
    			writeData(table3[1]);
    		}
    	}
    	if(oper == 1){ // 速度显示
    		writeCommand(0x80 + 0x40 + strlen(table1)); // 数据指针定位到第二行空闲处
    		writeData(table2[dat]);
    	}
    }
    
    /*写指令函数*/
    void writeCommand(uchar command){
    	lcdrs = 0; // 命令选择
    	lcdrw = 0;
    	P0 = command;
    	delay(5);
    	
    	lcden = 1; // 产生一个正脉冲使能信号
    	delay(5);
    	lcden = 0;
    }
    
    /*写数据函数*/
    void writeData(uchar dat){
    	lcdrs = 1; // 数据选择
    	lcdrw = 0;
    	P0 = dat;
    	delay(5);
    	
    	lcden = 1;
    	delay(5);
    	lcden = 0;
    }
    
    /*延时函数*/
    void delay(uchar xms){
    	uint i, j;
    	for(i = xms; i > 0; i--)
    		for(j = 110; j > 0; j--);
    }
    
    展开全文
  • 单片机-控制-步进电机

    千次阅读 多人点赞 2018-08-18 23:01:57
    开始浏览正文之前,请大家先花几分钟看完这段...通俗一点讲:当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(即步进角),是现代数字程序控制系统中的主要执行元件,应用极为...

    开始浏览正文之前,请大家先花几分钟看完这段视频:

    步进电机是如何工作的


    步进电机(stepping motor)

    步进电机

    步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件,是一种将电脉冲转化为角位移的执行机构。通俗一点讲:当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(即步进角),是现代数字程序控制系统中的主要执行元件,应用极为广泛。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。

    在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响。

    当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度,称为”步距角”,它的旋转是以固定的角度一步一步运行的。

    可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。

    步进电机是一种感应电机,它的工作原理是利用电子电路,将直流电变成分时供电的多相时序控制电流,用这种电流为步进电机供电,步进电机才能正常工作,驱动器就是为步进电机分时供电的多相时序控制器。

    虽然步进电机已被广泛地应用,但步进电机并不能像普通的直流电机、交流电机在常规下使用。它必须由双环形脉冲信号、功率驱动电路等组成控制系统方可使用。因此用好步进电机却非易事,它涉及到机械、电机、电子及计算机等许多专业知识。

    步进电机作为执行元件,是机电一体化的关键产品之一,广泛应用在各种自动化控制系统中。随着微电子和计算机技术的发展,步进电机的需求量与日俱增,在各个国民经济领域都有应用。

    步进电机又称为脉冲电机,基于最基本的电磁铁原理,它是一种可以自由回转的电磁铁,其动作原理是依靠气隙磁导的变化来产生电磁转矩。

    由于步进电机是一个把电脉冲转换成离散的机械运动的装置,具有很好的数据控制特性,因此,计算机成为步进电机的理想驱动源,随着微电子和计算机技术的发展,软硬件结合的控制方式成为了主流,即通过程序产生控制脉冲,驱动硬件电路。单片机通过软件来控制步进电机,更好地挖掘出了电机的潜力。因此,用单片机控制步进电机已经成为了一种必然的趋势,也符合数字化的时代趋势。


    步进电机和伺服电机、舵机有何区别?

    伺服电机不仅仅只包含一个电机,通常是包含电机、传感器和控制器的电机系统(具体可以参考伺服电机/舵机 控制)。
    舵机是个俗称,适用于航模上,其实是一种低端的但最常见的伺服电机系统,价格低廉但精度较低。


    单片机控制示例

    51单片机按键控制单反转仿真

    相信通过视频及文字内容,大家对步进电机相关原理已经非常熟悉。下面通过Arduino Pro MINI开发板进行一项简易的步进电机控制实验。
    步进电机内部结构
    步进电机(Stepper/Step/Stepping Motor),主要是依靠定子线圈序列通电,顺次在不同的角度形成磁场,推拉定子旋转。接触步进电机时会有很多容易混淆的概念。比如单极性、双极性、两相八线、四相八线等等。主要是由于线圈的接法不同,我们先简单地辩析一下:按照电机驱动架构可分为单极性 (unipolar) 和双极性 (bipolar) 步进电机。所谓的极性,就是电流通过线圈绕组产生磁场的极性,单极性就是只有一个磁极,双极就是有两个磁极。四相,八相是指步进电机的相数,即步进电机内部的线圈组数。电机的相数不同,步进电机接收到每个脉冲信号的角度也不同。通过不同的极性,不同的相数,线圈接法会得到不同的电机性能。


    实验前要准备的元件
    - 28BYJ-48 五线四相步进电机
    - ULN2003驱动芯片
    - Arduino Pro MINI开发板
    - 杜邦线若干
    28BYJ-48,DC5V,五线四相步进电机


    示例代码一:

        可以直接在Arduino IDE的File> Examples> Stepper> MotorKnob里直接导入示例代码。
    

    Stepper.h是Arduino IDE自带的控制步进电机的标准库。

    电路原理图(电位计10K即可)
    实物连接示意图

        /*
         * MotorKnob
         *
         * A stepper motor follows the turns of a potentiometer
         * (or other sensor) on analog input 0.
         *
         * http://www.arduino.cc/en/Reference/Stepper
         * This example code is in the public domain.
         */
        #include <Stepper.h>
        // change this to the number of steps on your motor
        #define STEPS 100
        // create an instance of the stepper class, specifying
        // the number of steps of the motor and the pins it's
        // attached to
        Stepper stepper(STEPS, 8, 9, 10, 11);
        // the previous reading from the analog input
        int previous = 0;
        void setup() {
          // set the speed of the motor to 30 RPMs
          stepper.setSpeed(30);
        }
        void loop() {
          // get the sensor value
          int val = analogRead(0);
    
          // move a number of steps equal to the change in the
          // sensor reading
          stepper.step(val - previous);
    
          // remember the previous value of the sensor
          previous = val;
        }

    Stepper函数是用来创建步进电机实例的,共有如下两种用法:
    steps代表电机转一圈所用的步数。这个一般是步进电机出厂是就固定的。
    - 也可以通过步距角计算得出:
    转一圈的步数 = 360 / 步距角

                Stepper(steps, pin1, pin2)
                Stepper(steps, pin1, pin2, pin3, pin4)

    pin1,pin2,pin3,pin4是连接至步进电机的引脚。
    根据电机的引线数确定。pin3,pin4是可选的。

        setSpeed(rpms)函数是用来设置步进电机的速度,即每分钟该转的步数。
    
    • 根据实例代码,当转动电位器,步进电机也会转动一定角度。

    示例代码二:

    调用大神写好的库当然可以节约大量的开发时间,不过懂得原理也是非常有必要的。下面通过五线四相步进电机来进行简单说明:

    五线四相步进电机的工作方式
    `四相步进电机可以在:`
        - 四相单拍   : 通电顺序为 A-B-C-D
        - 四相双拍1  : 通电顺序为 AB-BC-CD-DA
        - 四相双拍2  : 通电顺序为 AC-BC-BD-DA
        - 四相八拍   : 通电顺序为 A-AB-B-BC-C-CD-D-DA-A
    `四种工作方式下工作。`
    




    下面依照8拍驱动方式编写相关代码:
        /* Main.ino file 
         * by: 禾灮Studios
         * 
         * Created:   周三 6月 13 2018
         * Processor: Arduino Uno
         * Compiler:  Arduino AVR (Proteus)
         */
        #define motor_pin_1 8       //A
        #define motor_pin_2 9       //B
        #define motor_pin_3 10      //C
        #define motor_pin_4 11      //D
        int shiJian =10;        //参数用于电机调速
    
    
        void steps_to_move_N(){     //逆时针旋转 八拍方式驱动,顺序为A AB B BC C CD D DA
            //1000  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian); 
            //1100  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            // 0100  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            // 0110  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            //0010  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            //0011  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
            //0001  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
            //1001  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
        }
        void steps_to_move_Z(){
            //1001  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
            //0001  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
            //0011  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, HIGH);
            delay(shiJian);
            //0010  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            // 0110  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            // 0100  
            digitalWrite(motor_pin_1, LOW);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            //1100  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian);
            //1000  
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, LOW);
            digitalWrite(motor_pin_3, LOW);
            digitalWrite(motor_pin_4, LOW);
            delay(shiJian); 
        }
    
        void setup(){  //初始化
            // put your setup code here, to run once:
            // setup the pins on the microcontroller:
            pinMode(motor_pin_1, OUTPUT);
            pinMode(motor_pin_2, OUTPUT);
            pinMode(motor_pin_3, OUTPUT);
            pinMode(motor_pin_4, OUTPUT);
    
            digitalWrite(motor_pin_1, HIGH);
            digitalWrite(motor_pin_2, HIGH);
            digitalWrite(motor_pin_3, HIGH);
            digitalWrite(motor_pin_4, HIGH); 
        }
        void loop(){   // put your main code here, to run repeatedly:
            steps_to_move_Z();
            // steps_to_move_N();
        } 

    示例代码三:

    下面的代码适用于Arduino系列,以及AVR单片机编程,修改后可直接用于其他单片机。
    本段代码是对示例代码二中函数的简化表述方式。

        /* Main.ino file 
         * by: 禾灮Studios
         * 
         * Created:   周三 6月 13 2018
         * Processor: Arduino Uno
         * Compiler:  Arduino AVR (Proteus)
         */
        #define motor_pin_1 8       //A
        #define motor_pin_2 9       //B
        #define motor_pin_3 10      //C
        #define motor_pin_4 11      //D
        int shiJian =10;        //参数用于电机调速   
        //定义八拍方式驱动,顺序为A-AB-B-BC-C-CD-D-DA
        unsigned char clockWise[]={0x01,0x03,0x02,0x06,0x04,0x0c,0x08,0x0d}; 
    
            void zzz(){    //顺时针旋转
              for(int i=0;i<8;i++){
                PORTB = clockWise[i];
                delay(shiJian);
              }
            }
            void fff(){     //逆时针旋转
              for(int i=0;i<8;i++){
                PORTB = clockWise[8-i];
                delay(shiJian);
              } 
            }
    
        void setup(){  //初始化
            // put your setup code here, to run once:
            // setup the pins on the microcontroller:
            DDRB=0xFF;    //端口B设置为输出
            PORTB=0xFF;   //端口B初始值设置为1
        }
        void loop(){   // put your main code here, to run repeatedly:
            zzz();       
            fff();
        }

    实物连接


    以上内容就是控制步进电机的几种简易方法,各有优缺点大家根据自己的喜好和需要进行选择。不足之处还请大家批评指正。


        感谢一直关注着禾灮成长进步的朋友们。你们的信任、支持和鼓励,鞭策着我们一路走到了今天。
    
        感谢所有的合作伙伴,我们相互促进,共同见证了彼此的成长。
    
        感谢所有曾经在禾灮彼此倚靠、相互鼓励、携手同心、砥砺同行的兄弟姐妹。这里承载了我们的青春与热血。
    
                    禾灮,感谢有你。
    
        未来,我们将一如既往,砥砺前行。
    
                                            禾灮·小楊
                                           2018.07.05
    

    展开全文
  • 基于STM32F4单片机步进电机控制(有代码)

    万次阅读 多人点赞 2019-01-05 11:13:08
    步进电机是将电脉冲控制信号转变为角位移或线位移的一种常用的数字控制执行元件,又称为脉冲电机。在驱动电源的作用下,步进电机受到脉冲的控制,其转子的角位移量和速度严格地与输入脉冲的数量和脉冲频率成正比。...

    步进电机简介

    步进电机是将电脉冲控制信号转变为角位移或线位移的一种常用的数字控制执行元件,又称为脉冲电机。在驱动电源的作用下,步进电机受到脉冲的控制,其转子的角位移量和速度严格地与输入脉冲的数量和脉冲频率成正比。步进电机每接收一个电脉冲,转子就转过一个相应的角度(步距角)。**改变通电顺序可改变步进电动机的旋转方向;改变通电频率可改变步进电动机的转速。**因此,通过控制输入电脉冲的数目、频率及电动机绕组的通电顺序就可以获得所需要的转角、转速及转向,利用单片机就可以很容易实现步进电机的开环数字控制。
    传统的步进电机控制方法是由触发器产生控制脉冲来进行控制的,但此种控制方法工作方式单一而且难于实现人机交互,当步进电机的参数发生变化时,需要重新进行控制器的设计。因此适合于单片机控制,单片机通过向步进电机驱动电路发送控制信号就能实现对步进电机的控制。


    PWM调速方法

    在步进电机控制系统中可以通过输人PWM波的方法来对步进电动的运动进行控制。PWM波的产生可以通过时钟频率、自动重装值等参数进行设置,从而调节PWM波的占空比和输出频率。
    脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制, PWM 原理如图:这里写图片描述
    我们假定定时器工作在向上计数 PWM模式,且当 CNT小于CCRx 时,输出 0,当 CNT大于等于CCRx 时输出 1。那么就可以得到如上的 PWM示意图:当 CNT 值小于 CCRx 的时候, IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。,
    通过控制脉冲占空比来改变电机的电枢电压.改变占空比的方法有3种:(1)定宽调频法,这种方法是保持t1不变,只改变t2,这样周期(T或频率)也随之改变;(2)调宽调频法,保持t1不变,而改变t2,这样也使周期T(或频率)改变;(3)定频调宽法,这种方法是使周期(T或频率)不变,而同时改变t1和t2.由于前两利,方法都改变了周期(或频率),当控制频率与系统的固有频率接近时,将会引起振荡,用的比较少,因此本系统用的是定频调宽法.在脉冲作用下,当电机通电时,速度增加.电机断电时,速度逐渐减小.只要按一定规律,改变通断电时间,即可实现对电机的转速控制。


    系统硬件电路

    系统硬件电路设计框图如下:
    这里写图片描述

    通过对STM32F4单片机编写程序实现对步进电机的控制,并且可以利用计算机和单片机的串口通信,接收到单片机所反馈回来的控制数据,包括:**步进电机的正向转动、反向转动、步进电机的定位功能以及调速功能。**要实现上述功能需要对STM32F4的以下模块进行设置,主要包括:串口通信模块、按键输入模块、电机驱动模块三大部分。下面就以重要模块的实现过程来进行详细的论述。

    串口通信模块

    串口作为 MCU 的重要外部接口,同时也是软件开发重要的调试手段, 其重要性不言而喻。现在基本上所有的 MCU 都会带有串口, STM32 自然也不例外。STM32F4 的串口资源相当丰富的,功能也相当强劲。 ALIENTEK 探索者 STM32F4 开发板所使用的 STM32F407ZGT6 最多可提供 6 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、 支持调制解调器操作、 智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。
    处理器与外部设备通信的两种方式:
    并行通信:
    -传输原理:数据各个位同时传输。
    -优点:速度快
    -缺点:占用引脚资源多

    串行通信:
    -传输原理:数据按位顺序传输。
    -优点:占用引脚资源少
    -缺点:速度相对较慢
    这里我们选用串行通信。串行通信按照数据传送方向,分为:
    单工
    数据传输只支持数据在一个方向上传输
    半双工
    允许数据在两个方向上传输,但是,在某一时刻,只允许数
    据在一个方向上传输,它实际上是一种切换方向的单工通信;
    全双工
    允许数据同时在两个方向上传输,因此,全双工通信是两个
    单工通信方式的结合,它要求发送设备和接收设备都有独立
    的接收和发送能力。 这里写图片描述
    这里我们给出串口配置的一般步骤:
    ①串口时钟使能:RCC_APBxPeriphClockCmd();
    GPIO时钟使能:RCC_AHB1PeriphClockCmd();
    ② 引脚复用映射:
    GPIO_PinAFConfig();
    ③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF
    ④串口参数初始化:USART_Init();
    ⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
    NVIC_Init();
    USART_ITConfig();
    ⑥使能串口:USART_Cmd();
    ⑦编写中断处理函数:USARTx_IRQHandler();
    ⑧串口数据收发:
    void USART_SendData();//发送数据到串口,DR
    uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
    ⑨串口传输状态获取:
    FlagStatus USART_GetFlagStatus();
    void USART_ClearITPendingBit();

    相关代码如下:

    void uart_init(u32 bound) //GPIO端口设置 
    { 
    GPIO_InitTypeDef GPIO_InitStructure; 
    USART_InitTypeDef USART_InitStructure; 
    NVIC_InitTypeDef NVIC_InitStructure; 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIO时钟 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1 
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //使能GPIOA9时钟 
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //使能GPIOA10时钟 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHZ 
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用功能 
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 
    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 
    USART_InitStructure.USART_BaudRate = bound;//波特率设置 
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 
    USART_Init(USART1, &USART_InitStructure); //初始化串口1 
    USART_Cmd(USART1, ENABLE); //使能串口1 
    #if EN_USART1_RX 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 
    } 
    
    void USART1_IRQHandler(void)                	//串口1中断服务程序
    {
    	u8 Res;
    #if SYSTEM_SUPPORT_OS 	
    //如果SYSTEM_SUPPORT_OS为真,则需要支持OS
    	OSIntEnter();    
    #endif
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接受到的数据必须是0x0d 0x0a结尾)
    	{
    		Res =USART_ReceiveData(USART1);//(USART1->DR);	//(USART1->DR);//读取接收到的数据
    		
    		if((USART_RX_STA&0x8000)==0)//接收未完成
    		{
    			if(USART_RX_STA&0x4000)//接收到了0x0d
    			{
    				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    				else USART_RX_STA|=0x8000;	//接收完成了
    			}
    			else //还没收到0X0D
    			{	
    				if(Res==0x0d)USART_RX_STA|=0x4000;
    				else
    				{
    					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    					USART_RX_STA++;
    					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收  
    				}		 
    			}
    		}   		 
      } 
    #if SYSTEM_SUPPORT_OS 	
    //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntExit();  											 
    #endif
    } 
    #endif	
    

    按键输入模块

    STM32F4开发板上的按键 KEY0 连接在 PE4 上、 KEY1 连接在 PE3 上、 KEY2 连接在 PE2 上、 KEY_UP连接在 PA0 上。 如图所示:
    这里写图片描述
    在此次实验中,我们设置按下 KEY-UP, 电机以 所设定频率回到绝对原点; 按下 KEY0, 电机以所设定频率顺时针转动; 按下 KEY1, 电机以所设定频率逆时针转动。
    下面给出实现按键输入的一般步骤:
    ①使能按键对应IO口时钟。调用函数:
    RCC_AHB1PeriphClockCmd ();
    ②初始化IO模式:上拉/下拉输入。调用函数:
    GPIO_Init();
    ③扫描IO口电平(库函数/寄存器/位操作)。
    相关代码如下:

    void KEY_Init(void)
    {
      GPIO_InitTypeDef  GPIO_InitStructure;
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA,GPIOE时钟
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //KEY0 KEY1 KEY2对应引脚
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIO2,3,4
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
      GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
     } 
    
    u8 KEY_Scan(u8 mode)
    {	 
    	static u8 key_up=1;//按键松开标志
    	if(mode)key_up=1;  //支持连接		  
    	if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
    	{
    		delay_ms(10);//去抖动
    		key_up=0;
    		if(KEY0==0)return 1;
    		else if(KEY1==0)return 2;
    		else if(KEY2==0)return 3;
    		else if(WK_UP==1)return 4;
    	}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1; 	    
     	return 0;// 无按键按下
    }
    

    电机驱动模块

    电源与驱动器连接方法如下图:这里写图片描述
    驱动器与STM32F4连接如下图:
    这里写图片描述
    代码需要用到的 4 个主要函数如下:
    void Driver_Init(void);//驱动器初始化
    void TIM8_OPM_RCR_Init(u16 arr, u16 psc); //TIM8_CH2 初始化 单脉冲+重复计数模式
    void Locate_Rle(long num, u32 frequency, DIR_Type dir) //相对定位函数
    void Locate_Abs(long num, u32 frequency);/绝对定位函数
    1)驱动器初始化函数,主要就是初始化与驱动器 ENA+,DIR+相连的 2 个 IO为推挽输出。
    2) TIM8_CH2 初始化, 此例程产生脉冲所使用的定时器均是 TIM8_CH2(PC7) ,定时器工作在单脉冲+重复计数模式,需要注意的是定时器必须初始化为 1MHz 计数频率。
    3) 相对定位函数: 在步进电机当前位置基础上顺时针(CW)或者逆时针(CCW)走 num 个脉冲, 此函数带方向控制, DIR_Type 是 driver.h 下声明的一个枚举类型,用于设置电机旋转方向,参数 dir=CW,电机顺时针旋转; dir=CCW,电机逆时针旋转。
    绝对定位函数:步进电机按设定频率转动到设置的绝对位置, 开发板上电和复位时,当前位置为 0,电机的当前位置用一个 long 型变量 current_pos 指示。 在current_pos=0 的基础上顺时针转动后 current_pos 为正, 否则为负。 5) 此例程配置了 usmart 函数和按键函数,可以通过按键或者串口调用相对定位函数和绝对定位函数控制驱动器,从而控制步进电机。
    驱动模块代码如下:

    u8 rcr_remainder;   //重复计数余数部分
    u8 is_rcr_finish=1; //重复计数器是否设置完成
    long rcr_integer;	//重复计数整数部分
    long target_pos=0;  //有符号方向
    long current_pos=0; //有符号方向
    DIR_Type motor_dir=CW;//顺时针
    

    驱动控制信号线初始化:

    void Driver_Init(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStructure;
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOE时钟
     	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6; //DRIVER_DIR DRIVER_OE对应引脚
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    	GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE5,6
    		GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE5输出高 顺时针方向  DRIVER_DIR
    	GPIO_ResetBits(GPIOE,GPIO_Pin_6);//PE6输出低 使能输出 DRIVER_OE
    }
    

    单脉冲+重复计数模式:

    void TIM8_OPM_RCR_Init(u16 arr,u16 psc)
    {		 					 
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	TIM_OCInitTypeDef  TIM_OCInitStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);  	//TIM8时钟使能    
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 	//使能PORTC时钟	                                                                     	
    	GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM8); //GPIOC7复用为定时器8
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;           //GPIOC7
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;      //下拉
    	GPIO_Init(GPIOC,&GPIO_InitStructure);               //初始化PF9
    	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动自动重装载寄存器周期的值
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMX时钟频率除数的预分频值
    	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割TDTS = Tck_tim
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //根据TIM_TimeBaseStructure中指定的参数化TIMX的时间基数单位
    	TIM_ClearITPendingBit(TIM8,TIM_IT_Update);
    	TIM_UpdateRequestConfig(TIM8,TIM_UpdateSource_Regular); /********* 设计只有计数溢出作为更新中断 ********/
    	TIM_SelectOnePulseMode(TIM8,TIM_OPMode_Single);/******* 单脉冲模式**********/
     	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出2使能
    	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; /****** 比较输出2N使能 *******/
    	TIM_OCInitStructure.TIM_Pulse = arr>>1; //设置待装入捕获比较寄存器的脉冲值
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    	TIM_OC2Init(TIM8, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMX
    	TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable);  //CH2预装载值 
    	TIM_ARRPreloadConfig(TIM8, ENABLE); //使能TIMX在ARR上的预装载值
    	TIM_ITConfig(TIM8, TIM_IT_Update ,ENABLE);  //TIM8   使能或者失能指定的TIM中断
     	NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;  //TIM8中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    	TIM_ClearITPendingBit(TIM8, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM中断源
    	TIM_Cmd(TIM8, ENABLE);  //使能TIM8											  
    }
    

    TIM8更新中断服务程序:

    void TIM8_UP_TIM13_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM8,TIM_FLAG_Update)!=RESET)//更新中断
    	{
    		TIM_ClearITPendingBit(TIM8,TIM_FLAG_Update);//清除更新中断标志位		
    		if(is_rcr_finish==0)//重复计数器未设置完成
    		{
    			if(rcr_integer!=0) //整数部分脉冲还未发送
    			{
    				TIM8->RCR=RCR_VAL;//设置重复计数值
    				rcr_integer--;//减少RCR_VAL+1个脉冲			
    			}else if(rcr_remainder!=0)//余数部分脉冲 不为0
    			{
    				TIM8->RCR=rcr_remainder-1;//设置余数部分
    				rcr_remainder=0;//清零
    				is_rcr_finish=1;//重复计数器设置完成				
    			}else goto out;   //rcr_remainder=0直接退出			 
    			TIM_GenerateEvent(TIM8,TIM_EventSource_Update);//产生一个更新事件 重新初始化计数器
    			TIM_CtrlPWMOutputs(TIM8,ENABLE);	//MOE主输出使能	
    			TIM_Cmd(TIM8, ENABLE);  //使能TIM8			
    			if(motor_dir==CW) //如果方向为顺时针 
    				current_pos+=(TIM8->RCR+1);//加上重复计数值
    			else          //否则方向为逆时针
    				current_pos-=(TIM8->RCR+1);//减去重复计数		
    		}else
    		{
    out:		is_rcr_finish=1;//重复计数器设置完成
    			TIM_CtrlPWMOutputs(TIM8,DISABLE);	//MOE 主输出关闭
    			TIM_Cmd(TIM8, DISABLE);  //关闭TIM8				
    			printf("µ±Ç°Î»ÖÃ=%ld\r\n",current_pos);//打印输出
    		}	
    	}
    }
    

    启动TIM8:

    void TIM8_Startup(u32 frequency)   //启动定时器8
    {
    	u16 temp_arr=1000000/frequency-1; 
    	TIM_SetAutoreload(TIM8,temp_arr);//设定自动重装值	
    	TIM_SetCompare2(TIM8,temp_arr>>1); //匹配值2等于重装值一半,是以占空比为50%
    	TIM_SetCounter(TIM8,0);//计数器清零
    	TIM_Cmd(TIM8, ENABLE);  //使能TIM8
    }
    

    相对定位函数:

    void Locate_Rle(long num,u32 frequency,DIR_Type dir) //相对定位函数
    {
    	if(num<=0) //数值小于等于0 则直接返回
    	{
    		printf("\r\nThe num should be greater than zero!!\r\n");
    		return;
    	}
    	if(TIM8->CR1&0x01)//上一次脉冲还未发送完成 直接返回
    	{
    		printf("\r\nThe last time pulses is not send finished,wait please!\r\n");
    		return;
    	}
    	if((frequency<20)||(frequency>100000))//脉冲频率不在范围之内 直接返回
    	{
    		printf("\r\nThe frequency is out of range! please reset it!!(range:20Hz~100KHz)\r\n");
    		return;
    	}
    	motor_dir=dir;//得到方向	
    	DRIVER_DIR=motor_dir;//设置方向
    		if(motor_dir==CW)//顺时针
    		target_pos=current_pos+num;//目标位置
    	else if(motor_dir==CCW)//逆时针
    		target_pos=current_pos-num;//目标位置
    	rcr_integer=num/(RCR_VAL+1);//重复计数整数部分
    	rcr_remainder=num%(RCR_VAL+1);//重复计数余数部分
    	is_rcr_finish=0;//重复计数器未设置完成
    	TIM8_Startup(frequency);//开启TIM8
    }
    

    绝对定位函数:

    void Locate_Abs(long num,u32 frequency)//绝对定位函数
    {
    	if(TIM8->CR1&0x01)//上一次脉冲还未发送完成 直接返回
    	{
    		printf("\r\nThe last time pulses is not send finished,wait please!\r\n");
    		return;
    	}
    	if((frequency<20)||(frequency>100000))//脉冲频率不在范围内 直接返回
    	{
    		printf("\r\nThe frequency is out of range! please reset it!!(range:20Hz~100KHz)\r\n");
    		return;
    	}
    	target_pos=num;//设置目标位置
    	if(target_pos!=current_pos)//目标和当前位置不同
    	{
    		if(target_pos>current_pos)
    			motor_dir=CW;//顺时针
    		else
    			motor_dir=CCW;//逆时针
    		DRIVER_DIR=motor_dir;//设置方向
    		
    		rcr_integer=abs(target_pos-current_pos)/(RCR_VAL+1);//重复计数整数部分
    		rcr_remainder=abs(target_pos-current_pos)%(RCR_VAL+1);//重复计数余数部分
    		is_rcr_finish=0;//重复计数器未设置完成
    		TIM8_Startup(frequency);//开启TIM8
    	}
    }
    

    最后是主函数:

    int main(void)
    { 
    	u8 i;
    	u8 keyval;
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    	delay_init(168);      //初始化延时函数
    	uart_init(115200);		//初始化串口波特率115200
    	usmart_dev.init(84); 	//初始化USMART			
    	LED_Init();					  //初始化LED
     	KEY_Init();					//初始化按键
    	Driver_Init();			//驱动器初始化
    	TIM8_OPM_RCR_Init(999,168-1); //1MHz计数频率 单脉冲+重复计数模式
    	while(1) 
    	{		 	  
    		keyval=KEY_Scan(0);
    		if(keyval==WKUP_PRES)
    		{
    			Locate_Abs(0,500);//按下WKUP,回到零点
    		}else if(keyval==KEY0_PRES)
    		{
    			Locate_Rle(1200,500,CW);//按下KEY0,以500Hz的频率 顺时针发200脉冲
    		}else if(keyval==KEY1_PRES)
    		{
    			Locate_Rle(1200,500,CCW);//按下KEY1,以500Hz频率 逆时针发400脉冲
    		}			
    		delay_ms(10);
    		i++;
    		if(i==50)	
    		{	
    			i=0;
    			LED1=!LED1;
    		}			
    	}
    }
    

    可以根据实际要求修改参数值以达到相应目的控制步进电机转动的转速和步距角。


    实验验证

    设置驱动器细分和电流, 完成硬件接线, 然后给开发板上电(USB232 接口), 打开 XCOM 调试助手,找到对应COM,设置波特率 115200,1 位停止位,8位数据位,无校验,勾上发送新行,然后打开串口,最后给驱动器上电。按下 KEY-UP, 电机以 500Hz 频率回到绝对原点; 按下 KEY0, 电机以 500Hz 频率顺时针走 200 个脉冲; 按下KEY1,电机以 500Hz 频率逆时针走 400 个脉冲;依次按下KEY_UP, KEY0, KEY1,串口打印的情况如下。
    这里写图片描述
    点击发送条目后边的数字就可调用该函数, 这儿的相对的定位函数 Locate_Rle 最后
    一个参数用于设置电机旋转方向的, 0(CCW) 表示逆时针方向, 1(CW) 表示顺时针方向,实验时, 如果电机旋转方向和设置方向相反, 只需更改 driver.h 下的CW=0,CCW=1;每调用一次函数,串口打印当前的位置。 然后我们依次调用这 4 个函数, 根据串口的打印, 可以看到当前位置的变化情况,如图所示。
    这里写图片描述


    结语

    本文介绍了利用单片机控制基于STM32F4芯片的步进电机定位控制系统的整体设计思路以及用pwm实现对步进电机控制的脉冲时序的分配进行了详细的仿真。利用STM32F4芯片严谨的基于硬件的编程语言和精确的时间控制特点,准确地实现了精确定位功能,对步进电机的运行速度的精确控制。实践证明,这种方法定位准确,控制速度精确,是一种行之有效的方案。


    1.此次实验所选用的的驱动器为TB6600步进电机专用驱动器。
    TB6600步进电机驱动器是一款专业的两相步进电机驱动,可实现正反转控制。通 过S1 S2 S3 3位拨码开关选择8 档细分控制(1、2、4、8、16 ),通过S4 S5 S6 3位拨码开关选择6档电流控制(0.5A,1A,1.5A,2.0A,2.5A,3.0A, 3.5A, 4.0A)。适合驱动86,57,42,39 型两相、四相混合式步进电机。驱动器具有 噪音小,震动小,运行平稳的特点。
    这里写图片描述
    TB6600步进电机专用驱动器电气参数如下表:
    这里写图片描述
    2.此次实验所选用的步进电机为57步进电机。其相关参数及尺寸如下。
    这里写图片描述

    展开全文
  • 利用单片机控制基于STM32F4芯片的步进电机定位控制系统的整体设计思路以及用pwm实现对步进电机控制的脉冲时序的分配进行了详细的仿真。利用STM32F4芯片严谨的基于硬件的编程语言和精确的时间控制特点,准确地实现了...
  • 该工程是在keil开发环境下c51单片机控制步进电机的控制程序,实现四相四拍、四相八拍的正转及反转
  • 51单片机控制TB6600驱动器驱动42步进电机

    千次阅读 多人点赞 2020-08-15 10:45:44
    51单片机控制TB6600驱动器驱动42步进电机功能实现目标硬件简介硬件连接程序代码功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个...
  • Atmega16单片机控制步进电机实现正反针功能,采用S型曲线进行加减速,模块化的写法,可以实现任意角度转动的步进电机程序,具体内容可以关注我的博客,里面有详细的讲解。
  • 51单片机步进电机实验

    千次阅读 2019-05-19 19:30:38
    1.步进电机图片 2.步进电机介绍 ...这一线性关系的存在,加上步进电机只有周期性的误差而无累积误差等特点,使得步进电机在速度、位置等控制领域的控制操作非常简单。虽然步进电机应用广泛,但它并不像...
  • 单片机控制步进电机-AVR详细程序

    千次阅读 2019-02-22 21:24:48
    单片机控制步进电机-单片机程序(avr) 硬件线路连接图见上一篇文章 软件: ICCV7 FOR AVR-写程序 Progisp-烧程序 速度S曲线生成器(后续后单独讲解)-生成S曲线数组代码 硬件: Atmega16 ASP下载线 杜邦...
  • 单片机控制步进电机-电路连接

    千次阅读 多人点赞 2019-02-22 21:26:14
    单片机控制步进电机-线路连接 说明:如何利用单片机去控制步进电机?本案例讲解的内容是硬件连接部分,采用常用的电子器件去实现单片机控制步进电机的功能。后续会分别讲解单片机程序,S曲线生成方法,上位机等...
  • 如何用51单片机控制步进电机运动

    万次阅读 多人点赞 2017-01-05 17:56:26
    本来接触单片机挺久了的,但是一直只是停留在非常初级的认识阶段,本科的时候上过几门课,但是从来没有自己捣鼓过单片机,这次突然来了兴趣,感觉一下子学到了好多东西,在这里好好整理一下。这篇文章只适合于入门...
  • 单片机控制步进电机和部分机器人电路图,接线图,代码
  • Arduino单片机控制步进电机

    千次阅读 2019-12-12 01:05:10
    步进电机是一种将电脉冲转化为角位移的执行机构。当步进电机的驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(即步长)。通过控制脉冲个数来控制角位移量,达到准确定位的目的;通过...
  • 尝试用51单片机控制步进电机,所能实现功能包括按键控制步进电机加减速及正反转。
  • 51单片机控制步进电机,光电开关控制电机正转反转停止,控制两轴电机,设置标志位,欢迎一起学习单片机。
  • at89c51单片机扩展芯片控制步进电机proteus仿真图,可进行正转反转和速度控制
  • 本设计采用51单片机控制42/57步进电机启动,并且使用双按键进行加减速控制,数码管显示速度,使用专门的驱动器驱动电机,可以细分。工作的源码。
  • 51单片机按键控制步进电机加减速及正反转

    万次阅读 多人点赞 2020-04-09 18:44:09
    之前尝试用单片机控制42步进电机正反转,电机连接导轨实现滑台前进后退,在这里分享一下测试程序及接线图,程序部分参考网上找到的,已经实际测试过,可以实现控制功能。 所用硬件:步进电机及驱动器、STC89C52...
  • 单片机控制步进电机是通过时钟计数器计算次数产生脉冲 T型加速转换到数学计算 计数器每秒钟计数c=1000000次 脉冲频率从 v0 = 4K 到v = 40K次 v0时每个脉冲需要计数 m0 =1M/4K = 250 v时每个需要计数m1 = 1M/40K/ 25...
1 2 3 4 5 ... 20
收藏数 1,852
精华内容 740
关键字:

单片机控制步进电机