大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“SYK-0806-A2S1 工业自动化控制之【07-定时器产生特定频率脉冲】”,这一篇中,笔者要给大家介绍如何定时器产生特定频率来控制步进电机。
在工业自动化控制中,相信大家对步进电机并不会陌生,工业上用的步进电机一般还会配一个专门的驱动器,下图中就是其中的一款带驱动器的57型步进电机套件:
那么,我们先来看一下步进电机的控制原理:
根据上图中的步进电机的控制原理我们可以知道,通过控制器产生脉冲信号,再经过步进电机驱动器将脉冲信号转化为步进电机的角位移,便可以让步进电机动起来了!接下来我们看下步进电驱动器的接线方法。步进电机驱动器有三种接线方式,即:共阳极接法、共阴极接法和差分方式接法,分别如下图所示:
步进电机一般有四线、六线和八线三种,三种电机的接线方式如下图所示:
一般买电机的时候,厂家会给出具体的接线图,本例中,笔者就是用的8线步进电机并行高速接法。驱动器采用的是共阳极接法。线路连接好了,接下来我们就要来分析代码部分了。
首先,我们还是来说一下本例要实现的功能,本例中笔者是分两种方式来实现步进电机的启动与停止:
-
通过控制定时器0的启动与停止来实现电机0脉冲的输出与关闭
-
定时器2一直开启,通过一个变量来控制电机2的脉冲输出与关闭
具体控制逻辑如下:
- 当X00信号由低电平变成高电平时,电机0启动;
- 当X01信号由低电平变成高电平时,电机0停止;
- 当X02信号由低电平变成高电平时,电机2启动;
- 当X03信号由低电平变成高电平时,电机2停止;
控制逻辑弄清楚了,现在就可以来看代码部分了,首先,还是两个定时器的配置,本例中笔者还是将两个定时器单独来配置:
#include "bsp_timer.h"
void Timer0_config(u32 TIM0_Fre)
{
TIM_InitTypeDef TIM_InitStructure; //结构定义
TIM_InitStructure.TIM_Mode = TIM_16BitAutoReload; //指定工作模式, TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMask
TIM_InitStructure.TIM_Polity = PolityLow; //指定中断优先级(低到高) Polity_0,Polity_1,Polity_2,Polity_3
TIM_InitStructure.TIM_Interrupt = ENABLE; //中断是否允许, ENABLE或DISABLE
TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T; //指定时钟源, TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
TIM_InitStructure.TIM_ClkOut = DISABLE; //是否输出高速脉冲, ENABLE或DISABLE
TIM_InitStructure.TIM_Value = 65536UL - (MAIN_Fosc / TIM0_Fre/2); //初值,
TIM_InitStructure.TIM_Run = ENABLE; //是否初始化后启动定时器, ENABLE或DISABLE
Timer_Inilize(Timer0,&TIM_InitStructure); //初始化Timer0 Timer0,Timer1,Timer2,Timer3,Timer4
}
void Timer2_config(u32 TIM2_Fre)
{
TIM_InitTypeDef TIM_InitStructure; //结构定义
TIM_InitStructure.TIM_Mode = TIM_16BitAutoReload; //指定工作模式, TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload
TIM_InitStructure.TIM_Polity = PolityLow; //指定中断优先级, PolityHigh,PolityLow
TIM_InitStructure.TIM_Interrupt = ENABLE; //中断是否允许, ENABLE或DISABLE
TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T; //指定时钟源, TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
TIM_InitStructure.TIM_ClkOut = DISABLE; //是否输出高速脉冲, ENABLE或DISABLE
TIM_InitStructure.TIM_Value = 65536UL - (MAIN_Fosc / TIM2_Fre/2); //初值,
TIM_InitStructure.TIM_Run = ENABLE; //是否初始化后启动定时器, ENABLE或DISABLE
Timer_Inilize(Timer2,&TIM_InitStructure); //初始化Timer1 Timer0,Timer1,Timer2,Timer3,Timer4
}
然后,在app.c文件中的app_int()函数中调用Timer0_config()、Timer2_config()这两个函数对定时器0和定时器2分别进行初始化,同时因为电机0是直接通过控制定时器0的启动和停止来控制电机启停的,所以这里还需先将定时器0关闭,即“TR0= 0;”:
void app_init(void)
{
GPIO_Config(); //GPIO配置
Timer0_config(Timer0_Fre); //定时器0配置
Timer2_config(Timer2_Fre); //定时器2配置
TR0= 0; //定时器0停止运行
EA = 1; //开启总中断
}
这里设定的定时器0和定时器2的输出时钟频率依然为1000Hz:
u32 Timer0_Fre = 1000UL;//timer0频率初值 u32 Timer2_Fre = 1000UL;//timer2频率初值
接下来,我们为了后面程序的可读性更强,笔者这里用了一些宏定义来配置电机0和电机2的对应的控制信号:
#define M0_PUL Y00 //定义电机0脉冲对应的GPIO #define M2_PUL Y01 //定义电机2脉冲对应的GPIO #define M0_Start X00 //定义电机0启动信号对应的GPIO #define M0_Stop X01 //定义电机0停止信号对应的GPIO #define M2_Start X02 //定义电机2启动信号对应的GPIO #define M2_Stop X03 //定义电机2停止信号对应的GPIO
同时,还定义了一个变量用作电机2的脉冲输出使能控制:
bit F_Motor2_EN= 0; //电机2脉冲输出使能标志位
接下来,就是两个电机的控制逻辑部分了,还是跟之前一样,这部分的代码均放在了app_run()函数中,首先我们先定义4个标志位,用于X00-X03的状态的判断:
static bit F_M0_Start = 0;
static bit F_M0_Stop = 0;
static bit F_M2_Start = 0;
static bit F_M2_Stop = 0;
这里可能大家注意到了,每个变量前面都加了结果static关键字,相信有一定基础的小伙伴们都应该能明白此处加这个关键字的作用,试想一下,如果此处没有static这个关键字的话,那么F_M0_Start 、F_M0_Stop 、F_M2_Start 、F_M2_Stop这四个变量的值是不是在每次进入app_run()这个函数的时候,都会被初始化一遍(即赋值0)?这种情况肯定不是我们本例中想要的,因此,此处加static这个关键字是很有必要的。
废话不多说,附上app.c的完整代码吧,注释也比较全,相信大家一看就能理解了:
#include "app.h"
#define M0_PUL Y00 //定义电机0脉冲对应的GPIO
#define M2_PUL Y01 //定义电机2脉冲对应的GPIO
#define M0_Start X00 //定义电机0启动信号对应的GPIO
#define M0_Stop X01 //定义电机0停止信号对应的GPIO
#define M2_Start X02 //定义电机2启动信号对应的GPIO
#define M2_Stop X03 //定义电机2停止信号对应的GPIO
bit F_Motor2_EN= 0; //电机2脉冲输出使能标志位
u32 Timer0_Fre = 1000UL;//timer0频率初值
u32 Timer2_Fre = 1000UL;//timer2频率初值
void app_init(void)
{
GPIO_Config(); //GPIO配置
Timer0_config(Timer0_Fre); //定时器0配置
Timer2_config(Timer2_Fre); //定时器2配置
TR0= 0; //定时器0停止运行
EA = 1; //开启总中断
}
void app_run(void)
{
static bit F_M0_Start = 0;
static bit F_M0_Stop = 0;
static bit F_M2_Start = 0;
static bit F_M2_Stop = 0;
if(!F_M0_Start)
{
if(!M0_Start) //X00低电平
{
delay_ms(10); //10ms消抖
if(!M0_Start)
{
F_M0_Start = 1;//X00低电平标志置“1”
}
}
}
else
{
if(M0_Start) //X00高电平
{
delay_ms(10); //10ms消抖
if(M0_Start)
{
F_M0_Start = 0;//X00低电平标志清“0”
TR0 = 1;//定时器0开始运行
}
}
}
if(!F_M0_Stop)
{
if(!M0_Stop) //X01低电平
{
delay_ms(10); //10ms消抖
if(!M0_Stop)
{
F_M0_Stop = 1;//X01低电平标志置“1”
}
}
}
else
{
if(M0_Stop) //X01高电平
{
delay_ms(10); //10ms消抖
if(M0_Stop)
{
F_M0_Stop = 0; //X01低电平标志清“0”
TR0 = 0; //定时器0停止运行
M0_PUL = OutputT_OFF; //Y00保持高电平
}
}
}
if(!F_M2_Start)
{
if(!M2_Start) //X02低电平
{
delay_ms(10); //10ms消抖
if(!M2_Start)
{
F_M2_Start = 1;//X02低电平标志置“1”
}
}
}
else
{
if(M2_Start) //X02高电平
{
delay_ms(10); //10ms消抖
if(M2_Start)
{
F_M2_Start = 0;//X02低电平标志清“0”
F_Motor2_EN= 1;//电机2脉冲输出使能
}
}
}
if(!F_M2_Stop)
{
if(!M2_Stop) //X03低电平
{
delay_ms(10); //10ms消抖
if(!M2_Stop)
{
F_M2_Stop = 1;//X03低电平标志置“1”
}
}
}
else
{
if(M2_Stop) //X03高电平
{
delay_ms(10); //10ms消抖
if(M2_Stop)
{
F_M2_Stop = 0;//X03低电平标志清“0”
F_Motor2_EN= 0;//电机2脉冲输出失能
}
}
}
}
void timer0_int (void) interrupt TIMER0_VECTOR //频率可变
{
M0_PUL = !M0_PUL; //Y00状态翻转
}
void timer2_int (void) interrupt TIMER2_VECTOR //频率可变
{
if(F_Motor2_EN) //电机2脉冲输出使能
M2_PUL = !M2_PUL; //Y01状态翻转
else
M2_PUL = OutputT_OFF; //Y01保持高电平
}
最后,笔者给大家看一下程序运行的效果吧:
好了,关于使用本节内容笔者就介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,下一节笔者将给大家介绍“如何让步进电机正反转运行”,详见“SYK-0806-A2S1 工业自动化控制之【09-步进电机正反转】”,感谢大家的支持!



