STM32F103单片机使用ULN2003驱动步进电机

      最近整理东西,突然发现以前买的一个步进电机模块,以前使用5V单片机驱动的。突然想试试能不能用STM32F103单片机的3.3V直接驱动ULN2003这个芯片。

试了一下ULN2003可以用3.3V电压直接驱动。下面分享下电机驱动的过程。

先看看实物图

单片机使用的是STM32F103C8T6最小系统。电机和驱动板是网上买的整套模块。

驱动板的原理图如下

        步进电机使用的是5线4相直流减速步进电机   直径:28mm  电压:5V  步进角度:5.625 x 1/64  减速比:1/64

       步距角:5.625 / 64 = 0.087度 (也就是说理论上,你给一个脉冲,电机外部轴转动0.087度)也就是说一个脉冲内部转子转5.625度,但是由于减速64倍,所以外部轴只转了0.087度那么外部轴要转一圈的话,需要360/0.087=4096个脉冲。采用4相8拍驱动的话,8个脉冲是一个周期,那么就需要4096/8=512个周期,外部轴刚好转一圈。

    电机的驱动一般分为3种方法

    一.  1相励磁法:每一瞬间只有一个线圈相通,其它休息。

          (优点)简单,耗电低,精确性良好。

         (缺点)力矩小,振动大,每次励磁信号走的角度都是标称角度。  1相励磁法  A->B->C->D

   二、 2相励磁法:每一瞬间有两个线圈导通。

         (优点)力矩大,震动小。

         (缺点)每励磁信号走的角度都是标称角度。2相励磁法  AB->BC->CD->DA

   三、 1-2相励磁法:1相和2相交替导通。

           (优点)精度较高,运转平滑,每送一个励磁信号转动1/2标称角度,称为半步驱动。(前两种称为4相4拍,这一种称为4相8拍)

            1-2相励磁法  A-->AB-->B->BC->C-->CD->D-->DA

下面就用代码来实现这三种方式

       ULN2003这个小驱动板上IN1、IN2、IN3、IN4四个插针和单片机的 IO口直接用杜邦线连接,为了方便控制,首先定义好要使用的 IO口。就直接在LED的工程上修改。

       首先新建一个bsp_motor.h的头文件,在头文件里面进行端口声明。

#define LA    PBout(6) 			//A 相
#define LB    PBout(7) 			//B 相
#define LC    PBout(8) 			//C 相
#define LD    PBout(9) 			//D 相

/* 定义电机连接的GPIO端口, 用户只需要修改下面的代码即可改变控制电机的引脚 */
#define LA_GPIO_PORT    	GPIOB		                /* GPIO端口 */
#define LA_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LA_GPIO_PIN			GPIO_Pin_6

#define LB_GPIO_PORT    	GPIOB
#define LB_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LB_GPIO_PIN		    GPIO_Pin_7

#define LC_GPIO_PORT    	GPIOB
#define LC_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LC_GPIO_PIN		    GPIO_Pin_8

#define LD_GPIO_PORT    	GPIOB
#define LD_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LD_GPIO_PIN		    GPIO_Pin_9

       这里使用PB口的6、7、8、9四个端口来控制,端口使用了位带定义的方法,这样用程序控制起来更方便。关于位带定义可以自己查阅相关资料。端口和时钟都使用了宏定义重新命名。这样以后需要更改IO口的时候,只需要在头文件中更改,不需要更改程序中的代码,方便程序移植。  

      下面开始编写驱动代码,首先使用1相励磁法,也就是使4个IO口轮流为高电平,可以直接使用位带操作,依次给LA、LB、LC、LD给高电平。但是这样写的话每一种驱动方式都要写一个驱动函数,为了使操作起来更方便,将这4个口的值组成一个字节,并存在数组中,通过下标轮流调用数组中的数就行。这里将LA作为字节得最低位bit0,将LD作为字节第3位bit3。

       这样电机导通相序为  D-C-B-A时,定义数组中的值为

u8 phasecw[4] = {0x08, 0x04, 0x02, 0x01};

       电机导通相序为   A-B-C-D 是,数组中的值为

u8 phaseccw[4] = {0x01, 0x02, 0x04, 0x08};

下面新建bsp_motor.c文件,并在里面编写一个函数来调用这个数组就行了

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 4; i++ )
    {
        temp = phasecw[i];

        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 										
    }
}

        通过一个for循环,依次读取数组中的值,然后根据A、B、C、D四相的位置,去读取对应位的值,直接赋值给 IO口。这样电机驱动的函数就写好了,在主程序中直接调用这个函数就可以控制电机转动了。

       如果要改成 2相励磁法驱动的话,直接替换数组中的数据就行。

u8 phasecw[4] = {0x0c, 0x06, 0x03,0x09};   //正转 电机导通相序  DC-CB-BA-AD
u8 phaseccw[4] = {0x03, 0x06, 0x0c, 0x09}; //反转 电机导通相序  AB-BC-CD-DA

       如果要改成 1-2相励磁法,依然是直接修改数组中的值就行。

u8 phasecw[8] = {0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09}; //正转 电机导通相序  D-DC-C-CB-B-BA-A-AD
u8 phaseccw[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x89}; //反转 电机导通相序  A-AB-B-BC-C-CD-D-DA

      通过 1-2相励磁法控制的话,每次需要发送8个脉冲,也就是4相8拍,这样一个周期需要发送8个数组,要在for循环中将循环数量改为8。

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 8; i++ )
    {
        temp = phasecw[i];
        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 										
    }
}

然后在主程序中直接调用这个函数就可以让电机转起来了。

int main( void )
{
  
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
   
    Motor_GPIO_Config();
    while ( 1 )
    {      
      MotorCW();   
    }
}

这样看来驱动电机是很简单的用keil模拟的仿真波形

1相励磁法  D-C-B-A

2相励磁法 DC-CB-BA-AD

1-2相励磁法 D-DC-C-CB-B-BA-A-AD

波形和理论相符,说明驱动是正确的。

到这里应该就结束了,但是又手贱的给主程序加了个LED闪灯的程序,这一加出事了。

int main( void )
{
    u16 cnt = 0;
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    LED_GPIO_Config();
    Motor_GPIO_Config();
    while ( 1 )
    {
        LED1_TOGGLE;
        MotorCW();
	    delay_ms(20);
    }
}

主程序中加了一个20ms的延时,同时让LED灯翻转。程序烧进去之后,发现电机不转了。这是怎么回事?赶紧看看仿真波形。

       这一看发现了问题,A项的波形怎么高电平突然变长了?

u8 phasecw[4] = {0x08, 0x04, 0x02, 0x01};

        数组中依次给的值是8、4、2、1,也就是D、C、B、A相依次为高电平,其他三相高电平只持续了2ms,但是A相的电平持续了20多ms,这是什么原因?仔细分析程序执行流程后发现了问题所在。在for循环中依次给4个相赋值,A相位最后一个赋值,当给A项赋值时,其他3项为低电平,A项为高电平,然后程序退出for循环,回到主程序中,此时又执行了20ms的延时,这时A项的电平还是最后一次赋值的高电平。将这20ms延时去掉后,电机正常运转,波形也正常了。也就是说就是这个延时影响了电机的转动。

       那么主程序中就不能用延时了吗?LED闪烁功能都实现不了了吗?还能让一个电机驱动程序把单片机霸占了不成?这当然是不行了,一定要将这个问题的根源找到,既然是最后一次给A项赋值后,A项高电平没有复位,那么就手动让A项复位。强制让A项的电平归零。

    在增加一个复位的函数

void MotorStop( void )
{
    LA = 0;
    LB = 0;
    LC = 0;
    LD = 0;
}

      这个函数强制的将四相复位,然后在电机驱动的时候,每次执行完一个周期,就强制让这4项复位。

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 4; i++ )
    {
        temp = phasecw[i];

        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 													
    }
    MotorStop(); //一个周期转动结束后需要复位所有相,否则最后一项如果被设置为高电平后会持续维持高电平。
}

     增加复位功能后,主程序中依然加上20ms延时,继续仿真查看波形。

        这下波形正常了,下载程序后电机也缓慢转动起来了,LED灯也开始闪烁了。说明刚开始驱动时只想到了相位的逻辑,但是没有考虑到相位复位的问题,考虑问题还是不全面。

       下来在主程序中加上电机正反转功能,让电机正转一圈,然后又反转一圈。

int main( void )
{
    u16 cnt = 0;
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    LED_GPIO_Config();

    Motor_GPIO_Config();
    while ( 1 )
    {
        cnt++;

        if( cnt <= 512 )							//采用4相8拍,512个周期刚好是一圈
            MotorCW();								//正转
        else if( cnt <= 1024 )
            MotorCCW();								//反转
        else
        {
            cnt = 0;
            LED1_TOGGLE;
            delay_ms( 500 );
        }
    }
}

       这里采用4相8拍驱动,8个脉冲是一个周期,那么就需要4096/8=512个周期,外部轴刚好转一圈。计数器小于512时正转,计时器在512和1024之间时反转。然后LED灯翻转一次,在延时500ms。 这里还要注意一个问题,在电机驱动函数中,每个脉冲的延时时间经过实际测试不能小于2ms,延时太小的时候,电机抖动严重,但是不会转动。延时越大,电机转动越慢。

     到此电机驱动程序已经完美运行了。

      完整工程下载地址 STM32F103单片机+ULN2003驱动直流电机

版权声明:本文为CSDN博主「嵌入式@hxydj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_20222919/article/details/111604261

      最近整理东西,突然发现以前买的一个步进电机模块,以前使用5V单片机驱动的。突然想试试能不能用STM32F103单片机的3.3V直接驱动ULN2003这个芯片。

试了一下ULN2003可以用3.3V电压直接驱动。下面分享下电机驱动的过程。

先看看实物图

单片机使用的是STM32F103C8T6最小系统。电机和驱动板是网上买的整套模块。

驱动板的原理图如下

        步进电机使用的是5线4相直流减速步进电机   直径:28mm  电压:5V  步进角度:5.625 x 1/64  减速比:1/64

       步距角:5.625 / 64 = 0.087度 (也就是说理论上,你给一个脉冲,电机外部轴转动0.087度)也就是说一个脉冲内部转子转5.625度,但是由于减速64倍,所以外部轴只转了0.087度那么外部轴要转一圈的话,需要360/0.087=4096个脉冲。采用4相8拍驱动的话,8个脉冲是一个周期,那么就需要4096/8=512个周期,外部轴刚好转一圈。

    电机的驱动一般分为3种方法

    一.  1相励磁法:每一瞬间只有一个线圈相通,其它休息。

          (优点)简单,耗电低,精确性良好。

         (缺点)力矩小,振动大,每次励磁信号走的角度都是标称角度。  1相励磁法  A->B->C->D

   二、 2相励磁法:每一瞬间有两个线圈导通。

         (优点)力矩大,震动小。

         (缺点)每励磁信号走的角度都是标称角度。2相励磁法  AB->BC->CD->DA

   三、 1-2相励磁法:1相和2相交替导通。

           (优点)精度较高,运转平滑,每送一个励磁信号转动1/2标称角度,称为半步驱动。(前两种称为4相4拍,这一种称为4相8拍)

            1-2相励磁法  A-->AB-->B->BC->C-->CD->D-->DA

下面就用代码来实现这三种方式

       ULN2003这个小驱动板上IN1、IN2、IN3、IN4四个插针和单片机的 IO口直接用杜邦线连接,为了方便控制,首先定义好要使用的 IO口。就直接在LED的工程上修改。

       首先新建一个bsp_motor.h的头文件,在头文件里面进行端口声明。

#define LA    PBout(6) 			//A 相
#define LB    PBout(7) 			//B 相
#define LC    PBout(8) 			//C 相
#define LD    PBout(9) 			//D 相

/* 定义电机连接的GPIO端口, 用户只需要修改下面的代码即可改变控制电机的引脚 */
#define LA_GPIO_PORT    	GPIOB		                /* GPIO端口 */
#define LA_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LA_GPIO_PIN			GPIO_Pin_6

#define LB_GPIO_PORT    	GPIOB
#define LB_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LB_GPIO_PIN		    GPIO_Pin_7

#define LC_GPIO_PORT    	GPIOB
#define LC_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LC_GPIO_PIN		    GPIO_Pin_8

#define LD_GPIO_PORT    	GPIOB
#define LD_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LD_GPIO_PIN		    GPIO_Pin_9

       这里使用PB口的6、7、8、9四个端口来控制,端口使用了位带定义的方法,这样用程序控制起来更方便。关于位带定义可以自己查阅相关资料。端口和时钟都使用了宏定义重新命名。这样以后需要更改IO口的时候,只需要在头文件中更改,不需要更改程序中的代码,方便程序移植。  

      下面开始编写驱动代码,首先使用1相励磁法,也就是使4个IO口轮流为高电平,可以直接使用位带操作,依次给LA、LB、LC、LD给高电平。但是这样写的话每一种驱动方式都要写一个驱动函数,为了使操作起来更方便,将这4个口的值组成一个字节,并存在数组中,通过下标轮流调用数组中的数就行。这里将LA作为字节得最低位bit0,将LD作为字节第3位bit3。

       这样电机导通相序为  D-C-B-A时,定义数组中的值为

u8 phasecw[4] = {0x08, 0x04, 0x02, 0x01};

       电机导通相序为   A-B-C-D 是,数组中的值为

u8 phaseccw[4] = {0x01, 0x02, 0x04, 0x08};

下面新建bsp_motor.c文件,并在里面编写一个函数来调用这个数组就行了

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 4; i++ )
    {
        temp = phasecw[i];

        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 										
    }
}

        通过一个for循环,依次读取数组中的值,然后根据A、B、C、D四相的位置,去读取对应位的值,直接赋值给 IO口。这样电机驱动的函数就写好了,在主程序中直接调用这个函数就可以控制电机转动了。

       如果要改成 2相励磁法驱动的话,直接替换数组中的数据就行。

u8 phasecw[4] = {0x0c, 0x06, 0x03,0x09};   //正转 电机导通相序  DC-CB-BA-AD
u8 phaseccw[4] = {0x03, 0x06, 0x0c, 0x09}; //反转 电机导通相序  AB-BC-CD-DA

       如果要改成 1-2相励磁法,依然是直接修改数组中的值就行。

u8 phasecw[8] = {0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09}; //正转 电机导通相序  D-DC-C-CB-B-BA-A-AD
u8 phaseccw[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x89}; //反转 电机导通相序  A-AB-B-BC-C-CD-D-DA

      通过 1-2相励磁法控制的话,每次需要发送8个脉冲,也就是4相8拍,这样一个周期需要发送8个数组,要在for循环中将循环数量改为8。

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 8; i++ )
    {
        temp = phasecw[i];
        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 										
    }
}

然后在主程序中直接调用这个函数就可以让电机转起来了。

int main( void )
{
  
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
   
    Motor_GPIO_Config();
    while ( 1 )
    {      
      MotorCW();   
    }
}

这样看来驱动电机是很简单的用keil模拟的仿真波形

1相励磁法  D-C-B-A

2相励磁法 DC-CB-BA-AD

1-2相励磁法 D-DC-C-CB-B-BA-A-AD

波形和理论相符,说明驱动是正确的。

到这里应该就结束了,但是又手贱的给主程序加了个LED闪灯的程序,这一加出事了。

int main( void )
{
    u16 cnt = 0;
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    LED_GPIO_Config();
    Motor_GPIO_Config();
    while ( 1 )
    {
        LED1_TOGGLE;
        MotorCW();
	    delay_ms(20);
    }
}

主程序中加了一个20ms的延时,同时让LED灯翻转。程序烧进去之后,发现电机不转了。这是怎么回事?赶紧看看仿真波形。

       这一看发现了问题,A项的波形怎么高电平突然变长了?

u8 phasecw[4] = {0x08, 0x04, 0x02, 0x01};

        数组中依次给的值是8、4、2、1,也就是D、C、B、A相依次为高电平,其他三相高电平只持续了2ms,但是A相的电平持续了20多ms,这是什么原因?仔细分析程序执行流程后发现了问题所在。在for循环中依次给4个相赋值,A相位最后一个赋值,当给A项赋值时,其他3项为低电平,A项为高电平,然后程序退出for循环,回到主程序中,此时又执行了20ms的延时,这时A项的电平还是最后一次赋值的高电平。将这20ms延时去掉后,电机正常运转,波形也正常了。也就是说就是这个延时影响了电机的转动。

       那么主程序中就不能用延时了吗?LED闪烁功能都实现不了了吗?还能让一个电机驱动程序把单片机霸占了不成?这当然是不行了,一定要将这个问题的根源找到,既然是最后一次给A项赋值后,A项高电平没有复位,那么就手动让A项复位。强制让A项的电平归零。

    在增加一个复位的函数

void MotorStop( void )
{
    LA = 0;
    LB = 0;
    LC = 0;
    LD = 0;
}

      这个函数强制的将四相复位,然后在电机驱动的时候,每次执行完一个周期,就强制让这4项复位。

void MotorCW( void )
{
    u8 i;
    u8 temp = 0;
    for( i = 0; i < 4; i++ )
    {
        temp = phasecw[i];

        LD = ( temp >> 3 ) & 0x01;							//取bit4的值
        LC = ( temp >> 2 ) & 0x01;
        LB = ( temp >> 1 ) & 0x01;
        LA = ( temp >> 0 ) & 0x01;							//取bit0的值
        delay_ms( 2 ); 													
    }
    MotorStop(); //一个周期转动结束后需要复位所有相,否则最后一项如果被设置为高电平后会持续维持高电平。
}

     增加复位功能后,主程序中依然加上20ms延时,继续仿真查看波形。

        这下波形正常了,下载程序后电机也缓慢转动起来了,LED灯也开始闪烁了。说明刚开始驱动时只想到了相位的逻辑,但是没有考虑到相位复位的问题,考虑问题还是不全面。

       下来在主程序中加上电机正反转功能,让电机正转一圈,然后又反转一圈。

int main( void )
{
    u16 cnt = 0;
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    LED_GPIO_Config();

    Motor_GPIO_Config();
    while ( 1 )
    {
        cnt++;

        if( cnt <= 512 )							//采用4相8拍,512个周期刚好是一圈
            MotorCW();								//正转
        else if( cnt <= 1024 )
            MotorCCW();								//反转
        else
        {
            cnt = 0;
            LED1_TOGGLE;
            delay_ms( 500 );
        }
    }
}

       这里采用4相8拍驱动,8个脉冲是一个周期,那么就需要4096/8=512个周期,外部轴刚好转一圈。计数器小于512时正转,计时器在512和1024之间时反转。然后LED灯翻转一次,在延时500ms。 这里还要注意一个问题,在电机驱动函数中,每个脉冲的延时时间经过实际测试不能小于2ms,延时太小的时候,电机抖动严重,但是不会转动。延时越大,电机转动越慢。

     到此电机驱动程序已经完美运行了。

      完整工程下载地址 STM32F103单片机+ULN2003驱动直流电机

版权声明:本文为CSDN博主「嵌入式@hxydj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_20222919/article/details/111604261

嵌入式@hxydj

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32F103单片机使用ULN2003驱动步进电机

最近整理东西,突然发现以前买的一个步进电机模块,以前使用5V单片机驱动的。突然想试试能不能用STM32F103单片机的3.3V直接驱动ULN2003这个芯片。 试了一下ULN2003可以用3.3V电压直接驱动。下面分

STM32F103使用DAC功能输出正弦波

在DAC的主要特征中可以看出,DAC内部带有波形发生器。 DAC 主要特征 2个DAC转换器:每个转换器对应1个输出通道8位或者12位单调输出12位模式下数据左对齐或者右对齐同步更新功能噪声波形生成三角波形生成双