Microcontrollers tend to be dedicated to a particular purpose, so while they're very flexible devices these days, there are a number of different (often conflicting) options for how you might want the hardware to work. You might think that this configuration is done in software, but what if you want the hardware to act a certain way from the very start? Keeping in mind that Microchip PIC devices can "boot" in a matter of milliseconds, there needs to be a way you telling the hardware how to have in advance of it starting up. This article covers how to get your microcontroller based project working the way you expect when it starts up.
Enter config bits, or, as they used to be called, config fuses. Back in the days when microcontrollers were program-once devices, you really did blow a fuse in order to program them. Today, most micros have flash memory that can be programmed tens of thousands of times, but there are still one-time-programmable (OTP) devices about.
In any case, there's a bunch of "config words" that define how the micro is going to come from the get-go. Let's look at some of these options (we'll choose the PIC18F2620 as an example) and then see how to program them. Sourceboost gives you all the config strings in a ready-to-use format for including in your project. The format has the config bits suffixed with the same name as the config word so you can ensure that the right bits are going in the right word. Of course, your config words will be different if you're using a different microcontroller, but you should learn enough here to point you in the right direction.
In the 18F2620, the config bits are stored in flash, but at a high memory location. They're stored at 0x300001 and upwards. For historical reasons due to the way memory was previously arranged on PIC microcontrollers, config words are numbered with each number having a "high" and "low" byte.
CONFIG1L does not exist on the 18F2620. If it did, it would live at 0x300000 location.
CONFIG1H contains bits that specify oscillator options. Typically, in Embedded Adventures projects we try and use external crystals which give more accurate and reliable results. This does use up two pins however, and sometimes these can be more important than the speed at which the chip is running. The external crystal oscillator is referred to as the HS oscillator (if you're doing the typical thing of trying to run the micro as fast as it will go). In the 18F2620, you can also enable the PLL module which will give you a four-times speed boost. From CONFIG1H are also options to enable the fail-safe clock monitor (that switches to the internal oscillator should the external one fail, and an option to enable the switching between different oscillator sources.
We generally recommend that you use an external crystal, and turn off any oscillator switching / failover modes. In prototyping, it is important to have reliable, repeatable results (mostly so you can reduce the axes of error). It is possible to configure the 18F2620 to start up using the internal oscillator (which runs at 8Mhz, or 32Mhz with the PLL enabled) and then switch to the external one (we use a 10Mhz crystal with PLL enabled giving you 40Mhz). That gives you faster startup from interrupts in sleep mode or on bootup, if that's important, however for prototyping, this is generally not necessary.
Our recommended config string for CONFIG1H:
#pragma DATA _CONFIG1H, _OSC_HSPLL_1H & _IESO_OFF_1H & _FCMEN_OFF_1H
CONFIG2L handles brown-out reset and the power up timer. Brown Out Reset (or BOR) is the ability for the microcontroller to reset itself if the supply voltage falls below a specified threshold. It will remain in reset state until the supply goes back over the threshold. This can mean some battery saving (at least, once the battery is flat beyond a certain level, it'll stop draining power as quickly) – but at that point there's no functionality available anyway. The power on timer waits on initial power-on for the supply to go above the BOR voltage threshold, then hangs around for another 65ms before kicking things off for real. This can help ensure that your power supply is steady before trying to execute any code.
We have seen some pretty unpredictable results from the 18F2620 when testing some LED display panels. We thought the software was crashing or resetting the micro when in fact, what was happening was the BOR was being tripped as more LEDs were lit and the power supply dropped. As such, unless you've got a real need for it, we recommend switching it off. Leaving the power supply to settle before starting the firmware, is generally a good idea (you're probably not going to notice the 65ms it takes).
#pragma DATA _CONFIG2L, _BOREN_OFF_2L & _BORV_2_2L & _PWRT_ON_2L
CONFIG2H gives you settings for the watchdog timer. This is a timer that resets the PIC in a certain amount of time (you can broadly specify how long that might be). It might be good plan to reset your PIC if you're doing something from which you might never recover – although hopefully you're written good enough code that this can not happen! Alternately, the watchdog timer can also pull the PIC out of sleep mode. This means you can arrange for the PIC to go to sleep for a certain amount of time (if nothing else happens). For prototyping, we recommend switching the watchdog timer off. If you do enable it, make sure you reset the watchdog timer regularly or – well, you can guess what will happen.
#pragma DATA _CONFIG2H, _WDT_OFF_2H & _WDTPS_128_2H
CONFIG3H has a collection of configuration items. You can choose for the RE3 pin to be available (as input only) or for that pin to be MCLR (which resets the micro when dropped low). For prototyping, being able to reset the micro by pressing a button is easier than pulling the power out.
The LPT1OSC bit gives the option of a "high power" mode or a "low power mode". The low power mode is more sensitive in high noise environments. So if battery usage is very important, you need to design your circuit carefully. For prototyping, of course, we would recommend leaving this in high power mode.
PBABEN, allows you to specify if PORTB pins 0 – 4 should wake up as analog input pins or digital pins upon reset. Of course you can change this in software in the ADCON1 register at any time.
The CCP module is a Capture / Compare / PWM module and a final CONFIG3H bit allows the "steering" of this output to be either RC1 or RB3. This is handy to be able to change if you require the other functionality available on one of these pins (RB3 is also analogue input 9 and RC1 can be Timer 1 oscillator input). Alternately, your PCB layout might be easier if you could move this output to one or the other. For prototyping, generally this does not matter either way.
#pragma DATA _CONFIG3H, _CCP2MX_PORTC_3H & _PBADEN_OFF_3H & _MCLRE_ON_3H
CONFIG4L contains some interesting options and shows very clearly how important it is to be able to configure the PIC before startup.
The DEBUG bit allows hardware debugging. You can step through code, set breakpoints and so on – if you have your software set correctly and this bit cleared. Hardware debugging also requires the exclusive use of RB6 and RB7 (also used for ICSP programming). In all honesty, we find that serial output is generally sufficient to find bugs, although you can debug a lot of PICs with merely a PicKit2.
XINST allows for the 18F extended instruction set mode. This is not used by Sourceboost and if you switch it on by mistake (regardless of the compiler you're using) there will be some pretty unpredictable results. One of our readers spent days trying to find what was wrong with his code only to realize the XINST bit was set.
LVP gives you the ability to program the PIC using ICSP without requiring the "higher" Vpp voltage. There is a catch however – you need to dedicate the RB5 pin (PGM) to being an indicator if you are in programming mode or not. Given the PicKit2 generates the right voltage anyway, there does not seem to be a lot of value in switching this on for prototyping purposes.
Finally, the STVREN gives the option of having the PIC reset if the stack overflowed or underflowed. If, for example, you call too many nested functions, it would allow the PIC to reset. Once reset, you can examine why the PIC reset and report a fault. We recommend leaving this on – if your PIC boots in the middle of doing something, you can reasonably assume that you've used up the stack space (possible when you're deep in a nested situation and then an interrupt occurs).
#pragma DATA _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _DEBUG_OFF_4L & _XINST_OFF_4L
CONFIG5L gives the option of specifying if any one of four contiguous blocks of memory are protected from any attempts by the PIC to change (ie write) the flash memory values.
#pragma DATA _CONFIG5L, _CP0_OFF_5L & _CP1_OFF_5L & _CP2_OFF_5L & _CP3_OFF_5L
CONFIG5H givest the option of protecting the block of memory starting at 0x0000 to 0x7fff) – used by some bootloaders, as well as a bit to allow the protection of EEPROM memory. The PicPack bootloader, "Boostbloader", resides in upper memory, meaning that while it is slightly less robust than a locked-down, physically protected bootloader, it does not require the "double jump" for interrupts that bootloaders located in lower memory do . We may provide this as an option in the future.
#pragma DATA _CONFIG5H, _CPB_OFF_5H & _CPD_OFF_5H
CONFIG6L allows the protection of memory blocks being write protected.
#pragma DATA _CONFIG6L, _WRT0_OFF_6L & _WRT1_OFF_6L & _WRT2_OFF_6L & _WRT3_OFF_6L
CONFIG6H, CONFIG7L and CONFIG7H give more granular protection, allowing blocks of memory to be protected from when the code being executed is not in the same block. We could, for example, make a bootloader that was "secret" and unable to be ready by any other code.
#pragma DATA _CONFIG6H, _WRTC_OFF_6H & _WRTB_OFF_6H & _WRTD_OFF_6H
#pragma DATA _CONFIG7L, _EBTR0_OFF_7L & _EBTR1_OFF_7L & _EBTR2_OFF_7L & _EBTR3_OFF_7L
#pragma DATA _CONFIG7H, _EBTRB_OFF_7H
While strictly speaking they're not config words, the DEVID words allow code to determine which model of the chip is running and which hardware revision. This is important when there are bugs in the hardware that are fixed over different revision levels and code needs to execute differently based on the result.
You've probably worked out by now that the Sourceboost compiler's config defines are cleverly conceived so that you can simply & together the appropriate options; this means you do not need to work out exactly which bit positions do what. The #pragma DATA gives you the ability to shove a byte in a particular location (in our case, into a config word slot).
Here's the config we use with the PIC16F88:
#pragma DATA _CONFIG, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPDOFF & _LVP_OFF & _PWRTE_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_OFF & _MCLR_ON & _INTRC_IO
Yep, that's right, it's just one word long. Some of these options will probably be familiar to you already.
If your chip is not behaving and you want to work out if you've made a mistake in your config bits, and handy hint is to use Microsoft's MPLAB IDE to import the.hex file you've created by clicking on Menu | Import … then Configure | Configuration Bits … Here's the screen you'll find when importing the PIC18F2620 BoostBloader.
You can even change the config bits if you want, then export again as a.hex file, although keep in mind this does not change your source code itself.
What you've probably realized here is that the bootloader definitely has config bits embedded in the.hex file. Our usual PicPack programs do not – since we assume you're running with a bootloader already installed. If you do want to program directly with ICSP, of course you'll need to use config bits to make your PIC run the way you intend. Check out the configbits.h file used by Boostbloader – we're starting to build up a library of handy config bits including some configuration options to turn common things on or off (these are typically place in the config.h file in your project) .