以STC单片机为例A/D做按键扫描应用原理介绍


矩阵按键扫描或者是多个按键扫描,我们通常想到的可能大多是通过多个IO口来搭建按键扫描电路,例如下面的4X4矩阵按键,今天来介绍的是通过引入模拟量信号采集转换为数字量来确定按键的方法。当然这样的方法也有它的不足之处,就是不支持多按键同时按下,那样将会有不可预知的结果.

  • 4X4矩阵按键

以下是通过STC15系列单片机手册上给我们提供的参考方案:

  • A/D做按键扫描应用线路图

16按键示例程序代码

  • 数码管接线图可以参考:STC单片机使用IO或SPI方式通过74HC595驱动8位数码管
  • 基准电压参考图
  • 74HC595联级驱动数码管
  • ADC采集电路可以参考上面的原理图,下面提供一个参考,并没有画完16段按键区间:

仿真资源以及说明

所提供的的仿真文件时基于Proteus8.9平台,包含程序案例,程序案例可以在STC官方STC15实验箱中找到相应的案例。

  • 在Proteus中虽然可以仿真STC单片机,但是运行速度实际上慢了10倍,对于模拟量采集,并不能在仿真中体现实际硬件运行的效果。只是提供一个搭建的电路参考。
链接:https://pan.baidu.com/s/1ainWLXMjI-LJUcdAK-zRnw
提取码:i21j

仿真说明

  • 在Proteus仿真当中,定时器计时并不能实现自动重装载,需要在定时器0中断当中补充装载值。修改如下:
/********************** Timer0 1ms中断函数 ************************/
void timer0 (void) interrupt TIMER0_VECTOR
{TR0 =0;TH0 = (u8)(Timer0_Reload / 256);//Proteus仿真,需要填写重装载值。TL0 = (u8)(Timer0_Reload % 256);DisplayScan();   //1ms扫描显示一位B_1ms = 1;      //1ms标志TR0 =1;
}

仿真速度过慢。调整走时计算,X10倍速度

         if(++msecond >= 100)  //1秒到,实际是1000,Proteus仿真速度太慢,加速10倍{msecond = 0;RTC();DisplayRTC();}if(msecond == 50)    DisplayRTC();   //小时后的小数点做秒闪,实际是500,Proteus仿真速度太慢,加速10倍

完整代码

/*---------------------------------------------------------------------*/
/* --- STC MCU International Limited ----------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* 来自宏晶科技的资料及程序   */
/*---------------------------------------------------------------------*//************* 本程序功能说明 **************用STC的MCU的IO方式控制74HC595驱动8位数码管。显示效果为: 数码时钟.使用Timer0的16位自动重装来产生1ms节拍,程序运行于这个节拍下, 用户修改MCU主时钟频率时,自动定时于1ms.左边4位LED显示时间(小时,分钟), 右边最后两位显示按键值.ADC按键键码为1~16.按键只支持单键按下, 不支持多键同时按下, 那样将会有不可预知的结果.键按下超过1秒后,将以10键/秒的速度提供重键输出. 用户只需要检测KeyCode是否非0来判断键是否按下.调整时间键:
键码1: 小时+.
键码2: 小时-.
键码3: 分钟+.
键码4: 分钟-.******************************************/#define     MAIN_Fosc       22118400L   //定义主时钟#include "STC15Fxxxx.H"
#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒#define DIS_DOT      0x20
#define DIS_BLACK   0x10
#define DIS_        0x11u8 code t_display[]={                      //标准字库
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};       //位码sbit    P_HC595_SER   = P4^0;  //pin 14    SER     data input
sbit    P_HC595_RCLK  = P5^4;  //pin 12    RCLk    store (latch) clock
sbit    P_HC595_SRCLK = P4^3;  //pin 11    SRCLK   Shift data clocku8  LED8[8];        //显示缓冲
u8  display_index;  //显示位索引
bit B_1ms;          //1ms标志u8   ADC_KeyState,ADC_KeyState1,ADC_KeyState2,ADC_KeyState3; //键状态
u8  ADC_KeyHoldCnt; //键按下计时
u8  KeyCode;    //给用户使用的键码, 1~16有效
u8  cnt10ms;u8  hour,minute,second; //RTC变量
u16 msecond;void    CalculateAdcKey(u16 adc);
u16     Get_ADC10bitResult(u8 channel); //channel = 0~7
void    DisplayRTC(void);
void    RTC(void);/**********************************************/
void main(void)
{u8 i;u16   j;P0M1 = 0;    P0M0 = 0;  //设置为准双向口P1M1 = 0; P1M0 = 0;  //设置为准双向口P2M1 = 0; P2M0 = 0;  //设置为准双向口P3M1 = 0; P3M0 = 0;  //设置为准双向口P4M1 = 0; P4M0 = 0;  //设置为准双向口P5M1 = 0; P5M0 = 0;  //设置为准双向口P6M1 = 0; P6M0 = 0;  //设置为准双向口P7M1 = 0; P7M0 = 0;  //设置为准双向口display_index = 0;P1ASF = 0x10;      //P1.4做ADCADC_CONTR = 0xE0;    //90T, ADC power onAUXR = 0x80;    //Timer0 set as 1T, 16 bits timer auto-reload, TH0 = (u8)(Timer0_Reload / 256);TL0 = (u8)(Timer0_Reload % 256);ET0 = 1;  //Timer0 interrupt enableTR0 = 1;  //Tiner0 runEA = 1;        //打开总中断for(i=0; i<8; i++) LED8[i] = 0x10;    //上电消隐hour   = 12; //初始化时间值minute = 0;second = 0;DisplayRTC();ADC_KeyState  = 0;ADC_KeyState1 = 0;ADC_KeyState2 = 0;ADC_KeyState3 = 0;   //键状态ADC_KeyHoldCnt = 0;   //键按下计时KeyCode = 0;    //给用户使用的键码, 1~16有效cnt10ms = 0;while(1){if(B_1ms)   //1ms到{B_1ms = 0;if(++msecond >= 100)    //1秒到,实际是1000,Proteus仿真速度太慢,加速10倍{msecond = 0;RTC();DisplayRTC();}if(msecond == 50)    DisplayRTC();   //小时后的小数点做秒闪,实际是500,Proteus仿真速度太慢,加速10倍if(++cnt10ms >= 10)   //10ms读一次ADC{cnt10ms = 0;j = Get_ADC10bitResult(4);   //参数0~7,查询方式做一次ADC, 返回值就是结果, == 1024 为错误if(j < 1024)   CalculateAdcKey(j); //计算按键}if(KeyCode > 0)       //有键按下{LED8[6] = KeyCode / 10; //显示键码LED8[7] = KeyCode % 10;  //显示键码if(KeyCode == 1)    //hour +1{if(++hour >= 24)   hour = 0;DisplayRTC();}if(KeyCode == 2)  //hour -1{if(--hour >= 24)  hour = 23;DisplayRTC();}if(KeyCode == 3) //minute +1{second = 0;if(++minute >= 60)   minute = 0;DisplayRTC();}if(KeyCode == 4)    //minute -1{second = 0;if(--minute >= 60)  minute = 59;DisplayRTC();}KeyCode = 0;}}}
}
/**********************************************//********************** 显示时钟函数 ************************/
void    DisplayRTC(void)
{if(hour >= 10) LED8[0] = hour / 10;else           LED8[0] = DIS_BLACK;LED8[1] = hour % 10;LED8[2] = minute / 10;LED8[3] = minute % 10;if(second >= 10)    LED8[4] = second / 10;else         LED8[4] =  0;LED8[5] = second % 10;if(msecond >= 500)     LED8[5] |= DIS_DOT;    //小时后的小数点做秒闪
}/********************** RTC演示函数 ************************/
void    RTC(void)
{if(++second >= 60){second = 0;if(++minute >= 60){minute = 0;if(++hour >= 24)   hour = 0;}}
}//========================================================================
// 函数: u16  Get_ADC10bitResult(u8 channel)
// 描述: 查询法读一次ADC结果.
// 参数: channel: 选择要转换的ADC.
// 返回: 10位ADC结果.
// 版本: V1.0, 2012-10-22
//========================================================================
u16 Get_ADC10bitResult(u8 channel)  //channel = 0~7
{ADC_RES = 0;ADC_RESL = 0;ADC_CONTR = (ADC_CONTR & 0xe0) | 0x08 | channel;   //start the ADCNOP(4);while((ADC_CONTR & 0x10) == 0)  ;   //wait for ADC finishADC_CONTR &= ~0x10;       //清除ADC结束标志return   (((u16)ADC_RES << 2) | (ADC_RESL & 3));
}/***************** ADC键盘计算键码 *****************************
电路和软件算法设计: Coody
本ADC键盘方案在很多实际产品设计中, 验证了其稳定可靠, 即使按键使用导电膜,都很可靠.
16个键,理论上各个键对应的ADC值为 (1024 / 16) * k = 64 * k, k = 1 ~ 16, 特别的, k=16时,对应的ADC值是1023.
但是实际会有偏差,则判断时限制这个偏差, ADC_OFFSET为+-偏差, 则ADC值在 (64*k-ADC_OFFSET) 与 (64*k+ADC_OFFSET)之间为键有效.
间隔一定的时间,就采样一次ADC,比如10ms.
为了避免偶然的ADC值误判, 或者避免ADC在上升或下降时误判, 使用连续3次ADC值均在偏差范围内时, ADC值才认为有效.
以上算法, 能保证读键非常可靠.
**********************************************/
#define ADC_OFFSET  16
void    CalculateAdcKey(u16 adc)
{u8 i;u16   j;if(adc < (64-ADC_OFFSET)){ADC_KeyState = 0;   //键状态归0ADC_KeyHoldCnt = 0;}j = 64;for(i=1; i<=16; i++){if((adc >= (j - ADC_OFFSET)) && (adc <= (j + ADC_OFFSET))) break;  //判断是否在偏差范围内j += 64;}ADC_KeyState3 = ADC_KeyState2;ADC_KeyState2 = ADC_KeyState1;if(i > 16)  ADC_KeyState1 = 0; //键无效else                       //键有效{ADC_KeyState1 = i;if((ADC_KeyState3 == ADC_KeyState2) && (ADC_KeyState2 == ADC_KeyState1) &&(ADC_KeyState3 > 0) && (ADC_KeyState2 > 0) && (ADC_KeyState1 > 0)){if(ADC_KeyState == 0)  //第一次检测到{KeyCode  = i; //保存键码ADC_KeyState = i;    //保存键状态ADC_KeyHoldCnt = 0;}if(ADC_KeyState == i) //连续检测到同一键按着{if(++ADC_KeyHoldCnt >= 100)  //按下1秒后,以10次每秒的速度Repeat Key{ADC_KeyHoldCnt = 90;KeyCode  = i; //保存键码}}else    ADC_KeyHoldCnt = 0;    //按下时间计数归0}}
}/**************** 向HC595发送一个字节函数 ******************/
void Send_595(u8 dat)
{       u8  i;for(i=0; i<8; i++){dat <<= 1;P_HC595_SER   = CY;P_HC595_SRCLK = 1;P_HC595_SRCLK = 0;}
}/********************** 显示扫描函数 ************************/
void DisplayScan(void)
{   Send_595(~T_COM[display_index]);                //输出位码Send_595(t_display[LED8[display_index]]); //输出段码P_HC595_RCLK = 1;P_HC595_RCLK = 0;                          //锁存输出数据if(++display_index >= 8)  display_index = 0; //8位结束回0
}/********************** Timer0 1ms中断函数 ************************/
void timer0 (void) interrupt TIMER0_VECTOR
{TR0 =0;TH0 = (u8)(Timer0_Reload / 256);//Proteus仿真,需要填写重装载值。TL0 = (u8)(Timer0_Reload % 256);DisplayScan();   //1ms扫描显示一位B_1ms = 1;      //1ms标志TR0 =1;
}

以STC的单片机为例A/D做按键扫描应用原理介绍相关推荐

  1. 【自学51单片机】2 -- LED小灯及点亮原理介绍、keil软件编写程序、烧录程序和点亮 LED 小灯

    目录 1. 外设 LED 介绍 2.计算限流电阻 3.USB接口电路介绍 4.点亮LED小灯原理 5.特殊功能寄存器和位定义 6.通过keil软件建立工程 7.编写程序 8.烧录程序 9.收获 1. ...

  2. 【摘要】STC系列单片机ISP编程器/烧录器的说明

    [摘要]STC系列单片机ISP编程器/烧录器的说明 STC-ISP编程工具 STC-ISP下载编程烧录软件目前版本:STC-ISP软件V6.88K版 在系统可编程(ISP)原理使用说明流程图 STC系 ...

  3. STC单片机通过ADC分段采样读按键实现方法

    STC单片机通过ADC分段采样读按键实现方法 ✨在阅读官方FreeRTOS for STC32G12K128开源工程当中有使用到ADC键盘计算键码功能块,底层实现原理也很简单,就是通过串联电阻分压的方 ...

  4. KEIL中怎样添加STC系列单片机

    我们有时在KEIL中新建工程时需要添加STC系列单片机,但就是找不到. 这是因为你安转的Keil软件中没有STC的数据库,所以需要你手动添加.这就需要在STC-ISP软件(这个软件可以在STC网htt ...

  5. 有关利用PL2303HX USB到RS232 TTL转换器适配器模块把STC89C52RC等STC系列单片机与计算机连接、向Keil添加STC库及hex文件考录方法小结

    有关利用PL2303HX USB到RS232 TTL转换器适配器模块把STC89C52RC等STC系列单片机与计算机连接.向Keil添加STC库及hex文件考录方法小结 PL2303HX转换器适配器模 ...

  6. 单片机 spwm c语言程序,基于STC系列单片机的SPWM波形实现

    摘要:文章在比较了多种生成SPWM波的技术基础上,给出了利用等效面积法来产生SPWM波形的工作原理,详细介绍了由单片机STCl2C5410AD的可编程计数器阵列PCA实现SPWM控制软件的编写过程,并 ...

  7. STC单片机按键扫描程序

    STC单片机按键扫描程序 最近在做一个电子秤相关项目,使用STC系列单片机作为主控芯片,项目第一阶段直接使用IAP15W4K58S4驱动两个矩阵键盘,一切调试顺利,在项目即将结束时老板要求使用另一块单 ...

  8. STC 51单片机程序下载失败总结

    STC为宏晶公司推出的国产51单片机,其优点在于价格低廉,功能强大,使用方便,尤其是其串口ISP下载程序的方式方便了大量用户,免去了购买昂贵的编程器,非常适合单片机入门学习使用. 但是初学者常会遇到程 ...

  9. K_A08_013 基于 STM32等单片机驱动大功率MOS管模块按键控制直流电机加减速启停

    目录 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 大功率MOS模块驱动 对应程序: 四.部分代码说明 接线说明 STC89C52RC+大功率MOS模块 STM32F103C8T6+大功率 ...

最新文章

  1. 一个Portal处理流程
  2. AndroidWear开发之HelloWorld篇
  3. sas university edition在ubuntu中的使用
  4. Request.ServerVariables的详细应用(转)
  5. python 循环写文件_python-文件操作及循环
  6. 计算两个String 类型的时间相关几个月
  7. 算法导论9.1-1习题解答(二叉树)
  8. 【起航计划 027】2015 起航计划 Android APIDemo的魔鬼步伐 26 App-Preferences-Preferences from XML 偏好设置界面...
  9. MogaFX-M1日
  10. POJ 1088 滑雪 题解
  11. cdr 表格自动填充文字_【Excel技巧】excel单元格自动填充英文字母编号
  12. Mac 快速查找快捷键command+f失效解决办法
  13. Linux文件锁(Filelock)是什么,怎么用?
  14. 690家门店送万份小食 汉堡王“战舰世界堡胃战”活动开启
  15. message: 没有找到可以构建的 NPM 包,请确认需要参与构建的 npm 都在 `miniprogramRoot` 目录内,或配置 project.config.json 的 packNpmMa
  16. HBase的java代码开发(完整源码)
  17. i.MX283开发板移植RTL8188ETV无线网卡驱动
  18. 音频播放、录音、视频播放、拍照、视频录制
  19. 利用腾讯会议开展远程面试的解决方案
  20. db9接口(db9接口详细接线图)

热门文章

  1. 【MySQL运维】使用gh-ost工具实现大表在线DDL变更
  2. 魅族pro 6完美开启usb调试模式的经验
  3. 惹某人突然不舍de第七周(习题+感悟)
  4. Trucksim(一):Trucksim动力学模型搭建
  5. Git LFS(Large File Storage)使用简介
  6. Java基础语法-数据类型与idea配置
  7. ActiveX控件属性的下拉列表
  8. Laplacian eigenmap 拉普拉斯特征映射
  9. 云呐|PDA无线固定资产盘点管理系统
  10. 西门子数控机床如何用FTP进行程序传输