[STM32 HAL] 使用TIM 测量两次按键时间间隔

概述

测量两次按下按键之间经过的时间。第一次按下按键,点亮LED,开始计时。第二次按下按键,熄灭LED,停止计时。记录下两次按键的时间节点。计算出两次按键的时间间隔。最后将结果通过UART输出到电脑。本实验使用正点原子MiniSTM32板子,芯片为STM32F103RCT6

主要完成以下内容:

  • 按键触发外部中断并进行消抖。
  • 定时器启动和停止
  • 串口和上位机通信,发送时间数据到电脑
  1. 使用STM32CubeMX配置GPIO引脚。将PD2引脚配置为GPIO_Output,并设定标签LED,其他选项默认即可。将PC5引脚配置为GPIO_EXTI,因为按键按下后接地,并且没有外置上拉电阻,所以配置为上拉模式,下降沿触发,标签设置为KEY0。
  2. 设置定时器,这里选用TIM6。将其预分频值Prescaler设置为72 – 1,即定时器的时钟频率为1MHz。计数模式为Up,计数周期Counter Period设置为最大值65535。使能定时器中断。在定时器溢出时,增加计数值。如果不使能中断,则最大只能计量到65536×10^(-6)s = 0.065536秒。
  3. 设置串口外设。使能USART1即可,其他配置为默认,即波特率115200。
  4. 设置NVIC优先级。因为需要在按键触发的外部终端中使用HAL_Delay函数来进行按键延时消抖,而该函数需要使用到Systick Timer中断。在CubeMX配置中,Systick Timer的中断优先级最低。导致在外部中断回调函数中无法正常运行HAL_Delay,使得单片机宕机。所以一改将SystickTimer的中断优先级设置的比外部中断的优先级高。
硬件原理简图

在这里插入图片描述
在这里插入图片描述

代码

本次实验使用TIM6,一个基本计数器,只有简单的向上计数功能。当计数值CNT达到ARR值时,自动归零,重新开始计数,如果设置了中断,则进入中断回调函数。
TIM配置
启用TIM中断。
中断优先级配置

配置按键

使用外部中断,来检测按键状态。配置KEY0为外部中断EXTI模式,下降沿触发,上拉Pull-up。并且将其中断优先级设置的比Time base中断低,如上图所示。
按键外部中断配置

配置UART

直接启用即可,其他默认。
UART配置

代码

首先配置好串口,重定向printf,方便使用。

/* *
 * @brief 重定向printf函数,将printf的输出重定向的串口USART1
 * @note 根据宏,自动选择fputc或者时__io_putchar
 * */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE* f)
#endif /* __GNUC__ */
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  return (ch);
}
#ifdef __cplusplus
}
#endif //__cplusplus

接下来,配置TIM时钟的中断回调。每当TIM计数计满时,进入TIM的中断回调。在TIM中断回调中只需要简单,对记录中断次数的变量加一即可

/*@var time_period_cnt 
用于计数TIM6的中断次数,一次中断时间约为0.065536s*/
uint32_t time_period_cnt = 0;
/*@var time_cap_start_cnt 
记录第一次按键按下时,定时器Counter寄存器的值*/
uint16_t time_cap_start_cnt = 0;
/*@var time_cap_stop_cnt 
记录第二次按键按下时,计时结束,定时器Counter寄存器的值*/
uint16_t time_cap_stop_cnt = 0;
/* *
 * 标记当前定时器状态
 * 0:	默认状态Idle,什么事也没有,等待按键然后触发TIM
 * 1:	TIM开始计时
 * 2:	计时完成
 * */
uint8_t time_cap_state = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM6)
	{
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//该语句可不加,仅仅用来指示程序正常运行
		++time_period_cnt;
	}
}

接下来时对于按键的处理

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY0_Pin)
	{
		HAL_Delay(15);
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET)
		{
			/*第一次按键按下,开始计时*/
			if(time_cap_state == 0)
			{
				HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
				__HAL_TIM_SET_COUNTER(&htim6, 0); //清空当期计数器的,将CNT设为0
				HAL_TIM_Base_Start_IT(&htim6); //启用定时器,开始计时
				time_cap_start_cnt = __HAL_TIM_GET_COUNTER(&htim6);
				time_period_cnt = 0; //将TIM中断次数清零
				time_cap_state = 1;
			}
			/*第二次按键被按下,计时结束,设置time_cap_state为2,
			 * 表示定时结束,在主循环中打印数据到电脑*/
			else if(time_cap_state == 1)
			{
				HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
				HAL_TIM_Base_Stop_IT(&htim6);
				time_cap_stop_cnt = __HAL_TIM_GET_COUNTER(&htim6);
				time_cap_state = 2;
			}
		}
	}
}

主循环中负责轮询当前状态,如果已经完成测量,就进行串口发送。

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */
  while (1)
  {
	if(time_cap_state == 2)
	{
		double time_elapsed =  (1e-6) * (time_period_cnt * 65536 
		+ time_cap_start_cnt - time_cap_stop_cnt);// 计算经过的时间,1e-6表示tim计数一次的周期
		printf("Time Elapsed:%lf s\r\n", time_elapsed);
		time_cap_state = 0;
	}
  }
}

测量结果
例程下载例题代码,使用STM32CubeIDE打开更方便

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值