PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pic24_util.c
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 
31 
32 // Documentation for this file. If the \file tag isn't present,
33 // this file won't be documented.
34 /** \file
35  * This file contains implmentations for functions prototyped
36  * in pic24_util.h.
37  */
38 
39 #include "pic24_util.h"
40 #include "pic24_serial.h"
41 #include "pic24_clockfreq.h"
42 #include "pic24_ports.h"
43 #include "pic24_delay.h"
44 #include <stdio.h> // To define NULL
45 
46 
47 #if !USE_HEARTBEAT && !defined(__DOXYGEN__)
48 // No heartbeat; instead, just define empty functions.
49 void configHeartbeat(void) {
50 }
51 
52 void doHeartbeat(void) {
53 }
54 
55 void toggleHeartbeat(void) {
56 }
57 #else
58 
59 /** \name Heartbeat
60  * @{
61  * These routines provide heartbeat support by blinking a LED
62  * on a regular basis. See doHeartbeat() for more information.
63  *
64  */
65 /** The current heartbeat count. When this value reaches
66  * \ref u32_heartbeatMax, the heatbeat LED is toggled by
67  * doHeartbeat().
68  * \see doHeartbeat
69  */
71 /** When u32_heartbeatCount reaches this maximum, the
72  * heatbeat LED is toggled by doHeartbeat().
73  */
75 
76 
77 /** Configures a GPIO pin for use with the heartbeat and sets
78  * up the heartbeat counter.
79  * \see doHeartbeat
80  */
81 void configHeartbeat(void) {
82  CONFIG_HB_LED();
83  /* long enough to see LED toggle. Incrementing the heartbeat
84  takes several cycles - CYCLES_PER_MS used as the multiplier so that
85  we are tied to FCY
86  */
89  HB_LED = 0; // Turn LED off to show we started running
90  doHeartbeat(); // do Heartbeat at least once
91 }
92 
93 /** This heartbeat function should be called
94  * repeatedly in any sort of blocking wait loop. It will
95  * periodically toggle an LED after \ref u32_heartbeatMax increments.
96  */
97 void doHeartbeat(void) {
100  toggleHeartbeat();
101  u32_heartbeatCount = 0;
102  }
103 }
104 
105 
106 /** A function which toggles the hearbeat LED.
107  \see doHeartbeat
108  */
109 void toggleHeartbeat(void) {
110  HB_LED = !HB_LED;
111 }
112 /// @}
113 #endif
114 
115 /** Persistent storage for an error message, typically
116  * set by \ref reportError and reported at reset by
117  * \ref printResetCause.
118  */
119 static _PERSISTENT const char* sz_lastError;
120 /** Persistent storage for a timeout error, to be reported
121  * if a watchdog reset occurs.
122  */
123 _PERSISTENT const char* sz_lastTimeoutError;
124 
125 #ifndef __PIC24F__
126 /** Store a copy of the INTTREG register as a bitfield.
127  * This is not defined for the PIC24F, so work around
128  * with an \#ifdef. This is _PERSISTENT so that it
129  * survives the resets which occurs immeidately after
130  * the default interrupt handler \ref _DefaultInterrupt
131  * copies INTTREG to this variable.
132  */
133 static _PERSISTENT INTTREGBITS INTTREGBITS_last;
134 /** Make the \ref INTTREGBITS_last also accessible as
135  * a word. This is like <code>uint16_t u16_INTTREGlast</code>
136  * except that INTTREGBITS_last and u16_INTTREGlast
137  * refer to the same data.
138  */
139 #define u16_INTTREGlast BITS2WORD(INTTREGBITS_last)
140 #else
142 #endif
143 
144 /** Provide a default interrupt handler which records what
145  * interrupt was not handled then resets the chip. Typically,
146  * a call to \ref printResetCause during chip startup will then
147  * print the error.
148  */
149 void _ISR _DefaultInterrupt(void) {
150 #ifndef __PIC24F__
151  // Record the interrupt vector and priority of the
152  // unhandled interrupt.
153  u16_INTTREGlast = INTTREG;
154 #else
155  // Any non-zero value causes reportError to report
156  // this. This register doesn't exist on the PIC24F.
157  u16_INTTREGlast = 1;
158 #endif
159  reportError("Unhandled interrupt, ");
160 }
161 
162 
163 /** Report a critical error by recording a message
164  * in \ref sz_lastError then resetting the chip,
165  * assuming hat \ref printResetCause will be called
166  * during chip initialization.
167  * \param sz_errorMessage Error message to report.
168  * \see REPORT_ERROR
169  */
170 void reportError(const char* sz_errorMessage) {
171  //ignore if a previous error has already been triggerred
172  if (sz_lastError == NULL) {
173  sz_lastError = sz_errorMessage;
174  asm ("reset");
175  }
176 }
177 
178 /** Reads a 24-bit program memory word at the given address.
179  * \param u32_address Address of program memory to read.
180  * \return The 24-bit program memory word at u32_address.
181  * The upper 8 bits are 0.
182  */
184  uint16_t u16_offset = u32_address;
185  TBLPAG = u32_address >> 16;
186  return ( ((uint32_t) __builtin_tblrdh(u16_offset)) << 16) |
187  __builtin_tblrdl(u16_offset);
188 }
189 
190 /** Determines the device and revision of the PIC this program
191  * is executing on. This information is then output via the default
192  * UART. A warning message is issued if this program was not compiled
193  * for the chip it is running on.
194  */
196 #ifdef SIM
197  outString("**** SIMULATION MODE: cannot read device and revision ID ****\n");
198 #else
201  uint8_t correctChip = 1;
202  const char* devIDStr = "unknown";
203  const char* revisionStr = "unknown";
204 
205  if (devID == DEV_ID)
206  devIDStr = DEV_ID_STR;
207  else
208  correctChip = 0;
209 
210  switch (revision) {
211  case EXPECTED_REVISION1 :
212  revisionStr = EXPECTED_REVISION1_STR;
213  break;
214 #ifdef EXPECTED_REVISION2
215  case EXPECTED_REVISION2 :
216  revisionStr = EXPECTED_REVISION2_STR;
217  break;
218 #endif
219 #ifdef EXPECTED_REVISION3
220  case EXPECTED_REVISION3 :
221  revisionStr = EXPECTED_REVISION3_STR;
222  break;
223 #endif
224 #ifdef EXPECTED_REVISION4
225  case EXPECTED_REVISION4 :
226  revisionStr = EXPECTED_REVISION4_STR;
227  break;
228 #endif
229 #ifdef EXPECTED_REVISION5
230  case EXPECTED_REVISION5 :
231  revisionStr = EXPECTED_REVISION5_STR;
232  break;
233 #endif
234  }
235 
236  outString("Device ID = ");
237  outUint32(devID);
238  outString(" (");
239  outString(devIDStr);
240  outString("), revision ");
241  outUint32(revision);
242  outString(" (");
243  outString(revisionStr);
244  outString(")\n");
245 
246  if (!correctChip)
247  outString("\n\n"
248  "******************************************************\n"
249  "* WARNING - this program was compiled for the wrong *\n"
250  "* chip. This program may produce unexpected behvior! *\n"
251  "* Edit the header files to properly define this chip *\n"
252  "* and to insure correct operation. *\n"
253  "* *\n"
254  "* NOTE: If this was compiled for the correct chip, *\n"
255  "* and only occurs at power-on (not during a MCLR *\n"
256  "* reset), verify that AVDD and AVSS are connected. *\n"
257  "* On the PIC24H32GP202, not connecting AVDD *\n"
258  "* produces this message only at power-up. *\n"
259  "******************************************************\n");
260 #endif
261 }
262 
263 /** Reports the oscillator currently in use to the default
264  * serial port.
265  */
266 void checkOscOption(void) {
267  uint8_t u8_x;
268 
269  u8_x = _COSC; // Get current oscillator setting
270  switch (u8_x) {
271  case 0:
272  outString("Fast RC Osc\n");
273  break;
274  case 1:
275  outString("Fast RC Osc with PLL\n");
276  break;
277  case 2:
278  outString("Primary Osc (XT, HS, EC)\n");
279  break;
280  case 3:
281  outString("Primary Osc (XT, HS, EC) with PLL\n");
282  break;
283  case 4:
284  outString("Secondary Osc\n");
285  break;
286  case 5:
287  outString("Low Power RC Osc\n");
288  break;
289 #if ( defined(__PIC24H__) || defined(__dsPIC33F__) || defined(__PIC24E__) || defined(__dsPIC33E__))
290  case 6:
291  outString("Fast RC Osc/16\n");
292  break;
293  case 7:
294  outString("Fast RC Osc/N\n");
295  break;
296 #elif ( defined(__PIC24F__) || defined(__PIC24FK__) )
297  case 7 :
298  outString("Fast RC Osc with Postscale");
299  break;
300 #else
301 #error Unknown processor
302 #endif
303  default :
304  reportError("Unknown oscillator type.");
305  break;
306  }
307 }
308 
309 /** Determines and prints the cause of a CPU reset. This should
310  * be called when the chip first starts up. For an example, see
311  * the \ref configBasic function.
312  */
313 void printResetCause(void) {
314  uint8_t u8_resetIdentified;
315 
316  u8_resetIdentified = 0;
317  if (_SLEEP) {
318  outString("\nDevice has been in sleep mode\n");
319  _SLEEP = 0;
320  }
321  if (_IDLE) {
322  outString("\nDevice has been in idle mode\n");
323  _IDLE = 0;
324  }
325  outString("\nReset cause: ");
326  if (_POR) {
327  u8_resetIdentified = 1;
328  outString("Power-on.\n");
329  _POR = 0;
330  _BOR = 0; //clear both
331  // Set the values below, which reset all the
332  // error reporting variables to indicate that
333  // no error has (yet) occurred.
334  sz_lastError = NULL;
335  sz_lastTimeoutError = NULL;
336  u16_INTTREGlast = 0;
337  } else {
338  //non-POR
339  if (_SWR) {
340  outString("Software Reset.\n");
341  u8_resetIdentified = 1;
342  _SWR = 0;
343  _EXTR = 0; //also sets the EXTR bit
344  }
345  if (_WDTO) {
346  u8_resetIdentified = 1;
347  outString("Watchdog Timeout: ");
348  if (sz_lastTimeoutError != NULL) {
350  }
351  outString("\n");
352  _WDTO = 0;
353  _EXTR = 0; //also sets the EXTR bit
354  }
355  if (_EXTR) {
356  u8_resetIdentified = 1;
357  outString("MCLR assertion.\n");
358  _EXTR = 0;
359  }
360  if (_BOR) {
361  u8_resetIdentified = 1;
362  outString("Brown-out.\n");
363  _BOR = 0;
364  }
365  if (_TRAPR) {
366  u8_resetIdentified = 1;
367  outString("Trap Conflict.\n");
368  _TRAPR = 0;
369  }
370  if (_IOPUWR) {
371  u8_resetIdentified = 1;
372  outString("Illegal Condition.\n");
373  _IOPUWR = 0;
374  }
375 #ifdef _CM
376  if (_CM) {
377  u8_resetIdentified = 1;
378  outString("Configuration Mismatch.\n");
379  _CM = 0;
380  }
381 #endif
382  } //end non-POR
383 
384  if (!u8_resetIdentified) {
385  outString("Unknown reset.\n");
386  }
387  if (sz_lastError != NULL) {
388  outString("Error trapped: ");
390  if (u16_INTTREGlast != 0) {
391 #if ( defined(__PIC24H__) || defined(__dsPIC33F__) || defined(__PIC24E__) || defined(__dsPIC33E__) )
392  outString("Priority: ");
394  outString(", Vector number: ");
395  outUint8(INTTREGBITS_last.VECNUM);
396 #else
397  outString("Unknown priority/vector");
398 #endif
399  }
400  outString("\n\n");
401  sz_lastError = NULL;
402  u16_INTTREGlast = 0;
403  }
404 
406  checkOscOption();
407 }
408 
409 /** This function puts the PIC24 in low-power mode by:
410  * - Configuring all digital I/O pins as inputs
411  * - Configuring all analog I/O pins shared with
412  * digital I/O pins to be digital only
413  * - Enables pullups on all pins not used by the
414  * oscillator.
415  *
416  * WARNING: if pullups are enabled on pins used by
417  * the oscillator, the clock typically stops running.
418  * Currently, this code works for demo purposes with
419  * the FRC oscillator when used in the reset.c program.
420  * It *should* also work with primary and secondary
421  * oscillators, using conditional compiles in the code.
422 */
423 //this function is processor specific
424 #if (defined(__PIC24HJ32GP202__) || \
425  defined(__PIC24FJ64GA002__) || \
426  defined(__PIC24FJ64GA102__))
427 void configPinsForLowPower(void) {
428  // Configure all digital I/O pins for input
429  TRISB = 0xFFFF;
430  TRISA = 0xFFFF;
431  // Configure all analog pins as digital I/O
432 #if defined(__PIC24F__)
433  AD1PCFG = 0xFFFF;
434 #elif defined(__PIC24H__)
435  AD1PCFGL = 0xFFFF;
436 #else
437 #error Unknown processor
438 #endif
439  // Enable all pullups, except those which clocks are
440  // connected to.
441 
442  // CN31-CN16 enable, based on if primary osc is used
443  // or not.
444  /// \todo Base this on COSC instead, in case the oscillator
445  /// was switched.
446 #if (FNOSC_SEL == FNOSC_PRI) || (FNOSC_SEL == FNOSC_PRIPLL)
447 #if (POSCMD_SEL == POSCMD_EC)
448  // The external oscillator driving the clock is on OSCI,
449  // which is CN30. Don't enable the pullup on this pin to
450  // avoid interfering with it.
451  // Note that CNPU2 covers CN31 to CN16.
452  /// \todo Not tested with the EC.
453  CNPU2 = 0xBFFF;
454 #else
455  // The crystal driving the primary oscillator is on OSCI/OSCO,
456  // which is CN30 and CN29. Don't enable pullups on these pins,
457  // since that will prevent the crystal from oscillating.
458  // Note that CNPU2 covers CN31 to CN16.
459  CNPU2 = 0x9FFF;
460 #endif // #if (POSCMD_SEL == POSCMD_EC)
461 #else // The primary oscillator is not selected
462  // Turn on all the pull-ups
463  CNPU2 = 0xFFFF;
464 #endif
465 
466  // CN15-CN0 enable, based on if secondary osc is used
467  // or not.
468  if ((FNOSC_SEL == FNOSC_SOSC) ||
469 #if defined(__PIC24H__) || defined(__dsPIC33F__)
470  _LPOSCEN) {
471 #elif defined(__PIC24F__) || defined(__PIC24FK__)
472  _SOSCEN) {
473 #else
474 #error Unsupported processor.
475 #endif
476  // The crystal driving the secondary oscillator is on
477  // SOSCI/SOSCO, which is CN1 and CN0.
478  /// \todo Not tested with SOSC.
479  CNPU1 = 0xFFFC;
480  }
481  else {
482  CNPU1 = 0xFFFF;
483  }
484 }
485 #else
486 #warning Using dummy function for configPinsForLowPower() in 'common/pic24_util.c'
488 }
489 #endif
490 
491 
492 /** Perform basic chip configuration:
493  * - Configure the heartbeat
494  * - Configure the clock
495  * - Configure UART1
496  * - Determine and print the cause of reset
497  * - Print a hello message.
498  *
499  * \param sz_helloMsg Hello message to print.
500  */
501 void configBasic(const char* sz_helloMsg) {
502  configHeartbeat();
503  configClock();
505  printResetCause();
506  outString(sz_helloMsg);
507 }
508 
509 
510 /** Round a floating-point number to the nearest integer.
511  * \param f_x Floating-point value to round
512  * \return The nearest uint32_t to f_x.
513  */
515  uint32_t u32_y;
516 
517  u32_y = f_x;
518  if ((f_x - u32_y) < 0.5) return u32_y;
519  else return u32_y+1;
520 }
521 
522 /** Round a floating-point number to the nearest integer.
523  * \param f_x Floating-point value to round
524  * \return The nearest uint16_t to f_x.
525  */
527  uint16_t u16_y;
528 
529  u16_y = f_x;
530  if ((f_x - u16_y) < 0.5) return u16_y;
531  else return u16_y+1;
532 }