玩转步进电机第二转
前言
这一篇来讲讲stm32当中主要控制步进电机的方法,也就是通过配置我们的定时器的模式来进行对定时器的IO来进行电平翻转实现输出脉冲。
一、输出IO,定时器配置
初始化电机正反转输出IO
void Driver_Init(void){//初始话PC1
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能CDE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC,GPIO_Pin_1);//拉低io输出
}
并且在头文件中定义了
#define SET_MOTER PCout(1)
我们这里使用C1为我们的驱动正反转IO
初始化我们的定时器TIM2
void TIM2_OCPWM_Init(void){
GPIO_InitTypeDef GPIO_InitStructure; //GPIO初始化结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定时器时基初始化结构体
TIM_OCInitTypeDef TIM_OCInitStructure; //定时器输出初始化结构体
NVIC_InitTypeDef NVIC_InitStructure; //中断初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//使能定时器B、AFIO时钟
GPIO_AFIODeInit(); //重映射必加,以免出现不正常现象。 只需初始一次 避免把原先的重映射给取消了
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); // 关闭jtag 开启sw ps:重映射必须使能AFIO时钟
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);//开启 TIM2完全重映射:4通道重映射==>PB3s
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能定时器TIM2时钟
/*===================引脚初始化=======================*/
/*===========设置该引脚为复用输出功能==================*/
/*====================================================*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*====================================================*/
/*===================时基初始化=======================*/
/*====================================================*/
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); //结构体中元素初始化为默认值
TIM_TimeBaseStructure.TIM_Period = 65535; //也就是0xffff //自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 71; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:不分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //指定的参数初始化TIMx的时间基数单位 //(清除TIMx 的中断待处理位),TIM_ClearFlag(清除TIMx 的待处理标志位)
/*====================================================*/
/*=================比较输出初始化=====================*/
/*====================================================*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//输出比较主动模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能 下一次更新事件时被更新;
TIM_OCInitStructure.TIM_Pulse = 0;//设置比较值(跳变值)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高电平
TIM_OC2Init(TIM2, &TIM_OCInitStructure);//初始化输出比较寄存器
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);//关闭预转载
/*====================================================*/
/*====================中断初始化=======================*/
/*====================================================*/
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);//清除中断标志
TIM_Cmd(TIM2,ENABLE);
}
我们这里使用的定时器输出IO为B3,因为我们的B3是作为特殊IO,我记得是默认为JADI,好像是,反正我们要该IO输出的话需要进行完全重映射才能成功,至于功能方面各位可以去查看手册。
该代码中的输出化的配置都写比较详细的注释了,有不懂的欢迎评论区留言,大家一起讨论。
二、步进电机定位
1.定时器TIM2中断函数
代码如下(示例):
void TIM2_IRQHandler(void)
{
static u16 capture = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//通道1检测到比较事件
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);//清除标志位
TIM_SetCompare2(TIM2, TIM_GetCapture2(TIM2) + step.CCR_VAL);//重新设置比较值
step.i++;
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable); //CH2预装载失能-->立即生效
if(step.i %2 == 0){
step.pulse--; //因为每两次进入中断即为一次完整的脉冲
step.i = 0;
}
if(step.pulse==0){
step.CCR_VAL = 0;
TIM_ITConfig(TIM2, TIM_IT_CC2, DISABLE);//失能指定的TIM中断
TIM2->CCER &= 0xFFEF; //OC2输出失能
}
}
}
这里的step是我定义的一个结构体,如下:
typedef struct{
int angle;//目标角度
u8 dir; //旋转方向
u32 pulse;//脉冲个数
u16 i;
u16 CCR_VAL;
}SETP_MOTOR;
- 这里的TIM_SetCompare2(TIM2, TIM_GetCapture2(TIM2) +step.CCR_VAL);是对定时器2的通道2进行自动重装载值进行重新设置
- 然后这里的TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);作用是预装载失能,我大概搜了下就是说,如果是失能的话该设置的预装载值会在本次生效,而使能的话设置的预装载值就会在下一次生效。(应该是隔得有点久了,记忆稍微模糊)
- 因为我在使用的过程中我是用的库函数进行失能通道输出的,但是发现还是有一小段的脉冲,所以换成了寄存器来进行失能通道输出就好了。
2.接下来的是一个设置步进电机角度转脉冲函数的实现
代码如下(示例):
void step_angle2pulse(int angle){
if(angle >= 0)SET_MOTOER = 1;
step.pulse = angle/0.45;
step.i = 0;
step.CCR_VAL = 3000;
if(step.pulse!=0){
TIM2->CCR2=step.CCR_VAL;
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);//CH2预装载失能-->CCR2立即生效
TIM2->CCER |= 0x0010; //OC2输出使能
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE); //使能指定的TIM中断
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);//清除TIMx的中断待处理位
}
}
这个函数就比较简陋了,也没写到很详细,这里的输入变量angle只能是正值,然后这里除以0.45,是因为我设置的是800细分数,也就是说360°除以800=0.45,也就是我们的一个步距角。然后这里的CCR值我是直接用寄存器进行赋值了,至于为什么可以这样对通道的输出失能或者是使能的话,请看这个:
总结
接下来不出意外的话应该会更新关于机械臂的内容,上述如有出错请指出,谢谢啦。下次见