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 "pic24_unittest.h"
45 #include <stdio.h> // To define NULL
46 
47 
48 #if !USE_HEARTBEAT && !defined(__DOXYGEN__)
49 // No heartbeat; instead, just define empty functions.
50 void configHeartbeat(void) {
51 }
52 
53 void doHeartbeat(void) {
54 }
55 
56 void toggleHeartbeat(void) {
57 }
58 #else
59 
60 /** \name Heartbeat
61  * @{
62  * These routines provide heartbeat support by blinking a LED
63  * on a regular basis. See doHeartbeat() for more information.
64  *
65  */
66 /** The current heartbeat count. When this value reaches
67  * \ref u32_heartbeatMax, the heatbeat LED is toggled by
68  * doHeartbeat().
69  * \see doHeartbeat
70  */
72 /** When u32_heartbeatCount reaches this maximum, the
73  * heatbeat LED is toggled by doHeartbeat().
74  */
75 static uint32_t u32_heartbeatMax;
76 
77 
78 /** Configures a GPIO pin for use with the heartbeat and sets
79  * up the heartbeat counter.
80  * \see doHeartbeat
81  */
82 void configHeartbeat(void) {
83  CONFIG_HB_LED();
84  /* long enough to see LED toggle. Incrementing the heartbeat
85  takes several cycles - CYCLES_PER_MS used as the multiplier so that
86  we are tied to FCY
87  */
90  HB_LED = 0; // Turn LED off to show we started running
91  doHeartbeat(); // do Heartbeat at least once
92 }
93 
94 /** This heartbeat function should be called
95  * repeatedly in any sort of blocking wait loop. It will
96  * periodically toggle an LED after \ref u32_heartbeatMax increments.
97  */
98 void doHeartbeat(void) {
101  toggleHeartbeat();
102  u32_heartbeatCount = 0;
103  }
104 }
105 
106 
107 /** A function which toggles the hearbeat LED.
108  \see doHeartbeat
109  */
110 void toggleHeartbeat(void) {
111  HB_LED = !HB_LED;
112 }
113 /// @}
114 #endif
115 
116 /** Persistent storage for an error message, typically
117  * set by \ref reportError and reported at reset by
118  * \ref printResetCause.
119  */
120 static _PERSISTENT const char* sz_lastError;
121 
122 /** Persistent storage for a timeout error, to be reported
123  * if a watchdog reset occurs.
124  */
125 _PERSISTENT const char* sz_lastTimeoutError;
126 
127 /** Store a copy of the INTTREG register as a bitfield.
128  * This is not defined for all PICs, so work around
129  * with an \#ifdef of ILR, one of the bitfields in this register.
130  * This is _PERSISTENT so that it
131  * survives the resets which occurs immeidately after
132  * the default interrupt handler \ref _DefaultInterrupt
133  * copies INTTREG to this variable.
134  */
135 #ifdef _ILR
136 static _PERSISTENT INTTREGBITS INTTREGBITS_last;
137 
138 /** Make the \ref INTTREGBITS_last also accessible as
139  * a word. This is like <code>uint16_t u16_INTTREGlast</code>
140  * except that INTTREGBITS_last and u16_INTTREGlast
141  * refer to the same data.
142  */
143 # define u16_INTTREGlast BITS2WORD(INTTREGBITS_last)
144 #else
145 static uint16_t u16_INTTREGlast;
146 #endif
147 
148 /** Provide a default interrupt handler which records what
149  * interrupt was not handled then resets the chip. Typically,
150  * a call to \ref printResetCause during chip startup will then
151  * print the error.
152  */
153 void _ISR _DefaultInterrupt(void) {
154 #ifdef _ILR
155  // Record the interrupt vector and priority of the
156  // unhandled interrupt.
157  u16_INTTREGlast = INTTREG;
158 #else
159  // Any non-zero value causes reportError to report
160  // this. This register doesn't exist on the PIC24F.
161  u16_INTTREGlast = 1;
162 #endif
163  reportError("Unhandled interrupt, ");
164 }
165 
166 
167 /** Report a critical error by recording a message
168  * in \ref sz_lastError then resetting the chip,
169  * assuming hat \ref printResetCause will be called
170  * during chip initialization.
171  * \param sz_errorMessage Error message to report.
172  * \see REPORT_ERROR
173  */
174 void reportError(const char* sz_errorMessage) {
175  //ignore if a previous error has already been triggerred
176  if (sz_lastError == NULL) {
177  sz_lastError = sz_errorMessage;
178  asm("reset");
179  }
180 }
181 
182 /** Reads a 24-bit program memory word at the given address.
183  * \param u32_address Address of program memory to read.
184  * \return The 24-bit program memory word at u32_address.
185  * The upper 8 bits are 0.
186  */
187 uint32_t readProgramMemory(uint32_t u32_address) {
188  uint16_t u16_offset = u32_address;
189  TBLPAG = u32_address >> 16;
190  return ( ((uint32_t) __builtin_tblrdh(u16_offset)) << 16) |
191  __builtin_tblrdl(u16_offset);
192 }
193 
194 /** Determines the device and revision of the PIC this program
195  * is executing on. This information is then output via the default
196  * UART. A warning message is issued if this program was not compiled
197  * for the chip it is running on.
198  */
200 #ifdef SIM
201  outString("**** SIMULATION MODE: cannot read device and revision ID ****\n");
202 #else
203  uint32_t devID = readProgramMemory(DEV_ID_LOCATION);
204  uint32_t revision = readProgramMemory(REVISION_LOCATION);
205  uint8_t correctChip = 1;
206  const char* devIDStr = "unknown";
207  const char* revisionStr = "unknown";
208 
209  if (devID == DEV_ID)
210  devIDStr = DEV_ID_STR;
211  else
212  correctChip = 0;
213 
214  switch (revision) {
215  case EXPECTED_REVISION1 :
216  revisionStr = EXPECTED_REVISION1_STR;
217  break;
218 # ifdef EXPECTED_REVISION2
219  case EXPECTED_REVISION2 :
220  revisionStr = EXPECTED_REVISION2_STR;
221  break;
222 # endif
223 # ifdef EXPECTED_REVISION3
224  case EXPECTED_REVISION3 :
225  revisionStr = EXPECTED_REVISION3_STR;
226  break;
227 # endif
228 # ifdef EXPECTED_REVISION4
229  case EXPECTED_REVISION4 :
230  revisionStr = EXPECTED_REVISION4_STR;
231  break;
232 # endif
233 # ifdef EXPECTED_REVISION5
234  case EXPECTED_REVISION5 :
235  revisionStr = EXPECTED_REVISION5_STR;
236  break;
237 # endif
238  }
239 
240  outString("Device ID = ");
241  outUint32(devID);
242  outString(" (");
243  outString(devIDStr);
244  outString("), revision ");
245  outUint32(revision);
246  outString(" (");
247  outString(revisionStr);
248  outString(")\n");
249 
250  if (!correctChip)
251  outString("\n\n"
252  "******************************************************\n"
253  "* WARNING - this program was compiled for the wrong *\n"
254  "* chip. This program may produce unexpected behvior! *\n"
255  "* Edit the header files to properly define this chip *\n"
256  "* and to insure correct operation. *\n"
257  "* *\n"
258  "* NOTE: If this was compiled for the correct chip, *\n"
259  "* and only occurs at power-on (not during a MCLR *\n"
260  "* reset), verify that AVDD and AVSS are connected. *\n"
261  "* On the PIC24H32GP202, not connecting AVDD *\n"
262  "* produces this message only at power-up. *\n"
263  "******************************************************\n");
264 #endif
265 }
266 
267 /** Reports the oscillator currently in use to the default
268  * serial port. See \ref FNOSC_SEL for a table of which chips support which
269  * clocks.
270  */
271 void checkOscOption(void) {
272  switch (_COSC) {
273  case 0:
274  outString("Fast RC Osc\n");
275  break;
276  case 1:
277  outString("Fast RC Osc with PLL\n");
278  break;
279  case 2:
280  outString("Primary Osc (XT, HS, EC)\n");
281  break;
282  case 3:
283  outString("Primary Osc (XT, HS, EC) with PLL\n");
284  break;
285  case 4:
286  outString("Secondary Osc\n");
287  break;
288  case 5:
289  outString("Low Power RC Osc\n");
290  break;
291 #if defined(__PIC24H__) || defined(__dsPIC33F__) || defined(__PIC24E__) || defined(__dsPIC33E__)
292  case 6:
293  outString("Fast RC Osc/16\n");
294  break;
295  case 7:
296  outString("Fast RC Osc/N\n");
297  break;
298 #elif defined(__PIC24F__) || defined(__PIC24FK__)
299 # ifdef __PIC24FK__
300  case 6:
301  outString("Low power fast RC Osc with Postscaler\n");
302  break;
303 # endif
304  case 7 :
305  outString("Fast RC Osc with Postscaler\n");
306  break;
307 #else
308 # error "Unknown processor."
309 #endif
310  default :
311  reportError("Unknown oscillator type.");
312  break;
313  }
314 }
315 
316 /** Determines and prints the cause of a CPU reset. This should
317  * be called when the chip first starts up. For an example, see
318  * the \ref configBasic function.
319  */
320 void printResetCause(void) {
321  uint8_t u8_resetIdentified;
322 
323  u8_resetIdentified = 0;
324  if (_SLEEP) {
325  outString("\nDevice has been in sleep mode\n");
326  _SLEEP = 0;
327  }
328  if (_IDLE) {
329  outString("\nDevice has been in idle mode\n");
330  _IDLE = 0;
331  }
332  outString("\nReset cause: ");
333  if (_POR) {
334  u8_resetIdentified = 1;
335  outString("Power-on.\n");
336  _POR = 0;
337  _BOR = 0; //clear both
338  // Set the values below, which reset all the
339  // error reporting variables to indicate that
340  // no error has (yet) occurred.
341  sz_lastError = NULL;
342  sz_lastTimeoutError = NULL;
343  u16_INTTREGlast = 0;
344  } else {
345  //non-POR
346  if (_SWR) {
347  outString("Software Reset.\n");
348  u8_resetIdentified = 1;
349  _SWR = 0;
350  _EXTR = 0; //also sets the EXTR bit
351  }
352  if (_WDTO) {
353  u8_resetIdentified = 1;
354  outString("Watchdog Timeout: ");
355  if (sz_lastTimeoutError != NULL) {
357  }
358  outString("\n");
359  _WDTO = 0;
360  _EXTR = 0; //also sets the EXTR bit
361  }
362  if (_EXTR) {
363  u8_resetIdentified = 1;
364  outString("MCLR assertion.\n");
365  _EXTR = 0;
366  }
367  if (_BOR) {
368  u8_resetIdentified = 1;
369  outString("Brown-out.\n");
370  _BOR = 0;
371  }
372  if (_TRAPR) {
373  u8_resetIdentified = 1;
374  outString("Trap Conflict.\n");
375  _TRAPR = 0;
376  }
377  if (_IOPUWR) {
378  u8_resetIdentified = 1;
379  outString("Illegal Condition.\n");
380  _IOPUWR = 0;
381  }
382 #ifdef _CM
383  if (_CM) {
384  u8_resetIdentified = 1;
385  outString("Configuration Mismatch.\n");
386  _CM = 0;
387  }
388 #endif
389  } //end non-POR
390 
391  if (!u8_resetIdentified) {
392  outString("Unknown reset.\n");
393  }
394  if (sz_lastError != NULL) {
395  outString("Error trapped: ");
397  if (u16_INTTREGlast != 0) {
398 #ifdef _ILR
399  outString("Priority: ");
400  outUint8(INTTREGBITS_last.ILR);
401  outString(", Vector number: ");
402  outUint8(INTTREGBITS_last.VECNUM);
403 #else
404  outString("Unknown priority/vector");
405 #endif
406  }
407  outString("\n\n");
408  sz_lastError = NULL;
409  u16_INTTREGlast = 0;
410  }
411 
413  checkOscOption();
414 }
415 
416 /** Perform basic chip configuration:
417  * - Configure the heartbeat
418  * - Configure the clock
419  * - Configure UART1
420  * - Determine and print the cause of reset
421  * - Print a hello message.
422  *
423  * \param sz_helloMsg Hello message to print.
424  */
425 void configBasic(const char* sz_helloMsg) {
426  configHeartbeat();
427  configClock();
429  printResetCause();
430  outString(sz_helloMsg);
431 }
432 
433 
434 #ifndef _NOFLOAT
435 /** Round a floating-point number to the nearest integer.
436  * \param f_x Floating-point value to round
437  * \return The nearest uint32_t to f_x.
438  */
439 uint32_t roundFloatToUint32(float f_x) {
440  uint32_t u32_y;
441 
442  u32_y = f_x;
443  if ((f_x - u32_y) < 0.5) return u32_y;
444  else return u32_y+1;
445 }
446 
447 /** Round a floating-point number to the nearest integer.
448  * \param f_x Floating-point value to round
449  * \return The nearest uint16_t to f_x.
450  */
451 uint16_t roundFloatToUint16(float f_x) {
452  uint16_t u16_y;
453 
454  u16_y = f_x;
455  if ((f_x - u16_y) < 0.5) return u16_y;
456  else return u16_y+1;
457 }
458 
459 /** Choose UART baud rate, based on u32_fct.
460  * NOTE: Be careful about using BRGH=1 - this uses only four clock
461  * periods to sample each bit and can be very intolerant of
462  * baud rate % error - you may see framing errors. BRGH is selected
463  * via the DEFAULT_BRGH1 define above.
464  */
465 uint16_t compute_brg(uint32_t u32_fcy, uint16_t u16_brgh, uint32_t u32_baudrate) {
466  float f_brg;
467 
468  // Make sure u16_brgh is valid (1 or 0)
469  ASSERT(u16_brgh <= 1);
470  if (u16_brgh == 0) {
471  f_brg = (((float) u32_fcy)/((float) u32_baudrate)/16.0) - 1.0;
472  } else {
473  f_brg = (((float) u32_fcy)/((float) u32_baudrate)/4.0) - 1.0;
474  }
475  ASSERT(f_brg < 65535.5);
476  return roundFloatToUint16(f_brg);
477 }
478 
479 #else // #ifndef _NOFLOAT, so _NOFLOAT is defined.
480 
481 uint16_t compute_brg(uint32_t u32_fcy, uint16_t u16_brgh, uint32_t u32_baudrate) {
482  uint32_t u32_brg;
483 
484  // Make sure u16_brgh is valid (1 or 0)
485  ASSERT(u16_brgh <= 1);
486  u32_brg = u32_fcy/u32_baudrate;
487  if (u16_brgh == 0) {
488  if ((u32_brg & 0x0FL) >= 8) {
489  u32_brg = u32_brg/16;
490  } else {
491  u32_brg = u32_brg/16 - 1;
492  }
493  } else {
494  if ((u32_brg & 0x03L) >= 2) {
495  u32_brg = u32_brg/4;
496  } else {
497  u32_brg = u32_brg/4 - 1;
498  }
499  }
500  ASSERT(u32_brg < 65536);
501  return u32_brg;
502 }
503 
504 #endif