|
|
View previous topic :: View next topic |
Author |
Message |
edhaslam
Joined: 15 Jul 2005 Posts: 89 Location: UK
|
Help needed with RDA state machine |
Posted: Tue Mar 07, 2006 3:59 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 5:02 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 6:27 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 8:46 am |
|
|
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)
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
|
|
Posted: Tue Mar 07, 2006 9:02 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 9:17 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 9:41 am |
|
|
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
|
|
Posted: Tue Mar 07, 2006 9:48 am |
|
|
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 |
|
|
sandhya
Joined: 06 Nov 2012 Posts: 1
|
Not getting the proper heading value in HMR3300 |
Posted: Tue Nov 06, 2012 2:27 am |
|
|
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 |
|
|
|
|
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
|