矩阵键盘扫描——状态机方法(正确)

/********************************配置IO为上拉输入********************************************/
#define configInputPullUp(port, pin, GPIO_InitStruct) { \
/*        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); */ \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_INPUT ; \
        (GPIO_InitStruct)->Pull = GPIO_PULLUP ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
}

/********************************配置IO为推挽输出低电平********************************************/
#define configOutputLow(port, pin, GPIO_InitStruct) { \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_OUTPUT_PP ; \
        (GPIO_InitStruct)->Pull = GPIO_NOPULL ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET);  \
}
u8 Key_Value;

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
 while (1)
  {
  
	while(App_LEAVE != currState->loop());
	// Button released   (检测到了松开,就执行动作)
	if(currState == &colScanningPressed)
	{
		printf("KEY:%d \r\n",Key_Value); 
	}
	// Next state(执行完当前状态就应该把状态转换,到下个状态)
	//(可以看出,currState,作为一个指针结构体,就是一个指针的作用,存储当前状态用于判断)
	currState = currState == &rowScanning ? &colScanning : currState == &colScanning ? &colScanningPressed : &rowScanning;
	//如果切换到下一个状态,则调用其 enter() 函数
	currState->enter();	
}
	

}


或者main中也可以用这个
	currState=&rowScanning;
		currState->enter();	
	while(App_LEAVE != currState->loop());
//	if(0)  //(检测是否可以离开当前的状态,不可以就会推出当前状态)
//	{                                 //就是调用currstate的loop函数,得到返回值
//		return 0;
//	}
	// Button released   (检测到了松开,就执行动作)
		
	currState= &colScanning;
		currState->enter();	
	currState->loop();
	

	currState= &colScanningPressed;
		currState->enter();	
//		printf("aaaaaaa\r\n");
//	while(App_LEAVE != currState->loop()){
//		//printf("%d   \r\n",);
//	}
	while(App_LEAVE != currState->loop());
	printf("KEY:%d \r\n",Key_Value); 

#include "Key.h"

/*****************************************************************

对于行扫描状态,进入该状态时,应该对行、列的I/O口进行设置。
也即,在其enter() 实现中设置行I/O口为input模式,并启用其内部上拉电阻;列I/O为output模式,并输出0。
其 loop() 实现则依次检测行I/O口是否读数为0,

若读数为0,则表明该行有按键按下,记下行号,并离开本状态:

程序中使用了一个字节型全局变量 key 用来保存键值,
其第2-3位为行号(0-3),第0-1位为列号(0-3),因此,key 的值为0-0x0F,依次对应16个按键。
因为是状态机,这里设置的是行和列为两个状态机,每个状态机为4个状态,分别为0-3,对应三个按键按下

***这样最后key_value就是一个4位二进制数,值对应着按键0-15

第三阶段无需改变I/O口设置,只需检测被按下按键所在的列是否读取pin值为1。
读取pin值为1表明按键被松开,应该离开此状态,切换回行扫描状态:


*****************************************************************/

extern u8 Key_Value;

APP_ScanningState rowScanning = {rowScanningEnter, rowScanningLoop };
APP_ScanningState colScanning = {colScanningEnter, colScanningLoop  };
APP_ScanningState colScanningPressed = {colScanningPressedEnter, colScanningPressedLoop };

APP_ScanningState *currState = &rowScanning;


void rowScanningEnter()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	configInputPullUp(ROW3_GPIO_Port,ROW3_Pin,&GPIO_InitStruct);
	configInputPullUp(ROW2_GPIO_Port,ROW2_Pin,&GPIO_InitStruct);
	configInputPullUp(ROW1_GPIO_Port,ROW1_Pin,&GPIO_InitStruct);
	configInputPullUp(ROW0_GPIO_Port,ROW0_Pin,&GPIO_InitStruct);
	
	configOutputLow(COL3_GPIO_Port,COL3_Pin,&GPIO_InitStruct);
	configOutputLow(COL2_GPIO_Port,COL2_Pin,&GPIO_InitStruct);
	configOutputLow(COL1_GPIO_Port,COL1_Pin,&GPIO_InitStruct);
	configOutputLow(COL0_GPIO_Port,COL0_Pin,&GPIO_InitStruct);
}

//按键消抖要做好
GPIO_PinState checkPressedLow(GPIO_TypeDef *port, uint16_t pin)
{
		if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin))
		{
			 HAL_Delay(DEBOUNCE_DELAY);
			if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin))
			return HAL_GPIO_ReadPin(port, pin);
		}
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(port, pin))
		{
			 HAL_Delay(DEBOUNCE_DELAY);
			if(GPIO_PIN_SET == HAL_GPIO_ReadPin(port, pin))
			return HAL_GPIO_ReadPin(port, pin);
		}
		 return GPIO_PIN_SET;
}
	
	
uint8_t rowScanningLoop()
{
	if(GPIO_PIN_RESET == checkPressedLow(ROW0_GPIO_Port,ROW0_Pin))
	{
		Key_Value = 0;
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(ROW1_GPIO_Port,ROW1_Pin))
	{
		Key_Value = 1<<2; //高2位存储行值
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(ROW2_GPIO_Port,ROW2_Pin))
	{
		Key_Value = 2<<2; //高2位存储行值
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(ROW3_GPIO_Port,ROW3_Pin))
	{
		Key_Value = 3<<2; //高2位存储行值
    return App_LEAVE;
	}
	 return App_STAY;
}
void colScanningEnter()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	configOutputLow(ROW3_GPIO_Port,ROW3_Pin,&GPIO_InitStruct);
	configOutputLow(ROW2_GPIO_Port,ROW2_Pin,&GPIO_InitStruct);
	configOutputLow(ROW1_GPIO_Port,ROW1_Pin,&GPIO_InitStruct);
	configOutputLow(ROW0_GPIO_Port,ROW0_Pin,&GPIO_InitStruct);
	
	configInputPullUp(COL3_GPIO_Port,COL3_Pin,&GPIO_InitStruct);
	configInputPullUp(COL2_GPIO_Port,COL2_Pin,&GPIO_InitStruct);
	configInputPullUp(COL1_GPIO_Port,COL1_Pin,&GPIO_InitStruct);
	configInputPullUp(COL0_GPIO_Port,COL0_Pin,&GPIO_InitStruct);

}

uint8_t colScanningLoop()
{
	if(GPIO_PIN_RESET == checkPressedLow(COL0_GPIO_Port,COL0_Pin))
	{
		Key_Value |= 0;
		//printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL1_GPIO_Port,COL1_Pin))
	{
		Key_Value |= 1; //低两位存储列值
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL2_GPIO_Port,COL2_Pin))
	{
		Key_Value |= 2; //低两位存储列值
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL3_GPIO_Port,COL3_Pin))
	{
		Key_Value |= 3; //低两位存储列值
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	 return App_STAY;
}

//第三阶段无需改变I/O口设置,
//只需检测被按下按键所在的列是否读取pin值为1
//读取pin值为1表明按键被松开,应该离开此状态,切换回行扫描状态
void colScanningPressedEnter()
{
	colScanningEnter();
}

uint8_t colScanningPressedLoop()
{
	int col = 3 & Key_Value; //读取列值
	//可以改成switchcase
	if(0 == col)
	{
		if(GPIO_PIN_SET == checkPressedLow(COL0_GPIO_Port, COL0_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else if(1 == col)
	{
		if(GPIO_PIN_SET == checkPressedLow(COL1_GPIO_Port, COL1_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else if(2 == col)
	{
		if(GPIO_PIN_SET == checkPressedLow(COL2_GPIO_Port, COL2_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else 
	{
		if(GPIO_PIN_SET == checkPressedLow(COL3_GPIO_Port, COL3_Pin)) 
		{
			return App_LEAVE;
		}
	}
	
	return App_STAY;
}






#ifndef __KEY_H__
#define __KEY_H__

#include "main.h"
#include "gpio.h"

//==使用“状态机”设计模式
//==结构体 App_ScanningState 表示1个状态.
//==当进入该状态时,调用其 (函数指针)成员enter() 
//==在程序主循环中,则调用其 loop() 成员
//==loop() 函数返回值为 App_STAY 或 App_LEAVE,若返回前者,则表明应该停留在该状态,下次主循环将再次调用此状态的 loop() 函数;
//==反之,若返回后者,则表明应该切换到下一个状态。

typedef struct{
	void (*enter)();
	u8 (*loop)();
} APP_ScanningState;

#define App_STAY 0
#define App_LEAVE 1

//在读取pin值时,为了de-bouncing,增加了一个5ms的延时重读。一般,de-bouncing延时取5-10ms。
#define DEBOUNCE_DELAY 10

//==rowScanning, colScanning, colScanningPressed 3个App_ScanningState实例,分别为行扫描阶段、列扫描阶段及第三阶段(检测按键松开)。

void rowScanningEnter(void);
uint8_t rowScanningLoop(void);
void colScanningEnter(void);
uint8_t colScanningLoop(void);
void colScanningPressedEnter(void);
uint8_t colScanningPressedLoop(void);


extern APP_ScanningState rowScanning ;
extern APP_ScanningState colScanning;
extern APP_ScanningState colScanningPressed;

extern APP_ScanningState *currState;


#endif

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值