ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STM32学习-5,定时器TIM

2021-02-28 15:59:50  阅读:259  来源: 互联网

标签:NVIC 定时器 TIM5 STM32 TIM TIM3 GPIO


5 TIM

定时器类型

STM32内部最多包含8个定时/计数器。其中TIM6和TIM7为基本丁三歌曲,TIM2~TIM5为通用定时器,TIM和TIM8为高级控制定时器,功能最强,此外STM32中还有两个看门狗定时器和一个系统滴答定时器。

在这里插入图片描述

基本定时器

内部集成了1个16位自动加载递增计数器,1个16位预分频器。两个定时器相互独立。

通用定时器

内部集成了1个16位自动加载递增/递减计数器,1个16位预分频器和4个独立通道。每一个通道都可以用于输入捕获,输出比较,PWM输出和单脉冲输出。

高级定时器

内部集成了1个16位自动加载递增/递减计数器,1个16位预分频器和4个独立通道。每一个通道都可以用于输入捕获,输出比较,PWM输出和单脉冲输出。当高级定时器配置为16位标准定时器时,与通用定时器有相同的功能,被配置为16位PWM发生器,具有全调制能力,且调制范围为0-100%

计数模式

(1)向上计数模式

计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器向上溢出时间,每次溢出时可以产生更新事件。

(2)向下计数模式

计数器从自动加载值(TIMx_ARR计数器的内容)向下计数到0,然后从自动装载值重新开始并且产生一个计数器向下溢出时间,每次溢出时可以产生更新事件。

(3)中央对齐模式(向上/向下计数)

计数器从0开始计数到自动加载值(TIMx_ARR寄存器)-1,产生一个计数器向上溢出事件,最后向下计数到1并产生一个计数器向下溢出时间,最后再从0开始重新计数。

主要功能

定时

外部时间计数:可计算外部脉冲个数,频率和宽度,当计数不能满足要求时,可采用定时器级联,扩大计数范围

输入捕获:用来计算脉冲频率和宽度,输出比较用来控制一个输出波形,或者指示一段给地那个的时间已经到时

单脉冲输出

正交编码器

霍尔传感器输入

输出比较信号死区产生:高级控制定时器能够输出两路互补信号,并且能够管理输出的瞬间关断和接通,这段时间通常称为死区,用户应该根据连接的输出器件和他们的特性来调整死区时间

固件库函数

TIM_TimeBaseInit

在这里插入图片描述
在这里插入图片描述

TIM_Period

TIM_Period 设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。它的取值必须在 0x0000 和

0xFFFF 之间。

TIM_Prescaler

TIM_Prescaler 设置了用来作为 TIMx 时钟频率除数的预分频值。它的取值必须在 0x0000 和 0xFFFF 之间。

TIM_ClockDivision

TIM_ClockDivision 设置了时钟分割。该参数取值见下表。TIM

TIM_CounterMode

TIM_CounterMode 选择了计数器模式。该参数取值见下表。

定时器时间计算
 Tout=((ARR+1)*(PSC+1))/Tclk

Tout :TIM溢出时间 单位 us
ARR : 自动重载值
PSC : 预分频系数

Tclk : TIM 输入时钟频率 (MHZ)

例:

TIM_TimeBaseStructure.TIM_ClockDivision  =  TIM_CKD_DIV1; 
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 4999; 		//自动装载值
TIM_TimeBaseStructure.TIM_Prescaler =7199;    //预分频值

Tout = (4999+1)*(7199+1)/72 = 500000us= 500 ms

关于时钟分割(TIM_ClockDivision

时钟分割是对输入而言的,输入不是具有硬件滤波器嘛,那个就是时钟分割就是选择滤波器的采样频率的。

时钟分割定义的是在定时器时钟频率(CK_INT)与数字滤波器 (ETR,TIx)使用的采样频率之间的分频比例。

数字滤波器 (ETR,TIx)是为了将 *ETR进来的分频后的信号滤波,保证通过信号频率不超过某个限定。*

输入预分频。意思是控制在多少个输入周期做一次捕获,如果 输入的信号频率没有变,测得的周期也不会变。比如选择4分频,则每四个输入周期才做一次捕获,这样在输入信号变化不频繁的情况下, 可以减少软件被不断中断的次数。

TIM _ITConfig

在这里插入图片描述
在这里插入图片描述

TIM_ GetFlagStatus

在这里插入图片描述
在这里插入图片描述

TIM_ClearITPendingBit

在这里插入图片描述

TIM_OCInit

在这里插入图片描述
在这里插入图片描述

TIM_OCMode

TIM_OCMode 选择定时器模式。该参数取值见下表。

在这里插入图片描述

TIM_SetCompare1

在这里插入图片描述

就是控制的通道1的

TIM_ICInit

在这里插入图片描述
在这里插入图片描述

示例

定时器中断

//通用定时器 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 使能
 
//定时器 TIM3 初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIM3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //允许更新中断
//中断优先级 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); //使能 TIM3 
}
//定时器 3 中断服务程序
void TIM3_IRQHandler(void) //TIM3 中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查 TIM3 更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除 TIM3 更新中断标志
LED1=!LED1;
} 
}

PWM

STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出.

不管向上还是向下计数,只要是设置高极性TIM_SetCompare2(TIM3,320);设置的都是低电平

不管向上还是向下计数,只要是设置低极性TIM_SetCompare2(TIM3,320);设置的都是高电平

最大可以设置到

//TIM3 PWM 部分初始化
//PWM 输出初始化
//arr:自动重装值
//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 复用功能时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //重映射 TIM3_CH2->PB5 
//设置该引脚为复用输出功能,输出 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
//初始化 TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在自动重装载周期值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIMx
//初始化 TIM3 Channel2 PWM 模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //初始化外设 TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能 TIM3
}

TIM3_PWM_Init(899,0);	 //不分频,PWM频率为72000000/900=80Khz
TIM_SetCompare2(TIM3,320);
可以用keil5内部的功能仿真一下

1.点击魔术棒—>Debug—>Use Simultor勾选上

在这里插入图片描述

2.点击Debug

在这里插入图片描述

3.点击打开logic analysis窗口

在这里插入图片描述

4.点击Setup

在这里插入图片描述

5.输入点击右上方的框框添加要检测输出的GPIO引脚号(我这里用的是PB5,输入格式是:GPIOX_IDR.n),然后回车确认,勾选bit。

在这里插入图片描述

在这里插入图片描述

6.点击Run运行,如果没有波形点旁边的红叉应该就有了

在这里插入图片描述

在这里插入图片描述

输入捕获

//定时器 5 通道 1 输入捕获配置
TIM_ICInitTypeDef TIM5_ICInitStructure;
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能 TIM5 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟

//初始化 GPIOA.0 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 设置 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入 
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉
//②初始化 TIM5 参数
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器 
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //初始化 TIMx
//③初始化 TIM5 输入捕获通道 1
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端 IC1 映射到 TI1 上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到 TI1 上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure); //初始化 TIM5 输入捕获通道 1
//初始化 NVIC 中断优先级分组
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//④允许更新中断捕获中断
TIM_Cmd(TIM5,ENABLE ); //使能定时器 5 
}
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态 
u16 TIM5CH1_CAPTURE_VAL;//输入捕获值
//定时器 5 中断服务程序
void TIM5_IRQHandler(void)
{ 
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{ 
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{ 
if(TIM5CH1_CAPTURE_STA&0X40) //已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
} 
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) //捕获 1 发生捕获事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{ 
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次上升沿
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM5,0);
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //设置为下降沿捕获
} 
} 
}
 TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}

//主函数
extern u8 TIM5CH1_CAPTURE_STA; //输入捕获状态 
extern u16 TIM5CH1_CAPTURE_VAL; //输入捕获值
int main(void)
{
u32 temp=0; 
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2
uart_init(115200); //串口初始化波特率为 115200
LED_Init(); //LED 端口初始化
TIM3_PWM_Init(899,0); //不分频。PWM 频率=72000/(899+1)=80Khz
TIM5_Cap_Init(0XFFFF,72-1); //以 1Mhz 的频率计数
 while(1)
{
delay_ms(10);
TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);
if(TIM_GetCapture2(TIM3)==300)
TIM_SetCompare2(TIM3,0);
if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=65536;//溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
printf("HIGH:%d us\r\n",temp); //打印总的高点平时间
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}
} }

捕获高电平脉宽的思路:首先,设置 TIM5_CH1 捕获上升沿,这在TIM5_Cap_Init 函数执行的时候就设置好了,然后等待上升沿中断到来,当捕获到上升沿中断,此时如果 TIM5CH1_CAPTURE_STA 的第 6 位为 0,则表示还没有捕获到新的上升沿,就先把TIM5CH1_CAPTURE_STA、TIM5CH1_CAPTURE_VAL 和 TIM5->CNT 等清零,然后再设TIM5CH1_CAPTURE_STA 的第 6 位为 1,标记捕获到高电平,最后设置为下降沿捕获,等待下降沿到来。如果等待下降沿到来期间,定时器发生了溢出,就在 TIM5CH1_CAPTURE_STA里面对溢出次数进行计数,当最大溢出次数来到的时候,就强制标记捕获完成(虽然此时还没有捕获到下降沿)。当下降沿到来的时候,先设置 TIM5CH1_CAPTURE_STA 的第 7 位为 1,标记成功捕获一次高电平,然后读取此时的定时器值到 TIM5CH1_CAPTURE_VAL 里面,最后设置为上升沿捕获,回到初始状态。这样,我们就完成一次高电平捕获了,只要 TIM5CH1_CAPTURE_STA 的第 7 位一直为 1,那么就不会进行第二次捕获,我们在main函数处理完捕获数据后,将TIM5CH1_CAPTURE_STA置零,就可以开启第二次捕获。

标签:NVIC,定时器,TIM5,STM32,TIM,TIM3,GPIO
来源: https://blog.csdn.net/qq_45638430/article/details/114226036

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有