GD32F303 按键常用操作

目录

一、按键初始化相关代码

1、队列相关初始化

2、按键硬件相关初始化

3、按键逻辑层相关函数

二、按键的使用

1、硬件初始化

2、按键扫描:一般放在定时器 或者 while(1)

3、读取按键结果


一、按键初始化相关代码

1、队列相关初始化

/* uqueue.h 队列相关头文件 */
#ifndef _UQUEUE_H_
#define _UQUEUE_H_

#include "stdint.h"

#define UQUEUE_MAX_LEN	32

typedef struct{
	char *buff;
	int head;
	int size;
	int msize;
}UqueueType;

int UqueueCreate(UqueueType *queue, void *qbuff, int msize);
int UqueueLen(UqueueType *queue);
char UqueuePush(UqueueType *queue, char c);
char UqueuePop(UqueueType *queue);
char UqueueAt(UqueueType *queue, int index);
int UqueueWrite(UqueueType *queue, const char *buff, int num);
int UqueueRead(UqueueType *queue, char *buff, int num);
void UqueueClear(UqueueType *queue);
void UqueueThrow(UqueueType *queue, int num);

#endif

/* uqueue.c 队列相关 .c 文件 */
#include "uqueue.h"
#include <string.h>

#ifndef NULL
#define NULL	0
#endif

/**
 * 创建一个队列
 * queue - 需要初始化的队列
 * qbuff - 队列数据存储位置
 * msize - 队列最大长度
 * 返回:是否操作成功,0-失败,1-成功
 */
int UqueueCreate(UqueueType *queue, void *qbuff, int msize)
{
	if(queue==NULL || qbuff==NULL || msize==0) return 0;

	queue->buff = qbuff;
	queue->head=0;
	queue->size=0;
	queue->msize = msize;

	return 1;
}

/**
 * 获取队列长度
 * queue - 队列
 * 返回:队列长度
 */
int UqueueLen(UqueueType *queue)
{
	if(queue==NULL) return 0;
	
	return queue->size;
}

/**
 * 向队列压入数据
 * queue - 队列
 * c - 压入的数据
 * 返回:是否操作成功,0-失败,1-成功
 */
char UqueuePush(UqueueType *queue, char c)
{
	if(queue==NULL) return 0;

	if(queue->size<queue->msize){
		queue->buff[(queue->head+queue->size)%queue->msize]=c;
		queue->size++;
		return 1;
	}
	else{
		return 0;
	}
}

/**
 * 弹出数据
 * queue - 队列
 * 返回:弹出的数据
 */
char UqueuePop(UqueueType *queue)
{
	if(queue==NULL || queue->size==0) return 0;

	char c = queue->buff[queue->head++];
	queue->head = queue->head%queue->msize;
	queue->size--;
	return c;
}

/**
 * 读取队列中的一个数据
 * queue - 队列
 * index - 数据相对于队列头的偏移
 * 返回:读到的数据
 */
char UqueueAt(UqueueType *queue, int index)
{
	if(queue==NULL || queue->size<=index || queue->size<=0) return 0;

	return queue->buff[(queue->head+index)%queue->msize];
}

/**
 * 写入几个数据
 * queue - 队列
 * buff - 数据缓存
 * num - 写入数据量
 * 返回:写入的数据量
 */
int UqueueWrite(UqueueType *queue, const char *buff, int num)
{
    if(queue==NULL || buff==NULL || num==0) return 0;
    
    num = num>(queue->msize - queue->size) ? (queue->msize - queue->size):num;
    
    if(((queue->head+queue->size)%queue->msize+num)/queue->msize){
		int rem = (queue->head+queue->size+num)%queue->msize;
		memcpy(&queue->buff[(queue->head+queue->size)%queue->msize], buff, num-rem);
		memcpy(queue->buff, &buff[num-rem], rem);
	}
	else{
		memcpy(&queue->buff[(queue->head+queue->size)%queue->msize], buff, num);
	}
	queue->size+=num;

	return num;
}

/**
 * 读出几个数据
 * queue - 队列
 * buff - 数据缓存
 * num - 读出数据量
 * 返回:实际读到的数据量
 */
int UqueueRead(UqueueType *queue, char *buff, int num)
{
	if(queue==NULL || buff==NULL || num==0) return 0;

	num = num>queue->size ? queue->size:num;

	if((queue->head+num)/queue->msize){
		int rem = (queue->head+num)%queue->msize;
		memcpy(buff, &queue->buff[queue->head], num-rem);
		memcpy(&buff[num-rem], queue->buff, rem);
	}
	else{
		memcpy(buff, &queue->buff[queue->head], num);
	}

	return num;
}

/**
 * 清空队列
 * queue - 队列
 */
void UqueueClear(UqueueType *queue)
{
	if(queue==NULL) return;

	queue->head=0;
	queue->size=0;
}

/**
 * 弹出数个数据并丢弃
 * queue - 队列
 * num - 丢弃的数据个数
 */
void UqueueThrow(UqueueType *queue, int num)
{
	if(queue==NULL) return;

	num = queue->size>num ? num:queue->size;

	queue->size-=num;
	queue->head = (queue->head+num)%queue->msize;
}

2、按键硬件相关初始化

/* keys.h 文件 */
#ifndef __KEYS_H_
#define __KEYS_H_

#include "gd32f30x.h"

void KeyInit(void);
uint8_t KeyOxygenStartRead(void);
uint8_t KeyTimePlusRead(void);
uint8_t KeyTimeDivRead(void);


#endif

/* keys.c 文件 */
#include "keys.h"

#define KEY_PORT	              GPIOD
#define KEY_OXYGEN_START_PIN	  GPIO_PIN_2
#define KEY_TIME_PLUS_PIN       GPIO_PIN_0
#define KEY_TIME_DIV_PIN        GPIO_PIN_1


void KeyInit(void)
{
	rcu_periph_clock_enable(RCU_GPIOD);

	gpio_init(KEY_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, KEY_OXYGEN_START_PIN | KEY_TIME_PLUS_PIN | KEY_TIME_DIV_PIN);
}

uint8_t KeyOxygenStartRead(void)
{
    return gpio_input_bit_get(KEY_PORT, KEY_OXYGEN_START_PIN);
}

uint8_t KeyTimePlusRead(void)
{
    return gpio_input_bit_get(KEY_PORT, KEY_TIME_PLUS_PIN);
}

uint8_t KeyTimeDivRead(void)
{
    return gpio_input_bit_get(KEY_PORT, KEY_TIME_DIV_PIN);
}

3、按键逻辑层相关函数

/* KeyScan.h 文件 */
#ifndef __KEY_SCAN_H_
#define __KEY_SCAN_H_

#include "gd32f30x.h"


//按键ID
typedef enum{
    UQUEUE_KEY_NONE=0,
    UQUEUE_KEY_OXYGEN_START,
    UQUEUE_KEY_TIME_PLUS,
    UQUEUE_KEY_TIME_DIV,
    
    UQUEUE_KEY_MAX_NUM,
	
}UQUEUE_KEY_ID;

//按键状态
typedef enum{
    UQUEUE_KEY_STATUS_IDLE=0,
    UQUEUE_KEY_STATUS_SHORT_PRESS,
	  UQUEUE_KEY_STATUS_DOUBLE_SHORT_PRESS,
    UQUEUE_KEY_STATUS_LONG_PRESS,
    
    UQUEUE_KEY_MAX_STATUS,
	
}UQUEUE_KEY_STATUS;

//按键模式:单击 长按,单击 长按 双击,单击 连按
typedef enum{  
	  UQUEUE_KEY_NONE_MODE = 0,     //不使用按键
    UQUEUE_KEY_GENERAL_MODE,      //常规模式:  单击 长按
    UQUEUE_KEY_DOUBLE_MODE,       //带双击模式:单击 长按 双击,
	  UQUEUE_KEY_CONTINUS_MODE,     //连按模式:  单击 连按
}UQUEUE_KEY_MODE;



typedef struct{
    UQUEUE_KEY_ID      id;         //按键id
    UQUEUE_KEY_STATUS  status;     //按键状态
}KeyValType;

void KeyScanInit(void);
void KeyScan(uint16_t xms);
KeyValType KeyScanRead(void);


uint8_t KeyCombinationRead(UQUEUE_KEY_ID keyId1, UQUEUE_KEY_ID keyId2);
uint8_t KeyFind(KeyValType key);
void KeyScanClear(void);
uint8_t KeyCount(void);
void KeyAdd(KeyValType key);

#endif

/* KeyScan.c 文件*/
#include "KeyScan.h"
#include "keys.h"
#include "uqueue.h"

#include <stdint.h>

//长按短按时间判定
#define SHORT_KEY_TIME                0   //20ms
#define DOUBLE_SHORT_KEY_TIME        300  //第一次按下规定的松开时间范围
#define CLICK_DOUBLE_SHORT_KEY_TIME  400  //第一次短按之后,第二次要到来的时间范围
#define LONG_KEY_TIME                2000

//触发连按的阈值和连按间隔
#define CONTINUE_THRESHOLD   800
#define CONTINUE_INTERVAL    200


    
#define PRESS   0


typedef struct{
    KeyValType KeyVal;
    
    UQUEUE_KEY_MODE  KeyMode;           //连续触发模式
    uint16_t         PressTime;         //按下时间累加
	  uint8_t          PressCnt;          //按下次数
	  uint16_t         ReleaseTime;       //松开按键时间累加
    
    uint8_t (*KeyRead)(void);   //按键IO状态读取
}KeyCtr_t;


/* 注意:组合键只有在两个按键都是在常规模式的时候才能使用 */
//按键列表
KeyCtr_t KeyCtrMap[] = 
{
    {{UQUEUE_KEY_OXYGEN_START, UQUEUE_KEY_STATUS_IDLE}, UQUEUE_KEY_GENERAL_MODE, 0, 0, 0, KeyOxygenStartRead},
    {{UQUEUE_KEY_TIME_PLUS, UQUEUE_KEY_STATUS_IDLE}, UQUEUE_KEY_DOUBLE_MODE, 0, 0, 0, KeyTimePlusRead},
    {{UQUEUE_KEY_TIME_DIV, UQUEUE_KEY_STATUS_IDLE}, UQUEUE_KEY_GENERAL_MODE, 0, 0, 0, KeyTimeDivRead},
};



#define KeyCtrMapNumber   (sizeof(KeyCtrMap)/sizeof(KeyCtrMap[0]))
	
/* KEY_QUEUE_MSIZE:若能快速读取队列,队列可以只有一个成员;
                    若有组合键,队列成员至少要有两个以上 */
#define KEY_QUEUE_MSIZE  (KeyCtrMapNumber*sizeof(KeyValType))




static UqueueType KeyQueue;
static uint8_t KeyQueueBuff[KEY_QUEUE_MSIZE];

static uint8_t KeyVailCheck(KeyValType key);
static void KeyValInit(KeyValType *key);
static uint8_t KeyIsIdle(void);

/*!
    \brief      初始化按键扫描功能
    \param[in]  none
    \param[out] none
    \retval     none
*/
void KeyScanInit(void)
{
    KeyInit();
    UqueueCreate(&KeyQueue, KeyQueueBuff, KEY_QUEUE_MSIZE);
}

/*!
    \brief      按键扫描函数,需要定时器循环调用
    \param[in]  nms - 调用间隔
    \param[out] none
    \retval     none
*/



void KeyScan(uint16_t xms)
{
    KeyCtr_t *Keyn;
    
    for(int i=0; i<sizeof(KeyCtrMap)/sizeof(KeyCtrMap[0]); i++)
    {
        Keyn = &KeyCtrMap[i];
        //常规模式
        if(Keyn->KeyMode == UQUEUE_KEY_GENERAL_MODE)
        {
            //按键按下
            if(Keyn->KeyRead() == PRESS)
            {
                if(Keyn->PressTime < LONG_KEY_TIME)
                {
                    Keyn->PressTime += xms;
                }
                //长按(时间判定)
                else if(Keyn->KeyVal.status != UQUEUE_KEY_STATUS_LONG_PRESS)
                {
                    Keyn->KeyVal.status = UQUEUE_KEY_STATUS_LONG_PRESS;
                    UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
                }
            }
            //抬起
            else
            {
                if(Keyn->PressTime > 0)
                {
										//短按
										if(Keyn->PressTime >= SHORT_KEY_TIME && Keyn->PressTime < LONG_KEY_TIME)
										{
												Keyn->KeyVal.status = UQUEUE_KEY_STATUS_SHORT_PRESS;
												UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
										}
										
										Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
										Keyn->PressTime = 0;
                }
            }
        }    
				 //带双击模式
				else if(Keyn->KeyMode == UQUEUE_KEY_DOUBLE_MODE)
				{
				     //按键按下
            if(Keyn->KeyRead() == PRESS)
            {
                if(Keyn->PressTime < LONG_KEY_TIME)
                {
                    Keyn->PressTime += xms;
                }
                //长按(时间判定)
                else if(Keyn->KeyVal.status != UQUEUE_KEY_STATUS_LONG_PRESS)
                {
                    Keyn->KeyVal.status = UQUEUE_KEY_STATUS_LONG_PRESS;
                    UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
                }
            }
            //抬起
            else
            {
                if(Keyn->PressTime > 0)
                {
									  //双击
                    if((Keyn->PressTime >= SHORT_KEY_TIME && Keyn->PressTime < DOUBLE_SHORT_KEY_TIME) 
											  || (Keyn->ReleaseTime != 0))
                    {
											    Keyn->PressCnt++;
											    
											    if(Keyn->PressCnt >= 2)
													{
															Keyn->PressCnt = 0;
														  Keyn->ReleaseTime = 0;
														   
						                  Keyn->KeyVal.status = UQUEUE_KEY_STATUS_DOUBLE_SHORT_PRESS;
                              UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));  
													}
											
													Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
													Keyn->PressTime = 0;
                    }
										else
										{
										    //短按
												if(Keyn->PressTime >= SHORT_KEY_TIME && Keyn->PressTime < LONG_KEY_TIME)
												{
														Keyn->KeyVal.status = UQUEUE_KEY_STATUS_SHORT_PRESS;
														UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
												}
												
												Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
												Keyn->PressTime = 0;
												Keyn->PressCnt = 0;
										}
                }
								
								
								//第一次按下之后,计时
								if(Keyn->PressCnt>0)
								{
									 Keyn->ReleaseTime++;
									 
									 if(Keyn->ReleaseTime > CLICK_DOUBLE_SHORT_KEY_TIME)
									 {
											 //超出规定时间没有再次按下,按短按处理
											 Keyn->KeyVal.status = UQUEUE_KEY_STATUS_SHORT_PRESS;
											 UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
										 
											 Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
											 Keyn->PressTime = 0;
											 Keyn->ReleaseTime = 0;
											 Keyn->PressCnt = 0;
									 }
								}
								
            }
				}
				//连按模式
        else if(Keyn->KeyMode == UQUEUE_KEY_CONTINUS_MODE)
        {
            //按键按下
            if(Keyn->KeyRead()==PRESS)
            {
                Keyn->PressTime+=xms;
                //首次按下
                if(Keyn->PressTime == xms)
                {
                    Keyn->KeyVal.status = UQUEUE_KEY_STATUS_SHORT_PRESS;
                    UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
                }
                //触发连按
                else if(Keyn->PressTime >= CONTINUE_THRESHOLD+CONTINUE_INTERVAL)
                {
                    Keyn->KeyVal.status = UQUEUE_KEY_STATUS_SHORT_PRESS;
                    UqueueWrite(&KeyQueue, (char *)&Keyn->KeyVal, sizeof(Keyn->KeyVal));
                    Keyn->PressTime = CONTINUE_THRESHOLD;
                }
            }
            //抬起
            else
            {
                if(Keyn->PressTime > 0)
                {
                    Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
                    Keyn->PressTime = 0;
                }
            }
        }	
				else
				{
				     Keyn->KeyVal.status = UQUEUE_KEY_STATUS_IDLE;
             Keyn->PressTime = 0;
				}
    }
		
}


/*!
    \brief      读取按键
    \param[in]  none
    \param[out] none
    \retval     返回按键值和状态。按键值为KEY_NONE为没有按键事件
*/
KeyValType KeyScanRead(void)
{
    uint16_t qlen = UqueueLen(&KeyQueue);
    KeyValType key;
    
    KeyValInit(&key);
    
    if(qlen > 0)
    {
        //队列数据格式不正确,清零
        if((qlen % sizeof(KeyValType)) > 0)
        {
            UqueueClear(&KeyQueue);
        }
        else
        {
            UqueueRead(&KeyQueue, (char *)&key, sizeof(key));
            //为了实现组合按键,所有普通按键都抬起,才能判定短按
            if(key.status != UQUEUE_KEY_STATUS_SHORT_PRESS || KeyIsIdle())
            {
                UqueueThrow(&KeyQueue, sizeof(key));
            
                //按键值无效
                if(!KeyVailCheck(key))
                {
                    KeyValInit(&key);
                }
            }
            else
            {
                KeyValInit(&key);
            }
        }
    }
    
    return key;
}

/*!
    \brief      组合按键,两个普通按键按下时间有交叉,且都没有超过长按时间,判定为组合按键
    \param[in]  keyId1 - 按键1
    \param[out] keyId2 - 按键2
    \retval     返回是否存在按键组合
*/
uint8_t KeyCombinationRead(UQUEUE_KEY_ID keyId1, UQUEUE_KEY_ID keyId2)
{
    uint8_t pressSta = 0, qkey1, qkey2;
    KeyValType qkey[2];
    
    uint16_t qlen = UqueueLen(&KeyQueue);
    
    if((qlen/sizeof(KeyValType)) == 2 
        && keyId1 != keyId2 
        && KeyIsIdle()) 
    {
        UqueueRead(&KeyQueue, (char *)qkey, sizeof(qkey));
        
        if(qkey[0].status == UQUEUE_KEY_STATUS_SHORT_PRESS
            && qkey[1].status == UQUEUE_KEY_STATUS_SHORT_PRESS
            && qkey[0].id != qkey[1].id
            && (keyId1 == qkey[0].id || keyId1 == qkey[1].id)
            && (keyId2 == qkey[0].id || keyId2 == qkey[1].id))
        {
            UqueueClear(&KeyQueue);
            return 1;
        }
    }
    
    return 0;
}

/*!
    \brief      键值查找
    \param[in]  key - 键值
    \retval     返回是否存在该键值
*/
uint8_t KeyFind(KeyValType key)
{
    KeyValType Keyn;
    
    uint16_t kcount = KeyCount();
    
    if(kcount > 0)
    {
        for(int i=0; i<kcount; i++)
        {
            uint8_t res = 1;
            for(int j=0; j<sizeof(KeyValType); j++)
            {
                if(((char *)&key)[j] != UqueueAt(&KeyQueue, j+i*sizeof(KeyValType)))
                {
                    res = 0;
                    break;
                }
            }
            if(res == 1) return 1;
        }
    }
    
    return 0;
}

/*!
    \brief      清空按键队列和按键按下累计时间
    \param[in]  none
    \param[out] none
    \retval     none
*/
void KeyScanClear(void)
{
    KeyCtr_t *Keyn;
    
    for(int i=0; i<sizeof(KeyCtrMap)/sizeof(KeyCtrMap[0]); i++)
    {
        Keyn = &KeyCtrMap[i];
        
        Keyn->PressTime=0;
    }
    
    UqueueClear(&KeyQueue);
}

uint8_t KeyCount(void)
{
    return (UqueueLen(&KeyQueue)/sizeof(KeyValType));
}

/*!
    \brief      手动添加一个按键事件
    \param[in]  key - 键值
    \retval     none
*/
void KeyAdd(KeyValType key)
{
    UqueueWrite(&KeyQueue, (char *)&key, sizeof(key));
}

static uint8_t KeyVailCheck(KeyValType key)
{
    if(key.id > 0 && key.id < UQUEUE_KEY_MAX_NUM)
    {
        if(key.status > 0 && key.status < UQUEUE_KEY_MAX_STATUS)
        {
            return 1;
        }
    }
    
    return 0;
}

static void KeyValInit(KeyValType *key)
{
    key->id = UQUEUE_KEY_NONE;
    key->status = UQUEUE_KEY_STATUS_IDLE;
}

static uint8_t KeyIsIdle(void)
{
    KeyCtr_t *Keyn;

    for(int i=0; i<sizeof(KeyCtrMap)/sizeof(KeyCtrMap[0]); i++)
    {
        Keyn = &KeyCtrMap[i];
        
        if(Keyn->KeyRead() == PRESS && Keyn->KeyMode == UQUEUE_KEY_GENERAL_MODE)
        {
            return 0;
        }
    }
    
    return 1;
}

二、按键的使用

1、硬件初始化

KeyScanInit();  //硬件初始化

2、按键扫描:一般放在定时器 或者 while(1)

KeyScan(1);  //循环定时 1ms 的地方调用

3、读取按键结果


while(1)
{
    /* 读取组合按键,必须配合 KeyScanRead() 一起使用。使用时,可以不响应 KeyScanRead() 的结果 */
    if(KeyCombinationRead(UQUEUE_KEY_OXYGEN_START, UQUEUE_KEY_TIME_DIV)
    {
	    printf("KeyCombination!\r\n");
    }
		
    /* 读取单击,双击,长按,连按结果 */	
    Keyn = KeyScanRead();
		
    if((Keyn.id != UQUEUE_KEY_NONE) && (Keyn.status != UQUEUE_KEY_STATUS_IDLE))
    {
        printf("Keyn.id = %d\r\n", Keyn.id);
        printf("Keyn.status = %d\r\n", Keyn.status);				 
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值