参考:

简介

框图

重复计数器特性

输出指定个数PWM实验

实验原理

配置步骤

相关HAL库函数

关键结构体介绍

编程实战

示例

tim1 ch1 (PA8)输出指定数量pwm信号控制led闪烁

宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 高级定时器 定义 */

/* TIMX 输出指定个数PWM 定义
* 这里输出的PWM通过PA8(TIM1_CH1)输出, 我们用杜邦线连接PA8和PE5, 然后在程序里面将PE5设置成浮空输入
* 就可以 看到TIM1_CH1控制LED1(GREEN)的亮灭, 亮灭一次表示一个PWM波
* 默认使用的是TIM1_CH1.
* 注意: 通过修改这几个宏定义, 可以支持TIM1/TIM8定时器, 任意一个IO口输出指定个数的PWM
*/
#define ATIM_TIMX_NPWM_CHY_GPIO_PORT GPIOA
#define ATIM_TIMX_NPWM_CHY_GPIO_PIN GPIO_PIN_8
#define ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define ATIM_TIMX_NPWM TIM1
#define ATIM_TIMX_NPWM_IRQn TIM1_UP_IRQn
#define ATIM_TIMX_NPWM_IRQHandler TIM1_UP_IRQHandler
#define ATIM_TIMX_NPWM_CHY TIM_CHANNEL_1 /* 通道Y, 1<= Y <=4 */
#define ATIM_TIMX_NPWM_CHY_CCRX TIM1->CCR1 /* 通道Y的输出比较寄存器 */
#define ATIM_TIMX_NPWM_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0) /* TIM1 时钟使能 */

全局变量

1
2
3
4
/* g_npwm_remain表示当前还剩下多少个脉冲要发送
* 每次最多发送256个脉冲
*/
static uint32_t g_npwm_remain = 0;

初始化函数

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
/**
* @brief 高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数
* @note
* 高级定时器的时钟来自APB2, 而PCLK2 = 72Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值
* @param psc: 时钟预分频数
* @retval 无
*/
void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef gpio_init_struct;
TIM_OC_InitTypeDef timx_oc_npwm_chy; /* 定时器输出 */
ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE(); /* TIMX 通道IO口时钟使能 */
ATIM_TIMX_NPWM_CHY_CLK_ENABLE(); /* TIMX 时钟使能 */

g_timx_npwm_chy_handle.Instance = ATIM_TIMX_NPWM; /* 定时器x */
g_timx_npwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_npwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
g_timx_npwm_chy_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /*使能TIMx_ARR进行缓冲 */
g_timx_npwm_chy_handle.Init.RepetitionCounter = 0; /* 重复计数器初始值 */
HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle); /* 初始化PWM */

gpio_init_struct.Pin = ATIM_TIMX_NPWM_CHY_GPIO_PIN; /* 通道y的GPIO口 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推完输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(ATIM_TIMX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);

timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM 1*/
timx_oc_npwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比 */
/* 这里默认设置比较值为自动重装载值的一半,即占空比为50% */
timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */
HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy, ATIM_TIMX_NPWM_CHY); /* 配置TIMx通道y */

HAL_NVIC_SetPriority(ATIM_TIMX_NPWM_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_EnableIRQ(ATIM_TIMX_NPWM_IRQn); /* 开启ITMx中断 */

__HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE); /* 允许更新中断 */
HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, ATIM_TIMX_NPWM_CHY); /* 开启对应PWM通道 */
}

中断函数

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
/**
* @brief 高级定时器TIMX NPWM中断服务函数
* @param 无
* @retval 无
*/
void ATIM_TIMX_NPWM_IRQHandler(void)
{
uint16_t npwm = 0;

/* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
if(__HAL_TIM_GET_FLAG(&g_timx_npwm_chy_handle, TIM_FLAG_UPDATE) != RESET)
{
if (g_npwm_remain >= 256) /* 还有大于256个脉冲需要发送 */
{
g_npwm_remain = g_npwm_remain - 256;
npwm = 256;
}
else if (g_npwm_remain % 256) /* 还有位数(不到256)个脉冲要发送 */
{
npwm = g_npwm_remain % 256;
g_npwm_remain = 0; /* 没有脉冲了 */
}

if (npwm) /* 有脉冲要发送 */
{
ATIM_TIMX_NPWM->RCR = npwm - 1; /* 设置重复计数寄存器值为npwm-1, 即npwm个脉冲 */
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE); /* 产生一次更新事件,在中断里面处理脉冲输出 */
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle); /* 使能定时器TIMX */
}
else
{
ATIM_TIMX_NPWM->CR1 &= ~(1 << 0); /* 关闭定时器TIMX,使用HAL Disable会清除PWM通道信息,此处不用 */
}

__HAL_TIM_CLEAR_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE); /* 清除定时器溢出中断标志位 */
}
}

其他函数

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @brief 高级定时器TIMX NPWM设置PWM个数
* @param rcr: PWM的个数, 1~2^32次方个
* @retval 无
*/
void atim_timx_npwm_chy_set(uint32_t npwm)
{
if (npwm == 0) return;

g_npwm_remain = npwm; /* 保存脉冲个数 */
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE); /* 产生一次更新事件,在中断里面处理脉冲输出 */
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle); /* 使能定时器TIMX */
}

主函数

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
int main(void)
{
uint8_t key = 0;
uint8_t t = 0;

HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
atim_timx_npwm_chy_init(5000 - 1, 7200 - 1); /* 10Khz的计数频率,2hz的PWM频率. */
ATIM_TIMX_NPWM_CHY_CCRX = 2500; /* 设置PWM占空比,50%,这样可以控制每一个PWM周期,LED1(BLUE)
* 有一半时间是亮的,一半时间是灭的,LED1亮灭一次,表示一个PWM波
*/
atim_timx_npwm_chy_set(5); /* 输出5个PWM波(控制LED1(BLUE)闪烁5次) */

while (1)
{
key = key_scan(0);

if (key == KEY0_PRES) /* KEY0按下 */
{
atim_timx_npwm_chy_set(5); /* 输出5个PWM波(控制TIM8_CH1, 即PC6输出5个脉冲) */
}

t++;
delay_ms(10);

if (t > 50) /* 控制LED0闪烁, 提示程序运行状态 */
{
t = 0;
LED0_TOGGLE();
}
}
}

输出比较模式实验

输出比较模式实验

配置步骤

相关HAL库函数介绍

关键结构体介绍

编程实战

此处用tim1的四个通道实验,实验结果用逻辑分析仪观察

宏定义

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
/* TIMX 输出比较模式 定义 
* 这里通过TIM1的输出比较模式,控制PA8,PA9,PA10,PA11输出4路PWM,占空比50%,并且每一路PWM之间的相位差为25%
* 修改CCRx可以修改相位.
* 默认是针对TIM1
* 注意: 通过修改这些宏定义,可以支持TIM1/TIM8任意一个定时器,任意一个IO口使用输出比较模式,输出PWM
*/
#define ATIM_TIMX_COMP_CH1_GPIO_PORT GPIOA
#define ATIM_TIMX_COMP_CH1_GPIO_PIN GPIO_PIN_8
#define ATIM_TIMX_COMP_CH1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define ATIM_TIMX_COMP_CH2_GPIO_PORT GPIOA
#define ATIM_TIMX_COMP_CH2_GPIO_PIN GPIO_PIN_9
#define ATIM_TIMX_COMP_CH2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define ATIM_TIMX_COMP_CH3_GPIO_PORT GPIOA
#define ATIM_TIMX_COMP_CH3_GPIO_PIN GPIO_PIN_10
#define ATIM_TIMX_COMP_CH3_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define ATIM_TIMX_COMP_CH4_GPIO_PORT GPIOA
#define ATIM_TIMX_COMP_CH4_GPIO_PIN GPIO_PIN_11
#define ATIM_TIMX_COMP_CH4_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define ATIM_TIMX_COMP TIM1
#define ATIM_TIMX_COMP_CH1_CCRX ATIM_TIMX_COMP->CCR1 /* 通道1的输出比较寄存器 */
#define ATIM_TIMX_COMP_CH2_CCRX ATIM_TIMX_COMP->CCR2 /* 通道2的输出比较寄存器 */
#define ATIM_TIMX_COMP_CH3_CCRX ATIM_TIMX_COMP->CCR3 /* 通道3的输出比较寄存器 */
#define ATIM_TIMX_COMP_CH4_CCRX ATIM_TIMX_COMP->CCR4 /* 通道4的输出比较寄存器 */
#define ATIM_TIMX_COMP_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0) /* TIM1 时钟使能 */

全局变量

1
TIM_HandleTypeDef g_timx_comp_pwm_handle;       /* 定时器x句柄 */

初始化函数

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
/**
* @brief 高级定时器TIMX 输出比较模式 初始化函数(使用输出比较模式)
* @note
* 配置高级定时器TIMX 4路输出比较模式PWM输出,实现50%占空比,不同相位控制
* 注意,本例程输出比较模式,每2个计数周期才能完成一个PWM输出,因此输出频率减半
* 另外,我们还可以开启中断在中断里面修改CCRx,从而实现不同频率/不同相位的控制
* 但是我们不推荐这么使用,因为这可能导致非常频繁的中断,从而占用大量CPU资源
*
* 高级定时器的时钟来自APB2, 而PCLK2 = 72Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/
void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
{
TIM_OC_InitTypeDef timx_oc_comp_pwm = {0};

g_timx_comp_pwm_handle.Instance = ATIM_TIMX_COMP; /* 定时器8 */
g_timx_comp_pwm_handle.Init.Prescaler = psc ; /* 定时器分频 */
g_timx_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_comp_pwm_handle.Init.Period = arr; /* 自动重装载值 */
g_timx_comp_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能影子寄存器TIMx_ARR */
HAL_TIM_OC_Init(&g_timx_comp_pwm_handle); /* 输出比较模式初始化 */

timx_oc_comp_pwm.OCMode = TIM_OCMODE_TOGGLE; /* 比较输出模式翻转功能 */
timx_oc_comp_pwm.Pulse = 250 - 1; /* 设置输出比较寄存器的值 */
timx_oc_comp_pwm.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */
HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_1); /* 初始化定时器的输出比较通道1 */
__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_1); /* 通道1 预装载使能 */

timx_oc_comp_pwm.Pulse = 500 - 1;
HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_2); /* 初始化定时器的输出比较通道2 */
__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_2); /* 通道2 预装载使能 */

timx_oc_comp_pwm.Pulse = 750 - 1;
HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_3); /* 初始化定时器的输出比较通道3 */
__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_3); /* 通道3 预装载使能 */

timx_oc_comp_pwm.Pulse = 1000 - 1;
timx_oc_comp_pwm.OCIdleState = TIM_OCIDLESTATE_RESET;
HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_4); /* 初始化定时器的输出比较通道4 */
__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_4); /* 通道4 预装载使能 */

HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
}

/**
* @brief 定时器底层驱动,时钟使能,引脚配置
此函数会被HAL_TIM_OC_Init()调用
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == ATIM_TIMX_COMP)
{
GPIO_InitTypeDef gpio_init_struct;

ATIM_TIMX_COMP_CLK_ENABLE();

ATIM_TIMX_COMP_CH1_GPIO_CLK_ENABLE();
ATIM_TIMX_COMP_CH2_GPIO_CLK_ENABLE();
ATIM_TIMX_COMP_CH3_GPIO_CLK_ENABLE();
ATIM_TIMX_COMP_CH4_GPIO_CLK_ENABLE();

gpio_init_struct.Pin = ATIM_TIMX_COMP_CH1_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(ATIM_TIMX_COMP_CH1_GPIO_PORT, &gpio_init_struct);

gpio_init_struct.Pin = ATIM_TIMX_COMP_CH2_GPIO_PIN;
HAL_GPIO_Init(ATIM_TIMX_COMP_CH2_GPIO_PORT, &gpio_init_struct);

gpio_init_struct.Pin = ATIM_TIMX_COMP_CH3_GPIO_PIN;
HAL_GPIO_Init(ATIM_TIMX_COMP_CH3_GPIO_PORT, &gpio_init_struct);

gpio_init_struct.Pin = ATIM_TIMX_COMP_CH4_GPIO_PIN;
HAL_GPIO_Init(ATIM_TIMX_COMP_CH4_GPIO_PORT, &gpio_init_struct);
}
}

主函数

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
int main(void)
{
uint8_t t = 0;

HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_init(); /* 初始化LED */

atim_timx_comp_pwm_init(1000 - 1, 72 - 1); /* 1Mhz的计数频率 1Khz的周期. */

ATIM_TIMX_COMP_CH1_CCRX = 250 - 1; /* 通道1 相位25% */
ATIM_TIMX_COMP_CH2_CCRX = 500 - 1; /* 通道2 相位50% */
ATIM_TIMX_COMP_CH3_CCRX = 750 - 1; /* 通道3 相位75% */
ATIM_TIMX_COMP_CH4_CCRX = 1000 - 1; /* 通道4 相位100% */

while (1)
{
delay_ms(10);
t++;

if (t >= 20)
{
LED0_TOGGLE(); /* LED0(RED)闪烁 */
t = 0;
}
}
}

实验现象

互补输出带死区控制实验

参考:正点原子

死区是什么

死区的应用

捕获/比较通道的输出部分(通道1至3)

死区时间计算

刹车(断路)功能

发生刹车后,会怎么样?

实验配置步骤

相关HAL库函数介绍

关键结构体介绍

编程实战

本次实验使用tim1的ch1(A8)、ch1n(B13)、bkin(B12)通道,无重映射
设置CKD=2即fdts四分频,设置DTG=100, 死区时间55.56us
输出有效电平为低电平,频率1KHz,占空比30%
实验结果用逻辑分析仪观察

宏定义

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
/*****************************************以下是高级定时器互补输出带死区控制实验相关宏定义*************************************************/

/* TIMX 互补输出模式 定义
* 这里设置互补输出相关硬件配置, CHY即正常输出, CHYN即互补输出
* 修改CCRx可以修改占空比.
* 默认是针对TIM1
* 注意: 通过修改这些宏定义,可以支持TIM1/TIM8定时器, 任意一个IO口输出互补PWM(前提是必须有互补输出功能)
*/

/* 输出通道引脚 */
#define ATIM_TIMX_CPLM_CHY_GPIO_PORT GPIOA
#define ATIM_TIMX_CPLM_CHY_GPIO_PIN GPIO_PIN_8
#define ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */

/* 互补输出通道引脚 */
#define ATIM_TIMX_CPLM_CHYN_GPIO_PORT GPIOB
#define ATIM_TIMX_CPLM_CHYN_GPIO_PIN GPIO_PIN_13
#define ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */

/* 刹车输入引脚 */
#define ATIM_TIMX_CPLM_BKIN_GPIO_PORT GPIOB
#define ATIM_TIMX_CPLM_BKIN_GPIO_PIN GPIO_PIN_12
#define ATIM_TIMX_CPLM_BKIN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */

/* TIMX REMAP设置
* 因为PE8/PE9/PE15, 默认并不是TIM1的复用功能脚, 必须开启完全重映射, 才可以将: TIM1_CH1->PE9; TIM1_CH1N->PE8; TIM1_BKIN->PE15;
* 这样, PE8/PE9/PE15, 才能用作TIM1的CH1N/CH1/BKIN功能.
* 因此, 必须实现ATIM_TIMX_CPLM_CHYN_GPIO_REMAP, 通过sys_gpio_remap_set函数设置重映射
* 如果我们使用默认的复用功能输出, 则不用设置重映射, 是可以不需要该函数的! 根据具体需要来实现.
*/
#define ATIM_TIMX_CPLM_CHYN_GPIO_REMAP() do{__HAL_RCC_AFIO_CLK_ENABLE();\
__HAL_AFIO_REMAP_TIM1_ENABLE();\
}while(0) /* 通道REMAP设置, 该函数不是必须的, 根据需要实现 */

/* 互补输出使用的定时器 */
#define ATIM_TIMX_CPLM TIM1
#define ATIM_TIMX_CPLM_CHY TIM_CHANNEL_1
#define ATIM_TIMX_CPLM_CHY_CCRY ATIM_TIMX_CPLM->CCR1
#define ATIM_TIMX_CPLM_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0) /* TIM1 时钟使能 */

/******************************************************************************************/

void atim_timx_pwm_chy_init(uint16_t arr, uint16_t psc); /* 高级定时器 PWM初始化函数 */
void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc); /* 高级定时器 输出指定个数PWM初始化函数 */
void atim_timx_npwm_chy_set(uint32_t npwm); /* 高级定时器 设置输出PWM的个数 */
void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc); /* 高级定时器 输出比较模式输出PWM 初始化函数 */
void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc); /* 高级定时器 互补输出 初始化函数 */
void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg); /* 高级定时器 互补输出 设置输出比较值 & 死区时间 */

全局变量

1
2
TIM_HandleTypeDef g_timx_cplm_pwm_handle;                              /* 定时器x句柄 */
TIM_BreakDeadTimeConfigTypeDef g_sbreak_dead_time_config = {0}; /* 死区时间设置 */

初始化

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
/**
* @brief 高级定时器TIMX 互补输出 初始化函数(使用PWM模式1)
* @note
* 配置高级定时器TIMX 互补输出, 一路OCy 一路OCyN, 并且可以设置死区时间
*
* 高级定时器的时钟来自APB2, 而PCLK2 = 72Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/

void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef gpio_init_struct = {0};
TIM_OC_InitTypeDef tim_oc_cplm_pwm = {0};

ATIM_TIMX_CPLM_CLK_ENABLE(); /* TIMx 时钟使能 */
ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE(); /* 通道X对应IO口时钟使能 */
ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE(); /* 通道X互补通道对应IO口时钟使能 */
ATIM_TIMX_CPLM_BKIN_GPIO_CLK_ENABLE(); /* 通道X刹车输入对应IO口时钟使能 */

gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHY_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH ;
HAL_GPIO_Init(ATIM_TIMX_CPLM_CHY_GPIO_PORT, &gpio_init_struct);

gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHYN_GPIO_PIN;
HAL_GPIO_Init(ATIM_TIMX_CPLM_CHYN_GPIO_PORT, &gpio_init_struct);

gpio_init_struct.Pin = ATIM_TIMX_CPLM_BKIN_GPIO_PIN;
HAL_GPIO_Init(ATIM_TIMX_CPLM_BKIN_GPIO_PORT, &gpio_init_struct);

// ATIM_TIMX_CPLM_CHYN_GPIO_REMAP(); /* 映射定时器IO,PE不是本例程所用定时器的默认IO,需要复用 */

g_timx_cplm_pwm_handle.Instance = ATIM_TIMX_CPLM; /* 定时器x */
g_timx_cplm_pwm_handle.Init.Prescaler = psc; /* 定时器预分频系数 */
g_timx_cplm_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_cplm_pwm_handle.Init.Period = arr; /* 自动重装载值 */
g_timx_cplm_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; /* CKD[1:0] = 10, tDTS = 4 * tCK_INT = Ft / 4 = 18Mhz */
g_timx_cplm_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能影子寄存器TIMx_ARR */
HAL_TIM_PWM_Init(&g_timx_cplm_pwm_handle);

tim_oc_cplm_pwm.OCMode = TIM_OCMODE_PWM1; /* PWM模式1 */
tim_oc_cplm_pwm.OCPolarity = TIM_OCPOLARITY_LOW; /* OCy 低电平有效 */
tim_oc_cplm_pwm.OCNPolarity = TIM_OCNPOLARITY_LOW; /* OCyN 低电平有效 */
tim_oc_cplm_pwm.OCIdleState = TIM_OCIDLESTATE_SET; /* 当MOE=0,OCx=1 */
tim_oc_cplm_pwm.OCNIdleState = TIM_OCNIDLESTATE_SET; /* 当MOE=0,OCxN=1 */
HAL_TIM_PWM_ConfigChannel(&g_timx_cplm_pwm_handle, &tim_oc_cplm_pwm, ATIM_TIMX_CPLM_CHY);

/* 设置死区参数,开启死区中断 */
g_sbreak_dead_time_config.OffStateRunMode = TIM_OSSR_DISABLE; /* 运行模式的关闭输出状态 */
g_sbreak_dead_time_config.OffStateIDLEMode = TIM_OSSI_DISABLE; /* 空闲模式的关闭输出状态 */
g_sbreak_dead_time_config.LockLevel = TIM_LOCKLEVEL_OFF; /* 不用寄存器锁功能 */
g_sbreak_dead_time_config.BreakState = TIM_BREAK_ENABLE; /* 使能刹车输入 */
g_sbreak_dead_time_config.BreakPolarity = TIM_BREAKPOLARITY_HIGH; /* 刹车输入有效信号极性为高 */
g_sbreak_dead_time_config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; /* 使能AOE位,允许刹车结束后自动恢复输出 */
HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle, &g_sbreak_dead_time_config);

HAL_TIM_PWM_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY); /* 使能OCy输出 */
HAL_TIMEx_PWMN_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY); /* 使能OCyN输出 */
}

其他函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @brief 定时器TIMX 设置输出比较值 & 死区时间
* @param ccr: 输出比较值
* @param dtg: 死区时间
* @arg dtg[7:5]=0xx时, 死区时间 = dtg[7:0] * tDTS
* @arg dtg[7:5]=10x时, 死区时间 = (64 + dtg[6:0]) * 2 * tDTS
* @arg dtg[7:5]=110时, 死区时间 = (32 + dtg[5:0]) * 8 * tDTS
* @arg dtg[7:5]=111时, 死区时间 = (32 + dtg[5:0]) * 16 * tDTS
* @note tDTS = 1 / (Ft / CKD[1:0]) = 1 / 18M = 55.56ns
* @retval 无
*/
void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg)
{
g_sbreak_dead_time_config.DeadTime = dtg; /* 死区时间设置 */
HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle, &g_sbreak_dead_time_config); /* 重设死区时间 */
__HAL_TIM_MOE_ENABLE(&g_timx_cplm_pwm_handle); /* MOE=1,使能主输出 */
ATIM_TIMX_CPLM_CHY_CCRY = ccr; /* 设置比较寄存器 */
}

主函数

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
//tim1_ch1(PA8),tim1_ch1n(PB13)输出互补信号,使用死区,死区时间大约55.56us
//tim1_bkin(pB12)刹车输入

int main(void)
{
uint8_t t = 0;

HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
// usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */

atim_timx_cplm_pwm_init(1000 - 1, 72 - 1); /* 1Mhz的计数频率 1Khz的周期. */
atim_timx_cplm_pwm_set(300, 100); /* 占空比:7:3, 死区时间 100 * tDTS,约55.56us */

while (1)
{
delay_ms(10);
t++;

if (t >= 20)
{
LED0_TOGGLE(); /* LED0(RED)闪烁 */
t = 0;
}
}
}

实验结果

PWM输入模式实验

工作原理

个人感觉边沿检测那边的信号这样走有些问题
下面是我猜测的实际信号路线

PWM输入模式时序

实验配置步骤

相关HAL库函数介绍

关键结构体介绍

编程实战

本次实验用tim2_ch1(A0)输出pwm信号,频率100khz,占空比40%
用tim1_ch1(PA8)检测pwm信号

宏定义

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
/* TIMX PWM输出定义 
* 这里输出的PWM控制LED0(RED)的亮度
* 默认是针对TIM2~TIM5
* 注意: 通过修改这几个宏定义,可以支持TIM1~TIM8任意一个定时器,任意一个IO口输出PWM
*/
#define GTIM_TIMX_PWM_CHY_GPIO_PORT GPIOA
#define GTIM_TIMX_PWM_CHY_GPIO_PIN GPIO_PIN_0
#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PB口时钟使能 */

/* TIMX REMAP设置
* 因为我们LED0接在PB5上, 必须通过开启TIM3的部分重映射功能, 才能将TIM3_CH2输出到PB5上
* 因此, 必须实现GTIM_TIMX_PWM_CHY_GPIO_REMAP
* 对那些使用默认设置的定时器PWM输出脚, 不用设置重映射, 是不需要该函数的!
*/
#define GTIM_TIMX_PWM_CHY_GPIO_REMAP() do{__HAL_RCC_AFIO_CLK_ENABLE();\
__HAL_AFIO_REMAP_TIM3_PARTIAL();\
}while(0) /* 通道REMAP设置, 该函数不是必须的, 根据需要实现 */

#define GTIM_TIMX_PWM TIM2
#define GTIM_TIMX_PWM_CHY TIM_CHANNEL_1 /* 通道Y, 1<= Y <=4 */
#define GTIM_TIMX_PWM_CHY_CCRX TIM2->CCR1 /* 通道Y的输出比较寄存器 */
#define GTIM_TIMX_PWM_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM2_CLK_ENABLE(); }while(0) /* TIM3 时钟使能 */

/* TIMX PWM输入模式 定义
* 这里的输入捕获使用定时器TIM1_CH1,捕获WK_UP按键的输入
* 默认是针对TIM1/TIM8等高级定时器
* 注意: 通过修改这几个宏定义,可以支持TIM1~TIM8任意一个定时器的通道1/通道2
*/
#define ATIM_TIMX_PWMIN_CHY_GPIO_PORT GPIOA
#define ATIM_TIMX_PWMIN_CHY_GPIO_PIN GPIO_PIN_8
#define ATIM_TIMX_PWMIN_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */

#define ATIM_TIMX_PWMIN TIM1
#define ATIM_TIMX_PWMIN_IRQn TIM1_UP_IRQn
#define ATIM_TIMX_PWMIN_IRQHandler TIM1_UP_IRQHandler
#define ATIM_TIMX_PWMIN_CHY TIM_CHANNEL_1 /* 通道Y, 1<= Y <=2*/
#define ATIM_TIMX_PWMIN_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0) /* TIM8 时钟使能 */

/* TIM1 / TIM8 有独立的捕获中断服务函数,需要单独定义,对于TIM2~5等,则不需要以下定义 */
#define ATIM_TIMX_PWMIN_CC_IRQn TIM1_CC_IRQn
#define ATIM_TIMX_PWMIN_CC_IRQHandler TIM1_CC_IRQHandler

全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//pwm输出定时器
TIM_HandleTypeDef g_timx_pwm_chy_handle; /* 定时器x句柄 */

//pwm输入定时器
TIM_HandleTypeDef g_timx_pwmin_chy_handle; /* 定时器x句柄 */

/* PWM输入状态(g_timxchy_cap_sta)
* 0,没有成功捕获.
* 1,已经成功捕获了
*/
uint8_t g_timxchy_pwmin_sta = 0; /* PWM输入状态 */
uint16_t g_timxchy_pwmin_psc = 0; /* PWM输入分频系数 */
uint32_t g_timxchy_pwmin_hval = 0; /* PWM的高电平脉宽 */
uint32_t g_timxchy_pwmin_cval = 0; /* PWM的周期宽度 */

初始化

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
/**
* @brief 通用定时器TIMX 通道Y PWM输出 初始化函数(使用PWM模式1)
* @note
* 通用定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
* 通用定时器的时钟为APB1时钟的2倍, 而APB1为36M, 所以定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
TIM_OC_InitTypeDef timx_oc_pwm_chy = {0}; /* 定时器PWM输出配置 */

g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM; /* 定时器x */
g_timx_pwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_pwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle); /* 初始化PWM */

timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM1 */
timx_oc_pwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比 */
/* 默认比较值为自动重装载值的一半,即占空比为50% */
timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为低 */
HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */
HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY); /* 开启对应PWM通道 */
}

/**
* @brief 定时器底层驱动,时钟使能,引脚配置
此函数会被HAL_TIM_PWM_Init()调用
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == GTIM_TIMX_PWM)
{
GPIO_InitTypeDef gpio_init_struct;
GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE(); /* 开启通道y的CPIO时钟 */
GTIM_TIMX_PWM_CHY_CLK_ENABLE();

gpio_init_struct.Pin = GTIM_TIMX_PWM_CHY_GPIO_PIN; /* 通道y的CPIO口 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推完输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, &gpio_init_struct);
// GTIM_TIMX_PWM_CHY_GPIO_REMAP(); /* IO口REMAP设置, 是否必要查看头文件配置的说明 */
}
}

/**
* @brief 定时器TIMX 通道Y PWM输入模式 初始化函数
* @note
* 高级定时器的时钟来自APB2, 而PCLK2 = 72Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* 本函数初始化的时候: 使用psc=0, arr固定为65535. 得到采样时钟频率为72Mhz,精度约13.8ns
*
* @param 无
* @retval 无
*/
void atim_timx_pwmin_chy_init(void)
{
GPIO_InitTypeDef gpio_init_struct = {0};
TIM_SlaveConfigTypeDef slave_config = {0};
TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};

ATIM_TIMX_PWMIN_CHY_CLK_ENABLE();
ATIM_TIMX_PWMIN_CHY_GPIO_CLK_ENABLE();
// __HAL_RCC_AFIO_CLK_ENABLE();
// AFIO_REMAP_PARTIAL(AFIO_EVCR_PORT_PC, AFIO_EVCR_PIN_PX6); /* 复用TIM8_CH1/PC6 */

gpio_init_struct.Pin = ATIM_TIMX_PWMIN_CHY_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
gpio_init_struct.Pull = GPIO_PULLDOWN;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH ;
HAL_GPIO_Init(ATIM_TIMX_PWMIN_CHY_GPIO_PORT, &gpio_init_struct);

g_timx_pwmin_chy_handle.Instance = ATIM_TIMX_PWMIN; /* 定时器8 */
g_timx_pwmin_chy_handle.Init.Prescaler = 0; /* 定时器预分频系数 */
g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_pwmin_chy_handle.Init.Period = 65535; /* 自动重装载值 */
HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);

/* 从模式配置,IT1触发更新 */
slave_config.SlaveMode = TIM_SLAVEMODE_RESET; /* 从模式:复位模式 */
slave_config.InputTrigger = TIM_TS_TI1FP1; /* 定时器输入触发源:TI1FP1 */
slave_config.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING; /* 上升沿检测 */
slave_config.TriggerFilter = 0; /* 不滤波 */
HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);

/* IC1捕获:上升沿触发TI1FP1 */
tim_ic_pwmin_chy.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; /* 上升沿检测 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 选择输入端IC1映射到TI1 */
tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1; /* 不分频 */
tim_ic_pwmin_chy.ICFilter = 0; /* 不滤波 */
HAL_TIM_IC_ConfigChannel( &g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1 );

/* IC2捕获:上升沿触发TI1FP2 */
tim_ic_pwmin_chy.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; /* 下降沿检测 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI; /* 选择输入端IC2映射到TI1 */
HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle,&tim_ic_pwmin_chy,TIM_CHANNEL_2);

HAL_NVIC_SetPriority(ATIM_TIMX_PWMIN_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_EnableIRQ( ATIM_TIMX_PWMIN_IRQn ); /* 开启TIMx中断 */

/* TIM1/TIM8有独立的输入捕获中断服务函数 */
if ( ATIM_TIMX_PWMIN == TIM1)
{
HAL_NVIC_SetPriority(ATIM_TIMX_PWMIN_CC_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_EnableIRQ(ATIM_TIMX_PWMIN_CC_IRQn); /* 开启TIMx中断 */
}

__HAL_TIM_ENABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_UPDATE);
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
}

中断服务函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @brief 定时器TIMX 更新/溢出 中断服务函数
* @note TIM1/TIM8的这个函数仅用于更新/溢出中断服务,捕获在另外一个函数!
* 其他普通定时器则更新/溢出/捕获,都在这个函数里面处理!
* @param 无
* @retval 无
*/
void ATIM_TIMX_PWMIN_IRQHandler(void)
{
atim_timx_pwmin_chy_process();
}

/**
* @brief 定时器TIMX 输入捕获 中断服务函数
* @note 仅TIM1/TIM8有这个函数,其他普通定时器没有这个中断服务函数!
* @param 无
* @retval 无
*/
void ATIM_TIMX_PWMIN_CC_IRQHandler(void)
{
atim_timx_pwmin_chy_process();
}

其他函数

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
/**
* @brief 定时器TIMX 通道Y PWM输入模式 中断处理函数
* @note
* 因为TIM1/TIM8等有多个中断服务函数,而TIM2~5/TIM12/TIM15等普通定时器只有1个中断服务
* 函数,为了更好的兼容,我们对中断处理统一放到atim_timx_pwin_chy_process函数里面进行处理
*
* @param 无
* @retval 无
*/
static void atim_timx_pwmin_chy_process(void)
{
static uint8_t sflag = 0; /* 启动PWMIN输入检测标志 */

if (g_timxchy_pwmin_sta)
{
g_timxchy_pwmin_psc = 0;
ATIM_TIMX_PWMIN->SR = 0; /* 清除所有中断标志位 */
__HAL_TIM_SET_COUNTER(&g_timx_pwmin_chy_handle, 0); /* 计数器清零 */
return ;
}

if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_chy_handle, TIM_FLAG_UPDATE)) /* 发生了溢出中断 */
{
__HAL_TIM_CLEAR_FLAG(&g_timx_pwmin_chy_handle, TIM_FLAG_UPDATE); /* 清除溢出中断标记 */

if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_chy_handle, TIM_FLAG_CC1) == 0) /* 没有发生周期捕获中断,且捕获未完成 */
{
sflag = 0;
if (g_timxchy_pwmin_psc == 0) /* 从0 到 1 */
{
g_timxchy_pwmin_psc ++;
}
else
{
if (g_timxchy_pwmin_psc == 65535) /* 已经最大了,可能是无输入状态 */
{
g_timxchy_pwmin_psc = 0; /* 重新恢复不分频 */
}
else if (g_timxchy_pwmin_psc > 32767) /* 不能倍增了 */
{
g_timxchy_pwmin_psc = 65535; /* 直接等于最大分频系数 */
}
else
{
g_timxchy_pwmin_psc += g_timxchy_pwmin_psc; /* 倍增 */
}
}

__HAL_TIM_SET_PRESCALER(&g_timx_pwmin_chy_handle, g_timxchy_pwmin_psc); /* 设置定时器预分频系数 */
__HAL_TIM_SET_COUNTER(&g_timx_pwmin_chy_handle, 0); /* 计数器清零 */
ATIM_TIMX_PWMIN->SR = 0; /* 清除所有中断标志位 */
return ;
}
}

if (sflag == 0) /* 第一次采集到捕获中断 */
{
if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_chy_handle, TIM_FLAG_CC1)) /* 检测到了第一次周期捕获中断 */
{
sflag = 1; /* 标记第一次周期已经捕获, 第二次周期捕获可以开始了 */
}

ATIM_TIMX_PWMIN->SR = 0; /* 清除所有中断标志位 */
return ; /* 完成此次操作 */
}

if (g_timxchy_pwmin_sta == 0) /* 还没有成功捕获 */
{
if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_chy_handle, TIM_FLAG_CC1)) /* 检测到了周期捕获中断 */
{
g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1; /* 高定平脉宽捕获值 */
g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1; /* 周期捕获值 */

if (g_timxchy_pwmin_hval < g_timxchy_pwmin_cval) /* 高电平脉宽必定小于周期长度 */
{
g_timxchy_pwmin_sta = 1; /* 标记捕获成功 */

g_timxchy_pwmin_psc = ATIM_TIMX_PWMIN->PSC; /* 获取PWM输入分频系数 */

if (g_timxchy_pwmin_psc == 0) /* 分频系数为0的时候, 修正读取数据 */
{
g_timxchy_pwmin_hval++; /* 修正系数为1, 加1 */
g_timxchy_pwmin_cval++; /* 修正系数为1, 加1 */
}

sflag = 0;
/* 每次捕获PWM输入成功后, 停止捕获,避免频繁中断影响系统正常代码运行 */
ATIM_TIMX_PWMIN->CR1 &= ~(1 << 0); /* 关闭定时器TIMX */
__HAL_TIM_DISABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_CC1); /* 关闭通道1捕获中断 */
__HAL_TIM_DISABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_CC2); /* 关闭通道2捕获中断 */
__HAL_TIM_DISABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_UPDATE); /* 关闭溢出中断 */

ATIM_TIMX_PWMIN->SR = 0; /* 清除所有中断标志位 */
}
else
{
atim_timx_pwmin_chy_restart();
}
}
}

ATIM_TIMX_PWMIN->SR = 0; /* 清除所有中断标志位 */
}

主函数

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
extern uint16_t g_timxchy_pwmin_psc;    /* PWM输入状态 */
extern uint16_t g_timxchy_pwmin_sta; /* PWM输入状态 */
extern uint32_t g_timxchy_pwmin_hval; /* PWM的高电平脉宽 */
extern uint32_t g_timxchy_pwmin_cval; /* PWM的周期宽度 */

int main(void)
{
uint8_t t = 0;
double ht, ct, f, tpsc;

HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */

gtim_timx_pwm_chy_init(10 - 1, 72 - 1);
GTIM_TIMX_PWM_CHY_CCRX = 4;

atim_timx_pwmin_chy_init();

printf("Start:\n");

while (1)
{
delay_ms(10);
t++;

if (t >= 20) /* 每200ms输出一次结果,并闪烁LED0,提示程序运行 */
{
if (g_timxchy_pwmin_sta) /* 捕获了一次数据 */
{
printf("\r\n"); /* 输出空,另起一行 */
printf("PWM PSC :%d\r\n", g_timxchy_pwmin_psc); /* 打印分频系数 */
printf("PWM Hight:%d\r\n", g_timxchy_pwmin_hval); /* 打印高电平脉宽 */
printf("PWM Cycle:%d\r\n", g_timxchy_pwmin_cval); /* 打印周期 */
tpsc = ((double)g_timxchy_pwmin_psc + 1) / 72; /* 得到PWM采样时钟周期时间 */
ht = g_timxchy_pwmin_hval * tpsc; /* 计算高电平时间 */
ct = g_timxchy_pwmin_cval * tpsc; /* 计算周期长度 */
f = (1 / ct) * 1000000; /* 计算频率 */
printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高电平脉宽长度 */
printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期时间长度 */
printf("PWM Frequency :%.3fHz\r\n", f); /* 打印频率 */
atim_timx_pwmin_chy_restart(); /* 重启PWM输入检测 */
}

LED0_TOGGLE(); /* LED1(GREEN)闪烁 */
t = 0;
}
}
}

实验现象