前言:
下面的以通用定时器为例,当然高级定时器具有通用定时器的全部功能
灰色部分为我们的输入捕获部分,下节说
一:TIM输出比较功能
1: 简历
OC(Output Compare)输出比较
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
2:PWM波形
PWM(Pulse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
PWM参数: 频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
3:输出比较模式
CCR输出比较寄存器:CCR1,CCR2, CCR3,CCR4
有/无效状态由TIMx_CCER决定 CCxP=0:OCx高电平有效 ;CCxP=1:Ocx低电平有效
4:参数计算
条件:向上或者向下计数,有效电平为高电平3个公式全部符合。
中央计数:有效电平为高电平,只符合占空比的公式。
5:PWM基本结构
6:HAL库配置
要产生pwm必须使能计数器
7:HAL库函数
二:代码
定时器的功能
A:PWM驱动LED呼吸灯
下面的这个公式是周期的计算
使用TIM3的通道2,可以看到它复用的PA7上
我们在配置pwm模式的时候选择的是低电频有效,所以LED的负极连接——PA7;正极连接电源
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef TIM_HandleTIMCH3;
void TIM3_CH2_init(uint16_t psc,uint16_t arr)
{
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_HandleTIMCH3.Instance=TIM3;
TIM_HandleTIMCH3.Init.Period=arr;
TIM_HandleTIMCH3.Init.ClockDivision=TIM_COUNTERMODE_UP; /*向上计数*/
TIM_HandleTIMCH3.Init.Prescaler=psc;
HAL_TIM_Base_Init(&TIM_HandleTIMCH3);
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; /*为pwm模式1*/
TIM_OC_Init.Pulse=arr/2; /*比较值 CCRX的值*/
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_LOW; /*输出极性-低电频有效*/
/*配置pwm模式比较值等*/
HAL_TIM_PWM_ConfigChannel(&TIM_HandleTIMCH3,&TIM_OC_Init,TIM_CHANNEL_2);
//启动生成PWM波
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
B:PWM驱动LED呼吸灯
复用功能
和上面的内容一样不过为了练习我们的复用功能,我们使用复用功能;可以看到TIM3_CH2复用在PB5上面,这次我们使用复用功能
我们在配置pwm模式的时候选择的是低电频有效,所以LED的负极连接——PB5;正极连接电源
主函数完全一样
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_TIM3_PARTIAL();
每个引脚的重新映射是不同的: __HAL_AFIO_REMAP_TIM3_PARTIAL();是不同的;但是因为使用到了AFIO,使用AFIO的时钟必须打开
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef TIM_HandleTIMCH3;
void TIM3_CH2_init(uint16_t psc,uint16_t arr)
{
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_HandleTIMCH3.Instance=TIM3;
TIM_HandleTIMCH3.Init.Period=arr;
TIM_HandleTIMCH3.Init.ClockDivision=TIM_COUNTERMODE_UP; /*向上计数*/
TIM_HandleTIMCH3.Init.Prescaler=psc;
HAL_TIM_Base_Init(&TIM_HandleTIMCH3);
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; /*为pwm模式1*/
TIM_OC_Init.Pulse=arr/2; /*比较值 CCRX的值*/
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_LOW; /*输出极性-低电频有效*/
/*配置pwm模式比较值等*/
HAL_TIM_PWM_ConfigChannel(&TIM_HandleTIMCH3,&TIM_OC_Init,TIM_CHANNEL_2);
//使能生成PWM波
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_TIM3_PARTIAL();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
}
}
重新映射功能:
C:PWM驱动舵机
1:连接图
红色-----接USE5V(3v带不动) 注意舵机应该接的5V的下载器上面,3.3V带不动
橙色-----信号线 棕色---地线
2:舵机介绍
舵机是一种根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
高电平宽度=某电频的持续时间
CCR=高电频
舵机的控制一般需要一个20ms的时基脉冲(周期),该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度舵机为例,那么对应的控制关系是这样的:
0.5ms--------------0度;
1.0ms------------45度;
1.5ms------------90度;
2.0ms-----------135度;
2.5ms-----------180度;
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef TIM_HandleTIMCH3;
void TIM3_CH2_init(uint16_t psc,uint16_t arr)
{
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_HandleTIMCH3.Instance=TIM3;
TIM_HandleTIMCH3.Init.Period=arr;
TIM_HandleTIMCH3.Init.ClockDivision=TIM_COUNTERMODE_UP; /*向上计数*/
TIM_HandleTIMCH3.Init.Prescaler=psc;
HAL_TIM_Base_Init(&TIM_HandleTIMCH3);
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; /*为pwm模式1*/
TIM_OC_Init.Pulse=0; /*比较值 CCRX的值*/
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; /*输出极性-低电频有效*/
/*配置pwm模式比较值等*/
HAL_TIM_PWM_ConfigChannel(&TIM_HandleTIMCH3,&TIM_OC_Init,TIM_CHANNEL_2);
//启动生成PWM波形
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
#include "stm32f1xx_hal.h"
#include "pwm.h"
void serveo_init()
{
TIM3_CH2_init(72-1,20000-1); /*72*20000/72000000=0.02s=20ms*/
}
void Servo_SetAngle(float Angle)
{
/*修改占空比*/
__HAL_TIM_SET_COMPARE(&TIM_HandleTIMCH3,TIM_CHANNEL_2,Angle / 180 * 2000 + 500);
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "pwm.h"
#include "key.h"
#include "serveo.h"
int main(void)
{
uint8_t Angle=0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
serveo_init();
KEY_Init();
OLED_ShowString(1, 1, "Angle:");
while(1)
{
if(KEY_Scan()==1)
{
Angle+=30;
if(Angle>180) Angle=0;
}
Servo_SetAngle(Angle);
OLED_ShowNum(1,7,Angle,3);
}
}
计算
TIM_SetCompareX : 来单独更改CCR寄存器值的函数,可以更改占空比
D:PWM驱动直流电机
1:连接图
2:电机与驱动电路
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,
当电极反接时,电机反转直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
电机驱动详情见: 51:电机(ULN2003D)这一篇
3:代码
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef TIM_HandleTIMCH3;
void TIM3_CH2_init(uint16_t psc,uint16_t arr)
{
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_HandleTIMCH3.Instance=TIM3;
TIM_HandleTIMCH3.Init.Period=arr;
TIM_HandleTIMCH3.Init.ClockDivision=TIM_COUNTERMODE_UP; /*向上计数*/
TIM_HandleTIMCH3.Init.Prescaler=psc;
HAL_TIM_Base_Init(&TIM_HandleTIMCH3);
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; /*为pwm模式1*/
TIM_OC_Init.Pulse=0; /*比较值 CCRX的值*/
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; /*输出极性-低电频有效*/
/*配置pwm模式比较值等*/
HAL_TIM_PWM_ConfigChannel(&TIM_HandleTIMCH3,&TIM_OC_Init,TIM_CHANNEL_2);
//使能计数器
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
#include "stm32f1xx_hal.h"
#include "pwm.h"
void motor_init()
{
__HAL_RCC_GPIOA_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_4;
GPIO_InitType.Pull=GPIO_PULLUP;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
TIM3_CH2_init(720-1,100-1); /*720*100/72000000=0.001s=1MS*/
}
void motor_speed(int8_t speed)
{
if(speed>0)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(&TIM_HandleTIMCH3,TIM_CHANNEL_2,speed);
}
else
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
//speed为负数,需要把他变为正数,使用在加上负号
__HAL_TIM_SET_COMPARE(&TIM_HandleTIMCH3,TIM_CHANNEL_2,-speed);
}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "pwm.h"
#include "key.h"
#include "motor.h"
int main(void)
{
int8_t speed=0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
KEY_Init();
OLED_ShowString(1, 1, "speed:");
motor_init();
while(1)
{
if(KEY_Scan()==1)
{
speed+=20;
if(speed>100) speed=-100;
}
motor_speed(speed);
OLED_ShowSignedNum(1, 7, speed, 3);
}
}
E:通用定时器定时
使用通用定时器定时功能,定时1s并输出pwm波形
#include "stm32f1xx_hal.h"
int32_t num;
TIM_HandleTypeDef TIM_HandleTIMCH3;
void TIM3_CH2_init(uint16_t psc,uint16_t arr)
{
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_HandleTIMCH3.Instance=TIM3;
TIM_HandleTIMCH3.Init.Period=arr;
TIM_HandleTIMCH3.Init.ClockDivision=TIM_COUNTERMODE_UP; /*向上计数*/
TIM_HandleTIMCH3.Init.Prescaler=psc;
HAL_TIM_Base_Init(&TIM_HandleTIMCH3);
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; /*为pwm模式1*/
TIM_OC_Init.Pulse=arr/2; /*比较值 CCRX的值*/
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; /*输出极性-高2电频有效*/
/*配置pwm模式比较值等*/
HAL_TIM_PWM_ConfigChannel(&TIM_HandleTIMCH3,&TIM_OC_Init,TIM_CHANNEL_2);
//使能计数器
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);
//使能中段
HAL_TIM_Base_Start_IT(&TIM_HandleTIMCH3);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_TIM3_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(TIM3_IRQn,2,0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&TIM_HandleTIMCH3);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
num++;
}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "pwm.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
TIM3_CH2_init(7200-1,10000-1);
LED_Exit_Init();
while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
OLED_ShowNum(1,1,num,3);
}
}
时间的计算:第一种方法:先计算频率在计算周期频率:72000000/(7200-1+1)/(10000-1+1)=1 周期:1/频率=1
第二种方法:直接计算周期
7200*10000/72000000=1S-----直接就是周期
中断
想要产生中断必须使能中断,下面这个必须写,CNT==ARR溢出产生的中断。启动定时器,并使能更新中断
//使能中段
HAL_TIM_Base_Start_IT(&TIM_HandleTIMCH3);
产生PWM必须使能:启动生成PWM波
//使能计数器
HAL_TIM_PWM_Start(&TIM_HandleTIMCH3,TIM_CHANNEL_2);