• Aucun résultat trouvé

Saturating Counter

Dans le document Creating Cool MINDSTORMS® (Page 63-69)

Now, I’ll show you a simple FSM: a saturating counter. This example is useless for making a robot live and enjoy its artificial life, but don’t worry—we’ll work on something more “robotic”

and useful later in the book. For the time being though, a saturating counter is a counter that stops counting when it reaches its bounds. You certainly can implement this without summon-ing FSMs, but I’ll do it this way, because it serves as a basic clear example. The state diagram for this automaton is shown in Figure 3-3.

Figure 3-3. The state diagram of a saturating counter

In this example, if the user clicks a button, the counter will increment one unit until it reaches five, but if there is no input from the user within a certain timeout, the counter will decrement one unit at a time until zero, and remains there until the user starts clicking the button again.

Inside the circles, as shown in Figure 3-3, you can see the state names that are just numbers from 0 to 5. On the arcs, there are the labels describing the events (T, B) and the actions (R).

Figure 3-3 shows that every state can have more than one transition to other states: as many outgoing arcs as the number of the events possible in that state.

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 49

Our counter can count up to five (to keep the whole thing small), but we could easily go beyond this limit. When the system starts, the FSM is in state 0 (this is the entry state). When the event T (the timer elapsing) occurs in state 0, the state remains the same (the transition arc closes back on state 0) and the timer is reset. In all the states, the clicking of the orange button (the event called B) can cause a transition to the next-numbered state together with a timer reset action. The state 5 is an exception, because more button clicks cause only the timer to reset, but the state remains the same. The other event T can cause transitions to a lower-numbered state—back to state 0, in which more timer elapsing does not change the state, as previously mentioned.

All the intense activity described here could happen with no external visual feedback, but where would the fun be? So, the program that implements this FSM provides visual feedback showing the state transitions, with a level meter made of a bar of black squares. This bar shows what state the FSM is in. See the code for the main task of the saturating counter program in Listing 3-1.

Listing 3-1. The Main Task of the FSM Example task main()

{

short state = STATE_0; //entry state short event;

if (event == TOUCH) state = STATE_1;

if (event == TIMER_ELAPSE) state = STATE_0;

ResetTimer();

if (event == TOUCH) state = STATE_2;

if (event == TIMER_ELAPSE) state = STATE_0;

ResetTimer();

if (event == TOUCH) state = STATE_3;

if (event == TIMER_ELAPSE) state = STATE_1;

ResetTimer();

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 50

break;

case STATE_3:

ClearScreen();

Meter(3);

event = WaitEvent();

if (event == TOUCH) state = STATE_4;

if (event == TIMER_ELAPSE) state = STATE_2;

ResetTimer();

if (event == TOUCH) state = STATE_5;

if (event == TIMER_ELAPSE) state = STATE_3;

ResetTimer();

if (event == TOUCH) state = STATE_5;

if (event == TIMER_ELAPSE) state = STATE_4;

ResetTimer();

Note

The constants STATE_0,STATE_1, and so on that you find in the program are just aliases defined at the top of the program using the preprocessor directive #define STATE_0 0. The aliases mean that when the compiler finds STATE_0later in the program, it will view it as if it were the number 0. It is not essential, but it’s a commonly used practice in programming to write constants with all capital letters.

An FSM code skeleton is a switch statement nested inside an infinite loop. The switch variable is the state variable itself (called—no surprise—state); it’s the variable that contains the actual state value (defined at the top of the program).

In our example, the states have unimaginative names; for every state we find acase <value>:

label. The code to be executed in this state is included between the case <value>:label and the breakstatement.

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 51

Tip

The switch statement is equivalent to a series of if/elsestatements, resulting in more efficient compiled code. The breakkeyword makes the program flow out of the switch statement body once the part of code that’s of interest has been executed. Consult the NXC Programming Guide for details.

The code can perform output actions, and also change the internal state, by simply assigning a new value (among the ones defined in the program) to the state variable. Confused?

Let’s see an example, say state 4, in detail.

case STATE_4:

Meter(4);

event = WaitEvent();

if (event == TOUCH) state = STATE_5;

if (event == TIMER_ELAPSE) state = STATE_3;

ResetTimer();

break;

At the actual loop iteration, the switch(state)brought us to the caselabeled STATE_4. This means that in the previous loop iteration, the statevariable was assigned the STATE_4value.

The whole switch body is equivalent to this:

while(true)

First, you invoke the Meter(4)function to draw a bar with four black squares on the NXT display (the code of the Meterfunction is in Listing 3-3), then you use the WaitEvent()function to wait for the timer to elapse or for the button to be clicked (pressed and released). This func-tion returns a value to let the maintask know which event occurred.

Note

I omit here and later, on purpose, the code to read and reset the timer, to focus your attention better on the current topic. You’ll learn how to use the timer in Chapter 6, on the NXT Turtle.

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 52

If the timer had elapsed, the statevariable would be assigned the value of the lower STATE_3 (remember we are in state 4). But if the button was clicked, the next state would be the highest, STATE_5. At this point, the timer is reset and the program flow breaks out the switch, ready for another ride. On the next loop, the switch statement executes the code corresponding to the case labeled with the value just assigned to the state variable.

In Figure 3-4, you can see the two transitions described in the last paragraph: both the transition from state 4 to 5 (the button was pressed), and the transition from state 4 back to state 3 (the timer has elapsed).

Figure 3-4. Some NXT display outputs of the saturating counter

The program starts with a blank screen; if you don’t do anything, you’ll see the message

“Elapse—Reset” flashing periodically. This means you are in state 0, and when the timer elapses, it is reset, while the state remains at 0.

If you try clicking the NXT orange button quickly (the interval between two clicks has to be less than the program-defined timeout), you’ll see the number of squares increase. The message “Button—Reset” indicates that action R (see Figure 3-3) is performed, as a result of a button click event B. When in state 5, pressing frequently on the button maintains the counter in its top state. If you stop pressing the button, the bar decreases every time the timer elapses, back to state 0.

The NXC language and NXT firmware don’t manage events directly (using interrupts), so you can implement waiting for events with the WaitEvent()function (see Listing 3-2).

Listing 3-2. The WaitEvent() Function Code short WaitEvent()

{

short event = 0;

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 53

while ( event==0 )

else if (CurrentTick() > time_offset + TIMEOUT) {

The WaitEvent()function waits for two kinds of events: for the timer to elapse and for someone to click the orange button. It polls continuously whether the orange button has been clicked or if the timer has run out; when one of these events has occurred (the eventvariable is assigned a value other than 0), the function returns a different number (the constants TOUCH or TIMER_ELAPSE) according to the event that occurred.

Before going forward, as a curiosity, look at Listing 3-3, which draws the bar on the NXT screen.

Listing 3-3. The Code to Draw a Bar of Filled Squares on the NXT Display sub Meter(short level)

{

ClearScreen();

for(short i=0; i<level; i++) //draw filled rectangles {

The NXC language provides us with many high-level graphic functions to exploit all the NXT screen capabilities. For example, we can use the RectOutfunction inside aforloop to draw the number of empty squares specified by level, or we can use the LineOutfunction inside a nested forloop to get a square filled with black pixels. You can obtain filled squares by draw-ing as many lines as the length of the square. On the other hand, by removdraw-ing the comment on the RectOutfunction line and commenting out the nested forloop, you can draw empty squares, thus speeding up the NXT screen redrawing.

C H A P T E R 3■ F I N I T E S TAT E M A C H I N E S 54

Dans le document Creating Cool MINDSTORMS® (Page 63-69)