ad-矩阵-io口多按键驱动

按键实现思路

多按键驱动的思路和之前的io单按键,思路类似单按键是判断按键是按下,多按键判断是否有按键按下,按下的是哪个按键号,下面以最近工作用使用的ad按键为例
整个驱动一共4个c文件(键值、扫描、任务、驱动),6个h文件
核心思路是定时器中每10ms扫描一次按键,将按键值传入扫描函数,获得有效按键的按键号和按键状态,抄表将按键号按键状态两个变量转换成一个变量,将这个有效值传入队列,在主函数中出队列,将按键有效值作为参数哦传递给函数指针数组调用对应的任务函数,整个驱动没有使用一个与ic芯片有关的头文件,函数等,十分方便移植(整个驱动的思路来源于之前的几个项目,抄表是杰里的按键驱动,按键扫描代码是自己撸的,队列做数据透传的时候写的),可能程序的效率和代码量不够理想,但整体功能能比较好实现,最后面将会把10个文件全部贴出,附带完整工程下载
在这里插入图片描述

驱动分析:

一、有效键值

主要文件key_ad.c .h
h文件将每个按键对应电压的ad转换值算出来,选用两个键值的中值做作为有效键值判断
c文件,将h文件中的键值作为数组的元素,将获取的按键值和表对比获取获得按键号

#define R_UP       220L     //22K
/* 不同电压对应的ad值 12bit adc */
#define ADC_33   (4096)		//VCC
#define ADC_30   (3680)     //220K
#define ADC_27   (3357)     //100K
#define ADC_23   (2860)     //51K
#define ADC_20   (2450)     //33K
#define ADC_17   (2120)     //24K
#define ADC_13   (1610)     //15K
#define ADC_10   (1210)     //9.1K
#define ADC_07   (915)     //6.2K
#define ADC_03   (490)     //3K
#define ADC_00   (0)

/* key voltage threshold */
#define AD_NOKEY        ((ADC_33 + ADC_30)/2)
#define AD_KEY_0		((ADC_30 + ADC_27)/2)
#define AD_KEY_1		((ADC_27 + ADC_23)/2)
#define AD_KEY_2		((ADC_23 + ADC_20)/2)
#define AD_KEY_3		((ADC_20 + ADC_17)/2)
#define AD_KEY_4		((ADC_17 + ADC_13)/2)
#define AD_KEY_5		((ADC_13 + ADC_10)/2)
#define AD_KEY_6		((ADC_10 + ADC_07)/2)
#define AD_KEY_7		((ADC_07 + ADC_03)/2)
#define AD_KEY_8		((ADC_03 + ADC_00)/2)

void get_key_adc_address(u16* _address);
KEY_NUMBER_U get_current_key_number(void);
/
/* c文件 */
#define KEY_SIZE (KEY_NUMBER - 1)
const static u16 ad_key_table[KEY_SIZE] = {
   
    AD_KEY_0, AD_KEY_1, AD_KEY_2, 
    AD_KEY_3, AD_KEY_4, AD_KEY_5, 
    AD_KEY_6, AD_KEY_7, AD_KEY_8,
};

static u16* s_ad_key_value;
/* 初始化时将按键的ad值地址传进来,用指针变量存起来,就可以不用包含ad值的头文件了,方便移植 */
void get_key_adc_address(u16* _address)
{
   
    s_ad_key_value = _address;
}
KEY_NUMBER_U get_current_key_number(void)
{
   
    u8 l_number = KEY_NONE;
    if (*s_ad_key_value > AD_NOKEY){
   
        return KEY_NONE;  
    }
    for (l_number = 0; l_number < KEY_SIZE; l_number++){
   	
        if (*s_ad_key_value > ad_key_table[l_number]){
   
            break; 
        }      
    }
    return (KEY_NUMBER_U)(KEY_SIZE - l_number);   
}

二、键值扫描

这部分较为简单,主要文件key_scan.c .h 。将上一步扫描的键值传入key_scan中,基本是在io单按键的基础稍作修改的

void key_scan(KEY_SCAN_S *_key)
{
   
    _key->value = NO_KEY;
    _key->type = KEY_NO_PRESS; 
    if (_key->cur_key == NO_KEY){
   
        switch (_key->status){
   
        case KEY_NO_PRESS:
            
            break;
        case KEY_DOWN:
            
            break;
        case KEY_SHORT:
            _key->type = KEY_SHORT;
            break;
        case KEY_LONG:
        
            break;
        case KEY_HOLD:
            _key->type = KEY_HOLD_UP;
            break;
        default:
            break;
        }
        _key->cnt = 0;
        _key->status = KEY_NO_PRESS;
    }else{
   
        /* 上次的按值有效,但和这次不同 */
        if ((NO_KEY != _key->last_key)&&(_key->last_key != _key->cur_key)){
   
            /* 这次按键比上次小,认为是一个误触按键*/
            if (_key->cur_key <= _key->last_key){
   
                _key->last_key = _key->cur_key;
                _key->cnt = 0;
                _key->status = KEY_NO_PRESS;
                return;   //异常退出 
            }
            /* 实际测试的时候抬手的一瞬间内,按键抖动,导致最后一次传入一个比当前键值大的键值
                如果认为是异常则按键扫描识别率低,所以这次按键比上次大,认为是一个松手按键 */
            if (KEY_SHORT == _key->status){
   
                _key->type = KEY_SHORT;
            }else if (KEY_HOLD == _key->status){
   
                _key->type = KEY_HOLD_UP;
            }
            _key->cnt = 0;
            _key->status = KEY_NO_PRESS;
        }
        _key->cnt++;
        switch (_key->status){
   
        case KEY_NO_PRESS:
            _key->status = KEY_DOWN;
            break;
        case KEY_DOWN:
            if (_key->cnt >= KEY_BASE_CNT) {
           //长按
                _key->status = KEY_SHORT;
            }
            break;    
        case KEY_SHORT:
            if (_key->cnt >= KEY_LONG_CNT) {
           //长按
                _key->status = KEY_LONG;
                _key->type = KEY_LONG;
            }
            break;
        case KEY_LONG:
            if (_key->cnt >= (KEY_LONG_CNT + KEY_HOLD_CNT)) {
       //连按
                _key->status = KEY_HOLD;
                _key->type = KEY_HOLD;
                _key->cnt = KEY_LONG_CNT;
            }
            break;
        case KEY_HOLD:
            if (_key->cnt >= (KEY_LONG_CNT + KEY_HOLD_CNT)) {
       //连按
                _key->type = KEY_HOLD;
                _key->cnt = KEY_LONG_CNT;
            }
            break;
        default:
            break;
        } 
    }
    if (KEY_NO_PRESS != _key->type){
   
        _key->number = _key->last_key;
       _key->value = ((_key->type << 5)|(_key->number & 0x1f));     //高3位代表按键状态,低5位代表键值
    }
    _key->last_key = _key->cur_key;
}

三、按键处理

按键处理经过上一步,就已经获得了有效的键值和按键类型,开始按键处理 主要文件 key_device.c .h
按键初始化要传入按键ad值的地址,初始化一个fifo队列,不想用队列也是可以的,但是要扫描完立马进行按键任务处理

/* 为了方便抄表,NO_PRESS不是0,初始化赋值 */
static KEY_SCAN_S s_key = {
   KEY_NO_PRESS,0,0,0,0,0,0,0};
static QUEUE_ELEMENT_S s_queue;
/* 传入adc地址,初始化队列 */
static void task_key_init(u16* _key_adc_address)
{
   
	get_key_adc_address(_key_adc_address);	
    init_queue(&s_queue);
}
/*
	按键扫描,把消息传入队列
*/
static void task_key_scan(void)
{
   
    u8 l_msg = NO_MSG;
    s_key.cur_key = get_current_key_number();    /* 获得按键值 */
    key_scan(&s_key);				    /* 传入扫描,获取按键结果 */
    if(KEY_NONE != s_key.value){
   	    /* 将按键结果传入队列 */
    	/* 抄表获取对应的按键变量 */
        l_msg = key_msg_buff[s_key
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值