1.通用定时器常用库函数
常用库函数:stm32f4xx_tim.c/.h
定时器初始化函数 void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
结构体的定义:
2.常用函数的介绍: (函数原型)
1.定时器使能函数: void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
2.定时器中断使能函数:void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
3.状态标志位获取和清除
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);//此函数功能是判断 TIMx 的中断类型 TIM_IT 是否产生中断
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);//清除定时器 TIMx 的中断 TIM_IT 标志位
FlagStatus TIM_GetFlagStatus(); //获取状态标志位
void TIM_ClearFlag(); //清除状态标志位
ITStatus TIM_GetITStatus(); //获取中断状态标志位
void TIM_ClearITPendingBit(); //清除中断状态标志位
4.补充的信息:
FlagStatus 返回值是中断标志位状态(读SR寄存器)
该函数用于检测串口中断标志位的状态。
在没有使能相应的中断函数时,通常使用该函数来判断标志位是否置位。
.ITStatus 返回值是中断发生与否的判断(读CR寄存器)
读取串口控制寄存器CR1,CR2,CR3的状态,获取中断发生的动作,返回SET或RESET。
除了可以判断中断标志位外,还能判断是否发生了中断。
3.芯片里面自带的 定时器有哪些:
定时器种类 |
位数 |
计数器模式 |
产生DMA请求 |
捕获/比较通道 |
互补输出 |
特殊应用场景 |
高级定时器 (TIM1,TIM8) |
16 |
向上,向下,向上/下 |
可以 |
4 |
有 |
带可编程死区的互补输出 |
通用定时器(TIM2,TIM5) |
32 |
向上,向下,向上/下 |
可以 |
4 |
无 |
通用。定时计数,PWM输出, 输入捕获,输出比较 |
通用定时器(TIM3,TIM4) |
16 |
向上,向下,向上/下 |
可以 |
4 |
无 |
通用。定时计数,PWM输出, 输入捕获,输出比较 |
通用定时器(TIM9~TIM14) |
16 |
向上 |
没有 |
2 |
无 |
通用。定时计数,PWM输出, 输入捕获,输出比较 |
基本定时器 (TIM6,TIM7) |
16 |
向上,向下,向上/下 |
可以 |
0 |
无 |
主要应用于驱动DAC |
1. STM3 F4的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:
2. 16 /32 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)。
3. 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值。
4. 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
输入捕获
输出比较
PWM 生成(边缘或中间对齐模式)
单脉冲模式输出
5. 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
4.芯片中断发生的条件 和 定时器的用法 和实现的中断的步骤 :
如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
1. 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
2. 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
3. 输入捕获
4. 输出比较
5. 支持针对定位的增量(正交)编码器和霍尔传感器电路
6. 触发输入作为外部时钟或者按周期的电流管理
STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。
//定时器中断实现步骤
1.使能定时器时钟。
RCC_APB1PeriphClockCmd();
2.初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
3.允许更新中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
4. 使能定时器。
TIM_Cmd();
5.开启定时器中断,配置NVIC。
NVIC_Init();
6. 编写中断服务函数。
TIMx_IRQHandler();
7.在中断服务函数中清除中断标志位
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
实列: (使用定时器 TIM 3 中断)
#include "stm32f4xx.h"
// Header:stm32f407ve-version
// File Name: 定时器初始化函数
// Author:dandy;
// Date:2021.11.2;
void timer3init()
{
//定时器结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//用到中断,所以要有中断结构体
NVIC_InitTypeDef NVIC_InitStructure;
//1使能定时器时钟 步骤1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//自动重装载值
TIM_TimeBaseInitStructure.TIM_Period = 4999; //5000 -》0.5s ---》 0.0001s = 0.1ms
//定时器预分频
TIM_TimeBaseInitStructure.TIM_Prescaler=8399; // 84000000 / 8400 = 10000 //频率的倒数正好是0.0001s
//向上计数模式
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
//时钟分频
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
//2初始化时钟 步骤2
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
//3允许定时器3更新中断 步骤3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
//4使能定时器3 步骤4
TIM_Cmd(TIM3,ENABLE);
//中断配置部分。
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
//中断成员1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
//中断成员2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //响应优先级3
//中断成员3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
//5中断成员4 步骤5
NVIC_Init(&NVIC_InitStructure);
}
//定时器3中断服务函数 步骤6
void TIM3_IRQHandler(void)
{
//测试语句
GPIO_ResetBits(GPIOE,GPIO_Pin_9);
if(num % 2 == 0 )
{
GPIO_ResetBits(GPIOE,GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);
//delay();
GPIO_SetBits(GPIOE,GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);
//delay();
//清除中断标志位 步骤7
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
num++;
if(num > 1000)
num = 0 ;
}
//判断函数是否执行就用测试语句。
void TIM3_IRQHandler()
{
//测试语句
//GPIO_ResetBits(GPIOE,GPIO_Pin_9);
if(num % 2 == 0 )
{
GPIO_ResetBits(GPIOE,GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);
num++;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
else
{
GPIO_SetBits(GPIOE,GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);
num++;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
if(num > 100000)
num = 0 ;
}
.5.定时器计数器模式的 选择 和计数器的时钟源 :
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
①向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
②向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
③中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
计数器时钟可以由下列时钟源提供:
1. 内部时钟(CK_INT)
2. 外部时钟模式1:外部输入脚(TIx)
3. 外部时钟模式2:外部触发输入(ETR)(仅适用TIM2,3,4)
4. 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
6.PWM 模式(脉冲宽度调制) (只有定时器 2 3 4 可以使用)
定时器中断产生pwm
io口如何产生一个pwm ,无非就是做一个高低电平周期性的变化,这种思想很重要,确定频率就可以确定周期(T=1/f)也就是在一个周期内产生pwm的时间可以确定下来了,如何改变占空比? 确定了时间,高电平的时间不就是想要的占空比么,比如要产生一个频率1khz,占空比为70%的pwm,根据频率我们知道了周期为1ms,产生一个占空比为70%的不就是0.7ms的时间给高电平么,(我们用定时器中断的方式,使0.1ms产生一次中断,计数中断次数,中断处理函数前七次中断都给高电平就ok了)
PWM输出的配置步骤:
1. 使能相应的GPIO口的时钟
2. 配置相应的GPIO口为复用功能推挽输出
3. 选择复用功能
4. 使能定时器的相应时钟,即RCC->APB1ENR的相应位置1
5. TIMx_ARR 寄存器进行缓冲
6. 计数器在发生更新事件时不会停止计数(循环计数,循环定时)
7. 使能更新 (UEV)
8. UG位置1重新初始化定时器计数器(TIMx_CNT)
9. 状态寄存器清零
9. 配置预分频值
10. 配置自动重装载值 (用于控制脉冲的周期)
11. CCMR1配置为PWM的模式
12. CCR1影子寄存器有效
13. CC1 通道配置为输出。
14. 配置OC1 有效电平
15. 捕获/比较 1 中断使能
16. 使能定时器中断中断通道,调用NVIC_EnableIRQ函数,调整抢占和响应的优先级,
17. 捕获/比较 1 输出使能
18. 使能定时器
19. 改变CCR1的值可以改变占空比
7.代码:(图片加代码)(因为没有板子实践 只上过课可能有错误 望大家体谅!)
代码:
void Timer3_Init()
{
//1.配置时钟 (填写寄存器 ) 84 名
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); /*使能定时器3 的时钟*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrecture; //定时器时基初始化结构体
NVIC_InitTypeDef NVIC_InitStructure; //中断优先级配置结构体
TIM_TimeBaseInitStrecture.TIM_Period = 4999; /*重装载寄存器数值 0.0001 * 5000 =0.5s*/
TIM_TimeBaseInitStrecture.TIM_Prescaler = 8399; /*预分配数值*/
TIM_TimeBaseInitStrecture.TIM_ClockDivision = TIM_CKD_DIV1; /*时钟分频*/
TIM_TimeBaseInitStrecture.TIM_CounterMode = TIM_CounterMode_Up; /*向上计数*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrecture); /*初始化*/
TIM_ClearFlag(TIM3,TIM_FLAG_Update); /*清更新标志位*/
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); /*使能中断*/
TIM_Cmd(TIM3,ENABLE); /*使能计数*/
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;/*定时器1的中断通道使能*/
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/*定时器1的中断通道使能*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;/*抢占优先级*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;/*响应优先级*/
NVIC_Init(&NVIC_InitStructure);/*配置中断分组,并使能中断*/
}
//定时器中断函数 : 固定名字 并且只要定义 不要声明 不要调用
void TIM3_IRQHander()
{
if(num%2==0)
{
Openbeep();//打开蜂鸣器
num++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//清除TIMx的中断待处理位:TIM3中断源
}
else
{
Closebeep();//关闭蜂鸣器
num++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//清除TIMx的中断待处理位:TIM3中断源
}
}
代码:(PWM)
//TIM2 PWM部分初始化
//PWM输出初始化
void TIM2_PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能PORTA时钟
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//复用不能写成下面,会出问题 (两个引脚不能同时 初始化)
//GPIO_PinAFConfig(GPIOA,GPIO_PinSource8|GPIO_PinSource11,GPIO_AF_TIM1); //GPIO复用为定时器1
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2); //复用GPIOB_Pin10为TIM2,
//GPIO 引脚复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Low_Speed; //速度低速
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化P
TIM_TimeBaseStructure.TIM_Prescaler=1000; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=83r; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//初始化定时器1
//初始化TIM1 PWM模式
//PWM 模式 1–– 在递增计数模式下,只要 TIMx_CNT
8.补充知识:
我自己依据此图理解,应用思维导图画了一张方便理解:(如果看不清可通过ctrl+鼠标滑轮 放大看;)
前提条件1:组别优先顺序(第0组优先级最强,第4组优先级最弱):NVIC_PriorityGroup_0>NVIC_PriorityGroup_1>NVIC_PriorityGroup_2>NVIC_PriorityGroup_3>NVIC_PriorityGroup_4
前提条件2:“组”优先级别>“抢”占优先级别>“副”优先级别
前提条件3:同一组优先级别中,不同的抢占级别之间,其中一抢占级别正在做事,另外抢占级别不能打断他;(即”同一组优先级下的中断源间,没有中断嵌套“)
前提条件4:不同组优先级别间,依据优先级强弱,优先级别高的组的中断源可以打断优先级别低的组的正在做的事情;(即:不同组优先级间,可以中断嵌套)
通过定时器中断配置,每500ms中断一次,然后中断服务函数中控制LED实现LED1状态取反(闪烁)。
Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk;
arr:自动重装值
psc:时钟预分频数
定时器的寄存器:
TIMx计数器:TIMx_CNT
TIMx预分频器:TIMx_PSC
TIMx自动重载寄存器:TIMx_ARR
TIMx 控制寄存器 1:TIMx_CR1
TIMx DMA/中断使能寄存器:TIMx_DIER
6.定时器中断的实现步骤:
(1)使能定时器时钟:
RCC_APB1PeriphClockCmd();
(2)初始化定时器:配置ARR、PSC,TIM_TimeBaseInit();
(3)开启定时器中断:配置NVIC,NVIC_Init();
(4)使能定时器,TIM_Cmd();
(5)编写中断服务函数,TIMx_IRQHandler ();
注意:Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk;
计数器当前值寄存器CNT(图片):
预分频寄存器TIMx_PS(图片):
自动重装载寄存器(TIMx_ARR)(图片):
控制寄存器(图片):