2018-11-02 11:33:59 weixin_42670445 阅读数 14867
  • 直流电机步进电机-第1季第12部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第12个课程,主要讲解了直流电机和步进电机,其中步进电机是关键,通过学习让大家初步掌握步进电机相关的概念和时序,能够将时序转化为驱动程序以驱动步进电机。

    2674 人正在学习 去看看 朱有鹏

    之前尝试用单片机控制42步进电机正反转,电机连接导轨实现滑台前进后退,在这里分享一下测试程序及接线图,程序部分参考网上找到的,已经实际测试过,可以实现控制功能。

    所用硬件:步进电机及驱动器、STC89C52单片机、直流电源

1、硬件连接图

              

  • 注意:上图为共阳极接法,实际连接参考总体线路连接。
  • 驱动器信号端定义:

PUL+:脉冲信号输入正。( CP+ )

PUL-:脉冲信号输入负。( CP- )

DIR+:电机正、反转控制正。

DIR-:电机正、反转控制负。

EN+:电机脱机控制正。

EN-:电机脱机控制负。

  • 电机绕组连接

A+:连接电机绕组A+相。

A-:连接电机绕组A-相。

B+:连接电机绕组B+相。

B-:连接电机绕组B-相。

  • 电源连接

VCC:电源正端“+”

GND:电源负端“-”

注意:DC直流范围:9-32V。不可以超过此范围,否则会无法正常工作甚至损坏驱动器.

  • 总体线路连接

输入信号共有三路,它们是:①步进脉冲信号PUL+,PUL-;②方向电平信 号DIR+,DIR-③脱机信号EN+,EN-。输入信号接口有两种接法,可根据 需要采用共阳极接法或共阴极接法。

在这里我采用的是共阴极接法:分别将 PUL-,DIR-,EN-连接到控制系统的地端(接入单片机地端); 脉冲输入信号通过PUL+接入单片机(代码中给的P2^6脚),方向信号通过DIR+接入单片机(代码中给的P2^4脚),使能信号通过EN+接 入(不接也可,代码中未接,置空)。按键连接见代码,分别用5个按键控制电机启动、反转、加速、减速、正反转。

注意:接线时请断开电源,电机接线需注意不要错相,相内相间短路, 以免损坏驱动器。

2、代码

#include<reg51.h> 
#define MotorTabNum 5
unsigned char T0_NUM;
sbit K1 = P3^5;        // 启动
sbit K2 = P3^4;        // 反转
sbit K3 = P3^3;        // 加速
sbit K4 = P3^2;        // 减速
sbit K5 = P3^1;        //正反转

sbit FX      = P2^4;     // 方向
//sbit MotorEn = P2^5;     // 使能
sbit CLK     = P2^6;     // 脉冲

int table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};           

unsigned char g_MotorSt = 0;     // 
unsigned char g_MotorDir = 0;    // 
unsigned char MotorTab[7] = {12, 10, 8, 6, 4, 2,1};

signed char g_MotorNum = 0;

void delayms(xms);
void mDelay(unsigned int DelayTime);                
void T0_Init();

void KeyScan(void);



void main(void)
{
        T0_Init();            
 //       MotorEn = 0;     // 
        FX = 0;
        while(1)
        {
                KeyScan();              // 
        }


}

void T0_Init()
{
        TMOD = 0x01;
        TH0 = (65535-100)/256;  // 1ms
        TL0 = (65535-100)%256;
        EA = 1;
        ET0 = 1;
//        TR0 = 1; 

}

void T0_time() interrupt 1
{
//        TR0 = 0;
        TH0 = (65535-100)/256;   
        TL0 = (65535-100)%256;
        T0_NUM++;
        if(T0_NUM >= MotorTab[g_MotorNum])        // 
        {
                T0_NUM = 0;
                CLK=CLK^0x01;               //   
        }
//        TR0 = 1;
}         


//-----????---------------------
void KeyScan(void)
{
        if(K1 == 0)
        {
                delayms(10);   
                if(K1 == 0)
                {
                        g_MotorSt = g_MotorSt ^ 0x01;
                      //  MotorEn ^= 1;
                        TR0 = 1; 
						FX ^= 0;   //反转
                }
        }

        if(K2 == 0)
        {
                delayms(10);   //正转
                if(K2 == 0)
                {    
                        g_MotorDir = g_MotorDir ^ 0x01;
                        FX ^= 1;    //加速
                }
        }

        if(K3 == 0)  // 
        {
                delayms(5);   //加速
                if(K3 == 0)
                {   
                        g_MotorNum++;
                        if(g_MotorNum > MotorTabNum)
                                g_MotorNum = MotorTabNum;
                }
        }

        if(K4 == 0)  // 
        {
                delayms(5);   // 减速
                if(K4 == 0)
                {       
                    g_MotorNum--;
                    if(g_MotorNum < 0)
                    g_MotorNum = 0;
                }
        }

		if(K5 == 0)  // 
		{
				delayms(10);   // 正反转
				if(K5 == 0)
				{      
				    g_MotorSt = g_MotorSt ^ 0x01;
					g_MotorDir = g_MotorDir ^ 0x01;
                    MotorEn ^= 1;
                    TR0 = 1;
			        while(1)
					{
                       FX ^= 1;    //													        
                       delayms(90000);
					   FX ^= 0;    //
					   delayms(90000);
					}
				}
		}
}

void delayms(xms)//延时
{
         unsigned int x,y;
         for(x=xms;x>0;x--)
                 for(y=110;y>0;y--);
}

3、常见问题解答

  • 控制信号高于5v一定要串联电阻,否则可能会烧坏驱动器控制接口电路。
  • 接通电源后如果驱动器灯亮,但是无法控制电机旋转,考虑控制部分驱动能力不足或者驱动器所设置的驱动电流不够(我就遇到过这种情况,后来通过调高驱动器限制电流解决的此问题)。如果调高驱动电流步进电机仍无法转动,查看电路板上的按键有没有接对,程序中按键引脚可根据电路板设计的按键引脚连接自行改动。
  • 判断步进电机四条线的定义:将任意两条线接在一起,用手旋转电机,如果有阻力,则两条线是同一相。用相同方法测试另外两条线是否是同一相。确定同相的两条线任意接入两相接口,如果旋转方向相反只需换相即可。
  • 如需调试或增加功能可联系QQ:1932900294。
  • 增加启停及加减速功能代码:https://download.csdn.net/download/weixin_42670445/11978165
2019-02-21 22:12:28 LuDanTongXue 阅读数 2451
  • 直流电机步进电机-第1季第12部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第12个课程,主要讲解了直流电机和步进电机,其中步进电机是关键,通过学习让大家初步掌握步进电机相关的概念和时序,能够将时序转化为驱动程序以驱动步进电机。

    2674 人正在学习 去看看 朱有鹏

单片机控制步进电机-线路连接

说明:如何利用单片机去控制步进电机?本案例讲解的内容是硬件连接部分,采用常用的电子器件去实现单片机控制步进电机的功能。后续会分别讲解单片机程序,S曲线生成方法,上位机等相关内容。

硬件清单:
1、单片机最小系统(本案例使用Atmega16芯片)
2、步进电机(二相四线)
3、稳压电源(24V)
4、步进电机驱动器(TB6600)
步进电机单片机最小系统
稳压电源驱动器
整体连接图:
整体图
原理图:
原理图

控制原理:
1、单片机最小系统作用:
①输出脉冲到步进电机驱动器,从而控制步进电机的速度大小
②控制步进电机的使能
③控制步进电机的转动方向
2、步进电机作用:
①提供机械动力
3、稳压电源作用:
①为步进电机提供电源
4、步进电机驱动器作用:
①将单片机脉冲信号转化为步进电机的驱动信号,简化控制过程

硬件连接图:
实物连接图与原理图如上图所示。Atmega16单片机最小系统(其他单片系统接线原理类同)与步进电机驱动器(TB6600)采用共阴极接法(驱动器的ENA-、DIR-、PUL-与单片机的GND连接)。
单片机PA0口控制ENA+,PA0高电位的时,步进电机掉电,步进电机自由状态,用手可以转动;PA0低电位的时,步进电机上电,根据输入的脉冲信号进行转动,无脉冲信号时处于自锁状态;
单片机PA1口控制DIR+,PA1高电位与低点位分别对应步进电机的正转与反转;
单片机PA2口控制PUL+,通过PA2口高低电位的切换形成脉冲,单片机输出一个脉冲(一个高电位加一个低电位即为一个脉冲),步进电机就会走一步。对于二相四线步进电机而言,在驱动器无细分的情况下,单片机一个脉冲对应步进电机一个步距角1.8°;驱动器如果是2细分状态,单片机一个脉冲对应步进电机转动角度为1.8°/2=0.9°;驱动器如果是4细分状态,单片机一个脉冲对应步进电机转动角度为1.8°/4=0.45°;其他细分依次类推。TB6600驱动器上的SW1、SW2、SW3三个拨码开关控制细分数(1细分、2细分、4细分、8细分、16细分、32细分),细分数越大,步进电机转动过程中越平稳。通过脉冲的快慢可以控制步进电机的速度,一般分为三个过程:开始加速、然后匀速、最后减速,后续会单独讲解控制程序部分。
步进电机的A、B相分别接在驱动器A、B相。在不知道步进电机那两根线是一相的时候,有个简单的判断方法:将步进电机的任意两更线接在一起,用手转动步进电机,如果有较大的阻力说明这两根线对应的是一相。
稳压电源输入端接交流220V,输出端正负极(本案例采用的是24V步进电机)分别接在驱动器VCC、GND即可。
至此硬件电路连接完成,下篇将讲解单片机程序。https://blog.csdn.net/LuDanTongXue/article/details/87869806
(微信ID:saskingku)

2019-06-06 18:05:40 weixin_38679924 阅读数 2421
  • 直流电机步进电机-第1季第12部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第12个课程,主要讲解了直流电机和步进电机,其中步进电机是关键,通过学习让大家初步掌握步进电机相关的概念和时序,能够将时序转化为驱动程序以驱动步进电机。

    2674 人正在学习 去看看 朱有鹏

简介:
用单片机控制步进电机正转 反转 加速 减速;
由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--);
}
2019-02-21 22:43:14 LuDanTongXue 阅读数 2008
  • 直流电机步进电机-第1季第12部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第12个课程,主要讲解了直流电机和步进电机,其中步进电机是关键,通过学习让大家初步掌握步进电机相关的概念和时序,能够将时序转化为驱动程序以驱动步进电机。

    2674 人正在学习 去看看 朱有鹏

单片机控制步进电机-单片机程序(avr)

硬件线路连接图见上一篇文章:https://blog.csdn.net/LuDanTongXue/article/details/87869557

软件
ICCV7 FOR AVR-写程序
Progisp-烧程序
速度S曲线生成器(后续后单独讲解)-生成S曲线数组代码
硬件
Atmega16
ASP下载线
杜邦线

控制原理:
利用单片机定时器控制IO口高低电平产生脉冲,通过定时器控制每个脉冲的时间,以及脉冲的个数,从而控制步进电机速度以及转动角度,实现步进电机开环控制能力。步进电机常用的运动控制过程是:【静止】-【S曲线加速】-【匀速】-【S曲线减速】-【停止】,优点是速度平缓上升与下降,能够输出较大的扭矩,不容易失步、堵转。
以下会以【静止】-【正转180°】-【反转180°】-【停止】该运动控制过程进行演示,其中加减速过程均采用S曲线控制。
速度曲线具体控制过程是:
第一段S曲线加速30°:1转/秒启动,5转/秒结束
第二段匀速运动120°:5转/秒匀速
第三段S曲线减速30°:5转/秒启动,0.5转/秒结束
第四段S曲线反转加速30°:0.5转/秒启动,5转/秒结束
第五段匀速反转运动120°:5转/秒匀速
第六段S曲线反转减速30°:5转/秒启动,1转/秒结束

V-T图:
速度时间图
S-T图:
在这里插入图片描述

代码如下:(适用于Atmega16芯片,如用51芯片需要稍作改动)

//头文件
#include<iom16v.h>
#include<macros.h>//SEI()函数_NOP()BIT();
#define uint unsigned int
#define uchar unsigned char

//步进电机接口定义
#define ENA0 (PORTA &=~BIT(0))//电机由脉冲控制
#define ENA1 (PORTA |=BIT(0))//电机掉电、自由状态
#define DIR0 (PORTA &=~BIT(1))//电机正方向转动
#define DIR1 (PORTA |=BIT(1))//电机反方向转动
#define PUL0 (PORTA &=~BIT(2))//低电位
#define PUL1 (PORTA |=BIT(2))//高电位


unsigned int n0;//脉冲计数,用来控制电机转角
uchar duan;//步进电机曲线分段控制参数
uchar kaiguanflag;//该参数为0时,步进电机的启动开关才有效


//定义MEGA16接口输入输出
void port_init(void)
{
 PORTA = 0xFF; //BIT(4)为电机启动开关  BIT(5)电机释放开关
 DDRA  = 0x0F; 
 PORTB = 0xFF; 
 DDRB  = 0xFF; 
 PORTC = 0xFF; 
 DDRC  = 0xFF; 
 PORTD = 0xFF; 
 DDRD  = 0x02;
}


//ms延时函数
void delay_1ms(void) 
{  
unsigned int i;  
for(i=1;i<(unsigned int)(11.059*143-2);i++);//定义晶振频率
}  
void delay(unsigned int n)//延时微妙级 
{  
unsigned int i; 
for(i=0;i<n;i++) 
delay_1ms(); 
}


//主程序
//曲线数组的生成后续文章会单独分析
//第一段S曲线加速30°:1转/秒启动,5转/秒结束
A[67]={0XE5CF,0XE5EF,0XE614,0XE63E,0XE66E,0XE6A4,0XE6E2,0XE728,0XE777,0XE7D0,0XE832,0XE8A0,0XE919,0XE99E,0XEA2F,0XEACB,0XEB73,0XEC25,0XECE0,0XEDA3,0XEE6C,0XEF38,0XF006,0XF0D4,0XF19E,0XF264,0XF323,0XF3D9,0XF485,0XF526,0XF5BC,0XF646,0XF6C4,0XF737,0XF79E,0XF7FB,0XF84E,0XF898,0XF8D9,0XF913,0XF946,0XF972,0XF999,0XF9BB,0XF9D9,0XF9F3,0XFA09,0XFA1C,0XFA2D,0XFA3C,0XFA49,0XFA54,0XFA5D,0XFA65,0XFA6C,0XFA72,0XFA78,0XFA7C,0XFA80,0XFA83,0XFA86,0XFA89,0XFA8B,0XFA8D,0XFA8F,0XFA90,0XFA91};

//第三段S曲线减速30°:5转/秒启动,0.5转/秒结束
B[67]={0XFA8F,0XFA8D,0XFA8B,0XFA89,0XFA87,0XFA84,0XFA81,0XFA7D,0XFA79,0XFA73,0XFA6D,0XFA67,0XFA5F,0XFA55,0XFA4A,0XFA3E,0XFA2F,0XFA1F,0XFA0B,0XF9F5,0XF9DB,0XF9BD,0XF99A,0XF972,0XF944,0XF90F,0XF8D2,0XF88C,0XF83C,0XF7E0,0XF777,0XF700,0XF678,0XF5DE,0XF531,0XF46F,0XF396,0XF2A4,0XF199,0XF073,0XEF33,0XEDD9,0XEC65,0XEAD9,0XE939,0XE786,0XE5C4,0XE3FA,0XE22A,0XE05B,0XDE91,0XDCD2,0XDB21,0XD983,0XD7FB,0XD68B,0XD534,0XD3F8,0XD2D6,0XD1CF,0XD0E1,0XD00B,0XCF4C,0XCEA2,0XCE0B,0XCD86,0XCD10};

//第四段S曲线反转加速30°:0.5转/秒启动,5转/秒结束
C[67]={0XCD86,0XCE0B,0XCEA2,0XCF4C,0XD00B,0XD0E1,0XD1CF,0XD2D6,0XD3F8,0XD534,0XD68B,0XD7FB,0XD983,0XDB21,0XDCD2,0XDE91,0XE05B,0XE22A,0XE3FA,0XE5C4,0XE786,0XE939,0XEAD9,0XEC65,0XEDD9,0XEF33,0XF073,0XF199,0XF2A4,0XF396,0XF46F,0XF531,0XF5DE,0XF678,0XF700,0XF777,0XF7E0,0XF83C,0XF88C,0XF8D2,0XF90F,0XF944,0XF972,0XF99A,0XF9BD,0XF9DB,0XF9F5,0XFA0B,0XFA1F,0XFA2F,0XFA3E,0XFA4A,0XFA55,0XFA5F,0XFA67,0XFA6D,0XFA73,0XFA79,0XFA7D,0XFA81,0XFA84,0XFA87,0XFA89,0XFA8B,0XFA8D,0XFA8F,0XFA90};

//第六段S曲线反转减速30°:5转/秒启动,1转/秒结束
D[67]={0XFA90,0XFA8F,0XFA8D,0XFA8B,0XFA89,0XFA86,0XFA83,0XFA80,0XFA7C,0XFA78,0XFA72,0XFA6C,0XFA65,0XFA5D,0XFA54,0XFA49,0XFA3C,0XFA2D,0XFA1C,0XFA09,0XF9F3,0XF9D9,0XF9BB,0XF999,0XF972,0XF946,0XF913,0XF8D9,0XF898,0XF84E,0XF7FB,0XF79E,0XF737,0XF6C4,0XF646,0XF5BC,0XF526,0XF485,0XF3D9,0XF323,0XF264,0XF19E,0XF0D4,0XF006,0XEF38,0XEE6C,0XEDA3,0XECE0,0XEC25,0XEB73,0XEACB,0XEA2F,0XE99E,0XE919,0XE8A0,0XE832,0XE7D0,0XE777,0XE728,0XE6E2,0XE6A4,0XE66E,0XE63E,0XE614,0XE5EF,0XE5CF,0XE5B3};


void main(void)
{
TCCR1B=0X01;//不分频(定时小于5.9毫秒) 
TCNT1=0XE5F0;//存放初值,根据A[0]值确定
TIMSK|=0x00;//定时器中断关
SREG=0X80;//总中断开
port_init();//IO口初始化
DIR0;//定一个初始转向
ENA1;//上电后步进电机为自由状态
while(1)
{
//本程序将PA4口设置为一个开关,当PA4口与单片机GND连通时,电机开始启动,硬件图里面没有画出该部分
 if((PINA&0x10)==0 & kaiguanflag==0)//启动键
 {
  delay(10);//软件滤波
  if((PINA&0x10)==0 & kaiguanflag==0)
  {
  kaiguanflag=1;//防止在电机转动过程中再进入该部分程序
  n0=0;
  DIR1;//规定转向
  ENA0;//电机处于可操作状态
  TIMSK|=BIT(2);//开16位定时器1中中断,电机启动
  }
 }

//本程序将PA5口设置为一个开关,当PA5口与单片机GND连通时,电机处于掉电自由状态,防止在不用过程中电机一直带电发热,同时可以用手去转动电机,硬件图里面没有画出该部分
 if((PINA&0x20)==0)//切换步进电机可控状态
 {
  delay(10);
  if((PINA&0x20)==0)
  ENA1; 
 }
}
}

#pragma interrupt_handler time1:9//ATMEGA16定时器time1中断
void time1(void)
{
 switch(duan)//控制曲线处于哪个加速段
 {
//已第一段为例解释,后面几段原理一样
case 0: //第一段S曲线加速30°:1转/秒启动,5转/秒结束

  TCNT1=A[n0];//设置定时器初值
  PORTA ^=BIT(2);//PA2口取反,产生高低电平形成脉冲
  if((PINA & BIT(2))==0)//两次中断产生一个脉冲,取其中一次进行脉冲计数
   {
    n0++;
   	  if(n0==67)//判断是否到达指定脉冲数
	  {
		duan++;//到达脉冲个数之后,进入下一段速度
		n0=0;//清零
	  }
   	}break;
   	
case 1: //第二段匀速运动120°:5转/秒匀速
  TCNT1=0XFA99;//匀速5r/s;
  PORTA ^=BIT(2);
  if((PINA & BIT(2))==0)
   {
    n0++;
	if(n0==267)
	{
	 duan++;
	 n0=0;
	}
   }break;
   
case 2: //第三段S曲线减速30°:5转/秒启动,0.5转/秒结束
  TCNT1=B[n0];
  PORTA ^=BIT(2);
  if((PINA & BIT(2))==0)
   {
    n0++;
   	  if(n0==67)
	  {
	    duan++;
	    n0=0;
            DIR0;//开始换向
	  }
   	}break;
   	
case 3: //第四段S曲线反转加速30°:0.5转/秒启动,5转/秒结束
  TCNT1=C[n0];
  PORTA ^=BIT(2);
  if((PINA & BIT(2))==0)
   {
    n0++;
   	  if(n0==67)
	  {
	    duan++;
	    n0=0;
	  }
   	}break;
   	
case 4: //第五段匀速反转运动120°:5转/秒匀速
  TCNT1=0XFA99;//转速5r/s;
  PORTA ^=BIT(2);
  if((PINA & BIT(2))==0)
   {
    n0++;
   	  if(n0==267)
	  {
	    duan++;
	    n0=0;
	  }
   	}break;
case 5: //第六段S曲线反转减速30°:5转/秒启动,1转/秒结束
  TCNT1=D[n0];
  PORTA ^=BIT(2);
  if((PINA & BIT(2))==0)
   {
    n0++;
   	  if(n0==67)
	     {
		      duan=0;
		      kaiguanflag=0;//PA4开关标志清零,等待下一次PA4开关启动
		      TIMSK=0x00;//关闭中断,电机停止
	      }
   	}break;
 default:duan=0;kaiguanflag=0;TIMSK=0x00;break;
  } 
}

演示动画:
将单片机PA4口与GND口连通(时间有限没有接开关),步进电机按照上述设计的曲线进行运动,实现正转180°后反转180°回到原点。
(视频不好传,GIF质量不怎么好,将就看看)。(微信ID:saskingku)
在这里插入图片描述

2016-01-09 10:08:06 NK_test 阅读数 4078
  • 直流电机步进电机-第1季第12部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第12个课程,主要讲解了直流电机和步进电机,其中步进电机是关键,通过学习让大家初步掌握步进电机相关的概念和时序,能够将时序转化为驱动程序以驱动步进电机。

    2674 人正在学习 去看看 朱有鹏

通过采用C#语言实现的上位机控制单片机的步进电机模块、LED灯和蜂鸣器模块,使步进电机进行正、反转和停止并控制转速;LED灯模块进行有选择的呼吸式表达;蜂鸣器模块的开始和终止。

上位机通过串口和自定义的通信协议(8字节)控制单片机的步进电机、LED灯和蜂鸣器模块。其中在控制步进电机的过程中,为了使操作能够及时响应,使用了INT0中断来进行及时性速度响应;LED灯使用位运算控制灯的闪烁位置,合理利用了单片机的模块和操作。

注意:由于定时器个数的限制,没能控制更多的模块。

#include<reg52.h> 

sbit WEI=P2^7;
sbit DUAN=P2^6;
#define DataPort P0

unsigned char code dofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};// 显示段码值0~9
unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码
unsigned char TempData[8]; //存储显示值的全局变量
sbit SPK1=P1^4;  

void delay(int t)
{
   while(t--);
}


sbit A1=P1^0; //定义步进电机连接端口
sbit B1=P1^1;
sbit C1=P1^2;
sbit D1=P1^3;

#define Coil_AB1 {A1=1;B1=1;C1=0;D1=0;}//AB相通电,其他相断电
#define Coil_BC1 {A1=0;B1=1;C1=1;D1=0;}//BC相通电,其他相断电
#define Coil_CD1 {A1=0;B1=0;C1=1;D1=1;}//CD相通电,其他相断电
#define Coil_DA1 {A1=1;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通电,其他相断电
#define Coil_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通电,其他相断电
#define Coil_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通电,其他相断电
#define Coil_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_OFF {A1=0;B1=0;C1=0;D1=0;}//全部断电

unsigned char Speed;
unsigned char dir=0;  
unsigned char code rst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32};  // 复位代码              
void Init_Timer0(void);
void DelayUs2x(unsigned char t)
{   
 while(--t);
}

void DelayMs(unsigned char t)
{
     
 while(t--)
 {
     //大致延时1mS
     DelayUs2x(245);
	 DelayUs2x(245);
 }
}

void Display(unsigned char FirstBit,unsigned char Num)
{
      static unsigned char i=0;
	  

	   DataPort=0;   //清空数据,防止有交替重影
       DUAN=1;     //段锁存
       DUAN=0;

       DataPort=dofly_WeiMa[i+FirstBit]; //取位码 
       WEI=1;     //位锁存
       WEI=0;

       DataPort=TempData[i]; //取显示数据,段码
       DUAN=1;     //段锁存
       DUAN=0;
       
	   i++;
       if(i==Num)
	      i=0;


}

void Rorate()
{
   unsigned int i=512;//旋转一周时间
   Init_Timer0();

   EA=1;          //全局中断开
   EX0=1;         //外部中断0开
   IT0=1;         //1表示边沿触发

    //Speed=speed;
    TempData[0]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68, 
    TempData[1]=dofly_DuanMa[Speed%10];//则68/10=6  68%10=8 
    Coil_OFF
    while(i--&&dir==0)  //正向
    {
	   Coil_A1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_AB1                 //遇到Coil_AB1  用{A1=1;B1=1;C1=0;D1=0;}代替
      DelayMs(Speed);         //改变这个参数可以调整电机转速 ,
       P3=0xeb;
	   P3=0xff;                   //数字越小,转速越大,力矩越小
	  Coil_B1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_BC1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	  Coil_C1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_CD1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	  Coil_D1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_DA1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	}
   	 Coil_OFF
     i=512;
     
	while((i--)&&dir)//反向
	 {
       Coil_A1       
       DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_DA1                  //遇到Coil_AB1  用{A1=1;B1=1;C1=0;D1=0;}代替
      DelayMs(Speed);         //改变这个参数可以调整电机转速 ,
       P3=0xeb;
	   P3=0xff;                   //数字越小,转速越大,力矩越小
	  Coil_D1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
       Coil_CD1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	  Coil_C1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_BC1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	  Coil_B1       
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
      Coil_AB1
      DelayMs(Speed);
	   P3=0xeb;
	   P3=0xff;
	 }
	
}

/*------------------------------------------------
                    串口初始化
------------------------------------------------*/
void InitUART  (void)
{
    SCON  = 0x50;		        // SCON: 模式 1, 8-bit UART, 使能接收  
    TMOD |= 0x20;               // TMOD: timer 1, mode 2, 8-bit 重装
    TH1   = 0xFD;               // TH1:  重装值 9600 波特率 晶振 11.0592MHz  
    TR1   = 1;                  // TR1:  timer 1 打开                         
    EA    = 1;                  //打开总中断
    ES    = 1;                  //打开串口中断
}   


                         
/*------------------------------------------------
                    主函数
------------------------------------------------*/

unsigned int judge[8];
int cnt=0;

void main (void)
{
 
   InitUART();

   ES= 1;//打开串口中断
  while (1)                       
    {
	  
       if(judge[0]==0xFF&&judge[1]==0xFE&&judge[6]==0xFD&&judge[7]==0xFC)
	   { 
	       //(*((void (*)())(rst)))();  // ,将rst数组当函数调用,进行复位
	       if(judge[2]==0x00)//指定步进电机
		   {
		       P3=0xeb;
		       P3=0xff;
			   if(judge[5]!=0)
               Rorate();
		   }
		   else if(judge[2]==0x01)//指定LED
		   {
		       	      
		       unsigned int CYCLE=600,PWM_LOW=0;//定义周期并赋值
			  
			 

               while (1)         //主循环
               {

                 	WEI=0;         //位锁存置0电平,防止LED亮的时候数码管亮
                 	DUAN=0;	      //段锁存置0电平,防止LED亮的时候数码管亮
                    P0=0xFF;
                    delay(60000);        //特意加延时,可以看到熄灭的过程
                    for(PWM_LOW=1;PWM_LOW<CYCLE;PWM_LOW++){ //PWM_LOW表示低
                     //电平时间,这个循环中低电平时长从1累加到CYCLE(周期)的值,即600次

   	              	P0=judge[3];        //点亮LED  
   		            delay(PWM_LOW);//延时长度,600次循环中从1加至599
   		            P0=0xFF;        //熄灭LED
  	                delay(CYCLE-PWM_LOW);//延时长度,600次循环中从599减至1
     
               }
       	       P0=judge[3];
               for(PWM_LOW=CYCLE-1;PWM_LOW>0;PWM_LOW--){ //与逐渐变亮相反的过程

   		       P0=judge[3];
   	           delay(PWM_LOW);
   		       P0=0xFF;
  	           delay(CYCLE-PWM_LOW);
     
                }
                    
             }
			 
		   }
		   else if(judge[2]==0x02)
		   {
		      while(1)
			  {
			       
                   DelayMs(1); //发出大约500Hz的方波 频率越大声音越尖
	               SPK1=!SPK1;

			  }
		   }
       }
    }
}




/*------------------------------------------------
                     串口中断程序
------------------------------------------------*/
void UART_SER (void) interrupt 4 //串行中断服务程序
{
    unsigned char Temp;          //定义临时变量 
   
   if(RI)                        //判断是接收中断产生
     {
	  RI=0;                      //标志位清零
	  Temp=SBUF;                 //读入缓冲区的值
	                     
	  judge[cnt++]=Temp;
      if(cnt==8||judge[0]!=0xFF)	  
      cnt=0;

      SBUF=Temp;     //把接收到的值再发回电脑端
	 }
   if(TI)                        //如果是发送标志位,清零
     TI=0;
} 

/*------------------------------------------------
                    定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
 TMOD |= 0x01;	  //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响		     
 //TH0=0x00;	      //给定初值
 //TL0=0x00;
 EA=1;            //总中断打开
 ET0=1;           //定时器中断打开
 TR0=1;           //定时器开关打开
 PT0=1;           //优先级打开
}
/*------------------------------------------------
                 定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1 
{
 TH0=(65536-2000)/256;		  //重新赋值 2ms
 TL0=(65536-2000)%256;
 
  Display(0,8);
 
}

//外部中断程序
void ISR_INT0(void) interrupt 0 
{
    
         Speed=judge[4];
	     dir=judge[3];
   	     TempData[0]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68,  
	     TempData[1]=dofly_DuanMa[Speed%10];//则68/10=6  68%10=8
	  
}

C#端:


namespace 单片机
{
    public partial class frm : Form
    {
        public frm()
        {
            InitializeComponent();

        }

        private SerialPort com;

        //通信协议
       /// <summary>
       /// FF FE 00(电机) 00(方向) 00(速度) 00(停止) FD FC
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>

        private void btn_LED_Click(object sender, EventArgs e)
        {
            com = new SerialPort();
            com.BaudRate = 9600;
            com.PortName = "COM4";
            com.DataBits = 8;
            com.Open();
            Byte[] data = new Byte[8];
            data[0] = 0xFF;
            data[1] = 0xFE;
            if(rb_R.Checked==true)
            {
                data[2] = 0x00;
                if (rb_clock.Checked == true)
                {
                    data[3] = 0x00;
                }
                else if (rb_anclock.Checked == true)
                {
                    data[3] = 0x01;
                }
                if (cmb_speed.Text.ToString() == "停止")
                {
                    data[4] = 0x00;
                    data[5] = 0x00;
                }
                else
                {
                    data[4] = Byte.Parse(cmb_speed.Text);
                    data[5] = 0x01;
                }
            }
            else if (rb_LED.Checked == true)
            {
                data[2] = 0x01;
                uint num = 255;
                if (checkBox1.Checked == true)
                {
                    num = num & 254;
                }
                if (checkBox2.Checked == true)
                {
                    num = num & 253;
                }
                if (checkBox3.Checked == true)
                {
                    num = num & 251;
                }
                if (checkBox4.Checked == true)
                {
                    num = num & 247;
                }
                if (checkBox5.Checked == true)
                {
                    num = num & 239;

                }
                if (checkBox6.Checked == true)
                {
                    num = num & 223;
                }
                if (checkBox7.Checked == true)
                {
                    num = num & 191;
                }
                if (checkBox8.Checked == true)
                {
                    num = num & 127;
                }

                byte[] c = System.BitConverter.GetBytes(num);
                data[3] = c[0];

                if (led_s.Text.ToString() == "启用")
                    data[2] = 0x02;
                data[4] = 0x00;
                data[5] = 0x00;
            }
         
            data[6] = 0xFD;
            data[7] = 0xFC;
            com.Write(data, 0, 8);
            com.Close(); 
        }
    }
}



单片机之步进电机

阅读数 104

没有更多推荐了,返回首页