学习嵌入式入门(八)通用定时器实验

一、通用定时器中断实验

TIM2/TIM3/TIM4/TIM5 寄存器

控制寄存器 1(TIMx_CR1)

CMS[1:0]位,用于设置边沿对齐模式还是中心对齐模式,本实验我们使用边沿对齐模式, 所以设置为 00 即可。

DIR 位,用于控制定时器的计数方向,我们使用递增计数模式,所以设置 DIR 位为 0。

CEN 位,用于使能计数器的工作,必须要设置该位为 1,计数器才会开始计数。

从模式控制寄存器(TIMx_SMCR)

DMA/中断使能寄存器(TIMx_DIER)

该寄存器用于使能/失能触发 DMA 请求、捕获/比较中断以及更新中断。

状态寄存器(TIMx_SR)

计数寄存器(TIMx_CNT)

       TIM2/TIM3/TIM4/TIM5 的计数寄存器都是 16 位有效的,计数模式可以是递增计数模式、 递减计数模式和中心对齐计数模式,计数值范围 0~65535。

预分频寄存器(TIMx_PSC)

      定时器的预分频寄存器都是 16 位的,即写入该寄存器的数值范围是 0 到 65535,表示 1 到 65536 分频。

自动重载寄存器(TIMx_ARR)

       自动重载寄存器是低 16 位有效。该寄存器可以由 APRE 位设置是否进行缓冲。计数器的 值会和自动重装寄存器影子寄存器进行比较,当两者相等,定时器就会溢出,从而发生更新事 件,如果打开了更新中断,还会发生更新中断。

程序解析

gtim.h

/* TIMX 中断定义 
 * 默认是针对 TIM2~TIM5 
 * 注意: 通过修改这 4 个宏定义,可以支持 TIM1~TIM8 任意一个定时器. 
 */ 
 
#define GTIM_TIMX_INT TIM3 
#define GTIM_TIMX_INT_IRQn TIM3_IRQn 
#define GTIM_TIMX_INT_IRQHandler TIM3_IRQHandler 
/* TIM3 时钟使能 */ 
#define GTIM_TIMX_INT_CLK_ENABLE() do{ __HAL_RCC_TIM3_CLK_ENABLE(); }while(0)

gtim.c

void gtim_timx_int_init(uint16_t arr, uint16_t psc) 
{ 
 GTIM_TIMX_INT_CLK_ENABLE(); /* 使能 TIMx 时钟 */ 
 
 g_timx_handle.Instance = GTIM_TIMX_INT; /* 通用定时器 x */ 
 g_timx_handle.Init.Prescaler = psc; /* 预分频系数 */ 
 g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */ 
 g_timx_handle.Init.Period = arr; /* 自动装载值 */ 
HAL_TIM_Base_Init(&g_timx_handle); 
 
/* 设置中断优先级,抢占优先级 1,子优先级 3 */ 
 HAL_NVIC_SetPriority(GTIM_TIMX_INT_IRQn, 1, 3); 
 HAL_NVIC_EnableIRQ(GTIM_TIMX_INT_IRQn); /* 开启 ITMx 中断 */ 
 
 HAL_TIM_Base_Start_IT(&g_timx_handle); /* 使能定时器 x 和定时器 x 更新中断 */ 
} 

         这里配置的参数和基本定时器中断实验的是一样的,只是这里没有使用到 HAL 库的 HAL_TIM_Base_MspInit 函 数 来 存 放 NVIC 和 使 能 时 钟 的 代 码 , 而 是 全 部 存 放 到 gtim_timx_int_init 函数里。在一个项目中,用到多个定时器时,建议大家使用这种方式来处理 代码,这样方便代码的管理。

下面再来看看定时器中断服务函数,其定义如下:

void GTIM_TIMX_INT_IRQHandler(void) 
{ 
 /* 以下代码没有使用定时器 HAL 库共用处理函数来处理,而是直接通过判断中断标志位的方式 */ 
if(__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET) 
 { 
 LED1_TOGGLE(); 
/* 清除定时器溢出中断标志位 */ 
 __HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE); 
 } 
} 

二、通用定时器 PWM 输出实验

通用定时器输出比较部分框图介绍

捕获/比较通道1的主电路---输出部分

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

PWM 产生原理示意图如下图所示:

       上图中,定时器工作在递增计数模式,纵轴是计数器的计数值 CNT,横轴表示时。当 CNT时,IO 输出低电平(逻辑 0);当 CNT>=CCRx 时,IO 输出高电平(逻辑 1);当 CNT=ARR 时,定时器溢出,CNT 的值被清零,然后继续递增,依次循环。在这个循环中,改 变 CCRx 的值,就可以改变 PWM 的占空比,改变 ARR 的值,就可以改变 PWM 的频率,这就 是 PWM 输出的原理。

PWM模式

TIM2/TIM3/TIM4/TIM5 寄存器

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

        让 TIM3 的 CH2 输出 PWM 波为例进行介绍,该寄存器的模 式设置位 OC2M[2:0]就是对应着通道 2 的模式设置,此部分由 3 位组成。总共可以配置成 8 种 模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110 或者 111,分别对应 PWM 模式 1 和 PWM 模式 2。这两种 PWM 模式的区别就是输出有效电平的极性相反。

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

要让 TIM3 的 CH2 输出 PWM 波,这里我们要使能 CC2E 位,该位是 通道 2 输入/输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1。CC2P 位是设置通道 2 的输出极性。

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

     在输出模式下,捕获/比较寄存器影子寄存器的值与 CNT 的值比较,根据比较结果产生相 应动作,利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的占空比了。

相关HAL库函数介绍

HAL_TIM_PWM_Init 函数

函数描述: 用于初始化定时器的 PWM 输出模式。

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim); 

函数形参:

形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,

HAL_TIM_PWM_ConfigChannel 函数

函数描述: 该函数用于设置定时器的 PWM 通道。

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, 
TIM_OC_InitTypeDef *sConfig, uint32_t Channel); 

函数形参:

形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,用于配置定时器基本参数。

形参 2 是 TIM_OC_InitTypeDef 结构体类型指针变量,用于配置定时器的输出比较参数。

typedef struct 
{ 
 uint32_t OCMode; /* 输出比较模式选择,寄存器的时候说过了,共 8 种模式 */ 
 uint32_t Pulse; /* 设置比较值 */ 
 uint32_t OCPolarity; /* 设置输出比较极性 */ 
 uint32_t OCNPolarity; /* 设置互补输出比较极性 */ 
 uint32_t OCFastMode; /* 使能或失能输出比较快速模式 */ 
 uint32_t OCIdleState; /* 选择空闲状态下的非工作状态(OC1 输出) */ 
 uint32_t OCNIdleState; /* 设置空闲状态下的非工作状态(OC1N 输出) */ 
} TIM_OC_InitTypeDef; 

形参 3 是定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

HAL_TIM_PWM_Start 函数

函数描述: 用于使能通道输出和启动计数器,即启动 PWM 输出。

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel); 

函数形参:

形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。

形参 2 是定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

HAL_TIM_ConfigClockSource 函数

函数描述: 用于配置定时器时钟源。

HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, 
TIM_ClockConfigTypeDef *sClockSourceConfig); 

函数形参:

形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。

形参 2 是 TIM_ClockConfigTypeDef 结构体类型指针变量,用于配置定时器时钟源参数。

typedef struct 
{ 
 uint32_t ClockSource; /* 时钟源 */ 
 uint32_t ClockPolarity; /* 时钟极性 */ 
 uint32_t ClockPrescaler; /* 定时器预分频器 */ 
 uint32_t ClockFilter; /* 时钟过滤器 */ 
} TIM_ClockConfigTypeDef; 

定时器 PWM 输出模式配置步骤

1) 开启 TIMx 和通道输出的 GPIO 时钟,配置该 IO 口的复用功能输出

2) 初始化 TIMx,设置 TIMx 的 ARR 和 PSC 等参数

3) 设置 TIMx_CHy 的 PWM 模式,输出比较极性,比较值等参数

4) 使能 TIMx,使能 TIMx 的 CHy 输出

5) 修改 TIM3_CCR2 来控制占空比

gtim.h

/* TIMX PWM 输出定义 
 * 这里输出的 PWM 控制 LED0(RED)的亮度 
 * 默认是针对 TIM2~TIM5 
 * 注意: 通过修改这几个宏定义,可以支持 TIM1~TIM8 任意一个定时器,任意一个 IO 口输出 PWM 
 */ 
#define GTIM_TIMX_PWM_CHY_GPIO_PORT GPIOB 
#define GTIM_TIMX_PWM_CHY_GPIO_PIN GPIO_PIN_5 
#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_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) 
#define GTIM_TIMX_PWM TIM3 
#define GTIM_TIMX_PWM_CHY TIM_CHANNEL_2 /* 通道 Y, 1<= Y <=4 */ 
#define GTIM_TIMX_PWM_CHY_CCRX TIM3->CCR2 /* 通道 Y 的输出比较寄存器 */ 
#define GTIM_TIMX_PWM_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM3_CLK_ENABLE(); 
}while(0) /* TIM3 时钟使能 */

gtim.c

/** 
 * @brief 通用定时器 TIMX 通道 Y PWM 输出 初始化函数(使用 PWM 模式 1) 
 * @note 
 * 通用定时器的时钟来自 APB1,当 D2PPRE1≥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) 
{ 
 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 */ 
 
 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_OCPOLARITY_LOW;/* 输出比较极性为低 */ 
HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &g_timx_oc_pwm_chy_handle, 
GTIM_TIMX_PWM_CHY); /* 配置 TIMx 通道 y */ 
 HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);/*开启 PWM 通道*/ 
} 

       本实验我们使用 PWM 的 MSP 初始化回调函数 HAL_TIM_PWM_MspInit 来存放时钟、 GPIO 的初始化代码,其定义如下:

/** 
 * @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 设置,设置重映射 */ 
 } 
} 

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); /* 延时初始化 */ 
 usart_init(115200); /* 串口初始化为 115200 */ 
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++; /* dir==1 ledrpwmval 递增 */ 
 else ledrpwmval--; /* dir==0 ledrpwmval 递减 */ 
 
 if (ledrpwmval > 300)dir = 0; /* ledrpwmval 到达 300 后,方向为递减 */ 
 if (ledrpwmval == 0)dir = 1; /* ledrpwmval 递减到 0 后,方向改为递增 */ 
 
 /* 修改比较值控制占空比 */ 
 __HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY, 
ledrpwmval); 
} 
} 

三、通用定时器输入捕获实验

输入捕获脉宽测量原理

       首先设置定时器通道 x 为上升沿捕获,这样在 t1 时刻上升沿到来时, 就会发生捕获事件。这里我们还会打开捕获中断,所以捕获事件发生就意味着捕获中断也会发 生。在捕获中断里将计数器值清零,并设置通道 x 为下降沿捕获,这样 t2 时刻下降沿到来时, 就会发生捕获事件和捕获中断。捕获事件发生时,计数器的值会被锁存到捕获/比较寄存器中(比 如通道 1 对应的是 CCR1 寄存器)。 

TIM2/TIM3/TIM4/TIM5 寄存器

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

TIMx_CCMR1 寄存器对应于通道 1 和通道 2 的设置,CCMR2 寄存器对应通道 3 和通道 4。

其中 CC1S[1:0],这两个位用于 CCR1 的通道配置,这里我们设置 IC1S[1:0]=01,也就是配 置 IC1 映射在 TI1 上。

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

TIM2/TIM3/TIM4/TIM5 的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开 关和极性。

      用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。要使能输入捕获,必须设置 CC1E=1, 而 CC1P 则根据自己的需要来配置。

定时器输入捕获模式配置步骤

1) 开启 TIMx 和输入通道的 GPIO 时钟,配置该 IO 口的复用功能输入

2) 初始化 TIMx,设置 TIMx 的 ARR 和 PSC 等参数

3) 设置 TIMx_CHy 的输入捕获模式,开启输入捕获

4) 使能定时器更新中断,开启捕获功能以及捕获中断,配置定时器中断优先级

5) 编写中断服务函数

gtim.h

#define GTIM_TIMX_CAP_CHY_GPIO_PORT GPIOA 
#define GTIM_TIMX_CAP_CHY_GPIO_PIN GPIO_PIN_0 
#define GTIM_TIMX_CAP_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE();\ 
}while(0) /* PA 口时钟使能 */ 
 
#define GTIM_TIMX_CAP TIM5 
#define GTIM_TIMX_CAP_IRQn TIM5_IRQn 
#define GTIM_TIMX_CAP_IRQHandler TIM5_IRQHandler 
#define GTIM_TIMX_CAP_CHY TIM_CHANNEL_1 
/* 通道 Y, 1<= Y <=4 */ 
#define GTIM_TIMX_CAP_CHY_CCRX TIM5->CCR1 
/* 通道 Y 的输出比较寄存器 */ 
#define GTIM_TIMX_CAP_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM5_CLK_ENABLE();\ 
}while(0) /* TIM5 时钟使能 */ 

gtim.c

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 = GTIM_TIMX_CAP; /* 定时器 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, 
GTIM_TIMX_CAP_CHY); /* 配置 TIM5 通道 1 */ 
 
 __HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE); /* 使能更新中断 */ 
/* 使能通道输入以及使能捕获中断*/ 
 HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY); 
}

HAL_TIM_IC_Init 初始化定时器的基础工作参数,第二部分是调用 HAL_TIM_IC_ConfigChannel 函数配置输入捕获通道映射关系,滤波和分频等。最后是使能更 新中断和使能通道输入以及定时器捕获中断。

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim) 
{ 
 if (htim->Instance == GTIM_TIMX_CAP) /*输入通道捕获*/ 
 { 
 GPIO_InitTypeDef gpio_init_struct; 
 GTIM_TIMX_CAP_CHY_CLK_ENABLE(); /* 使能 TIMx 时钟 */ 
 GTIM_TIMX_CAP_CHY_GPIO_CLK_ENABLE(); /* 开启捕获 IO 的时钟 */ 
 
 gpio_init_struct.Pin = GTIM_TIMX_CAP_CHY_GPIO_PIN;/* 输入捕获的 GPIO 口 */ 
 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(GTIM_TIMX_CAP_CHY_GPIO_PORT, &gpio_init_struct); 
 
 HAL_NVIC_SetPriority(GTIM_TIMX_CAP_IRQn, 1, 3); /* 抢占 1,子优先级 3 */ 
 HAL_NVIC_EnableIRQ(GTIM_TIMX_CAP_IRQn); /* 开启 ITMx 中断 */ 
  } 
} 

下面开始看中断服务函数的逻辑程序,HAL_TIM_IRQHandler 函数会调用下面两个回调函 数,我们的逻辑代码就是放在回调函数里,函数定义如下:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) 
{ 
 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_handler,

GTIM_TIMX_CAP_CHY); /* 获取当前的捕获值 */ 
 TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handler, 
GTIM_TIMX_CAP_CHY);/* 一定要先清除原来的设置 */ 
 TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handler, GTIM_TIMX_CAP_CHY, 
TIM_ICPOLARITY_RISING);/* 配置 TIM5 通道 1 上升沿捕获 */ 
 } 
 else /* 还未开始,第一次捕获上升沿 */ 
 { 
 g_timxchy_cap_sta = 0; /* 清空 */ 
 g_timxchy_cap_val = 0; 
 g_timxchy_cap_sta |= 0X40; /* 标记捕获到了上升沿 */ 
 __HAL_TIM_DISABLE(&g_timx_cap_chy_handler); /* 关闭定时器 5 */ 
 __HAL_TIM_SET_COUNTER(&g_timx_cap_chy_handler,0); /* 计数器清零 */ 
 TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handler, 
GTIM_TIMX_CAP_CHY); /* 一定要先清除原来的设置!! */ 
 TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handler, GTIM_TIMX_CAP_CHY, 
TIM_ICPOLARITY_FALLING); /* 定时器 5 通道 1 设置为下降沿捕获 */ 
 __HAL_TIM_ENABLE(&g_timx_cap_chy_handler); /* 使能定时器 5 */ 
   } 
  } 
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 
{ 
 if (htim->Instance == GTIM_TIMX_CAP) 
 { 
 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, 
GTIM_TIMX_CAP_CHY); /* 一定要先清除原来的设置 */ 
/* 配置 TIM5 通道 1 上升沿捕获 */ 
 TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, 
GTIM_TIMX_CAP_CHY, TIM_ICPOLARITY_RISING); 
 g_timxchy_cap_sta |= 0X80; /* 标记成功捕获了一次 */ 
 g_timxchy_cap_val = 0XFFFF; 
 } 
 else /* 累计定时器溢出次数 */ 
 { 
 g_timxchy_cap_sta++; 
     } 
    } 
   } 
  } 
}

main.c

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); /* 延时初始化 */ 
 usart_init(115200); /* 串口初始化为 115200 */ 
 led_init(); /* 初始化 LED */ 
 key_init(); /* 初始化按键 */ 
 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); 
 } 
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值