首页 > 其他分享 >TIM输出比较---STM

TIM输出比较---STM

时间:2024-12-07 10:04:29浏览次数:6  
标签:TIM2 定时器 STM --- TIM 占空比 GPIO PWM

一、简介

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

相关文章

  • Vsphere-软件组件
    VMwarevSphere是用于虚拟化的软件组件套件。这些组件包括ESXi、vCenterServer以及在vSphere环境中实现多个不同功能的其他软件组件。ESXIHypervisor运行虚拟机。每个虚拟机都有一组配置文件和磁盘文件,旨在共同执行物理机的所有功能。通过ESXi,可以运行虚拟机......
  • 举例说明with属性的fill-available有什么应用场景?
    fill-available在width或height属性中与grid布局或flexbox布局一起使用时,可以创建一些有趣的布局效果。它本质上是让元素填满可用空间,与stretch类似,但在某些情况下表现不同。以下是一些fill-available的应用场景:1.Grid布局中的等高列:假设你有一个网格布局,希望......
  • 面向对象进阶-03-static的注意事项
    static的注意事项:静态方法只能访问静态变量和静态方法非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法静态方法中没有this关键字总结:静态方法中,只能访问静态非静态方法可以访问所有静态方法中没有this关键字 创建一个Javabean类......
  • 参加【2025年3月】全国CTF夺旗赛-从零基础入门到竞赛,看这一篇就稳了!
    ......
  • width属性的min-content和max-content有什么作用?
    min-content和max-content是CSS中的宽度属性值,它们根据内容的内部结构来确定元素的宽度,而不是依赖于容器的可用空间。它们通常与width、min-width和max-width属性一起使用。min-content(最小内容宽度):作用:计算元素内容在没有任何换行的情况下所需的最小宽度。表......
  • AI - 谈谈RAG中的查询分析
    AI-谈谈RAG中的查询分析大家好,今天我们来聊聊RAG(Retrieval-AugmentedGeneration)中的一个重要环节——查询分析(QueryAnalysis)。什么是查询分析查询分析,说简单点,就是理解用户在问什么。在RAG系统中,用户输入一个查询,我们的任务是通过一些技术手段弄明白这个查询的真正......
  • ECharts柱状图-某地区蒸发量和降水量,附视频讲解与代码下载
    引言: 在数据可视化的世界里,ECharts凭借其丰富的图表类型和强大的配置能力,成为了众多开发者的首选。今天,我将带大家一起实现一个柱状图图表,通过该图表我们可以直观地展示和分析数据。此外,我还将提供详细的视频讲解和代码下载链接,帮助大家快速上手。一、图表效果预览 二、......
  • CodeBERT: A Pre-Trained Model for Programming and Natural Languages
    本次介绍的论文是《CodeBERT:APre-TrainedModelforProgrammingandNaturalLanguages》原文链接:http://www.semanticscholar.org/paper/0fe2636446cd686830da3d971b31a004d6094b3c源代码和数据集:GitHub-microsoft/CodeBERT:CodeBERT本篇论文主要是介绍了CodeBERT......
  • dd破坏asm磁盘头恢复---惜分飞
    联系:手机/微信(+8617813235971)QQ(107644445)标题:dd破坏asm磁盘头恢复作者:惜分飞©版权所有[未经本人同意,不得以任何形式转载,否则有进一步追究法律责任的权利.]有朋友对asmdisk的磁盘头dd了2048byte的数据通过分析,gi软件版本,确认是11.2.0.4 OracleDatabase......
  • 【花雕学编程】Arduino动手做(229)---带编码器350w机器人轮毂马达6.5 英寸电动轮毂伺服
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来——小小的......