I had my first opportunity to work with microcontrollers about two years ago. Before that, I didn’t really know what they were or how I could use them. I don’t use them very often so I still have a lot to learn.
A microcontroller is just like a computer, only it is much smaller than an ordinary computer. They contain a processor, their own internal memory, and they can interact with inputs and outputs. There are a lot of different microcontrollers out there and it can be overwhelming when you are new to try to pick the one you need. The first family of microcontrollers I used was Microchip Pic. Even within this family, you have 8 bit, 16 bit, and 32 bit to choose from, and each bit size has a plethora of different chips available.
The first Pic I used is still my favorite, the Pic18F24K20. The 24K20 is an 8 bit chip with 28 pins, 16 KB of program memory, 2 comparators, several timers, and up to 16 MHZ internal oscillator clock. It’s an all around good chip to use for projects and it is cheap (about $2!). It is usually the first chip I look at when considering a microcontroller since I am familiar with it.
If you are interested in learning microcontrollers, a good place to start is with the Microchip Pickit 3 Debug Express. This kit will get you a prototype board which includes some LEDs, a push button, and a PIC18F45K20 microcontroller. In addition, you will get the Pickit 3, which is used to program and debug your microcontroller.
Building Your First Microcontroller Program
I want to program my microcontroller to turn on 3 LEDs for about 30 seconds and then turn them off. I will eventually integrate an accelerometer into this to tell the LEDs to turn on but not until later. As mentioned above, I will be using the Pic18F24K20 for the microctonroller. The LEDs are all 4200 mcd white LEDs with a forward current of 30 mA. A list of everything I’m using is below:
- 3 4200 mcd LEDs
- 2 10k resistors
- 0.1 uF capacitor
- Pickit 3 in circuit debugger
- MPLAB X IDE
- C18 Compiler
A microcontroller’s datasheet is critical. When working with electronics, I don’t typically refer to a component’s datasheet very often outside of specing it out. However, with microcontrollers, you will find yourself constantly referring to its datasheet. Fortunately, Pic’s datasheets are all pretty standardized so once you get used to one datasheet, you should be able to find whatever you are looking for in a different Pic datasheet.
In order to find your microcontroller’s datasheet, simply google your microcontroller followed by datasheet (for instance, “pic18f24k20 datasheet”). A pdf document should be one of the first couple of hits on Google. I recommend bookmarking or saving the pdf because you will be using it so often.
Wiring Your Prototype
For this project, I’ll be using my Pickit 3 to debug. When using the Pickit 3 in debug mode, you can also use it to power the circuit at 3.3 v. You won’t always be able or want to use the Pickit to power the circuit because you might need a higher voltage than 3.3. However, since we are just powering LEDs, 3.3 v is just fine!
First, we will put the chip onto our breadboard and figure out where to plug the LEDs in. Page 5 of the datasheet shows the pin-out for the Pic18f24k20. We want to figure out which leg of the pic can be used as an output. Because I’ve used this chip before, I know that the RA pins can be used as digital inputs and outputs. RA1 is on pin 3, so we will hook our LEDs up to that leg.
If you have never used a microcontroller before and therefore aren’t sure which legs can be used as a digital output, look in the table of contents (page 9) for I/O Ports. On this datasheet, that is on page 121. Section 10.1 tells us that PORTA is a bidirectional port, so we should be able to use any of the RA pins. Section 10 also tells us that we will need to use the TRIS register to set the pin’s direction (meaning tell the Pic if a certain pin will be an input or an output) and that the PORT register sets the level of the pin. We’ll get more into this later but it is always good to know where to find this information if you ever forget it.
We will run our microcontroller at 3.3 volts. LEDs tend to have a voltage drop of about 0.7v across them. According to the datasheet, the input and output pins of the microcontroller can handle 25 mA of current. We will need to put a resistor in between our LEDs and the microcontroller to limit the current. The minimum resistance value we need for this is 100 ohms.
After figuring out which pin will be used for the LED, I connected the resistor to RA1 (pin3). Next, connect the anode of the LED (the long leg) to the resistor. The cathode leg of each LED gets connected to ground. Next, we need to figure out how to hook up the debugger to our microcontroller. We will need to hook up five of the six legs of the debugger to five of the legs of the microcontroller. In order to figure out where they go, we will need to check the pinout of the debugger. The pinout is located on page 15 of the debugger’s datasheet (which can be found here). To find this, I simply searched the document for ‘pinout’ using control-f.
Pin 1 on the debugger is noted with an arrow next to it. Pin 1 is labeled MCLR/Vpp (the line above MCLR means it is active low, so it will typically be in a high state, though this isn’t relevant to us for this project). Next, we search the microcontroller’s pinout for a pin labeled MCLR or Vpp. For my microcontroller, this is pin 1 as well. Pin 1 on a microcontroller will have a small circle next to it. This is noted on the pinout diagram in case you forget.
The next pin on the debugger is labeled Vdd Target. Searching the microcontroller’s pinout, we find Vdd is on pin 20. Next, Vss (ground) is on pin 19. I will connect this pin to one of the ground pins on the breadboard, so that we can connect to it more easily. The 4th pin on the debugger is PGD, which we can find on pin 28 on the microcontroller. Pin 5, PGC is pin 27 and the final pin, PGM, we won’t use. To recap:
|Debugger Pin Number||Pic18f24k20 Pin Number|
Next, we will look at the datasheet and find the two ground pins on the pic — they are labeled Vss and are on pins 8 and 19. The positive power line (Vdd) is on pin 20. In an attempt to stablize the power, put a 0.1uF capacitor near the pic on pins 19 and 20. Additionally, put a wire from the ground pin on pin 8 to the ground on the circuit board.
Finally, connect a 10k pullup resistor between the MCLR pin (pin1) of the microcontroller and the positive input rail.
The final wiring looks like this (the electrical components are not optional):
Programming Your Microcontroller
As mentioned above, I’ll be using MPLAB X with the C18 compiler. If you are new to C++, a couple things will be important:
- Every statement must end in a semicolon (;)
- Every function must be declared
- Every variable must be noted before its use
- White space doesn’t matter
When programming your microcontroller, you will always need to have your microcontroller’s header file in the INCLUDES section of the script. I’ll also be using a delay function, so I will need to tell the script to include that as well. There are also some configuration bits that I always set in every file. The top of my C++ script looks like this:
/** C O N F I G U R A T I O N B I T S ******************************/
#pragma config FOSC = INTIO67 //, FCMEN = OFF, IESO = OFF // CONFIG1H
#pragma config BOREN = OFF //PWRT = OFF, , BORV = 30 // CONFIG2L
#pragma config WDTEN = OFF
#pragma config MCLRE = OFF, LVP = OFF//, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTBE // CONFIG3H
/** I N C L U D E S **************************************************/
Next we will put our declarations. Though we won’t be using any functions for this simple script, I will still declare our interrupt service routines (ISR). With this microcontroller, we can have a low interrupt and a high interrupt. This essentially gives us two options for triggering, should we decide to use them later.
/** D E C L A R A T I O N S *******************************************/
void high_isr( void );
void low_isr( void );
/** Declare Interrupt Vector Sections ****************************/
#pragma code high_vector=0x08
_asm goto high_isr _endasm
#pragma code low_vector=0x18
void interrupt_at_low_vector (void)
_asm goto low_isr _endasm
Now that we have all of our setup complete, we can write the meat of the code. The code is below:
#pragma code // declare executable instructions
* Turn on 3 LEDs for approximately 30 seconds, then turn them off
* initialize chip’s pins and allow interrupts. Next, turn LED output pin
* (pin RA1) on for 30 seconds, then turn it off
* 0.1 – initial release
void main (void)
char i; //counter
// Set unused pins as output
TRISA = 0b10000000; // CLKI is input (RA7)
OSCCONbits.IRCF = 0b111; // sets oscillator to 16MHz 111
RCONbits.IPEN = 1; // Allow interrupts
INTCONbits.GIEL = 1; // Allow low priority interrupts
// set RA1 as output and turn it on
TRISAbits.RA1 = 0;
PORTAbits.RA1 = 1;
// for LED to light up 30 seconds: 10000*4*109*109/16000000
for (i = 0; i <= 109; i++)
// turn RA1 off
PORTAbits.RA1 = 0;
First, we declare the variable i to be a char. We’ll use this variable later as simply a counter. Next, you will notice the TRIS settings. As previously mentioned, the TRIS bits on my microcontroller set the different pins to be either inputs (1) or outputs (0). The line
TRISA = 0b1000000
sets every RA pin as an output except RA7. RA7 is the CLKI pin and we set that as an input. The IRCF bit of the OSCCON register sets the oscillator frequency. All of this is in the datasheet, so if in doubt, check the datasheet! In order to set a specific bit of a register, we can use the general formula:
REGISTERbits.BITTOCHANGE = XX
Otherwise, we can set the entire register by declaring each bit as a binary number (this is what we did for the TRISA register). Be careful with this — you have to make sure you set each of the bits correctly and always make sure you include ‘0b’ before the binary number! If you don’t have the ‘0b’, the script will error out and it can be incredibly difficult to find where the mistake is.
Setting the internal oscillator frequency will always be critical. For this project is a critical because it will determine how our math will work out to get the LEDs on for 30 seconds. If you are wanting to communicate via SPI or I2C to a peripherial device, the oscillator will have to be of a compatible speed for the communication. Even if the oscillator frequency does not matter for either of those reasons, you will still want to pay attention of it for battery efficiency reasons. If the frequency doesn’t matter, turn it down as low as possible.
The next two lines will allow interrupts. Again, we aren’t using them now but I like to set them up just in case we decide in the future to use them.
Next, we set RA1 to be an output (0) with the TRIS statement and we turn the LED on by setting the RA bit of the PORT register to 1.
Finally, we have a while statement. Generally with your microcontroller, you will have the meat of your code within a while(1) statement, so that it continually runs. This will be true for us here.
The general theory here is that we will use one of the built in delay functions to delay a number of cycles. The time for the cycles is related to our oscillator speed. The longest delay function is Delay10KTCYx(). This function can take an input from 0-255 and will delay input * 10,000 cycles. Our cycles are the clock/4. Therefore, the most time we can get with our longest delay is:
10,000 * 4 * 255 / 16,000,000 = 0.6375 seconds
In order to get to 30 seconds, we will need to have several consecutive delays. The number of delays we need is:
30 * 16,000,000 / (4 * 10,000) = 12,000
So, whatever our input is for the delay will need to be looped a number of times such that:
delay input * number of loops = 12,000 cycles
To make it easy, I am using 109 for both numbers:
// for LED to light up 30 seconds: 10000*4*109*109/16000000
for (i = 0; i <= 109; i++)
// turn RA1 off
PORTAbits.RA1 = 0;
And that’s it! When we program and run the script, the LEDs illuminate for approximately 30 seconds and then turn off. Our first microcontroller program!