stm32的按键扫描[操作寄存器+库函数]

发布者:BlossomSunrise最新更新时间:2017-02-06 来源: eefocus关键字:stm32  按键扫描 手机看文章 扫描二维码
随时随地手机看文章

本例将实现stm32的按键扫描功能。

 

操作寄存器

 

stm32的I/O口作为输入使用时,是通过读取GPIOx -> IDR 寄存器的内容来读取I/O口状态的。

 

IDR寄存器各位描述如下:

gpio_idr.png

 

由于systick不能像库函数那样方便的产生中断,通过查询systick状态位后,再查询各管脚状态反而更为不方便,所以和库函数方法不一样,直接查询了管脚状态来检测按键。

 

代码中调用 PAout(x) 、 PAin(x)等函数 在sys.h文件中,参见:(sys.h 代码参照 stm32 直接操作寄存器开发环境配置

 

直接操作寄存器代码:

#include 	   
#include "system.h"


//Key  按键端口定义
#define key0 PAin(0)// PA0
#define key1 PAin(1)// PA1
#define key2 PAin(2)// PA2
#define key3 PAin(3)// PA3

//LED  按键端口定义
#define LED0 PAout(4)// PA4
#define LED1 PAout(5)// PA5
#define LED2 PAout(6)// PA6
#define LED3 PAout(7)// PA7

void Gpio_Init(void);//初始化函数
void Key_Scan(void);

int main(void)
{	
				  
	Rcc_Init(9); //系统时钟设置
	Gpio_Init(); //初始化与LED连接的硬件接口 

	while(1)
	{		
		Key_Scan();
	}
}

void Key_Scan(void)
{	 
	if(key0 == 0 || key1 == 0 || key2 == 0 ||key3 == 0)
	//if(GPIOA -> IDR != 0x000F)
	{
		 delay(10000);  	//去抖动
		 if(key0 == 0)
		 {
		 	while(key0 == 0);	   //检测按键松开
		 	LED0 = !LED0;
		 }
		 if(key1 == 0)
		 {
		    while(key1 == 0);
		 	LED1 = !LED1;
		 }
		 if(key2 == 0)
		 {
		 	while(key2 == 0);
		 	LED2 = !LED2;
		 }
		 if(key3 == 0)
		 {
		 	while(key3 == 0);
		 	LED3 = !LED3;
		 }
	 }
}

void Gpio_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟	   	 

	GPIOA->CRL&=0X0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
	GPIOA->CRL|=0X33334444; 
	  
}


 

 

库函数操作

 

学过EDA都应该知道一个概念叫状态机,触发某一条件后进入另一状态,再触发一个条件就进入下一状态,不满足条件就进入初态,或者不改变状态。实现按键扫描的思路,大致如此。

  1. Systick 产生一个20ms的定时,在中断中去查询各个管脚的按键是否按下。 有按键按下,进入状态1.

  2. 如果按下,判断是否是抖动,是则返回状态0,不是则判断是哪个管脚按键按下,实现相应功能后进入状态2.

  3. 在状态2中,检测按键是否松开,松开则返回状态0,否则不改变状态。

代码如下: main.c


#include "stm32f10x.h"

#define KEYPORT GPIOA
#define KEY0 GPIO_Pin_3
#define KEY1 GPIO_Pin_1
#define KEY2 GPIO_Pin_2
#define KEY3 GPIO_Pin_0

typedef enum
{
	KeyScanState_0 = 0x00,
	KeyScanState_1 = 0x01,
	KeyScanState_2 = 0x02,

}KeyScanState_Typedef;

KeyScanState_Typedef KeyScanState;

void RCC_Configuration(void);
void GPIO_Configuration(void);
void SysTick_Set(vu32 x);


int main(void)
{
  	RCC_Configuration();
  	GPIO_Configuration();
	SysTick_Set(20000);
	while(1);
}

void SysTick_Handler(void)
{
	vu16 keyState;
 
	keyState = GPIO_ReadInputData(KEYPORT) & 0x000f;
	switch(KeyScanState)
	{
	 	case KeyScanState_0:
		{
			if(keyState != 0x000f)
			{
				KeyScanState = KeyScanState_1;
			}
			break;
		}
		case KeyScanState_1:
		{
			if(keyState != 0x000f)
			{
				if(GPIO_ReadInputDataBit(KEYPORT,KEY0) == 0)
				{
					GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
				}else if(GPIO_ReadInputDataBit(KEYPORT,KEY1) == 0)
				{
					GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5)));
				}else if(GPIO_ReadInputDataBit(KEYPORT,KEY2) == 0)
				{
					GPIO_WriteBit(GPIOA,GPIO_Pin_6,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_6)));
				}else if(GPIO_ReadInputDataBit(KEYPORT,KEY3) == 0)
				{
					GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_7)));
				}

				KeyScanState = KeyScanState_2;
			}else{
			 	KeyScanState = KeyScanState_0;
			}
			break;
		}
		case KeyScanState_2:
		{
		 	if(keyState == 0x000f)
			{
			 	KeyScanState = KeyScanState_0;
			}
			break;
		} 
	}
}


void SysTick_Set(vu32 x)
{
	if(SysTick_Config(x*72))	 //配置错误返回1,max 16777216 
	{							
		GPIO_SetBits(GPIOA , GPIO_Pin_7); 	//错误处理 								
	}
}

  
void GPIO_Configuration(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;

  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 

  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
}


void RCC_Configuration(void)
{
	/* 定义枚举类型变量 HSEStartUpStatus */
	ErrorStatus HSEStartUpStatus;

  	/* 复位系统时钟设置*/
  	RCC_DeInit();
  	/* 开启HSE*/
  	RCC_HSEConfig(RCC_HSE_ON);
  	/* 等待HSE起振并稳定*/
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	/* 判断HSE起是否振成功,是则进入if()内部 */
  	if(HSEStartUpStatus == SUCCESS)
  	{
    	/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    	RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    	/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    	RCC_PCLK2Config(RCC_HCLK_Div1); 
    	/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    	RCC_PCLK1Config(RCC_HCLK_Div2);
    	/* 设置FLASH延时周期数为2 */
    	FLASH_SetLatency(FLASH_Latency_2);
    	/* 使能FLASH预取缓存 */
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    	/* 使能PLL */ 
    	RCC_PLLCmd(ENABLE);
    	/* 等待PLL输出稳定 */
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    	/* 选择SYSCLK时钟源为PLL */
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    	/* 等待PLL成为SYSCLK时钟源 */
    	while(RCC_GetSYSCLKSource() != 0x08);
  	} 
  	/* 打开APB2总线上的GPIOA时钟*/
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	//RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);		
}

本例中将Systick 中断处理函数从 stm32f10x_it.c中移至了main.c中 避免了需要在stm32f10x_it.c中声明外部变量等操作。


关键字:stm32  按键扫描 引用地址:stm32的按键扫描[操作寄存器+库函数]

上一篇:stm32 i2c通信 [操作寄存器+库函数]
下一篇:stm32上最方便的定时器Systick[操作寄存器+库函数]

推荐阅读最新更新时间:2024-03-16 15:32

ST和Arduino联手扩宽创客社区使用STM32微控制器和传感器的途径
2016年6月30日,致力于为创客提供更多创造空间,横跨多重电子应用领域、全球领先的半导体供应商意法半导体 (STMicroelectronics,简称ST)和教育、创客和物联网市场最大的开源生态系统Arduino公司,宣布了一项合作协议,让业界领先的STM32系列微控制器(MCU)以及意法半导体的传感器、功率器件和通信连接技术走近Arduino创客社区。STAR (ST和Arduino的前两个字母组合)开发项目的首款产品基于STM32F469的STAR Otto基板于近日向公众展出。现在,物联网开发人员和创客可以在他们的智能硬件内创建高性能图形功能,使用容易上手的软硬件开发好用的触屏和声控以及媒体流功能,改进他们的应用设计。
[物联网]
ST和Arduino联手扩宽创客社区使用<font color='red'>STM32</font>微控制器和传感器的途径
STM32 ST-LINK Utility介绍、下载、安装、使用方法
Ⅰ、写在前面 本文讲述的内容是STM32 ST-LINK Utility介绍、下载、安装、使用方法,如需要了解更多关于STM32相关的文章,可以到我博客,或微信公众号查看并下载。 STM32 ST-LINK Utility这个软件工具其实主要就是配套“ST-LINK”这个下载工具一起使用的上位机软件。因此使用STM32 ST-LINK Utility上位机软件需要有一个ST-LINK工具才行。它的功能和J-Link对应的工具类似,用于烧写代码。 STM32 ST-LINK Utility工具在产品开发过程中测试一些其他版本的代码,可以直接下载hex,而不用打开工程再编译去下载。 当你开发完一个STM32产品,需要量产的
[单片机]
<font color='red'>STM32</font> ST-LINK Utility介绍、下载、安装、使用方法
STM32如何产生PWM信号
本文实践用到的芯片为:STM32F103VET6。文中所述内容符合同系列芯片的应用。本文适合初学者阅读。 上面是在STM32引脚PA0上测得的频率为1KHz占空比为25%的PWM信号。下面讲如何实现。 如上图所示,我们使用定时器2通道1产生PWM。 执行思想:1.需要把引脚配置为复用推挽输出;2.需要配置定时器2。 1.配置引脚 我们用库函数实现的话,肯定要涉及到GPIO初始化配置结构体,所以需要定义一个GPIO_InitTypeDef类型的结构体变量,然后把变量成员配置好。 2.配置定时器2 我们用库函数的话,一是要涉及到定时器的基本配置结构体,所以需要定义一个TIM_TimeBaseInitTypeDef类型的结构体变
[单片机]
<font color='red'>STM32</font>如何产生PWM信号
STM32学习笔记之低功耗模式的机制
本文主要解读STM32低功耗模式的机制,并不侧重STM32低功耗的程序实现,而且借助STM32固件库实现STM32低功耗会变的非常简单。 一、STM32芯片性能 使用芯片型号:stm32,CORTEX -M0.封装TSSOP20. 运行模式:内部时钟(HSI),系统时钟频率采用48MHZ。 工作电压:3.3V 芯片具体参数如下: 二、芯片功耗 功耗: 芯片工作模式: 工作模式:外设正常运行,内核CPU及SRAM供电,未使用外设的时钟默认关闭。 睡眠模式:只有CPU停止工作,各个外设正常工作,依靠任何中断/事件唤醒。 停机模式:1.8V供电区域时钟被停止,内部HSI,PLL,外部时钟HSE均关闭,同时电压检测器
[单片机]
<font color='red'>STM32</font>学习笔记之低功耗模式的机制
stm32 上电初始化串口输出一个字节FF问题
最近玩stm32,使用串口发送数据在PC端使用串口工具检测接收到的数据,发现每次上电串口工具都会蹦出一个FF,这让我郁闷好久。在网上查了好多解决问题的办法,有的说先初始化UART 在初始化UART对应的GPIO脚,有的说把中断关闭等等 。我试了都不行,串口还是会发送FF,简直郁闷。。。。 后来我单步调试,发现在初始化的时候函数GPIO_PinAFConfig();初始化导致串口上电在TX脚上输出一个高电平。所以我就尝试在函数GPIO_Iinit();初始化之前首先初始化GPIO_PinAFConfig();这样就不会出现FF了。。。 总之解决办法如下就不会出现问题: 1.开启IO和外设USART时钟
[单片机]
STM32的16位编码器溢出问题
STM32定时器有编码器接口,但是它的计数器只有16位。当要记录的数过大时,会溢出。下文介绍了一种方法,能有效解决因计数器位数过少引起的溢出问题。 (在网上搜了好多,感觉不他们说的方法都不准。这个方法经过我自己验证,可以准确记录编码器的位置) 原理一: unsigned short int j;(j的长度为16bit) ① 当j=65535 ,运行j++后,j=0; ② 当j=0 , 运行j- -后,j=65535; 定时器的16位计数器寄存器(CNT)同样符合上面的逻辑。在编码器模式时, (一)当加计数时(up计数),加到65535后,再加1,CNT的值变为0,且溢出标志位 被置1(UIF=1),
[单片机]
STM32和ROS机器人的串口通信方案
具体协议大致如下,易读、易调用、易拓展、易更改。 源码文件:进入下面公众号:小白学移动机器人,发送:串口通信升级。即可获得。 http://weixin.qq.com/r/KERAWIvE1daqrc879xE6 (二维码自动识别) 本方案解决的问题:解决以STM32做ROS机器人底层驱动的串口通信问题。 为什么要写篇文章?: 最近发现越来越多的小伙伴走入ROS机器人的领域,而ROS机器人与底层驱动的串口通信问题,是大家学习路上的一个难题。很多小伙伴对STM32单片机并不熟悉,对串口通信的理解并不透彻,自己去解决这个问题,费时费力,最后也可能没有好的结果,并且这又不是大多数学习ROS机器人的重点。最后发现网上也没有很好的
[单片机]
<font color='red'>STM32</font>和ROS机器人的串口通信方案
STM32低功耗唤醒方式
MCU进入低功耗之后,以极低的功耗维持着系统“活着”,但是醒过来是需要一定条件的,比如定个“闹钟”,按键“按一下”等,目前常用的“正常的”唤醒方式有以下几种: 1、RTC定时唤醒; 2、外部中断唤醒(按键或者通讯唤醒); 3、特殊唤醒引脚唤醒(某些引脚具有专门的唤醒功能)。 下面,我们再来看一看如何通过RTC和外部中断唤醒MCU。 1、RTC定时唤醒 依然是从手册中我们可以看到,所有的RTC时间都可以把MCU从低功耗模式中唤醒: 介绍使用RTC的定时功能实现,定时1S唤醒一次,使用cubemx进行对RTC进行配置: 生成代码的时候,勾选这个选项,可以把不用的引脚配置为模拟输入模式,降低功耗: RTC的配置如图,使能RTC,
[单片机]
<font color='red'>STM32</font>低功耗唤醒方式
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved