小四轴从入门到入土(软件篇1——PWM输出控制电机)

STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有互补输出信号,每个定时器有 8 个外部 IO。更加具体的分类详情见图 :
小四轴从入门到入土(软件篇1——PWM输出控制电机)_第1张图片
PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器ARR 的值决定,占空比由比较寄存器CCR 的值决定。PWM 模式分为两种,PWM1 和PWM2,总得来说是差不多,我使用的是PWM1模式。以计数器CNT 计数的方向不同还分为边沿对齐模式和中心对齐模式,PWM 信号主要都是用来控制电机,一般的电机控制用的都是边沿对齐模式。
边沿对齐模式:在递增计数模式下,计数器从 0 计数到自动重载值( TIMx_ARR 寄存器的内容),然后重新从 0 开始计数并生成计数器上溢事件。
小四轴从入门到入土(软件篇1——PWM输出控制电机)_第2张图片
在边沿对齐模式下,计数器CNT 只工作在一种模式,递增或者递减模式。这里我们以CNT 工作在递增模式为例,在中,ARR=8,CCR=4,CNT 从0 开始计数,当CNT 定时器初始化结构体详解
查阅stm32库文件(stm32f10x_tim.h)可知,定时器外设有四个初始化结构体,分别为时基初始化结构体、输出比较初始化结构体、输入捕获初始化结构体和断路和死区初始化结构体。本程序用到的是TIM计时器的输出功能,用到的分别是时基初始化结构体TIM_TimeBaseInitTypeDef 、输出比较初始化结构体TIM_OCInitTypeDef。

typedef struct {
uint16_t TIM_Prescaler; // 预分频器
uint16_t TIM_CounterMode; // 计数模式
uint32_t TIM_Period; // 定时器周期
uint16_t TIM_ClockDivision; // 时钟分频
uint8_t TIM_RepetitionCounter; // 重复计算器
 } TIM_TimeBaseInitTypeDef;

TIM_Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器计数时钟CK_CNT,它设定PSC 寄存器的值。计算公式为:计数器时钟频(fCK_CNT)等于fCK_PSC / (PSC[15:0] + 1),可实现1 至65536 分频。
TIM_CounterMode:定时器计数方式,可设置为向上计数、向下计数以及中心对齐。高级控制定时器允许选择任意一种。
TIM_Period:定时器周期,实际就是设定自动重载寄存器ARR 的值,ARR 为要装载到实际自动重载寄存器(影子寄存器)的值,可设置范围为0 到65535。
TIM_ClockDivision:时钟分频,设置定时器时钟CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择1、2、4 分频。
TIM_RepetitionCounter:重复计数器,只有8 位,只存在于高级定时器。

typedef struct {
uint16_t TIM_OCMode; // 比较输出模式
uint16_t TIM_OutputState; // 比较输出使能
uint16_t TIM_OutputNState; // 比较互补输出使能
uint32_t TIM_Pulse; // 脉冲宽度
uint16_t TIM_OCPolarity; // 输出极性
uint16_t TIM_OCNPolarity; // 互补输出极性
uint16_t TIM_OCIdleState; // 空闲状态下比较输出状态
uint16_t TIM_OCNIdleState; // 空闲状态下比较互补输出状态
} TIM_OCInitTypeDef;

TIM_OCMode:比较输出模式选择,总共有八种,常用的为PWM1/PWM2。本程序使用PWM1,它设定CCMRx 寄存器OCxM[2:0]位的值。
TIM_OutputState:比较输出使能,决定最终的输出比较信号OCx 是否通过外部引脚输出。它设定TIMx_CCER 寄存器CCxE/CCxNE 位的值。
TIM_OutputNState:比较互补输出使能,决定OCx 的互补信号OCxN 是否通过外部引脚输出。它设定CCER 寄存器CCxNE 位的值。
TIM_Pulse:比较输出脉冲宽度,实际设定比较寄存器CCR 的值,决定脉冲宽度。可设置范围为0 至65535。
TIM_OCPolarity:比较输出极性,可选OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。它设定CCER 寄存器的CCxP 位的值。
TIM_OCNPolarity:比较互补输出极性,可选OCxN 为高电平有效或低电平有效。它设定TIMx_CCER 寄存器的CCxNP 位的值。
TIM_OCIdleState:空闲状态时通道输出电平设置,可选输出1 或输出0,即在空闲状态(BDTR_MOE 位为0)时,经过死区时间后定时器通道输出高电平或低电平。它设定CR2 寄存器的OISx 位的值。
TIM_OCNIdleState:空闲状态时互补通道输出电平设置,可选输出1 或0,即在空闲状态(BDTR_MOE 位为0)时,经过死区时间后定时器互补通道输出高电平或低电平,设定值必须与TIM_OCIdleState 相反。它设定是CR2 寄存器的OISxN 位的值。
硬件连接:
本例使用的是PB9:TIM4_CH4(PWM1),PB15:TIM1_CH3N(PWM2),PB14:TIM1_CH2N(PWM3),PB13:TIM1_CH1N(PWM4)分别控制四个720空心杯电机。原本PWM1为TIM1_CH1,但是TIM1_CH1与TIM1_CH1N同时使用只能输出互补pwm(如果各位大神有办法使其单独输出请告知小弟,不胜感激)故而把PWM1通过跳线的方式接到PB9使用TIM4_CH4。

频率:Fpwm = 72M / ((arr+1)*(psc+1))(单位:Hz)
占空比:duty circle = TIMx->CCRn / arr(单位:%)0%为静止,100%为满载
代码:moto.c

#include "./moto/moto.h"
#include "stm32f10x.h"
int16_t pwm1 = 0;
int16_t pwm2 = 0;
int16_t pwm3 = 0;
int16_t pwm4 = 0;
void MOTO_INIT(void)
{
    TIM4_INIT();
	TIM1_INIT();
}
void SET_PWM(uint16_t pwm1,uint16_t pwm2,uint16_t pwm3,uint16_t pwm4)
{
	//限制pwm范围:0~1000
	if(pwm1>=PWM_MAX)	pwm1 = PWM_MAX;
	if(pwm1<=0)	pwm1 = 0;
 	if(pwm2>=PWM_MAX)	pwm2 = PWM_MAX;
	if(pwm2<=0)	pwm2 = 0;
	if(pwm3>=PWM_MAX)	pwm3 = PWM_MAX;
	if(pwm3<=0)	pwm3 = 0;
	if(pwm4>=PWM_MAX)	pwm4 = PWM_MAX;
	if(pwm4<=0)	pwm4 = 0;
	//设置pwm
	TIM4->CCR4 = pwm1;//PWM1,PB9
	TIM1->CCR3 = pwm2;//PWM2,PB15
	TIM1->CCR2 = pwm3;//PWM3,PB14
	TIM1->CCR1 = pwm4;//PWM4,PB13
}
void TIM4_INIT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  	TIM_TimeBaseInitTypeDef  TIM4_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM4_OCInitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //GPIOB时钟
 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 ,ENABLE);   //定时器4时钟
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
  	TIM_DeInit(TIM4);
  	 //TIM4时基初始化
  	 TIM4_TimeBaseStructure.TIM_Period = 999;		            
  	 TIM4_TimeBaseStructure.TIM_Prescaler =72 ;	//pwm时钟频率1000HZ	(1ms)
     TIM4_TimeBaseStructure.TIM_ClockDivision = 0;	
  	 TIM4_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	 TIM_TimeBaseInit(TIM4,&TIM4_TimeBaseStructure);   
  	 TIM_OCStructInit(&TIM4_OCInitStructure);
  	 TIM4_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  	 TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
     TIM4_OCInitStructure.TIM_Pulse = 0;    //初始占空比0
     TIM4_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
     TIM_OC4Init(TIM4,&TIM4_OCInitStructure);
     TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
	//使能计时器
  	TIM_Cmd(TIM4,ENABLE);
}
void TIM1_INIT(void)
{
GPIO_InitTypeDef GPIO1_InitStructure;
TIM_TimeBaseInitTypeDef  TIM1_TimeBaseStructure;
TIM_OCInitTypeDef  TIM1_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 ,ENABLE);   //定时器4
GPIO1_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO1_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO1_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO1_InitStructure);
TIM_DeInit(TIM1);
//TIM4时基初始化
TIM1_TimeBaseStructure.TIM_Period = 999;		            
TIM1_TimeBaseStructure.TIM_Prescaler =72;	//pwm时钟频率1000HZ	(1ms)
TIM1_TimeBaseStructure.TIM_ClockDivision = 0;	
TIM1_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
//配置TIM1互补输出通道输出pwm,若不是用互补通道,与上面TIM4一样即可
TIM_TimeBaseInit(TIM1,&TIM1_TimeBaseStructure);   
TIM_OCStructInit(&TIM1_OCInitStructure);
TIM1_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM1_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
TIM1_OCInitStructure.TIM_Pulse = 0;
TIM1_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM1_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(TIM1,&TIM1_OCInitStructure);
TIM_OC2Init(TIM1,&TIM1_OCInitStructure);
TIM_OC3Init(TIM1,&TIM1_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_Cmd(TIM1,ENABLE);
 //高级定时器(TIM1,TM8)必须加上这句
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

moto.h

#ifndef moto_H
#define moto_H
#include "stm32f10x.h"
#define PWM_MAX 999

void TIM4_INIT(void);
void TIM1_INIT(void);
void MOTO_INIT(void);
void SET_PWM(uint16_t pwm1,uint16_t pwm2,uint16_t pwm3,uint16_t pwm4);
void Tim4_Init(void);
#endif

你可能感兴趣的:(小四轴从入门到入土(软件篇1——PWM输出控制电机))