提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录

CON3中将1,2用跳线帽短接是矩阵键盘,可用范围为S4到S19。将2,3短接则是独立按键,可用范围是S4到S7.
单取一个按键,一端接到GND,一段接到单片机的IO口上(以下称I口,输入口)。当按键按下时,I口读取电平为低电平,按键未按下单片机的I口读取为高电平。此时只需要单片机按时扫描这个I口是否为低电平,即可判断按键按是否按下。如果有四个这样的按键,就是上图中的独立按键。
如果将GND同时也换成单片机的另IO口(以下称O口,输出口),那么当我有需要读取按键时,将O口设置输出为低电平,再去读取I口的电平,即可判断按键是否按下。这样做有什么好处呢。
如果我需要有16个按键,如果用独立按键,那么我将需要16个I口取读取按键电平。如果将16个按键每行放置4个,放4列,如上图CON1,2口短接时。并却我采用O口输出口的话,采用4个O口,每个O口控制一列4个按键,采用4个I口,每个I口读取每行4个按键。这样输出对应竖排,输入对应横排。两个分量,就可以在二维平面上定位到按键的位置。如此,将本来16个IO口减少到了8个IO口,节约了单片机的资源,这就是矩阵键盘。
矩阵键盘的使用,先将一个O口置零,读取所有的I口,再依次将O口置零读取I口。那么这样去读取按键会不会有时间差。不会的,一个按键按下的过程至少需要大几十个毫秒,我只去要将O口轮换的速度变快,就可以了。
#ifndef __KEY_H
#define __KEY_H
#include "STC15F2K60S2.H"
unsigned char Key_Read();
unsigned char Key_Read_BTN();
#endif
#include "key.h"
//P30-P34为I口,读取高低电平判断按键是否按下
unsigned char Key_Read_BTN()
{
unsigned char key_value; //设置按键代号变量
if(P33 == 0) key_value = 4; //S4按键,用4代替
else if(P32 == 0) key_value = 5;
else if(P31 == 0) key_value = 6;
else if(P30 == 0) key_value = 7;
else key_value = 0;
// 如果没有按键按下返回值为零,这个很重要。
return key_value;//将按键代号返回,便于接下来的处理
}
#include "key.h"
unsigned char Key_Read()
{
unsigned int key_new;
// I口读取变量,有16个按键,16位,两个字节,所以用int型变量
unsigned char key_value; //设置按键代号变量
P44 = 0; P42 = 1; P35 = 1; P34 = 1; //将一个O口置零
key_new = P3&0x0F; //读取全部的I口数据
P44 = 1; P42 = 0; P35 = 1; P34 = 1;
key_new =(key_new<<4)|(P3&0x0F); //将上次读取的数据左移,并存储这次I口全部数据
P44 = 1; P42 = 1; P35 = 0; P34 = 1;
key_new =(key_new<<4)|(P3&0x0F);
P44 = 1; P42 = 1; P35 = 1; P34 = 0;
key_new =(key_new<<4)|(P3&0x0F);
//4个O口对应16个I口数据全部读取。
switch(~key_new) //取反,按键按下是0,不按是1
{
case 0x8000: key_value = 4; break;
case 0x4000: key_value = 5; break;
case 0x2000: key_value = 6; break;
case 0x1000: key_value = 7; break;
case 0x0800: key_value = 8; break;
case 0x0400: key_value = 9; break;
case 0x0200: key_value = 10; break;
case 0x0100: key_value = 11; break;
case 0x0080: key_value = 12; break;
case 0x0040: key_value = 13; break;
case 0x0020: key_value = 14; break;
case 0x0010: key_value = 15; break;
case 0x0008: key_value = 16; break;
case 0x0004: key_value = 17; break;
case 0x0002: key_value = 18; break;
case 0x0001: key_value = 19; break;
default: key_value = 0;
}
return key_value;
}
void Key_Proc()
{
if(key_slow_down) return;
key_slow_down = 1;
//降速处理每10毫秒进入一次按键处理
key_value = Key_Read_BTN();//进入底层驱动读取键值
key_down = key_value&(key_value^key_old);//下降沿
key_up = (~key_value)&(key_value^key_old);//上升沿
key_old = key_value;
//三行代码按键消抖,文章中讲解
switch(key_down)//对按键的下降沿进行扫描处理
{
case 4:
if(display>>4 == 0x1)
display = 0x20;
else if(display>>4 == 0x2)
display = 0x31;
else if(display>>4 == 0x3)
display = 0x41;
else if(display>>4 == 0x4)
display = 0x11;
break; //S4按下时的处理,13届蓝桥杯国赛部分参考
default: break;
}
}
key_value = Key_Read_BTN();
key_down = key_value&(key_value^key_old);
key_old = key_value;
这三行代码是蓝桥杯官方提供的按键消抖代码,简洁精炼,值得我们去学习。
学习代码之前先看一幅图

A是上升沿,B是下降沿,A-B为高电平,B-A为低电平。
key_value = Key_Read_BTN();
//这一行就是去读取高低电平状态,高电平即使未按下,低电平是按下。这一行只能说明是一种状态,如果是最基本的按下触发,状态值是不可以使用,因为一次按键按下至少维持几十毫秒,再这几十个毫秒里程序会不只一次获取状态值,那么我们通过按键所执行的任务就会多次执行。
所以仅仅读取电平状态是不可行的,如何做到按键一次按下,只执行一次。我们想到去读取跳变沿A或B,官方给出的是读取下降沿(即B);为此引入第二行代码,再讲解第二行代码前,先说一下第三行代码
key_old = key_value;
//为了运用代码得到跳变沿,即我们需要获取上一次的电平状态与这一次的电平状态经过逻辑运算得到。这第三行代码就是为了得到上一次的电平状态
key_down = key_value&(key_value^key_old);
//第二行代码既要获得下降沿又要获得按键键值,即那个按键发送下降沿。
key_value^key_old 将上次的电平状态与当前的电平状态取按位异或,可以检测出跳变沿,没有跳变沿为0,又跳变沿为当前跳变沿的键值。当然仅仅一个按位异或是不够的,并不能区分出上升沿和下降沿。所以要在按位与key_value(得到下降沿)或者按位与(~key_value)(得到上升沿)。
//为什么上升沿检测比下降沿检测多一个取反
key_down = key_value&(key_value^key_old);//下降沿
key_up = (~key_value)&(key_value^key_old);//上升沿
首先下降沿检测前一次状态是抬起,键值是0,而瞬时状态为按下,键值为x,(key_value^key_old)为x。x与上x,任然为x,得到键值。
而检测上升沿,前一次状态是按下,键值是下,而瞬时状态为抬起,键值为0。(key_value^key_old)为x。应为瞬时键值为0,所以通过取反来获得键值。
总结,前半段决定上升沿还是下降沿,后半段检测跳变沿。

unsigned char state_flag;
switch(key_down)
{
case 12:
if(++state_flag == 3) state_flag = 0;
//用state_flag代表界面,并且加到3清零。
//显示模块通过state_flag的值显示
break;
default: break;
}

简介表示
1.数据界面
(1)时间
(2)温度
(3)亮暗状态
2.参数界面
(1)时间参数
(2)温度参数
(3)指示灯参数
unsigned char display = 0x11;
//采用16进制数据,高位代表高模式,低位代表低模式
switch(key_down)
{
case 4: //高模式模式切换
if(display>>4 == 1)
display = 0x21;
else
display = 0x11;
break;
case 5://低模式模式切换
if(display == 0x11)
display = 0x12;
else if(display == 0x12)
display = 0x13;
else if(display == 0x13)
display = 0x11;
else if(display == 0x21)
display = 0x22;
else if(display == 0x22)
display = 0x23;
else if(display == 0x23)
display = 0x21;
break;
}
总结:用一个对应变量决定模式状态。按键部分是对变量的处理。

if( key_old == 4)//S4按下
{
if(state_flag != 1 && state_flag != 2)//非设置状态
{
led_class_disp = 1; //长按状态设置
}
}
else led_class_disp = 0;
注意点在于,长按显示不需要去读取跳边沿,而是直接读取电平状态。


下降沿开始计数,上升沿停止计数。判断计数时间。
//按键程序
key_value =Key_Read_BTN();
key_down = key_value & ( key_value ^ key_old);//下降沿
key_up = (key_value^0xff) & ( key_value ^ key_old);//上升沿
key_old = key_value;
if(key_down == 7)
{
ms_statrt = ms;//定时器中断ms每1毫秒加1,检测S7下降沿,将ms赋值到ms_statrt。
}
if(key_up == 7)
{
if((ms - ms_statrt) >= 1000) led_flag ^= 1 ;//抬起时检测(ms - ms_statrt)是否超过规定时间
else f_value = freq;
}
为什么if(key_down)和if(key_up)同级:
答:按键只检测跳变沿并不是检测高低电平,跳变沿是瞬时性的所以在跳变沿里检测另一个跳变沿是不合理的。

注意点:下降沿仅开始计数,对时间进行判断。对短按识别上升沿识别。对长按进行一次进入判断(一次判断,赋值卡死而不是取反)。
下降沿,开始计数,(是否进行一次判断)
短时间上升沿扫描,
长时间,电平扫描加一次判断。
if(key_down == 7)
{
key_time = ms; // 计数开始
key_long = 1;//长按一次判断,即长按达到时间后只进行一次操作
}
if(ms-key_time<1000)//时间未到,短按操作
{
if(key_up == 7)
{
if(display == 0x41)
freq_compare = freq_compare-0.5;
else if(display == 0x42)
temp_compare = temp_compare-10;
else if(display == 0x43)
dist_compare = dist_compare-0.1;
else if(display == 0x11)
display = 0x12;
else if(display == 0x12)
display = 0x11;
}
}
else // 长按操作
{
if(key_value == 7&&key_long == 1) // 判断此时按键电平状态和一次操作
{
key_long = 0;
if(display==0x20)
relay_count = 0;
}
}
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源
嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时