STM32——TIMER(定时器)篇

技术笔记!

1.   定时器概述(了解)

1.1  软件定时器原理

使用纯软件(CPU死等)的方式实现定时(延时)功能

缺点:1. 延时不准确        2. CPU死等。      

1.2  定时器定时原理

1.3  STM32定时器分类

1.4  STM32定时器特性表(F1为例)

可参考芯片数据手册或者各个开发板对应的开发指南

1.5  STM32定时器之间的区别

2.  基本定时器(掌握)

基础定时器 通用定时器 没有输入输出通道,常用作时基,即定时功能

2.1  基本定时器简介(了解)

主要特性:

16位递增计数器(计数值:0~65535)

16位预分频器(分频系数:1~65536)

可用于触发DAC

在更新事件(计数器溢出)时,会产生中断/DMA请求

2.2  基本定时器框图(熟悉)

2.3  定时器计数模式及溢出条件(熟悉)

2.4  定时器中断实验相关寄存器(了解)F1为例

1.  TIM6和TIM7控制寄存器1(TIMx_CR1)

2.  TIM6 和TIM7 DMA/中断使能寄存器(TIMx_DIER)

3.  TIM6 和TIM7 状态寄存器(TIMx_SR)

4.  TIM6 和TIM7 计数器(TIMx_CNT)

5.   TIM6 和TIM7 预分频器(TIMx_PSC)

6.  TIM6 和TIM7 自动重装载寄存器(TIMx_ARR)

2.5  定时器溢出时间计算方法(掌握)

2.6  定时器中断实验配置步骤(掌握)

2.7  基础定时器实验

使用定时器6,实现500ms定时器更新中断,在中断里翻转LED0

btim.c

#include "./BSP/LED/led.h"
#include "btim.h"TIM_HandleTypeDef g_timx_handle;/* 定时器中断初始化函数 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{g_timx_handle.Instance = TIM6;              //基地址g_timx_handle.Init.Prescaler = psc;         //预分频系数g_timx_handle.Init.Period = arr;            //预加载值HAL_TIM_Base_Init(&g_timx_handle);            //基础时钟初始化函数HAL_TIM_Base_Start_IT(&g_timx_handle);      //使能更新中断并启动计数器}/* 定时器基础MSP初始化函数 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM6){__HAL_RCC_TIM6_CLK_ENABLE();HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3);HAL_NVIC_EnableIRQ(TIM6_IRQn);}
}/* 定时器6中断服务函数 */
void TIM6_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_timx_handle);
}/* 定时器溢出中断中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM6){LED0_TOGGLE();}
}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BTIM/btim.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */led_init();                         /* LED初始化 */btim_timx_int_init(5000-1, 7200 -1);/* 10Khz的计数频率,计数5K次为500ms */while(1){ delay_ms(500);}
}

实验小结:

        首先通过自定义的中断初始化函数对基础定时器进行初始化,配置定时器句柄结构体的相关成员变量赋值,通过基础时钟初始化函数对基础时钟初始化,通过基础时钟中断并启动计数器函数进行使能该基础时钟,再通过定时器基础msp初始化函数对时钟和中断进行使能及配置,最后通过对应定时器中断服务函数进行中断处理。

3.  通用计时器(掌握)

3.1  通用计时器简介(了解)

主要特性:

16位递增、递减、中心对齐计数器(计数值:0~65535)

16位预分频器(分频系数:1~65536)

可用于触发DAC、ADC 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求

4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式 使用外部信号控制定时器且可实现多个定时器互连的同步电路 支持编码器和霍尔传感器电路等

3.2  通用定时器框图(熟悉)

3.3  计数器时钟源(掌握)

3.4  通用定时器PWM输出实验(掌握)

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微 处理器的数字输出来对模拟电路进行控制的一种非常有效的技术

3.4.1  通用定时器输出比较部分框图介绍(熟悉)

3.4.2  通用定时器输出PWM原理(掌握)

        让定时器产生PWM, 在计数器频率固定时,PWM 频率或者周期由自动重载寄存(TIMx_ARR)的值决定,其占空 比由捕获/比较寄存器(TIMx_CCRx)的值决定。

        当 CNT=CCRx 时,IO 输出高电平(逻辑 1);当 CNT=ARR 时,定时器溢出,CNT 的值被清零,然后继续递增,依次循环。

        在这个循环中,改 变 CCRx 的值,就可以改变 PWM 的占空比,改变 ARR 的值,就可以改变 PWM 的频率,这就 是 PWM 输出的原理。

3.4.3  PWM模式(熟悉)

3.4.4  通用定时器PWM输出实验配置步骤(掌握)

相关寄存器讲解:

1.  捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

        TIM2/TIM3/TIM4/TIM5 的捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有 2 个TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3 和 CH4。TIMx_CCMR1 寄存器描述如图 21.3.1.1 所示:

2.  捕获/比较使能寄存器(TIMx_CCER)

        TIM2/TIM3/TIM4/TIM5 的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开 关和极性。TIMx_CCER 寄存器描述如图 21.3.1.2 所示:

3.  捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)

        捕获/比较寄存器(TIMx_CCR1/2/3/4),该寄存器总共有 4 个,对应 4 个通道 CH1~CH4。 我们使用的是通道 2,所以来看看 TIMx_CCR2 寄存器,描述如图 21.3.1.3 所示

3.4.5  通用定时器PWM输出实验(掌握)

pwm.c

#include "./BSP/LED/led.h"
#include "./BSP/PWM/pwm.h"TIM_HandleTypeDef g_timx_pwm_chy_handle;
TIM_OC_InitTypeDef g_timx_oc_pwm_chy_handle;void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{g_timx_pwm_chy_handle.Instance = TIM3 ;                     /* 定时器 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 */g_timx_oc_pwm_chy_handle.OCMode = TIM_OCMODE_PWM1;          /* 模式选择 PWM1 *//* 设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为 50% */g_timx_oc_pwm_chy_handle.Pulse = arr/2;g_timx_oc_pwm_chy_handle.OCPolarity = TIM_OCNPOLARITY_LOW;  /* 输出比较极性为低 */HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &g_timx_oc_pwm_chy_handle, TIM_CHANNEL_2);/* 配置 TIMx 通道 y */HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2);               /*开启 PWM 通道*/}void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOB_CLK_ENABLE();                   /* 开启通道 y 的 CPIO 时钟 */__HAL_RCC_TIM3_CLK_ENABLE();gpio_init_struct.Pin = GPIO_PIN_5;              /* 通道 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(GPIOB, &gpio_init_struct);GTIM_TIMX_PWM_CHY_GPIO_REMAP();                 /* IO 口 REMAP 设置,设置重映射 */}
}

main.c

int main(void)
{uint16_t ledrpwmval = 0;uint8_t dir = 1;HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */led_init();                         /* LED初始化 *//* 72M/72=1M 的计数频率,自动重装载为 500,那么 PWM 频率为 1M/500=2kHZ */gtim_timx_pwm_chy_init(500 - 1, 72 - 1);while(1){ delay_ms(10);if(dir)ledrpwmval++;else ledrpwmval--;if(ledrpwmval > 300)dir = 0;if(ledrpwmval == 0) dir = 1;__HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, TIM_CHANNEL_2,ledrpwmval);}
}
3.5  通用定时器输入捕获实验(掌握)
3.5.1  通用定时器输入捕获部分框图介绍(熟悉)

3.5.2  通用定时器输入捕获脉宽测量原理(掌握)

3.5.3  通用定时器输入捕获实验配置步骤(掌握)

3.5.4  通用定时器输入捕获实验(掌握)

tim.c
#include "./BSP/LED/led.h"
#include "tim.h"TIM_HandleTypeDef g_timx_cap_chy_handle;        /* 定时器x句柄 */
/* 通用定时器通道y 输入捕获 初始化函数 */
void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc)
{TIM_IC_InitTypeDef timx_ic_cap_chy = {0};g_timx_cap_chy_handle.Instance  = TIM5;                         /* 定时器5 */g_timx_cap_chy_handle.Init.Prescaler = psc;                     /* 定时器分频 */g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;    /* 递增计数模式 */g_timx_cap_chy_handle.Init.Period = arr;                        /* 自动重装载值 */HAL_TIM_IC_Init(&g_timx_cap_chy_handle);timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;              /* 上升沿捕获 */timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;          /* 映射到TI1上 */timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;                    /* 配置输入分频,不分频 */timx_ic_cap_chy.ICFilter = 0;                                    /* 配置输入滤波器,不滤波 */HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TIM_CHANNEL_1);  /* 配置TIM5通道1 */__HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE);     /* 使能更新中断 */HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, TIM_CHANNEL_1);     /* 开始捕获TIM5的通道1 */
}/* 定时器 输入捕获 MSP初始化函数 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM5)                              /*输入通道捕获*/{                                                       GPIO_InitTypeDef gpio_init_struct;                  __HAL_RCC_TIM5_CLK_ENABLE();                        /* 使能TIM5时钟 */__HAL_RCC_GPIOA_CLK_ENABLE();                       /* 开启捕获IO的时钟 */gpio_init_struct.Pin = GPIO_PIN_0;                  gpio_init_struct.Mode = GPIO_MODE_AF_PP;            /* 复用推挽输出 */gpio_init_struct.Pull = GPIO_PULLDOWN;              /* 下拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */HAL_GPIO_Init(GPIOA, &gpio_init_struct);HAL_NVIC_SetPriority(TIM5_IRQn, 1, 3);              /* 抢占1,子优先级3 */HAL_NVIC_EnableIRQ(TIM5_IRQn);                      /* 开启ITMx中断 */}
}/* 输入捕获状态(g_timxchy_cap_sta)* [7]  :0,没有成功的捕获;1,成功捕获到一次.* [6]  :0,还没捕获到高电平;1,已经捕获到高电平了.* [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303*       注意:为了通用,我们默认ARR和CCRy都是16位寄存器,对于32位的定时器(如:TIM5),也只按16位使用*       按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒**      (说明一下:正常32位定时器来说,1us计数器加1,溢出时间:4294秒)*/uint8_t g_timxchy_cap_sta = 0;              /* 输入捕获状态 */
uint16_t g_timxchy_cap_val = 0;             /* 输入捕获值 *//* 定时器5中断服务函数 */
void TIM5_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_timx_cap_chy_handle);  /* 定时器HAL库共用处理函数 */
}/* 定时器输入捕获中断处理回调函数 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM5){if ((g_timxchy_cap_sta & 0X80) == 0)                /* 还没有成功捕获 */{                                                   if(g_timxchy_cap_sta & 0X40)                    /* 捕获到一个下降沿 */{                                               g_timxchy_cap_sta |= 0X80;                  /* 标记成功捕获到一次高电平脉宽 */g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&g_timx_cap_chy_handle,TIM_CHANNEL_1);        /* 获取当前的捕获值 */TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1);                            /* 一定要先清除原来的设置 */TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);      /* 配置TIM5通道1上升沿捕获 */__HAL_TIM_ENABLE(&g_timx_cap_chy_handle);           /* 使能定时器5 */}}}
}/* 定时器更新中断回调函数 */
void HAL_TIM_PeriorElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM5){if((g_timxchy_cap_sta & 0X80) == 0)                 /* 还未成功捕获 */{                                                   if(g_timxchy_cap_sta & 0X40)                    /* 已经捕获到高电平了 */{                                               if((g_timxchy_cap_sta & 0X3F) == 0X3F)      /* 高电平太长了 */{TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1);                        /* 一定要先清除原来的设置 */TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);  /* 配置TIM5通道1上升沿捕获 */g_timxchy_cap_sta |= 0x80;          /* 标记成功捕获了一次 */g_timxchy_cap_val = 0XFFFF;}else      /* 累计定时器溢出次数 */{g_timxchy_cap_sta++;}}}}
}

main.c

extern uint8_t  g_timxchy_cap_sta;  /* 输入捕获状态 */
extern uint16_t g_timxchy_cap_val;  /* 输入捕获值 */int main(void)
{uint32_t temp = 0;uint8_t t = 0;HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */led_init();                         /* LED初始化 */gtim_timx_cap_chy_init(0XFFFF, 72 - 1);         /* 以1Mhz的频率计数 捕获 */while(1){ if (g_timxchy_cap_sta & 0X80)       /* 成功捕获到了一次高电平 */{temp = g_timxchy_cap_sta & 0X3F;temp *= 65536;                  /* 溢出时间总和 */temp += g_timxchy_cap_val;      /* 得到总的高电平时间 */printf("HIGH:%d us\r\n", temp); /* 打印总的高点平时间 */g_timxchy_cap_sta = 0;          /* 开启下一次捕获*/}t++;if (t > 20)         /* 200ms进入一次 */{t = 0;LED0_TOGGLE();  /* LED0闪烁 ,提示程序运行 */}delay_ms(10);}
}
3.6  通用定时器脉冲计数实验(掌握)
3.6.1  脉冲计数实验原理(熟悉)

3.6.2  通用定时器脉冲计数实验配置步骤(掌握)

3.6.3  通用定时器脉冲计数实验(掌握)

tim.c

#include "./BSP/LED/led.h"
#include "tim.h"TIM_HandleTypeDef g_timx_cnt_chy_handle;        /* 定时器x句柄 *//* 通用定时器通道y 脉冲计数 初始化函数 */
void gtim_timx_cnt_chy_init(uint16_t psc)
{TIM_SlaveConfigTypeDef tim_salve_config = {0};g_timx_cnt_chy_handle.Instance = TIM2;g_timx_cnt_chy_handle.Init.Prescaler = psc;g_timx_cnt_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;g_timx_cnt_chy_handle.Init.Period = 65535;HAL_TIM_IC_Init(&g_timx_cnt_chy_handle);tim_salve_config.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;tim_salve_config.InputTrigger = TIM_TS_TI1F_ED;tim_salve_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;tim_salve_config.TriggerFilter = 0;HAL_TIM_SlaveConfigSynchro(&g_timx_cnt_chy_handle, &tim_salve_config);HAL_TIM_IC_Start(&g_timx_cnt_chy_handle, TIM_CHANNEL_1);
}/* 定时器 输入捕获 MSP初始化函数 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_TIM2_CLK_ENABLE();gpio_init_struct.Pin = GPIO_PIN_0;gpio_init_struct.Mode = GPIO_MODE_AF_PP;        /* 推挽式复用功能 */gpio_init_struct.Pull = GPIO_PULLDOWN;          /* 下拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;  /* 高速 */HAL_GPIO_Init(GPIOA, &gpio_init_struct);}}

key.c

#include "key.h"
#include "./SYSTEM/delay/delay.h"/*按键初始化函数*/void key_init(void)
{GPIO_InitTypeDef gpio_init_struct;KEY0_GPIO_CLK_ENABLE();                                     /* KEY0时钟使能 */KEY1_GPIO_CLK_ENABLE();                                     /* KEY1时钟使能 */WKUP_GPIO_CLK_ENABLE();                                     /* WKUP时钟使能 */gpio_init_struct.Pin = KEY0_GPIO_PIN;                       /* KEY0引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct);           /* KEY0引脚模式设置,上拉输入 */gpio_init_struct.Pin = KEY1_GPIO_PIN;                       /* KEY1引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct);           /* KEY1引脚模式设置,上拉输入 */gpio_init_struct.Pin = WKUP_GPIO_PIN;                       /* WKUP引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */gpio_init_struct.Pull = GPIO_PULLDOWN;                      /* 下拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct);           /* WKUP引脚模式设置,下拉输入 */}/*** @brief       按键扫描函数* @note        该函数有响应优先级(同时按下多个按键): WK_UP > KEY1 > KEY0!!* @param       mode:0 / 1, 具体含义如下:*   @arg       0,  不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,*                  必须松开以后, 再次按下才会返回其他键值)*   @arg       1,  支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)* @retval      键值, 定义如下:*              KEY0_PRES, 1, KEY0按下*              KEY1_PRES, 2, KEY1按下*              WKUP_PRES, 4, WKUP按下*/
uint8_t key_scan(uint8_t mode)
{static uint8_t key_up = 1;  /* 按键按松开标志 */uint8_t keyval = 0;if (mode) key_up = 1;       /* 支持连按 */if (key_up && (KEY0 == 0 || KEY1 == 0 || WK_UP == 1))  /* 按键松开标志为1, 且有任意一个按键按下了 */{delay_ms(10);           /* 去抖动 */key_up = 0;if (KEY0 == 0)  keyval = KEY0_PRES;if (KEY1 == 0)  keyval = KEY1_PRES;if (WK_UP == 1) keyval = WKUP_PRES;}else if (KEY0 == 1 && KEY1 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */{key_up = 1;}return keyval;              /* 返回键值 */
}

main.c

extern TIM_HandleTypeDef g_timx_cnt_chy_handle;        /* 定时器x句柄 */int main(void)
{uint16_t curcnt;uint16_t oldcnt;uint8_t key;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();                                 /* 初始化按键 */gtim_timx_cnt_chy_init(0);while(1){ key = key_scan(0);if(key == KEY0_PRES){__HAL_TIM_SET_COUNTER(&g_timx_cnt_chy_handle, 0);}curcnt = __HAL_TIM_GET_COUNTER(&g_timx_cnt_chy_handle);if(oldcnt != curcnt){oldcnt = curcnt;printf("CNT:%d\r\n", oldcnt);}t++;if(t > 20){t = 0;LED0_TOGGLE();}delay_ms(10);}
}

4.  高级定时器 (掌握)

4.1  高级定时器简介(了解)

主要特性:

16位递增、递减、中心对齐计数器(计数值:0~65535)
16位预分频器(分频系数:1~65536)
可用于触发DAC、ADC
在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
使用外部信号控制定时器且可实现多个定时器互连的同步电路
支持编码器和霍尔传感器电路等
重复计数器
死区时间带可编程的互补输出
断路输入,用于将定时器的输出信号置于用户可选的安全配置中

4.2  高级定时器框图(熟悉)

4.3  高级定时器输出指定个数PWM实验(掌握)
4.3.1  重复计数器特性(熟悉)

4.3.2  高级定时器输出指定个数PWM实验原理(掌握)

4.3.3  高级定时器输出指定个数PWM实验配置步骤(掌握)

相关寄存器:

1.  控制寄存器 1(TIMx_CR1)

2.  捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

3.  捕获/比较使能寄存器(TIMx_ CCER)

4.  事件产生寄存器(TIMx_ EGR)

5.  重复计数器寄存器(TIMx_ RCR)

重复计数器寄存器用于设置重复计数器值,因为它具有影子寄存器,所以它本身只是起缓 冲作用。

6.  断路和死区寄存器(TIMx_ BDTR)

7.  捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)

4.3.4  实战

gtim.c

#include "./BSP/LED/led.h"
#include "tim.h"TIM_HandleTypeDef g_timx_npwm_chy_handle;     /* 定时器x句柄 */static uint8_t g_npwm_remain = 0;/* 高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数 */void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc){TIM_OC_InitTypeDef timx_oc_npwm_chy = {0};g_timx_npwm_chy_handle.Instance = TIM8;                            /* 定时器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.RepetitionCounter = 0;                 /* 重复计数器初始值 */HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle);                         /* 初始化PWM */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, TIM_CHANNEL_1);__HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, TIM_CHANNEL_1);}/* 定时器 PWM输出 MSP初始化函数 */void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM8){GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_TIM8_CLK_ENABLE();gpio_init_struct.Pin = GPIO_PIN_6;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(GPIOC, &gpio_init_struct);HAL_NVIC_SetPriority(TIM8_UP_IRQn, 1, 3);HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);}
}/* 高级定时器TIMX NPWM设置PWM个数函数 */
void atim_timx_npwm_chy_set(uint8_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);
}/* 定时器8中断服务函数 */
void TIM8_UP_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_timx_npwm_chy_handle);
}/* 定时器更新中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM8){if(g_npwm_remain){TIM8->RCR = g_npwm_remain - 1;HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);g_npwm_remain = 0;}else{TIM8->CR1 &= ~(1 << 0);}}
}

main.c

extern TIM_HandleTypeDef g_timx_cnt_chy_handle;        /* 定时器x句柄 */int main(void)
{uint8_t key;uint8_t t = 0;GPIO_InitTypeDef gpio_init_struct;HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */usart_init(115200);                         /* 串口初始化为115200 */led_init();                         /* LED初始化 */key_init();                                 /* 初始化按键 *//* 把PE5设置为输入,避免与PC6冲突 */__HAL_RCC_GPIOE_CLK_ENABLE();gpio_init_struct.Pin = GPIO_PIN_5;gpio_init_struct.Mode = GPIO_MODE_INPUT;                /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */HAL_GPIO_Init(GPIOE, &gpio_init_struct);atim_timx_npwm_chy_init(5000 - 1, 7200 - 1);atim_timx_npwm_chy_set(5);while (1){key = key_scan(0);if(key == KEY0_PRES){atim_timx_npwm_chy_set(6);}t++;delay_ms(10);if (t > 50)                    /* 控制LED0闪烁, 提示程序运行状态 */{t = 0;LED0_TOGGLE();}}
}
4.4  高级定时器输出比较模式实验(掌握)
4.4.1  高级定时器输出比较模式实验原理(掌握)

总结:PWM波周期或频率由ARR决定,占空比固定50%,相位由CCRx决定

4.4.2  高级定时器输出比较模式实验配置步骤(掌握)

4.4.3  ,编程实战:高级定时器输出比较模式实验(掌握)

tim.c

#include "./BSP/LED/led.h"
#include "tim.h"/**
* @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 无
*/
TIM_HandleTypeDef g_timx_comp_pwm_handle;void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
{TIM_OC_InitTypeDef timx_oc_comp_pwm = {0};TIM_OC_InitTypeDef tim_oc_handle = {0};g_timx_comp_pwm_handle.Instance = ATIM_TIMX_COMP; /* 定时器 x */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 *//* CCR1 寄存器预装载使能 */__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);tim_oc_handle.Pulse = 500;HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle, \TIM_CHANNEL_2); /* 初始化定时器的输出比较通道 2 *//* CCR2 寄存器预装载使能 */__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);tim_oc_handle.Pulse = 750;HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,TIM_CHANNEL_3); /* 初始化定时器的输出比较通道 3 *//* CCR3 寄存器预装载使能 */__HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);tim_oc_handle.Pulse = 1000;HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,TIM_CHANNEL_4); /* 初始化定时器的输出比较通道 4 *//* CCR4 寄存器预装载使能 */ __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_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);}}

mian.c

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初始化 */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;}}}
4.5  高级定时器互补输出带死区控制实验(掌握)
4.5.1  互补输出,还带死区控制,什么意思?(了解)

上图中,CH1 输出黄色的 PWM,它的互补通道 CH1N 输出绿色的 PWM。通过对比,可以 知道这两个 PWM 刚好是反过来的,CH1 的 PWM 为高电平期间,CH1N 的 PWM 则是低电平, 反之亦然,这就是互补输出。

4.5.2  带死区控制的互补输出应用之H桥(了解)

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

4.5.4  死区时间计算(掌握)

4.5.5  刹车(断路)功能(熟悉)

4.5.6  高级定时器互补输出带死区控制实验配置步骤(掌握)

4.5.7  编程实战:高级定时器互补输出带死区控制实验(掌握)

tim.c

#include "./BSP/LED/led.h"
#include "tim.h"/* @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 无
*/TIM_OC_InitTypeDef tim_oc_cplm_pwm;
TIM_HandleTypeDef g_timx_cplm_pwm_handle;
TIM_BreakDeadTimeConfigTypeDef g_sbreak_dead_time_config;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);/* 重映射定时器 IO */__HAL_RCC_AFIO_CLK_ENABLE();__HAL_AFIO_REMAP_TIM1_ENABLE();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; /* 自动重装载值 *//* CKD[1:0] = 10, tDTS = 4 * tCK_INT = Ft / 4 = 18Mhz */g_timx_cplm_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;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; /* 使能 AOE 位,允许刹车结束后自动恢复输出 */g_sbreak_dead_time_config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle,&g_sbreak_dead_time_config);/* 使能 OCy 输出 */HAL_TIM_PWM_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY);/* 使能 OCyN 输出 */HAL_TIMEx_PWMN_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY);
}/**
* @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; /* 设置比较寄存器 */
}

tim.h

#ifndef __TIM_H__
#define __TIM_H__#include "./SYSTEM/sys/sys.h"
#include "stm32f1xx_hal_gpio_ex.h"/* TIMX 互补输出模式 定义
* 这里设置互补输出相关硬件配置, CHY 即正常输出, CHYN 即互补输出
* 修改 CCRx 可以修改占空比.
* 默认是针对 TIM1
* 注意: 通过修改这些宏定义,可以支持 TIM1/TIM8 定时器, 任意一个 IO 口输出互补 PWM(前提是必须有
互补输出功能)
*/
/* 输出通道引脚 */
#define ATIM_TIMX_CPLM_CHY_GPIO_PORT GPIOE
#define ATIM_TIMX_CPLM_CHY_GPIO_PIN GPIO_PIN_9
#define ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); \
}while(0) /* PE 口时钟使能 */
/* 互补输出通道引脚 */
#define ATIM_TIMX_CPLM_CHYN_GPIO_PORT GPIOE
#define ATIM_TIMX_CPLM_CHYN_GPIO_PIN GPIO_PIN_8
#define ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE();\
}while(0) /* PE 口时钟使能 */
/* 刹车输入引脚 */
#define ATIM_TIMX_CPLM_BKIN_GPIO_PORT GPIOE
#define ATIM_TIMX_CPLM_BKIN_GPIO_PIN GPIO_PIN_15
#define ATIM_TIMX_CPLM_BKIN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE();\
}while(0) /* PE 口时钟使能 *//* 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 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) void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg);
void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc);#endif

main.c

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初始化 */atim_timx_cplm_pwm_init(1000 - 1, 72 - 1); /* 1Mhz 的计数频率 1Khz 的周期. */atim_timx_cplm_pwm_set(300, 100); /* 占空比:7:3, 死区时间 100*tDTS */while (1){delay_ms(10);t++;if (t >= 20){LED0_TOGGLE(); /* LED0(RED)闪烁 */t = 0;}}
}
4.6  高级定时器PWM输入模式实验(掌握)
4.6.1  PWM输入模式工作原理(熟悉)

4.6.2  PWM输入模式时序(熟悉)

4.6.3  高级定时器PWM输入模式实验配置步骤(掌握)

4.6.4  编程实战:高级定时器PWM输入模式实验(掌握)

tim.c

#include "./BSP/TIMER/atim.h"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的周期宽度 *//* PWM输入模式 初始化函数,采样时钟频率为72Mhz,精度约13.8ns */
void atim_timx_pwmin_chy_init(void)
{TIM_SlaveConfigTypeDef slave_config = {0};TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};g_timx_pwmin_chy_handle.Instance = TIM8;                        /* 定时器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_TRIGGERPOLARITY_RISING;      /* 上升沿检测 */slave_config.TriggerFilter = 0;                                 /* 不滤波 */HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);/* IC1捕获:上升沿触发TI1FP1 */tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_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_ICPOLARITY_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_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);HAL_TIM_IC_Start(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
}/* 定时器 输入捕获 MSP初始化函数 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM8){GPIO_InitTypeDef gpio_init_struct = {0};__HAL_RCC_TIM8_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();gpio_init_struct.Pin = GPIO_PIN_6;gpio_init_struct.Mode = GPIO_MODE_AF_PP;gpio_init_struct.Pull = GPIO_PULLDOWN;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOC, &gpio_init_struct);/* TIM1/TIM8有独立的输入捕获中断服务函数 */HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);}
}/* 定时器8 输入捕获 中断服务函数,仅TIM1/TIM8有这个函数,其他普通定时器没有这个中断服务函数! */
void TIM8_CC_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_timx_pwmin_chy_handle); /* 定时器共用处理函数 */
}/* PWM输入模式 重新启动捕获 */
void atim_timx_pwmin_chy_restart(void)
{sys_intx_disable();                     /* 关闭中断 */g_timxchy_pwmin_sta = 0;                /* 清零状态,重新开始检测 */g_timxchy_pwmin_hval=0;g_timxchy_pwmin_cval=0;sys_intx_enable();                      /* 打开中断 */
}/* 定时器输入捕获中断处理回调函数 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM8){if(g_timxchy_pwmin_sta == 0){if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1 + 1;g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1 + 1;g_timxchy_pwmin_sta = 1;}}}
}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/atim.h"
#include "./BSP/TIMER/gtim.h"extern uint16_t g_timxchy_pwmin_sta;    /* PWM输入状态 */
extern uint16_t g_timxchy_pwmin_psc;    /* 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 */gtim_timx_pwm_chy_init(10 - 1, 72 - 1);TIM3->CCR2 = 3;atim_timx_pwmin_chy_init();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输入检测 */} LED1_TOGGLE();  /* LED1(GREEN)闪烁 */t = 0;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/11520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

品牌舆情都包含什么内容?建议收藏

一个品牌的声誉、形象、产品质量、服务质量等&#xff0c;无时无刻不在接受着大众的检验。互联网传播迅速&#xff0c;一个不好的舆论直接导致整个品牌的声誉受到严重影响。品牌舆情都包含什么内容&#xff1f;接下来伯乐网络传媒就来给大家讲一讲。 一、品牌舆情的基本构成 1…

冯喜运:5.13黄金晚间还会跌吗?原油还会涨吗?

【黄金消息面分析】&#xff1a;自5月初以来&#xff0c;黄金和白银一直在享受需求的回归&#xff0c;买家在过去几天加大了力度&#xff0c;一度推动金价重返2370美元上方&#xff0c;白银重返28.5美元上方。不过&#xff0c;经过几天的盘整后&#xff0c;黄金白银价格双双下跌…

【npm】解决npm包突然消失MODULE_NOT_FOUND

今天折腾新特性时需要升级nodejs&#xff0c;安装新版后npm离奇消失了。C:\Users\**用户名\AppData\Roaming\npm\node_modules下只有cnpm&#xff0c;没有npm的目录。重装nodejs也不好使。 机智如我&#xff0c;试了下cnpm -v是正常的&#xff0c;而且能看到nodejs&#xff0c;…

什么是.faust勒索病毒?应该如何防御?

faust勒索病毒详细介绍 faust勒索病毒是一种新型的勒索软件&#xff0c;最早出现在2018年。该病毒通过加密计算机系统中的文件并要求支付赎金来解锁文件&#xff0c;从而获取经济利益。与传统的勒索软件相比&#xff0c;faust勒索病毒采用了更加先进的加密算法和隐藏技术&#…

山洪灾害无线预警广播系统的主要设备和功能

一、背景 山洪泥石流是指在山区或者其他沟谷深壑&#xff0c;地形险峻的地区&#xff0c;因为暴雨、暴雪或其他自然灾害引发的洪水、山体滑坡并携带有大量泥沙以及石块的特殊洪流。山洪泥石流等地质灾害具有突然性以及流速快&#xff0c;流量大&#xff0c;物质容量大和破坏力…

光数据传送器|光通讯传感器极速版OPT系列尺寸与安装步骤

光数据传送器|光通讯传感器极速版OPT系列是利用可见光及不可见光作为信息载体&#xff0c;无需光纤、网线等有线介质&#xff0c;在空中直接进行信息传输的无线方式通信。驱动光源以可见光及不可见光的高速明暗变化来传输数字信号&#xff0c;以极高光频率双向发射接收光信号&a…

python跟C++选哪个?

选择使用Python还是C取决于你的具体需求和项目背景。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在后台发给你。 在通信工程行业…

QT C++(QWidget类及其常见的属性)

文章目录 1. QWidget类及其常见的属性 1. QWidget类及其常见的属性 QT各种控件都是继承自QWidget类&#xff0c;QWidget类是QT控件体系中通用的部分。 QWidget属性如下图 常见的QT属性为&#xff1a; enabled&#xff1a;描述控件是否处于可用状态&#xff08;禁用状态这个…

Python 求高斯误差函数 erf 和 erfc

文章目录 Part.I IntroductionPart.II 概念定义Chap.I 误差函数 erfChap.II 误差函数补 erfc Part.II 求值与绘图Chap.I 求取方式Chap.II 绘图 Reference Part.I Introduction 本文将对误差函数&#xff08;ERror Function, ERF&#xff09;进行简单的介绍&#xff0c;并探索如…

【Flutter】极光推送配置流程(VIVO/OPPO/荣耀厂商通道) 章三

前言 很高兴大家来看小编写的文章&#xff5e;&#xff5e; 继【Flutter】极光推送配置流程(极光通道/华为厂商/IOS) 章一 继【Flutter】极光推送配置流程(小米厂商通道) 章二 接下配置VIVO/OPPO/华为荣耀的厂商通道 所有截图来源于公司项目&#xff0c;所以会有大量马赛克&am…

虚拟数字人及AI相关应用分享

一、虚拟数字人 1、简介 虚拟数字人可分为基础类和仿真智能类。可用于直播的&#xff0c;一般是仿真智能类&#xff1b;基础类动作缓慢&#xff0c;体验差&#xff0c;很容易被直播平台封号。 目前各大短视频平台上介绍的数字人&#xff0c;出于营销目的&#xff0c;有夸大宣传…

prophet时间序列模型水质预测应用

前言 此前已经分析了&#xff0c;ARIMA 模型在水质预测中的应用&#xff0c;今天用 prophet 模型测试下在水质预测中的效果。 Prophet 简介 Prophet 是 Facebook 于2017年开源的一个时间序列预测框架&#xff0c;特别适合于处理具有明显趋势性和季节性的数据。该模型设计初衷…

springboot3项目练习详细步骤(第三部分:文章管理模块)

目录 发布文章 接口文档 业务实现 自定义参数校验 项目参数要求 实现思路 实现步骤 文章列表(条件分页) 接口文档 业务实现 mapper映射 更新文章 接口文档 业务实现 获取文章详情 接口文档 业务实现 删除文章 接口文档 业务实现 文章管理业务表结构…

数据可视化训练第6天(美国人口调查获得关于收入与教育背景的数据,并且可视化)

数据来源 https://archive.ics.uci.edu/dataset/2/adult 过程 首先&#xff1b;关于教育背景的部分翻译有问题。 本次使用字典嵌套记录数据&#xff0c;并且通过lambda在sorted内部进行对某个字典的排序&#xff0c;最后用plotly进行绘图 本次提取数据的时候&#xff0c;用到…

ubuntu server 22.04.4 系统安装详细教程

本教程使用vmware workstation 17创建虚拟机进行安装演示&#xff0c;安装方式和真机安装没有区别。 1、下载镜像 下载ubuntu server版本系统镜像&#xff0c;官网下载地址&#xff1a;https://cn.ubuntu.com/download/server/step1 注意&#xff1a;自己下载时需要确认是否是…

基于springboot + vue 实现的简易博客系统

项目效果图 登陆页面 文章列表 发表文章 用户管理 栏目管理 数据统计 后端技术栈后端主要采用了&#xff1a; 1.SpringBoot 2.SpringSecurity 3.MyBatis 4.部分接口遵循Restful风格 5.MySQL 前端技术栈前端主要采用了&#xff1a; 1.Vue 2.axios 3.Elemen…

每周一算法:传递闭包

题目描述 不等式排序 给定 n n n个变量和 m m m个不等式。其中 n n n小于等于 26 26 26&#xff0c;变量分别用前 n n n 的大写英文字母表示。 不等式之间具有传递性&#xff0c;即若 A > B A>B A>B 且 B > C B>C B>C&#xff0c;则 A > C A>C …

GPU prompt

提问&#xff1a; GPU是如何与CPU协调工作的&#xff1f; GPU也有缓存机制吗&#xff1f;有几层&#xff1f;速度差异是多少&#xff1f; GPU渲染流程有哪些阶段&#xff1f;他们的功能分别是什么&#xff1f; Early-Z技术是什么&#xff1f;发生在哪个阶段&#xff1f;这个…

Cocos 2048从创建到发布上线

二、审核通过之后上线流程 代码通过审核之后&#xff0c;会通过站内信和微信消息发送通知&#xff0c;在管理后台&#xff0c;点击提交发布&#xff0c;去备案

File类~路径、创建文件对象

路径分为相对路径&#xff08;不带盘符&#xff09;&#xff0c;绝对路径&#xff08;带盘符&#xff09; 路径是可以存在的&#xff0c;也可以是不存在的 创建文件对象的三个方法&#xff1a;