【物联网智能网关-05】扫描键盘编程设计

简介: .NET Micro Framework模拟器提供了5个模拟按键(上、下、左、右和确认按键),所以一般.NET MF开发板也只需要提供5个按键就可以了,而这5个键,也是直接和CPU的pin脚相连,用GPIO的输入相关的函数就可以操作了,使用非常简单。

.NET Micro Framework模拟器提供了5个模拟按键(上、下、左、右和确认按键),所以一般.NET MF开发板也只需要提供5个按键就可以了,而这5个键,也是直接和CPU的pin脚相连,用GPIO的输入相关的函数就可以操作了,使用非常简单。

但是对一些特殊的应用,如一些.NET Micro Framework教育箱或一些工业实际用的系统,5个按键显然太少了点。但是如果需要十几个按键,如果直连芯片pin脚,显然占用的资源比较多了,也会导致其它的功能无法使用了,这时候最常用的就是扫描键盘了。
image.png

上述扫描键盘的原理图应该是最简单的一种了,复杂一点的,在行或列上,通过一个上拉电阻接VCC。这样,我们只需要8个pin脚,就可以获取16个按键的信息了。

一般实现的思路也比较简单:就是把行(或列)接芯片输出pin脚,把列(或行)接芯片输入pin脚,输出pin脚依次输出低(或高,需要看电路中接的上拉还是下拉电阻)电平,然后检查输入pin脚的电平变化。如果有变化,那么就说明,该列和该行的按键被按下了。

往往这个判断就放在while循环或线程里,不断的去运行。对一些单片而言,如果实现的功能单一,这样做也无可厚非,但是对一个系统平台来说,如果也这样做,显然对系统的资源占用还是比较厉害的。

所以最好的办法还是要采用中断的方式,平时的时候不去判断,靠中断触发,一旦中断触发了,然后再启动一轮判断,确定是哪一个按键被按下了。

1、扫描方式实现按键获取

public class ScanKeypad

    {

        public event NativeEventHandlerOnInterrupt;

 

        OutputPort[]rows = null;

        InputPort[]cols = null;

        publicScanKeypad(Cpu.Pin[]Output_Pins, Cpu.Pin[]Input_Pins)

        {

            rows = newOutputPort[] { newOutputPort(Output_Pins[0], false), new OutputPort(Output_Pins[1], false), new OutputPort(Output_Pins[2], false), new OutputPort(Output_Pins[3], false) };

            cols = newInputPort[] { newInputPort(Input_Pins[0], true, Port.ResistorMode.PullUp), newInputPort(Input_Pins[1], true, Port.ResistorMode.PullUp), newInputPort(Input_Pins[2], true, Port.ResistorMode.PullUp), newInputPort(Input_Pins[3], true, Port.ResistorMode.PullUp) };

 

            ThreadthreadKeypad = new Thread(new ThreadStart(KeypadScan));

            threadKeypad.Start();

        }

 

        voidKeypadScan()

        {

            intkey = -1, oldKey = -1;

            while(true)

            {

                key = -1;

                for(int i = 0; i < rows.Length; i++)

                {

                    rows[i].Write(false);

                    for(int j = 0; j < cols.Length; j++)

                    {

                        if (!cols[j].Read())

                        {

                            key = i *rows.Length + j;

                            break;

                        }

                    }

                    rows[i].Write(true);

                    if(key > -1) break;

                }

                if(key > -1 && key != oldKey)

                {

                    if(OnInterrupt != null) OnInterrupt((uint)key, 1, DateTime.Now);

                    oldKey = key;

                }

                elseif (oldKey > -1 && key == -1)

                {

                    if(OnInterrupt != null) OnInterrupt((uint)oldKey, 0, DateTime.Now);

                    oldKey = -1;

                }

                Thread.Sleep(100);

            }

        }

    }

2、中断方式实现按键获取

public class InterruptKeypad

    {

        public event NativeEventHandlerOnInterrupt;

 

        OutputPort[]rows = null;

        InterruptPort[]cols = null;

        Cpu.Pin[] Pins = null;

        uintkey = 0;

        publicInterruptKeypad(Cpu.Pin[]Output_Pins, Cpu.Pin[]Input_Pins)

        {

            rows = newOutputPort[] { newOutputPort(Output_Pins[0], false), new OutputPort(Output_Pins[1], false), new OutputPort(Output_Pins[2], false), new OutputPort(Output_Pins[3], false) };

            cols = newInterruptPort[Input_Pins.Length];

            Pins = Input_Pins;

            for(int i = 0; i < Input_Pins.Length; i++)

            {

                cols[i] = new InterruptPort(Input_Pins[i],true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);

                cols[i].OnInterrupt += new NativeEventHandler(InterruptKeypad_OnInterrupt);

            }

        }

 

        privateuint GetPinIndex(uintpin)

        {

            for(uint i = 0; i < Pins.Length; i++)

            {

                if(pin == (uint)Pins[i]) returni;

            }

            return0;

        }

 

        voidInterruptKeypad_OnInterrupt(uint data1, uint data2, DateTimetime)

        {

            if(data2 == 1)

            {

                for(int i = 0; i < cols.Length; i++)

                {

                    cols[i].OnInterrupt -= new NativeEventHandler(InterruptKeypad_OnInterrupt);

                }

                //--

                uintcol = GetPinIndex(data1);

                for(int i = 0; i < rows.Length; i++)

                {

                    rows[i].Write(true);

                    if(cols[col].Read())

                    {

                        key = (uint)(i * rows.Length + col);

                        Thread threadKeypad = new Thread(new ThreadStart(KeypadRun));

                        threadKeypad.Start();

                        break;

                    }

                }

                //--

                for(int i = 0; i < rows.Length; i++)rows[i].Write(false);

                for(int i = 0; i < cols.Length; i++)

                {

                    cols[i].OnInterrupt += new NativeEventHandler(InterruptKeypad_OnInterrupt);

                }

            }

        }

 

        voidKeypadRun()

        {

            OnInterrupt(key, 1, DateTime.Now);

            OnInterrupt(key, 0, DateTime.Now);

        }

    }

注意,中断方式中,触发事件必须放在线程里执行,否则会有问题(如果在Winform中使用,最好不用线程,而用winfrom提供的timer,否则就无法直接操作UI了,那就必须用委托方式了,和windows上的编程类似)。

问题点1:由于我们采用的键盘并没有加上拉(或下拉)电阻电路,在最初做这个程序的时候,InputPort(Input_Pins[1], true,Port.ResistorMode.PullUp),最后一个参数,底层并没有实现内部上拉,下拉和悬空功能,所以设置是无效的。这就造成了,在按钮没有按下时,输入pin脚的状态是未知的,有时候是1,有时候是0,程序是无法正确运行的。

此外STM32F103和STM32F207的GPIO寄存器差别很大,内部实现上拉、下拉的设置也是不同的。分别实现后,发现内部上拉正常,设置下拉效果不明显,pin脚的状态还是未知的。所以我们实现的程序都设置为上拉。

问题点2:在实现中断方式的扫描键盘的代码的时候,发现PB6、PC0和PB1三个pin脚触发中断异常,但是在NativeSample层面又正常。目前没有发现这三个pin脚有何特别之处,此问题以后待查。所以如果采用中断方式,这三个pin脚不能使用。

注:该问题已修正,需要更新固件(版本V1.6.10以上),另外示例需要参考最新的扫描键盘示例。

以上两种方式都是在应用层面实现的,其实如果扫描键盘的pin脚固定,更好的方式可以在底层用C++实现,并且还可以把8个物理pin脚,虚拟出16个pin脚来,用法和物理的pin脚完全一样。

官方SimpleWPFApplication示例,是一个比较典型的WPF应用,但是需要5个按键才能操作,我们的紫藤207系统仅提供了一个物理按钮,所以是无法操作的。接上扫描键盘后,我们就有可能完整的演示这个示例了,不过由于我们使用的是扫描键盘,所以原程序无法使用,必须做如下修改才可以。

    public sealed class GPIOButtonInputProvider

    {

        public readonly DispatcherDispatcher;

        privateDispatcherOperationCallback callback;

        privateInputProviderSite site;

        privatePresentationSource source;

 

        publicGPIOButtonInputProvider(PresentationSourcesource)

        {

            this.source= source;

            site = InputManager.CurrentInputManager.RegisterInputProvider(this);

            callback = newDispatcherOperationCallback(delegate(objectreport)

            {

                InputReportArgsargs = (InputReportArgs)report;

                returnsite.ReportInput(args.Device, args.Report);

            });

            Dispatcher = Dispatcher.CurrentDispatcher;

 

            Cpu.Pin[] Output_Pins = { (Cpu.Pin)GPIO_NAMES.PC8,(Cpu.Pin)GPIO_NAMES.PC9, (Cpu.Pin)GPIO_NAMES.PB7,(Cpu.Pin)GPIO_NAMES.PC2 };

            Cpu.Pin[] Input_Pins = { (Cpu.Pin)GPIO_NAMES.PC3,(Cpu.Pin)GPIO_NAMES.PA0, (Cpu.Pin)GPIO_NAMES.PA5,(Cpu.Pin)GPIO_NAMES.PA6 };

           

            InterruptKeypadkey = new InterruptKeypad(Output_Pins,Input_Pins);

            key.OnInterrupt += new NativeEventHandler(key_OnInterrupt);

        }

 

        voidkey_OnInterrupt(uint data1, uint data2, DateTimetime)

        {

            RawButtonActionsaction = (data2 != 0) ? RawButtonActions.ButtonUp: RawButtonActions.ButtonDown;

            RawButtonInputReportreport = new RawButtonInputReport(source,time, GetButton(data1), action);

            Dispatcher.BeginInvoke(callback, new InputReportArgs(InputManager.CurrentInputManager.ButtonDevice,report));

        }

 

        ButtonGetButton(uint data)

        {

            switch(data)

            {

                case2:

                    returnButton.VK_UP;

                case5:

                    returnButton.VK_LEFT;

                case6:

                    returnButton.VK_SELECT;

                case10:

                    returnButton.VK_DOWN;

                case7:

                    returnButton.VK_RIGHT;

            }

            returnButton.None;

        }

    }

把GpioButtonInputProvider.cs里面的程序这样修改后,就可以使用了。

效果图如下:
image.png

实际运行视频链接如下:

 http://v.youku.com/v_show/id_XNDI3ODU4OTg4.html

从视频可以看出,STM32F207平台运行WPF程序还是蛮流畅的。

------------------------------------------------------------------------------- 

下载地址:http://www.sky-walker.com.cn/MFRelease/Sample/ScanKey_WPFTest.rar     

MF简介:http://blog.csdn.net/yefanqiu/article/details/5711770

MF资料:http://www.sky-walker.com.cn/News.asp?Id=25

作者:叶帆
来源:CSDN
原文:https://blog.csdn.net/yfiot/article/details/7749732
版权声明:本文为博主原创文章,转载请附上博文链接!

相关实践学习
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
5天前
|
人工智能 Java 物联网
Java与边缘AI:构建离线智能的物联网与移动应用
随着边缘计算和终端设备算力的飞速发展,AI推理正从云端向边缘端迁移。本文深入探讨如何在资源受限的边缘设备上使用Java构建离线智能应用,涵盖从模型优化、推理加速到资源管理的全流程。我们将完整展示在Android设备、嵌入式系统和IoT网关中部署轻量级AI模型的技术方案,为构建真正实时、隐私安全的边缘智能应用提供完整实践指南。
86 3
|
9月前
|
传感器 人工智能 监控
AI与物联网的融合:开启智能化未来的新篇章
AI与物联网的融合:开启智能化未来的新篇章
1458 96
|
11月前
|
存储 边缘计算 物联网
揭秘边缘计算:物联网时代的分布式智能
揭秘边缘计算:物联网时代的分布式智能
643 92
|
7月前
|
存储 监控 安全
工业物联网关应用:PLC数据通过智能网关上传阿里云实战
本文介绍如何使用智能网关将工厂PLC数据传输至阿里云平台,适合中小企业远程监控设备状态。硬件准备包括三菱FX3U PLC、4G智能网关和24V电源。接线步骤涵盖PLC编程口与网关连接、运行状态检测及天线电源接入。配置过程涉及通讯参数、阿里云对接和数据点映射。PLC程序关键点包括数据上传触发和温度值处理。阿里云平台操作包含实时数据查看、数据可视化和规则引擎设置。最后提供常见故障排查表和安全建议,确保系统稳定运行。
602 1
|
11月前
|
物联网 智能硬件
物联网技术:连接智能生活的桥梁
物联网技术:连接智能生活的桥梁
|
4月前
|
应用服务中间件 网络安全 数据安全/隐私保护
网关服务器配置指南:实现自动DHCP地址分配、HTTP服务和SSH无密码登录。
哇哈哈,道具都准备好了,咱们的魔术秀就要开始了。现在,你的网关服务器已经魔法满满,自动分配IP,提供网页服务,SSH登录如入无人之境。而整个世界,只会知道效果,不会知道是你在幕后操控一切。这就是真正的数字世界魔法师,随手拈来,手到擒来。
221 14
|
监控 负载均衡 安全
微服务(五)-服务网关zuul(一)
微服务(五)-服务网关zuul(一)
|
12月前
|
安全 5G 网络性能优化
深入理解5G中的SAEGW:服务网关边界
【10月更文挑战第9天】
397 0
|
运维 Kubernetes 安全
利用服务网格实现全链路mTLS(一):在入口网关上提供mTLS服务
阿里云服务网格(Service Mesh,简称ASM)提供了一个全托管式的服务网格平台,兼容Istio开源服务网格,用于简化服务治理,包括流量管理和拆分、安全认证及网格可观测性,有效减轻开发运维负担。ASM支持通过mTLS提供服务,要求客户端提供证书以增强安全性。本文介绍如何在ASM入口网关上配置mTLS服务并通过授权策略实现特定用户的访问限制。首先需部署ASM实例和ACK集群,并开启sidecar自动注入。接着,在集群中部署入口网关和httpbin应用,并生成mTLS通信所需的根证书、服务器证书及客户端证书。最后,配置网关上的mTLS监听并设置授权策略,以限制特定客户端对特定路径的访问。
358 2
|
10月前
|
NoSQL 前端开发 测试技术
👀探秘微服务:从零开启网关 SSO 服务搭建之旅
单点登录(Single Sign-On,简称SSO)是一种认证机制,它允许用户只需一次登录就可以访问多个应用程序或系统。本文结合网关和SaToken快速搭建可用的Session管理服务。
546 8

热门文章

最新文章

相关产品

  • 物联网平台