赞
踩
我觉得在冗长的理论之前,我们首先要对定时器的输出比较功能做一个理解。
【问】输出比较用来做什么?应用场景是什么??
输出比较,就是可以控制定时器,去比较 CNT 和 CCR 寄存器值的关系,来对输出的电平进⾏置1、置0或翻转的操作,⽤于输出⼀定频率和占空比的 PWM 波形。
【问】什么是PWM波?
PWM全称为脉冲宽度调制。
PWM波输出原理:
当 CNT<CCRx 时, IO 输出低电平(逻辑 0 );当 CNT>=CCRx 时, IO 输出高电平(逻辑 1 );当 CNT=ARR 时,定时器溢出, CNT 的值被清零,然后继续递增,依次循环;
占空比:高电平所占时间 / 周期时间
占空比 = CCR / (ARR + 1)频率 = CK_PSC / (PSC + 1) / (ARR + 1)
【问】我们已经知道了输出比较,就是通过cnt和arr的比较,输出高低电平,输出PWM波,那么PWM波有什么用???
1.PWM输出呼吸灯
之前学完了最简单的基本定时器,我们来学习通用定时器。
首先人眼对80Hz以上的刷新频率是感受不到闪烁的。
我们来分析一下比如说:
所以:频率相同,占空比越大,灯越亮。
改变占空比,即可改变灯的亮度,实现呼吸灯。
2.PWM控制电机转速
直流电机中,电机转速是周期内输出的平均电压值,那么占空比越大,输出电压越大,速度越大。
改变占空比,即可改变电机转速。
3.PWM控制舵机
舵机频率一般为50Hz,相当于20毫秒的脉冲,脉冲的高电平部分范围是0.5ms到2.5ms之间。如果是180度的舵机,那么:
0.5ms对应0度
1ms对应45度
以此类推,2.5ms对应180度
---------------------------------------------------------------------------------------------------------------------------------
有了这些知识铺垫后,我们开始定时器输出比较的学习。
之前学完了最简单的基本定时器,我们来学习通用定时器。
我们着重看一下输出比较这部分框图:
说白了就是先初始化定时器模块和输出比较模块,然后通过控制CCR的值,改变灯的亮度或者是电机的速度。
那么例程就写一个控制直流电机转速,为后面的STM32小车做铺垫。
首先看一下硬件连接:
因为直流电机属于大功率器件,单片机引脚输出电压太小无法直接驱动,所以需要一个电机驱动模块,这里用TB6612。
【PWM初始化】
- void PWM_Init(void)
- {
- /*开启时钟*/
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
-
- /*GPIO初始化*/
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA2引脚初始化为复用推挽输出
- //受外设控制的引脚,均需要配置为复用模式
-
- /*时基单元初始化*/
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
- TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值
- TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //预分频器,即PSC的值
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
-
- /*输出比较初始化*/
- TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
- TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值
- TIM_OC3Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3
-
- /*TIM使能*/
- TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
- }
【问】为什么PA2被设置为了复用推挽输出?
因为PA2接的是PWMA,由内部外设控制的时候,需要设置为复用推挽输出。
后面都是比较基础的设置了。
【电机驱动模块的GPIO初始化】
- void Motor_Init(void)
- {
- /*开启时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
-
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- PWM_Init(); //初始化直流电机的底层PWM
- }
【设置电机速度的函数】
- void PWM_SetCompare3(uint16_t Compare)
- {
- TIM_SetCompare3(TIM2, Compare); //设置CCR3的值
- }
这里是对TIM_SetCompare3做一个封装。
- void Motor_SetSpeed(int8_t Speed)
- {
- if (Speed >= 0) //如果设置正转的速度值
- {
- GPIO_SetBits(GPIOA, GPIO_Pin_4); //PA4置高电平
- GPIO_ResetBits(GPIOA, GPIO_Pin_5); //PA5置低电平,设置方向为正转
- PWM_SetCompare3(Speed); //PWM设置为速度值
- }
- else //否则,即设置反转的速度值
- {
- GPIO_ResetBits(GPIOA, GPIO_Pin_4); //PA4置低电平
- GPIO_SetBits(GPIOA, GPIO_Pin_5); //PA5置高电平,设置方向为反转
- PWM_SetCompare3(-Speed); //PWM设置为负的速度值,因为此时速度值为负数,而PWM只能给正数
- }
- }
此函数就是根据TB6612的数据手册编写的(所以查Datasheet非常重要)
此时在主函数中利用封装好的Motor_SetSpeed函数即可控制电机速度了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。