【物聯網智能網關-05】掃描鍵盤編程設計

來源:互聯網
上載者:User

.NET Micro Framework模擬器提供了5個類比按鍵(上、下、左、右和確認按鍵),所以一般.NET MF開發板也只需要提供5個按鍵就可以了,而這5個鍵,也是直接和CPU的pin腳相連,用GPIO的輸入相關的函數就可以操作了,使用非常簡單。

但是對一些特殊的應用,如一些.NET Micro Framework教育箱或一些工業實際用的系統,5個按鍵顯然太少了點。但是如果需要十幾個按鍵,如果直連晶片pin腳,顯然佔用的資源比較多了,也會導致其它的功能無法使用了,這時候最常用的就是掃描鍵盤了。

 

上述掃描鍵盤的原理圖應該是最簡單的一種了,複雜一點的,在行或列上,通過一個上拉電阻接VCC。這樣,我們只需要8個pin腳,就可以擷取16個按鍵的資訊了。

一般實現的思路也比較簡單:就是把行(或列)接晶片輸出pin腳,把列(或行)接晶片輸入pin腳,輸出pin腳依次輸出低(或高,需要看電路中接的上拉還是下拉電阻)電平,然後檢查輸入pin腳的電平變化。如果有變化,那麼就說明,該列和該行的按鍵被按下了。

往往這個判斷就放在while迴圈或線程裡,不斷的去運行。對一些單片而言,如果實現的功能單一,這樣做也無可厚非,但是對一個系統平台來說,如果也這樣做,顯然對系統的資源佔用還是比較厲害的。

所以最好的辦法還是要採用中斷的方式,平時的時候不去判斷,靠中斷觸發,一旦中斷觸發了,然後再啟動一輪判斷,確定是哪一個按鍵被按下了。

1、掃描方式實現按鍵擷取

public class ScanKeypad    {        public event NativeEventHandler OnInterrupt;         OutputPort[] rows = null;        InputPort[] cols = null;        public ScanKeypad(Cpu.Pin[] Output_Pins, Cpu.Pin[] Input_Pins)        {            rows = new OutputPort[] { new OutputPort(Output_Pins[0], false), new OutputPort(Output_Pins[1], false), new OutputPort(Output_Pins[2], false), new OutputPort(Output_Pins[3], false) };            cols = new InputPort[] { new InputPort(Input_Pins[0], true, Port.ResistorMode.PullUp), new InputPort(Input_Pins[1], true, Port.ResistorMode.PullUp), new InputPort(Input_Pins[2], true, Port.ResistorMode.PullUp), new InputPort(Input_Pins[3], true, Port.ResistorMode.PullUp) };             Thread threadKeypad = new Thread(new ThreadStart(KeypadScan));            threadKeypad.Start();        }         void KeypadScan()        {            int key = -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;                }                else if (oldKey > -1 && key == -1)                {                    if (OnInterrupt != null) OnInterrupt((uint)oldKey, 0, DateTime.Now);                    oldKey = -1;                }                Thread.Sleep(100);            }        }    }

 

2、中斷方式實現按鍵擷取

public class InterruptKeypad    {        public event NativeEventHandler OnInterrupt;         OutputPort[] rows = null;        InterruptPort[] cols = null;        Cpu.Pin[] Pins = null;        uint key = 0;        public InterruptKeypad(Cpu.Pin[] Output_Pins, Cpu.Pin[] Input_Pins)        {            rows = new OutputPort[] { new OutputPort(Output_Pins[0], false), new OutputPort(Output_Pins[1], false), new OutputPort(Output_Pins[2], false), new OutputPort(Output_Pins[3], false) };            cols = new InterruptPort[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);            }        }         private uint GetPinIndex(uint pin)        {            for (uint i = 0; i < Pins.Length; i++)            {                if (pin == (uint)Pins[i]) return i;            }            return 0;        }         void InterruptKeypad_OnInterrupt(uint data1, uint data2, DateTime time)        {            if (data2 == 1)            {                for (int i = 0; i < cols.Length; i++)                {                    cols[i].OnInterrupt -= new NativeEventHandler(InterruptKeypad_OnInterrupt);                }                //--                uint col = 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);                }            }        }         void KeypadRun()        {            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腳不能使用。

以上兩種方式都是在應用程式層面實現的,其實如果掃描鍵盤的pin腳固定,更好的方式可以在底層用C++實現,並且還可以把8個物理pin腳,虛擬出16個pin腳來,用法和物理的pin腳完全一樣。

官方SimpleWPFApplication樣本,是一個比較典型的WPF應用,但是需要5個按鍵才能操作,我們的紫藤207系統僅提供了一個物理按鈕,所以是無法操作的。接上掃描鍵盤後,我們就有可能完整的示範這個樣本了,不過由於我們使用的是掃描鍵盤,所以原程式無法使用,必須做如下修改才可以。

   

 public sealed class GPIOButtonInputProvider    {        public readonly Dispatcher Dispatcher;        private DispatcherOperationCallback callback;        private InputProviderSite site;        private PresentationSource source;         public GPIOButtonInputProvider(PresentationSource source)        {            this.source = source;            site = InputManager.CurrentInputManager.RegisterInputProvider(this);            callback = new DispatcherOperationCallback(delegate(object report)            {                InputReportArgs args = (InputReportArgs)report;                return site.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 };                       InterruptKeypad key = new InterruptKeypad(Output_Pins, Input_Pins);            key.OnInterrupt += new NativeEventHandler(key_OnInterrupt);        }         void key_OnInterrupt(uint data1, uint data2, DateTime time)        {            RawButtonActions action = (data2 != 0) ? RawButtonActions.ButtonUp : RawButtonActions.ButtonDown;            RawButtonInputReport report = new RawButtonInputReport(source, time, GetButton(data1), action);            Dispatcher.BeginInvoke(callback, new InputReportArgs(InputManager.CurrentInputManager.ButtonDevice, report));        }         Button GetButton(uint data)        {            switch (data)            {                case 2:                    return Button.VK_UP;                case 5:                    return Button.VK_LEFT;                case 6:                    return Button.VK_SELECT;                case 10:                    return Button.VK_DOWN;                case 7:                    return Button.VK_RIGHT;            }            return Button.None;        }    }

 

把GpioButtonInputProvider.cs裡面的程式這樣修改後,就可以使用了。

如下: 

實際運行視頻連結如下:

 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

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.