Microchip Technology
Welcome to www.microchip.com
Search: Click here to Search Microchip.com
Forums Home Register LoginLog Out Inbox Address Book My Subscription Member List Search My Profile FAQ
Most efficient way to "decode" serial data?

Most efficient way to "decode" serial data?

 
View related threads: (in this forum | in all forums)

Logged in as: Guest
Users viewing this topic: none
  Printable Version
All Forums >> [Microcontroller Discussion Group] >> SCI/USART/EUSART >> Most efficient way to "decode" serial data? Page: [1]
Login
Message << Older Topic   Newer Topic >>
Most efficient way to "decode" serial data? - May 16, 2006 1:09:30 PM   
pzoom

 

Posts: 22
Joined: Sep. 6, 2005
Status: offline
What is the best way to decode 2 bytes of data to perform functions?  Right now, I am using CASE statements.  I read in the first byte use a CASE statement then read the second byte and then another CASE statement to chose the function. 

Is this the most efficient/best way to do this?  My code is beginning to get out of hand as I add functions.

Below is sample of my code.  If I read in an ASCII "D" then I read in the second byte and decide to change the state of the diverter value.

input=Read2USART(); //read in first byte
switch (input) {
 case 0x44: // D 
  while(!DataRdy2USART());
  input2=Read2USART(); //read in second byte
  switch(input2){
   case 0x30:  // 0 diverter 
    Write2USART(input);  //echo back read in data
    while (Busy2USART());
    Write2USART(input2);
    while (Busy2USART());
    diverter=0; //change diverter value
   break;
   case 0x31:  // 1 diverter 
    Write2USART(input);
    while (Busy2USART());
    Write2USART(input2);
    while (Busy2USART());
    diverter=1;
   break;
  }
Post #: 1
RE: Most efficient way to "decode" serial data? - May 16, 2006 3:25:34 PM   
Guest
switch() {}  statements are good to select code based on some discrete values that a var may take. But you have another problem: noise.

The major concern in any communication link is noise. Your code will execute a sequence if any garbage that include a 'D' is received. Suppose you have a lightning nearby and a ground spike gets coupled to your PC power supply and flips a bit in the RS232 cable just when the command is received, changing the "D" to an "E". And suppose the "E" wrongly received gets interpreted as "start sequence for missile launch" instead of "set room temperature".

In serial communications, you need to validade data. Either you use longer command sequences, e.g. for human interfacing, or you use data frames with integrity check.


(in reply to pzoom)
  Post #: 2
RE: Most efficient way to "decode" serial data? - May 16, 2006 3:48:22 PM   
pzoom

 

Posts: 22
Joined: Sep. 6, 2005
Status: offline
Thanks for the reply.  I plan on doing error checking etc in the future, but is the "CASE" statment the most efficient way to decode serial data?

(in reply to Guest)
Post #: 3
RE: Most efficient way to "decode" serial data? - May 16, 2006 4:11:41 PM   
Guest
Yes, and No.
A switch(){} is a good way to implement multiple decisions.
That is not the best way to 'decode' data that comes from a serial port.

(in reply to pzoom)
  Post #: 4
RE: Most efficient way to "decode" serial data? - May 16, 2006 5:06:46 PM   
jtemples
5+ years with MCHP products

 

Posts: 4751
Joined: Feb. 13, 2004
From: Southern California
Status: offline
You want to read the serial port in exactly one place in your code.  If your commands are always two bytes, you might want to always read two bytes, then dispatch those bytes to a handler.  You don't want to read the serial port in 10 or 100 different places in your code.  Likewise, you don't want to repeat the "write two bytes to the serial port" code over and over.  Make it a function, or do it in an interrupt handler.

(in reply to pzoom)
Post #: 5
RE: Most efficient way to "decode" serial data? - May 16, 2006 8:31:29 PM   
Guest
pzoom, the classic solution to this problem is a finite state machine.  it may not be the best solution for a pic processor because the finite state machine in it's basic implementation is table/array based and memory is often at a premium in a pic.  but as i said it is the classic solution, you set up a finite state machine to do things like what you are trying to do ... and it scales well, you can even use them to parse whole grammars and implement complex protocols.  basically each character that you read changes the state you are in, moving you one step closer to some "event" state where an action is performed, or resetting you back to the beginning state.  the genius is that every character is accounted for, the behavior of the state machine is very predictable, always works, and when done right doesn't have any weird states in it that are unaccounted for, so there are no surprises.  it's also very straight-forward to implement and easy to validate and test.

(in reply to pzoom)
  Post #: 6
RE: Most efficient way to "decode" serial data? - May 16, 2006 8:53:48 PM   
Guest
Excellent advice, both. I implement user interfaces with Hyperterminal with a syntax parser that is a classic directed graph vector FSM where the multiple languages are implemented as string vectors.

It is all implemented using switch() statements (in assembly), and handles very well garbage input and correctly typed commands, including number parsing.

Much simpler state machines can be done with fixed parsing patterns.


(in reply to Guest)
  Post #: 7
RE: Most efficient way to "decode" serial data? - May 17, 2006 2:03:52 PM   
DavidP5

 

Posts: 460
Joined: Feb. 1, 2006
From: New Zealand
Status: offline
quote:

ORIGINAL: j_doin

Excellent advice, both. I implement user interfaces with Hyperterminal with a syntax parser that is a classic directed graph vector FSM where the multiple languages are implemented as string vectors.

It is all implemented using switch() statements (in assembly), and handles very well garbage input and correctly typed commands, including number parsing.

Much simpler state machines can be done with fixed parsing patterns.



So, you read all the characters, do error checking, then use the checked characters as input the the FSM?
Can you give details about how to implement a FSM on a PIC, or point me to something good to read about this. I am familiar with the theory of FSMs, but I have not seen how to implement this well in C, particularly in a PIC environment. Thanks.

(in reply to Guest)
Post #: 8
RE: Most efficient way to "decode" serial data? - May 17, 2006 4:23:24 PM   
Guest
quote:

ORIGINAL: DavidP5

Can you give details about how to implement a FSM on a PIC, or point me to something good to read about this. I am familiar with the theory of FSMs, but I have not seen how to implement this well in C, particularly in a PIC environment. Thanks.

This is better shown than explained.
I have written a quick example of state machine in a simple working program. This program simulates a heater and the state machine is a ON/OFF setpoint thermostat. It exemplifies the use of enumerated types, switch() and a realtime process implemented as a state machine. You can run it in MPSIM, and watch the variables. The temperature will rise from 0.00000 to 26.9999 and then will oscillate around 25.0000:
#include "p18f452.h"
   
#define red_led     LATBbits.LATB0
#define green_led   LATBbits.LATB1
   
typedef enum tag_heater_cmd { ON=0, OFF } t_heater_cmd;
t_heater_cmd heater_state;
char setpoint;
float temperature;

//
// This simulates the increase / decrease of temperature.
//
void heater_process ( void )
{
   if (heater_state == ON) {
      temperature += 0.0001;
   } else {
      temperature -= 0.0001;
   }
}

//
// This function sets the heater ON/OFF.
//    
void heater ( t_heater_cmd cmd )
{
   heater_state = cmd;
}

//
// This function simulates a temperature acquisition.
//    
char get_temp( void )
{
   return temperature;
}
   
// this enumerated type defines the states for the FSM
enum tag_thermostat { 
                   _init=0x00,
                   _checktemplow,
                   _checktemphigh,
                   _heater_ON,
                   _heater_OFF } thermostat_state = _init;

//
// This state machine implements a simple thermostat
//    
void thermostat_process (void)
{
   static char setpointlow, setpointhigh;
   unsigned char temp;
   
   switch(thermostat_state)
   {
   case _checktemplow:
       temp = get_temp();
       if (temp <= setpointlow) {
           thermostat_state = _heater_ON;
       }
       break;
   
   case _checktemphigh:
       temp = get_temp();
       if (temp >= setpointhigh) {
           thermostat_state = _heater_OFF;
       }
       break;
   
   case _heater_ON:
       heater(ON);
       red_led = 1;
       green_led = 0;
       setpointhigh = setpoint + 2;
       thermostat_state = _checktemphigh;
       break;
   
   case _heater_OFF:
       heater(OFF);
       red_led = 0;
       green_led = 1;
       setpointlow = setpoint - 2;
       thermostat_state = _checktemplow;
       break;
   
   case _init:
   default:
       setpointhigh = 0;
       heater(OFF);
       setpointlow = setpoint - 2;
       thermostat_state = _checktemplow;
   }
}
   
void main (void)
{
   TRISB = 0x00;
   setpoint = 25;
   
   while (1) {
       thermostat_process();
       heater_process();
   }
}

(in reply to DavidP5)
  Post #: 9
RE: Most efficient way to "decode" serial data? - May 17, 2006 7:02:19 PM   
Guest
davidp5,

i will try to give you a simple example also.

pretend you want to have the pic recognize two commands.  the commands are "help" and "hell".  the "help" command, when recognized, should call a function HELP_USER().  the "hell" command, when recognized, should call a function DESTROY_UNIVERSE().  to make this example even easier the commands can be embedded in other text, it doesn't have to come right after a return character on a new line, but that isn't any harder to implement that this, it just makes the example longer and i don't feel like typing it haha.

ok, so we need to recognize the commands "help" and "hell".  now, what your first instinct is .. is to read characters into a buffer until you reach a newline character and then do something like a string compare operation against a list of words to get your command.  and that's ok in this simple example, but we're going to do this with a finite state machine instead, the better way.

basically your state machine works like this, in this simple example.  you are in state zero, STATE=0.  you have to start somewhere, so we start at state zero.

you read in a character.

when STATE=0 we only have two rules, two possible courses of action.  either we get an "h" character, or we don't.  if we get an "h" character, we move to STATE = 1.  if we get absolutely anything else, we stay in STATE = 0.  now go back and wait for another character.

you read a character.

we already know what happens if we are in state 0.  but what about if we are in state 1 ?

when STATE=1 we only have two rules, two possible courses of action.  either we get an "e" character, or we don't.  if we get an "e" character, we move to STATE = 2.  if we get absolutely anything else, we go back to STATE = 0.  now go wait for another character.

see where this is going ? lol.

ok, so we read in another character (or we exit our interupt service routine and later another character causes an interupt and we get another character that way).

ok, we know what happens in state 0 and state 1.  what about if we are in state 2 ?

when STATE = 2 we only have two rules, two possible courses of action.  either we get an "l" character, or we don't.  if we get an "l" character, we move to STATE = 3.  if we get absolutely anything else, we go back to STATE = 0.  now go wait for another character.

ok, so now we read in another character.

we know what to do in state 0, 1, and 2.  what about 3 ?

when STATE = 3 we only have THREE RULES (not two! lol), three possible courses of action.  either we get an "l" character, or we get a "p" character, or we don't.  if we get an "l" character we have reached an action state where we call our function DESTROY_UNIVERSE().  if we get a "p" character we have also reached an action state and we call our function HELP_USER().  no matter what happens though we go back to STATE = 0.

so now imagine what happens when you give this finite state machine some input.  first i will write the character sequence the user types, then i will after that put the states they go through.

"HZ".  "H" puts us into state 1, but in state 1 "Z" kicks us back to state 0.

"HEZ".  "H" puts us in 1, "E" puts us into state 2, but again "Z" kicks us back to state 0.

"ZZHE".  "Z" keeps us in 0, "Z" again keeps us in 0, then "H" puts us into state 1, and "E" into state 2.

"PLEH".  "P" keeps us in 0, "L" keeps us in 0, "E" keeps us in 0, and then "H" put us into state 1.

"HH".  "H" puts us into state 1, but then the next "H" puts us back into state 0.  see, i just found an error in my finite state machine!  really another "H" should have put us into state 1 and not back into state 0, because "HHELL" is a "bad H" followed by a "good HELL".  but my finite state machine wouldn't recognize it.

ok, so now a valid command.

"HELLO".  "H" puts us in 1, "E" in 2, "L" in 3, and "L" again puts us into an action state which executes DESTROY_UNIVERSE() and we go back into state 0, and the final "O" keeps us in state 0.

i think that's enough to see the basics of how this works.  part of the awesome beauty of this is that it's completely reentrant.  all you have to remember is what state you are in (probably only a single 8 bit byte!), and you can hang this finite state machine off of an interupt service routine for your serial port in the pic.  each time the interupt happens you read a character, and you send that character through the machine.  it either changes your state and/or executes a command, then you return from your interupt.  you don't care what happened before during the last time the interupt was called, you don't have to buffer any strings or remember any array indices, you don't give care what's going to happen next, you just simply don't care about anything except what character you read and what state you are in.  you get to process one character at a time with very minimal overhead, you don't have to wonder/worry about what kind of weird junk the user is going to type in, nothing, you just read in a character and either change states and/or execute functions.

and that's about all there is to it.  you can see that one way to implement this is with nested switch statements.  but another way is to do it with an array where the character is the index to the array and the array elements contain two pieces of information ... 1st, it contains the next state to move to, and 2nd it contains a value that you will use in a case statement to execute commands.  so it's a two dimensional array or table, the first dimension is horizonal (or whatever) and you use the character as the index, and the second dimension is what state you are in.  so if you are in state "1" and you read in an "a", you go to the table entry "1","a" and see what state you are supposed to move to and what command you are supposed to execute, if any.  it has very fast execution and easy to implement.

best of luck.

(in reply to DavidP5)
  Post #: 10
RE: Most efficient way to "decode" serial data? - May 17, 2006 7:30:02 PM   
Guest
quote:

ORIGINAL: fly_by

and that's about all there is to it.  you can see that one way to implement this is with nested switch statements.  but another way is to do it with an array where the character is the index to the array and the array elements contain two pieces of information ... 1st, it contains the next state to move to, and 2nd it contains a value that you will use in a case statement to execute commands.  so it's a two dimensional array or table, the first dimension is horizonal (or whatever) and you use the character as the index, and the second dimension is what state you are in.  so if you are in state "1" and you read in an "a", you go to the table entry "1","a" and see what state you are supposed to move to and what command you are supposed to execute, if any.  it has very fast execution and easy to implement.

That is some very good piece of explanation.

What fly_by described is a directed graph deterministic finite state automaton (digraph DFSA for short). The 'execute' portions are the 'nodes' and the 'rules' are the 'paths' of the digraph. The nice thing about a digraph is that you can use a data structure (e.g., an array, or a string table) to describe the paths, and can have a fixed code that 'interprets' the data structures. Then you can extract unique token codes at the last node that can be used in another state machine, to carry out the 'commands'. Another nice thing about them is that they are the realization of state diagrams that you draw on paper (hence their name).

The standard or theoretical DFSA can be described as a number of nodes (states) with directed arrows from node to node. Each arrow has a condition associated to it, representing the changing state condition. On each node there can be semantic rules, or actions, to be performed. The states (nodes) are the states in our switch() {case} structures. The conditions are the characters to be matched agains the input stream.

You can use such a digraph structure to parse a data stream for interpreting words. Instead of having the 'rules' fixed in code structures, you can have the 'languages' be defined as an array of strings, and then the code will perform pattern matching with the strings in the language dictionary (string table).

Fairly complex command syntax can be parsed in this way, using optimal code and data space usage.

(in reply to Guest)
  Post #: 11
RE: Most efficient way to "decode" serial data? - May 17, 2006 9:48:56 PM   
DavidP5

 

Posts: 460
Joined: Feb. 1, 2006
From: New Zealand
Status: offline
Thanks to both of you for these explanations. The code example from j_doin is very similar to programs I have been writing recently for a PIC, without really thinking of them as implementing a state machine. The explanation from fly-by corresponds more closely to what I have read of the theory of FSMs (particularly the way that state changes and actions are both represented in the table). It gives me an excellent opportunity to experiment with this concept. Thanks.


(in reply to Guest)
Post #: 12
Page:   [1]
All Forums >> [Microcontroller Discussion Group] >> SCI/USART/EUSART >> Most efficient way to "decode" serial data? Page: [1]
Jump to:





New Messages No New Messages
Hot Topic w/ New Messages Hot Topic w/o New Messages
Locked w/ New Messages Locked w/o New Messages
 Post New Thread
 Reply to Message
 Post New Poll
 Submit Vote
 Delete My Own Post
 Delete My Own Thread
 Rate Posts


  Site Index  |  Legal Information  |  microchipDIRECT  |  Samples  |  Technical Support  |  Investor Information  |  Careers at Microchip  |  Contact Us  |  RSS Feeds ©2009 Microchip Technology Inc.  
  Shanghai ICP Recordal No.09049794  
Forum Software © ASPPlayground.NET Advanced Edition 2.5.5 Unicode

0.219