STM32单片机定时器
程序员文章站
2024-02-21 21:13:46
...
通过控制占空比作用到电机上可以改变电压进而控制电机的转速,作用到LED上就可以控制LED灯的亮度
输入捕获:
预分频器:设置计数器每加/减一个数需要多长时间
当引脚产生一个下降沿时,触发输入捕获比较寄存器,输入捕获寄存器将当前计数器中的值复制到自身寄存器中并用一个变量来存储这个值(记为x1 = 1000)。
当第二个下降沿来临时,触发输入捕获比较寄存器将定时器中的值复制到自身寄存器中并用一个变量来存储这个值(记为x2 = 1500)。
方波周期 = (x2 - x1) * 时钟频率(1us) = (1500 - 1000) * 1us = 500 us
输出比较产生PWM:
事先在输出比较寄存器当中存放一个值(例如存放: 25000),重装载寄存器的值为(50000)(当计数器的值达到重装载寄存器中的值时产生溢出中断,然后执行中断中的一些功能函数(例如行LED灯的翻转)),当计数器的值小于25000时输出低电平,大于25000小于50000时输出高电平,循环往复就产生一个连续的方波。
通过修改输出比较寄存器的值来来改变占空比,通过膝盖重装载寄存器的值来改变周期
源码下载链接:https://taileliekaishi.lanzous.com/iV330fiesaf
工程项目结构如下图所示:
其中画红色方框部分为重要函数来进行讲解
TIMx.c
#include "TIMx/TIMx.h"
#include "LED/LED.h"
#include "UART/uart.h"
#include "stdio.h"
/**
* 功能:初始化定时器
* 参数:
* TIMx:指定待设置的定时器,TIM1-TIM4
* prescaler:设置预分频值 0-65535
* period:设置中断周期,即设置重装载寄存器的值 0-65535
* IT_Source:中断源,比如更新中断,四个通道的输入比较中断,取值查看:TIM_interrupt_sources
* NewState:是否使能IT_Source参数指定的中断,ENABLE,DISENABLE
* 返回值:None
*/
void initTIMx(TIM_TypeDef* TIMx,u16 prescaler,u16 period,u16 IT_Source,FunctionalState NewState)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
switch((u32)TIMx)
{
case (u32)TIM1: RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);break; //开启定时器1时钟
case (u32)TIM2: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);break; //开启定时器2时钟
case (u32)TIM3: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);break; //开启定时器3时钟
case (u32)TIM4: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);break; //开启定时器4时钟
/*其他密度的单片机对case进行删减即可兼容,本程序针对中密度单片机*/
default : break;
}
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频因子,该分频不是预分频值,要区分
TIM_TimeBaseStructure.TIM_Period = period; //设置定时器周期
TIM_TimeBaseStructure.TIM_Prescaler =prescaler; //设置预分频值
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); //生效更改
TIM_Cmd(TIMx, ENABLE); //开启计数器
TIM_ITConfig(TIMx,IT_Source,NewState); //使能定时器更新中断
}
/**
* 功能:设置定时器周期,即设置重装载寄存器的值
* 参数:
* TIMx:指定待设置的定时器,TIM1-TIM4
* period:设置中断周期,即设置重装载寄存器的值 0-65535
* 返回值:None
*/
void setPeriod(TIM_TypeDef* TIMx,u16 period)
{
TIM_SetAutoreload(TIMx, period); //设置重装载值
// TIM_SetCounter(TIMx,0);
}
/**
* 功能:设置定时器预分频值
* 参数:
* TIMx:指定待设置的定时器,TIM1-TIM4
* period:设置中断周期,即设置重装载寄存器的值 0-65535
* 返回值:None
*/
void setPrescaler(TIM_TypeDef* TIMx,u16 prescaler)
{
/*设置预分频值等于fCK_PSC/(PSC[15:0]+1)
*fCK_PSC为内部输入时钟,默认72MHz,假如我们想要72000倍分频,即时钟周期1ms
*则我们传入的值应该是71999*/
TIM_PrescalerConfig(TIMx, prescaler,TIM_PSCReloadMode_Update); //设置定时器周期
}
/****************************************定时器中断服务函数************************************************/
void TIM3_IRQHandler(void)
{
// 一个变量被static修饰之后,这个变量的存活周期会一直持续到整个函数执行完毕
// 一个静态的局部变量有些类似于 全局变量:多次调用这个中断服务函数的时候不会每次都初始化,
// 当第一次调用的时候只会初始刷一次,当之后再调用的时候会使用之前的那么旧值
static u32 times = 0;
// 由于定时器中断源很多,因此要判断是哪个中断源触发的中断
// 判断是否定时器更新时间产生的中断
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
// 清除之后等待下一次中断的产生
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //软件清除中断挂起位,否则会一直卡死在中断服务函数
toggleLED();
//显示定时器3中断服务函数执行了多少次
printf("Curren time is %d\n",times++);
}
}
main.c
#include "DELAY/Delay.h"
#include "NVIC/NVIC.h"
#include "TIMx/TIMx.h"
#include "KEY/key.h"
#include "UART/uart.h"
#include "LED/LED.h"
int main(void)
{
u8 key_value;
/*初始化各外设*/
initSysTick();
initUART(); //波特率9600
initLED();
initKey();
initNVIC(NVIC_PriorityGroup_2); // 设置中断优先级的分组
// 7199: 72M / (7199 + 1) = 100us:计数器每加/减一个数需要100us
// 999: 999 + 1 = 1000:要记1000个数,则定时计数器溢出的时间是 100us * 1000 = 100ms
initTIMx(TIM3,7199,999,TIM_IT_Update,ENABLE); // 7199+1才是写入的分频值
// 影子寄存器,缓冲器
TIM_ARRPreloadConfig(TIM3,ENABLE);
while (1)
{
key_value = getKeyValue(KEY_PRESS);
if(key_value==KEY_UP) //UP键按下
{
setPeriod(TIM3,999); // 设置周期为100ms
}else if(key_value==KEY_DOWN) //DOWN键按下
{
setPeriod(TIM3,7999); // 设置周期为800ms
}else //没有按键按下或全按时不进行任何操作
{
}
}
}