stm32定时器输出比较模型翻转电平驱动步进电机

15 篇文章 4 订阅
2 篇文章 0 订阅

玩转步进电机第二转



前言

这一篇来讲讲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值我是直接用寄存器进行赋值了,至于为什么可以这样对通道的输出失能或者是使能的话,请看这个:
在这里插入图片描述


总结

接下来不出意外的话应该会更新关于机械臂的内容,上述如有出错请指出,谢谢啦。下次见

  • 9
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐乐小工厂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值