Gem #115: Lego Mindstorms Ada Environment — Part 2

by Pat Rogers —AdaCore

Let’s get started…

The Mindstorms NXT “brick” contains both a 32-bit ARM processor and an 8-bit AVR processor. The ARM executes the application code, while the AVR is responsible for the lower-level device functionality, including controlling outputs, gathering sensor inputs, and even shutting down the brick itself.

The two processors coordinate by sending an unending stream of messages to each other. Upon initialization, the AVR begins sending messages containing information such as the current buttons pressed, if any, and the battery status, among other data. The ARM sends messages back to the AVR, for example to set power levels on outputs for motors or to command an analog-to-digital conversion to take place. The messages go back and forth continually at a fixed rate. In other words, they are not event-driven, although their content certainly reflects external events and internally generated commands. Look in the “drivers” directory for the package body of NXT.AVR if you want to see how these messages are sent and received. The one task declared in the entire API is located in that package body for that purpose.

The timing constraints on message handling are fixed by the hardware and are very strict. It is therefore possible for messages to be lost or garbled occasionally. A checksum is calculated to detect these errors, resulting in the problematic message being discarded.

The Ada API hides all these details behind high-level abstract data types for the sensors and motors, and procedural interfaces for the other lower-level facilities such as the A/D converter and discrete I/O capabilities of the ports. Nonetheless, application code must be written with some of the above architecture in mind. Specifically, incoming data are not available until the AVR has been initialized and has sent at least one message to the ARM. Although the AVR is initialized automatically by the Ada drivers, the application programmer must still await receipt of that message. For example, the current voltage of the battery is stored in a variable declared in the NXT.AVR package. The value of that variable is dependent upon arrival of a message from the AVR and is undefined beforehand. The accessor functions that decode the voltage representation simply process the variable as if the value is already defined. (There are ways to mitigate use of the undefined value, of course, but none are ideal.) Other values, such as the raw button readings, are also stored as variables that are set by the decoded AVR messages.

Therefore, the NXT.AVR package provides a procedure that awaits AVR initialization and initial message receipt. That procedure is named Await_Data_Available. A call to this procedure should typically be the first thing done by the application.

Another procedure is provided to power down the brick for those applications that are intended to complete (unlike, say, embedded control systems that only stop when external power is removed). This is procedure Power_Down, also found in the NXT.AVR package declaration. The AVR is responsible for actually shutting down the power, so the procedure injects an appropriate message to the AVR into the message stream. However, as mentioned, messages can be lost or garbled, so the power-down request can be lost. As a result, it is best to use an infinite loop that calls Power_Down repeatedly. In effect, the loop “exits” when the AVR disconnects power to the brick. For example:

   loop
      NXT.AVR.Power_Down;
      delay until Clock + Seconds (1);
   end loop;

The absolute delay statement emulates a relative delay (as the Ravenscar subset doesn’t include relative delays) by calling the Ada.Real_Time.Clock function and adding an interval to it. The duration of the interval is arbitrary and need not be one second.

Note that the NXT.AVR package also detects the situation in which the Power button on the brick is held down for an interval, and will shut down the brick automatically in response.

In summary, although the API goes to some lengths to hide the gory details of the ARM/AVR interactions, ramifications of the message-based architecture remain visible.

In the next Gem in this series we will explore the high-level abstract data types representing the sensors and motors.


About the Author

Pat Rogers has been a computing professional since 1975, primarily working on microprocessor-based real-time applications in Ada, C, C++ and other languages, including high-fidelity flight simulators and Supervisory Control and Data Acquisition (SCADA) systems controlling hazardous materials. Having first learned Ada in 1980, he was director of the Ada9X Laboratory for the U.S. Air Force’s Joint Advanced Strike Technology Program, Principle Investigator in distributed systems and fault tolerance research projects using Ada for the U.S. Air Force and Army, and Associate Director for Research at the NASA Software Engineering Research Center. He has B.S. and M.S. degrees in computer systems design and computer science from the University of Houston and a Ph.D. in computer science from the University of York, England. As a member of the Senior Technical Staff at AdaCore, he specializes in supporting real-time/embedded systems developers, creates and provides training courses, and is project leader and a developer of the GNATbench Eclipse plug-in for Ada. He also has a 3rd Dan black belt in Tae Kwon Do and is founder of the AdaCore club “The Wicked Uncles”.