PIC24 Support Libraries
dataXferImpl.h
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 
30 #pragma once
31 
32 /** \file
33  * \brief Routines which implement the \ref dataXfer "uC comm protocol".
34  *
35  * This implements code which receives data from a uC sent
36  * either as individual characters or as data packets. For a
37  * complete specification, see the \ref dataXfer "uC comm protocol".
38  *
39  * Two state machines implement this spec. The receive state
40  * machine uses the command-finding state machine in its operation,
41  * so that a user of this code will not normally need access
42  * to the command-finding machine.
43  *
44  * Both machines can be reset (resetCommandFindMachine(),
45  * resetReceiveMachine()) and advanced by one state
46  * (stepCommandFindMachine(), stepReceiveMachine()).
47  *
48  * Two mechanisms provide read access to the receive
49  * state machine. Low-level calls (getReceiveMachineOutChar(),
50  * getReceiveMachineIndex(), getReceiveMachineError(),
51  * getReceiveMachineIsSpec()) report machine
52  * state, while high-level calls (isReceiveMachineChar(),
53  * isReceiveMachineData(), isReceiveMachineSpec()) aggreate that state into more
54  * meaningful information.
55  *
56  * Note: this is implemented in C (not C++), so that similar code can be re-used on
57  * a uC.
58  *
59  * \section implementation Implementation
60  * The uC keeps state in \ref xferVar;
61  * a NULL address indicates nothing is present.
62  *
63  * On receive: implement as a state machine (see sketch below).
64  *
65  * \section receiveSketch Receive state machine sketch
66  * For simplicity, "getch" really means one invocation of the receive
67  * state machine, which is runs until the next getch. Timeouts are not
68  * shown: if too much time passes, the state machine is reset to the
69  * top. Likewise, receiving a command at any unexpected point causes
70  * a state machine reset.
71 <pre>
72 varBits = getch
73 if !command:
74  c = varBits // This was just a normal char; return it
75  return index = 0
76  restart
77 // varBits is a command, not a char
78 if isLongVar(varBits):
79  varNum, len = getch
80 else
81  assign varNum, len from varBits
82 if isVarSpec and !MICROCONTROLLER
83  getch len times to buf
84  parse to var spec
85  report any errors, restart
86 else if isValid(varNum, len)
87  getch len times to var
88  return varIndex
89 else
90  getch len times and discard
91  report error, restart
92 </pre>
93  *
94  * \author Bryan A. Jones, bjones AT ece DOT msstate DOT edu.
95  */
96 
97 #include <stdint.h>
98 #include <stdlib.h>
99 
100 // Determine if we're compiling for a microcontroller or not. If so, include
101 // an ASSERT statement.
102 #if defined(__PIC24H__) || defined(__PIC24F__) || defined(__dsPIC33F__) || defined(__PIC24FK__) || defined(__PIC24E__) || defined(__dsPIC33E__)
103 #define MICROCONTROLLER
104 #include "pic24_unittest.h"
105 #endif
106 
107 
108 // Specify that this is C code for proper C++ linking
109 #ifdef __cplusplus
110 extern "C" {
111 #endif
112 
113 /** \name Constants
114  * These values are \#defined as necessary so that the work under:
115  * - Pre-C99 (no bool, true, or false); this is MSVC++ in C mode
116  * and GCC for the PIC24.
117  * - C++ (bool, true, and false built in)
118  * - C++/CLI (NULL not defined)
119  */
120 // @{
121 #if defined(__cplusplus)
122 # ifndef NULL
123 /// Define NULL under C++/CLI for portability
124 # define NULL nullptr
125 # endif
126 
127 # ifndef FALSE
128 /// The boolean value false.
129 # define FALSE false
130 # endif
131 
132 # ifndef TRUE
133 /// The boolean value true.
134 # define TRUE true
135 # endif
136 
137 /// A boolean data type, which can take on values of
138 /// \ref TRUE and \ref FALSE.
139 # define BOOL bool
140 
141 /// On MSVC under C++, use a throw for an assert. This
142 /// is defined already for the microcontroller.
143 # ifndef ASSERT
144 # ifdef _NOASSERT
145 # define ASSERT(placeholder) (void)0
146 # else
147 # define ASSERT(x) if (!(x)) throw #x
148 # endif
149 # endif
150 
151 /// An assert with message macro
152 # ifndef ASSERTM
153 # ifdef _NOASSERT
154 # define ASSERT(msg, expr) (void)0
155 # else
156 # define ASSERTM(msg, expr) if (! (expr)) throw msg ": " #expr
157 # endif
158 # endif
159 
160 
161 #else // #if defined(__cplusplus)
162 
163 # ifndef FALSE
164 # define FALSE 0
165 # endif
166 
167 # ifndef TRUE
168 # define TRUE 1
169 # endif
170 
171 # ifndef ASSERT
172 # ifdef _NOASSERT
173 # define ASSERT(placeholder) (void)0
174 # else
175 # include <assert.h>
176 # define ASSERT(x) assert(x)
177 # endif
178 # endif
179 
180 /// An assert with message macro; the msg isn't used in C
181 # ifndef ASSERTM
182 # define ASSERTM(msg, expr) ASSERT(expr)
183 # endif
184 
185 # define BOOL unsigned char
186 #endif
187 // @}
188 
189 /// An abbreviation for an unsigned integer.
190  typedef unsigned int uint;
191 
192 #ifndef MICROCONTROLLER
193 /// An abbreviation for an 8-bit unsigned integer.
194  typedef unsigned char uint8_t;
195 #endif
196 
197 
198 /// \name Command-finding state machine
199 //@{
200 
201 /// The character used to begin a command. If this is not a command,
202 /// then use the two-character sequence \ref CMD_TOKEN
203 /// \ref ESCAPED_CMD; a \ref CMD_TOKEN followed
204 /// by any other value is a command.
205 #define CMD_TOKEN ((char) 0xAA)
206 
207 /// After a \ref CMD_TOKEN "command token", this value specifies that
208 /// the character \ref CMD_TOKEN was sent.
209 #define ESCAPED_CMD ((char) 0xFC)
210 
211 /// After a \ref CMD_TOKEN "command token", this value specifies that
212 /// the command is a long variable.
213 #define CMD_LONG_VAR ((char) 0xFD)
214 
215 /// After a \ref CMD_TOKEN "command token", this value specifies that
216 /// the command is a send-only var.
217 #define CMD_SEND_ONLY ((char) 0xFE)
218 
219 /// After a \ref CMD_TOKEN "command token", this value specifies that
220 /// the command is a send/receive var.
221 #define CMD_SEND_RECEIVE_VAR ((char) 0xFF)
222 
223 /// The number of bits in the variable size field of the varBits field, following a
224 /// command token.
225 #define VAR_SIZE_BITS 2
226 
227 /// A mask which removes all but the variable size bits in the varBits field.
228 #define VAR_SIZE_MASK ((1 << VAR_SIZE_BITS) - 1)
229 
230 /// The maximum size of a short variable (which must fit in \ref VAR_SIZE_BITS number
231 /// of bits).
232 #define SHORT_VAR_MAX_LEN (1 << VAR_SIZE_BITS)
233 
234 /// State of the command-finding state machine. See \ref stepCommandFindMachine
235 /// for more information.
236  typedef enum {
237  /// The machine is in its starting state.
239  /// The machine is waiting for another character; c_outChar is not valid.
241  /// The machine is waiting for an additional character; c_outChar is not valid.
243  } CMD_STATE;
244 
245 /// The output of the command-finding state machine. See \ref stepCommandFindMachine
246 /// for more information.
247  typedef enum {
248  /// The state machine produced no output, but is waiting for additional input.
250  /// A character was received; c_outChar contains the character.
252  /// A command was received; c_outChar contains the command.
254  /// A repeated command was received; c_outChar contains the command.
256  /// The machine received a \ref CMD_TOKEN \ref CMD_TOKEN \ref CMD_TOKEN,
257  /// so report a repeated command and wait for the next character to
258  /// finish decoding.
260  } CMD_OUTPUT;
261 
262 /// Resets the command-finding state machine; see \ref stepCommandFindMachine
263 /// for more information.
265 
266  /** The command-finding state machine looks for commands in the data
267  * passed to it. Sequences it recognizes:
268  * - c, where c != \ref CMD_TOKEN, outputs the character c.
269  * - \ref CMD_TOKEN \ref ESCAPED_CMD outputs the character \ref CMD_TOKEN
270  * (known as an escaped command).
271  * - \ref CMD_TOKEN c, where c != \ref CMD_TOKEN and c != \ref ESCAPED_CMD,
272  * outputs the command c.
273  * - \ref CMD_TOKEN \ref CMD_TOKEN \ref ESCAPED_CMD outputs the command
274  * \ref CMD_TOKEN (note that the second \ref CMD_TOKEN is "escaped",
275  * so it is treated as a character specifying the command, not as the
276  * beginning of a command).
277  * - \ref CMD_TOKEN \ref CMD_TOKEN \ref CMD_TOKEN outputs a repeated
278  * command then waits for the next character. See the state machine
279  * sketch below for more information.
280  * - \ref CMD_TOKEN \ref CMD_TOKEN c, where c != \ref CMD_TOKEN and
281  * c != \ref ESCAPED_CMD, outputs
282  * a repeated command c. This is a protocol violation, but must be
283  * reported at a higher level; this routine merely reports a repeated command.
284  * A sketch of the state machine: <pre>
285  * case START :
286  * if (c == CMD_TOKEN) state = WAIT1
287  * else output c as a character
288  * case WAIT1 :
289  * if (c == CMD_TOKEN) state = WAIT2
290  * if (c == ESCAPED_CMD) state = START, output CMD_TOKEN as a character
291  * else output c as a command
292  * case WAIT2 :
293  * if (c == ESCAPED_CMD) state = START, output command CMD_TOKEN
294  * if (c == CMD_TOKEN) output repeated command, remain in this state
295  * else output repeated command c
296  * </pre>
297  * \param c_inChar A character input to the machine.
298  * \param c_outChar The character/command output by the machine when
299  * the returned state is not CMD_WAIT.
300  * \return The output of the machine, which indicates if a
301  * command or character is available.
302  */
303  CMD_OUTPUT stepCommandFindMachine(char c_inChar, char* c_outChar);
304 //@}
305 
306 
307 
308 /// \name Data structures to store received variables
309 //@{
310 
311 /// Struct to hold send/receive data. An array of these entries holds all the
312 /// necessary state.
313  typedef struct {
314  /// Pointer to the data to be exchanged. NULL means this entry is not
315  /// defined.
316  uint8_t* pu8_data;
317  /// Size of data in bytes - 1: 0 = 1 byte, etc.
318  uint8_t u8_size;
319 #if !defined(MICROCONTROLLER) || defined(__DOXYGEN__)
320  /// printf format string to use in displaying the variable. <b>PC only.</b>
321  char* psz_format;
322  /// Name of this variable, typically the same as used
323  /// in the code. <b>PC only.</b>
324  char* psz_name;
325  /// Description of this variable. <b>PC only.</b>
326  char* psz_desc;
327 #endif
328  } XFER_VAR;
329 
330 /// Maximum number of transfer variables supported
331 #define MAX_NUM_XFER_VARS ((1 << (8 - VAR_SIZE_BITS)) - 1)
332 
333 /// Number of transfer variables supported. Must be less than
334 /// the \ref MAX_NUM_XFER_VARS.
335 #define NUM_XFER_VARS 62
336 #if NUM_XFER_VARS > MAX_NUM_XFER_VARS
337 # error "Too many transfer variables; there must be MAX_NUM_XFER_VARS or fewer."
338 #endif
339 
340 /// A table to hold the state of transfer variables.
342 
343 /// An array of isWriteable bits for each var. Each bit is true if the PC is
344 /// allowed to change this variable; false otherwise. This does *NOT*
345 /// restrict the microcontroller to read-only access to this variable.
346  extern uint8_t au8_xferVarWriteable[NUM_XFER_VARS/8 + ((NUM_XFER_VARS % 8) > 0)];
347 
348 //@}
349 
350 
351 
352 /// \name Receive state machine
353 //@{
354 
355 /// This value retured for the index from the receive state
356 /// machine (see, e.g., \ref isReceiveMachineData) indicates
357 /// that a character, not a command, was received.
358 #define CHAR_RECEIVED_INDEX 0xFF
359 
360 /// States of the receive state machine. See
361 /// \ref stepReceiveMachine for more information.
362  typedef enum {
363  /// At the start of the machine
365  /// Waiting for a command or escaped \ref CMD_TOKEN
367  /// Reading data bytes in from a command
369  /// Reading the variable index for a long var command
371  /// Reading the variable length for a long/specification command
373  /// Reading the variable index for a specification command
375 } RECEIVE_STATE;
376 
377 
378 /// Protocol errors produced by the receive state machine.
379 /// Internal errors (invalid state transitions, etc.) are
380 /// detected via ASSERTs.
381 typedef enum {
382  /// No error; all state machine outputs are valid
383  /// when the state is \ref STATE_RECV_START after execution of
384  /// \ref stepReceiveMachine.
385  ERR_NONE = 0,
386  /// A repeated command (the sequence \ref CMD_TOKEN
387  /// \ref CMD_TOKEN c, where c != \ref ESCAPED_CMD),
388  /// was received.
390  /// A timeout occurred in the middle of receiving a
391  /// command.
393  /// A command occurred in the middle of receiving
394  /// data belonging to an earlier command.
396  /// Data was sent to a variable that has not been
397  /// specified: the pointer to its data is NULL.
399  /// Data was sent to a variable which exceeds the
400  /// \ref NUM_XFER_VARS.
402  /// The size of data sent to a variable does not
403  /// match the size specified earlier.
405  /// The destination variable is read-only.
407  /// The microcontroller is sent a variable specification
409 } RECEIVE_ERROR;
410 
411 /// Number of error codes in the \ref RECEIVE_ERROR enum.
412 #define NUM_ERROR_CODES (ERR_MICROCONTROLLER_VAR_SPEC + 1)
413 
414 /// Return the current receive machine state. See \ref stepReceiveMachine
415 /// for more information.
416 RECEIVE_STATE getReceiveMachineState();
417 
418 /// Return the character output by the receive state machine.
419 /// See \ref stepReceiveMachine for more information.
421 
422 /// Return the index output by the receive state machine.
423 /// See \ref stepReceiveMachine for more information.
425 
426 /// Return the error last encountered by the receive state machine.
427 /// See \ref stepReceiveMachine for more information. This also
428 /// clears the error status.
429 RECEIVE_ERROR getReceiveMachineError();
430 
431 #if !defined(MICROCONTROLLER) || defined(__DOXYGEN__)
432 /// Determine if the last data found by the receive state machine
433 /// was a specification; if not, it was data.
434 /// See \ref stepReceiveMachine for more information. <b>PC only.</b>
436 #endif
437 
438 /// Reset the receive state machine to its initial state and clear the
439 /// error status. The outputs are not reset, because they will not be
440 /// valid until after an invocation of the state machine.
441 void resetReceiveMachine();
442 
443 /// Clear the current receive machine error status; the caller should
444 /// therefore handle or report this error to a higher level of the program.
446 
447 /// Clear the received data structure, so that no variables are
448 /// specified.
449 void clearReceiveStruct();
450 
451 /** Determines if the receive state machine just received a character.
452  * \return True when the machine just received a character.
453  */
455 
456 /** Determines if the receive state machine just received some data.
457  * \return True when the machine just received some data.
458  */
460 
461 #if !defined(MICROCONTROLLER) || defined(__DOXYGEN__)
462 /** Determines if the receive state machine just received an updated specification.
463  * <b>PC only.</b>
464  * \return True when the machine just received an updated spec.
465  */
467 #endif
468 
469 /** Return the index of a variable in a command byte.
470  * \param c_cmd Command byte.
471  * \return Index of the variable.
472  */
473 uint getVarIndex(char c_cmd);
474 
475 /** Return the number of bytes of a variable in a command byte.
476  * \param c_cmd Command byte.
477  * \return Number of bytes.
478  */
479 uint getVarLength(char c_cmd);
480 
481 /** Assign a bit in the \ref au8_xferVarWriteable bit field.
482  * \param u_index The index of the variable to set.
483  * \param b_bitVal Bit value to set at this index.
484  */
485 void assignBit(uint u_index, BOOL b_bitVal);
486 
487 /** Read a bit in the \ref au8_xferVarWriteable bit field.
488  * \param u_index The index of the variable to set.
489  * \return The bit value at this index. TRUE indicated the
490  * variable is writeable; FALSE indicates a read-only
491  * variable: only the microcontroller, but not the PC, may change
492  * its value.
493  */
495 
496 /** Notify the state machine that a timeout occurred between receiving
497  * the previous and next character. A timeout may not lead to an error;
498  * for example, between two received data packets or two received characters,
499  * timeout are allowed.
500  * \return An error code; ERROR_NONE (which is false) means no error
501  * occurred.
502  */
503 RECEIVE_ERROR notifyOfTimeout();
504 
505 /** This state machine receives data from the microcontroller. It takes a character
506  * received plus an indication if a timeout occurred since the last
507  * invocation of this function and advances the machine. The machine
508  * produces outputs when the returned state is \ref STATE_RECV_START. Outputs:
509  * - c_outChar, set if a character (versus a data packet) was received
510  * - u_index, set to the index of the data received
511  * - receiveError, an error code
512  * - b_isSpec, true if a specification packet was received.
513  * \param c_inChar A character for the state machine to process.
514  * \return An error code; ERROR_NONE (which is false) means no error
515  * occurred.
516  */
517 RECEIVE_ERROR stepReceiveMachine(char c_inChar);
518 
519 /// Returns an error string matching the last error code.
520 const char* getReceiveErrorString();
521 
522 #ifdef __cplusplus
523 }
524 #endif
The machine is waiting for an additional character; c_outChar is not valid.
Definition: dataXferImpl.h:242
void resetReceiveMachine()
Definition: dataXferImpl.c:160
RECEIVE_ERROR notifyOfTimeout()
Definition: dataXferImpl.c:361
The machine is waiting for another character; c_outChar is not valid.
Definition: dataXferImpl.h:240
Reading the variable index for a long var command.
Definition: dataXferImpl.h:370
unsigned int uint
An abbreviation for an unsigned integer.
Definition: dataXferImpl.h:190
#define NUM_XFER_VARS
Definition: dataXferImpl.h:335
A repeated command was received; c_outChar contains the command.
Definition: dataXferImpl.h:255
void clearReceiveStruct()
Definition: dataXferImpl.c:188
A character was received; c_outChar contains the character.
Definition: dataXferImpl.h:251
Reading the variable length for a long/specification command.
Definition: dataXferImpl.h:372
CMD_STATE
Definition: dataXferImpl.h:236
uint8_t au8_xferVarWriteable[NUM_XFER_VARS/8+((NUM_XFER_VARS % 8) > 0)]
Definition: dataXferImpl.c:101
char * psz_name
Definition: dataXferImpl.h:324
BOOL
Definition: all_generic.h:402
void clearReceiveMachineError()
Definition: dataXferImpl.c:169
BOOL isReceiveMachineChar()
Definition: dataXferImpl.c:200
static char c_outChar
Definition: dataXferImpl.c:112
CMD_OUTPUT
Definition: dataXferImpl.h:247
RECEIVE_STATE
Definition: dataXferImpl.h:362
RECEIVE_ERROR getReceiveMachineError()
Definition: dataXferImpl.c:146
char * psz_desc
Description of this variable. PC only.
Definition: dataXferImpl.h:326
Reading data bytes in from a command.
Definition: dataXferImpl.h:368
const char * getReceiveErrorString()
Returns an error string matching the last error code.
Definition: dataXferImpl.c:612
char getReceiveMachineOutChar()
Definition: dataXferImpl.c:137
BOOL isVarWriteable(uint u_index)
Definition: dataXferImpl.c:279
BOOL isReceiveMachineData()
Definition: dataXferImpl.c:206
uint getVarIndex(char c_cmd)
Definition: dataXferImpl.c:220
At the start of the machine.
Definition: dataXferImpl.h:364
XFER_VAR xferVar[NUM_XFER_VARS]
A table to hold the state of transfer variables.
Definition: dataXferImpl.c:100
BOOL isReceiveMachineSpec()
Definition: dataXferImpl.c:213
uint getVarLength(char c_cmd)
Definition: dataXferImpl.c:224
static uint u_index
Definition: dataXferImpl.c:117
uint getReceiveMachineIndex()
Definition: dataXferImpl.c:141
char * psz_format
printf format string to use in displaying the variable. PC only.
Definition: dataXferImpl.h:321
The microcontroller is sent a variable specification.
Definition: dataXferImpl.h:408
void assignBit(uint u_index, BOOL b_bitVal)
Definition: dataXferImpl.c:267
RECEIVE_STATE getReceiveMachineState()
Definition: dataXferImpl.c:133
The state machine produced no output, but is waiting for additional input.
Definition: dataXferImpl.h:249
CMD_OUTPUT stepCommandFindMachine(char c_inChar, char *c_outChar)
Definition: dataXferImpl.c:22
The destination variable is read-only.
Definition: dataXferImpl.h:406
RECEIVE_ERROR
Definition: dataXferImpl.h:381
Reading the variable index for a specification command.
Definition: dataXferImpl.h:374
uint8_t * pu8_data
Definition: dataXferImpl.h:316
A command was received; c_outChar contains the command.
Definition: dataXferImpl.h:253
Waiting for a command or escaped CMD_TOKEN.
Definition: dataXferImpl.h:366
void resetCommandFindMachine()
Definition: dataXferImpl.c:17
RECEIVE_ERROR stepReceiveMachine(char c_inChar)
Definition: dataXferImpl.c:374
BOOL getReceiveMachineIsSpec()
Definition: dataXferImpl.c:154
unsigned char uint8_t
An abbreviation for an 8-bit unsigned integer.
Definition: dataXferImpl.h:194
uint8_t u8_size
Size of data in bytes - 1: 0 = 1 byte, etc.
Definition: dataXferImpl.h:318
The machine is in its starting state.
Definition: dataXferImpl.h:238