正点原子STM32F103学习笔记(十)——定时器、PWM

通用定时器基本原理

正点原子STM32F103学习笔记(十)——定时器、PWM_第1张图片

通用定时器功能特点描述

STM3 的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:

  • 位于低速的APB1总线上(APB1)
  • 16 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)。
  • 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值。
  • 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
    • 输入捕获
    • 输出比较
    • PWM 生成(边缘或中间对齐模式)
    • 单脉冲模式输出
  • 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
  • 如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
    • 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
    • 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
    • 输入捕获
    • 输出比较
    • 支持针对定位的增量(正交)编码器和霍尔传感器电路
    • 触发输入作为外部时钟或者按周期的电流管理
  • STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
  • 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

计数器模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式。

  1. 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件
  2. 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件
  3. 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
    正点原子STM32F103学习笔记(十)——定时器、PWM_第2张图片

正点原子STM32F103学习笔记(十)——定时器、PWM_第3张图片

时钟来源

  1. 内部时钟(CK_INT)默认
  2. 外部时钟模式1:外部输入脚(TIx)
  3. 外部时钟模式2:外部触发输入(ETR)
  4. 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
    实际单元:经过CK_PSC预分频得到计数器最终的时钟,技术结束后会重装载或产生相应的触发事件溢出事件
    输入捕获部分:捕获通道引脚上的电平,常用于得到脉冲长度计算eg:上升沿
    输出比较:每个通道要么用于输入比较要么用于输出比较

定时器中断

正点原子STM32F103学习笔记(十)——定时器、PWM_第4张图片

正点原子STM32F103学习笔记(十)——定时器、PWM_第5张图片

定时器中断实验相关寄存器

计数器当前值寄存器CNT

正点原子STM32F103学习笔记(十)——定时器、PWM_第6张图片

预分频寄存器TIMx_PSC

正点原子STM32F103学习笔记(十)——定时器、PWM_第7张图片

自动重装载寄存器(TIMx_ARR)

正点原子STM32F103学习笔记(十)——定时器、PWM_第8张图片

控制寄存器1(TIMx_CR1)

正点原子STM32F103学习笔记(十)——定时器、PWM_第9张图片

DMA中断使能寄存器(TIMx_DIER)

正点原子STM32F103学习笔记(十)——定时器、PWM_第10张图片

库函数

定时器参数初始化:

void TIM_TimeBaseInit(TIM_TypeDef* TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

typedef struct
{
  uint16_t TIM_Prescaler;      //预分频系数  
  uint16_t TIM_CounterMode;     //计数模式
  uint16_t TIM_Period;        //自动装载值
  uint16_t TIM_ClockDivision;  //不重要
  uint8_t TIM_RepetitionCounter;   //不重要
} TIM_TimeBaseInitTypeDef; 


TIM_TimeBaseStructure.TIM_Period = 4999; 
TIM_TimeBaseStructure.TIM_Prescaler =7199; 
TIM_TimeBaseStructure.TIM_ClockDivision =   TIM_CKD_DIV1; 
TIM_TimeBaseStructure.TIM_CounterMode =   TIM_CounterMode_Up; 
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 

定时器使能函数:

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)

定时器中断使能函数:

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

状态标志位获取和清除

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

实现步骤

  1. 使能定时器时钟。
RCC_APB1PeriphClockCmd();  
  1. 初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
  1. 开启定时器中断,配置NVIC。
void TIM_ITConfig();    //使能响应中断  
NVIC_Init();			  //中断优先级设置
  1. 使能定时器。
TIM_Cmd();			  //使能定时器
  1. 编写中断服务函数。
TIMx_IRQHandler();


void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否,TIM_IT_Update更新中断
		{
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 

			//执行内容
		}
}

程序实操

程序要求

通过定时器中断配置,每500ms中断一次,然后中断服务函数中控制LED实现LED1状态取反(闪烁)。

Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk
其中(PSC+1)/Tclk相当于周期的时间长度
Tclk为时钟频率(72MHz),系统初始化函数初始化APB1
PSC+1预分频系数加一,设为7199,则定时器的时钟为72M/7200=10k,T=0.1ms

timer.c

#include "timer.h"
#include "led.h"

//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	
	//定时器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断,中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器


	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
		LED1=!LED1;
		}
}

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"

 int main(void)
 {		
 
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
 	LED_Init();			     //LED端口初始化
	TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms  
   	while(1)
	{   
	}	 
}	 

PWM处处实验

STM32 PWM工作过程

正点原子STM32F103学习笔记(十)——定时器、PWM_第11张图片

ARR确定周期
CCRx确定占空比
正点原子STM32F103学习笔记(十)——定时器、PWM_第12张图片

  • CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
  • CCMR1: OC1M[2:0]位:
    对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】
  • CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
  • CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。

PWM模式1 & PWM模式2

正点原子STM32F103学习笔记(十)——定时器、PWM_第13张图片

PWM模式1:CNT< CCRx时为有效电平
高电平有效就是输出为有效电平时输出高电平

STM32 PWM工作过程

正点原子STM32F103学习笔记(十)——定时器、PWM_第14张图片

void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

自动重载的预装载寄存器

正点原子STM32F103学习笔记(十)——定时器、PWM_第15张图片

正点原子STM32F103学习笔记(十)——定时器、PWM_第16张图片

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//简单的说,更改ARR后,ARPE=1,ARR立即生效。。。APRE=0,ARR下个比较周期生效。

STM32 定时器3输出通道引脚

正点原子STM32F103学习笔记(十)——定时器、PWM_第17张图片

PWM库函数

初始化

void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

typedef struct
{
  uint16_t TIM_OCMode;  //PWM模式1或者模式2
  uint16_t TIM_OutputState; //输出使能 OR失能
  uint16_t TIM_OutputNState;//没用
  uint16_t TIM_Pulse; //比较值,写CCRx
  uint16_t TIM_OCPolarity; //比较输出极性
  uint16_t TIM_OCNPolarity; //没用
  uint16_t TIM_OCIdleState;  //没用
  uint16_t TIM_OCNIdleState; //没用
} TIM_OCInitTypeDef;


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure. TIM_Pulse=100; //初始化设置,后面可以不断改变
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

设置比较值函数:

void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Compare2);

使能输出比较预装载:

void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

改变CCER寄存器相关位——极性

void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

使能自动重装载的预装载寄存器允许位:

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

PWM输出实验

使用定时器3的PWM功能,输出占空比可变的PWM波,用来驱动LED灯,从而达到LED[PB5]亮度由暗变亮,又从亮变暗,如此循环。

PWM输出配置步骤:

  1. 使能定时器3和相关IO口时钟。
    • 使能定时器3时钟:
    RCC_APB1PeriphClockCmd();
    
    • 使能GPIOB时钟:
    RCC_APB2PeriphClockCmd();
    
  2. 初始化IO口为复用功能输出。函数:
    GPIO_Init();
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      
    
  3. 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 
    
  4. 初始化定时器:ARR,PSC等:
TIM_TimeBaseInit();
  1. 初始化输出比较参数:
TIM_OC2Init();
  1. 使能预装载寄存器:
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 
  1. 使能定时器。
TIM_Cmd();
  1. 不断改变比较值CCRx,达到不同的占空比效果:
TIM_SetCompare2();

程序实操

  1. 在头文件中声明void TIM3_PWM_Init(u16 arr,u16 psc)
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
   //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}
  1. 主函数中
    TIM3_PWM_Init(899,0);	 //不分频。PWM频率=72000000/900=80Khz
    

你可能感兴趣的:(STM32,嵌入式,stm32)