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

.NET Micro Framework模擬器提供了5個模擬按鍵(上、下、左、右和確認按鍵),因此通常.NET MF開發板也只須要提供5個按鍵就能夠了,而這5個鍵,也是直接和CPU的pin腳相連,用GPIO的輸入相關的函數就能夠操做了,使用很是簡單。html

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


上述掃描鍵盤的原理圖應該是最簡單的一種了,複雜一點的,在行或列上,經過一個上拉電阻接VCC。這樣,咱們只須要8個pin腳,就能夠獲取16個按鍵的信息了。編程

通常實現的思路也比較簡單:就是把行(或列)接芯片輸出pin腳,把列(或行)接芯片輸入pin腳,輸出pin腳依次輸出低(或高,須要看電路中接的上拉仍是下拉電阻)電平,而後檢查輸入pin腳的電平變化。若是有變化,那麼就說明,該列和該行的按鍵被按下了。windows

每每這個判斷就放在while循環或線程裏,不斷的去運行。對一些單片而言,若是實現的功能單一,這樣作也無可厚非,可是對一個系統平臺來講,若是也這樣作,顯然對系統的資源佔用仍是比較厲害的。ide

因此最好的辦法仍是要採用中斷的方式,平時的時候不去判斷,靠中斷觸發,一旦中斷觸發了,而後再啓動一輪判斷,肯定是哪個按鍵被按下了。函數

一、掃描方式實現按鍵獲取ui

public class ScanKeypadthis

    {spa

        public event NativeEventHandlerOnInterrupt;.net

 

        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);

            }

        }

    }

二、中斷方式實現按鍵獲取

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:在實現中斷方式的掃描鍵盤的代碼的時候,發現PB六、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裏面的程序這樣修改後,就可使用了。

效果圖以下:

實際運行視頻連接以下:

 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

相關文章
相關標籤/搜索