第18集,ADC采集电源电压和ADC按键中
ADC按键,按下按键时,通过电阻分压得到不同的电压值,ADC采集在各个范围内的值来判定是哪个按键按下
优点是只需要一个IO就能实现多个按键的识别
缺点是识别精度差,不能识别同时按下多个按键,程序要一直保持扫描来识别按键按下
示例代码,和之前的普通按键和矩阵按键集成到一起了,因为没有ADC按键所以ADC按键部分未经验证
yuyy_key.h
- #ifndef __YUYY_KEY_H_
- #define __YUYY_KEY_H_
- #include "config.h"
-
- #define KEYSCANINTERVAL 10//按键扫描间隔
- #define DEBOUNCETIME 30 //去抖时间
- #define LONGPRESSTIME 2000 //长按识别时间
- //普通按键
- #define GPIOKEYNUMS 4 //按键总数
- #define KEYPORT P3 //按键io端口号
- #define KEYIOSTART 2 //按键io起始值 屠龙刀的按键从P3.2开始
- #define KEYDOWNLEV 0 //按键按下的io电平
- //矩阵按键
- #define MATRIXKEYCOLS 4 //按键列数
- #define MATRIXKEYROWS 2 //按键行数
- #define MATRIXKEYNUMS MATRIXKEYCOLS*MATRIXKEYROWS //矩阵按键总数
- /**
- *按键分布示例
- * P00 P01 P02 P03
- * P06 0 1 2 3
- * P07 4 5 6 7
- */
- #define MATRIXKEYROW0PIN P06 //第0行IO
- #define MATRIXKEYROW1PIN P07 //第1行IO
- #define MATRIXKEYCOL0PIN P00 //第0列IO
- #define MATRIXKEYCOL1PIN P01 //第1列IO
- #define MATRIXKEYCOL2PIN P02 //第2列IO
- #define MATRIXKEYCOL3PIN P03 //第3列IO
-
- //ADC按键
- #define ADCKEYNUMS 16 //ADC按键总数
- #define ADCKEYPIN P10 //ADC按键io
- #define ADCKEYCHANNEL 0//
- #define ADCKEYOFFSET 64//允许的误差值
-
- enum YUYY_KEYSTATE
- {
- KEY_UP = 0, //按键未按下
- KEY_DOWN, //按键按下
- KEY_SINGLECLICK, //单击按键 按下按键后在LONGPRESSTIME之前松开
- KEY_LONGPRESS, //长按按键 按下按键的时间超过LONGPRESSTIME
- KEY_LONGPRESSUP //长按后松开
- };
-
- enum YUYY_KEYTYPE
- {
- KEYTYPE_GPIO = 0, //普通按键
- KEYTYPE_MATRIX, //矩阵按键
- KEYTYPE_ADC, //ADC按键
- };
- //定义回调
- typedef void (*yuyy_key_cb)(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state);
- /**
- * 初始化按键,设置回调函数
- * 外部文件通过回调函数监控按键状态
- */
- void yuyy_keyinit(yuyy_key_cb cb);
-
- /**
- * 10ms一次检查按键状态
- */
- void yuyy_keyloop(void);
-
- #endif
复制代码
yuyy_key.c
- #include "yuyy_key.h"
- #include "yuyy_delay.h"
- #if(GPIOKEYNUMS)
- uint16_t gpiokeydowncount[GPIOKEYNUMS] = {0};
- #else
- uint16_t gpiokeydowncount[1] = {0};
- #endif
- #if(MATRIXKEYNUMS)
- uint16_t matrixkeydowncount[MATRIXKEYNUMS] = {0};
- #else
- uint16_t matrixkeydowncount[1] = {0};
- #endif
- #if(ADCKEYNUMS)
- uint16_t adckeydowncount[ADCKEYNUMS] = {0};
- #else
- uint16_t adckeydowncount[1] = {0};
- #endif
- uint16_t adcperkey;
- yuyy_key_cb keycb;
-
- void sendkeystate(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state)
- {
- if(keycb)
- {
- keycb(keytype,keynum,state);
- }
- }
-
- void yuyy_refreshkey(enum YUYY_KEYTYPE keytype,uint8_t key,uint8_t down)
- {
- uint16_t *keydowncount;
- if(keytype == KEYTYPE_GPIO)
- {
- keydowncount = gpiokeydowncount;
- }
- else if(keytype == KEYTYPE_MATRIX)
- {
- keydowncount = matrixkeydowncount;
- }
- else if(keytype == KEYTYPE_ADC)
- {
- keydowncount = adckeydowncount;
- }
- else
- {
- return;
- }
- if(down)
- {
- if(keydowncount[key] < 0xFFFF)
- {
- keydowncount[key]++;
- }
- if(keydowncount[key]*KEYSCANINTERVAL == DEBOUNCETIME)
- {
- sendkeystate(keytype,key,KEY_DOWN);
- }
- if(keydowncount[key]*KEYSCANINTERVAL == LONGPRESSTIME)
- {
- sendkeystate(keytype,key,KEY_LONGPRESS);
- }
- }
- else
- {
- if(keydowncount[key]*KEYSCANINTERVAL > DEBOUNCETIME)
- {
- if(keydowncount[key]*KEYSCANINTERVAL < LONGPRESSTIME)
- {
- sendkeystate(keytype,key,KEY_SINGLECLICK);
- }
- else
- {
- sendkeystate(keytype,key,KEY_LONGPRESSUP);
- }
- }
- keydowncount[key] = 0;
- }
- }
-
- void yuyy_gpiokeyloop(void)
- {
- uint8_t key = 0;
- while (key<GPIOKEYNUMS)
- {
- if(((KEYPORT >> (key+KEYIOSTART)) & 0x01) == KEYDOWNLEV)
- {
- yuyy_refreshkey(KEYTYPE_GPIO,key,1);
- }
- else
- {
- yuyy_refreshkey(KEYTYPE_GPIO,key,0);
- }
- key++;
- }
- }
-
- void yuyy_matrixkeyloop(void)
- {
- uint8_t key = 0;
- uint8_t i,j,col = 0,row = 0;
- MATRIXKEYROW0PIN = 1;
- MATRIXKEYROW1PIN = 1;
- MATRIXKEYCOL0PIN = 0;
- MATRIXKEYCOL1PIN = 0;
- MATRIXKEYCOL2PIN = 0;
- MATRIXKEYCOL3PIN = 0;
- yuyy_delay_us(10);
- if (MATRIXKEYROW0PIN == 0)
- {
- row |= 0x01;
- }
- if (MATRIXKEYROW1PIN == 0)
- {
- row |= 0x02;
- }
- MATRIXKEYROW0PIN = 0;
- MATRIXKEYROW1PIN = 0;
- MATRIXKEYCOL0PIN = 1;
- MATRIXKEYCOL1PIN = 1;
- MATRIXKEYCOL2PIN = 1;
- MATRIXKEYCOL3PIN = 1;
- yuyy_delay_us(10);
- if (MATRIXKEYCOL0PIN == 0)
- {
- col |= 0x01;
- }
- if (MATRIXKEYCOL1PIN == 0)
- {
- col |= 0x02;
- }
- if (MATRIXKEYCOL2PIN == 0)
- {
- col |= 0x04;
- }
- if (MATRIXKEYCOL3PIN == 0)
- {
- col |= 0x08;
- }
- MATRIXKEYROW0PIN = 1;
- MATRIXKEYROW1PIN = 1;
- MATRIXKEYCOL0PIN = 1;
- MATRIXKEYCOL1PIN = 1;
- MATRIXKEYCOL2PIN = 1;
- MATRIXKEYCOL3PIN = 1;
- for(i=0;i<MATRIXKEYROWS;i++)
- {
- for (j = 0; j < MATRIXKEYCOLS; j++)
- {
- if((row&(1<<i)) && (col&(1<<j)))
- {
- yuyy_refreshkey(KEYTYPE_MATRIX,key,1);
- }
- else
- {
- yuyy_refreshkey(KEYTYPE_MATRIX,key,0);
- }
- key++;
- }
-
- }
- }
-
- uint16_t yuyy_readadc(uint8_t channel)
- {
- uint16_t res;
- ADC_CONTR &= 0xF0;
- ADC_CONTR |= (channel&0x0F);
- ADC_START = 1; //启动 AD 转换
- _nop_();
- _nop_();
- while (!ADC_FLAG); //查询 ADC 完成标志
- ADC_FLAG = 0; //清完成标志
- res = ((ADC_RES << 8)&0x0F00) | ADC_RESL; //读取 ADC 结果
- return res;
- }
-
- void yuyy_adcKeyloop(void)
- {
- uint16_t keyadc,key = 0,adccmp = adcperkey;
- keyadc = yuyy_readadc(ADCKEYCHANNEL);
- while (key < ADCKEYNUMS)
- {
- if(keyadc>=adccmp-ADCKEYOFFSET && keyadc<adccmp+ADCKEYOFFSET)
- {
- //按键按下
- yuyy_refreshkey(KEYTYPE_ADC,key,1);
- }
- else
- {
- //按键未按下
- yuyy_refreshkey(KEYTYPE_ADC,key,0);
- }
- key++;
- adccmp += adcperkey;
- }
- }
-
- void yuyy_keyloop(void)
- {
- #if(GPIOKEYNUMS)
- yuyy_gpiokeyloop();
- #endif
- #if(MATRIXKEYNUMS)
- yuyy_matrixkeyloop();
- #endif
- #if(ADCKEYNUMS)
- yuyy_adcKeyloop();
- #endif
- }
-
- void yuyy_adcKeyinit(void)
- {
- P1M0 = 0x00;
- P1M1 = 0x01; //P1.0高阻输入,作为ADC的引脚必须设置为高阻输入
- ADCTIM = 0x3F; //设置ADC内部时序
- ADCCFG = 0x2F; //结果右对齐,设置ADC时钟为系统时钟/2/16
- ADC_POWER = 1;
- adcperkey = 4096/ADCKEYNUMS;
- }
-
- void yuyy_keyinit(yuyy_key_cb cb)
- {
- #if(ADCKEYNUMS)
- yuyy_adcKeyinit();
- #endif
- keycb = cb;
- }
复制代码
main.c
- #include "config.h"
- #include "string.h"
- #include "yuyy.h"
-
- //USB调试及复位所需定义
- char *USER_DEVICEDESC = NULL;
- char *USER_PRODUCTDESC = NULL;
- char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
-
- void test_timer0cb(void)
- {
- yuyy_keyloop();
- }
- void test_keycb(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state)
- {
- printf("按键类型:%d 按键号:%d 按键行为:%d\r\n",keytype,keynum,state);
- }
- /******************** 主函数 **************************/
- void main(void)
- {
- int res;
- int vcc;
- int i;
-
- WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
- EAXFR = 1; //扩展寄存器(XFR)访问使能
- CKCON = 0; //提高访问XRAM速度
-
- RSTFLAG |= 0x04; //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式
-
- P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
- P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
- P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
- P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
- P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
- P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
- P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
- P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
-
- //USB调试及复位所需代码-----
- P3M0 &= ~0x03;
- P3M1 |= 0x03;
- IRC48MCR = 0x80;
- while (!(IRC48MCR & 0x01));
- //如果使用USB-CDC需要下面的两行代码
- USBCLK = 0x00;
- USBCON = 0x90;
- //如果使用USB-HID注释掉上面两行代码
- usb_init();
- //-------------------------
- yuyy_timer0init(10000,test_timer0cb); //10ms触发一次回调
- yuyy_keyinit(test_keycb);
-
- EUSB = 1; //IE2相关的中断位操作使能后,需要重新设置EUSB
- EA = 1; //打开总中断
-
- while(1)
- {
- }
- }
复制代码
只验证了普通按键和矩阵按键的工作情况
|