33 typedef char (*FsmCallbackFunc)(void);   // pointer to function
34 .....
50 struct FsmTable * FsmStructActive;
51 
52 struct FsmTable
53 {// Scheduler for Finite State Machine
54     unsigned char Stat;         // status index
55     const unsigned char * Str;  // string to send or receive
56     FsmCallbackFunc pCallback;  // function to call after RX or TX is over
57 };
58 
59 enum Fsm{FsmRx, FsmTx, FsmDo, FsmEnd};

For each sequence we must define a structure with some defined fields.

The first field is the code of the operation to perform.
The second is the string to transmit or to wait for as a terminator during receiving.
The third is a pointer to a function to call back at the end of every single step of the FSM.

68 struct FsmTable ReadTimeFsm[] =
69 {/* scheduler used for comm protocol with WiFLy
70   this reads time from http://www.inrim.it
71  */
72     {FsmTx, "$$$", (FsmCallbackFunc)CommFsmIdle},
73     {FsmRx, "CMD", (FsmCallbackFunc)CommFsmIdle},
74     {FsmTx, "open\r\n", (FsmCallbackFunc)CommFsmIdle},
75     {FsmRx, "*CLOS*", (FsmCallbackFunc)TimeDecode},
76     {FsmDo, "", (FsmCallbackFunc)CommFsmWriteRTC},
77     {FsmEnd, "", (FsmCallbackFunc)CommFsmClear}
78 };
In this example when the string related to the time is fully received, it must be decoded. In the previous steps it just calls a dummy callback that does nothing.
Another example for a sequence that just sends a carriage return/line feed and waits for the string "EXIT" without calling any other function
61 struct FsmTable ExitCmdFsm[] =
62 {// scheduler used to exit from command mode
63     {FsmTx, "exit\r\n", (FsmCallbackFunc)CommFsmIdle}, 
64     {FsmRx, "EXIT", (FsmCallbackFunc)CommFsmIdle},
65     {FsmEnd, "", (FsmCallbackFunc)CommFsmIdle}
66 };

After the operations to perform in the FSM have been defined, we must only call the FSM starter pointing to the desired sequence structure.
Of course we must also build the needed procedures to call back.
After that the machine goes autonomously driven only by the USART interrupt or by TMR1 timeout interrupt in case of communication failure.
A flag tells to application layer procedures if the FSM is free to perform another sequence.

 72 void StartCommFsmSched(struct FsmTable * FsmStruct)
 73 {// initialize the FSM
 74     FsmIndx = 0;
 75     CommFsmFlag = 1;        // kick off the FSM
 76     TxFsmFlag = 0;
 77     RxFsmFlag = 0;
 78     FsmStructActive = FsmStruct;
 79     CommFsmDoneFlag = 1;    // the FSM is started
 80 }
 82 void CommFsmSched(struct FsmTable * FsmStruct)
 83 {//Communication Finite State Machine
 84     unsigned char FsmStat;
 85 
 86     FsmStat = FsmStruct[FsmIndx].Stat;
 87     
 88     switch (FsmStat)
 89     {
 90         case FsmRx:
 91           if(RxFsmFlag==0)
 92           {// the first time is called
 93               StartRx(FsmStruct[FsmIndx].Str);
 94           }
 95           else
 96           {// after terminator string has been received
 97               // execute the callback function
 98               CommFsmState = (* FsmStruct[FsmIndx].pCallback);
 99               (* CommFsmState) () ;
100               FsmIndx++;
101               RxFsmFlag = 0;
102           }
103           break;
104 
105         case FsmTx:
106           // transmit the string and then call the callback function
107              
108           if(TxFsmFlag==0)
109           {// the first time is called
110               StartTx(FsmStruct[FsmIndx].Str);
111           }
112           else
113           {// after all the buffer is transmitted
114               // execute the final function
115               CommFsmState = (* FsmStruct[FsmIndx].pCallback);
116               (* CommFsmState) () ;
117               FsmIndx++;
118               TxFsmFlag = 0;
119           }
120           
121           break;
122 
123         case FsmDo:
124           CommFsmState = (* FsmStruct[FsmIndx].pCallback);
125           (* CommFsmState) () ;
126           FsmIndx++;
127           break;
128 
129 
130         case FsmEnd:
131           CommFsmState = (* FsmStruct[FsmIndx].pCallback);
132           (* CommFsmState) () ;
133           CommFsmFlag = 0;
134           CommFsmDoneFlag = 0;    // the FSM is over
135           break;
136 
137         default:
138           CommFsmFlag = 0;
139           break;
140     }
141 }
The Finite State Machine reads all the informations defined in the structure and performs the needed operations.
Structure definition
FSM programming
FSM start
Finite State Machine
update on 20-08-2014
Software

Software

A multipurpose serial communication procedure

The whole MPLABX C project
for the PIC18F4620
is available as an open source at
GitHub repository

To drive the WiFly it needs a series of very simple commands, as well explained in the data sheet and following some examples in this page. The protocol is simple but slow, not structured and differs case by case from both timing and syntax point of view. Once more an asynchronous Finite State Machine scheduled by interrupt is perfect to be integrated with other, time-critical procedures. But I didn't want to write an "hard-coded" program for each different sequence of command. My goal is to realize a generic, multi-purpose procedure applicable for different WiFly operations and re-usable also in different situations.

To be compatible with other time critical procedures, the communication sequence must be developed on several layers. The upper level starts TX or RX of a string and everything must run autonomously driven only by USART peripheral interrupt. The scheduling of the procedure sequence must be managed by an easy configurable FSM. This is possible with an intensive use of the pointers.