STM32F4 TIM(外设定时器)

TIM概述

  外设定时器除了和系统定时器一样具有基本定时功能外,还具有PWM(Pulse width modulation)输出的功能,stm32f4的外设定时器非常多,一 共有14个,分为2个高级控制定时器、10 个通用定时器 和 2 个基本定时器:
    1.高级控制定时器(TIM1和TIM8),挂载到APB2:具有16位定时器功能,具有4通道、输入捕获、PWM输出高级控制功能;
    2.通用定时器(TIM2到TIM5),挂载到APB1:具有16或32位定时功能,具有4通道、输入捕获、PWM输出控制功能;
      通用定时器(TIM9到TIM14),挂载到AP1或APB2:具有16位定时功能,具有2通道、输入捕获、PWM输出控制功能;
    3.基本定时器(TIM6和TIM7),挂载到APB1:具有16位定时功能。

定时计算

  对于TIM的外设定时器的时钟频率,需要注意参考手册RCC章节中如下两句话

STM32F405xx/07xx 和 STM32F415xx/17xx 的定时器时钟频率由硬件自动设置。分为两种
情况:
1. 如果APB预分频器为1,定时器时钟频率等于APB域的频率。
2. 否则,等于APB域的频率的两倍 (×2)。

  而在函数SetSysClock中可以看到,APB1=1/4 x AHB,APB2=1/2 x AHB,也就是挂载在APB下的TIM,其时钟频率为对应的APB时钟频率的两倍,而外设TIM也不会对所得到时钟频率进行再次分频才使用,而是直接使用。但是在使用PWM输出/比较或定时功能时需要使用预分频器对其分频,从TIM的框图中可以看出:

实验程序1

  通过原理图发现LED4的引脚连接了TIM14的通道1,便可以通过TIM14通道1实现呼吸灯。
led.c

#include "stm32f4xx.h"
#include "common.h"
#include "led.h"

void breathinglight_init()
{
        GPIO_InitTypeDef 	GPIO_InitStructure ;
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_OCInitTypeDef  TIM_OCInitStructure;        
	//初始化对应端口的引脚 
	GPIO_InitStructure.GPIO_Pin  = LED_PIN_4;//指定第9根引脚 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//配置为复用模式
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;//配置引脚的响应时间=1/50MHz 
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽的输出模式,增加输出电流和灌电流的能力
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL ;//不使能内部上下拉电阻
	GPIO_Init(LED_PORT2,&GPIO_InitStructure);
	
	//将LED4的引脚连接到TIM14
	GPIO_PinAFConfig(LED_PORT2, LED4_PinSource, GPIO_AF_TIM14);

	//配置TIM14的相关参数:计数值(决定定时时间)、分频值
	//TIM14的硬件时钟频率=AHBCLK/4*2=APB1CLK*2=84000000Hz
        //设置预分频器为8400:84000000/8400
	//只要进行10000次计数,就是1秒时间的到达,也就是1Hz的频率输出
	TIM_TimeBaseStructure.TIM_Period = 10000/200-1;//如:计数值10000/100-1,决定了输出频率为100Hz,计数值为10000/50-1,频率为50Hz
	TIM_TimeBaseStructure.TIM_Prescaler = 8400-1;//预分频,也就是第一次分频,当前8400分频,就是84MHz/8400
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;//时钟分频,也称之二次分频这个F407是不支持的,因此该参数是不会生效的
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//就是从0开始计数,然后计数到TIM_Period
	TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);
	
	//占空比的配置
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//工作在PWM1模式,当计时器值小于比较器设定值时则输出脚此时输出有效电位。
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//脉冲的输出开关,当前是输出脉冲
	TIM_OCInitStructure.TIM_Pulse = 25;//这个值是决定了占空比,配置比较值,现在的输出占空比为50%
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高电平

	//TIM14的通道1配置
	TIM_OC1Init(TIM14, &TIM_OCInitStructure);
	//使能定时器14工作
	TIM_Cmd(TIM14, ENABLE);
}

led_test.c

#include "led_test.h"
#include "sys_tick.h"

void breathinglight_test()
{
	uint32_t pwm = 50;
	uint32_t flag = 0;
        while(1)
	{
		//设置TIM14的通道1比较值为20,亮度高
		if (0 == flag)
		{
			TIM_SetCompare1(TIM14, pwm);//设置TIM14通道1的比较值
			pwm  = pwm  - 1;
			if (pwm <= 0)
			{
				flag = 1;
			}
		}
		delay_ms(50);
		if (1 == flag)
		{
			TIM_SetCompare1(TIM14,l);
			pwm = pwm  + 1;
			if (pwm >= 50)
			{	
				flag = 0;
			}
		}
	}
}

main.c

#include "hwconf.h"
#include "led.h"
#include "led_test.h"

int main()
{
    init_board(); //初始化相关时钟
    breathinglight_init();
    breathinglight_test();
}

实验程序2

  使用TIM3的定时器中断功能实现LED4闪烁。
tim.c

#include "stm32f4xx.h"
#include "tim.h"

void tim3_init()
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
	NVIC_InitTypeDef 	NVIC_InitStruct;

	TIM_TimeBaseStruct.TIM_Period = 10000/2-1;//计数值5000-1,决定了定时时间500ms,即频率为2Hz
	TIM_TimeBaseStruct.TIM_Prescaler = 8400-1;//预分频,也就是第一次分频,当前8400分频,就是84MHz/8400
	TIM_TimeBaseStruct.TIM_ClockDivision = 0;
	TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);
	
	//配置TIM3的中断触发方式:时间更新中断
	TIM_ITConfig(TIM3,TIM_IT_Update , ENABLE);

	//配置TIM3的优先级
	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	//使能定时器3工作
	TIM_Cmd(TIM3, ENABLE);
}

main.c

#include "hwconf.h"
#include "common.h"
#include "led.h"
#include "tim.h"

void main()
{
    init_board();//初始化相关时钟
    led4_init();//初始化LED4
    tim3_init();
}

void TIM3_IRQHandler(void)
{
	//检测是否超时时间到达
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
	{
		FLASH_LED4;
		//清空标志位,告诉CPU当前数据接收完毕,可以响应新的中断请求
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
	}
}

  以上的程序都是基于作者本人之前工程模板,和文件进行编写的,有需要可以参考我之前的stm32相关随笔。

总结

  1.注意区分不同类型的TIM;
  2.TIM的输出频率计数有些复杂,需要谨慎计算;
  3.注意PWM占空比的计算:假设高电平有效,比较器值/(计数器值+1)。

原文地址:https://www.cnblogs.com/ding-ding-light/p/14427389.html