项目中需要对步进电机进行较为精准的控制。经过查找资料,可以通过对STM32的定时器采用主从定时器的模式。由主定时器输出方波信号,从定时器对主定时器输出的脉冲进行计数,溢出时触发从定时器的中断服务函数。以此达到控制步进电机转动的圈数的目的。
本文所采用的STM32开发板是正点原子的STM32F103(精英板)、STM32F407(探索者),参考资料多来自正点原子资料下载中心(http://www.openedv.com/docs/index.html)。我依然处于刚入门学习STM32的阶段,程序是在正点原子的例程的基础上修改的。
原理/思路
- 步进电机:步进电机需要通过步进驱动器进行驱动,STM32通过串口输出一定频率的PWM波信号给步进电机驱动器的脉冲输入引脚(如PU-),输出高低电平给步进电机驱动器的方向控制信号输入引脚(如DR-)。
- 脉冲数与圈数:我使用的是一般的两相四线步进电机,步距角为1.8°。由此可知,360°/1.8°=200;也就是说,在不细分的情况下,200个脉冲使步进电机转动一圈。如果设置PWM波输出频率为 1kHz,也就使一秒内输出1000个脉冲,相当于步进电机一秒内可以转动5圈。
- 主从定时器:主定时器输出PWM波脉冲信号,从定时器对主定时器输出的脉冲进行计数。对于STM32,除了基本定时器,高级定时器和通用定时器都可以分别作为主、从定时器,但其中的主从关系要遵循参考手册中所提供的配置。
下图,表74,表78,来自《STM32中文参考手册》P237,P285;适用于STM32F103。
下图,表72,表79,来自《STM32F4xx中文参考手册》P370,P464;适用于STM32F407。
- 注意:由上表可知,对于STM32F103,最多三组主从定时器;对于STM32F407,最多四组主从定时器。TS值 - ITRx 不能重复使用,定时器也不可重复使用。所以配置要注意选取的定时器的输出I/O,合理分配引脚。
代码实现
- “主从定时器是高级定时器和通用定时器的组合”,程序上会与“主从定时器都是通用定时器的组合”略有区别。
STM32F103:主要对定时器配置进行展示
- timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
/* Group A: TIM1 - 主定时器,TIM2 - 从定时器
TIM1: CH1 - PA8, CH4 - PA11 RCC_APB2Periph_TIM1
TIM2: RCC_APB1Periph_TIM2
Group B: TIM5 - 主定时器,TIM8 - 从定时器
TIM5: CH1 - PA0, CH2 - PA1 RCC_APB1Periph_TIM5
TIM8: RCC_APB2Periph_TIM8
Group C: TIM3 - 主定时器,TIM4 - 从定时器
TIM3: CH1 - PA6, CH2 - PA7 RCC_APB1Periph_TIM3
TIM4: RCC_APB1Periph_TIM4
*/
/*****定时器初始化函数*****/
// Group A
void TIM1_GPIO_Config(uint16_t TIM1_Prescaler, uint16_t TIM1_Period, uint16_t CCR_A, uint16_t DIR_A);
void TIM2_GPIO_Config(u32 PulseNum_A);
void PWM_Output_A(u16 Cycle_A, u32 PulseNum_A, u16 DIR_A);
void TIM2_IRQHandler(void);
// Group B
void TIM5_GPIO_Config(uint16_t TIM5_Prescaler, uint16_t TIM5_Period, uint16_t CCR_B, uint16_t DIR_B);
void TIM8_GPIO_Config(u32 PulseNum_B);
void PWM_Output_B(u16 Cycle_B, u32 PulseNum_B, u16 DIR_B);
void TIM8_UP_IRQHandler(void);
// Group C
void TIM3_GPIO_Config(uint16_t TIM3_Prescaler, uint16_t TIM3_Period, uint16_t CCR_C, uint16_t DIR_C);
void TIM4_GPIO_Config(u32 PulseNum_C);
void PWM_Output_C(u16 Cycle_C, u32 PulseNum_C, u16 DIR_C);
void TIM4_IRQHandler(void);
#endif /* __TIMER_H */
- timer.c
#include "timer.h"
/*********** Group A ***********/
// 定时器1主模式
void TIM1_GPIO_Config(uint16_t TIM1_Prescaler, uint16_t TIM1_Period, uint16_t CCR_A, uint16_t DIR_A)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_11; // TIM1_CH1 - PA8, CH4 - PA11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = TIM1_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM1_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数,一定要 = 0; 高级定时器TIM1,TIM8,这句必须有。
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM1 - 通道1设置函数,50/100,脉冲信号
TIM_OCInitStructure.TIM_Pulse = CCR_A; // 设置待装入捕获寄存器的脉冲值
TIM_OC1Init( TIM1, &TIM_OCInitStructure);
TIM_SelectMasterSlaveMode( TIM1, TIM_MasterSlaveMode_Enable); // 主从模式下,作为主定时器使能
TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_Update); // TIM - EGR寄存器, 定义UG位,产生更新事件
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Enable);
// PWM通道,TIM1 - 通道4设置函数,100/100 or 0/100,方向信号
TIM_OCInitStructure.TIM_Pulse = DIR_A; // 初始化 TIM1-OC4
TIM_OC4Init( TIM1, &TIM_OCInitStructure); // CH4预装载使能,修改
TIM_OC4PreloadConfig( TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE); // 预装载使能
}
//定时器2从模式
void TIM2_GPIO_Config(u32 PulseNum_A)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_A;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM2, TIM_TS_ITR0); // TIM1-主,TIM2-从,表中对应 ITR0
TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_External1); //主从模式下作为从定时器使能
TIM_ITConfig( TIM2, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure);
}
// Cycle_A = Preiod_A; PulseNum_A 输出脉冲个数; DIR_A 高/低电平-方向信号
void PWM_Output_A(u16 Cycle_A, u32 PulseNum_A, u16 DIR_A) // TIM1-主,TIM2-从,输出PWM
{
TIM2_GPIO_Config(PulseNum_A);
TIM_Cmd( TIM2, ENABLE);
TIM_ClearITPendingBit( TIM2, TIM_IT_Update);
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE);
TIM1_GPIO_Config( 72, Cycle_A, Cycle_A / 2, DIR_A); // 72M / 72 = 1MHz;
TIM_Cmd( TIM1, ENABLE);
TIM_CtrlPWMOutputs( TIM1, ENABLE); // 高级定时器必须有,使能其输出
}
/*********** Group B ***********/
// 定时器5主模式
void TIM5_GPIO_Config(uint16_t TIM5_Prescaler, uint16_t TIM5_Period, uint16_t CCR_B, uint16_t DIR_B)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM5通道1\2
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM5, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // TIM5: CH1 - PA0, CH2 - PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 时钟频率设置
TIM_TimeBaseStructure.TIM_Period = TIM5_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM5_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM5, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置工作模式是PWM,且为PWM1工作模式,TIMx_CNT<TIMx_CCR1时为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 也就是使能PWM输出到端口
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM5 - 通道1设置函数,50/100,脉冲信号
TIM_OCInitStructure.TIM_Pulse = CCR_B; // 设置待装入捕获寄存器的脉冲值
TIM_OC1Init( TIM5, &TIM_OCInitStructure); // 初始化 TIM5-OC1
TIM_SelectMasterSlaveMode( TIM5, TIM_MasterSlaveMode_Enable); // 定时器主从模式使能
TIM_SelectOutputTrigger( TIM5, TIM_TRGOSource_Update); // 选择触发方式:使用更新事件作为触发输出
TIM_OC1PreloadConfig( TIM5, TIM_OCPreload_Enable); // CH1预装载使能,修改
// PWM通道,TIM5 - 通道2设置函数,100/100 or 0/100,方向信号
TIM_OCInitStructure.TIM_Pulse = DIR_B; // 初始化 TIM5-OC2
TIM_OC2Init( TIM5, &TIM_OCInitStructure); // CH2预装载使能,修改
TIM_OC2PreloadConfig( TIM5, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig( TIM5, ENABLE); // 使能ARR预装载寄存器
}
//定时器8从模式
void TIM8_GPIO_Config(u32 PulseNum_B)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM8, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_B;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM8, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM8, TIM_TS_ITR3); // TIM5-主,TIM8-从 ITR3
TIM_SelectSlaveMode( TIM8, TIM_SlaveMode_External1);
TIM_ITConfig( TIM8, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure);
}
// Cycle_B = Preiod_B; PulseNum_B 输出脉冲个数; DIR_B 高/低电平-方向信号
void PWM_Output_B(u16 Cycle_B, u32 PulseNum_B, u16 DIR_B) // TIM5-主,TIM8-从
{
TIM8_GPIO_Config(PulseNum_B);
TIM_Cmd( TIM8, ENABLE);
TIM_ClearITPendingBit( TIM8, TIM_IT_Update);
TIM_ITConfig( TIM8, TIM_IT_Update, ENABLE);
TIM5_GPIO_Config( 72, Cycle_B, Cycle_B / 2, DIR_B);
TIM_Cmd( TIM5, ENABLE);
}
/*********** Group C ***********/
// 定时器3主模式
void TIM3_GPIO_Config(uint16_t TIM3_Prescaler, uint16_t TIM3_Period, uint16_t CCR_C, uint16_t DIR_C)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM3通道1\2
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // TIM3_CH1 PA6, CH2 - PA7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 时钟频率设置
TIM_TimeBaseStructure.TIM_Period = TIM3_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM3_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置工作模式是PWM,且为PWM1工作模式,TIMx_CNT<TIMx_CCR1时为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 也就是使能PWM输出到端口
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM3 - 通道1设置函数,50/100
TIM_OCInitStructure.TIM_Pulse = CCR_C ; // 设置待装入捕获寄存器的脉冲值
TIM_OC1Init( TIM3, &TIM_OCInitStructure); // 初始化 TIM3-OC1
TIM_SelectMasterSlaveMode( TIM3, TIM_MasterSlaveMode_Enable); // 定时器主从模式使能
TIM_SelectOutputTrigger( TIM3, TIM_TRGOSource_Update); // 选择触发方式:使用更新事件作为触发输出
TIM_OC1PreloadConfig( TIM3, TIM_OCPreload_Enable); // CH1预装载使能,修改
// PWM通道,TIM3 - 通道2设置函数,100/100 or 0/100
TIM_OCInitStructure.TIM_Pulse = DIR_C; // 初始化 TIM3-OC2
TIM_OC2Init( TIM3, &TIM_OCInitStructure); // CH2预装载使能,修改
TIM_OC2PreloadConfig( TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig( TIM3, ENABLE); // 使能ARR预装载寄存器
}
// 定时器4从模式
void TIM4_GPIO_Config(u32 PulseNum_C)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_C;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM4, TIM_TS_ITR2); // TIM3-主,TIM4-从 ITR2
TIM_SelectSlaveMode( TIM4,TIM_SlaveMode_External1 ); // 等同 TIM4->SMCR |= 0x07
TIM_ITConfig( TIM4, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// Cycle_C = Preiod_C; PulseNum_C 输出脉冲个数; DIR_C 高/低电平-方向信号
void PWM_Output_C(u16 Cycle_C, u32 PulseNum_C, u16 DIR_C) // TIM3-主,TIM4-从
{
TIM4_GPIO_Config(PulseNum_C);
TIM_Cmd( TIM4, ENABLE);
TIM_ClearITPendingBit( TIM4, TIM_IT_Update);
TIM_ITConfig( TIM4, TIM_IT_Update, ENABLE);
TIM3_GPIO_Config(72, Cycle_C, Cycle_C / 2, DIR_C);
TIM_Cmd( TIM3, ENABLE);
}
// Group A
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus( TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM2, TIM_IT_Update); // 清除中断标志位
TIM_CtrlPWMOutputs( TIM1, DISABLE); // 主 输出使能关闭,高级定时器必须有
TIM_Cmd( TIM1, DISABLE); // 关闭定时器1
TIM_Cmd( TIM2, DISABLE); // 关闭定时器2
TIM_ITConfig( TIM2, TIM_IT_Update, DISABLE);
}
}
// Group B
void TIM8_UP_IRQHandler(void)
{
if (TIM_GetITStatus( TIM8, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM8, TIM_IT_Update); // 清除中断标志位
TIM_Cmd( TIM5, DISABLE); // 关闭定时器5
TIM_Cmd( TIM8, DISABLE); // 关闭定时器8
TIM_ITConfig( TIM8, TIM_IT_Update, DISABLE);
}
}
// Group C
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus( TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM4, TIM_IT_Update); // 清除中断标志位
TIM_Cmd( TIM3, DISABLE); // 关闭定时器3
TIM_Cmd( TIM4, DISABLE); // 关闭定时器4
TIM_ITConfig( TIM4, TIM_IT_Update, DISABLE);
}
}
- main.c
/**
******************************************************************************
* @file main.c
* @author SieYuan
* @version V1.0
* @date 2020-12-09
* @brief 配置主从定时器,输出精确脉冲个数的PWM波
*
******************************************************************************
*/
#include "stm32f10x.h"
#include "timer.h" // 主从定时器配置
#include "led.h"
#include "exti.h"
#include "delay.h"
int main(void)
{
// 初始化
LED_Init();
KEY_Init();
EXTIx_Init();
// 初始化完成
LED1(0);
delay_ms(500);
LED1(1);
delay_ms(500);
LED1(0);
delay_ms(500);
LED1(1);
PWM_Output_A(1000, 10, 1000); // 72M / 72 = 1MHz, 1M / 1k = 1kHz
PWM_Output_B(500, 10, 0); // 72M / 72 = 1MHz, 1M / 500 = 2kHz
PWM_Output_C(100, 10, 0); // 72M / 72 = 1MHz, 1M / 100 = 10kHz
while (1);
}
- 注意事项1:高级定时器作为主定时器,如在 TIM1_GPIO_Config() 函数中需要配置:
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
大家可以右键 Go To Definition,当采用 TIM1 和 TIM8 时,TIM_RepetitionCounter 才有效。它的作用是当从定时器对脉冲计数溢出时,会产生一个更新事件使得 高级定时器TIM1和TIM8 重复计数寄存器(TIMx_RCR)的REP值减1。当REP_CNT = 0时,又会产生一个更新事件并且计数器REP_CNT重新从设定的REP值开始计数(向下计数)。也就是说,TIM_RepetitionCounter = 0, 从定时器溢出1次(REP+1)就会进入中断;如果设置 TIM_RepetitionCounter = 1, 从定时器溢出2次(REP+1)才会进入中断,如例程中产生20(10*2)次脉冲才会停止。具体规范地说明,参考 stm32f10x_tim.h 或《STM32中文参考手册》P246,如下图。
- 注意事项2:高级定时器作为主定时器,如在 PWM_Output_A() 函数中需要配置:
TIM_CtrlPWMOutputs( TIM1, ENABLE);
同时,在中断服务函数中也要关闭,如TIM2_IRQHandler()中:
TIM_CtrlPWMOutputs( TIM1, DISABLE);
TIM_Cmd( TIM1, DISABLE);
大家依然可以右键 Go To Definition,当采用 TIM1 和 TIM8 时,要在刹车和死区寄存器(TIMx_BDTR)中使能MOE。具体规范地说明,参考 stm32f10x_tim.c 或《STM32中文参考手册》P248,如下图。
- 小程序实验结果:TIM1-CH1输出,10个脉冲,1kHz(这里输出PWM后一直是高电平,这个未找到原因,但输出的是10个脉冲)
- 小程序实验结果:TIM5-CH1输出,也是10个脉冲,2kHz。
- 小程序实验结果:TIM3-CH1输出,也是10个脉冲,10kHz。
STM32F407:F407的定时器配置与F103稍有区别,我在移植时想当然了,没有仔细学习,花了很长时间才找到问题。只有少部分与F103不同,我在注释中会添加“F407”以表示。(开始我很奇怪为什么都是F103的主从定时器,没什么人写F407,原来改动很小,而且是常用设置。看来还是要认真仔细,独立思考呀)
- Timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
/* Group A: TIM1 - 主定时器,TIM2 - 从定时器
TIM1: CH3 - PE13, CH4 - PE14 RCC_APB2Periph_TIM1, RCC_AHB1Periph_GPIOE
TIM2: TIM2: RCC_APB1Periph_TIM2
Group B: TIM4 - 主定时器,TIM8 - 从定时器
TIM4: CH1 - PB6, CH2 - PB7 RCC_APB1Periph_TIM4
TIM8: RCC_APB2Periph_TIM8
Group C: TIM3 - 主定时器,TIM4 - 从定时器
TIM3: TIM3: CH3 - PB0, CH4 - PB1 RCC_APB1Periph_TIM3
TIM5: RCC_APB1Periph_TIM5
*/
/*****定时器初始化函数*****/
// Group A
void TIM1_GPIO_Config(u16 TIM1_Prescaler, u16 TIM1_Period, u16 CCR_A, u16 DIR_A);
void TIM2_GPIO_Config(u32 PulseNum_A);
void PWM_Output_A(u16 Cycle_A, u32 PulseNum_A, u16 DIR_A);
void TIM2_IRQHandler(void);
// Group B
void TIM4_GPIO_Config(u16 TIM4_Prescaler, u16 TIM4_Period, u16 CCR_B, u16 DIR_B);
void TIM8_GPIO_Config(u32 PulseNum_B);
void PWM_Output_B(u16 Cycle_B, u32 PulseNum_B, u16 DIR_B);
void TIM8_UP_TIM13_IRQHandler(void);
// Group C
void TIM3_GPIO_Config(u16 TIM3_Prescaler, u16 TIM3_Period, u16 CCR_C, u16 DIR_C);
void TIM5_GPIO_Config(u32 PulseNum_C);
void PWM_Output_C(u16 Cycle_C, u32 PulseNum_C, u16 DIR_C);
void TIM5_IRQHandler(void);
#endif /* __TIMER_H */
/****************************END OF FILE****************************/
- Timer.c
#include "Timer.h"
/*********** Group A ***********/
// 定时器1主模式
void TIM1_GPIO_Config(u16 TIM1_Prescaler, u16 TIM1_Period, u16 CCR_A, u16 DIR_A)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE);
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOE, ENABLE); // F407: GPIO 串口初始化 与F103不同
GPIO_PinAFConfig( GPIOE, GPIO_PinSource13, GPIO_AF_TIM1); // F407 端口复用映射
GPIO_PinAFConfig( GPIOE, GPIO_PinSource14, GPIO_AF_TIM1); // F407 端口复用映射
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; // TIM1_CH1 - PE13, CH4 - PE14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = TIM1_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM1_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数,一定要 = 0
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM1 - 通道1设置函数,50/100
TIM_OCInitStructure.TIM_Pulse = CCR_A; // 设置待装入捕获寄存器的脉冲值
TIM_OC3Init( TIM1, &TIM_OCInitStructure);
TIM_SelectMasterSlaveMode( TIM1, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_Update);
TIM_OC3PreloadConfig( TIM1, TIM_OCPreload_Enable);
// PWM通道,TIM1 - 通道4设置函数,100/100 or 0/100
TIM_OCInitStructure.TIM_Pulse = DIR_A; // 初始化 TIM1-OC4
TIM_OC4Init( TIM1, &TIM_OCInitStructure); // CH4预装载使能,修改
TIM_OC4PreloadConfig( TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE);
}
//定时器2从模式
void TIM2_GPIO_Config(u32 PulseNum_A)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_A;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM2, TIM_TS_ITR0); // TIM1-主,TIM2-从
TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_External1);
TIM_ITConfig( TIM2, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure);
}
void PWM_Output_A(u16 Cycle_A, u32 PulseNum_A, u16 DIR_A) // TIM1-主,TIM2-从
{
TIM2_GPIO_Config(PulseNum_A);
TIM_Cmd( TIM2, ENABLE);
TIM_ClearITPendingBit( TIM2, TIM_IT_Update);
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE);
TIM1_GPIO_Config( 84, Cycle_A, Cycle_A / 2, DIR_A); //F407: 高级定时器是 168MHz, 故168MHz / 84 = 2MHz
TIM_Cmd( TIM1, ENABLE);
TIM_CtrlPWMOutputs( TIM1, ENABLE); // 高级定时器 TIM1 使能 MOE
}
/*********** Group B ***********/
// 定时器4主模式
void TIM4_GPIO_Config(u16 TIM4_Prescaler, u16 TIM4_Period, u16 CCR_B, u16 DIR_B)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4, ENABLE);
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE); // F407: GPIO 串口初始化 与F103不同
GPIO_PinAFConfig( GPIOB, GPIO_PinSource6, GPIO_AF_TIM5); // F407 端口复用映射
GPIO_PinAFConfig( GPIOB, GPIO_PinSource7, GPIO_AF_TIM5); // F407 端口复用映射
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // TIM4_CH3 PB0, CH4 - PB1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 时钟频率设置
TIM_TimeBaseStructure.TIM_Period = TIM4_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM4_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置工作模式是PWM,且为PWM1工作模式,TIMx_CNT<TIMx_CCR1时为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 也就是使能PWM输出到端口
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM4 - 通道1设置函数,50/100
TIM_OCInitStructure.TIM_Pulse = CCR_B; // 设置待装入捕获寄存器的脉冲值
TIM_OC1Init( TIM4, &TIM_OCInitStructure); // 初始化 TIM4-OC3
TIM_SelectMasterSlaveMode( TIM4, TIM_MasterSlaveMode_Enable); // 定时器主从模式使能
TIM_SelectOutputTrigger( TIM4, TIM_TRGOSource_Update); // 选择触发方式:使用更新事件作为触发输出
TIM_OC1PreloadConfig( TIM4, TIM_OCPreload_Enable); // CH1预装载使能,修改
// PWM通道,TIM4 - 通道2设置函数,100/100 or 0/100
TIM_OCInitStructure.TIM_Pulse = DIR_B; // 初始化 TIM4-OC4
TIM_OC2Init( TIM4, &TIM_OCInitStructure); // CH2预装载使能,修改
TIM_OC2PreloadConfig( TIM4, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig( TIM4, ENABLE); // 使能ARR预装载寄存器
}
//定时器8从模式
void TIM8_GPIO_Config(u32 PulseNum_B)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM8, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_B;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM8, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM8, TIM_TS_ITR2); // TIM2-主,TIM4-从
TIM_SelectSlaveMode( TIM8, TIM_SlaveMode_External1);
TIM_ITConfig( TIM8, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure);
}
void PWM_Output_B(u16 Cycle_B, u32 PulseNum_B, u16 DIR_B) // TIM2-主,TIM4-从
{
TIM8_GPIO_Config(PulseNum_B);
TIM_Cmd( TIM8, ENABLE);
TIM_ClearITPendingBit( TIM8, TIM_IT_Update);
TIM_ITConfig( TIM8, TIM_IT_Update, ENABLE);
TIM4_GPIO_Config( 84, Cycle_B, Cycle_B / 2, DIR_B); //F407:通用定时器是 84MHz, 故84MHz / 84 = 1MHz
TIM_Cmd( TIM4, ENABLE);
}
/*********** Group C ***********/
// 定时器3主模式
void TIM3_GPIO_Config(u16 TIM3_Prescaler, u16 TIM3_Period, u16 CCR_C, u16 DIR_C)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM3通道1\2 - PWM Z轴步进电机脉冲信号
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE);
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE); // F407: GPIO 串口初始化 与F103不同
GPIO_PinAFConfig( GPIOB, GPIO_PinSource0, GPIO_AF_TIM3); // F407 端口复用映射
GPIO_PinAFConfig( GPIOB, GPIO_PinSource1, GPIO_AF_TIM3); // F407 端口复用映射
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // TIM3_CH1 PA6, CH2 - PA7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 时钟频率设置
TIM_TimeBaseStructure.TIM_Period = TIM3_Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = TIM3_Prescaler - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
// 设置工作模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置工作模式是PWM,且为PWM1工作模式,TIMx_CNT<TIMx_CCR1时为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 也就是使能PWM输出到端口
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出极性
// PWM通道,TIM3 - 通道1设置函数,50/100
TIM_OCInitStructure.TIM_Pulse = CCR_C; // 设置待装入捕获寄存器的脉冲值
TIM_OC3Init( TIM3, &TIM_OCInitStructure); // 初始化 TIM3-OC1
TIM_SelectMasterSlaveMode( TIM3, TIM_MasterSlaveMode_Enable); // 定时器主从模式使能
TIM_SelectOutputTrigger( TIM3, TIM_TRGOSource_Update); // 选择触发方式:使用更新事件作为触发输出
TIM_OC3PreloadConfig( TIM3, TIM_OCPreload_Enable); // CH1预装载使能,修改
// PWM通道,TIM3 - 通道2设置函数,100/100 or 0/100
TIM_OCInitStructure.TIM_Pulse = DIR_C; // 初始化 TIM3-OC2
TIM_OC4Init( TIM3, &TIM_OCInitStructure); // CH2预装载使能,修改
TIM_OC4PreloadConfig( TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig( TIM3, ENABLE); // 使能ARR预装载寄存器
}
// 定时器5从模式
void TIM5_GPIO_Config(u32 PulseNum_Z)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum_Z;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM5, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger( TIM5, TIM_TS_ITR1);
TIM_SelectSlaveMode( TIM5,TIM_SlaveMode_External1 ); // 等同下一句 TIM5->SMCR |= 0x07
TIM_ITConfig( TIM5, TIM_IT_Update, DISABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void PWM_Output_C(u16 Cycle_C, u32 PulseNum_C, u16 DIR_C) // TIM3-主,TIM5-从
{
TIM5_GPIO_Config(PulseNum_C);
TIM_Cmd( TIM5, ENABLE);
TIM_ClearITPendingBit( TIM5, TIM_IT_Update);
TIM_ITConfig( TIM5, TIM_IT_Update, ENABLE);
TIM3_GPIO_Config( 56, Cycle_C, Cycle_C / 2, DIR_C); //84MHz / 56 = 1.5MHz
TIM_Cmd( TIM3, ENABLE);
}
// Group A
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus( TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM2, TIM_IT_Update); // 清除中断标志位
TIM_CtrlPWMOutputs( TIM1, DISABLE); // 高级定时器 TIM1 关闭MOE
TIM_Cmd( TIM1, DISABLE); // 关闭定时器1
TIM_Cmd( TIM2, DISABLE); // 关闭定时器2
TIM_ITConfig( TIM2, TIM_IT_Update, DISABLE);
}
}
// Group B
void TIM8_UP_TIM13_IRQHandler(void)
{
if (TIM_GetITStatus( TIM8, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM8, TIM_IT_Update); // 清除中断标志位
TIM_Cmd( TIM4, DISABLE); // 关闭定时器4
TIM_Cmd( TIM8, DISABLE); // 关闭定时器8
TIM_ITConfig( TIM8, TIM_IT_Update, DISABLE);
}
}
// Group C
void TIM5_IRQHandler(void)
{
if (TIM_GetITStatus( TIM5, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit( TIM5, TIM_IT_Update); // 清除中断标志位
TIM_Cmd( TIM3, DISABLE); // 关闭定时器3
TIM_Cmd( TIM5, DISABLE); // 关闭定时器5
TIM_ITConfig( TIM5, TIM_IT_Update, DISABLE);
}
}
/****************************END OF FILE****************************/
- main.c
/**
**************************************************************************************
* @file main.c
* @author SieYuan
* @version V1.0
* @date 2020-12-09
* @brief 配置主从定时器,输出精确脉冲个数的PWM波
*
**************************************************************************************
*/
#include "sys.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "Timer.h"
#include "delay.h"
int main(void)
{
/* 程序初始化:对【LED】【KEY】【EXIT】【USART】*/
LED_Init();
KEY_Init();
EXTIx_Init();
delay_init(168);
LED1 = 0;
delay_ms(500);
LED1 = 1;
delay_ms(500);
LED1 = 0;
delay_ms(500);
LED1 = 1;
/* 程序初始化完成 !*/
PWM_Output_A(1000, 10, 1000); // 168M / 84 = 2MHz, 2M / 1000 = 2kHz
PWM_Output_B(100, 10, 0); // 84M / 84 = 1MHz, 1M / 100 = 10kHz
PWM_Output_C(1000, 10, 0); // 84M / 56 = 1.5MHz, 1.5M / 1000 = 1.5kHz
while(1);
}
/****************************END OF FILE****************************/
- 注意事项3:串口(GPIOA—GPIOK)在AHB1总线桥,详见 stm32f4xx_rcc.c 的RCC_AHB1PeriphClockCmd();高级定时器 TIM1\TIM8,通用定时器TIM9\TIM10\TIM11在APB2总线桥,详见 stm32f4xx_rcc.c 的RCC_APB2PeriphClockCmd();通用定时器TIM2—TIM7、TIM12\TIM13\TIM14在APB1总线桥,详见 stm32f4xx_rcc.c 的RCC_APB1PeriphClockCmd() 。
如:
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE);
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOE, ENABLE);
- 注意事项4:STM32F4端口复用时,要使用函数 GPIO_PinAFConfig(),详见 stm32f4xx_gpio.c ;需要,不是STM32F1配置引脚宏定义格式(如 GPIO_Pin_3),而应该是 GPIO_PinSource3。另外,每次只能配置一个引脚的端口复用映射。
举例:上面TIM1_GPIO_Config()
正确方式:
GPIO_PinAFConfig( GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);
GPIO_PinAFConfig( GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);
错误方式:
GPIO_PinAFConfig( GPIOE, GPIO_Pin_13, GPIO_AF_TIM1);
GPIO_PinAFConfig( GPIOE, GPIO_Pin_14, GPIO_AF_TIM1);
或
GPIO_PinAFConfig( GPIOE, GPIO_PinSource13 | GPIO_PinSource14, GPIO_AF_TIM1);
-
小程序实验结果:TIM1-CH3输出,也是10个脉冲,2kHz。
-
小程序实验结果:TIM4-CH1输出,也是10个脉冲,10kHz。
-
小程序实验结果:TIM3-CH3输出,也是10个脉冲,1.5kHz。
小结
-
以上就是我使用主从定时器方式控制PWM波输出给步进电机驱动器来控制步进电机的解决方法(纯开环,无法考虑丢步等问题)。
-
这种方法很多论坛都有资料,我只是一个借鉴、总结优秀的人提出的方法并复现出来的搬运工、学习者。可能针对我搜索中遇到诸多不便,以我小白的视角做了一定的补充,希望给其他小白提供便利。我还将两份代码压缩上传,供大家下载(其实上面展示出来的已经够用了)。
-
第一次写这种交流分享的文档。作为小白,欢迎大家发现文章中的问题,检验我的代码,提出改进的建议,期待你留言与我交流、探讨,共同进步。
-
CSDN下载链接: STM32:F103/F407定时器主从模式输出精准脉冲个数.