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