stm32通用定时器用法

1.通用定时器基本介绍
通用定时器包括TIM2、TIM3、TIM4和TIM5
STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。
定时器可以进行定时器基本定时,输出4路PWM,输入捕获
本文详细介绍这三个功能并且利用定时器3并且示例代码使用
2.开发环境
开发平台:keil5
单片机:STM32F103ZET6

3.基本定时功能
3.1定时器时钟来源分析
STM32部分时钟树:

3.1.1 首先我们我们的系统时钟(SYSCLK 72MHz) 经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器27,所以定时器27的时钟频率为还是72MHz

3.1.2 分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值

/*
* 初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频
* 然后指定重装载值arr,这个重装载值的意思就是当 我们的定时器的计数值 达到这个arr时,定时器就会重新装载其他值.
例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数
* 定时器计数的值被重装载一次被就是一个更新(Update)
* 计算Update时间公式
Tout = ((arr+1)(psc+1))/Tclk
公式推导详解:
Tclk是定时器时钟源,在这里就是72Mhz
我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1) MHz
这里的频率的意思就是1s中记 Tclk/(psc+1)M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk 秒
然后我们从0记到arr 就是 (arr+1)
(psc+1)/Tclk
举例:比如我们设置arr=7199,psc=9999
我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz
就是一秒钟记录9000数,每记录一个数就是1/7200秒
我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update
*/
//简单进行定时器初始化,设置 预装载值 和 分频系数
void MY_TIM3_Init(u16 arr,u16 psc){

//初始化结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

//1.分配时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

//2.初始化定时器相关配置
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;

/*在这里说一下这个TIM_ClockDivision 是设置与进行输入捕获相关的分频
    设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

//3.打开定时器
TIM_Cmd(TIM3,ENABLE);

}

/****************** 主函数 ********************/
//在主函数中我们可以调用初始化
int main(){
//定时器初始化
MY_TIM3_Init(7199,9999);
while(1){

    //检测更新标志位
    if(TIM_GetFlagStatus(TIM3,TIM_IT_Update)){
        //清除标志位
        TIM_ClearFlag(TIM3,TIM_IT_Update);
        //....(每隔一秒执行任务)
    }

}

}

4.定时器输出PWM
4.1基本介绍
4.1.1 PWM是脉冲宽度调制,我们是通过改变脉冲的宽度来达到改变输出电压的效果,本质上就是调节占空比实现的,STM32除了基本定时器(TIM6,TIM7)不能输出PWM以外,其它的定时器都具有输出PWM,其中高级定时器(TIM1和TIM8)还能输出7路PWM,基本定时器(TIM2,TIM3,TIM4,TIM5)也可以输出4路PWM

输出PWM是很有用的,比如我们可以通过控制电机来玩小车,或者通过输出PWM改变LED的亮度,制造呼吸灯等等

4.1.2 我们通用定时器能输出PWM的IO口是固定的,虽然我们可以通过重映射可以改变引脚,具体是哪一些IO口我们要通过查阅STM32的参考手册

这里涉及到一个重映射的概念,重映射就是管脚的外设功能映射到另一个管脚,但是不是可以随便映射的,具体对应关系参考手册上的管脚说明。这样优点是可以优化电路设计;扩展功能,减少外设芯片资源

/**
定时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下
TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4
没有重映像的对应情况:
PA6,PA7,PB0,PB1
部分重映像:
PB4,PB5,PB0,PB1
完全重映像:
PC6,PC7,PC8,PC9

当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.
这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!

我们这里是没有使用重映射功能.

*/
// 宏定义
//判断当前是处于哪一种模式,以便于我们初始化IO口
#define NO_REAMP 0
#define PART_REAMP 1
#define FULL_REAMP 2

// —> 这里是需要制定的参数

//指定这里的 当前的模式,我们给她默认指定是 没有重映射
#define CURRENT_MODE NO_REAMP

//*************根据当前模式初始化IO口 函数
void MY_TIM3_GPIO_Init(void){

GPIO_InitTypeDef    GPIO_InitStructure;

//1.开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

//2. 根据当前的重映像的模式 配置时钟 和 初始化相关引脚
switch(CURRENT_MODE){

    //2.1 如果没有重映射
    case NO_REAMP:{

        // 时钟分配
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
        // 初始化IO口
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
        GPIO_Init(GPIOB,&GPIO_InitStructure);

        break;
    }
    //2.2 部分重映射
    case PART_REAMP:{

        // 时钟分配
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
        // 初始化IO口
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
        GPIO_Init(GPIOB,&GPIO_InitStructure);

        break;
    }
    //2.3 全映射
    case FULL_REAMP:{

        // 时钟分配
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
        // 初始化IO口
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
        GPIO_Init(GPIOB,&GPIO_InitStructure);

        break;
    }
    default:break;
}   

}

//***************** 定时器PWM输出初始化函数
void MY_TIM3_PWM_Init(u16 arr,u16 psc){

//初始化结构体
TIM_OCInitTypeDef TIM_OCInitstrcuture;

//1.初始化定时器 和 相关的IO口
MY_TIM3_Init(arr,psc); 
MY_TIM3_GPIO_Init();

//2.初始化PWM的模式

/**
选择PWM模式:
    PWM1模式:
        向上计数时,当我们 当前的 计数值 小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反
    PWM2模式:
        与PWM1模式向上向下计数时完全相反
*/
TIM_OCInitstrcuture.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitstrcuture.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitstrcuture.TIM_OCPolarity = TIM_OCPolarity_High;   //输出电平为高,也就是有效电平为高
TIM_OC1Init(TIM3,&TIM_OCInitstrcuture);                     //这里是设置利用通道1输出

//这里只初始化通道1,我们可以根据自己需求初始化其它通道

// TIM_OC2Init(TIM3,&TIM_OCInitstrcuture);
// TIM_OC3Init(TIM3,&TIM_OCInitstrcuture);
// TIM_OC4Init(TIM3,&TIM_OCInitstrcuture);

TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器

}

//*********************主函数调用
int main(){

//因为我们单片机引脚输出电压3.3V左右,我们设置预装载值为330
MY_TIM3_PWM_Init(330,0);

//我们初始化的时候选择的是PWM1模式,当计数值小于我们的设定值100时为有效电平,这里是高电平
//所以对于的1通道(PA6)电压是大概就是 3.3 * (100/330) = 1V 左右,我们可以用万用表测量
TIM_SetCompare1(TIM3,100);

while(1);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
5.定时器输入捕获
5.1基本介绍
上面介绍了定时器的四路通道可以输出PWM,同样的我们也可以捕获该定时器这四路通道上的边沿状态(上升沿,下降沿)

由此可见基本定时器也不能进行输入捕获,没有思路通道

我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间

5.2开发步骤
输入捕获 (捕获边沿信号,上升沿和下降沿)
首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤
—— 这就是定时器里面的滤波器的任务

指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频
波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为0101(二进制),采样频率(fsampling)为 滤波器频率/2 = 36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。

配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8
//定时器输入捕获初始化
void MY_TIM3_Cap_Init(u16 arr,u16 psc){

//初始化结构体
TIM_ICInitTypeDef TIM_ICInitStructure;

//1.初始化定时器 和 相关的IO口
MY_TIM3_Init(arr,psc); 

//这里的IO口根据自己需求改成输入,我这改成下拉输入,具体代码就不展现了
MY_TIM3_GPIO_Init();

//2.初始化定时器输入捕获
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ; // 设置输入捕获的通道

//不使用过滤器,假设我们想使用,例如上述举例使用0101
//我们就给TIM_ICFilter  = 0x05 ,(0000 0101),根据上表可以知道这个值范围(0x00~0x0F)
TIM_ICInitStructure.TIM_ICFilter = 0x00;

TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;       //配置输入分频,这里不分频,1次检测到边沿信号就发生捕获

/*
    这里说一下定时器通道可以进行交叉捕获,通道1捕获通道2引脚上的边沿信号,通道2捕获通道1引脚,通道3可以捕获通道4对应引脚,... 
    但是只能相邻一对可以相互捕获,例如通道2不能捕获通道3引脚边沿信号
    TIM_ICSelection_DirectTI 表示直接捕获,通道1对应通道1引脚,通道2对应通道2引脚
    TIM_ICSelection_IndirectTI 表示进行交叉捕获
*/
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射捕获对应通道的引脚
TIM_ICInit(TIM3,&TIM_ICInitStructure);                                                  

}
//****************主函数
int main(){
//初始化输入捕获
MY_TIM3_Cap_Init(1000,0);

while(1){
    //检测是否捕获到上升沿
    if(TIM_GetFlagStatus(TIM3,TIM_IT_CC1)){
        TIM_ClearFlag(TIM3,TIM_IT_CC1);
        //捕获到上升沿之后的任务...
        //一般测量高电平脉宽,我们可以先捕获上升沿再捕获下降沿
        //TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); 修改为下降沿捕获
    }

}

}

作者:private_void_main
来源:CSDN
原文:https://blog.csdn.net/private_void_main/article/details/81491015
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值