Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Symbolic Access To Embedded Controllers


March 1994/Symbolic Access To Embedded Controllers

Symbolic Access To Embedded Controllers

Odd A. S. Olsen and Petter H. Heyerdahl


Odd A.S. Olsen and Petter H. Heyerdahl received their Master of Engineering degrees in Engineering Cybernetics at the Norwegian Institute of Technology, where Mr. Olsen later received a Ph.D. He is currently an independent consultant, developing electronics and programs for embedded controllers. Mr. Heyerdahl is associate professor at the Agricultural University of Norway, Department of Agricultural Engineering. Odd may be contacted at Jutulveien 11, N-0852 Oslo, Norway or by the Internet: odd. [email protected].

Introduction

A PC often functions as the user interface, debugging tool, and general supervisor of an embedded controller. With this arrangement it is important for the programs that transfer parameters between the PC and the controller to maintain agreement on the names and meanings of all variables. If the PC sets parameter 63 to the value of 123, for instance, the PC and the controller must agree on what parameter 63 is and what the value 123 represents. This consistency is especially at risk during development because new parameters are sometimes introduced on-the-fly and often end up in different locations on the two computers. The processors may not even agree on a format for the values. (An Intel PC could be storing numbers in big-endian format while a Motorola controller might want them in small-endian format.)

A symbol table is an efficient solution to the consistency problem because it allows the data structures of the PC and controller to develop separately, yet links the two systems with a simple communication protocol. By this method, which resembles the message format found in the general-purpose interface bus (GPIB), values are referenced by their symbolic name, e.g. measured_pH, heater_power etc. We used this principle to control a group of bioreactors. Each reactor is controlled by an embedded controller which communicates with a supervising PC for operator instructions and data logging.

How Things are Connected

Figure 1 illustrates the information flow in the system. In our implementation the PC is always the initiator of communication and the embedded controller only responds to received messages. The most important entries in the symbol tables are the parameter names and their values. (In this context a parameter is more general than a constant. It can also represent measured data, debug information, etc.) Parameter values move from the PC symbol table to the controller symbol table through messages sent over the RS-232 line. The PC symbol table can be altered either by accessing the user interface or by causing the PC to read a refreshed version of the parameter file. The new data is then sent to the symbol table in the embedded controller. The controller symbol table can thus be changed from the PC, or through the measurement and control algorithms of the controller itself. Parameters for the control algorithms are stored in the EEPROM, enabling the controller to initialize its symbol table values at start-up and maintain its control process by itself should the PC fail.

The Symbol Tables

Both the PC program and the embedded controller program are centered on their respective symbol tables. The structure of the PC symbol table is:

typedef struct {
    char *name;
    int ival;
    double fval;
    void *(*scalefunc)(void *);
    char eeProm;
    char file;} symTabEntry;
Each of the process variables maintained by the controller (temperature, oxygen level, pH, etc.) is represented as a record in the symbol table. Every record in the table contains a unique name (e.g. measured_pH), an integer or double floating-point value, and a pointer to an optional scaling function. The table also contains two flags: eeProm and file. If eeProm is set to TO_EE, the record will be transferred to the controller upon start-up, and will be stored in the controller EEPROM if its value is different from the one already residing there. The file flag indicates whether the current record contains data associated with the system's parameter file. This parameter file contains parameter names (of process variables and other system variables) and their values. If the system finds a particular parameter name in the file during startup, the system copies the parameter's value into the record, and sets a bit in the file flag. Likewise, if the user later wants to save the current parameter values back to the file, the system will save only those parameters whose file flags are set. While the system is operating, a record holds a parameter's value in either ival (for integer parameters) or fval (for floating-point parameters).

The user interface is also oriented to the symbol table. The user can change parameters by choosing a parameter window. Each parameter entry field knows the pointer to its entry in the symbol table. It can thus read and write the value and call the scaling function. When the user changes a parameter, the new value is immediately scaled from floating-point to integer and transmitted to the controller. Because values are referred to by name rather than by pointer, the set-up of the parameter windows is simplified.

The symbol tables are alphabetically sorted, which allows the processor to use a binary search when looking for a name. (A binary search is faster than a linear search, but if the tables were large, I would consider using hash-tables.) The programmer will now and then insert new entries to the tables at a wrong position. On start-up both the PC and controller programs therefore go through their tables and check for alphabetic order. If unordered elements are found, the function issues an error message.

The structure of controller's symbol table is

typedef struct {
    char *name;
    int ival;
    char *(*func)(char *);
    int eeOffset;} symTabEntry;
name is the name of the parameter, ival the value, and the function pointer either a function that can be invoked by a message or a hook for individual processing of received parameters. eeOffset is the EEPROM address where the parameter is stored. (Parameters that are not saved in EEPROM have an offset value of —1.)

The controller's program runs periodically, e.g. every five seconds. The program first reads all measurements and stores the values in the symbol table. The control algorithms then take their values and parameters from the symbol table and calculate responses. The results are stored into the table and output to actuators. Both measured values and actuator states are thus available to the PC.

To understand the use of the symbol table, say the control algorithm is pHerror=pHsetpoint-pHmeasured. This can be programmed as:

FindSymbol ("pHerror")->ival = 
    FindSymbol ("pHsetpoint")->ival - FindSymbol ("pHmeasured")->ival;
FindSymbol returns a pointer to the symbol table entry with the given name. However, less typing and a faster program are achieved by initializing pointers to the symbol table entries at start-up:

int *ppHerror;
ppHerror= &FindSymbol ("pHerror")->ival;
etc. and coding the algorithm as

*ppHerror= *ppHsetpoint - *ppHmeasured;
At start-up the symbol tables are initialized according to the following sequence. First the tables are initialized with the values specified at compile time. The controller then reads values from the EEPROM into its table. The PC reads a parameter file, which may change some of the values in its table. The PC program will then transfer those values having flag TO_EE set to the controller EEPROM and symbol table.

Message Format

The messages between the PC and controller are ASCII strings sent over an RS-232 connection. Before transmission the message assembler adds a checksum. The receiver checks and removes the checksum from the message before passing the message to the message interpreter.

Each message consists of one or more submessages. The submessages and their elements can be separated by spaces and have the following format:

<name><operator>[<value>]
The name field is the symbolic name of a parameter or a command. The operator is a single character as given in Table 1. For messages with an associated value, the value field is the numerical value of the parameter. The name must begin with a letter (a-zAZ), followed by a number of letters, digits, or underscores (a-zAZ0-9_). In our implementation the value field is always an integer because all processing in the controller is integer based.

To add other value types the programmer need only define new operators and modify the structures and programs accordingly. A floating-point type is useful in some systems. A string type can also be of use, for example to transfer strings directly to an operator display on the controller. In a multi-processor embedded system, the string type can be used to transfer messages verbatim to subprocessors.

The following message transfers temp_setp=20 to EEPROM, sends delay=10 to the symbol table, requests the present value of temp, and executes the function pwr_sav:

temp_setp>20 delay:10 temp? pwr_sav!
The response might be

temp:22 pwr_sav#16
The error message indicates that pwr_sav was not found in the symbol table of the controller. (Such error messages are hopefully only encountered during program development.) In this case the message indicates a discrepancy between the PC and controller programs.

Message Interpretation

The interpreter parses the received messages into a task list which is handed over to an execution function. The structure of the list records is:

typedef struct {
    int command;
    int type;
    char *name;
    int value; } toDoList;
command is a value specifying the command, type the type of the value (in this case always INTEGER), *name a pointer to the name of the parameter, and value the numerical value of the parameter.

In the controller the interpreter and reply assembler are integrated. In the PC, however, parameters are sent through a message assembler and replies decoded in a separate interpreter. The receiver is interrupt driven and executes the interpreter after a full message has arrived. The interpreter places the received values into the symbol table and transfers error messages to the user interface. The PC interpreter resembles the controller interpreter, which I am about to describe.

A Sample Application

This slightly modified version of the controller's interpreter and message assembler will illustrate message handling and symbol table operations. (This version runs on a PC.) Listing 1 presents the definitions and function prototypes; Listing 2 shows the main program and message interpreter.

The main program reads a message from the keyboard. It parses the message, then prints and executes the task list toDo. The task list is a list of structures with one entry for each subcommand. The Parse function assumes the message is formated as defined in Table 1. Since the grammar is quite simple, there is no need for recursive descent or other strategies used in compilers. There are several tests that check adherence to the format, and issue error messages if discrepancies are found. (This is most valuable during program development.) Finding an error stops the message interpretation. Inserting an EMPTY command terminates the list.

GetNextToken reads the next token in the message string. If the first character encountered is a letter, the token is a name. If it is a digit, the token is a value. If the first character is neither a name nor a value, the token is an operator. The function then copies the token into the token string of the parser and sets the type parameter according to the type. The size of the token-string (MAX_TOKNE_LEN) must be larger than the longest token that can occur. The type parameter is set equal to the operator's defined constant.

Parser calls StroreToken to save the encountered names in a string pool. This pool is recycled for each message by setting pPool=stringPool. The pool size (STRINGPOOL) must be large enough to store all the names can occur in one message. The address of the names is stored in the name element of the structure.

The task list ToDo can now be processed by calling DoCommands. This functin is essentially a loop which executes the commands through a switch statement. However, it first initializes a pointer to the beginning of the txBuffer, where the response message is stored. This buffer might be local to the more hardware-dependent parts of the program and not globally accessible, so this requires a function call to obtain the pointer. All function called in the DoCommands maintain the buffer pointer by accepting it as an argument and returning the possibly changed value.

SayError writes an error message to the transmit buffer. It receives a pointer to the task list, where it finds the name relating to the error, and error number.

The functions Assign, Request, ToEeprom, and RunCommand each begin by getting a name from the task list and looking it up in the symbol table. If the name isn't found, they write an error message to the transmit buffer.

The Assign function inserts the value in the symbol table. The Request function reads the value from the symbol table and generates a response submessage. ToEeprom puts the value into the symbol table and checks if the parameter has a place in the EEPROM. If eeOffset is greater than or equal to zero, it writes the value. If not, it issues an error message. RunCommand executes the program pointed to in the symbol structure. If the pointer is NULL, RunCommand generates an error message.

Assign, Request and ToEeprom all check for a function hook before returning. If they find one they call it. Assign, Request, and ToEeprom also maintain the transmitter buffer pointer so they can write to the buffer.

The program was compiled with Borland TurboC++ version 3.0. The library functions are all UNIX and ANSI defined, so using other compilers should not present any problem.

Final Thoughts

This symbolic data transfer scheme enabled us to implement a complete control system from scratch in about one programmer-month. When we started we had no clear picture of what the final result would be, so parameters were constantly added and removed as we tried different control strategies. By using the described method, we were able to respond quickly to these changes. This arrangement also made it easier to add auxiliary parameters for debugging. There are of course faster ways to transfer data, but the kinds of processes maintained by embedded controllers are often slow enough that a minor increase in response time isn't significant.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.