导语:上一节讲述了时钟树和基本定时器的配置方法,本节先介绍通用定时器和基本定时器的差异,然后粗略讲述PWM波原理,然后讲述如何配置通用定时器,最后进行PWM波驱动电机的示例。
PWM:
基本定时器计数方式只能向上,即1.2.3.4.5......
而通用定时器计数方式有上/下/中心对齐三种方式,即1.2.3.4.5...../x.x-1....3.2.1/1.2.3....x.x-1.x-2....3.2.1三种方式。
通用定时器可设置TIM_ClockDivision的值,是时间分割系数,这与数字滤波器的采样频率有关,即跟输入捕获有关,跟我们PWM输出没有关系,暂时不用管。
以F407为例,通用定时器有TIM2~TIM5和TIM9~TIM14,每个定时器相互独立互不干扰,每个通用定时器的功能都相同,具体区别只涉及到参数位数大小,接口时钟频率上限值之类的,初学暂时不用区分。通用定时器为通用定时器一个通用定时器有四个通道,相互独立,可以输出四个不同的PWM波。
所谓PWM波,称为脉冲宽度调制,就是在一个周期内分为两段时间,前一段时间输出高/低电平,后一段时间输出低/高电平,这样总的平均电压就可以在高电平和低电平之间任意设置,取决于你高电平持续时间占整个周期的比例,网上教程很多,这里粗略介绍一下。
下图为我们之前设置基本定时器每隔一段特定时间进行中断的方法
CNT从0开始向上不断计数,到达设定的自动装载值ARR后回到0,开始下一次循环,我们只需要设置好分频系数(控制计数速度)和ARR值(计数终点)就可以控制循环周期。
下图为我们实现PWM输出的方法
假设我们设定一个值为CCR,称为比较值,介于0到ARR之间,在CNT计数到ARR之前,CNT小于CCR的时间里IO口输出低电平,CNT大于CCR的时间里IO口输出高电平,这样我们通过控制CCR的值就可以控制高电平占整个周期的时间,一个周期内的平均电平也就得到了控制,我们可以通过这种方法输出不同大小的电压值。
复用IO口
复用IO口又称之为AFIO口,和我们一般所说的GPIO口不同
GPIO口是用户端直接向CPU发送指令,CPU收到指令后向对应IO口的写入寄存器配置,直接对IO口进行配置高低电平
AFIO是指IO口的复用功能,是为外设的输出IO服务,当外设(定时器就是一种)需要IO口来输出时,我们可以配置特定的IO口为复用功能,由外设直接控制IO口的高低电平,无需通过CPU来控制,STM32的每个IO口有很多不同的复用功能,下图就是F407引脚分配图的不同IO口的复用功能汇总,可见每个IO口除了自己本身的通用IO功能,还有甚至八九个不同的复用IO功能,需要使用特定的复用功能时需要借助库函数来指定。
复用IO的配置很简单,我们之后代码用到时再说。
示例,利用通用定时器控制12伏直流电机,实现调速和变向功能
1.电机的驱动只需要对电机的两个接口输出高低电平就行, 两端口的电压差决定速度大小,电压差的正负决定转向。但单片机无法给电机供电,电机需要12伏,而我们32最多只有5伏的电压,需要一个叫做电压驱动模块的小板子和一个12伏外接电源进行驱动。
下图是我用的电机驱动模块,在淘宝上找的一个一样的功能图,单片机的输出IO口接在电机驱动模块上,电源连接电机驱动模块,电机驱动模块的两个输出端口接电机即可,驱动模块使能端记得用跳帽连接旁边的5伏,具体接线不在这里讲啦。
2.每个通用定时器有四个独立通道,可以输出四个不同PWM波,而且有专门的库函数来配置它的PWM输出模式。
我们要实现电机的正反转,如前所述,需要给正负电压差,我们可以在一个IO口设置为一个通用定时器的复用端口输出PWM波,再设置一个新的普通的通用IO口高电平或低电平,然后两个IO口分别接电机两个端口(通过电机驱动模块来接,不是直接接),这样通过控制新的IO口电平的高低,我们可以得到不同的电压差,就可以实现电机的正反转。同时我们通过设置原来复用IO口的PWM的高电平持续时间来控制平均电平,达到调速的目的。
3.配置通用定时器的PWM模式
通用定时器有专门的PWM输出模式,配置TIM_OCInitStructure的参数,需要配置其中以下参数:
(1)PWM模式:1或2,1为向上计数时,CNT小于CCR时为有效电平,CNT大于CCR时为无效电平;2模式相反。
(2)设置输出极性,OCPolarity为High还是Low,如果为High,即有效电平为高电平;如果为Low,即有效电平为低电平,因此该设置可以和PWM模式结合来控制一个周期内两段时间哪一段为高电平哪一段为低电平。
(3)设置CCR比较值,控制高电平一个周期内的持续时间来控制平均电平
(4)PWM通道输出使能,TIM使能,结构体初始化
代码讲解:
PWM_config.c
提醒一点,在F103系列中需要使能AFIO的时钟,我用的F407,F407简化了复用功能,不用手动去使能了,F407有少部分函数代码也与103不同,思路是完全一样的。
#include"stm32f4xx.h"
void PWM_config(int ARR,int CCR)
{
/*引用库函数的GPIO,TIM2时基,TIM2PWM输出的结构体进行初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //初始化GPIO结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//初始化TIM2时基结构体
TIM_OCInitTypeDef TIM_OCInitStructure; //初始化TIMPWM输出的结构体
/* 配置TIM2_CH3对应的PA2为复用口(配置GPIO结构体) */
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_TIM2); //设置PA2的TIM2通道复用
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//配置为复用推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置TIM2的时基结构体 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = ARR; //自动装载值,我们自己设置
TIM_TimeBaseStructure.TIM_Prescaler =8399;//分频系数,记得等于实际分频系数减一
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//分割系数,关系不大
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //计数方式,向上
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* 配置TIM2PWM的结构体 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选用PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = CCR; //比较值设置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平输出为高电平
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);//自动重载使能
TIM_Cmd(TIM2, ENABLE); //TIM2使能
}
/* 设置另一个IO口(正反转即取决于它的高低电平) */ //选取的PC1
void GPIO_config(int dir)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_WriteBit(GPIOC,GPIO_Pin_1,dir);
}
main.c
库函数有个直接设置占空比的函数为TIM_SetCompare3(TIM2,50);
意思是设置TIM2的3通道的比较值为50,这个看函数也懂啦,不必过多解释,这个可以很方便的设置定时器的占空比。
#include"stm32f4xx.h"
#include "led.h"
#include"basic_tim.h"
#include"PWM_config.h"
int main(void)
{
GPIO_config(1); //设置另一个端口为高电平(控制电机正反转)
PWM_config(100,50); //设置占空比为50(控制电机速度大小)
while(1);//死循环,保证STM32正常工作
}
PWM_config.h
#ifndef PWM_CONFIG_H
#define PWM_CONFIG_H
void PWM_config(int ARR,int CCR);
void GPIO_config(int dir);
#define Bit_RESET 0;
#define Bit_SET 1;
#endif
本节就到这里了,中途写到2000字突然撤销为500字并保存了,我真的哭死!!!
如果想要代码也可以私聊我或者留言
接下来可能会更的比较慢,但到下一期应该不超过5天(应该吧QAQ,我相信自己),我要去学编码器的使用,学成后来记录它的PID算法和编码器模式,实现位置环的控制。
再次感谢大家,有不足和错误求大佬指正!orz