Local, Global, and Shared Variables


Local and global variables are, technically speaking, LabVIEW structures. If you've done any programming in conventional languages like C or Pascal, then you're already familiar with the concept of a local or global variable. Up until now, we have read data from or written to a front panel object via its terminal on the block diagram. However, a front panel object has only one terminal on the block diagram, and you may need to update or read a front panel object from several locations on the diagram or other VIs.

Local variables (locals for short) provide a way to access front panel objects from several places in the block diagram of a VI in instances where you can't or don't want to connect a wire to the object's terminal.

Global variables allow you to access values of any data type (or several types at the same time if you wish) between several VIs in cases where you can't wire the subVI nodes or where several VIs are running simultaneously. In many ways, global variables are similar to local variables, but instead of being limited to use in a single VI, global variables can pass values among several VIs.

Shared variables are similar to global variables, but work across multiple local and networked applications. They also provide additional features that can buffer data and help avoid the synchronization problems that can be encountered with globals. VIs that use shared variables can run on all platforms, but can only be created on Windows.

This section will teach you some of the benefits of using locals, globals, and shared variables, as well as show you some common pitfalls to watch out for.

Local Variables

Local variables in LabVIEW are built-in objects that are accessible from the Programming>>Structures subpalette in the Functions palette (see Figure 13.1). When you select a local variable object, a node showing a "?" first appears to indicate the local is undefined. Clicking on this node with the Operating tool brings up a list of all current controls and indicators; selecting one of these will define the local. Or you can pop up on the local variable and choose Select Item to access this list (see Figure 13.2). You can also create a local variable by popping up on an object or its terminal and selecting Create>>Local Variable.

Figure 13.1. Local Variable structure, found on the Programming>>Structures palette


Figure 13.2. Associating a front panel control or indicator with the Local Variable structure on the block diagram


There are at least a couple of reasons why you might want to use locals in your VI:

  • You can do things, such as control parallel loops with a single variable, that you otherwise couldn't do.

  • Virtually any control can be used as an indicator, or any indicator as a control.

Controlling Parallel Loops

We've discussed previously how LabVIEW controls execution order through dataflow. The principle of dataflow is part of what makes LabVIEW so intuitive and easy to program. However, occasions may arise (and if you're going to develop any serious applications, the occasions will arise) when you will have to read from or write to front panel controls and indicators without wiring directly to their corresponding terminals. A classical problem is shown here: We want to end the execution of two independent While Loops with a single Boolean stop control (see Figure 13.3).

Figure 13.3. Two While Loops, but only one stop button (a dilemma)


How can we do this? Some might say we could simply wire the stop button to the loop terminals. However, think about how often the loops will check the value of the stop button if it is wired from outside the loops (see Figure 13.4).

Figure 13.4. Wiring one stop button into two While Loops (this doesn't work)


Wiring the stop button from outside the loops to both conditional terminals will not work, because controls outside the loops are not read again after execution of the loop begins. The loops in this case would execute only once if the stop button is TRUE when the loop starts, or execute forever if stop is FALSE.

So why not put the stop button inside one loop, as shown in Figure 13.5? Will this scheme work?

Figure 13.5. A stop button inside one While Loop and wired into the next (this doesn't work, either)


Putting the stop button inside one loop and stretching a wire to the other loop's conditional terminal won't do the trick either, for similar reasons. The second loop won't even begin until the first loop finishes executing (data dependency, remember?).

The solution to this dilemma isyou guessed ita local variable. Local variables create, in a sense, a "copy" of the data in another terminal on the diagram. The local variable always contains the up-to-date value of its associated front panel object. In this manner, you can access a control or indicator at more than one point in your diagram without having to connect its terminal with a wire.

Figure 13.6. A stop button inside one While Loop and a local variable inside another (this works)


Referring to the previous example, we can now use one stop button for both loops by wiring the Boolean terminal to one conditional terminal, and its associated local variable to the other conditional terminal.

There's one condition on creating a Boolean local variable: The front panel object can't be set to Latch mode (from the Mechanical Action option). Although it isn't obvious at first, a Boolean in Latch mode along with a local variable in read mode produces an ambiguous situation. Therefore, LabVIEW will give you the "broken arrow" if you create a local variable of a Boolean control set to Latch mode. On the other hand, you can use local variables to mimic the Latch mode, by writing a value of TRUE to the button after it is read, as you'll learn next.


Blurring the Control/Indicator Distinction

One of the really nice features about local variables is that they allow you to write to a control or read from an indicator, which is something you can't normally do with the regular terminals of an object. Locals have two modes: read and write. A local variable terminal can only be in one mode at a time, but you can create a second local terminal for the same variable in the other mode. Understanding the mode is pretty straightforward: in read mode, you can read the value from the local's terminal, just as you would from a normal control; in write mode, you can write data to the local's terminal, just as you would update a normal indicator. Just remember this formula for wiring locals:

READ mode = CONTROL

WRITE mode = INDICATOR

Another way to look at it is to consider a local in read mode the data "source," while a local in write mode is a data "sink."

You can set a local variable to either read or write mode by popping up on the local's terminal and selecting the Change To. . . . . option. A local variable in read mode has a heavier border around it than one in write mode (just like a control has a heavier border than an indicator), as shown in Figures 13.7 and 13.8.

Figure 13.7. Local variable (write mode)


Figure 13.8. Local variable (read mode)


Pay close attention to these borders when you are wiring the locals, to make sure you are in the correct mode. If you attempt to write to a local variable in read mode, for example, you'll get a broken wireand it may drive you crazy trying to figure out why.

Last but not least, you must give a label to the control or indicator that the local refers to. That is, when creating a control or indicator, if you don't give it a name (which is possible in LabVIEW), you won't be able to create a local variable for it.

As a simple example, let's say you want to create a knob that represents a timer: The user can set the time, and watch the knob turn as it counts down the time, just like those old-fashioned timers used in the kitchen. Obviously, the front panel object is going to have to be a control because the user will set the time, but it must also be able to accept block diagram values and "turn" accordingly as time goes by. Try building the example shown in Activity 13-1.

Activity 13-1: Using Local Variables

In this activity you will create a simple "kitchen timer" VI that uses a local variable to update the timer's dial as it counts down to zero, behaving just like a real kitchen timer.

1.

Build a front panel with a knob and select the Visible Items>>Digital Display option, as shown in Figure 13.9.

Figure 13.9. Front panel of the VI you will create during this activity


2.

Create a local variable by selecting it from the Programming>>Structures palette. You will get a local variable icon. Click on this icon with the operating tool and select Timer (seconds). The local should be in write mode by default.

3.

Build the following simple block diagram (see Figure 13.10).

Figure 13.10. Block diagram of the VI you will create during this activity


4.

Save your VI as Kitchen Timer.vi.

In this example, the shift register is initialized with the value of Timer that was set at the front panel. Once loop execution begins, the shift register's value is passed to the Timer local variable, which is in write mode, and then the shift register value is decremented. The Loop executes once per second, until the value counts down to zero. The knob on the front panel rotates to reflect the changed value.

A nice feature to add to this example would be a sound to alert you that the timer reached zero, just like the old-fashioned ones (Ding!). Later in this chapter, you will see how we can create simple sounds with LabVIEW.

Locals sound like a great structure to use, and they are. But you should watch out for a common pitfall when using locals: race conditions. A race condition occurs if two or more copies of a local variable in write mode can be written to at the same time.


There is a hazard to using locals and globals: accidentally creating a race condition. To demonstrate, build this simple example shown in Figures 13.11 and 13.12.

Figure 13.11. Front panel of another VI you will create during this activity


Figure 13.12. Block diagram of another VI you will create during this activity


Notice the two While Loops controlled by RUN and a local variable RUN. However, the local variable input received is being written in the left loop as well as in the right loop. Now, execute this VI with RUN set to FALSE and different values on the two sliders. The loops will execute just once. What value appears at current value? We can't tell you, because it could be the value from either input A or input B! There is nothing in LabVIEW that says execution order will be left-to-right, or top-to-bottom.

If you run the preceding VI with RUN turned on, you will likely see current value jump around between the two input values, as it should. To avoid race conditions such as this one, one must define the execution order by dataflow, sequence structures, or more elaborate schemes.

Another fact to keep in mind is that every read of a local creates a copy of the data in memory, a real problem for large arrays such as images. So when using locals, remember to examine your diagram and the expected execution order to avoid race conditions, and use locals sparingly if you're trying to reduce your memory requirement.

Activity 13-2: Fun with Locals

Another case where locals are very useful is in an application where you want a "Status" indicator to produce a more interactive VI. For example, you may want a string indicator that is updated with a comment or requests an input every time something happens in the application.

1.

Build a simple data acquisition VI similar to the ones you wrote in Chapter 11, "Data Acquisition in LabVIEW," but modify it to have a string indicator that tells the user:

  • when the program is waiting for input.

  • when the program is acquiring data.

  • when the program has stopped.

  • if the program had an error, indicate it.

To help you get started, Figures 13.13 and 13.14 show the front panel and block diagram.

Figure 13.13. Front panel of the VI you will create during this activity




Figure 13.14. Block diagram of the VI you will create during this activity


2.

Save your VI as Status Indicator.vi in the MYWORK folder.

Activity 13-3: More Fun with Locals

In many applications, you may want some type of "master" control that modifies the values on other controls. Suppose you wanted a simple panel to control your home stereo volumes. The computer presumably is connected to the stereo volume control in some way. In the VI shown next, a simulated sound control panel has three slide controls: left channel, right channel, and master. The left and right channel can be set independently; moving the master slide needs to increment or decrement the left and right volumes proportionally.

Build the block diagram for the front panel shown in Figure 13.15. The fun part about this is that by moving the master slide, you should be able to watch the other two slides move in response.

Figure 13.15. Front panel of the VI you will create during this activity


Save your VI as Master and Slave.vi.

You will need to use shift registers for this exercise.


Global Variables

Global variables can be powerful and necessary, but before we even begin, beware. They are perhaps the most misused and abused structure in programming. We've seen many examples of bad programming because of an over-reliance on global variables. Globals are more often than not the cause of mysterious bugs, unexpected behavior, and awkward programming structures. Having said this, there are still occasions when you might want to and need to resort to globals. (Again, it's not that globals are badthey just need to be used with caution.)

Recall that you can use local variables to access front panel objects at various locations in your block diagram. Those local variables are accessible only in that single VI. Suppose you need to pass data between several VIs that run concurrently or whose subVI icons cannot be connected by wires in your diagram. You can use a global variable to accomplish this. In many ways, global variables are similar to local variables, but instead of being limited to use in a single VI, global variables can pass values among several VIs.

Consider the following example. Suppose you have two VIs running simultaneously. Each VI writes a data point from a signal to a waveform chart. The first VI also contains a Boolean Power button to terminate both VIs. Remember that when both loops were on a single diagram, we needed to use a local variable to terminate the loops. Now that each loop is in a separate VI, we must use a global variable to terminate the loops. Notice that the global terminal is similar to a local terminal except that a global terminal has a "world" icon inside it (see Figures 13.1613.19).

Figure 13.16. First VI front panel


Figure 13.17. First VI block diagram


Figure 13.18. Second VI front panel


Figure 13.19. Second VI block diagram


Creating Globals

Like locals, globals are a LabVIEW structure accessible from the Programming>> Structures palette. And like locals, a single global terminal can be in write or read mode. Unlike locals, different VIs can independently call the same global variable. Globals are effectively a way to share data among several VIs without having to wire the data from one VI to the next; globals store their data independently of individual VIs. If one VI writes a value to a global, any VI or subVI that reads the global will contain the updated value.

Undefined Global Variable

Once the global structure is selected from the palette, an Undefined Global Variable appears on the diagram. The icon symbolizes a global that has not been defined yet. By double-clicking on this icon, you will see a screen pop-up that is virtually identical to a VI's front panel. You can think of globals as a special kind of VIthey can contain any type and any number of data structures on their front panels, but they have no corresponding diagram. Globals store variables without performing any kind of execution on these variables. Placing controls or indicators on a global's front panel is done in an identical fashion to a VI's front panel. An interesting tidbit about globals: It makes no difference whether you choose a control or an indicator for a given data type, because you can both read from and write to globals. Finally, be sure to give labels to each object in your global, or you won't be able to use them.

A global might contain, as in the following example, a numeric variable, a stop button, and a string control (see Figure 13.20).

Figure 13.20. A global VI front panel containing a few controls (which act as the global variables)


Save a global just like you save a VI (many people name globals with a "_gbl" suffix [for example, myglobalvariable_gbl.vi] just to help keep track of which files are globals and which are regular VIs). To use a saved global in a diagram, you can choose Select a VI . . . in the Functions palette. A terminal showing one of the global's variables will appear on the diagram.

Operating Tool

To select the variable you want to use, pop up on the terminal and choose an item from the Select Item submenu, or simply click on the terminal using the Operating tool (see Figure 13.21). You can select only one variable at a time on a global's terminal. To use another variable, or to use another element in the global, create another terminal (cloning by <ctrl>-dragging or <option>-dragging is easiest).

Figure 13.21. Associating a global with a variable


Just like locals, globals can be in a read or a write mode. To choose the mode, pop up on the terminal and select the Change To . . . option. Read globals have heavier borders than write globals. As with locals, globals in read mode behave like controls, and globals in write mode act like indicators. Again, a global in read mode is a data "source," while a global in write mode is a data "sink."

READ mode = CONTROL

WRITE mode = INDICATOR

Some important tips on using global variables:

1.

Only write to a global variable in one location, or carefully limit the number of locations, so that your global cannot be written to in more than one place at the same time. You may read from it in many locations. This convention avoids the problem of tracking down where a global variable's value is being changedyou only have one place to look. Also, it is best to limit the number of times that you write to a global variablewriting only once, during initialization of your application, is ideal.

2.

Always, always initialize your globals in your diagram. The initial value of a global should always be cleared from your code by writing to them with initial values, before you read from them anywhere. Globals don't preserve any of their default values unless you quit and restart LabVIEW (or explicitly reset them to their default values).

3.

Never read from and write to global variables in the same place; i.e., where either one could occur before the other (this is the famous "race condition").

4.

Because globals can store several different data types, group global data together in one global instead of several global variables.

It's important that you pay attention to the names you give the variables in your globals. All the VIs that call the global will reference the variable by the same name; therefore, be especially careful to avoid giving identical names to controls or indicators.

An Example of Globals

Let's look at a problem similar to the two independent While Loops. Suppose that now, instead of having two independent While Loops on our diagram, we have two independent subVIs that need to run concurrently.

The subsequent figures show two subVIs and their respective front panels. These two VIs, Generate Time.vi and Plot.vi, are designed to be running at the same time. (Both of these VIs may be found on the CD-ROM, in the EVERYONE\CH13 folder.) Generate Time.vi just continuously obtains the tick count from the internal clock in milliseconds starting at the time the VI is run (see Figure 13.22). Plot.vi generates random numbers once a second until a stop button is pressed, after which it takes all the tick count values from Generate Time.vi and plots the random numbers versus the time at which the numbers were generated (see Figure 13.23).

Figure 13.22. Generate Time.vi front panel


Figure 13.23. Plot.vi front panel


The way these two subVIs exchange data is through the use of a global. We want Plot.vi to obtain an array of time values provided by Generate Time.vi, and more importantly, we want both subVIs to be stopped by a single Boolean control.

So, first we create a global with the two necessary variables. Remember, to create a new global, select the Global Variable structure from the Programming>>Structures palette, and double-click on the "world" icon to define the global's components. In this case, we define a numeric component Time (ms) and a Boolean Stop. The name of the global variable is The Global_gbl.vi (see Figure 13.24). (This VI may be found on the CD-ROM, in the EVERYONE\CH13 folder.)

Figure 13.24. The Global_gbl.vi front panel (a global VI)


Then we use the global's variables at the appropriate places in both subVIs (see Figures 13.25 and 13.26).

Figure 13.25. Generate Time.vi block diagram


Figure 13.26. Plot.vi block diagram


Notice how the stop Boolean variable is used: A stop button from Plot writes to the global variable stop, which is in turn used to stop the While Loop in Generate Time. When the stop button is pressed in Plot, it will break the loop in Generate Time as well. Similarly, the time values from Generate Time are passed to the global variable Time, which is called by the Plot VI to build an array.

In Generate Time, the stop global is initialized inside of the "one-framed" sequence structure (the only good kind). Because the sequence structure outputs a data wire that is an input to the While Loop, the laws of data flow ensure that the stop global is initialized to FALSE before the While Loop executes and reads the stop global. This is a perfect example of how to avoid a race condition. If the sequence structure was not used, our code might not work correctlythe global inside the While Loop might actually be read before it is initialized outside the While Loop!


Hopefully, these two VIs have given you an example of how globals work. We possibly could have avoided using globals for what we wanted to accomplish in this program, but the simple example is good for illustrative purposes.

If you look at a block diagram that calls the two subVIs, you will see another problem with using globals: There are no wires anywhere! Globals obscure dataflow because we can't see how the two subVIs are related. Even when you see a global variable on the block diagram, you don't know where else it is being written to. Fortunately, LabVIEW addresses this inconvenience by allowing you to search for instances of a global.

If you pop up on a global's terminal, you can select Find>>Global Definition, which will take you to the front panel where the global is defined (see Figure 13.27). The other option is Find>>Global References, which will provide you with a list of all the VIs that contain the global. For more information about LabVIEW's search capabilities, see Chapter 15, "Advanced LabVIEW Features."

Figure 13.27. Opening the front panel of global by selecting Find>>Global Definition from its pop-up menu


Shared Variables

So you've seen how to make copies of data within a VI, using locals. You've seen how to make copies of data across multiple VIs, using globals.

Sometimes you need to go a step further and share data between two or more applications that might run on the same computer or perhaps on different computers over a network. LabVIEW has a structure called the Shared Variable (found on the Programming>>Structures palette), which is similar to a global variable, but works across multiple local and networked applications. We will learn more about this exciting feature in Chapter 16, "Connectivity in LabVIEW."




LabVIEW for Everyone. Graphical Programming Made Easy and Fun
LabVIEW for Everyone: Graphical Programming Made Easy and Fun (3rd Edition)
ISBN: 0131856723
EAN: 2147483647
Year: 2006
Pages: 294

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net