Welcome to DrDAQ Linux Programming
Lesson #2: picoMgr

Course content Enter chat room Send email 
to mailing list
Check calendar

JUST A REMINDER:  for those of you who have not yet received your confirmation that you have been
subscribed to the mailing list please contact us by email immediately.

In memory of Linda.  Worldwide Cancer sites here.    Canadian Cancer donations here.
 
 

Hint:

All of these lessons  are designed to be worked on offline.  For those of you with dialup Internet access  you might find it helpful to print off the page for each lesson and work from the printout. 

At this point you should have the Linux SIMPL tools installed on your box.

As part of those SIMPL tools you will have downloaded and installed the SIMPL softwareICs package.
 
 

Overview of this lesson

In the previous lesson we installed the DrDAQ driver for Linux.     We also ran the ddtest program designed to exercise this driver for amongst other things the toggling of the LED on the DrDAQ board.

In otherwords we worked on the 2 items on the extreme right of our system picture:

Continuing from right to left the next module we encounter is the picoMgr. This will be the first of three SIMPL modules we will be building in this course.    The construction of the picoMgr will be the subject of this lesson.

For those of you who have taken other iCanProgram courses which employ the SIMPL tools,  you will know that this toolset promotes very modular code.

As we described in the first lesson, the picoMgr is a SIMPL receiver.

It will be sent tokenized messages instructing it on how to interact with the pico driver and by extension with the DrDAQ.    Our strategy will be to have the picoMgr compose and issue the ioctl() calls required to exercise the driver in much the same manner as the ddtest program did in the previous lesson.

Taking full advantage of the modular and testable nature of the SIMPL toolset, we are going to expand upon the SIMPL stimulator softwareIC to evolve it into a test stub for our picoMgr.

We will set up the keyboard menu for our stimulator to emulate that which was presented in ddtest previously.
 
 

Getting organized

We are going to to be working in the

icanprogram/picoMgr/
for much of this lesson.

In anticipation of creating some source code I want you to create the following subdirectories:

icanprogram/picoMgr/src/
icanprogram/picoMgr/include/
icanprogram/picoMgr/test/

Reminder:

Recall from the section above what we are after here.     We want to adapt the stimulator softwareIC code to our particular application.   It makes sense to copy that softwareIC code into these directories now to act as our "seed code".

Here's a list of files I want you to copy (assuming you are currently sitting at icanprogram/).

cp $SIMPL_HOME/softwareICs/stimulator/Makefile  picoMgr/Makefile
cp $SIMPL_HOME/softwareICs/stimulator/include/*  picoMgr/include
cp $SIMPL_HOME/softwareICs/stimulator/src/*  picoMgr/test
cp $SIMPL_HOME/softwareICs/stimulator/test/*  picoMgr/src

Notice that we have "crossed" the src and test directories from the way they were stored in the stimulator softwareIC area.     This is because the picoMgr (receiver) is our principal focus and the stimulator is going to become our test stub.

At this point your directory tree should look something like:

icanprogram/picoMgr/Makefile
icanprogram/picoMgr/include/receiver.h
icanprogram/picoMgr/include/receiverMsgs.h
icanprogram/picoMgr/include/receiverProto.h
icanprogram/picoMgr/include/recvTester.h
icanprogram/picoMgr/include/recvTesterProto.h
icanprogram/picoMgr/include/stimulator.h
icanprogram/picoMgr/include/stimulatorMsgs.h
icanprogram/picoMgr/include/stimulatorProto.h
icanprogram/picoMgr/test/Makefile
icanprogram/picoMgr/test/stimulator.c
icanprogram/picoMgr/test/stimulatorInit.c
icanprogram/picoMgr/test/stimulatorUtils.c
icanprogram/picoMgr/src/Makefile
icanprogram/picoMgr/src/goauto
icanprogram/picoMgr/src/gostim
icanprogram/picoMgr/src/readme
icanprogram/picoMgr/src/receiver.c
icanprogram/picoMgr/src/receiverInit.c
icanprogram/picoMgr/src/recvTester.c
icanprogram/picoMgr/src/recvTesterInit.c
icanprogram/picoMgr/src/recvTesterUtils.c
The objective in this lesson is for us to convert the receiver above into our picoMgr and to adjust the stimulator so that we can replicate the LED toggle functionality contained in the ddtest module from lesson #1.

Let's get at it now.

As always with any tokenized messaging SIMPL system,  it makes good sense to start with the definition of the tokens and tokenized messages we are going to be employing.
 
 

Tokenized messages going to picoMgr:  picoMgrMsgs.h

In the stimulator softwareIC code the tokenized message definitions were housed in

icanprogram/picoMgr/include/receiverMsgs.h.
This is not a particularly good name for this header file any longer.

Let's use the Linux "mv" command to rename this header in a more consistent way:

mv  receiverMsgs.h  picoMgrMsgs.h
Obviously the contents of this file will need some adjusting.

Remember what we are after here.     We want to expose a messaging interface to the picoMgr which will allow a SIMPL sender (our stimulator for now) to compose and send() a message instructing the picoMgr to toggle the LED on the DrDAQ board.

It makes sense to create a token called:

PICO_LED_TOGGLE
and a corresponding tokenized message called
PICO_LED_TOGGLE_MSG
Since to toggle the LED, no other information is required to be transported into or out of the picoMgr other than the token itself our message is going to be as simple as it gets.

You will then want to adjust the contents of picoMgrMsgs.h to look something like this below.
 
/*=====================================================

        iCanProgram: Linux Programming for DrDAQ

FILE:           picoMgrMsgs.h

DESCRIPTION:    
This file contains message templates for picoMgr.

Revision history:
=======================================================
$Log$
=======================================================

=====================================================*/

#ifndef _PICO_MGR_MSGS_DEF
#define _PICO_MGR_MSGS_DEF


/*=======================================
         message tokens
=======================================*/
typedef enum
        {
        PICO_LED_TOGGLE,
        MAX_NUM_PICO_TOKENS
        }PICO_TOKEN;

/*============================================
        message templates
============================================*/
typedef struct 
        {
        PICO_TOKEN token;       // PICO_LED_TOGGLE
        }PICO_LED_TOGGLE_MSG;
        
#endif

Part of our objective in these lessons is going to be to impart some good development habits that the SIMPL paradigm affords.   One of these is that we can efficiently employ a time honored iterative process to our code evolution.   ie.  the somewhat tedious approach of:

small changes -> recompile -> small changes -> recompile ...


Go ahead and try recompiling your code in picoMgr/src/ now.

Not to worry,  we expect it to be broken.

It is very worthwhile to take the time now to work on getting a clean recompilation even though we anticipate that several files are going to be edited multiple times throughout this lesson.      It is so much easier to solve problems with the code when only a small set of changes have been applied since the last successful build/operation.      It is a common sense matter of cause and effect.

To get this picoMgr version  to recompile you are going to have to change all occurances of receiverMsgs.h in the source code over to picoMgrMsgs.h

Wait a minute !     It still doesn't recompile !

Patience.

The next best "friend" you have in this code evolution process is going to be the preprocessor comment out block.  ie.

#if 0
<code section to be commented out>
#endif
A good place to start with this might be in the receiver.c file.      I would suggest commenting out the entire case RECV_TEST
switch block.  ie. from case to break.

You could delete this section but you never know when it might come in handy as a source for cut and paste,   so I recommend the comment out method.

Keep going until you get a clean recompile.
 
 

Reminder:

You are not alone in this course.    If you need guidance don't hesitate to ask your fellow students on the mailing list for help. 

Many books and courses on C frown on the use of global variables.     I happen to believe that when properly employed global variables can lead to cleaner and more maintainable code.

For those of you coming from an earlier iCanProgram course you will immediately spot the home of these global variables complete with the _ALLOC preprocessor construct to allow them to be shared across mulitple source files associated with the picoMgr.
 
 

Global variables go into picoMgr.h

Following in the footsteps of the process above I want you to rename another file in picoMgr/include/ as:

mv  receiver.h  picoMgr.h
Other than to perhaps cleanup the header block we don't need to make too many changes to this file just yet.

Go ahead and change all instances of receiver.h over to picoMgr.h in the relevant source files and get your code recompiling once again.

As you can gather we are going to be converting our receiver seed code over to a more appropriate name:  picoMgr.   Next up on that path will be our seed Makefile at picoMgr/src.
 
 

Makefile changes and renaming of receiver as picoMgr

We have one more housekeeping change to make before we can begin to add our functional bits into picoMgr.

Following in the footsteps of the process above I want you to rename some other files in picoMgr/src/ as:

mv  receiver.c  picoMgr.c
mv  receiverInit.c  picoMgrInit.c
Now when you try a recompilation in picoMgr/src/ you will be getting Makefile errors.

All that is required is that you edit the Makefile and replace the appropriate filenames:

receiver -> picoMgr  (including occurances with .c .o extensions)
RECEIVER_OBJ -> PICOMGR_OBJ
Get you system back to recompiling in the picoMgr/src.

This should take care of housekeeping for the picoMgr/src directory.

We now need to turn our attention to the picoMgr/test or stimulator directory.
 
 
 

picoMgr stimulator

I'm a big fan of the programming school which says that wherever possible you should build your test stubs and tests before you begin to add functionality to your main code.

The advantage of this approach is that you can actually run your code as you make incremental changes and thereby tackle complex problems bit by bit.

This is the approach we are going to adopt here.

For those of you who have taken other iCanProgram courses such as "Extreme Linux Programming" you will recognize this approach.

Our objective here is to make our stimulator do essentially what the menu in the ddtest program from lesson #1 did.     We are going to focus on the toggling of the LED because that is the most basic functionality of the DrDAQ unit.     The idea being that any other functionality is going to be an extension of these concepts.

By now in this lesson the icanprogram/picoMgr/test/ code should at least compile.

How are we going to run our stimulator-picoMgr pair?

We are going to modify the gostim BASH script which is located in icanprogram/picoMgr/src to replace receiver with picoMgr.    You will also want to modify the path to the stimulator to remove it from the $SIMPL_HOME/softwareICs path and point it toward your icanprogram/picoMgr/bin local build directory.

Go ahead and do this and run the gostim script.

The stimulator will run in the foreground and will present a prompt "->".      Type "q" a the the prompt and the script should tear down the SIMPL modules and exit.

How do we know details of what happened other than what was presented at this interface?     It is not going to be easy for us to work on anything but the most trivial of code without this debug and trace capability.
 
 

debugging picoMgr and stimulator

One of the essential tools you will need in this code modification exercise is the ability to debug your code as you go along.

If you know how to use a source code debugger you could use that.

You could also sprinkle printf() statements strategically throughout your code.

There is one fundamental challenge with either of these methods:

these techniques, by their very nature, alter the releasable code in order to enable the debugging.
There is another way:   the trace logger.

SIMPL comes with a very capable trace logger which for historical reasons is called fclogger.     If you examine your gostim script closely you will see the invocation of this trace logger.

Where does it direct its output?     Open a file called junk and have a look.

Everywhere the execution encounters  an fcLogx() call in the source code you will see a corresponding entry in the junk file.      Kind of like a printf() with one important difference.    You can manipulate the masking bits at startup time or at run time to disable all but the no op function call once your code is operational.    Hence you can actually test the actual shipping compilation of your code.     Obviously enabling or disabling the trace logger mask bits will alter the code timing and execution paths,   but it is as close as it gets to testing your shipping code.

We now have all the infrastructure elements in place to begin our actual coding.

We have

Let's begin our next phase in the test stub itself:   the stimulator.
 

stimulator.c

Remember what we are after here.     We are wanting to present a menu with at least an option to toggle the LED on the DrDAQ board.

A quick examination of our seed code in stimulator.c reveals a series of printf()'s under a case statement called "?" which look very much like seed code for the menu we saw presented in our previous runs with gostim.

We are going to want to change the menu case in the switch first as in:
 
                      case '?':       // help
                                printf("stimulator commands:\n");
                                printf("L - toggle the LED test\n");
                                printf("q - quit\n");
                                break;

Go ahead recompile and run the gostim script.      This time at the prompt type

-> ?
-> L
-> q
You should see evidence of your changes above particularly on the screen after the "?" answer above.

Obviously,  we don't expect much to happen when we type "L" as our menu response just yet.

Let's now make our stimulator actually do something with this "L" response.

Once again a cursory examination of our seed code reveals a likely candidate for this change would be another case 'L': block which would rewrite the existing "t" case:
 
 
                      case 'L':       // toggle the LED
                                {
                                PICO_LED_TOGGLE_MSG *outMsg;
                               
                                fcLogx(__FILE__, fn,
                                        globalMask,
                                        STIM_MISC,
                                        "LED toggle"                              
                                        );

                                outMsg=(PICO_LED_TOGGLE_MSG *)outArea;

                                outMsg->token=PICO_LED_TOGGLE;
                                
                                fcLogx(__FILE__, fn,
                                        globalMask,
                                        STIM_MISC,
                                        "msg -> picoMgr at slot=%d",
                                        recvID
                                        );

                                Send(recvID, outArea, inArea, sizeof(PICO_LED_TOGGLE_MSG), MAX_MSG_SIZE);
                                hndlReply(inArea);
                                }
                                break;

Go ahead recompile and  run.

You are beginning to get the hang of our development cycle here:

This latest change should now at least be getting some trace log messages to appear from the picoMgr right after the Send() call is executed in the stimulator.

The problem is that our picoMgr is not handling this type of message token yet.

Let's fix that now.
 
 

picoMgr.c

Edit your copy of picoMgr.c and add a case statement in for PICO_LED_TOGGLE as in:
 
          case PICO_LED_TOGGLE:
                        {
                        fcLogx(__FILE__, fn,
                                globalMask,
                                RECV_MISC,
                                "LED toggle");
                                
                        Reply(sender, NULL, 0);
                        }
                        break;  

 

Note about enums:

On most PC's an enum is stored as a 32 bit integer.    We plan to standardize on 32 bit tokens for this course material.     Unfortunately, most of the softwareIC code uses an unsigned short integer (UINT16) or 16 bit integer.  

You will need to convert all instances of UINT16 over to int in your picoMgr.c code.

At this point you are almost done ... just kidding of course.

You will however have all the messaging bits working for the LED toggle.

All we need to do now is add in the actual hooks to the DrDAQ hardware and we will be done.

Let's do one more layer of changes before we connect ourselves into the DrDAQ driver.
 
 

picoMgrUtils.c

I want you to copy ddtest.c from lesson #1 into your picoMgr/src/ directory and rename this file as

picoMgrUtils.c
Using the
#if 0
#endif
Commenting out technique,  I want you to comment out the entire contents of this file.      We are going to make use of things from this as we go forward and it is just convenient to have it in this one location.

You'll now want to modify the

picoMgr/src/Makefile
to enable picoMgrUtils.o to become part of PICOMGR_OBJ set and get compiled from picoMgrUtils.c.
 
 

If you need assistance:

Makefiles can be fiddly with the basic parser which processes these files.    If you need guidance don't hesitate to ask your fellow students on the mailing list for help. 

The next thing I want you to do is create two empty functions at the top of picoMgrUtils.c called:

turnLedOn()
turnLedOff()
The top portion of your picoMgrUtils.c file might end up looking something like:
 
/*=======================================================

        This software was developed for
        iCanProgram: Linux Programming for the DrDAQ


FILE:           picoMgrUtils.c

DESCRIPTION:    
This file contains source for picoMgr.

Revision history:
=======================================================
$Log$
=====================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>

#define _ALLOC extern
#include "picoMgr.h"
#undef _ALLOC

#include "picoMgrProto.h"
#include "loggerProto.h"

/*============================================
        turnLedOn - entry point
============================================*/
int turnLedOn()
{
static char *fn="turnLedOn";
int rc=0;

fcLogx(__FILE__, fn,
        globalMask,
        RECV_FUNC_IO,
        "ding"
        );

return(rc);

} // turnLedOn

/*============================================
        turnLedOff - entry point
============================================*/
int turnLedOff()
{
static char *fn="turnLedOff";
int rc;

fcLogx(__FILE__, fn,
        globalMask,
        RECV_FUNC_IO,
        "ding"
        );

return(rc);

} // turnLedOff

A  little more house keeping to take care of.

In C programming it is very advisable to use function prototyping to allow the compiler to detect function invocation mismatches.

For this you will need to

This is all very well and good but we still don't have anyone calling these functions.

Let's rectify that now.
 
 

ledState global variable

If you are still with me here ... congratulations ... we are nearly home.

If you scan way back up the beginning of this lesson you'll see that we allocated global variables for picoMgr inside

picoMgr/include/picoMgr.h
Go ahead and edit in a couple of lines something like
_ALLOC int ledState;
_ALLOC int digital_output;
to this file.

At this point everything should still recompile and run as before.

Let's now modify our picoMgr.c PICO_LED_TOGGLE case statement to look more complete as in:
 
 
          case PICO_LED_TOGGLE:
                        {
                        fcLogx(__FILE__, fn,
                                globalMask,
                                RECV_MISC,
                                "LED is currently %s",
                                (ledState == 1) ? "on" : "off");

                        if(ledState == 1) // its on
                                {
                                turnLedOff();
                                ledState =0;
                                }
                        else
                                {
                                turnLedOn();
                                ledState =1;
                                }
                        
                        fcLogx(__FILE__, fn,
                                globalMask,
                                RECV_MISC,
                                "LED is now %s",
                                (ledState == 1) ? "on" : "off");

                        Reply(sender, NULL, 0);
                        }
                        break;  

When you recompile and run this code you can sense a real application beginning to take form.      The trace log messages should indicate that our ledState variable is allowing the "L" command to function as a toggle switch.   ie. it starts with the LED off and then on etc.

The only trouble is ... nothing is happening with our LED.
 
 

Connecting into the DrDAQ driver

In all UNIX's including Linux everything in sight is a file descriptor.

We need to create another global variable to hold this picofd.

As before you'll need to add a line something like

_ALLOC int picofd;
So far we really haven't done too much with our picoMgrInit.c file.

We'll need to make some changes now, however.   Near the top of that file we'll need to add some new include files.
 
 
#include <fcntl.h>
#include <sys/ioctl.h>
#include "pico_lnx.h"

The first two of these are standard Linux header files so the compiler already knows where to find them.     The pico_lnx.h, however, came with the Linux DrDAQ driver we installed in lesson #1.

We'll need to modify our include path in the picoMgr/src/Makefile to point the compiler to this location.

If you recall in lesson #1 we had you put all the DrDAQ driver code in

icanprogram/picodriver/
This is where the pico_lnx.h header will be found.      The Makefile addition will look something like
 
 
PICO_INCL_DIR=../../picodriver



CDFLAGS=-c\
        -Wall \
        -I $(LOCAL_INCL_DIR)\
        -I $(PICO_INCL_DIR)\
        -I $(SIMPL_INCL_DIR)

Go ahead and do a test recompile.     The compiler should happily find the pico_lnx.h header.

Near the bottom of our picoMgr/src/picoMgrInit.c file we'll need to make the first connections to the driver.
 
 
  fcLogx(__FILE__, fn,
        RECV_MARK,   // force it to log
        RECV_MARK,
        "trace logger mask = 0x%04X",
        globalMask);

// default state of LED
ledState = 0;
turnLedOff();

// open the fd to driver
picofd=drdaq_open(0);

// set some stuff in driver
ioctl (picofd, IOCTL_PICO_GET_VERSION, &value);

fcLogx(__FILE__, fn,
        RECV_MARK,   // force it to log
        RECV_MARK,
        "Kernel driver version %04x", value);

value = PRODUCT_DRDAQ;
ioctl (picofd, IOCTL_PICO_SET_PRODUCT, &value);

} /* end initialize */

Oops there are compilation problems.

One of these concerns the value variable.

This can be easily solved by declaring a local variable near the top of picoMgrInit.c as in:

int value;
We also need to create the function drdaq_open().   For this we will want to "borrow" some code from the commented out portion in picoMgrUtils.c  as in:
 
 
 /****************************************************************************
 *
 *
 ****************************************************************************/
int drdaq_open (int lp)
{
int           file;
char          dev_name [20];


/* Open the device for this printer port */
sprintf (dev_name, "/dev/pico%d", lp);
file = open (dev_name, 0);
  
return file;
} // end drdaq_open

Don't forget to add the prototype for drdaq_open()  in picoMgrProto.h.

While you are at it you may as well add the

#include "pico_lnx.h"
to the top of the picoMgrUtils.c file because we are going to need this shortly.

OK  ... but we still don't have the LED toggling!
 
 

Final step

If you recall all we had you do so far in the turnLedOn() and turnLedOff() functions is log a trace logger message to indicate that these functions are getting called correctly.

We now need to add the connection to the driver which actually causes the LED to fire.
 
 
 /*============================================
        turnLedOn - entry point
============================================*/
int turnLedOn()
{
static char *fn="turnLedOn";
int value;
int rc=0;

fcLogx(__FILE__, fn,
        globalMask,
        RECV_FUNC_IO,
        "ding"
        );

value = 1 * DRDAQ_LED + digital_output * DRDAQ_DIGITAL_OUTPUT;
ioctl (picofd, IOCTL_PICO_SET_DIGITAL_OUT, &value);

return(rc);

} // turnLedOn

/*============================================
        turnLedOff - entry point
============================================*/
int turnLedOff()
{
static char *fn="turnLedOff";
int value;
int rc;

fcLogx(__FILE__, fn,
        globalMask,
        RECV_FUNC_IO,
        "ding"
        );

value = 0 * DRDAQ_LED + digital_output * DRDAQ_DIGITAL_OUTPUT;
ioctl (picofd, IOCTL_PICO_SET_DIGITAL_OUT, &value);

return(rc);

} // turnLedOff

 

Don't forget:

Recall from lesson #1 in the README associated with the DrDAQ driver we were instructed to insert a kernel module as the driver:
insmod   /usr/sbin/pico.o
Don't forget this step. 

If all has gone well you should have an LED toggle.
 
 
Congratulations!  You have successfully created a SIMPL messaging interface to your DrDAQ driver on your system!

 
 
 

Exercises

In the next few lessons we will be building the  GUI using Tcl/Tk with enough smarts to toggle the LED.

Go ahead and develop the skeleton of this process.
 
 

Summary

Let's summarize what you have learned in Lesson2

End of Lesson 2.


Copyright of iCanProgram Inc.  2003