定时器&PWM应用编程

一、STM32定时功能介绍

TIM(Timer)定时器是一种用于计数和定时的高精度硬件设备。它内部包含16位计数器、预分频器和自动重装寄存器的时基单元,可以实现对输入时钟的精确计数。当计数值达到设定值时,定时器会触发中断,以便执行相应的操作。这种定时器具有高精度、长定时时间的优点,因此在许多嵌入式系统中得到广泛应用。

二、PWM功能介绍

PWM(Pulse width modulation)脉冲宽度调制。PWM是通过编程控制输出方波的频率和占空比(高低电平的比例)。应用:测量,通信,功率控制与变换等各种领域(呼吸灯、电机)。
STM32中PWM属于定时器的功能,通过直接配置定时器就可以使用PWM,除了定时器的基本配置以外,还要加入一个比较计数值确定一个周期内翻转电平的时机,还需要GPIO输出方波,需要用到GPIO的复用功能。
PWM波的高低带你平的顺序是由极性、PWM模式和计数模式共同决定。极性决定有效电平(默认电平),PWM模式指的是一个周期内有效电平和无效电平的顺序。

    注意:只有有定时器服用功能的GPIO才可以输出PWM。

要实现PWM信号的输出,需要用到三个寄存器:自动重载寄存器TIMx_ARR,捕获/比较寄存器TIMx_CCRn(n表示通道编号1~4,下同)以及计数器寄存器TIMx_CNT,并通过通道引脚TIMx_CHn输出PWM信号。
为了表述方便,我们将TIMX_ARR寄存器的内容记为自动重载值ARR,TIMx_CCRn寄存器的内容记为捕获/比较值CCR,计数器存器TIMx_CNT的内容记为计数值CNT。整个PWM信号的输出过程下图所示
在这里插入图片描述
图中,我们假定定时器工作在向上计数PWM模式,且当CNT<CCRx时,输出0,当CNT>=CCRx时输出1。那么就可以得到如上的PWM示意图:当CNT值小于CCRx的时候,IO输出低电平(O0),当CNT值大于等于CCRx的时候,IO输出高电平(1),当CNT达到ARR值的时候,重新归零,然后重新向上计数,依次循环。改变CCRx的值,就可以改变PWM输出的占空比,改变ARR的值,就可以改变PWM输出的频率,这就是PWM输出的原理。

三、创建工程

创建基本的keil工程请参照这篇博客
https://blog.csdn.net/qq_69626808/article/details/133870540?spm=1001.2014.3001.5501
在原有基础工程上,我们在左边文件栏“Hardware"添加一个c文件,和一个头文件,统一命名为”PWM"。注意添加时把路径加上
在这里插入图片描述
我们在“PWM.h”中添加基本的函数语句

#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h"                  // Device header




#endif

3.1内置时钟使能
在STM32中,控制TIM2使能的是APB1,且需要控制TIM2为内部时钟模式,配置的相关函数为

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2);
	

3.2配置时基单元
配置时基单元有点类似于GPIO,也是需要定义一个结构体变量,再将其结构体成员引出来分别进行配置。声明结构体的语句为

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

根据公式,时钟频率为72MHZ/(PSC+1)/(ARR+1),经过计算,当PSC设置为719,ARR设置为99,时钟频率为1000HZ,也就是0.001s,也就是说,每个电平存在时间为0.001s

//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=100-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler= 720-1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);

3.3使能更新中断
我们需要配置使能中断,设置为输出比较模式,这样才能将已激活的时钟信号进行输出比较从而得到PWM波形。

输出比较模式的配置有专门的标准库函数,也是结构体类型,我们一个个了解并将其配置。输出比较结构体定义为

//使能更新中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
  
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0 ;
    TIM_OC1Init(TIM2,&TIM_OCInitStruct);	

3.4配置GPIO并启动定时器
此外,由于需要使用LED灯,所以还需要加上GPIO的配置函数。此外,TIM对应GPIO引脚需要查找定义表,TIM2_CH1_ETR引脚复用在PA0上,故我们配置PA0。

//配置GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_0);
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);

3.5 输出比较函数
根据前面的公式我们知道,PWM的占空比的公式为CCR/ARR+1。前面我们设定为ARR为99,故我们可以设定CCR在0-100波动,这样算出的占空比就是0-100%了,具体结果会在主函数里展示。我们现在只是定义了一个输出比较函数

在STM32标准库里,系统给出了设置CCR函数为

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)

我们可以通过这个函数来设定一个不断变化的CCR值。

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);
	
	
	
}

最后C文件主要代码

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	TIM_InternalClockConfig(TIM2);
	

	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=100-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler= 720-1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
  
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0 ;
  TIM_OC1Init(TIM2,&TIM_OCInitStruct);	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_0);
	
	TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);
	
	
	
}


H文件

#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h"                  // Device header

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);


#endif

3.6主函数代码
在for循环中,不断调用设置CCR的函数,不断改变CCR从而实现呼吸灯

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
//#include "OLED.h"
#include "PWM.h"
#include "LED.h"
#include "Delay.h"
uint16_t Num;
int main(void)
{
//	OLED_Init();
    PWM_Init();
//  OLED_ShowString(1,1,"a:");
//  OLED_ShowString(2,1,"b:");
	while (1)
	{
		for(Num=0;Num<=100;Num++)
		{
			
			PWM_SetCompare1(Num);
			Delay_ms(10);
		}
		for(Num=0;Num<=100;Num++)
		{
			
			PWM_SetCompare1(100-Num);
		  Delay_ms(10);	
	
		}
		
	}
}


烧录
在这里插入图片描述
在这里插入图片描述
结果:

QQ视频20231104213936

打开魔法棒
在这里插入图片描述
打开keil的调试界面,点击逻辑分析仪
在这里插入图片描述
在这里插入图片描述

QQ录屏20231104214808

四.总结

本文主要介绍PWM相关理论知识,在学习理论知识的基础上,实现在STM32F103C8T6上,利用定时器TIM3和TIM4输出PWM波形,PWM的占空比随时间变化,去驱动你外接的一个LED以及最小开发板上已焊接的LED(固定接在 PC0 GPIO端口).通过本次的练习,让我更加深入的理解了STM32的定时器功能以及PWM的相关知识。

参考:

https://www.bilibili.com/video/BV1th411z7sn?p=16&vd_source=282ea78ce81b7a1817b467d1df493b2a
https://blog.csdn.net/qq_43533553/article/details/128206887

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值