定时器搭配GPIO做定时扫描按键 -- STM32


在STM32F103系列的单片机应用中,定时器(TIM)和通用输入/输出(GPIO)是常用的模块之一。这两个模块结合起来,可以实现非常多的实际应用,其中,定时器可以用来实现定时扫描按键,而GPIO可以用来控制LED等设备。

本文将介绍,在STM32F103系列的单片机中如何通过定时器和GPIO模块,实现定时扫描按键功能。
本文将从定时器、GPIO原理入手,讲解使用到的函数以及配置参数,最后做总结。

定时器(TIM)

使用定时器,需要先了解定时器的原理及其相关参数。

定时器工作原理

定时器是一种计算时间的模块,可以用来触发一些事件、完成一些操作或者调度系统任务。定时器的核心部分为计数器和控制寄存器。

定时器开始计数后,计数值会每次增加1,当计数值达到设定的上限值时,会触发一个中断或者其他的事件。同时,可以通过调整定时器的各个参数来控制计数器的计数速率、计数上限以及触发中断的条件。
在这里插入图片描述

定时器相关参数

在使用定时器前,需要对其相关参数进行配置,常用的定时器参数包括:

  • 周期(ARR):定时器计数器的上限值,当计数器增加到该值时,会触发定时器的中断或其他操作。
  • 预分频器(PSC):用于降低计数器输入时钟的频率,从而达到降低定时器计数速率的目的。
  • 计数模式(Counter Mode):定时器可以分为向上计数模式和向下计数模式,可以根据具体需求进行配置。
  • 输出比较通道(Output Compare Channel):可以通过定时器的输出比较通道来产生PWM波形等复杂的定时信号。

相关函数和配置参数

常用的配置定时器的函数包括:

void TIM_DeInit(TIM_TypeDef* TIMx);         //TIMx 定时器的初始化

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);      //定时器基本配置

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);       //开启或关闭定时器

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);      //设置定时器输出比较通道 1 的占空比

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);      //允许或禁止定时器中断

void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);        //清除定时器的中断标志位

其中,TIM_TimeBaseInitTypeDef 结构体用于初始化定时器的基本配置参数,常用的配置参数包括:

//定时器基本配置结构体
typedef struct
{
  uint16_t TIM_Prescaler;           //定时器计数器的预分频值
  uint16_t TIM_CounterMode;         //定时器的计数模式(向上或向下)
  uint32_t TIM_Period;              //定时器计数器的上限值
  uint16_t TIM_ClockDivision;       //采样分频
  uint8_t TIM_RepetitionCounter;    //自动重载计数器值
} TIM_TimeBaseInitTypeDef;

在使用定时器时,需要对其进行初始化并配置相关参数。以下代码段为一个示例初始化函数:

void Timer_Init(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  TIM_TimeBaseInitStruct.TIM_Prescaler = 7200;
  TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInitStruct.TIM_Period = 1000-1;
  TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
  TIM_Cmd(TIM3, ENABLE);
}

该函数使用的是 TIM3 定时器,采用向上计数模式。定时器预分频器值为7200,计数到1000后(即从0计数到999),会触发定时器中断。TIM_Cmd() 函数用于启用定时器。

推荐查看之前文章:https://blog.csdn.net/hongyun1221/article/details/130973099

通用输入/输出(GPIO)

通用输入/输出(GPIO)是单片机上最为基础,也是最常用的模块之一。GPIO模块包含多个引脚,每个引脚具有输入和输出两种模式,可以用来控制各种外设,例如LED、按键等。

GPIO原理

GPIO模块的输入输出控制是通过寄存器进行的,每个IO口都与一个寄存器相关联,我们可以通过修改该寄存器的值来控制IO口的状态,从而实现相应的输入/输出功能。常见的GPIO寄存器包括:GPIOx_CRL、GPIOx_CRH、GPIOx_ODR、GPIOx_IDR、GPIOx_BSRR、GPIOx_BRR、GPIOx_LCKR等。

GPIO相关函数

在STM32F103系列单片机中,使用GPIO模块时,常用的函数包括:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);           //初始化GPIO引脚

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);                   //GPIO引脚锁定

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);      //设置或清除GPIO引脚的输出状态

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);             //读取GPIO引脚的输入状态

其中,GPIO_InitTypeDef 结构体用于配置GPIO引脚的具体参数,常用的配置参数包括:

//GPIO 配置结构体
typedef struct
{
  uint16_t GPIO_Pin;            //需要初始化的GPIO引脚
  GPIOMode_TypeDef GPIO_Mode;   //GPIO引脚工作模式
  GPIOSpeed_TypeDef GPIO_Speed; //GPIO引脚速度
  GPIOOType_TypeDef GPIO_OType; //GPIO引脚输出类型
  GPIOPuPd_TypeDef GPIO_PuPd;   //GPIO引脚上下拉模式
} GPIO_InitTypeDef;

使用GPIO模块,需要对相关引脚进行初始化并配置其相应的工作模式、输出类型、上下拉模式等参数。以下为一个示例初始化函数:

void GPIO_InitExample(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);    //使能GPIOB的时钟
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;                   //初始化GPIOB的0号引脚
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;               //输入上拉模式
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;           //50MHz速度
  GPIO_Init(GPIOB, &GPIO_InitStruct);                      //初始化GPIOB
}

该函数使用的是GPIOB模块的0号引脚,初始化为输入上拉模式。

实现定时扫描按键

下面我们结合以上的知识,来实现简单的定时扫描按键功能。该功能的基本实现思路为:

  1. 初始化GPIO引脚,设置为输入上拉模式
  2. 初始化TIM定时器,启用定时中断
  3. 在定时器中断处理函数中检测按键状态

为了便于演示,我们使用开发板上的资源做一个示例:定时器每20ms扫描一次按键,按键配置LED亮灭。

主程序代码

int main(void){
    LED_GPIO_Init();
    KEY_GPIO_Init();
    TIM_EXIT_Init(20000-1, 72-1);
    while(1){
        
    }
}

定时器头文件

#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"


#define TIM             TIM3
#define TIM_CLK         RCC_APB1Periph_TIM3
#define TIM_IRQ         TIM3_IRQn
#define TIM_IRQHandler  TIM3_IRQHandler


void TIM_EXIT_Init(uint16_t arr, uint16_t psc);


#endif /* __TIMER_H */

定时器源代码

#include "bsp_timer.h"

void TIM_NVIC_Init(void){
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStruct.NVIC_IRQChannel = TIM_IRQ;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);


}


void TIM_EXIT_Init(uint16_t arr, uint16_t psc){
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_NVIC_Init();
    RCC_APB1PeriphClockCmd(TIM_CLK, ENABLE);
    TIM_TimeBaseInitStruct.TIM_Prescaler = psc;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = arr;
    TIM_TimeBaseInit(TIM, &TIM_TimeBaseInitStruct);
    TIM_ITConfig(TIM, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM, ENABLE);
}

按键源文件

#ifndef __KEY_H
#define __KEY_H


#include "stm32f10x.h"
typedef enum {
    key_button = 0,
    key_release = 1
} KeyStatus;


#define KEY_GPIO_PORT   GPIOB
#define KEY_GPIO_CLK    RCC_APB2Periph_GPIOB
#define KEY_GPIO_PIN    GPIO_Pin_1


void KEY_GPIO_Init(void);
void KEY_GPIO_Scan(void);


#endif /* __KEY_H */

总结

本文介绍了在STM32F103单片机中,如何使用定时器和GPIO模块实现定时扫描按键功能。通过学习定时器和GPIO的相关原理和函数,我们可以更加深入地了解STM32F103单片机的工作原理,从而更好地完成各类实际应用。

总的来说,定时器和GPIO模块是STM32F103单片机非常常用和重要的模块,掌握其相关知识和应用技巧,对于提高单片机的应用水平和开发效率具有重要的作用。

程序代码资源下载:https://download.csdn.net/download/hongyun1221/87852606

在这里插入图片描述

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以利用 STM32定时器和外部中断实现按键扫描。具体步骤如下: 1. 定义定时器和外部中断的初始化参数。 ``` TIM_HandleTypeDef htim2; TIM_OC_InitTypeDef sConfigOC; GPIO_InitTypeDef GPIO_InitStruct; EXTI_HandleTypeDef hexti; ``` 2. 初始化定时器和外部中断。 ``` void MX_TIM2_Init(void) { htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000; //设定定时器计数周期为1ms htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } } void MX_GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; //下降沿触发 GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { //按键被按下 } } ``` 3. 启动定时器和外部中断。 ``` HAL_TIM_Base_Start_IT(&htim2); //启动定时器 HAL_NVIC_EnableIRQ(EXTI0_IRQn); //启动外部中断 ``` 4. 在定时器的中断回调函数中进行按键扫描。 ``` void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim2) { static uint16_t key_state = 0; //按键状态,0为未按下,1为按下 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) //按键被按下 { if (key_state == 0) //如果之前未按下 { key_state = 1; HAL_GPIO_EXTI_Callback(GPIO_PIN_0); //调用外部中断回调函数 } } else //按键未被按下 { key_state = 0; } } } ``` 这样就可以实现按键扫描了。需要注意的是,如果有多个按键,可以在中断回调函数中根据不同的 GPIO_Pin 进行区分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值