Q. How do I speed up my interrupts?
A. Here is a few tips:
//incorrect code (if SSPIE or ADIE is being disabled/enabled in main)
interrupt void isr (void)
{
if (SSPIF) {
SSP_INT_SERV();
}
if (ADIF) {
ADC_INT_SERV();
}
//...add other ints here
}
Symptoms: The program hangs after some minutes.
Use: if (ADIE && ADIF)... and etc. intead of if (ADIF). Consider what will heppen if you will disable AD interrupt for some reason (ADIE=0), and the SPI interrupt will occur. both handlers will be executed, not only the SPI handler as expected , because ADIF is set independetly of ADIE value.
//correct code (if SSPIE or ADIE is being disabled/enabled in main)
interrupt void isr (void)
{
if (SSPIF && SSPIE) {
SSP_INT_SERV();
}
if (ADIF && ADIE) {
ADC_INT_SERV();
}
//...add other ints here
}
Q. How do I time exact intervals?
My favorite way to handle this 'non-commensurate' intervals problem is to steal a concept from the Bresenham line drawing algorithm.
Using the current case:
On each timer interrupt you subtract 16384 from the counter.
If the counter goes negative you update the time by one second and then add 1,000,000 back in to the counter.
This technique will work for any interval. It can be made perfect for intervals that have a rational relationship to the instruction cycle time, and can be abitrary close to perfect even for irrational ratios, for example a SQRT(2) Mhz crystal.
Simulator/Emulator Breakpoint Problems
Q. I cant seem to get a breakpoint in the right spot. It either wont put one on the line that I want it, or I cant stop on the right line.
A. Theres a trick to setting breakpoints. See the closing bracket ‘}’ in the code? It hasn’t got 4 dots to the left of it. This means that you cannot right click on that line, and put a breakpoint there.
As to making it stop on the right line, sometimes you have to add extra lines of code so it can stop exactly where you want it. Use the #define to set ‘b’ (for breakpoint) to inline assembly language, asm(“nop”). ‘Nop’ stands for ‘No Operation’ – it doesn’t do anything.
Emulator Problems
Q. My circuit works with the emulator, but not if I plug in a programmed chip.
A. Check the following:
1. The program may be wrong. Usually, the simulator and emulator environment has all variables are set to 0 initially. If a program doesn’t initialise its variables, it will work in the simulator and emulator, but not on a standalone chip.
Rule of thumb: Initialise every variable to a known value before using it
2. Is the chip programmed at all?
3. Is the chip programmed properly? XT for <8Mhz, HS for >8Mhz, etc.
4. Does the chip have power? Check 5V on the correct pins.
5. Is the reset pin pulled high with a 10k resistor? Check that pin 1 on micro is at 5V.
6. Is the crystal oscillating? Get an oscilloscope and touch it to the oscillator pins on the micro. They should both be buzzing. If
ts not oscillating, it means either the chip is not programmed at all, or not programmed properly, the crystal is bad, the capacitors are the wrong value, or your circuit is broken or wrong somehow. Check manual. 15pF to 33pF is about right, depending on frequency. You can check the value of the capacitors down in the electronics lab with their component tester.
7. I all else fails, program up a chip with the following program, then check to see if the ports B is oscillating:
#include
main()
{
TRISB=0x0; //output
while(1)
{
PORTB=0;PORTB=0xFF;
}
}
Q. My program works with the ICEPIC 2000 but not the ICD emulator.
A. Add the switch ‘-ICD’ under the linker options, menu ‘project’ then ‘edit project’ then ‘node properties’. This reserves the last 256 bytes of memory, and 13 miscellaneous ram bytes that the ICD uses. Check the compiler is v7.85 or above. Then, match the ICD options to the box below.
These settings are the default for almost every situation, with the exception of the oscillator.
1. When using an external crystal with speeds of higher than 8Mhz use the settings as shown above.
2. Crystal speeds of lower than 8MHz use ‘XT’ for the oscillator setting instead ‘HS’.
3. When using a RC oscillator for the clock, made up of a resistor and a capacitor, use ‘RC’ for the oscillator setting instead of ‘HS’.
Watchdog Timer
Q. What is the watchdog timer and why do I want it?
A. The watchdog is a good way to ensure that the microprocessor does not freeze forever from a crash, due to a bad power supply or a rogue program. When the chip is programmed, the watchdog timer bit is enabled. It cannot be turned off in software, in case a rogue program overwrites it. If the timer is not reset regularly with a CLRWDT() instruction, it will reset the micro. This will happen within 18ms to 2 seconds, depending on the prescaler selected.
Q. After I programmed my PIC, it kept restarting itself every couple of seconds.
A. Have you got the watchdog timer bit set? The watchdog is a good way to ensure that the microprocessor does not freeze forever from a crash. When the chip is programmed, the watchdog timer bit is enabled. It cannot be turned off in software, in case a rogue program overwrites it. If the timer is not reset regularly with a CLRWDT() instruction, it will reset the micro. This will happen within 18ms to 2 seconds, depending on the prescaler selected, and on the temperature.
It is interesting to note that it is possible to make a PIC into an accurate temperature sensor using the time of the watchdog timeout to sense the temperature.
The advantage of using a watchdog timer is that the micro is protected from crashing forever due to a bad power supply or a rogue program.
Useful techniques for using C with PIC
Q. Have you got any delay routines?
A. Its important to have accurate delay routines. Delay routines written in C aren’t accurate – depending on optimisations, they can vary. That’s why some of the routines have inline assembly. Delay routines are available for download.
Q. How do I measure the time code takes?
A. If you’re using the simulator or the ICEPIC 2000, bring up the stopwatch under menu ‘windows’. Put breakpoints before and after the section of code, and measure the time.
The MPLAB-ICD doesn’t have a stopwatch. Add a piece of code to read out the value of timer 1 at the start of the code, then check it again at the finish. The timer increments at the same frequency as instructions executed. Do some maths and you have the time in clock cycles that the code takes.
Q. How do I make a variable oscillate, ie: go 1-0-1-0
A. Do the following code:
unsigned char x;
x=1-x; //every time this statement is executed, x goes 1-0-1-0-1-0 etc
Q. How do I reset the micro?
A. One way is to reset all variables to their defaults, as listed in the PIC manual. Then, use assembly language to jump to location 0x0000 in the micro.
#asm
ljmp 0x0000
#endasm
This is quite safe to use, even when called within interrupts or procedures. The PIC 16x series micros have 8 stack levels. Each time a procedure is called, one stack level is used up for the return address. It is a circular buffer, so even if the micro is 7 procedure levels deep and in an interrupt when a reset is called, this is the new start of the stack buffer, and the micro will continue as per normal.
Another way is to set watchdog the timer when the chip is programmed, and use CLRWDT() instructions all through the code. When you want the micro to reset, stop clearing the watchdog bit and the micro will reset after around 18ms to 2 seconds depending on the prescaler.
Q. Theres a variable in the PIC manual that the compiler doesn’t recognise, even though I used the same name. How do I tell what the compilers called it?
A. Go to directory c:\ht-pic\include\ and look at the header files. These give the names that the compiler gives to the internal variables. This is extremely useful for finding out exactly what variables are available in C. Heres some example code from the header file, pic1687x.h for the PIC16F87x micro:
/*
* Header file for the Microchip
* PIC 16F873 chip
* PIC 16F874 chip
* PIC 16F876 chip
* PIC 16F877 chip
* Midrange Microcontroller
*/
//[added for FAQ] we can see that the compiler has called PORTA by the same name as the manual
static volatile unsigned char PORTA @ 0x05;
//[added for FAQ] ... the listing continues on ...
/* PORTA bits */
static volatile bit RA5 @ (unsigned)&PORTA*8+5;
static volatile bit RA4 @ (unsigned)&PORTA*8+4;
//[added for FAQ] we now know that each bit in PORTA is called, for example, RA5, as per manual
//[added for FAQ] ... the listing continues on ...
Q. Whats the rule of thumb for timeouts?
A. Rule of thumb: every time a program waits for an operation that could fail, have a timeout to exit it after too much time. For example, reading in a byte with SPI. There is the clock and data line, and a byte with 8 bits is read into the micro. If the sender of the byte stops halfway through, there is four garbage bits remaining. Later on, after 5 minutes, the sender sends a new byte. Everything is now out of sync, with the first half going into one byte, and the last half going into the next.
Use the following code to implement timeouts.
//(c)Shane Tolmie, http://www.microchipc.com/, distribute freely for non commercial use on the condition that you include this web link somewhere in your document.
// example of how to use timeouts:
unsigned int timeout_int;
//for max 491512us timeout @ 8Mhz
//for max 245756us timeout @ 16Mhz
timeout_int=timeout_int_us(500);
while(timeout_int-- >= 1)
{
if (x==y) break; //typical overhead for checking ring buffer
}
if (timeout_int<=2) { //theres been too much time elapsed }
//for max timeout of 1147us @ 8Mhz
//for max timeout of 573us @ 16Mhz
timeout_char=timeout_char_us(500);
while(timeout_char-- >= 1)
{
if (x==y) break; //typical overhead for checking ring buffer
}
if (timeout_int<=2) { //theres been too much time elapsed }
/*
Time taken: optimisations on: 9cyc/number loop, 4.5us @ 8Mhz
with extra check ie: && (RB7==1), +3cyc/number loop, +1.5us @ 8Mhz
Formula: rough timeout value = (/) * (PIC_CLK/4.0)
*/
#define LOOP_CYCLES_CHAR 9 //how many cycles per loop, optimisations on
#define timeout_char_us(x) (unsigned char)((x/LOOP_CYCLES_CHAR)*(PIC_CLK/4.0))
#define LOOP_CYCLES_INT 16 //how many cycles per loop, optimisations on
#define timeout_int_us(x) (unsigned int)((x/LOOP_CYCLES_INT)*(PIC_CLK/4.0))
Q. I need more space in the micro – 8k is not enough for a 16F876.
A. You’ve done well. One thing to check is that you havnt got any orphaned functions which are never called. Hi-Tech C includes the code, even though it is redundant. To automatically check for this, set the warning level to –9 (negative nine) under menu ‘Project’ then ‘Edit Project’ then ‘node properties’. This introduces all sorts of warnings. Most of them can be ignored, except the one:
::function _myfunction is never called (warning)
This means that the code for the function ‘myfunction’ is included somewhere in your code, but it is never called. If you’re not going to use it, comment it out.
You must have your project set up to 'compile at once' rather than 'linked' for the compiler to give this information. See tutorial.
Q. Can I use printf() when communicating with a PC computer via the RS232 serial port?
A. You can use printf() to write to the USART on a PIC micro if you define your own putch() routine and #include the correct header file. It uses an extra 650 words of program memory. See the sample code here.
Q. Why cant I print long (32 bit) variables with printf()?
A. Add the switch ‘-lf’ to the ‘additional command line options’, under menu ‘Project’ then ‘Edit Project’ then ‘Node Properties’. This uses up a additional 1600 words of rom space, but it allows printf to print longs.
Q. How do I store large amounts of data?
A. Use EEPROM or the Dataflash available from Microchip or Atmel. Capacities range from 1Kbyte to 4Mbyte non-volatile chips.
Hardware for PIC
Q. Can I control 2 LEDs from one port?
A. Yes. The only catch is that one has to be on at any time. Connect the 2 LEDs in series, between the 5V and GND rails. Attach the port to the middle between the LEDs. Connect a current limiting resistor in series between the VCC rail and the top LED, and between the GND rail and the bottom LED. Different color LEDs require different voltages acorss their terminals, read the datasheet.
Q. I want to protect my circuit from overcurrents and shorts.
A. Get a polyswitch, available from, among others, the manufacturer RayChem. See http://www.raychem.com/, search for “miniSMDC075”. These are tiny autoresetting fuses that limit the current to no more than a certain value.
Q. How do I save power in my embedded micro system?
A. There are many methods to reduce the power consumption of a circuit with a micro in it.
1. Use the lowest clock frequency possible. For PIC micros, the power consumption seems to rise as the square of the frequency.
2. Put the micro into sleep mode. It can wake itself up when an internal timer rolls over. Be careful to complete all operations, in software and hardware, before putting the micro to sleep. For example, if a serial character is only half received in hardware, wait until it is fully received before shutting down.
3. Switch off external peripherals when they are not in use. For example, have one pin on the micro connected to the standby pin of the serial chip. You could even power external chips off the micro, as the micro can source 20mA, and simply switch off the port when it is not in use.
4. Use the lowest voltage possible. In saying this, be very careful to look up the voltage range for each chip when using it in your circuit. An example is a 5V embedded datalogger, which had a 4Mbit flash chip in it. Instead of using the AT45D041, the 5V part, the AT45DB041, the 3.5V part, was used. It seemed to work perfectly. The only problem was that every 20th page of data read from the chip was completely corrupted.
Q. My PIC sometimes resets by itself.
A. Bad power? Brownouts? Put a decoupling cap as close to the power supply as possible, so when it switches it doesn’t brown itself out. 0.1uF for <8Mhz, 0.01uF for >8Mhz.
Bootloader for PIC16F876
Q. How do I do a bootloader for the flash based 16F876?
A. Although code is available from Hi-Tech, this is a modified and enhanced version that doesnt need RB0 - it uses timeouts, so the code is loaded within 1 second of powerup. It also handles config bits properly, and programs the EEPROM also. See sample code archive here.
Simple Multitasking Kernel for PIC under Hi-Tech C
Q. How do I handle many things at once on a PIC? I want a simple multitasking system.
A. See here for a big explanation of this.
Tips for using the PICSTART Plus Programmer
Q. The programmer doesn’t seem to work – it doesn’t program
A. Sometimes this happens, maybe MPLab has got something wrong with it or the programmer itself has crashed. It can crash – its running code just like the computer. Pull the plug out of the programmer, shutdown the computer and restart it, then retry it. This usually fixes the problem.
Q. How do I embed configuration words for the programmer into my C program
Q. MPLab always loses my settings for the PICStart plus programmer when I exit
A. Use the __CONFIG(); macro in your C code, after #include to set these bits. First, look up the appropriate include file for your micro. Then compare this to the include bits in the PIC .pdf manual. For the PIC16C76, includes file pic1677.h, found in c:\ht-pic\include\. At the bottom of the file,these lines appear:
//look up datasheet for explanation of terms
#define CONFIG_ADDR 0x2007
#define FOSC0 0x01
#define FOSC1 0x02
#define WDTE 0x04 //Watch Dog Timer (Enable low) - (include to disable)
#define PWRTE 0x08 //PoWeR Up Timer (Enable low) - (include to disable)
#define BODEN 0x40 //Brown Out Detect ENable - active HIGH (include to enable)
#define RC (FOSC1 | FOSC0)
#define HS FOSC1
#define XT FOSC0
#define LP 0x00
#define CP0 0x1510
#define CP1 0x2A20
/* code protection */
#define UNPROTECT (CP0 | CP1) // unprotected
#define PROTECT50 CP1 // upper half program memory code protected
#define PROTECT75 CP0 // upper 3/4 program memory code protected
#define PROTECT 0x00 // all memory code is protected
Thus, to make PICStart Plus default to HS, Watchdog On, Power up time enabled, Brown Out Timer enabled, and Unprotected, use the following lines:
#include
__CONFIG(HS | BODEN | UNPROTECT);
Be careful of PWRTE. According to the PIC manual, this is active low, so missing it out turns the power up timer on. Including it turns it off. This is the same for WTDE.
Some compilers recommend linking the fuse names with logical "OR", the symbol "|". Other compilers recommend linking the fuse names with logical "AND", the symbol "&".
Tips on using Libraries with MPLab
Q. What is a library, consisting of a ".lib" file, and how do I use it?
A. Normally, most people have a library of commonly-used routines that are always included in every project they do.
For example, there is the following files:
· delay.c - delay routines in C such as DelayMs(x)
· delay.h - header file for delay.c
· serial.c - serial RS232 routines such as putch() and getch()
· serial.h - header file for serial.c
One method of using these routines could be to include these files in your project. Put the following lines in your main .c file:
#include "delay.h"
#include "delay.c"
#include "serial.h"
#include "serial.c"
main()
{
}
However, this method has one key disadvantage: even if a routine is not used, it will still take up memory in the final compiled .hex file.
Using a library solves this problem. Once you have made a library out of your commonly used routines, the linker picks out the necessary routines and includes them in the final .hex file.
Q. How do I make a library file?
View the latest answer to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
Lets say I have a number of modules, each one an object file, ending in ".obj". I can put these in a library by executing the following at the dos prompt:
libr r xx.lib i2c.obj ser_877.obj delay1.obj
I now have a library called xx.lib which can be included in my project. Under 'Project' and 'Edit Project' and the 'Library Path' box, enter the path to xx.lib. If it gives an error, see here. Alternatively, use 'Add Node' to add the ".lib" file to the project.
Q. I make a library file ending in ".lib" but I cant tell MPLab how to get to it
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Under Project..Edit Project, click in 'Library Path'. If you see the following error message, you have set up the project incorrectly.
You need install the 3 languages: under "Project..Install Language Tool" for compiler, Assembler and Linker, all select "c:\ht-pic\bin\picc.exe".
Select "Project..New project (or Edit Project)" then click on "node property"
***[.HEX] : select language tool: Linker
***[.C]: select language tool: Compiler
If the language tool for ***[.HEX] is set to Compiler, then this means the linker is never used, and no libraries or additional C files can be added.
Serial Numbers with Hi-Tech C
Q. How do I add serial numbers to a program coded in Hi-Tech C?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Use the following to preserve the Rom location for a serial number:
;Reserves 4 bytes for Serial Number programming
;To be placed at 0x0FFC to 0x0FFF with the PICC option -L-Ppreserve=ffch
;The 0x0FFF=4096 is for a 4k micro, a 2k or 8k micro would be different
psect preserve
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
psect preserve
The "-L-Ppreserve=ffch" must be entered in the option field in MPLAB for the HiTech Linker to place the code segment at the specific address.
The code is saved as an *.as file and included as a node in the MBLAB project and uses the PICC Assembler to compile. Remember to set the Assembler in the Language tool to Picc.exe.
Incremental Compiles with Hi-Tech C
Q. Under Hi-Tech C and MPLab, every time I recompile, it recompiles everything and then links it. How do I do incremental compiles?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Add the line 'c:\ht-pic\include' under 'include path' in the 'edit project' dialogue to enable incremental compiles.
You must already have the 'language tool' under the root node set to 'PIC C Linker', not 'PIC C compiler'. Add each source file to the list to be compiled and then linked.
PIC12F672 and OSCCAL
Q. How do I set the OSCCAL register in Hi-Tech C for the PIC12F672?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechC PIC12F672 OSCCAL HiTechCFAQ
A. If the built-in 4Mhz RC oscillator for the PIC12Cx67x is used, it can drift with temperature variations. The oscillator calibration speed is trimmed by setting register OSCCAL at run time. The top ROM byte in memory is read to obtain this value at runtime.
Looking in the header file, c:\htpic\include\pic1267x.h, we find the following lines, in different parts of the header file
static volatile unsigned char bank1 OSCCAL @ 0x8F;
#if defined(_12C672) || defined(_12CE674)
#define _READ_OSCCAL_DATA() (*(unsigned char(*)())0x7FF)()
#endif
Thus, use the following C line at the start of the program:
OSCCAL=_READ_OSCCAL_DATA();
If you are using a flash -F- part or a UV-erasable -JW- part, remember to record the value of the highest ROM address. It will be in the form retfw N where N=calibration value. For example, if N=0x90, the instruction will be 0x3490.
I used seven PIC12C672-JW parts in development. The OSCCAL values that I recorded for them were 0x90, 0x94, 0xB0, 0x84, 0xC8, 0xB0, 0xC0. This illustrates the fact that there is process variation among batches of PIC micros.
If you lose the value of OSCCAL for a particular JW part, you can retrieve it, with effort. Write a little routine to output 10kHz pulses. Alter the OSCCAL value until the pulses are indeed 10kHz as viewed on an oscilloscope.
A. Here is a few tips:
- Execute the least amount of code possible in the interrupt, do all background processing in the main(). Trigger the tasks in main() by setting a flag in the interrupt which is polled in main(). See the debt counter project for an example of this.
- Do not use any functions in your interrupt. This means the compiler has to save the state of absolutely everything, so it slows it down. This is a problem of most compilers. Make everything inline or the equivalent by copying it straight in from the function. If you absolutely must have a function, to be on the safe side, use in-line assembly to call it.
- Double check how many cycles it takes to get into the interrupt.
Here is an example for the PIC16F87x under Hi-Tech C. To check if your interrupt is saving too much state, in MPLab, in the ROM memory window, put a breakpoint at 0x0004, the entry point of the interupt service routine (ISR). Run the program until it interrupts. Then, single step until the program gets to the first C line in the ISR. This is how many cycles it takes to get into the interrupt.
//incorrect code (if SSPIE or ADIE is being disabled/enabled in main)
interrupt void isr (void)
{
if (SSPIF) {
SSP_INT_SERV();
}
if (ADIF) {
ADC_INT_SERV();
}
//...add other ints here
}
Symptoms: The program hangs after some minutes.
Use: if (ADIE && ADIF)... and etc. intead of if (ADIF). Consider what will heppen if you will disable AD interrupt for some reason (ADIE=0), and the SPI interrupt will occur. both handlers will be executed, not only the SPI handler as expected , because ADIF is set independetly of ADIE value.
//correct code (if SSPIE or ADIE is being disabled/enabled in main)
interrupt void isr (void)
{
if (SSPIF && SSPIE) {
SSP_INT_SERV();
}
if (ADIF && ADIE) {
ADC_INT_SERV();
}
//...add other ints here
}
Q. How do I time exact intervals?
My favorite way to handle this 'non-commensurate' intervals problem is to steal a concept from the Bresenham line drawing algorithm.
Using the current case:
- 4.00 MHz crystal
- 1Mhz instruction rate
- 256 cycles and prescaler of 64
- each overflow of the timer represents 256*64 == 16384 microseconds
On each timer interrupt you subtract 16384 from the counter.
If the counter goes negative you update the time by one second and then add 1,000,000 back in to the counter.
This technique will work for any interval. It can be made perfect for intervals that have a rational relationship to the instruction cycle time, and can be abitrary close to perfect even for irrational ratios, for example a SQRT(2) Mhz crystal.
Simulator/Emulator Breakpoint Problems
Q. I cant seem to get a breakpoint in the right spot. It either wont put one on the line that I want it, or I cant stop on the right line.
A. Theres a trick to setting breakpoints. See the closing bracket ‘}’ in the code? It hasn’t got 4 dots to the left of it. This means that you cannot right click on that line, and put a breakpoint there.
As to making it stop on the right line, sometimes you have to add extra lines of code so it can stop exactly where you want it. Use the #define to set ‘b’ (for breakpoint) to inline assembly language, asm(“nop”). ‘Nop’ stands for ‘No Operation’ – it doesn’t do anything.
Emulator Problems
Q. My circuit works with the emulator, but not if I plug in a programmed chip.
A. Check the following:
1. The program may be wrong. Usually, the simulator and emulator environment has all variables are set to 0 initially. If a program doesn’t initialise its variables, it will work in the simulator and emulator, but not on a standalone chip.
Rule of thumb: Initialise every variable to a known value before using it
2. Is the chip programmed at all?
3. Is the chip programmed properly? XT for <8Mhz, HS for >8Mhz, etc.
4. Does the chip have power? Check 5V on the correct pins.
5. Is the reset pin pulled high with a 10k resistor? Check that pin 1 on micro is at 5V.
6. Is the crystal oscillating? Get an oscilloscope and touch it to the oscillator pins on the micro. They should both be buzzing. If
ts not oscillating, it means either the chip is not programmed at all, or not programmed properly, the crystal is bad, the capacitors are the wrong value, or your circuit is broken or wrong somehow. Check manual. 15pF to 33pF is about right, depending on frequency. You can check the value of the capacitors down in the electronics lab with their component tester.
7. I all else fails, program up a chip with the following program, then check to see if the ports B is oscillating:
#include
main()
{
TRISB=0x0; //output
while(1)
{
PORTB=0;PORTB=0xFF;
}
}
Q. My program works with the ICEPIC 2000 but not the ICD emulator.
A. Add the switch ‘-ICD’ under the linker options, menu ‘project’ then ‘edit project’ then ‘node properties’. This reserves the last 256 bytes of memory, and 13 miscellaneous ram bytes that the ICD uses. Check the compiler is v7.85 or above. Then, match the ICD options to the box below.
These settings are the default for almost every situation, with the exception of the oscillator.
1. When using an external crystal with speeds of higher than 8Mhz use the settings as shown above.
2. Crystal speeds of lower than 8MHz use ‘XT’ for the oscillator setting instead ‘HS’.
3. When using a RC oscillator for the clock, made up of a resistor and a capacitor, use ‘RC’ for the oscillator setting instead of ‘HS’.
Watchdog Timer
Q. What is the watchdog timer and why do I want it?
A. The watchdog is a good way to ensure that the microprocessor does not freeze forever from a crash, due to a bad power supply or a rogue program. When the chip is programmed, the watchdog timer bit is enabled. It cannot be turned off in software, in case a rogue program overwrites it. If the timer is not reset regularly with a CLRWDT() instruction, it will reset the micro. This will happen within 18ms to 2 seconds, depending on the prescaler selected.
Q. After I programmed my PIC, it kept restarting itself every couple of seconds.
A. Have you got the watchdog timer bit set? The watchdog is a good way to ensure that the microprocessor does not freeze forever from a crash. When the chip is programmed, the watchdog timer bit is enabled. It cannot be turned off in software, in case a rogue program overwrites it. If the timer is not reset regularly with a CLRWDT() instruction, it will reset the micro. This will happen within 18ms to 2 seconds, depending on the prescaler selected, and on the temperature.
It is interesting to note that it is possible to make a PIC into an accurate temperature sensor using the time of the watchdog timeout to sense the temperature.
The advantage of using a watchdog timer is that the micro is protected from crashing forever due to a bad power supply or a rogue program.
Useful techniques for using C with PIC
Q. Have you got any delay routines?
A. Its important to have accurate delay routines. Delay routines written in C aren’t accurate – depending on optimisations, they can vary. That’s why some of the routines have inline assembly. Delay routines are available for download.
Q. How do I measure the time code takes?
A. If you’re using the simulator or the ICEPIC 2000, bring up the stopwatch under menu ‘windows’. Put breakpoints before and after the section of code, and measure the time.
The MPLAB-ICD doesn’t have a stopwatch. Add a piece of code to read out the value of timer 1 at the start of the code, then check it again at the finish. The timer increments at the same frequency as instructions executed. Do some maths and you have the time in clock cycles that the code takes.
Q. How do I make a variable oscillate, ie: go 1-0-1-0
A. Do the following code:
unsigned char x;
x=1-x; //every time this statement is executed, x goes 1-0-1-0-1-0 etc
Q. How do I reset the micro?
A. One way is to reset all variables to their defaults, as listed in the PIC manual. Then, use assembly language to jump to location 0x0000 in the micro.
#asm
ljmp 0x0000
#endasm
This is quite safe to use, even when called within interrupts or procedures. The PIC 16x series micros have 8 stack levels. Each time a procedure is called, one stack level is used up for the return address. It is a circular buffer, so even if the micro is 7 procedure levels deep and in an interrupt when a reset is called, this is the new start of the stack buffer, and the micro will continue as per normal.
Another way is to set watchdog the timer when the chip is programmed, and use CLRWDT() instructions all through the code. When you want the micro to reset, stop clearing the watchdog bit and the micro will reset after around 18ms to 2 seconds depending on the prescaler.
Q. Theres a variable in the PIC manual that the compiler doesn’t recognise, even though I used the same name. How do I tell what the compilers called it?
A. Go to directory c:\ht-pic\include\ and look at the header files. These give the names that the compiler gives to the internal variables. This is extremely useful for finding out exactly what variables are available in C. Heres some example code from the header file, pic1687x.h for the PIC16F87x micro:
/*
* Header file for the Microchip
* PIC 16F873 chip
* PIC 16F874 chip
* PIC 16F876 chip
* PIC 16F877 chip
* Midrange Microcontroller
*/
//[added for FAQ] we can see that the compiler has called PORTA by the same name as the manual
static volatile unsigned char PORTA @ 0x05;
//[added for FAQ] ... the listing continues on ...
/* PORTA bits */
static volatile bit RA5 @ (unsigned)&PORTA*8+5;
static volatile bit RA4 @ (unsigned)&PORTA*8+4;
//[added for FAQ] we now know that each bit in PORTA is called, for example, RA5, as per manual
//[added for FAQ] ... the listing continues on ...
Q. Whats the rule of thumb for timeouts?
A. Rule of thumb: every time a program waits for an operation that could fail, have a timeout to exit it after too much time. For example, reading in a byte with SPI. There is the clock and data line, and a byte with 8 bits is read into the micro. If the sender of the byte stops halfway through, there is four garbage bits remaining. Later on, after 5 minutes, the sender sends a new byte. Everything is now out of sync, with the first half going into one byte, and the last half going into the next.
Use the following code to implement timeouts.
//(c)Shane Tolmie, http://www.microchipc.com/, distribute freely for non commercial use on the condition that you include this web link somewhere in your document.
// example of how to use timeouts:
unsigned int timeout_int;
//for max 491512us timeout @ 8Mhz
//for max 245756us timeout @ 16Mhz
timeout_int=timeout_int_us(500);
while(timeout_int-- >= 1)
{
if (x==y) break; //typical overhead for checking ring buffer
}
if (timeout_int<=2) { //theres been too much time elapsed }
//for max timeout of 1147us @ 8Mhz
//for max timeout of 573us @ 16Mhz
timeout_char=timeout_char_us(500);
while(timeout_char-- >= 1)
{
if (x==y) break; //typical overhead for checking ring buffer
}
if (timeout_int<=2) { //theres been too much time elapsed }
/*
Time taken: optimisations on: 9cyc/number loop, 4.5us @ 8Mhz
with extra check ie: && (RB7==1), +3cyc/number loop, +1.5us @ 8Mhz
Formula: rough timeout value = (
*/
#define LOOP_CYCLES_CHAR 9 //how many cycles per loop, optimisations on
#define timeout_char_us(x) (unsigned char)((x/LOOP_CYCLES_CHAR)*(PIC_CLK/4.0))
#define LOOP_CYCLES_INT 16 //how many cycles per loop, optimisations on
#define timeout_int_us(x) (unsigned int)((x/LOOP_CYCLES_INT)*(PIC_CLK/4.0))
Q. I need more space in the micro – 8k is not enough for a 16F876.
A. You’ve done well. One thing to check is that you havnt got any orphaned functions which are never called. Hi-Tech C includes the code, even though it is redundant. To automatically check for this, set the warning level to –9 (negative nine) under menu ‘Project’ then ‘Edit Project’ then ‘node properties’. This introduces all sorts of warnings. Most of them can be ignored, except the one:
::function _myfunction is never called (warning)
This means that the code for the function ‘myfunction’ is included somewhere in your code, but it is never called. If you’re not going to use it, comment it out.
You must have your project set up to 'compile at once' rather than 'linked' for the compiler to give this information. See tutorial.
Q. Can I use printf() when communicating with a PC computer via the RS232 serial port?
A. You can use printf() to write to the USART on a PIC micro if you define your own putch() routine and #include the correct header file. It uses an extra 650 words of program memory. See the sample code here.
Q. Why cant I print long (32 bit) variables with printf()?
A. Add the switch ‘-lf’ to the ‘additional command line options’, under menu ‘Project’ then ‘Edit Project’ then ‘Node Properties’. This uses up a additional 1600 words of rom space, but it allows printf to print longs.
Q. How do I store large amounts of data?
A. Use EEPROM or the Dataflash available from Microchip or Atmel. Capacities range from 1Kbyte to 4Mbyte non-volatile chips.
Hardware for PIC
Q. Can I control 2 LEDs from one port?
A. Yes. The only catch is that one has to be on at any time. Connect the 2 LEDs in series, between the 5V and GND rails. Attach the port to the middle between the LEDs. Connect a current limiting resistor in series between the VCC rail and the top LED, and between the GND rail and the bottom LED. Different color LEDs require different voltages acorss their terminals, read the datasheet.
Q. I want to protect my circuit from overcurrents and shorts.
A. Get a polyswitch, available from, among others, the manufacturer RayChem. See http://www.raychem.com/, search for “miniSMDC075”. These are tiny autoresetting fuses that limit the current to no more than a certain value.
Q. How do I save power in my embedded micro system?
A. There are many methods to reduce the power consumption of a circuit with a micro in it.
1. Use the lowest clock frequency possible. For PIC micros, the power consumption seems to rise as the square of the frequency.
2. Put the micro into sleep mode. It can wake itself up when an internal timer rolls over. Be careful to complete all operations, in software and hardware, before putting the micro to sleep. For example, if a serial character is only half received in hardware, wait until it is fully received before shutting down.
3. Switch off external peripherals when they are not in use. For example, have one pin on the micro connected to the standby pin of the serial chip. You could even power external chips off the micro, as the micro can source 20mA, and simply switch off the port when it is not in use.
4. Use the lowest voltage possible. In saying this, be very careful to look up the voltage range for each chip when using it in your circuit. An example is a 5V embedded datalogger, which had a 4Mbit flash chip in it. Instead of using the AT45D041, the 5V part, the AT45DB041, the 3.5V part, was used. It seemed to work perfectly. The only problem was that every 20th page of data read from the chip was completely corrupted.
Q. My PIC sometimes resets by itself.
A. Bad power? Brownouts? Put a decoupling cap as close to the power supply as possible, so when it switches it doesn’t brown itself out. 0.1uF for <8Mhz, 0.01uF for >8Mhz.
Bootloader for PIC16F876
Q. How do I do a bootloader for the flash based 16F876?
A. Although code is available from Hi-Tech, this is a modified and enhanced version that doesnt need RB0 - it uses timeouts, so the code is loaded within 1 second of powerup. It also handles config bits properly, and programs the EEPROM also. See sample code archive here.
Simple Multitasking Kernel for PIC under Hi-Tech C
Q. How do I handle many things at once on a PIC? I want a simple multitasking system.
A. See here for a big explanation of this.
Tips for using the PICSTART Plus Programmer
Q. The programmer doesn’t seem to work – it doesn’t program
A. Sometimes this happens, maybe MPLab has got something wrong with it or the programmer itself has crashed. It can crash – its running code just like the computer. Pull the plug out of the programmer, shutdown the computer and restart it, then retry it. This usually fixes the problem.
Q. How do I embed configuration words for the programmer into my C program
Q. MPLab always loses my settings for the PICStart plus programmer when I exit
A. Use the __CONFIG(); macro in your C code, after #include
//look up datasheet for explanation of terms
#define CONFIG_ADDR 0x2007
#define FOSC0 0x01
#define FOSC1 0x02
#define WDTE 0x04 //Watch Dog Timer (Enable low) - (include to disable)
#define PWRTE 0x08 //PoWeR Up Timer (Enable low) - (include to disable)
#define BODEN 0x40 //Brown Out Detect ENable - active HIGH (include to enable)
#define RC (FOSC1 | FOSC0)
#define HS FOSC1
#define XT FOSC0
#define LP 0x00
#define CP0 0x1510
#define CP1 0x2A20
/* code protection */
#define UNPROTECT (CP0 | CP1) // unprotected
#define PROTECT50 CP1 // upper half program memory code protected
#define PROTECT75 CP0 // upper 3/4 program memory code protected
#define PROTECT 0x00 // all memory code is protected
Thus, to make PICStart Plus default to HS, Watchdog On, Power up time enabled, Brown Out Timer enabled, and Unprotected, use the following lines:
#include
__CONFIG(HS | BODEN | UNPROTECT);
Be careful of PWRTE. According to the PIC manual, this is active low, so missing it out turns the power up timer on. Including it turns it off. This is the same for WTDE.
Some compilers recommend linking the fuse names with logical "OR", the symbol "|". Other compilers recommend linking the fuse names with logical "AND", the symbol "&".
Tips on using Libraries with MPLab
Q. What is a library, consisting of a ".lib" file, and how do I use it?
A. Normally, most people have a library of commonly-used routines that are always included in every project they do.
For example, there is the following files:
· delay.c - delay routines in C such as DelayMs(x)
· delay.h - header file for delay.c
· serial.c - serial RS232 routines such as putch() and getch()
· serial.h - header file for serial.c
One method of using these routines could be to include these files in your project. Put the following lines in your main .c file:
#include "delay.h"
#include "delay.c"
#include "serial.h"
#include "serial.c"
main()
{
}
However, this method has one key disadvantage: even if a routine is not used, it will still take up memory in the final compiled .hex file.
Using a library solves this problem. Once you have made a library out of your commonly used routines, the linker picks out the necessary routines and includes them in the final .hex file.
Q. How do I make a library file?
View the latest answer to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
Lets say I have a number of modules, each one an object file, ending in ".obj". I can put these in a library by executing the following at the dos prompt:
libr r xx.lib i2c.obj ser_877.obj delay1.obj
I now have a library called xx.lib which can be included in my project. Under 'Project' and 'Edit Project' and the 'Library Path' box, enter the path to xx.lib. If it gives an error, see here. Alternatively, use 'Add Node' to add the ".lib" file to the project.
Q. I make a library file ending in ".lib" but I cant tell MPLab how to get to it
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Under Project..Edit Project, click in 'Library Path'. If you see the following error message, you have set up the project incorrectly.
You need install the 3 languages: under "Project..Install Language Tool" for compiler, Assembler and Linker, all select "c:\ht-pic\bin\picc.exe".
Select "Project..New project (or Edit Project)" then click on "node property"
***[.HEX] : select language tool: Linker
***[.C]: select language tool: Compiler
If the language tool for ***[.HEX] is set to Compiler, then this means the linker is never used, and no libraries or additional C files can be added.
Serial Numbers with Hi-Tech C
Q. How do I add serial numbers to a program coded in Hi-Tech C?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Use the following to preserve the Rom location for a serial number:
;Reserves 4 bytes for Serial Number programming
;To be placed at 0x0FFC to 0x0FFF with the PICC option -L-Ppreserve=ffch
;The 0x0FFF=4096 is for a 4k micro, a 2k or 8k micro would be different
psect preserve
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
retlw 0xFF ;Serial No
psect preserve
The "-L-Ppreserve=ffch" must be entered in the option field in MPLAB for the HiTech Linker to place the code segment at the specific address.
The code is saved as an *.as file and included as a node in the MBLAB project and uses the PICC Assembler to compile. Remember to set the Assembler in the Language tool to Picc.exe.
Incremental Compiles with Hi-Tech C
Q. Under Hi-Tech C and MPLab, every time I recompile, it recompiles everything and then links it. How do I do incremental compiles?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechCcompiler HiTechCFAQ
A. Add the line 'c:\ht-pic\include' under 'include path' in the 'edit project' dialogue to enable incremental compiles.
You must already have the 'language tool' under the root node set to 'PIC C Linker', not 'PIC C compiler'. Add each source file to the list to be compiled and then linked.
PIC12F672 and OSCCAL
Q. How do I set the OSCCAL register in Hi-Tech C for the PIC12F672?
View the latest answers to this question on MicrochipFault.com; tags PIC HiTechC PIC12F672 OSCCAL HiTechCFAQ
A. If the built-in 4Mhz RC oscillator for the PIC12Cx67x is used, it can drift with temperature variations. The oscillator calibration speed is trimmed by setting register OSCCAL at run time. The top ROM byte in memory is read to obtain this value at runtime.
Looking in the header file, c:\htpic\include\pic1267x.h, we find the following lines, in different parts of the header file
static volatile unsigned char bank1 OSCCAL @ 0x8F;
#if defined(_12C672) || defined(_12CE674)
#define _READ_OSCCAL_DATA() (*(unsigned char(*)())0x7FF)()
#endif
Thus, use the following C line at the start of the program:
OSCCAL=_READ_OSCCAL_DATA();
If you are using a flash -F- part or a UV-erasable -JW- part, remember to record the value of the highest ROM address. It will be in the form retfw N where N=calibration value. For example, if N=0x90, the instruction will be 0x3490.
I used seven PIC12C672-JW parts in development. The OSCCAL values that I recorded for them were 0x90, 0x94, 0xB0, 0x84, 0xC8, 0xB0, 0xC0. This illustrates the fact that there is process variation among batches of PIC micros.
If you lose the value of OSCCAL for a particular JW part, you can retrieve it, with effort. Write a little routine to output 10kHz pulses. Alter the OSCCAL value until the pulses are indeed 10kHz as viewed on an oscilloscope.
No comments:
Post a Comment