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 };
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 }
Software
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.