一、简介
TIM定时器:OC输出比较
输出比较可以通过比较CNT与CCR寄存器(捕获/比较寄存器)值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形;
每个高级定时器和通用定时器都拥有4个输出比较通道;
高级定时器的前3个通道额外拥有死区生成和互补输出的功能;
PWM:脉冲宽度调试
在惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机空速等领域
控制原理:对于呼吸灯来说,是一个“亮起、熄灭、亮起”的过程,当“亮起、熄灭、亮起”足够快时,就会出现一个中间值:“微亮”;电机控制亦是如此。
PWM秘诀:“天下武功、唯快不破”
PWM输出:
蓝线:CNT
红线:CCR
黄线:ARR
1:CNT小于CCR,为有效电平,所以图二1为高电平
2:CNT大于CCR,为无效电平,所以图二2为低电平
3:CNT小于CCR,为有效电平,所以图二3为高电平
4:CNT大于CCR,为无效电平,所以图二4为低电平
5:CNT小于CCR,为有效电平,所以图二5为高电平
6:CNT大于CCR,为无效电平,所以图二6为低电平
PWM参数:
频率 = 1/Ts Ts:高低电平变化周期的时间(图一当中的1~3,对应CNT溢出周期)
PWM频率 = 计数器更新频率:Freq = CK_PSC/(PSC+1)/(ARR+1)
占空比 = Ton/Ts 占空比:一个周期内高电平所占比例(图一当中的1~2所占1~3的比列,对应图二中一个高电平占一个周期的比例)
PWM占空比:Duty = CCR/(ARR+1)
PWM分辨率:Reso = 1/(ARR+1)
分辨率 = 占空比变化步距
二、项目需求
PWM配置流程:
1.开启时钟
2.配置时基单元
3.配置输出比较器
4.配置GPIO
项目一:
通过PWM使LED实现呼吸灯的效果:LED--->PA0(如果需要使用重映射,打开注释代码即可)
pwm_led.c
#include "stm32f10x.h" // STM32F10x外设库头文件
#include "pwm_led.h" // PWM控制相关头文件
#include "delay.h" // 延时函数头文件
// 按键初始化函数
void PWM_LED_Init(void)
{
// 定义定时器基础结构体和输出比较结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStruct;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能TIM2和GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
// 使能 TIM2 的部分引脚重映射(将原本分配给其他功能的引脚,重新映射为 TIM2 的功能)
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//重映射
// 使能 SWJ (Serial Wire JTAG) 的引脚重映射,禁用 JTAG 接口功能,恢复为普通引脚
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//重映射·
// 配置GPIOA的引脚0为定时器的输出引脚(PWM输出)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 配置GPIOA的Pin0
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // 重映射
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 配置引脚的速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 配置为复用推挽输出模式
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的Pin0引脚
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 自动重载值,定时器的计数周期为100(0到99),即100个时钟周期
TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // 预分频器值,定时器时钟源被分频为72MHz / 720 = 100kHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割设置,设为1,表示不进行时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数模式设置为向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数器为0,不启用
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCStructInit(&TIM_OCInitStruct); // 使用默认值初始化输出比较配置结构体
// 配置PWM模式
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // 设置PWM模式1(边沿对齐,且有效时钟边缘为上升沿)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 使能输出状态(输出PWM信号)
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性设置为高电平有效
TIM_OCInitStruct.TIM_Pulse = 0; // 初始占空比为0%(即PWM信号处于低电平)
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
// 启动定时器TIM2
TIM_Cmd(TIM2, ENABLE); // 启动定时器2,使其开始计数并产生PWM信号
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void TIME_SetCompare1(u16 compare)
{
// 设置定时器TIM2通道1的比较值(调整PWM信号的占空比)
TIM_SetCompare1(TIM2, compare); // 设置通道1的比较值,调整PWM输出的占空比
}
设置PWM频率
//void TIME_PrescalerConfig(u16 prescaler)
//{
// TIM_PrescalerConfig(TIM2,prescaler,TIM_PSCReloadMode_Immediate);
//}
pwm_led.h
#ifndef __PWM_LED_H
#define __PWM_LED_H
#include "stm32f10x.h"
#include "sys.h"
void PWM_LED_Init(void);
void TIME_SetCompare1(u16 compare);
#endif
main.c
#include "stm32f10x.h" // 设备相关头文件
#include "pwm_led.h" // PWM控制相关头文件
#include "delay.h" // 延时相关头文件
int i = 0;
int main(){
PWM_LED_Init(); // 初始化PWM
delay_init(); // 初始化延时函数
while(1)
{
// 渐强效果:占空比从0到100逐步增加
for(i = 0; i < 100; i++)
{
TIME_SetCompare1(i); // 设置PWM占空比(i是设置CCR寄存器的值,PWM占空比有CCR和ARR共同决定;ARR+1的值等于100,CRR的值才直接等于ARR)
delay_ms(10); // 延时10ms,控制渐变速度
}
// 渐弱效果:占空比从100到0逐步减小
for(i = 0; i < 100; i++)
{
TIME_SetCompare1(100 - i); // 设置PWM占空比(i是设置CCR寄存器的值,PWM占空比有CCR和ARR共同决定;ARR+1的值等于100,CRR的值才直接等于ARR)
delay_ms(10); // 延时10ms,控制渐变速度
}
}
}
项目二:
如果使用多个舵机或者电机,只需要驱动一个PWM的多个多个通道即可;因为对于同一个PWM它们的计数器使同一个,所以频率和相位是一样的,它们的占空比则有各自的CCR决定,是可以自行设置的。
上述要求:1.周期为20ms、2.高电平宽度为0.5ms~2.5ms
频率 = 1/T(周期)= 1/0.02 = 50Hz = 72M(CK_PSC)/(PSC+1)/(ARR+1) = 50Hz
(PSC+1)和 (ARR+1)自行设置,我设置(PSC+1)= 72、(ARR+1)= 20000us
这样也能满足第二个要求。
程序现象:按键每按下一次舵机角度增加30°,旋转角度大于180°,角度归0。
pwm_servo.c
#include "stm32f10x.h" // STM32F10x外设库头文件
#include "pwm_led.h" // PWM控制相关头文件
#include "delay.h" // 延时函数头文件
// 按键初始化函数
void PWM_Servo_Init(void)
{
// 定义定时器基础结构体和输出比较结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStruct;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能TIM2和GPIOA的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
// 配置GPIOA的引脚0为定时器的输出引脚(PWM输出)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // 配置GPIOA的Pin1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 配置引脚的速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 配置为复用推挽输出模式
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的Pin0引脚
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 自动重载值,定时器的计数周期为100(0到99),即100个时钟周期
TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // 预分频器值,定时器时钟源被分频为72MHz / 720 = 100kHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割设置,设为1,表示不进行时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数模式设置为向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数器为0,不启用
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 初始化定时器输出比较配置结构体
TIM_OCStructInit(&TIM_OCInitStruct); // 使用默认值初始化输出比较配置结构体
// 配置PWM模式
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // 设置PWM模式1(边沿对齐,且有效时钟边缘为上升沿)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 使能输出状态(输出PWM信号)
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性设置为高电平有效
TIM_OCInitStruct.TIM_Pulse = 0; // 初始占空比为0%(即PWM信号处于低电平)
TIM_OC2Init(TIM2, &TIM_OCInitStruct); // 初始化定时器TIM2的通道1(即PA0)为PWM输出
// 启动定时器TIM2
TIM_Cmd(TIM2, ENABLE); // 启动定时器2,使其开始计数并产生PWM信号
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void TIME_SetCompare2(u16 compare)
{
// 设置定时器TIM2通道1的比较值(调整PWM信号的占空比)
TIM_SetCompare2(TIM2, compare); // 设置通道1的比较值,调整PWM输出的占空比
}
/*
舵机旋转函数:
0° 500
180° 2500
*/
void Servo_Rotate(float Angle)
{
TIME_SetCompare2(Angle/180*2000+500);
}
pwm_servo.h
#ifndef __PWM_SERVO_H
#define __PWM_SERVO_H
#include "stm32f10x.h"
#include "sys.h"
void PWM_Servo_Init(void);
void TIME_SetCompare1(u16 compare);
void Servo_Rotate(float Angle);
#endif
main.c
#include "stm32f10x.h" // 设备相关头文件
#include "pwm_servo.h" // PWM控制相关头文件
#include "delay.h" // 延时相关头文件
#include "key.h"
int i = 0;
int keynum = 0;
float Angle;
int main(){
PWM_Servo_Init(); // 初始化PWM
delay_init(); // 初始化延时函数
Key_Init();
while(1)
{
keynum = KeyScan();
if(keynum == 1)
{
Angle += 30;
if(Angle > 180)
{
Angle = 0;
}
}
Servo_Rotate(Angle);
}
}
项目三:
程序现象:按键按下,电机加速旋转
TB6612电机控制模块:
VM--->接STLK5V
VCC--->接单片机3.3V
GND--->接单片机地线
AO1--->接电机
AO2--->接电机
AN1--->单片机A4
AN2--->单片机A5
PWMA--->单片机A2
pwm_motor.c
#include "stm32f10x.h" // STM32F10x外设库头文件
#include "pwm_motor.h" // PWM控制相关头文件
#include "delay.h" // 延时函数头文件
// 按键初始化函数
void PWM_Motor_Init(void)
{
// 定义定时器基础结构体和输出比较结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStruct;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能TIM2和GPIOA的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
// 配置GPIOA的引脚0为定时器的输出引脚(PWM输出)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // 配置GPIOA的Pin1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 配置引脚的速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 配置为复用推挽输出模式
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的Pin0引脚
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 自动重载值,定时器的计数周期为100(0到99),即100个时钟周期
TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // 预分频器值,定时器时钟源被分频为72MHz / 720 = 100kHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割设置,设为1,表示不进行时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数模式设置为向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数器为0,不启用
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 初始化定时器输出比较配置结构体
TIM_OCStructInit(&TIM_OCInitStruct); // 使用默认值初始化输出比较配置结构体
// 初始化定时器输出比较配置结构体
TIM_OCStructInit(&TIM_OCInitStruct); // 使用默认值初始化输出比较配置结构体
// 配置PWM模式
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // 设置PWM模式1(边沿对齐,且有效时钟边缘为上升沿)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 使能输出状态(输出PWM信号)
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性设置为高电平有效
TIM_OCInitStruct.TIM_Pulse = 0; // 初始占空比为0%(即PWM信号处于低电平)
TIM_OC3Init(TIM2, &TIM_OCInitStruct);
// 启动定时器TIM2
TIM_Cmd(TIM2, ENABLE); // 启动定时器2,使其开始计数并产生PWM信号
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void TIME_SetCompare3(u16 compare)
{
// 设置定时器TIM2通道1的比较值(调整PWM信号的占空比)
TIM_SetCompare3(TIM2, compare); // 设置通道1的比较值,调整PWM输出的占空比
}
///*
//舵机旋转函数:
//0° 500
//180° 2500
//*/
//void Servo_Rotate(float Angle)
//{
// TIME_SetCompare3(Angle/180*2000+500);
//}
void Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Motor_Init();
}
void Motor_SetSpeed(int8_t speed)
{
if(speed >= 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
TIME_SetCompare3(speed);
}
if(speed <= 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
TIME_SetCompare3(-speed);
}
}
pwm_motor.h
#ifndef __PWM_MOTOR_H
#define __PWM_MOTOR_H
#include "stm32f10x.h"
#include "sys.h"
void PWM_Motor_Init(void);
void TIME_SetCompare3(u16 compare);
void Servo_Rotate(float Angle);
void Motor_Init(void);
void Motor_SetSpeed(int8_t speed);
#endif
main.c
#include "stm32f10x.h" // 设备相关头文件
#include "pwm_servo.h" // PWM控制相关头文件
#include "delay.h" // 延时相关头文件
#include "key.h"
int i = 0;
int keynum = 0;
float speed;
int main(){
Motor_Init(); // 初始化PWM
delay_init(); // 初始化延时函数
Key_Init();
while(1)
{
keynum = KeyScan();
if(keynum == 1)
{
speed+= 20 ;
if(speed> 100)
{
speed= 0;
}
}
Motor_SetSpeed(speed);
}
}
标签:TIM2,定时器,STM,---,TIM,占空比,GPIO,PWM
From: https://blog.csdn.net/2301_76762351/article/details/144256452