Tuesday, March 8, 2011

Data transfer from microcontroller to notebook (Part 2)

In this Part 2, we now look at the data transfer from the notebook point of view. If we look at the microprocessor output sent to the notebook running Hyperterminal, all we see is garbage with ‘MERLIN’  strings appearing 10 times per second. Now we are free to do whatever we want with this data.
WARNING : the following will be of interest only to those using or willing to use the Microsoft Visual C++ environment to develop their own displays. This does not prevent using other environments to do the same thing. I have chosen to develop the real-time displays using the MFC classes in Microsoft Visual C++ 2005. This would also apply to Microsoft Visual C++ 2008 and 2010.
The first challenge is to recover the information coming through the USB port. The serial-to-USB converter appears to the notebook as a virtual COM port.
There are some classes that are available on the Internet to read from COM ports. They can be used with success when reading from real COM ports that were still available some years ago on any computer. But times have changed and we have now to live only with USB ports. When I switched from real COM ports to (USB) virtual ones, I discovered that these classes won’t do the job anymore, because they were designed to read the incoming characters one at a time at a regular and predictable baud rate. A USB-based virtual port will receive the characters at the specified baud rate on the average, but with short-term bursts and idle periods, and the old classes cannot follow.
Fortunately, these guys have developed a life-saver class that can deal with the situation:
http://cpp.developpez.com/faq/vc/?page=NetWork. Go to the section ‘Comment travailler avec le port série ?’ and you will find the source code for the class, then download the example : http://farscape.developpez.com/Samples/SerialDemo.zip to see how to use it.

I will not go through all the implementation here (look at the example for that), but I will concentrate on coding the particular function that gets called when something shows up at the virtual COM port. The communication object lives in a separate thread, and when one or more characters appear at the port, triggers the main application to call the ‘OnReceiveCom’ function. Note that all running averages calculations and updates also have to be done in this function, but they are not shown here.

This is a very partial extract of the final implementation, but the objective here is to document how to synchronize with the incoming data and repopulate the local data structure at the 10 Hz refresh rate, before asking the display window to redraw itself using this new data.

typedef union
{
   unsigned char dump[76];
   struct
   {
      float mwa; // measured apparent wind angle
      float awa; // awa corrected for heel
      float twa; // true wind angle
      float aws;  // apparent wind speed
      float tws; // true wind speed
      float wdir; // wind direction
      float mbs1; // measured boat speed (starboard)
      float mbs2; // measured boat speed (port)
      float stw; // boat speed through water
      float vmg; // velocity made good
      float heading; // corrected heading
      float heel; // heel angle
      float leeway; // calculated leeway angle
      float doc; // direction of current
      float soc; // speed of current
      float cog; // GPS course over ground
      float sog; // GPS speed over ground
      short long1; // GPS longitude (1st part)
      short long2; // GPS longitude (2nd part)
      short lat1;  // GPS latitude (1st part)
      short lat2;  // GPS latitude (2nd part)
   };
} dump_union;  //  float are 4 bytes, short are 2 bytes 


// partial extract of
class CMerlinDlg : public CDialog
{
    //...

 public:
    int counter;
    unsigned char merlin[6];
    dump_union dump_info;

    //...
}


BOOL CMerlinDlg::OnInitDialog()
{
   //...

   counter = -1;

   //...
}


/* Most of the time, this function will be called with
 * only one character in the receive buffer (nr = 1),
 * but occasionnaly there will be 2, 3 or more.
*/
LONG CMerlinDlg::OnReceiveCom(WPARAM ch, LPARAM port)
{
    int nr, i;
    char *szReceive;

    CCom *pCom=(CCom*)ch;
    szReceive = new char[(nr=(pCom->SizeUsedInRXBuf()))];
    pCom->ReadBuffer(szReceive,nr);

    for(i = 0; i < nr; i++)
    {
       merlin[0] = merlin[1];
       merlin[1] = merlin[2];
       merlin[2] = merlin[3];
       merlin[3] = merlin[4];
       merlin[4] = merlin[5];
       merlin[5] = (unsigned char)(szReceive[i]);

      
       if(merlin[5] == 'N')
       {
          if (merlin[0] == 'M' && merlin[1] == 'E' && merlin[2] == 'R'
              && merlin[3] == 'L' && merlin[4] == 'I')
          {
             counter = 0;
             continue;
          }
       }

      
       if(counter >= 0)
       {
          dump_info.dump[counter] = (unsigned char)(szReceive[i]);
          counter++;
   

          if(counter == 76)    // we have a complete data structure
          {
             counter = -1;

             // update all the running averages here
             // ...


             // command the display window to redraw itself using the new data
             dispwnd->InvalidateRect(NULL);
           }
        }
     }
   
     delete [] szReceive;
     return 0L;
}


Typical results can be seen on 2 short video clips in the following post:
http://sailboatinstruments.blogspot.com/2011/01/damping.html

No comments:

Post a Comment