PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 #ifndef __DATA_XFER_IMPL_H__
31 #define __DATA_XFER_IMPL_H__
32 
33 /** \file
34  * \brief Routines which implement the \ref dataXfer "uC comm protocol".
35  *
36  * This implements code which receives data from a uC sent
37  * either as individual characters or as data packets. For a
38  * complete specification, see the \ref dataXfer "uC comm protocol".
39  *
40  * Two state machines implement this spec. The receive state
41  * machine uses the command-finding state machine in its operation,
42  * so that a user of this code will not normally need access
43  * to the command-finding machine.
44  *
45  * Both machines can be reset (resetCommandFindMachine(),
46  * resetReceiveMachine()) and advanced by one state
47  * (stepCommandFindMachine(), stepReceiveMachine()).
48  *
49  * Two mechanisms provide read access to the receive
50  * state machine. Low-level calls (getReceiveMachineOutChar(),
51  * getReceiveMachineIndex(), getReceiveMachineError(),
52  * getReceiveMachineIsSpec()) report machine
53  * state, while high-level calls (isReceiveMachineChar(),
54  * isReceiveMachineData(), isReceiveMachineSpec()) aggreate that state into more
55  * meaningful information.
56  *
57  * Note: this is implemented in C (not C++), so that similar code can be re-used on
58  * a uC.
59  *
60  * \section implementation Implementation
61  * The uC keeps state in \ref xferVar;
62  * a NULL address indicates nothing is present.
63  *
64  * On receive: implement as a state machine (see sketch below).
65  *
66  * \section receiveSketch Receive state machine sketch
67  * For simplicity, "getch" really means one invocation of the receive
68  * state machine, which is runs until the next getch. Timeouts are not
69  * shown: if too much time passes, the state machine is reset to the
70  * top. Likewise, receiving a command at any unexpected point causes
71  * a state machine reset.
72 <pre>
73 varBits = getch
74 if !command:
75  c = varBits // This was just a normal char; return it
76  return index = 0
77  restart
78 // varBits is a command, not a char
79 if isLongVar(varBits):
80  varNum, len = getch
81 else
82  assign varNum, len from varBits
83 if isVarSpec and !PIC
84  getch len times to buf
85  parse to var spec
86  report any errors, restart
87 else if isValid(varNum, len)
88  getch len times to var
89  return varIndex
90 else
91  getch len times and discard
92  report error, restart
93 </pre>
94  *
95  * \author Bryan A. Jones, bjones AT ece DOT msstate DOT edu.
96  */
97 
98 #include <stdlib.h>
99 
100 // Determine if we're compiling for the PIC or not.
101 #if defined(__PIC24H__) || defined(__PIC24F__) || defined(__dsPIC33F__) || defined(__PIC24FK__) || defined(__PIC24E__) || defined(__dsPIC33E__)
102 #define __PIC__
103 #include "pic24_unittest.h"
104 #include "stdint.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 PIC.
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
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 __PIC__
193 /// An abbreviation for an 8-bit unsigned integer.
194  typedef unsigned char uint8_t;
195 #endif
196 
197 
198 // Unit testing: uncomment the following line to run PIC tests on PC.
199 //#define __PIC__
200 
201 /// \name Command-finding state machine
202 //@{
203 
204 /// The character used to begin a command. If this is not a command,
205 /// then use the two-character sequence \ref CMD_TOKEN
206 /// \ref ESCAPED_CMD; a \ref CMD_TOKEN followed
207 /// by any other value is a command.
208 #define CMD_TOKEN ((char) 0xAA)
209 
210 /// After a \ref CMD_TOKEN "command token", this value specifies that
211 /// the character \ref CMD_TOKEN was sent.
212 #define ESCAPED_CMD ((char) 0xFC)
213 
214 /// After a \ref CMD_TOKEN "command token", this value specifies that
215 /// the command is a long variable.
216 #define CMD_LONG_VAR ((char) 0xFD)
217 
218 /// After a \ref CMD_TOKEN "command token", this value specifies that
219 /// the command is a send-only var.
220 #define CMD_SEND_ONLY ((char) 0xFE)
221 
222 /// After a \ref CMD_TOKEN "command token", this value specifies that
223 /// the command is a send/receive var.
224 #define CMD_SEND_RECEIVE_VAR ((char) 0xFF)
225 
226 /// The number of bits in the variable size field of the varBits field, following a
227 /// command token.
228 #define VAR_SIZE_BITS 2
229 
230 /// A mask which removes all but the variable size bits in the varBits field.
231 #define VAR_SIZE_MASK ((1 << VAR_SIZE_BITS) - 1)
232 
233 /// The maximum size of a short variable (which must fit in \ref VAR_SIZE_BITS number
234 /// of bits).
235 #define SHORT_VAR_MAX_LEN (1 << VAR_SIZE_BITS)
236 
237 /// State of the command-finding state machine. See \ref stepCommandFindMachine
238 /// for more information.
239  typedef enum {
240  /// The machine is in its starting state.
242  /// The machine is waiting for another character; c_outChar is not valid.
244  /// The machine is waiting for an additional character; c_outChar is not valid.
246  } CMD_STATE;
247 
248 /// The output of the command-finding state machine. See \ref stepCommandFindMachine
249 /// for more information.
250  typedef enum {
251  /// The state machine produced no output, but is waiting for additional input.
253  /// A character was received; c_outChar contains the character.
255  /// A command was received; c_outChar contains the command.
257  /// A repeated command was received; c_outChar contains the command.
259  /// The machine received a \ref CMD_TOKEN \ref CMD_TOKEN \ref CMD_TOKEN,
260  /// so report a repeated command and wait for the next character to
261  /// finish decoding.
263  } CMD_OUTPUT;
264 
265 /// Resets the command-finding state machine; see \ref stepCommandFindMachine
266 /// for more information.
268 
269  /** The command-finding state machine looks for commands in the data
270  * passed to it. Sequences it recognizes:
271  * - c, where c != \ref CMD_TOKEN, outputs the character c.
272  * - \ref CMD_TOKEN \ref ESCAPED_CMD outputs the character \ref CMD_TOKEN
273  * (known as an escaped command).
274  * - \ref CMD_TOKEN c, where c != \ref CMD_TOKEN and c != \ref ESCAPED_CMD,
275  * outputs the command c.
276  * - \ref CMD_TOKEN \ref CMD_TOKEN \ref ESCAPED_CMD outputs the command
277  * \ref CMD_TOKEN (note that the second \ref CMD_TOKEN is "escaped",
278  * so it is treated as a character specifying the command, not as the
279  * beginning of a command).
280  * - \ref CMD_TOKEN \ref CMD_TOKEN \ref CMD_TOKEN outputs a repeated
281  * command then waits for the next character. See the state machine
282  * sketch below for more information.
283  * - \ref CMD_TOKEN \ref CMD_TOKEN c, where c != \ref CMD_TOKEN and
284  * c != \ref ESCAPED_CMD, outputs
285  * a repeated command c. This is a protocol violation, but must be
286  * reported at a higher level; this routine merely reports a repeated command.
287  * A sketch of the state machine: <pre>
288  * case START :
289  * if (c == CMD_TOKEN) state = WAIT1
290  * else output c as a character
291  * case WAIT1 :
292  * if (c == CMD_TOKEN) state = WAIT2
293  * if (c == ESCAPED_CMD) state = START, output CMD_TOKEN as a character
294  * else output c as a command
295  * case WAIT2 :
296  * if (c == ESCAPED_CMD) state = START, output command CMD_TOKEN
297  * if (c == CMD_TOKEN) output repeated command, remain in this state
298  * else output repeated command c
299  * </pre>
300  * \param c_inChar A character input to the machine.
301  * \param c_outChar The character/command output by the machine when
302  * the returned state is not CMD_WAIT.
303  * \return The output of the machine, which indicates if a
304  * command or character is available.
305  */
306  CMD_OUTPUT stepCommandFindMachine(char c_inChar, char* c_outChar);
307 //@}
308 
309 
310 
311 /// \name Data structures to store received variables
312 //@{
313 
314 /// Struct to hold send/receive data. An array of these entries holds all the
315 /// necessary state.
316  typedef struct {
317  /// Pointer to the data to be exchanged. NULL means this entry is not
318  /// defined.
319  uint8_t* pu8_data;
320  /// Size of data in bytes – 1: 0 = 1 byte, etc.
321  uint8_t u8_size;
322 #if !defined(__PIC__) || defined(__DOXYGEN__)
323  /// printf format string to use in displaying the variable. <b>PC only.</b>
324  char* psz_format;
325  /// Name of this variable, typically the same as used
326  /// in the code. <b>PC only.</b>
327  char* psz_name;
328  /// Description of this variable. <b>PC only.</b>
329  char* psz_desc;
330 #endif
331  } XFER_VAR;
332 
333 /// Maximum number of transfer variables supported
334 #define MAX_NUM_XFER_VARS ((1 << (8 - VAR_SIZE_BITS)) - 1)
335 
336 /// Number of transfer variables supported. Must be less than
337 /// the \ref MAX_NUM_XFER_VARS.
338 #define NUM_XFER_VARS 62
339 #if NUM_XFER_VARS > MAX_NUM_XFER_VARS
340 #error Too many transfer variables; there must be MAX_NUM_XFER_VARS or fewer.
341 #endif
342 
343 /// A table to hold the state of transfer variables.
345 
346 /// An array of isWriteable bits for each var. Each bit is true if the PC is
347 /// allowed to change this variable; false otherwise. This does *NOT*
348 /// restrict the PIC to read-only access to this variable.
349  extern uint8_t au8_xferVarWriteable[NUM_XFER_VARS/8 + ((NUM_XFER_VARS % 8) > 0)];
350 
351 //@}
352 
353 
354 
355 /// \name Receive state machine
356 //@{
357 
358 /// This value retured for the index from the receive state
359 /// machine (see, e.g., \ref isReceiveMachineData) indicates
360 /// that a character, not a command, was received.
361 #define CHAR_RECEIVED_INDEX 0xFF
362 
363 /// States of the receive state machine. See
364 /// \ref stepReceiveMachine for more information.
365  typedef enum {
366  /// At the start of the machine
368  /// Waiting for a command or escaped \ref CMD_TOKEN
370  /// Reading data bytes in from a command
372  /// Reading the variable index for a long var command
374  /// Reading the variable length for a long/specification command
376  /// Reading the variable index for a specification command
378  } RECEIVE_STATE;
379 
380 
381 /// Protocol errors produced by the receive state machine.
382 /// Internal errors (invalid state transitions, etc.) are
383 /// detected via ASSERTs.
384  typedef enum {
385  /// No error; all state machine outputs are valid
386  /// when the state is \ref STATE_RECV_START after execution of
387  /// \ref stepReceiveMachine.
388  ERR_NONE = 0,
389  /// A repeated command (the sequence \ref CMD_TOKEN
390  /// \ref CMD_TOKEN c, where c != \ref ESCAPED_CMD),
391  /// was received.
393  /// A timeout occurred in the middle of receiving a
394  /// command.
396  /// A command occurred in the middle of receiving
397  /// data belonging to an earlier command.
399  /// Data was sent to a variable that has not been
400  /// specified: the pointer to its data is NULL.
402  /// Data was sent to a variable which exceeds the
403  /// \ref NUM_XFER_VARS.
405  /// The size of data sent to a variable does not
406  /// match the size specified earlier.
408  /// The destination variable is read-only.
410  /// The PIC is sent a variable specification
412  } RECEIVE_ERROR;
413 
414 /// Number of error codes in the \ref RECEIVE_ERROR enum.
415 #define NUM_ERROR_CODES (ERR_PIC_VAR_SPEC + 1)
416 
417 /// Return the current receive machine state. See \ref stepReceiveMachine
418 /// for more information.
420 
421 /// Return the character output by the receive state machine.
422 /// See \ref stepReceiveMachine for more information.
424 
425 /// Return the index output by the receive state machine.
426 /// See \ref stepReceiveMachine for more information.
427  uint getReceiveMachineIndex();
428 
429 /// Return the error last encountered by the receive state machine.
430 /// See \ref stepReceiveMachine for more information. This also
431 /// clears the error status.
433 
434 #if !defined(__PIC__) || defined(__DOXYGEN__)
435 /// Determine if the last data found by the receive state machine
436 /// was a specification; if not, it was data.
437 /// See \ref stepReceiveMachine for more information. <b>PC only.</b>
439 #endif
440 
441 /// Reset the receive state machine to its initial state and clear the
442 /// error status. The outputs are not reset, because they will not be
443 /// valid until after an invocation of the state machine.
444  void resetReceiveMachine();
445 
446 /// Clear the current receive machine error status; the caller should
447 /// therefore handle or report this error to a higher level of the program.
449 
450 /// Clear the received data structure, so that no variables are
451 /// specified.
452  void clearReceiveStruct();
453 
454  /** Determines if the receive state machine just received a character.
455  * \return True when the machine just received a character.
456  */
458 
459  /** Determines if the receive state machine just received some data.
460  * \return True when the machine just received some data.
461  */
463 
464 #if !defined(__PIC__) || defined(__DOXYGEN__)
465  /** Determines if the receive state machine just received an updated specification.
466  * <b>PC only.</b>
467  * \return True when the machine just received an updated spec.
468  */
470 #endif
471 
472  /** Return the index of a variable in a command byte.
473  * \param c_cmd Command byte.
474  * \return Index of the variable.
475  */
476  uint getVarIndex(char c_cmd);
477 
478  /** Return the number of bytes of a variable in a command byte.
479  * \param c_cmd Command byte.
480  * \return Number of bytes.
481  */
482  uint getVarLength(char c_cmd);
483 
484  /** Assign a bit in the \ref au8_xferVarWriteable bit field.
485  * \param u_index The index of the variable to set.
486  * \param b_bitVal Bit value to set at this index.
487  */
488  void assignBit(uint u_index, BOOL b_bitVal);
489 
490  /** Read a bit in the \ref au8_xferVarWriteable bit field.
491  * \param u_index The index of the variable to set.
492  * \return The bit value at this index. TRUE indicated the
493  * variable is writeable; FALSE indicates a read-only
494  * variable: only the PIC, but not the PC, may change
495  * its value.
496  */
498 
499  /** Notify the state machine that a timeout occurred between receiving
500  * the previous and next character. A timeout may not lead to an error;
501  * for example, between two received data packets or two received characters,
502  * timeout are allowed.
503  * \return An error code; ERROR_NONE (which is false) means no error
504  * occurred.
505  */
507 
508  /** This state machine receives data from the PIC. It takes a character
509  * received plus an indication if a timeout occurred since the last
510  * invocation of this function and advances the machine. The machine
511  * produces outputs when the returned state is \ref STATE_RECV_START. Outputs:
512  * - c_outChar, set if a character (versus a data packet) was received
513  * - u_index, set to the index of the data received
514  * - receiveError, an error code
515  * - b_isSpec, true if a specification packet was received.
516  * \param c_inChar A character for the state machine to process.
517  * \return An error code; ERROR_NONE (which is false) means no error
518  * occurred.
519  */
520  RECEIVE_ERROR stepReceiveMachine(char c_inChar);
521 
522 /// Returns an error string matching the last error code.
523  const char* getReceiveErrorString();
524 
525 #ifdef __cplusplus
526 }
527 #endif
528 
529 #endif