(一)硬件信息
STM32C8T6,42步进电机,TB660电机驱动
(二)参考博客
1.【STM32】实战3.1—用STM32与TB6600驱动器驱动42步进电机(一)_白白与瓜的博客-CSDN博客
2.利用STM32F103精确控制步进电机_stm32控制步进电机_jl_mlh的博客-CSDN博客
(三)前言
42步进电机的详细原理和运行方式都可以从上面的文章中学习到,本章是站在前人的肩膀上重新改写的代码,更基础,更简单,便于二次改写用于不同的场合。
(四)工作原理
1.本驱动代码利用定时器TIM2和定时器TIM3构造一个主从定时器,TIM2作为主定时器控制电机的转速,TIM3作为从定时器控制电机的转动角度。
2.电机的转速和转角还与驱动器自身的细分数有关,但是驱动器细分数是通过影响电机的步距角来影响转速和转角,而TIM2和TIM3是控制步进电机的频率和脉冲数来控制转速转角。
定时器二进行输出,通过计算可以得出旋转速度,通过配置合适的装载值可以实现对步进电机的速度控制(参考博客二),定时器作为从定时器可以在内部计算定时器二的脉冲数,通过计算可以得到目标角度所需要的脉冲数,通过向定时器三中写入装载值,让定时器三开始计数,中断。
中从定时器目的就是实现对内部定时器发出的脉冲进行计数。
STM32单片机每个通用定时器可以独立产生4路PWM信号,每个通道的PWM信号频率由预分频器PSC和重装载寄存器ARR决定,脉宽由预分频器PSC和比较/捕获寄存器CCRx决定,但对于一些特殊使用场景,如可控硅控制,步进电机控制,或基带信号PPM调制等,需要对PWM输出信号的位时(类似于正弦信号的相位)进行调节,普通PWM输出模式无法满足这种需求,本文所述主从定时器结合定时器单脉冲模式实现输出频率、脉宽、位时均可自由调节的PWM信号。且相对于传统PWM输出方式来说,可以最大限度的提高脉宽调节精度,72M主频下可实现13.89ns的调节精度。而缺点正是多耗费一个定时器。
话不多说上代码。
(五)代码部分
timer.c
#include "timer.h"
#include "stm32f10x.h"
/****************************************************
* 函数名:GPIO_Config
* 描述 :无
* 输入 :无
* 输出 :无
* 调用 :主函数
* 返回值:无
******************************************************/
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIOA
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE); //使能TIM2,TIM3的时钟
/* Timer2 Channel 1, PA0 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2; //初始化引脚用于控制方向(GPIOA1)和使能(GPIOA2)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //指定GPIO引脚可输出的最高频率为50MHZ
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_1); //指定引脚输出低电平,此时灯全灭,方向
GPIO_ResetBits(GPIOA, GPIO_Pin_2); //指定引脚输出低电平,此时灯全灭 使能
}
//================================================================================
/****************************************************
* 函数名:TIM2_Master__TIM3_Slave_Configuration
* 描述 :主从定时器配置
* 输入 :电机转速speed,转角angle
* 输出 :无
* 调用 :主函数
* 返回值:无
******************************************************/
void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency, u32 pulse)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
u16 nPDTemp ;
u16 pulse_number;
float p=PulseFrequency;
TIM_Cmd(TIM2, DISABLE);
nPDTemp = (11.25/p); //TIM2重装值是11.25时1s转一圈(电机32细分下) 控制速度
pulse_number = (17.7778*pulse); //TIM3重装值是17.7778时转1°(电机32细分下)。TIM_Period不要超过0xFFFF 控制旋转角度
// 时基配置:配置PWM输出定时器——TIM2
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = nPDTemp; //定时周期为nPDTemp
TIM_TimeBaseStructure.TIM_Prescaler = 999; //预分频值1000,即f=72khz
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子,会影响滤波器采样频率,与本实验无影响
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //指定重复计数器值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 输出配置:配置PWM输出定时器——TIM2
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 脉冲宽度调制模式 1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平有效
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
TIM_OCInitStructure.TIM_Pulse = nPDTemp>>1; //50% //比较tim_ccr的值,输出脉冲发生跳变
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //初始化
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能 TIMx 在 CCR1 上的预装载寄存器
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能 TIMx 在 ARR 上的预装载寄存器
// 时基配置:配置脉冲计数寄存器——TIM3
TIM_TimeBaseStructure.TIM_Period = pulse_number; //0x1900是360°;//改变给电机的脉冲个数
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 输出配置:配置输出比较非主动模式定时器——TIM3
// Output Compare Active Mode configuration: Channel1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; //输出比较非主动模式,(匹配时设置输出引脚为无效电平,当计数值为比较/捕获寄存器值相同时,强制输出为低电平)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0xFFFF; // 这里的配置值意义不大
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 配置TIM2为主定时器
// Select the Master Slave Mode
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //设置 TIM2 主/从模式并使能
// Master Mode selection
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //使用更新事件作为触发输出
// 配置TIM3为从定时器
// Slave Mode selection: TIM3
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated); //选择 TIM3为从模式 TIM_SlaveMode_Gated-当触发信号(TRGI)为高电平时计数器时钟使能
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); //选择 TIM3 输入触发源 TIM_TS_ITR1-TIM 内部触发 1
TIM_ITRxExternalClockConfig(TIM3, TIM_TS_ITR1); //设置 TIM3 内部触发为外部时钟模式 TIM_TS_ITR1-TIM 内部触发 1
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TIM3 TIM 捕获/比较 1 中断源
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
/****************************************************
* 函数名:Frequence_Setting
* 描述 :无
* 输入 :无
* 输出 :无
* 调用 :主函数
* 返回值:无
******************************************************/
void Frequence_Setting(u32 PulseFrequency)
{
u16 nPDTemp ;
TIM_Cmd(TIM2, DISABLE); //关闭定时器二
nPDTemp = 72000UL/PulseFrequency; //计算装载数
TIM_SetAutoreload(TIM2,nPDTemp);
TIM_GenerateEvent(TIM2,TIM_EventSource_Update);
TIM_SetCompare1(TIM2,nPDTemp/2);
}
/****************************************************
* 函数名:Output_Pulse
* 描述 :无
* 输入 :无
* 输出 :无
* 返回值:无
******************************************************/
void Output_Pulse(u16 Num)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2); //指定引脚输出低电平,使能
TIM3->CCR1 = Num; //把脉冲数写入定时器三的寄存器里
TIM3->CNT = 0; //把定时器三的计数归零
TIM_Cmd(TIM3, ENABLE); //使能定时器三
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能定时器三的中断
TIM_Cmd(TIM2, ENABLE); //使能定时器二
}
/****************************************************
* 函数名:angle_set
* 描述 :无
* 输入 :无
* 输出 :无
* 返回值:无
******************************************************/
void angle_set(u8 dir,u8 angle)
{
if(dir==0)
GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,方向
else
GPIO_SetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,方向
Output_Pulse(angle*6400);
}
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) // TIM_IT_CC1
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除中断标志位
TIM_Cmd(TIM2, DISABLE); // 关闭定时器
TIM_Cmd(TIM3, DISABLE); // 关闭定时器
TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE);
}
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
extern unsigned char Flag;
extern unsigned char TIM2_Pulse_TIM3_Counter_OK;
void GPIO_Config(void);
void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency,u32 pulse);
void Frequence_Setting(u32 PulseFrequency);
void Output_Pulse(u16 Num);
void angle_set(u8 dir,u8 angle);
#endif /* __USART1_H */
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "timer.h"
#include "sys.h"
int main()
{
delay_init(); //延时初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断初始化
GPIO_Config(); //引脚初始化
u8 flag=1;
u8 angle=90;
while(1)
{
if(flag==1)
{
TIM2_Master__TIM3_Slave_Configuration(1,angle);
delay_ms(40000);
angle=0;
flag=0;
}
}
}