接触过实际项目后,才发现实际运用的步进电机的控制并不是采用初学单片机时采用高低电平延时输出相序控制(当然这种方法并未使用专门的步进电机驱动器),也不是采用PWM波输出的模式(这里使用了专门的步进电机驱动器,PWM输出模式只适用于电机一直转,不适合精准控制脉冲个数和精准角度以及做步进电机梯形加速和S型加速等算法),个人觉得使用单片机做控制的话,无疑比较输出模式是最好的策略(当然还是比较推荐采用PLC控制器)。STM32比较输出模式效果还是不错,实际控制9个步进电机还是比较容易。
首先先介绍stm32比较输出模式以及配置过程(这里我采用的控制芯片是STM32F103ZET6)
1.stm32比较输出模式简介以及配置原理 此项功能是用来控制一个输出波形,或者指示一段给定的的时间已经到时。当计数器与捕获/比较寄存器的内容相同时,输出比较功能做如下操作: ● 将输出比较模式(tiMx_CCMRx寄存器中的OCxM位)和输出极性(TIMx_CCER寄存器中的CCxP位)定义的值输出到对应的引脚上。在比较匹配时,输出引脚可以保持它的电平(OCxM=000)、被设置成有效电平(OCxM=001)、被设置成无效电平(OCxM=010)或进行翻转(OCxM=011)。 ● 设置中断状态寄存器中的标志位(TIMx_SR寄存器中的CCxIF位)。 ● 若设置了相应的中断屏蔽(TIMx_DIER寄存器中的CCxIE位),则产生一个中断。 ● 若设置了相应的使能位(TIMx_DIER寄存器中的CCxDE位,TIMx_CR2寄存器中的CCDS位选择DMA请求功能),则产生一个DMA请求。 TIMx_CCMRx中的OCxPE位选择TIMx_CCRx寄存器是否需要使用预装载寄存器。 在输出比较模式下,更新事件UEV对OCxREF和OCx输出没有影响。 同步的精度可以达到计数器的一个计数周期。输出比较模式(在单脉冲模式下)也能用来输出一个单脉冲。
输出比较模式的配置步骤: 1. 选择计数器时钟(内部,外部,预分频器) 2. 将相应的数据写入TIMx_ARR和TIMx_CCRx寄存器中 3. 如果要产生一个中断请求和/或一个DMA请求,设置CCxIE位和/或CCxDE位。 4. 选择输出模式,例如当计数器CNT与CCRx匹配时翻转OCx的输出引脚,CCRx预装载未用,开启OCx输出且高电平有效,则必须设置OCxM=’011’、OCxPE=’0’、CCxP=’0’和CCxE=’1’。 5. 设置TIMx_CR1寄存器的CEN位启动计数器TIMx_CCRx寄存器能够在任何时候通过软件进行更新以控制输出波形(这里就是我们采用输出比较模式的关键),条件是未使用预装载寄存器(OCxPE=’0’,否则TIMx_CCRx影子寄存器只能在发生下一次更新事件时被更新)。下图给出了一个例子。
简单的来说假设我们开启了定时器向上计数,再设置CCRX的值。当定时器计数值CNT
2.stm32比较输出配置代码
- //TIM2 CH3 PB10 比较输出模式 完全重映射
- //逆时针转72° 电机 86GYG250D-156 驱动器 HB860 匀速
- //需要做开漏输出5V强上拉
- //电机2 400细分数 23次252脉冲 4次251脉冲 依次一个循环
- void TIM2_Compare_output(u16 arr,u16 psc) //比较输出
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
-
- GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射 TIM2_CH3->PB10
-
- //设置该引脚为复用输出功能,输出TIM2_CH3的PWM脉冲波形 GPIOB10
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //TIM_CH2
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏输出 外部记住外接上拉5v 不然用示波器看不到PWM输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
-
- //初始化TIM2
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 0XFFFF这里并没有用到
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
-
- //初始化TIM2 Channel3
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; //选择翻转模式
- TIM_OCInitStructure.TIM_Pulse = 500; //指定要加载到捕获比较寄存器中的脉冲值
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
- TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
- TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
- TIM_OC3Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 OC3
-
- TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //禁止TIM3在CCR2上的预装载寄存器 这里很重要不要使能啊
-
- TIM_ITConfig(TIM2, TIM_IT_CC3, ENABLE);
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占式优先级为0
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置子优先级为0
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- TIM_Cmd(TIM2, ENABLE); //使能TIM2
- /* TIM主输出使能 */
- TIM_CtrlPWMOutputs(TIM2, ENABLE);
-
- }
-
-
- //TIM2 CH3 PB10比较输出 完全重映射
- //输出 160个脉冲
- u16 tt_2=0;
- u16 pulse2_num=0; //输出脉冲数
- void TIM2_IRQHandler(void) //TIM3中断
- {
- if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
- {
- tt_2++;
- TIM_ClearITPendingBit(TIM2, TIM_IT_CC3); //清除TIMx的中断待处理位:TIM 中断源
- if(tt_2==2) //每两次输出一个完整脉冲
- {
- tt_2=0;
- pulse2_num++;
- TIM2->ARR=Motor2_frequency; //设置重装载值 改变这个可以改变频率
- TIM_SetCompare3(TIM2,Motor2_frequency); //设置比较值
- if(pulse2_num==Motor2_Step) //完成226°
- {
- pulse2_num=0;
- TIM_Cmd(TIM2,DISABLE); //关闭定时器2
- }
-
- }
- }
- }
复制代码
这里需要注意以上几点:
1. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏输出 外部记住外接上拉5v 不然用示波器看不到PWM输出
这里我因为实际需要做了外部上拉,如果不接外部上拉到5V的话,没有输出。当然你可以改为复用开漏输出模式
2.TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
这里就是比较输出模式下,先是输出高电平还是低电平。
3.TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //禁止TIM3在CCR2上的预装载寄存器 这里很重要不要使能啊
这里也是很重要的一点,CCR2预装载寄存器不能使能,否者不会比较输出高低电平
4.还有一点需要注意的是其实是每进入两次中断才算输出一个完整的pwm脉冲
|