目录
  1. 1. 通用定时器基本原理
    1. 1.1. 三种STM32定时器区别
    2. 1.2. 通用定时器功能特点描述
    3. 1.3. 计数器模式
    4. 1.4. 通用定时器(TIM2,3,4,5)工作过程
  2. 2. 定时器中断原理与配置
    1. 2.1. 时钟选择
    2. 2.2. 定时器中断实验相关寄存器
    3. 2.3. 定时器时基部分常用函数
    4. 2.4. 定时器中断实现步骤
    5. 2.5. 程序源码
  3. 3. PWM输出原理与配置
    1. 3.1. STM32 PWM工作过程
    2. 3.2. 定时器PWM功能常用函数
    3. 3.3. PWM输出配置步骤:
    4. 3.4. 程序源码
  4. 4. 输入捕获原理与配置
    1. 4.1. STM32 输入捕获工作过程
    2. 4.2. 定时器输入捕获功能常用函数
    3. 4.3. 输入捕获的一般配置关键步骤
    4. 4.4. 程序源码
  5. 5. TIMER电容触摸按键原理与实验
    1. 5.1. 电容触摸按键原理
    2. 5.2. 几个重要的函数
    3. 5.3. 程序源码
TIMER通用定时器

通用定时器基本原理

三种STM32定时器区别

定时器 种类 位数 计数器 模式 产生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

通用定时器功能特点描述

  • STM32的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5等)定时器功能特点包括:

  • 16 /32 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)。

  • 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值。

  • 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:

    ① 输入捕获

    ② 输出比较

    ③ PWM 生成(边缘或中间对齐模式)

    ④ 单脉冲模式输出

  • 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

  • 如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):

    ①更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)

    ②触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

    ③输入捕获

    ④输出比较

    ⑤支持针对定位的增量(正交)编码器和霍尔传感器电路

    ⑥触发输入作为外部时钟或者按周期的电流管理

  • STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。

  • 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

计数器模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式

①向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。

②向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。

③中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

通用定时器(TIM2,3,4,5)工作过程

通用定时器工作过程

定时器中断原理与配置

时钟选择

计数器时钟可以由下列时钟源提供:

内部时钟(CK_INT)

除非APB1的分频系数是1,否则通用定时器的时钟CK_INT等于APB1时钟的2倍。

内部时钟选择

②外部时钟模式1:外部输入脚(TIx)

③外部时钟模式2:外部触发输入(ETR)(仅适用TIM2,3,4)

④内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

定时器中断实验相关寄存器

计数器当前值寄存器CNT

计数器当前值寄存器CNT

预分频寄存器TIMx_PSC

预分频寄存器TIMx_PSC

自动重装载寄存器(TIMx_ARR)

自动重装载寄存器(TIMx_ARR)

控制寄存器1(TIMx_CR1)

控制寄存器1(TIMx_CR1)

DMA中断使能寄存器(TIMx_DIER)

DMA中断使能寄存器(TIMx_DIER)

定时器时基部分常用函数

①定时器时基参数初始化函数:

1
2
3
4
5
6
7
8
9
10
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);

typedef struct
{
uint32_t Prescaler; //预分频系数
uint32_t CounterMode; //计数模式:向上/下
uint32_t Period; //自动装载值
uint32_t ClockDivision; //时钟分频因子:定时器时钟与数字滤波器分频比
uint32_t RepetitionCounter; //重复计数次数:高级定时器使用
} TIM_Base_InitTypeDef;

②定时器时基参数初始化回调函数:

1
2
3
4
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim);
/*主要用来编写定时器时钟使能,以及中断优先级。*/

__HAL_RCC_TIM3_CLK_ENABLE();//定时器3时钟使能

③使能定时器:

1
2
3
4
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
//此函数同时开启了定时器更新中断

④定时器中断通用处理函数:

1
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);

该函数被中断服务函数调用。是定时器中断处理通用入口函数,

通过对中断类型进行分析判断,调用对应的回调函数。

⑤定时器中断处理回调函数:

1
2
3
4
5
6
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_ErrorCallback(TIM_HandleTypeDef *htim)

定时器中断实现步骤

① 使能定时器时钟。

1
__HAL_RCC_TIM3_CLK_ENABLE();

② 初始化定时器,配置ARR,PSC。

1
HAL_TIM_Base_Init();

③ 开启定时器/中断。

1
2
3
HAL_TIM_Base_Start();

HAL_TIM_Base_Start_IT();

④ 设置中断优先级。

1
2
HAL_NVIC_SetPriority();
HAL_NVIC_EnableIRQ();

⑤ 编写中断服务函数。

1
2
3
4
5
TIMx_IRQHandler();//中断服务函数

HAL_TIM_IRQHandler();//中断处理入口函数

HAL_TIM_PeriodElapsedCallback();//定时器更新中断回调函数

程序源码

timer.c

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
#include "timer.h"
#include "led.h"

TIM_HandleTypeDef TIM3_Handler; //定时器句柄

//通用定时器3中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!(定时器3挂在APB1上,时钟为HCLK/2)
void TIM3_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //通用定时器3
TIM3_Handler.Init.Prescaler=psc; //分频系数
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM3_Handler.Init.Period=arr; //自动装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
HAL_TIM_Base_Init(&TIM3_Handler);

HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE
}


//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟
HAL_NVIC_SetPriority(TIM3_IRQn,1,3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM3_IRQn); //开启ITM3中断
}
}


//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler);
}


//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
LED1=!LED1; //LED1反转
}
}

PWM输出原理与配置

STM32 PWM工作过程

PWM模式:脉冲宽度调制模式可以生成一个信号,该信号频率由TIMx_ARR寄存器决定,其占空比则由TIMx_CRRx寄存器决定。

PWM工作过程1

PWM工作过程2

CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。

CCMR1: OC1M[2:0]位:

​ 对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】

PWM模式

CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。

CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。

定时器PWM功能常用函数

①定时器PWM时基参数初始化函数:

1
2
3
4
5
6
7
8
9
10
11
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)

typedef struct
{
uint32_t Prescaler; //预分频系数
uint32_t CounterMode; //计数模式:向上/下
uint32_t Period; //自动装载值
uint32_t ClockDivision; //时钟分频因子:定时器时钟与数字滤波器分频比

uint32_t RepetitionCounter; //重复计数次数:高级定时器使用
} TIM_Base_InitTypeDef;

②定时器PWM时基参数初始化回调函数:

1
2
3
4
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim);
/*主要用来编写定时器时钟使能等。*/

__HAL_RCC_TIM3_CLK_ENABLE();//定时器3时钟使能

③PWM输出比较通道参数配置函数:

1
2
3
4
5
6
7
8
9
10
11
12
HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, TIM_OC_InitTypeDef* sConfig, uint32_t channel);

typedef struct
{
uint32_t OCMode; //模式PMW1 OR PWM2
uint32_t Pulse; //设置比较值
uint32_t OCPolarity; //输出比较极性
uint32_t OCNPolarity;
uint32_t OCFastMode;
uint32_t OCIdleState;
uint32_t OCNIdleState;
} TIM_OC_InitTypeDef;

④使能定时器和PWM输出比较通道:

1
2
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);//此函数同时开启了定时器PWM通道的输出比较中断

PWM输出配置步骤:

①使能定时器时钟和通道IO口时钟。

②配置IO口复用映射:

1
HAL_GPIO_Init();

③初始化PWM时基参数:

1
HAL_TIM_PWM_Init();

④初始化PWM通道参数:

1
HAL_TIM_PWM_ConfigChannel();

⑤使能定时器PWM

1
HAL_TIM_PWM_Start();

程序源码

timer.c

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
#include "timer.h"
#include "led.h"

TIM_HandleTypeDef TIM3_Handler; //定时器3PWM句柄
TIM_OC_InitTypeDef TIM3_CH4Handler; //定时器3通道4句柄

//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //定时器3
TIM3_Handler.Init.Prescaler=psc; //定时器分频
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM3_Handler.Init.Period=arr; //自动重装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWM

TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4

HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道4
}


//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器3
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟

GPIO_Initure.Pin=GPIO_PIN_1; //PB1
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate= GPIO_AF2_TIM3; //PB1复用为TIM3_CH4
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}


//设置TIM通道4的占空比
//compare:比较值
void TIM_SetTIM3Compare4(u32 compare)
{
TIM3->CCR4=compare;
}

输入捕获原理与配置

STM32 输入捕获工作过程

STM32 输入捕获工作过程

通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。

步骤1:设置输入捕获滤波器

设置输入捕获滤波器1

设置输入捕获滤波器2

步骤2:设置输入捕获极性

设置输入捕获极性1

设置输入捕获极性2

步骤三:设置输入捕获映射通道

设置输入捕获映射通道1

设置输入捕获映射通道2

步骤四:设置输入捕获分频器

设置输入捕获分频器(通道1为例)1

设置输入捕获分频器(通道1为例)2

设置输入捕获分频器(通道1为例)3

步骤五:捕获到有效信号可以开启中断

捕获到有效信号可以开启中断

定时器输入捕获功能常用函数

①定时器输入捕获时基参数初始化函数:

1
2
3
4
5
6
7
8
9
10
HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)

typedef struct
{
uint32_t Prescaler; //预分频系数
uint32_t CounterMode; //计数模式:向上/下
uint32_t Period; //自动装载值
uint32_t ClockDivision; //时钟分频因子:定时器时钟与数字滤波器分频比
uint32_t RepetitionCounter; //重复计数次数:高级定时器使用
} TIM_Base_InitTypeDef;

该函数和HAL_TIM_Base_Init/HAL_TIM_PWM_Init函数作用一样,

不同的是引导调用不同的回调函数。

②定时器输入捕获时基参数初始化回调函数:

1
2
3
4
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim);
/*主要用来编写定时器时钟使能,通道IO配置以及中断优先级设置等。*/

__HAL_RCC_TIM5_CLK_ENABLE();//定时器3时钟使能

③输入捕获通道参数配置函数:

1
2
3
4
5
6
7
8
9
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim,TIM_IC_InitTypeDef* sConfig, uint32_t Channel);

typedef struct
{
uint32_t ICPolarity; //捕获极性
uint32_t ICSelection; //输入映射
uint32_t ICPrescaler; //输入分频
uint32_t ICFilter; //输入滤波器
} TIM_IC_InitTypeDef;

④使能定时器并使能输入捕获通道:

1
2
3
HAL_StatusTypeDef HAL_TIM_IC_Start (TIM_HandleTypeDef *htim, uint32_t Channel);

HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef *htim, uint32_t Channel);//该函数同时还使能了捕获中断。

⑤捕获中断回调函数:

1
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

⑥读取捕获值:

1
uint32_t HAL_TIM_ReadCapturedValue(TIM_HandleTypeDef*htim,uint32_t Channel);

输入捕获的一般配置关键步骤

①使能定时器时钟和通道IO口时钟。

②配置IO口复用映射:

1
HAL_GPIO_Init();

③初始化输入捕获时基参数:

1
HAL_TIM_IC_Init();

④初始化输入捕获通道参数:

1
HAL_TIM_IC_ConfigChannel();

⑤使能定时器(中断),设置优先级:

1
HAL_TIM_IC_Start_IT();

⑥编写中断服务回调函数:

1
HAL_TIM_IC_CaptureCallback();

程序源码

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include "timer.h"
#include "led.h"


TIM_HandleTypeDef TIM3_Handler; //定时器3PWM句柄
TIM_OC_InitTypeDef TIM3_CH4Handler; //定时器3通道4句柄


/***************************************************************************
****************************************************************************
下面是PWM输出实验相关函数源码
****************************************************************************
****************************************************************************/

//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //定时器3
TIM3_Handler.Init.Prescaler=psc; //定时器分频
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM3_Handler.Init.Period=arr; //自动重装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWM

TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比,
//默认比较值为自动重装载值的一半,即占空比为50%
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道4
}

//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器3
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟

GPIO_Initure.Pin=GPIO_PIN_1; //PB1
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推完输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate= GPIO_AF2_TIM3; //PB1复用为TIM3_CH4
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}


//设置TIM通道4的占空比
//compare:比较值
void TIM_SetTIM3Compare4(u32 compare)
{
TIM3->CCR4=compare;
}

//获取TIM捕获/比较寄存器值
u32 TIM_GetTIM3Capture4(void)
{
return HAL_TIM_ReadCapturedValue(&TIM3_Handler,TIM_CHANNEL_4);
}


/***************************************************************************
****************************************************************************
下面是输入捕获相关源码实验相关函数源码
****************************************************************************
****************************************************************************/


TIM_HandleTypeDef TIM5_Handler; //定时器5句柄


//定时器5通道1输入捕获配置
//arr:自动重装值(TIM2,TIM5是32位的!!)
//psc:时钟预分频数
void TIM5_CH1_Cap_Init(u32 arr,u16 psc)
{
TIM_IC_InitTypeDef TIM5_CH1Config;

TIM5_Handler.Instance=TIM5; //通用定时器5
TIM5_Handler.Init.Prescaler=psc; //分频系数
TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM5_Handler.Init.Period=arr; //自动装载值
TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频银子
HAL_TIM_IC_Init(&TIM5_Handler);//初始化输入捕获时基参数

TIM5_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获
TIM5_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到TI1上
TIM5_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_CH1Config.ICFilter=0; //配置输入滤波器,不滤波
HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1);//配置TIM5通道1

HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_1); //开启TIM5的捕获通道1,并且开启捕获中断
__HAL_TIM_ENABLE_IT(&TIM5_Handler,TIM_IT_UPDATE); //使能更新中断
}


//定时器5底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_IC_Init()调用
//htim:定时器5句柄
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM5_CLK_ENABLE(); //使能TIM5时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟

GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF2_TIM5; //PA0复用为TIM5通道1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);

HAL_NVIC_SetPriority(TIM5_IRQn,2,0); //设置中断优先级,抢占优先级2,子优先级0
HAL_NVIC_EnableIRQ(TIM5_IRQn); //开启ITM5中断通道
}


//捕获状态
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到低电平;1,已经捕获到低电平了.
//[5:0]:捕获低电平后溢出的次数(对于32位定时器来说,1us计数器加1,溢出时间:4294秒)
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)


//定时器5中断服务函数
void TIM5_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM5_Handler);//定时器共用处理函数
}


//定时器更新中断(计数溢出)中断处理回调函数, 该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中断(溢出)发生时执行
{

if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
}


//定时器输入捕获中断处理回调函数,该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_1);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
__HAL_TIM_DISABLE(&TIM5_Handler); //关闭定时器5
__HAL_TIM_SET_COUNTER(&TIM5_Handler,0);
TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定时器5通道1设置为下降沿捕获
__HAL_TIM_ENABLE(&TIM5_Handler);//使能定时器5
}
}

}

TIMER电容触摸按键原理与实验

电容触摸按键原理

结论:同样的条件下,电容值C跟时间值t成正比关系,电容越大,充电到达某个临界值的时间越长。

电容触摸按键原理

R:外接电容充放电电阻。

Cs:TPAD和PCB间的杂散电容。

Cx:手指按下时,手指和TPAD之间的电容。

开关:电容放电开关,由STM32 IO口代替。

检测电容触摸按键过程

①TPAD引脚设置为推挽输出,输出0,实现电容放电到0。

②TPAD引脚设置为浮空输入(IO复位后的状态),电容开始充电。

③同时开启TPAD引脚的输入捕获开始捕获。

④等待充电完成(充电到底Vx,检测到上升沿)。

⑤计算充电时间。

没有按下的时候,充电时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。我们可以通过检测充放电时间,来判断是否按下。如果T2-T1大于某个值,就可以判断有按键按下。

几个重要的函数

void TPAD_Reset(void)函数:复位TPAD

设置IO口为推挽输出输出0,电容放电。等待放电完成之后,设置为浮空

​ 输入,从而开始充电。同时把计数器的CNT设置为0。

TPAD_Get_Val()函数:获取一次捕获值(得到充电时间)

复位TPAD,等待捕获上升沿,捕获之后,得到定时器的值,计算充电时间。

TPAD_Get_MaxVal()函数:

多次调用TPAD_Get_Val函数获取充电时间。获取最大的值。

④ TPAD_Init()函数:初始化TPAD

在系统启动后,初始化输入捕获。先10次调用TPAD_Get_Val()函数获取

10次充电时间,然后获取中间N(N=8或者6)次的平均值,作为在没有电容触摸按键按下的时候的充电时间缺省值tpad_default_val。

⑤ TPAD_Scan()函数:扫描TPAD

调用TPAD_Get_MaxVal函数获取多次充电中最大的充电时间,跟

tpad_default_val比较,如果大于某个阈值,则认为有触摸动作。

⑥ void TIM2_CH2_Cap_Init(u16 arr,u16 psc//输入捕获通道初始化

可以使用任何一个定时器。M3使用定时器5,M4使用的定时器2。

程序思路程序思路

程序源码

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include "tpad.h"
#include "delay.h"
#include "usart.h"

TIM_HandleTypeDef TIM2_Handler; //定时器2句柄

#define TPAD_ARR_MAX_VAL 0XFFFFFFFF //最大的ARR值(TIM2是32位定时器)
vu16 tpad_default_val=0; //空载的时候(没有手按下),计数器需要的时间

//初始化触摸按键
//获得空载的时候触摸按键的取值.
//psc:分频系数,越小,灵敏度越高.
//返回值:0,初始化成功;1,初始化失败
u8 TPAD_Init(u8 psc)
{
u16 buf[10];
u16 temp;
u8 j,i;
TIM2_CH1_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//设置分频系数
for(i=0;i<10;i++)//连续读取10次
{
buf[i]=TPAD_Get_Val();
delay_ms(10);
}
for(i=0;i<9;i++)//排序
{
for(j=i+1;j<10;j++)
{
if(buf[i]>buf[j])//升序排列
{
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
temp=0;
for(i=2;i<8;i++)temp+=buf[i];//取中间的8个数据进行平均
tpad_default_val=temp/6;
printf("tpad_default_val:%d\r\n",tpad_default_val);
if(tpad_default_val>TPAD_ARR_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!
return 0;
}
//复位一次
//释放电容电量,并清除定时器的计数值
void TPAD_Reset(void)
{
GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_5; //PA5
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); //PA5输出0,放电
delay_ms(5);
__HAL_TIM_CLEAR_FLAG(&TIM2_Handler,TIM_FLAG_CC1|TIM_FLAG_UPDATE); //清除标志位
__HAL_TIM_SET_COUNTER(&TIM2_Handler,0); //计数器值归0

GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
GPIO_Initure.Alternate=GPIO_AF1_TIM2; //PA5复用为TIM2通道1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}

//得到定时器捕获值
//如果超时,则直接返回定时器的计数值.
//返回值:捕获值/计数值(超时的情况下返回)
u16 TPAD_Get_Val(void)
{
TPAD_Reset();
while(__HAL_TIM_GET_FLAG(&TIM2_Handler,TIM_FLAG_CC1)==RESET) //等待捕获上升沿
{
if(__HAL_TIM_GET_COUNTER(&TIM2_Handler)>TPAD_ARR_MAX_VAL-500) return __HAL_TIM_GET_COUNTER(&TIM2_Handler);//超时了,直接返回CNT的值
};
return HAL_TIM_ReadCapturedValue(&TIM2_Handler,TIM_CHANNEL_1);
}

//读取n次,取最大值
//n:连续获取的次数
//返回值:n次读数里面读到的最大读数值
u16 TPAD_Get_MaxVal(u8 n)
{
u16 temp=0;
u16 res=0;
u8 lcntnum=n*2/3;//至少2/3*n的有效个触摸,才算有效
u8 okcnt=0;
while(n--)
{
temp=TPAD_Get_Val();//得到一次值
if(temp>(tpad_default_val*5/4))okcnt++;//至少大于默认值的5/4才算有效
if(temp>res)res=temp;
}
if(okcnt>=lcntnum)return res;//至少2/3的概率,要大于默认值的5/4才算有效
else return 0;
}

//扫描触摸按键
//mode:0,不支持连续触发(按下一次必须松开才能按下一次);1,支持连续触发(可以一直按下)
//返回值:0,没有按下;1,有按下;
u8 TPAD_Scan(u8 mode)
{
static u8 keyen=0; //0,可以开始检测;>0,还不能开始检测
u8 res=0;
u8 sample=3; //默认采样次数为3次
u16 rval;
if(mode)
{
sample=6; //支持连按的时候,设置采样次数为6次
keyen=0; //支持连按
}
rval=TPAD_Get_MaxVal(sample);
if(rval>(tpad_default_val*4/3)&&rval<(10*tpad_default_val))//大于tpad_default_val+(1/3)*tpad_default_val,且小于10倍tpad_default_val,则有效
{
if(keyen==0)res=1; //keyen==0,有效
printf("r:%d\r\n",rval);
keyen=3; //至少要再过3次之后才能按键有效
}
if(keyen)keyen--;
return res;
}

//定时器2通道1输入捕获配置
//arr:自动重装值(TIM2是32位的!!)
//psc:时钟预分频数
void TIM2_CH1_Cap_Init(u32 arr,u16 psc)
{
TIM_IC_InitTypeDef TIM2_CH1Config;

TIM2_Handler.Instance=TIM2; //通用定时器3
TIM2_Handler.Init.Prescaler=psc; //分频
TIM2_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM2_Handler.Init.Period=arr; //自动装载值
TIM2_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_IC_Init(&TIM2_Handler);

TIM2_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获
TIM2_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到TI1上
TIM2_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM2_CH1Config.ICFilter=0; //配置输入滤波器,不滤波
HAL_TIM_IC_ConfigChannel(&TIM2_Handler,&TIM2_CH1Config,TIM_CHANNEL_1);//配置TIM2通道1
HAL_TIM_IC_Start(&TIM2_Handler,TIM_CHANNEL_1); //开始捕获TIM2的通道1
}

//定时器2底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_IC_Init()调用
//htim:定时器2句柄
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM2_CLK_ENABLE(); //使能TIM2时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟

GPIO_Initure.Pin=GPIO_PIN_5; //PA5
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF1_TIM2; //PA5复用为TIM2通道1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}
文章作者: jiangzuojiben
文章链接: http://jiangzuojiben.github.io/2020/01/16/TIMER%E9%80%9A%E7%94%A8%E5%AE%9A%E6%97%B6%E5%99%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 jiangzuojiben