超乎你想象的Stm32中的 TIM 定时器(提高篇)
2020-09-23 14:12:43

注:本文属博主学习时所作笔记,内容源大参考于野火的《零死角玩转STM32F103》以及部分网络资料,笔记内容仅作为自己参考,免去频繁查询参考手册的麻烦,如有错误,还请指出!

高级定时器(STM32F1 系列中为 TIM1 和 TIM8)和通用定时器在基本定时器的基础上引入了外部引脚,可以实现输入捕获和输出比较功能。高级定时器比通用定时器增加了可编程死区互补输出、重复计数器、带刹车(断路)功能,这些功能都是针对电机控制方面。

高级控制定时器框图

由于TIM高级定时器结构复杂,功能众多,可以直接看野火的开发书籍,不做过多详述,文章主要侧重几个常用实验,并在代码中层层递进。

PWM 信号输出控制 SG90 舵机

该例程将通过将使用 STM32 输出 PWM 信号,控制 SG90 舵机进行转动,使用的是 TIM3 的通道1 为 PA6 口,舵机接线如下:

SG90 舵机实物图

控制原理及 PWM 配置

伺服电机通常被称为舵机,它是一种带有输出轴的小装置。当我们向伺服器发送一个控制信号时,输出轴就可以转到特定的位置。只要控制信号持续不变,伺服机构就会保持轴的角度位置不改变。如果控制信号发生变化,输出轴的位置也会相应发生变化。

舵机的内部结构如上图所示。你可以看到控制电路,马达,一组齿轮和外壳

舵机的内部结构

舵机内部的控制电路,电位计(可变电阻器)和电机均被连接到电路板上,如内部结构图的右边部分。控制电路通过电位计可监控舵机的当前角度。

如果轴的位置与控制信号相符,那么电机就会关闭。如果控制电路发现这个角度不正确,它就会控制马达转动,直到它达到指定的角度。舵机角度根据制造商的不同而有所不同。比如,一个180度的舵机,它可以在0度至180度之间运动。由于限位装置被安装在主输出装置上,超出这个范围机械结构就不能再转动了。

舵机的输出功率与它所需要转动的距离成正比。如果输出轴需要转动很长的距离,马达就会全速运转,如果它只需要短距离转动,马达就会以较慢的速度运行,这叫做速度比例控制。

舵机的控制一般需要一个 20ms 的时基脉冲,该脉冲的高电平部分一般为 0.5ms~2.5ms 范围内的角度控制脉冲部分。

舵机的运动原理

控制线用于传输角度控制信号。这个角度是由控制信号脉冲的持续时间决定的,这叫做脉冲编码调制(PCM)。舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为 0.5ms-2.5ms 范围,总间隔为 2ms。脉冲的宽度将决定马达转动的距离。例如:1.5毫秒的脉冲,电机将转向90度的位置(通常称为中立位置,对于180°舵机来说,就是90°位置)。如果脉冲宽度小于1.5毫秒,那么电机轴向朝向0度方向。如果脉冲宽度大于1.5毫秒,轴向就朝向180度方向。

伺服电机时序图

综上,设置PWM周期为20ms = (7200*200)/72000000=0.02,所以TIM_Period = 199,TIM_Prescaler = 7199。

以180度舵机 SG90 为例,对应的控制关系是这样的:

脉冲宽度 转动角度 设置占空比
0.5 ms 175
1.0 ms 45° 180
1.5 ms 90° 185
2.0 ms 135° 190
2.5 ms 180° 195

STM32工程目录

很久不接触 STM32 工程,文件结构差点乱了,这里贴个目录树:

├─Doc
│ readme.txt

├─Libraries
│ ├─CMSIS
│ │ └─startup
│ │
│ └─FWlib
│ ├─inc
│ └─src

├─Listing

├─Output

├─Project
│ └─RVMDK(uv5)
│ └─DebugConfig

└─User
│ main.c
│ stm32f10x_conf.h
│ stm32f10x_it.c
│ stm32f10x_it.h

├─PWM
│ bsp_pwm.c
│ bsp_pwm.h

└─SysTick
bsp_SysTick.c
bsp_SysTick.h

参考代码

bsp_pwm.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef  _BSP_PWM_H_
#define _BSP_PWM_H_

#include "stm32f10x.h"
// 这里使用 TIM3
#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
#define GENERAL_TIM_Period 199
#define GENERAL_TIM_Prescaler 7199
// TIM3 输出比较通道
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_6

void GENERAL_TIM_Init(void);

#endif

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

static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
}


static void GENERAL_TIM_Mode_Config(void)
{

// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);

/*--------------------时基结构体初始化-------------------------*/


TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period = GENERAL_TIM_Period;

// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler = GENERAL_TIM_Prescaler;

// 时钟分频因子 ,配置死区时间时需要用到
// TIM_TimeBaseStructure.TIM_ClockDivision = 0;

// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;

// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;

// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);

/*--------------------输出比较结构体初始化-------------------*/
// 占空比配置
// uint16_t CCR_Val = 100;


TIM_OCInitTypeDef TIM_OCInitStructure;

// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

// 输出比较通道 1
//TIM_OCInitStructure.TIM_Pulse = CCR_Val;

TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);

// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}


void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}

main.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
#include "stm32f10x.h"
#include "bsp_pwm.h"
#include "bsp_SysTick.h"


/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 定时器初始化 */
GENERAL_TIM_Init();

/* 配置SysTick 为10us中断一次 */
SysTick_Init();

while(1)
{
SysTick_Delay_Ms( 1000 );
TIM_SetCompare1(TIM3, 175); // 0°
SysTick_Delay_Ms( 1000 );
TIM_SetCompare1(TIM3, 180); // 45°
SysTick_Delay_Ms( 1000 );
TIM_SetCompare1(TIM3, 185); // 90°
SysTick_Delay_Ms( 1000 );
TIM_SetCompare1(TIM3, 190); // 135°
SysTick_Delay_Ms( 1000 );
TIM_SetCompare1(TIM3, 195); // 180°
SysTick_Delay_Ms( 1000 );
}

}

舵机测试

舵机测试