Monday, February 25, 2013

Controller

This one is still related to the my previous posts. This time around, I have added features that I had promised in my earlier posts. The usage of the software remains mainly unchanged. What has changed is the way in which the firmware is written. The firmware now uses polymorphism to make it easier to adapt the communication frame work to different hardware platforms (The selected hardware platform should support C++ as a programming language to use this approach). The current implementation makes use of polymorphism at two different places
(a) For the implementation of the communication channel (UART/serial protocol in our case)
(b) For  implementing the functions required for accessing the digital and analog peripherals



What is polymorphism?
Let me try and explain this with a simple example that of a shoe. The coefficient of friction for a shoe on normal road is different than that on a polished smooth surface such as ice. The shoe with which you were comfortable with while walking on a normal road, changes behavior on a smooth road. It would be difficult for you walk on a icy surface with the same shoe. This happens due to variation of the coefficient of friction. Now if you were to write a simulator that uses persons using shoes and their walking behavior on different surfaces, you can use polymorphism to use this function. The gait simulator can simulate appropriate gait based on the surface with the same shoe object.
Coming back to our usage scenario, there are multiple boards that are supported, each having its own APIs for accessing the peripherals and communication channels. The functions that implement the communication protocol are generic and can be used on any platform. These functions can use objects that behave according to the hardware platform on which they are running.

How is polymorphism implemented here?
In the current implementation, the generic communication protocol frame work expects certain function to be implemented that are the bare minimum which has to be implemented by the user for a given board. Here is one such class that has be defined.

   
 class Console  
 {  
   public:  
   virtual char getCh() = 0;  
   virtual void putCh(char ch) = 0;  
   virtual void puts(char *str) = 0;  
   virtual void printf(char *fmt, ...) = 0;  
   virtual void printErr(uint err)  
   {  
    char errMsgs[][20] = {  
     "ERROR",  
     "SUCCESS",  
     "Invalid pin",  
     "Invalid arguments"  
    };  
    if (err > 4)  
    {  
      err = 0;  
    }   
    puts(&errMsgs[err][0]);  
    puts("OK");  
   }  
   virtual int available() = 0;  
 };  

This class defines a template that can be used by the generic frame work. The user has to derive this class and implement the communication methods. This class has to be derived and implemented by the user for the required hardware platform. Once the class is defined, create an object of this type and assign the pointer to the object of type "Console"(or "PerAccess" in case of peripheral access object). These methods let the protocol frame work fetch and put the data onto the communication network. These methods remain unchanged on any given platform but how they work changes from platform to platform. This is where we use polymorphism, that is, same object expected by the protocol frame work behaves differently based on the board on which the code is being implemented and run.

 extern Serial pc;  
 class MbedConsole : public Console   
 {  
   public:  
   virtual char getCh()   
   {  
     return pc.getc();  
   }  
   virtual void putCh(char ch)   
   {  
     pc.printf("%c", ch);  
   }  
   virtual void puts(char *str)  
   {  
     pc.printf("%s", str);  
   }  
   virtual void printf(char *fmt, ...);  
   virtual int available();  
 };  

This is a "concrete" class that can be used by the communication protocol. Similarly, the methods for accessing the digital pins, ADCs and DACs are implemented.

Using the software:
Now that the implementation details are out of the way, let us now see how you can use the host app to control the peripherals. The application is written in python (tested with 2.7) for GUI, wxWidgets is used along with pyserial for serial communication.  You can start the application by double clicking on "App.py" or under Linux, you could type in "sudo python App.py" to start the application. As described in the previous post, the configuration files are used to get information on communicating with the different hardware modules. On startup, the application looks for all the files in the current directory ending with '.cfg' and displays the following window


As you can see, there were two config files that were identified and are displayed in the combo box. The name that appears in the combo box is same as the config file name with '.cfg' removed. Select configuration file, connect the board and hit 'start'. In the window that appears next, select the appropriate com port and click on 'open' to start controlling the peripherals.



All the operations performed are displayed on the status bar, at the bottom as well.

 The screen shots are from the application controlling a mbed module.

The entire project is on GitHub. Please report bugs through the Git.  
Get the mbed project from here.

P.S: I am unable to upload the windows executable of the GUI app to my regular file hosting service. Please suggest a better free file hosting service so that I can upload the executable. 
You can download the pre-built GUI app for windows from here.  (Thank you Grenadier Werbenjagermanjensen)

You might also want to have a look at the updated version of this project here.

2 comments: