欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

STM32按键实验

发布时间:2025/4/5 编程问答 64 豆豆
生活随笔 收集整理的这篇文章主要介绍了 STM32按键实验 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

后面实现按键功能

过程
使能按键对应IO口时钟。调用函数:RCC_APB2PeriphClockCmd()
初始化IO模式:上拉/下拉输入。调用函数GPIO_Init()
扫描IO口电平(库函数/寄存器/位操作)

按键扫描:

支持连续按:检测到高电平,认为按键没有按下;检测到低电平,认为按键已经按下。
连续按:只要是低电平就有效。

补充c语言关键字 static,static申明的局部变量,存储在静态存储区。它在函数调用之后,不会被释放,它的值一直保留下来。
static举例

下面两个getValue函数,执行效果有什么不一样呢?
第一个

int getValue(void){int flag=0;flag++;return flag; }

flag是局部变量,每次调用getValue函数,flag首先被置0,然后flag++,所以每次调用返回值都是1
第二个

int getValue(void){ static int flag=0; flag++; return flag; }

这里flag使用static 声明,由于static具有记忆功能,flag变量在静态存储区不被释放,所以每次getValue函数执行完flag的值一直保留。 第一次调用flag=1;第二次调用返回flag=2;依次类推。

下面给出按键扫描的代码框架
支持连续按:
按下没有松开,一直有效。
扫描按键是否按下,如果按下并且 给10ms的消抖,然后再次确认是否按下,如果确实是按下,则返回键值;否则就返回无效值。假设按键按下返回值为1,如果按键一直按下,则一直返回1.

u8 KEY_Scan(void){if(KEY按下){delay_ms(10);//延时10ms,消抖if(KEY确实按下){return KEY_Value;}return 无效值;}}

不支持连续按:
前一次按下如果没有松开,则本次按无效。
这里需要static 来声明一个标志位key_up来记录上一次是否按下:如果没按,记为1;如果按过,标记为0。

u8 KEY_Scan(void){static u8 key_up =1;if(key_up&&KEY按下){//如果上次按键没按下,现在按delay_ms(10);key_up=0;//标志位=0,表示本次按下if(KEY确实按下){return KEY_VALUE:}}else if (KEY没有按下) key_up=1;return 没有按下(无效值) }

然后把上面两组代码合二为一,通过参数来控制是否支持连续按。

如果参数mode=1,表示支持连续按

u8 KEY_Scan( u8 mode){static u8 key_up =1;if(mode==1 ) key_up=1;//支持连续按if(key_up&&KEY按下){//如果上次按键没按下,现在按delay_ms(10);key_up=0;//标志位=0,表示本次按下if(KEY确实按下){return KEY_VALUE:}}else if (KEY没有按下) key_up=1;return 没有按下(无效值) }

下面开始动手操作

这是宏定义,表示读取按键

#define KEY0 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)//读取按键0 #define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)//读取按键1 #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键2

由原理图可以知道如下信息:

来源:正点原子
KEY0,KEY1按键一端接的是低电平(接地),如果按键没有按下的话,检测到的是高电平,需要设置成上拉输入

上拉输入的含义是:端口设置为输入,并且通过电阻接高电平。这样在没有按键的时候,检测到的就是高电平。

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA15GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PC5GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC5

WK_UP按键一端接的是高电平,如果按键没有按下,检测到的是低电平,需要设置成下拉输入

下拉输入的含义是:端口设置为输入,并且接低电平(接地)。这样在没有按键的时候,检测到的就是低电平。

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PA0GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

初始化按键
根据原理图:
WK_UP 对应的是PA0
KEY1 对应的是PA15
KEY0对应的是PC5

时钟使能函数使用RCC_APB2PeriphClockCmd(),为什么不使用RCC_APB1PeriphClockCmd()呢?
这里的原因是
两种的区别包括但是不局限于所对接的端口不同:
APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。
APB2负责AD,I/O,高级TIM,串口1。
这里有一篇介绍APB1和APB2区别的博客文章,马住以后看
RCC_APB2PeriphClockCmd()在库函数中的定义

/*** @brief Enables or disables the High Speed APB (APB2) peripheral clock.* @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.* This parameter can be any combination of the following values:* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,* RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,* RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,* RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,* RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,* RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,* RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11 * @param NewState: new state of the specified peripheral clock.* This parameter can be: ENABLE or DISABLE.* @retval None*/ void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) {/* Check the parameters */assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));assert_param(IS_FUNCTIONAL_STATE(NewState));if (NewState != DISABLE){RCC->APB2ENR |= RCC_APB2Periph;}else{RCC->APB2ENR &= ~RCC_APB2Periph;} }

我们要使用的IO口是PORTA和PORTC,所以时钟使能端口A和C,这是因为:WK_UP 对应的是PA0,KEY1 对应的是PA15,KEY0对应的是PC5。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟

下面这句话,还需要时间去学习

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试

它的第一个参数意思是

@arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled

后面是结构体的介绍
GPIO_InitTypeDef是一个结构的别名,该结构体成员包括端口,速度,操作模式。

typedef struct {uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.This parameter can be any value of @ref GPIO_pins_define */GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.This parameter can be a value of @ref GPIOSpeed_TypeDef */GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.This parameter can be a value of @ref GPIOMode_TypeDef */ }GPIO_InitTypeDef;

然后声明一个结构体变量,名字叫做GPIO_InitStructure,通过它来初始化IO口。

//初始化KEY1=PA15GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;//PA15GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化GPIOA15

GPIO引脚记录如下

/** @defgroup GPIO_pins_define * @{*/#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */ #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */ #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */ #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */ #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */ #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */ #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */ #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */ #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */ #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */ #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */ #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */ #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */ #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */ #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */ #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */ #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */

IO口模式有八种,列举如下

/** * @brief Configuration Mode enumeration */typedef enum { GPIO_Mode_AIN = 0x0,GPIO_Mode_IN_FLOATING = 0x04,GPIO_Mode_IPD = 0x28,GPIO_Mode_IPU = 0x48,//上拉输入GPIO_Mode_Out_OD = 0x14,GPIO_Mode_Out_PP = 0x10,GPIO_Mode_AF_OD = 0x1C,GPIO_Mode_AF_PP = 0x18 }GPIOMode_TypeDef;

GPIO初始化函数GPIO_Init()在库函数中的一部分如下
包含两个参数,第一个参数是IO口(比如PORTA),第二个参数是含有IO口配置信息的的结构体引用。

/*** @brief Initializes the GPIOx peripheral according to the specified* parameters in the GPIO_InitStruct.* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.* @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that* contains the configuration information for the specified GPIO peripheral.* @retval None*/ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

下面进行一些变量的宏定义
读取输入端口函数

/*** @brief Reads the specified input port pin.* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.* @param GPIO_Pin: specifies the port bit to read.* This parameter can be GPIO_Pin_x where x can be (0..15).* @retval The input port pin value.*/ uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

IO口输入输出

//IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入

mini板中
LED0 对应的端口是PA8
LED1对应的端口是PD2

#define LED0 PAout(8) //PA8 输出 #define LED1 PDout(2) //PD2

下面的代码是学习正点原子的代码
main.c文件

#include "led.h" #include "delay.h" #include "sys.h" #include "key.h"int main(void){ u8 t=0; delay_init(); LED_Init(); //初始化与LED连接的硬件接口KEY_Init(); //初始化与按键连接的硬件接口LED0=0; //点亮LED0while(1){t=KEY_Scan(0); //键值switch(t){ case KEY0_PRES:LED0=!LED0;break;case KEY1_PRES:LED1=!LED1;break;case WKUP_PRES: LED0=!LED0;LED1=!LED1;break;default:delay_ms(10); } } }

key.c文件

#include "key.h" #include "stm32f10x.h" #include "delay.h"//端口的初始化函数定义 void KEY_Init(void){GPIO_InitTypeDef GPIO_InitStructure;//结构体变量RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//端口A和端口C时钟使能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//调试模式的选择,使能SWD//初始化KEY1=PA15GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;//PA15GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化GPIOA15//初始化KEY2=PC5GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC15//初始化WK_UP=PA0GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PA0GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0}//按键扫描函数的定义 /*mode ==1:允许连续按mode==0 :不允许连续按 */u8 KEY_Scan(u8 mode){static u8 key_up=1;if(mode==1) key_up=1;//如果有按if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)){delay_ms(10);key_up=0;if(KEY0==0) return KEY0_PRES;else if(KEY1==0) return KEY1_PRES;else if(WK_UP==1) return WK_UP_PRES;}//如果都没有按下else if(KEY0==1&&KEY1==1&&WK_UP==0) key_up=1;//标志位=1,表示没有按下return 0;//返回值0,表示无按键按下 }

key.h文件

#ifndef __KEY_H //避免头文件内容重复引用定义 #define __KEY_H #include "sys.h"//宏定义 按键 #define KEY0_PRES 1 #define KEY1_PRES 2 #define WK_UP_PRES 3#define KEY0 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)//读取按键0 #define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)//读取按键1 #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键2 //声明函数 void KEY_Init(void); u8 KEY_Scan(u8 mode);#endif

led.c

#include "led.h"#include "stm32f10x.h"void LED_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);//GPIOE的时钟GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//设置推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_8;//GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_8);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//设置推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2;//GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度GPIO_Init(GPIOD,&GPIO_InitStructure);GPIO_SetBits(GPIOD,GPIO_Pin_2);}

led.h

#ifndef __LED_H #define __LED_H#define LED0 PAout(8) //PA8 输出 #define LED1 PDout(2) //PD2void LED_Init(void);#endif

总结

以上是生活随笔为你收集整理的STM32按键实验的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。