CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Help needed with RDA state machine

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
edhaslam



Joined: 15 Jul 2005
Posts: 89
Location: UK

View user's profile Send private message

Help needed with RDA state machine
PostPosted: Tue Mar 07, 2006 3:59 am     Reply with quote

Hi all,

I'm trying to write a state machine that will allow me to parse data from a Honeywell HMR3300 compass module. The problem is two-fold. Firstly, there isn't a character at the beginning of the sentence to signify that the data transmission has begun. Secondly, the width of the data is variable, i.e. one value could be 25.5 in one sentence and 132.8 in the next. The second two values in the string could also be negative and four or five characters i.e. -31.6 or 32.8. There isn't any bit stuffing either, e.g. 25.5 as opposed to 025.5.

An example of the output from the module is as follows:

Code:

Honeywell HMR3300 (0.7)
#N
3.1,-0.9,0.2
359.1,-0.8,-1.7
2.8,-1.5,-2.2
4.4,-1.1,-2.2
0.1,-0.4,-3.0
359.7,-0.0,-3.0
359.1,0.3,3.0


Data format is Heading, Pitch, Roll<cr><lf> (sent 8 times per second).
My first thoughts are that I need to know when the sentence starts, otherwise by the time the serial interrupt routine fires, the HMR3300 could be transmitting any of those characters. Perhaps testing for the Line Feed character would be the best thing to do and buffer the next sentence. But how to I accomodate for the variablility in the sentence and negative numbers??

The code that I have so far is as follows. Many thanks to rnielsen for his help. It is untested as yet:

Code:

/************************* Start of RDA()  ************************************/

#int_RDA
RDA_isr()
{
static int i, j, k, rxState, scount, valid, ready;
static char rxBuff;
char* hdg[];
char* pitch[];
char* roll[];



  rxBuff = getc(PORT1);

  switch(rxBuff)
  {
    case 0x0a:       //'LF' end of last sentence, get next sentence
      rxState = 1;
      break;
    default:
      break;
  }                  //end of switch(rxBuff)

  switch(rxState)
  {   
     case 1:

      if(rxBuff != 0x2C)       // If !'comma', next 4 or 5 chars are the heading
       {
        if(i < 5)
         {
          hdg[i++] = rxBuff;
          rxState= 2;               // stuff the variable 'hdg' with the current
         }                               // speed value to be displayed elsewhere
        else
         {
          hdg[5] = rxBuff;               // Fill variable with rxBuff contents
          rxState= 2;
         }
      }
    else
      {
        hdg[0] = hdg[1] = hdg[2] = hdg[4] = 0x30;      // Stuff with 0's
        hdg[3] = 0x2E;                                 // Decimal place '.'
        hdg[5] = 0x00;                                 // Null
      }
      break;

    case 2:
      if(rxBuff == 0x2C)             // If 'comma', next 5 chars are the pitch
      {
   if(j < 5)
         {
          pitch[j++] = rxBuff;
          rxState= 3;               // Stuff the variable 'pitch' with the current buffer value
         }                               // Pitch value to be displayed elsewhere
        else
         {
          pitch[5] = rxBuff;               // Fill variable with rxBuff contents
          rxState= 3;
         }
      }
      else
      {
   pitch[0] = pitch[1] = pitch[2] = pitch[4] = 0x30;      // Stuff with 0's
        pitch[3] = 0x2E;                                 // Decimal place '.'
        pitch[5] = 0x00;   
        rxState = 3;
      }
      break;

    case 3:

      if(rxBuff == 0x2C)             // If 'comma', next 5 chars are the pitch
      {
        if(k < 5)
         {
          roll[k++] = rxBuff;
          rxState= 0;               // Stuff the variable 'roll' with the current buffer value
         }                               // Roll value to be displayed elsewhere
        else
         {
          roll[5] = rxBuff;               // Fill variable with rxBuff contents
          rxState= 0;
         }
      }
      else
       {
        roll[0] = roll[1] = roll[2] = roll[4] = 0x30;      // Stuff with 0's
        roll[3] = 0x2E;                                 // Decimal place '.'
        roll[5] = 0x00;   
        rxState = 0;
       }
      break;

    default:
      rxState = 0;
      break;   
   
  }                         // End of switch
}

/**************************  End of RDA()  ************************************/


Any help or comments would be massively appriciated!

Ed
Ttelmah
Guest







PostPosted: Tue Mar 07, 2006 5:02 am     Reply with quote

I think you are on the right lines, but I'd approach it slightly differently. The biggest problems with the approach so far, is that you are doing a lot of array accesses, which are slow, and remember that if you use an alphabetic array to hold a string, you will need to add the null terminators.
You also declare pointers to hold the data, declaring these ars pointers to pointers, but never actually make any storage to hold the values...
Perhaps something along the lines of:

Code:

//External global variables, so the numbers can be externally accessed

int16 heading;
signed int16 pitch;
signed int16 roll;
int1 vals_true=false;

#define aval(x) (x-'0')

#int_RDA
void RDA_isr(void) {
    int8 val;
    static int1 sign;
    static signed int16 temp;
    static int8 state=0;

    val=getc();
    switch (state) {
    case 0:
        //Here I have not yet started the main scan
        if (val=='/n') {
           state=1;
           temp=0;
        }
        break;
    case 1:
        //Here I am looking for a numeric field (no sign on heading)
        if (val==',') {
           state=2;
           heading=temp;
           sign=false;
           temp=0;
           break;
        }
        if (val>='0' && val<='9'
           temp=(temp*10)+aval(val);
        //This will drop 'back' here on the initial text messages
        else if (val!='.') state=0;
        break;
     case 2:
        //Now looking for the second numeric field
        if (val==',') {
           state=3;
           if (sign) pitch=-temp;
           else pitch=temp;
           sign=false;
           temp=0;
           break;
        }
        if (val>='0' && val<='9')
           temp=(temp*10)+aval(val);
        else if (val=='-') sign=true;
        else if (val!='.') state=0;
        break;
     case 3:
        //Now the third
        if (val=='/n') {
           state=1;
           if (sign) roll=-temp;
           else roll=temp;
           vals_true=true;
           temp=0;
           break;
        }
        if (val>='0' && val<='9')
           temp=(temp*10)+aval(val);
        else if (val=='-') sign=true;
        else if (val!='.') state=0;
        break;
     }
}

Now in the main, check for 'vals_true' becoming set, at which point, the three values 'heading', 'pitch', and 'roll'will contain the data received. Clear this flag, and deal with the values. The values will be integers, representing the incoming value*10.
Now I delibrately don't put a 'default' statement (normally bad practice), since this should make the compiler generate a jump table for the switch. The integer multiplication by ten, on a PIC18, will be quick, keeping the interrupt times short.
I think this ought to get close to what you need.

Best Wishes
edhaslam



Joined: 15 Jul 2005
Posts: 89
Location: UK

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 6:27 am     Reply with quote

Quote:

Now in the main, check for 'vals_true' becoming set, at which point, the three values 'heading', 'pitch', and 'roll'will contain the data received. Clear this flag, and deal with the values. The values will be integers, representing the incoming value*10.
Now I delibrately don't put a 'default' statement (normally bad practice), since this should make the compiler generate a jump table for the switch. The integer multiplication by ten, on a PIC18, will be quick, keeping the interrupt times short.
I think this ought to get close to what you need.


Hi Ttelmah,

That is fastastic! Many thanks indeed. I knew I was heading down the right lines, but i'd got it around my kneck a little! With a little tweaking I now have it printing out the values to my second com port. All I need to do is divide the values by 10 and display the float. Rather than use an inefficient (heading/10) in my fprintf, is there a better way of doing this?



Many thanks once again,
Ed

Code:


if (vals_true = true){
         vals_true=false;
         
         heading /= 10;
         pitch /= 10;
         roll /= 10;
         
         fprintf(PORT2, "Bearing %3.1f   Pitch %2.1f   Roll %2.1f\n\r", (float) heading, (float) pitch, (float) roll);
         vals_true=false;
      }   
      else
        fprintf(PORT2, "No Compass Data");
        vals_true=false;





Humberto



Joined: 08 Sep 2003
Posts: 1215
Location: Buenos Aires, La Reina del Plata

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 8:46 am     Reply with quote

I like the solution suggested by Ttelmah in this thread from the C point of view.
The following comments are regarding the design of a new device.

Quote:

Data format is Heading, Pitch, Roll<cr><lf> (sent 8 times per second).

Such amount of data / sec is unpractical if what you want only is to update a display. (Unless if you are working in a missil head guidance flight director) Mr. Green

I guess that you do not need to handle datas at such rate. With the HMR3300 you
can do it very easy with the help of predefined commands that can control its behaviour.
Command: *S<CR><LF>
This command is to Start or Stop the data output.
At power up is set as Start but it is a toggling command that can Stop the data
output as fast as it receive a Stop command.
The HMR3300 has a set of very useful commands that will make you code not so tight
in time, using commands you can querry for an output or ask for an average value.


Humberto


Last edited by Humberto on Tue Mar 07, 2006 9:03 am; edited 2 times in total
edhaslam



Joined: 15 Jul 2005
Posts: 89
Location: UK

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 9:02 am     Reply with quote

Humberto wrote:
I like the solution suggested by Ttelmah in this thread from the C point of view.


Yes, it's a neat bit of code! I specifically wanted to do it this way in order to learn how to parse data. I may even attach a GPS receiver to the device, so it is good practice.

Do yoiu have any examples for querying the HMR3300? I suppose you issue a stop condition (*S<CR><LF>), set to heading mode (*H<CR><LF>) then use a gets() once the HMR3300 has been queried (*Q<CR><LF>) ?

BTW: Can anyone suggest a way of printing out the decimal place in my last code? I thought of dividing each int32 in hundreds, tens, units etc and displaying them in the appropriate position around the decimal place, but it seems like overkill. Is there a better way?

Cheers,
Ed
Humberto



Joined: 08 Sep 2003
Posts: 1215
Location: Buenos Aires, La Reina del Plata

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 9:17 am     Reply with quote

Quote:

Do yoiu have any examples for querying the HMR3300? I suppose you issue a stop condition (*S<CR><LF>), set to heading mode (*H<CR><LF>) then use a gets() once the HMR3300 has been queried (*Q<CR><LF>) ?

Not exactly. To send a Querry the HMR3300 must be in Stop state. This command do not
change the Heading mode wich is the same previous to the Querry command.

Humberto
Ttelmah
Guest







PostPosted: Tue Mar 07, 2006 9:41 am     Reply with quote

You don't need to 'display the float'. Assuming you have a reasonably recent compiler, you can use the %w format. Look in the 'readme.txt' for this. This allows you to treat an integer as 'scaled', and automatically add a decimal point!...
If it is not possible to stop the transmission, consider having 'vals_true', just block the parser, when it is set (still receive the data, but don't parse it). Then in main, leave it set till you want another set of data. When you clear it, the code will look for the data line, and only set the value again when it has a complete value. :-)

Best Wishes
edhaslam



Joined: 15 Jul 2005
Posts: 89
Location: UK

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 9:48 am     Reply with quote

Ttelmah wrote:
You don't need to 'display the float'. Assuming you have a reasonably recent compiler, you can use the %w format. Look in the 'readme.txt' for this. This allows you to treat an integer as 'scaled', and automatically add a decimal point!...
If it is not possible to stop the transmission, consider having 'vals_true', just block the parser, when it is set (still receive the data, but don't parse it). Then in main, leave it set till you want another set of data. When you clear it, the code will look for the data line, and only set the value again when it has a complete value. :-)

Best Wishes


Fantastic! I'll give %w a try. I like the idea of using 'vals_true' to block the parser. Many thanks again Ttelmah!

Now back to getting my ICD-U40 working again... Everything had been going so well today until the da*m thing packed up 10 minutes ago. Now I can't even update the firmware Evil or Very Mad
sandhya



Joined: 06 Nov 2012
Posts: 1

View user's profile Send private message

Not getting the proper heading value in HMR3300
PostPosted: Tue Nov 06, 2012 2:27 am     Reply with quote

Hai,

We are using an HMR3300 digital Magnetic compass in one of our project
we are an *H Command through controller to DMC, from DMC we are receiving the value but its not an true north heading with respect to our gimbal. My DMC is kept on the top of the Gimbal in an 270 deg forward direction.

So help me out how to get the true north heading value with respect to hardware and software.

Thanks
SANDHYA
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group