Aldens Blog

  • Fun with Buttons

    Fun with Buttons

    by James Webster, Test Manager, .NET Micro Framework

    and Jerry Kindall, Programmer/Writer

     

    Many programs running on the Microsoft® .NET Micro Framework get user input through General Purpose Input/Output (GPIO)-type buttons on their respective hardware devices. The .NET Micro Framework hardware emulator provides five built-in GPIO buttons – four directional buttons and a Select button. Of course, some devices have additional buttons.

     

    When you press or release a button, the GPIO interface generates an interrupt. The .NET Micro Framework translates these interrupts into events called ButtonUp and ButtonDown. You can register your own handler to receive either or both of these events.

     

    Microsoft Visual Studio® can provide the code you need to get started handling button events when you create a new .NET Micro Framework project. To start a new project in Visual Studio, perform the following steps.

    1                    On the File menu, point to New and then click Project.

    2                    In the New Project window, expand the Visual C# node in the Project Types pane if it is not already expanded.

    3                    Under Visual C#, click Micro Framework. You’ll see the standard .NET Micro Framework project templates displayed in the Templates pane.

    4                    In the Templates pane, double-click Window Application.

     

    This article shows you how to write code that handles button events using the .NET Micro Framework. Keep in mind that you won’t always have to do all this work yourself. Many .NET Micro Framework user interface elements can handle buttons for you. For example, the ListBox class supports using the Up and Down buttons to move the selection bar through a list; you do not need to handle this task yourself. Even in this case, though, you will need to handle the Select button, so it is still important to be familiar with techniques for handling button input.

    Button Mapping

    A new MFWindowApplication project like the one you’ve just created contains an automatically generated class called GPIOButtonInputProvider. This class establishes a mapping scheme between the device’s GPIO pins and the buttons they represent.

     

    The GPIOButtonInputProvider class works with the .NET Micro Framework emulator, which uses GPIO pins 0 through 4 for buttons. If you are running your applications on the emulator (or on a hardware device with the same button mappings), no changes are needed. If you are using a hardware device with other button mappings, you can modify the class to map the correct pins to the buttons. A recent article on this site, titled “Getting Started with Freescale i.MXS,” provides an example of adapting the GPIOButtonInputProvider class for the Freescale i.MXS platform. The same concept applies to other hardware, although the details might differ. The code in this article uses the default mappings.

    Basic Button Fun

    The Program.cs file in a fresh MFWindowApplication project includes a stub event handler method, OnButtonUp, which is called when any button is released. The stub prints the name of the pressed button in the Output window in Visual Studio. This method is where you will add your own code, probably using a switch statement to provide an action for each button, as in the following sample method:

     

    private void OnButtonUp(object sender, ButtonEventArgs e)

    {

        switch (e.Button) // e is the event record

        {

    case Button.Left:

        ((Text)mainWindow.Child).TextContent = "Left";

        break;

    case Button.Right:

        ((Text)mainWindow.Child).TextContent = "Right";

        break;

    case Button.Up:

        ((Text)mainWindow.Child).TextContent = "Up";

        break;

    case Button.Down:

        ((Text)mainWindow.Child).TextContent = "Down";

        break;

    case Button.Select:

        ((Text)mainWindow.Child).TextContent = "Select";

        break;

    default:

        ((Text)mainWindow.Child).TextContent = "Button " + e.Button;

        break;

        }

    }

     

    Our example handler places the name of any pressed button in the middle of the screen (where the text “Hello World!” initially appears). Try this now by replacing the stub OnButtonUp method with the one shown here, then pressing F5 to run the project in the .NET Micro Framework emulator.

    Advanced Button Fun

    The stub handler in the Window Application template responds to ButtonUp events, which means that your code does not receive an event until the user has already released the button. This is the conventional way to handle simple button events, but in some cases, it is too late to be of any use. For example, you might want a button to repeatedly perform some action as long as it is held down. This requires code to run when the button is first pressed. The .NET Micro Framework provides a ButtonDown event that you can use in conjunction with a timer to implement this functionality.

     

    The Timer object in the System.Threading namespace periodically calls a method that you specify when you instantiate the timer. This callback method actually does most of the work; the ButtonDown and ButtonUp handlers primarily serve to instantiate and dispose of the timer. In this section, you will learn how to program the Up and Down buttons to continuously increment or decrement a counter. Additionally, the longer the buttons are held down, the faster the counter changes. You can apply similar techniques to graphical elements or controls, such as sliders.

     

    If you have not used timers before, you may not know that a method called by a timer runs in its own thread. For this reason, you should use the lock statement to synchronize the timer thread and the main thread whenever you are entering a critical section of code—that is, any code that touches the counter or the timer object while more than one thread might be running. You can only lock on object references, so in this example, the timer will be used for locking.

    Preliminary Steps

    To get started, create a new .NET Micro Framework Window Application as before, then open the Program.cs file in the new project. Near the top of the file, with the other using directives, add the following code to facilitate easier use of the Timer class.

     

    using System.Threading;

     

    Next, find the following lines in the CreateWindow class.

     

    // Connect the button handler to all of the buttons.

    mainWindow.AddHandler(Buttons.ButtonUpEvent,

        new ButtonEventHandler(OnButtonUp), false);

     

    After these lines, insert the following line to add a ButtonDown event handler. By convention, this method is named OnButtonDown. You will add the code for this method later.

     

    mainWindow.AddHandler(Buttons.ButtonDownEvent,

        new ButtonEventHandler(OnButtonDown), false);

     

    Now, somewhere in the Program class, add the following declarations. (They can be anywhere in the class as long as they are not in a method, but you might want to put them right before the stub OnButtonUp method so they will be close to the methods that use them.) The variable counterValue is the number we will be adjusting with the buttons. The rest of these variables have to do with the timer that will provide the repeat function.

     

    // the value to be adjusted using Up/Down buttons

    private int counterValue = 50;

     

    // initial, minimum, and current settings for timer in milliseconds

    private const int initialInterval = 500;

    private const int minimumInterval = 50;

    private int currentInterval;

     

    // the active timer

    private Timer repeatTimer = null;

    OnButtonDown

    With the preceding code in place, insert the following lines to add the OnButtonDown method.

     

    // handle a button press, starting a timer for repeats

    private void OnButtonDown(object sender, ButtonEventArgs e)

    {

        HandleButtons(e);

        lock (repeatTimer)

        {

            // dispose of existing timer (in case more than one button is down)

            if (repeatTimer != null) repeatTimer.Dispose();

     

            // start the timer firing every initialInterval milliseconds

            currentInterval = initialInterval;

            if (e.Button == Button.Up || e.Button == Button.Down)

                repeatTimer = new Timer(OnTimer, e, currentInterval,

                    currentInterval);

        }

    }

     

    The OnButtonDown method receives control whenever any button is pressed. First, it calls the HandleButtons method (passing the event record, e, so that HandleButtons knows which button was pressed and can adjust counterValue appropriately). If the Up button or Down button was pressed, this method instantiates a Timer object, specifying the OnTimer method (which we will discuss later in this article) as the callback function. Timers can pass a state object to the callback; in this example we specify the event record, e, because OnTimer needs the event record to pass to HandleButtons.

     

    It is possible for a user to press two or more buttons simultaneously on an actual hardware device. Although this is not possible with the emulator, you should address this possibility in your code so that the application does not behave in a way the user does not expect. If the timer already exists (as indicated by a non-null value in repeatTimer), meaning that another button is already pressed, the existing timer is disposed of before a new one is instantiated. This results in “last button wins” behavior that users will find natural.

    OnButtonUp

    The ButtonUp event handler, OnButtonUp, merely disposes of the timer to stop the repeating behavior. You should replace the stub OnButtonUp handler with the following:

     

    // stop the repeat timer when the user releases a button

    private void OnButtonUp(object sender, ButtonEventArgs e)

    {

        lock (repeatTimer)

        {

            // if a timer exists, dispose of it

            if (repeatTimer != null) repeatTimer.Dispose();

            repeatTimer = null;

        }

    }

     

    The OnButtonUp method is called when any button is released, not just the Up button or the Down button, although the latter two buttons are the only ones that start the timer. Therefore, we dispose of the timer only if it actually exists (that is, if the repeatTimer reference is non-null). Otherwise, releasing the Select button, for example, would throw an exception trying to dispose of a timer that does not exist. (If a user has pressed more than one button on the device at the same time, this means that the first button that is released will stop the timer. Releasing any other buttons afterward will have no effect.) After disposing of the timer, we set repeatTimer to null to record that the timer no longer exists.

    OnTimer

    The OnTimer method, which follows, is called periodically by the timer, initially every 500 milliseconds (every half second).

     

    // called by the timer to repeat a button action

    private void OnTimer(Object e)

    {

        HandleButtons(e);

        lock (repeatTimer)

        {

            // decrease timer interval for the next repeat

            if (currentInterval > minimumInterval)

            {

                currentInterval -= 50;

                if (currentInterval < minimumInterval)

                    currentInterval = minimumInterval;

                repeatTimer.Change(currentInterval, currentInterval);

            }

        }

    }

     

    The first thing OnTimer does is call HandleButtons to make sure that the counter gets updated appropriately for the button being held. Then the timer interval is reduced so that the repeated increment or decrement action goes faster (up to a prescribed limit) the longer the button is held down.

    HandleButtons

    The HandleButtons method is the last method needed for our press-and-hold functionality. It contains a switch statement much like the one you saw in the “Basic Button Fun” section earlier in this article. Because OnButtonDown and OnTimer both need this functionality to process button actions, the necessary code has been “broken out” into HandleButtons, as follows:

                                                                                                       

    // button handler called from OnButtonDown and by timer

    private void HandleButtons(Object e)

    {

        lock (repeatTimer)

        {

            switch (((ButtonEventArgs)e).Button)

            {

                case Button.Up:

                    if (counterValue < 99) counterValue++;

                    break;

                case Button.Down:

                    if (counterValue > 0) counterValue--;

                    break;

                case Button.Select:

                    counterValue = 50;

                    break;

            }

        }

     

        // display the new value of the counter on the screen

        ((Text)mainWindow.Child).TextContent = counterValue.ToString();

        mainWindow.Child.Invalidate();

    }

     

    The only slightly tricky aspect of the HandleButtons method is the cast of the event record, e, to type ButtonEventArgs, which is necessary because the method is used as a callback function for a timer, which can pass any object reference. Note also that we need to invalidate the text object to force it to be redrawn immediately. The rest of the code should be familiar to you by now.

    Trying It Out

    After making the preceding changes to Program.cs, save it and run it in the .NET Micro Framework emulator by pressing F5. Hold the emulator’s Up button and observe how the displayed value changes slowly at first, but then changes faster and faster as you continue to hold the button down. Also notice how you can easily make fine adjustments by pressing and releasing the buttons quickly. This behavior comes for “free”—as long as you release the button before the initial timer interval, it acts much like it would have if you had programmed only a ButtonUp handler. Behind the scenes, of course, a timer is being instantiated by OnButtonDown, then disposed of by OnButtonUp before it has time to activate.

     

    The emulator screen initially displays “Hello World!” until you press the Up button, the Down button, or the Select button. This is a leftover behavior from the Window Application template. You can eliminate it by opening the Resources.resx file and changing the value of String1 to 50, the initial value of the counter, as in the following illustration.

     

     

    As an exercise, you might add functionality for the Left and Right buttons. Perhaps the Left button could set the counter to 0 and the Right button could set it to 99, providing easier access to the top and bottom of the counter’s range. This requires adding just a few lines of code to one method. A more ambitious addition would be to give the Left and Right buttons repeat functionality as well, perhaps having them add or subtract 5 from the counter.


    Copyright © 2007 Microsoft Corporation. All rights reserved. This article was originally published on MSDN Blogs.

  • Getting Started with Freescale i.MXS

    Getting Started with Freescale i.MXS

    The Freescale i.MXS applications processor is a low-cost ARM-based platform for deploying software solutions developed with the Microsoft® .NET Micro Framework. To develop applications for i.MXS, you need the i.MXS Development Kit, which is available from Freescale. In particular, the i.MXS USB device driver included in the i.MXS Development Kit must be installed on your computer before you can deploy applications to your i.MXS device. The Development Kit also includes a wealth of other information, including schematics, sample code, and a hardware emulator.

     

    You can download the i.MXS Development Kit from the following location on the Freescale Web site: http://www.freescale.com/files/32bit/doc/support_info/iMXS_DevKit_CD_Contents.zip

     

    You may want to run the samples included with the .NET Micro Framework on the i.MXS hardware to see how they behave and to better understand how to run your own applications on the platform. To do this, you must make some minor changes to the samples.

     

    There are two key differences between the i.MXS hardware and the .NET Micro Framework emulator:

    • The i.MXS buttons are mapped to different General Purpose Input/Output (GPIO) pins than the emulator’s buttons. Thus, the i.MXS buttons will not be recognized when the samples are run.
    • The i.MXS buttons use a different resistor mode than the emulator’s do. This will cause the samples to throw an exception when you press an i.MXS button.

     

    The following section of this article discusses the simple changes you need to make before you can run the Presentation sample solution on the i.MXS Development Kit hardware. For ease of development and testing, you can use conditional compilation so that your solution can be run either on the .NET Micro Framework emulator or on the i.MXS hardware. You can make similar changes to the other .NET Micro Framework samples and to your own code.

     

    The last part of this article explains how to deploy solutions to the i.MXS device for testing. If you have previously deployed software to other platforms by means of USB, you are already familiar with the steps you’ll be using for your i.XMS deployments.

    Changing the Code

    Start by opening the Presentation sample solution, following these steps.

    1        On the Start menu, point to All Programs.

    2        Point to Microsoft .NET Micro Framework, then click Samples.

    3        Double-click the Presentation folder.

    4        Double-click the Presentation solution icon.

     

    With the Presentation solution open, you can now edit the GPIOButtInputProvider.cs file.

    Button Mapping

    The i.MXS hardware uses GPIO pins numbered in the 40s for its button interface, whereas the .NET Micro Framework emulator uses pins 0 through 4. An added wrinkle is that the .NET Micro Framework Cpu.Pin enumeration includes only pins 0 through 15, which means that there is no pin named Cpu.Pin.GPIO_Pin40 (pin 40 is the Select button on the i.MXS hardware). Thus, you must represent the i.MXS pin numbers as integers in your C++ code, using the (Cpu.Pin) type casting syntax. In code, then, you would represent the i.MXS Select button as (Cpu.Pin)40.

     

    To address the issue of the mapping between buttons and GPIO pins, first define a symbol to be used for the conditional code section. At the beginning of the file, add the following two lines of code:

     

    // comment out next line when running on the emulator

    #define RUN_ON_IMXS_HARDWARE

     

    Next, update the GPIO mapping for the buttons by editing the ButtonPinMap class. The following lines are the code that needs changing:

     

    // associate the buttons to the pins as setup in the emulator/hardware

    m_buttonToPin[(Int32)Button.Select] = Cpu.Pin.GPIO_Pin3;

    m_buttonToPin[(Int32)Button.Up] = Cpu.Pin.GPIO_Pin2;

    m_buttonToPin[(Int32)Button.Down] = Cpu.Pin.GPIO_Pin4;

    m_buttonToPin[(Int32)Button.Left] = Cpu.Pin.GPIO_Pin0;

    m_buttonToPin[(Int32)Button.Right] = Cpu.Pin.GPIO_Pin1;

     

    Change this section of code to read as follows (the bold lines are the added code):

     

    // associate the buttons to the pins as setup in the emulator/hardware

    #if RUN_ON_IMXS_HARDWARE

              m_buttonToPin[(Int32)Button.Select] = (Cpu.Pin)40;

              m_buttonToPin[(Int32)Button.Up] = (Cpu.Pin)43;

              m_buttonToPin[(Int32)Button.Down] = (Cpu.Pin)42;

              m_buttonToPin[(Int32)Button.Left] = (Cpu.Pin)48;

              m_buttonToPin[(Int32)Button.Right] = (Cpu.Pin)44;

    #else

                m_buttonToPin[(Int32)Button.Select] = Cpu.Pin.GPIO_Pin3;

                m_buttonToPin[(Int32)Button.Up] = Cpu.Pin.GPIO_Pin2;

                m_buttonToPin[(Int32)Button.Down] = Cpu.Pin.GPIO_Pin4;

                m_buttonToPin[(Int32)Button.Left] = Cpu.Pin.GPIO_Pin0;

                m_buttonToPin[(Int32)Button.Right] = Cpu.Pin.GPIO_Pin1;

    #endif

    Resistor Mode

    The sample code uses the PullDown resistor mode. However, the i.MXS reference platform requires the PullUp mode because the hardware does not have pull-down resistors. If you try to use the PullDown mode, an exception occurs when you press any of the i.MXS buttons.

     

    Conveniently, the .NET Micro Framework emulator works correctly with either resistor mode, so you can just change the mode to PullUp. Conditional code is not needed in this case.

     

    In the sample solution, locate the GpioButtonInputProvider constructor, and then change PullDown to PullUp, as shown in the following examples.

     

    Before editing:

    InterruptPort port = new InterruptPort(pin, true,

                Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeBoth);

     

    After editing:

     

    InterruptPort port = new InterruptPort(pin, true,

    Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);

     

    These are the only code changes required for the Presentation solution.

    Deploying a Solution

    Deploying a software solution to the i.MXS hardware is even simpler than making the code changes. First, click Configuration Manager on the Build menu. Then make sure that the check box under Deploy is selected in the Configuration Manager dialog box, as shown in the following illustration.

     

    http://blogs.msdn.com/photos/aldenl/picture2062327.aspx

     

    Now edit the solution’s properties to run it on the i.MXS hardware, following these steps.

    1        On the Build menu, click Presentation Properties.

    2         Click the Micro Framework tab.

    3        Under Deployment, select USB in the Transport box.

    4        In the Device box, select the connected i.MXS device, as shown in the following screen shot.

     

    http://blogs.msdn.com/photos/aldenl/picture2062339.aspx

     

    If there are no i.MXS devices displayed in the Device box after you select USB in the Transport box, this usually means that the USB device driver is not installed on your computer. If you are sure that it is installed, check your USB connections.

     

    Finally, click Deploy Solution on the Build menu to download your code to the i.MXS device.

    Conclusion

    Developing .NET Micro Framework solutions for the i.MXS platform is much like developing applications for other supported platforms. While some code changes are needed, they are few and minor. Using conditional compilation, you can make one version of the solution that works with both the i.MXS hardware and the .NET Micro Framework emulator. Why not download the i.MXS Development Kit today and try this for yourself?

     

  • Using Fonts in the Microsoft .NET Micro Framework

    This is a short primer on how to use fonts in the Microsoft® .NET Micro Framework. In this article, I’ll show you how to add a font to a project that can be used in the Windows® Presentation Foundation (WPF), which is a desktop application development framework.

     

    First you’ll need to create a new .NET Micro Framework project in Microsoft Visual Studio®, using the following steps:

    1. On the File menu, point to New and then click Project.
    2. Under Project types, expand the Visual C# node, and then click Micro Framework.
    3. Under Templates, click Window Application and then click OK.

    Now let’s add a font to our new project. You’ll notice that under the Resources node  in Solution Explorer there’s already a file named small.tinyfnt, as shown in the following illustration.

     

    http://blogs.msdn.com/photos/aldenl/images/1882313/original.aspx

     

    Next, browse to and open the program.cs file so that we can add some text to it that uses our new font. In program.cs, change line 39 from the following:

          text.Font = Resources.GetFont(Resources.FontResources.small);

            to this:

          text.Font = Resources.GetFont(Resources.FontResources.NinaB);

     

    Behind the scenes, the .NET Micro Framework uses an index number to search for resources in the system. This is unlike the .NET Framework on the desktop, where you can use strings to search for resources. For example, the desktop accesses fonts by using the following code:

          Font ft = new Font("Arial", 30);

     

    What’s important here is that the .NET Micro Framework reduced the code size by using an index number rather than by using a bulky string for each resource and by removing the ability to specify a font size. Notice that the Resources.Designer.cs file has an index number appended to the enumeration, as shown in the following code snippet:

     

     

    internal enum FontResources : short

    {

        small = 13070,

        NinaB = 18060,

    }

    As you can see, this enumeration is what was used in the earlier Resources.GetFont() call to access the resource.

     

    The other basic difference between the two frameworks is that with the desktop, you can define the size of the fonts you use, whereas with the .NET Micro Framework, fonts have a predetermined size and are displayed only in that size.

     

    These .tinyfnt files are a new font format used by the .NET Micro Framework. The .NET Micro Framework SDK contains additional prebuilt .tinyfnt files, so now we’ll go ahead and add a reference to one of the other .tinyfnt files, using the following steps:

    1.  Double-click Resources.resx in Solution Explorer.

    2.  On the Resources.resx tab, click the down arrow to the right of Add Resources, and then click Add Existing File on the drop-down menu, as shown in the following illustration.

     

    http://blogs.msdn.com/photos/aldenl/images/1882317/original.aspx

     

    3. In the Add existing file to resources dialog box, select All Files in the Files of type box.

    4. While still in the dialog box, browse to \\Program Files\Microsoft .NET Micro Framework\vX.X.XXXX\Fonts, where X.X.XXXX is the version number of your .NET Micro Framework SDK.

    5.  In the Fonts folder, double-click the file NinaB.tinyfnt.

    6.  Finally, build the solution by clicking Rebuilt Solution on the Build menu.

     

    And that’s all there is to it. You now have another font you can use in your .NET Micro Framework projects. 

     

    Note: There are only two .tinyfnt files currently available in the .NET Micro Framework, and at present there’s no way to create or add custom fonts.  This is something that is being considered for the next release of the framework.

     


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker