STM32按键实验
后面实现按键功能
过程
使能按键对应IO口时钟。调用函数:RCC_APB2PeriphClockCmd()
初始化IO模式:上拉/下拉输入。调用函数GPIO_Init()
扫描IO口电平(库函数/寄存器/位操作)
按键扫描:
支持连续按:检测到高电平,认为按键没有按下;检测到低电平,认为按键已经按下。
连续按:只要是低电平就有效。
补充c语言关键字 static,static申明的局部变量,存储在静态存储区。它在函数调用之后,不会被释放,它的值一直保留下来。
static举例
下面两个getValue函数,执行效果有什么不一样呢?
第一个
flag是局部变量,每次调用getValue函数,flag首先被置0,然后flag++,所以每次调用返回值都是1
第二个
这里flag使用static 声明,由于static具有记忆功能,flag变量在静态存储区不被释放,所以每次getValue函数执行完flag的值一直保留。 第一次调用flag=1;第二次调用返回flag=2;依次类推。
下面给出按键扫描的代码框架
支持连续按:
按下没有松开,一直有效。
扫描按键是否按下,如果按下并且 给10ms的消抖,然后再次确认是否按下,如果确实是按下,则返回键值;否则就返回无效值。假设按键按下返回值为1,如果按键一直按下,则一直返回1.
不支持连续按:
前一次按下如果没有松开,则本次按无效。
这里需要static 来声明一个标志位key_up来记录上一次是否按下:如果没按,记为1;如果按过,标记为0。
然后把上面两组代码合二为一,通过参数来控制是否支持连续按。
如果参数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);//初始化GPIOC5WK_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()在库函数中的定义
我们要使用的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是一个结构的别名,该结构体成员包括端口,速度,操作模式。
然后声明一个结构体变量,名字叫做GPIO_InitStructure,通过它来初始化IO口。
//初始化KEY1=PA15GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;//PA15GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化GPIOA15GPIO引脚记录如下
/** @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口配置信息的的结构体引用。
下面进行一些变量的宏定义
读取输入端口函数
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
下面的代码是学习正点原子的代码
main.c文件
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);#endifled.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跑马灯实验