Lukas PIC Tutorial
Lukas PIC Tutorial
How to hook up the PIC so it will run?
Materials
PIC16F877A
Breadboard
20 Megahertz crystal oscillator
2 7-‐20pf capacitor
Black & red wires
>7.2 volt battery
x For use with a higher voltage supply, you will need to use a voltage regulator to convert the higher
ǀŽůƚĂŐĞƚŽϱǀ͘dLJƉŝĐĂůůLJĂϳϴϬϱŝƐƵƐĞĚ͘>ŽŽŬƵƉƚŚĞĚĂƚĂƐŚĞĞƚďLJƐĞĂƌĐŚŝŶŐ'ŽŽŐůĞ͞ϳϴϬϱǀŽůƚĂŐĞ
ƌĞŐƵůĂƚŽƌ͘͟
x /ĨLJŽƵĚŽŶ͛ƚŬŶŽǁŚŽǁƚŚĞƐĞŵĂƚĞƌŝĂůƐǁŽƌŬŽƌǁŚĂƚƚŚĞLJůŽŽŬůŝŬĞ͕ůŽŽŬŝƚƵƉŽŶůŝŶĞ͘
Circuit
1. Insert PIC on breadboard. Refer to pin diagram above for pin numbers. To prevent confusion, orient the
PIC so the dimple on top faces the same way as the pin diagram.
2. Connect breadboard side strips -‐ power to power (red strips), ground to ground (black strips). Just like the
ďŽƚƚŽŵϮǁŝƌĞƐďĞůŽǁ͘dŚŽƐĞƐƚƌŝƉƐĂƌĞŶŽǁƌĞĨĞƌƌĞĚƚŽĂƐ͞ƉŽǁĞƌ͟Θ͞ŐƌŽƵŶĚ͘͟
3. Power to Pin 1 (Vpp) ʹ red wire (connected via 47k Resistor in between)
4. Power to Pin 11 (Vdd) ʹ red wire
5. Ground to Pin 12 (Vss) ʹ black wire
6. Power to Pin 32 (Vdd) ʹ red wire
7. Ground to Pin 31 (Vss) ʹ black wire
8. ƌLJƐƚĂůKƐĐŝůůĂƚŽƌƚŽWŝŶƐϭϯΘϭϰ;K^ϭ͕K^ϮͿ͕ƚŚĞƉŽůĂƌŝƚLJĚŽĞƐŶ͛ƚŵĂƚƚĞƌ͘;ƉŽůĂƌŝƚLJсǁŚŝĐŚŽĨƚŚĞϮǁŝƌĞƐ
goes in which hole).
9. Capacitor into power & ground ʹ make sure the polarity is correct according to the datasheet for the
capacitor. If not it could damage the circuit.
10. Ground to battery ground (-‐) ʹ black wire.
11. Power (red) to battery power (+) ʹ red wire. Or, solder that wire to a switch & run another wire from the
switch to battery power, so you can switch the power on & off.
If using voltage regulator:
raw battery power wire goes into voltage regulator input
breadboard ground strip goes to battery ground
voltage regulator output wire (+5 volts) goes to breadboard power strip.
voltage regulator ground wire goes to breadboard ground strip
What do the pins mean?
The pins RB0-‐RB7, RC0-‐RC7, and RD0-‐RD7 are digital I/O pins. The pins CCP1 and CCP2, which share locations
with RC1 and RC2, can be used for a PWM signal (see DC Motor tutorial). The pins AN0-‐AN7 are for analog I/O
(see Photoresistor tutorial). TX and RX are for debugging I/O (see Output Messages to Computer tutorial).
The remaining pins deal with power/ground, the clock signal, and programmer I/O.
W/ŝƐŵĂĚĞŽĨƐĞǀĞƌĂů͞ƉŽƌƚƐ͘͟ĂĐŚƉŽƌƚŝƐĚĞƐŝŐŶĂƚĞĚǁŝƚŚĂůĞƚƚĞƌ͕ZϬ-‐RB7 are a port. RC0-‐RC7 and RD0-‐
RD7 are a port as well. RA0-‐RA5 and RE0-‐RE2 are also ports, but with fewer pins. Some of these pins have
special purposes, but most can be used as basic input/output pins.
For example, you can set pin RB0 to be either an input pin, or an output pin. As an input pin, the digital voltage
on the pin can be read in. For example, if RB0 is connected to ground (0v), then you would read a digital 0. If
RB0 was connected to power (5v), then you would read a digital 1.
On the other hand, if you wanted to set RBO as an output pin, you could choose to make RB0 either be 5v, or
0v. This can be used, for example, to turn off or on a LED, or to turn off or on a motor.
How to program the PIC?
I omit step by step details of how to program the PIC because software always changes.
As of spring 2010, the club uses software from CCS (http://www.ccsinfo.com/). We use the PCW Compiler to
write & compile programs and CCS Load and the ICD-‐U40 programmer to write programs to the PIC. Always
load a .hex file onto the PIC ʹ the compiled machine language program. The hex file should be located in the
same folder as your project file.
You need to connect the ICD͛ƐƚŚĞƌŶĞƚĐĂďůĞƉŝŶƐƚŽƚŚĞĂƉƉƌŽƉƌŝĂƚĞƉŝŶƐŽŶƚŚĞW/ŝŶŽƌĚĞƌƚŽůŽĂĚĂ
program. You also need to have the PIC running (i.e. under +5V power) when programming.
^ĞĞ͞,ŽǁĚŽ/ĐŽŶŶĞĐƚƚŚĞ^/ƚŽŵLJŽǁŶŚĂƌĚǁĂƌĞ͍͟ŝŶƚŚĞ^&Y͕
http://www.ccsinfo.com/faq.php?page=icd_connection.
Don Crouse, the 2010 president, made custom programming boards to do that. They might still be in the lab.
You can also cut up the wires on an Ethernet cable & connect them to the appropriate pins on the
ďƌĞĂĚďŽĂƌĚ͘/ĨLJŽƵĚŽŶ͛ƚǁĂŶƚƚŽŵĂŬĞLJŽƵƌŽǁŶĚĞǀŝĐĞ͕LJŽƵĐĂŶďƵLJǁŚĂƚLJŽƵŶĞĞĚƚŽƉƌŽŐƌĂŵƚŚĞW/͘
Another available programmer the club most likely had is the PicKit 2. To program with a PicKit, connect the
pin marked by the arrow on the PicKit device to the mclr pin. Pin 2 (next to the arrow) is +5, Pin 3 is Gnd, Pin 4
is B7, Pin 5 is B6, and Pin 6 is the optional debug pin PGM (not necessary to connect). Also, connect the PIC to
power & ground (the Vdd/Vss pins). To program a PIC, connect the PIC IC to the PicKit, and a usb mini cable
from the PicKit to the computer. To use the PicKit, run the PicKit V2 program on the desktop of the PC. The
first necessary step, if the PicKit is configured to program a different pic than your own, is to select the family
of the PIC. For the 16f877A, you must select "base device" in the device options. In order to program a hex
file, it must be loaded / imported first. Do this by going to file and importing your program's hex file. After the
file is loaded, click on the "write hex" button. This should write your hex file and say it programmed
successfully. The program will automatically start running even with the PicKit connected unless the /MCLR
checkbox is selected.
Note: The PicKit has the capability to supply power to the pic via the USB bus. It will automatically detect if
the device is powered when trying to program. If you want to run your PIC just from the USB, you can select
the "Vdd On" checkbox. This can supply up to 1A of current at 5V. Make sure the voltage number next to the
checkbox is set to 5 ʹ unless your processor does not support 5V. The PIC used in this tutorial is a 5v device.
Miscellaneous Advice
You must have the PIC wired up & be able to compile & load programs to do the other tutorials.
To run the other tutorials, follow these steps:
1. Connect circuit as described.
2. Compile the example code with whatever compiler you are using.
3. Load the program onto the PIC.
4. Turn on the power to run the program
/ĨLJŽƵĚŽŶ͛ƚŬŶŽǁŚŽǁƚŽĐŽĚĞƐŽŵĞƚŚŝŶŐ͕ůŽŽŬĂƚŽůĚW/ĐŽĚĞŽŶƚŚĞƌŽďŽƚŝĐƐůĂď͛ƐĐŽŵƉƵƚĞƌƐ͘/ŚĂǀĞ
found that we need to solve similar programming problems year after year, especially when we are talking
about sensor input & motor output. Chances are good that somebody already wrote it. The PIC MCU C
compiler manual is a good reference too.
I recommend that you try to be neat when attaching wires & other components. Cut your wires so
that they lie flat on the breadboard, not sticking up. Make sure the bare ends are long enough to make a solid
connection with the breadboard ʹ LJŽƵĚŽŶ͛ƚǁĂŶƚĂůŽŽƐĞǁŝƌĞ͊ĞŽƌĚĞƌůLJǁŚĞŶLJŽƵƌƵŶǁŝƌĞƐƚŽƚŚĞǀĂƌŝŽƵƐ
sensors & motors, too. You will have a confusing spaghetti forest when you add sensors, motors, switches,
and lights.
Another good idea is to use wire colors consistently. For example, every time you have a wire going to
ground, use the same color, usually black. That way, you can understand what it does with a glance. This is
important when you have 50+ wires on the board. I use red for power (the + on the battery & breadboard),
black for ground (the -‐), blue for inputs, and green for outputs.
Always double check your connections before flicking the switch. If just one wire is in the wrong
position, it could disable your circuit or burn something out.
You can build your circuit with test LEDs, to make sure it is working when you turn it on. I have a
power light directly connected to the battery to so I know when the circuit is on. I also have an LED controlled
by the PIC. In my programs, I make the PIC turn the LED on when it starts running. That way I know that the
PIC is working correctly.
The Anode of the LED goes to the +5v rail out of the regulator. The cathode of the LED goes to a
current limiting resistor ʹ any value between 220 and 1k is fine. The other end of the resistor goes to Ground.
This LED should turn on at full brightness when the circuit is turned on. If the LED does not turn on, you may
have the LED in backwards. If you know the LED is in correctly, and it does not turn on or turns on dimly when
the power is turned on, TURN OFF YOUR CIRCUIT, and check your wiring. This most likely means there is a
short somewhere with your power rails.
If you have been running the robot for a while, check the power battery voltage. If the voltage gets too
ůŽǁ͕ƚŚĞƌŽďŽƚǁŝůůďĞŚĂǀĞƐƚƌĂŶŐĞůLJĚƵĞƚŽW/ĚŽŝŶŐĂ͞ďƌŽǁŶŽƵƚ͘͟
LED Blinker
Materials
ŝƌĐƵŝƚĨƌŽŵ͞/ŶƚƌŽĚƵĐƚŝŽŶƚŽƚŚĞW/ϭϲ&ϴϳϳ͟
100 Ohm resistor
LED
Circuit
1. Pin RB7 to resistor
2. Resistor to LED
3. LED to ground
Code
//all these # below set up the PIC
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) // Sets crystal oscillator at 20 megahertz
#use rs232(baud=9600, xmit=PIN_C6, invert) //Sets up s erial port output pin & baud rate
//main program starts here
void main() {
//Infinite program loop starts. LED blinks forever.
while(true){
ŽƵƚƉƵƚͺŚŝŐŚ;W/EͺϳͿ͖ͬͬƐĞŶĚĂ͞ϭ͟ƚŽƉŝŶZϳ͕ŵĂŬŝŶŐZϳ͞,ŝŐŚ͟Ăƚϱǀ
//this will turn on the LED hooked to RB7
delay_ms(500); //wait half a s econd, delays for 500ms
ŽƵƚƉƵƚͺůŽǁ;W/EͺϳͿ͖ͬͬƐĞŶĚĂ͞Ϭ͟ƚŽƉŝŶZϳ͕ ŵĂŬŝŶŐZϳ͞>Žǁ͟ĂƚϬǀ
delay_ms(500); //wait half a s econd, delays for 500ms
}
}
Notes
You can easily add more LEDs and make them flash in different patterns.
For more readable code, use
#define R ED_LED PIN_B7
#define GREEN_LED PIN_B6
͙
͙
output_high(RED_LED);
output_high(GREEN_LED);
Photoresistor Input
Materials
ŝƌĐƵŝƚĨƌŽŵ͞/ŶƚƌŽĚƵĐƚŝŽŶƚŽƚŚĞW/ϭϲ&ϴϳϳ͟
Photoresistor
1K ohm resistor ʹ or whatever is appropriate for your photoresistor
Wire
Circuit ʹ the diagram on the right
1. Breadboard power (Vcc) to 1K-‐ohm resistor.
2. 1K-‐ohm resistor to photoresistor power.
3. Photoresistor ground to breadboard ground.
4. Wire from between resistor & photoresistor to PIC pin AN0.
If R1 is the photoresistor, the voltage will increase with increasing light intensity. If R2 is the photoresistor, the
voltage will decrease with increasing light intensity.
Code for single photoresistor
//all these # below set up the PIC
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) //crystal oscillator at 20000000 hertz
#use rs232(baud=9600, xmit=PIN_C6, invert) //serial port output pin & baud rate
//run photoresistor signal wire to pin AN0
//connect LED/resistor to pin RB7
void main(){
int16 photo=0; //16 bit i nteger, safer than using int because
//int is only 8 bit which might l ead to overflow problems for add, multiply
setup_adc(ADC_CLOCK_INTERNAL); //configure analog to digiral converter
setup_adc_ports(ALL_ANALOG); //set pins AN0-‐AN7 to analog (can read values from 0 -‐255 instead of j ust 0,1)
while(true){ //loop forever
set_adc_channel(0);//set the pic to read from AN0
delay_us(20);//delay 20 microseconds to allow PIC to switch to analog c hannel 0
photo=read_adc(); //read input from pin AN0: 0 <=photo<=255
//turn on L ED when input > 127, else turn off L ED
//Put finger over photoresistor & take it off to s ee LED turn on/off
//127 may not be the actual value that separates light from dark, s o try different values
if(photo > 127){
output_high(PIN_B7);
}
else{
output_low(PIN_B7);
}
}
}
Code for multiple photoresistors
//all these # below set up the PIC
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) //crystal oscillator at 20000000 hertz
#use rs232(baud=9600, xmit=PIN_C6, invert) //serial port output pin & baud rate
//read input from 3 photoresistors
//run photoresistor signal wires to pin AN0, AN1, AN2
void main(){
int16 photo0=0; //16 bit integer, safer than using i nt
//int is only 8 bit which might l ead to overflow problems for add, multiply
int16 photo1=0;
int16 photo2=0;
setup_adc(ADC_CLOCK_INTERNAL); //configure analog to digiral converter
setup_adc_ports(ALL_ANALOG); //set pins AN0-‐AN7 to analog (can read values from 0-‐255 instead of j ust 0,1)
while(true){ //loop forever
set_adc_channel(0);//set the pic to read from AN0
delay_us(20);//delay 20 microseconds to allow PIC to switch to analog c hannel 0
photo0=read_adc(); //read i nput from pin AN0: 0 <=photo<=255
set_adc_channel(1);//set the pic to read from AN1
delay_us(20);
photo1=read_adc();
set_adc_channel(2); //set the pic to read from AN2
delay_us(20);
photo2 = read_adc();
//You could add 3 LEDs and turn them on if photo0/1/2 > 127
//just as with c ode for single photoresistor
}
}
ŶĂůŽŐ/ŶƉƵƚ;WŚŽƚŽƌĞƐŝƐƚŽƌ͕^ŽŶĂƌƐĞŶƐŽƌ͕/ZƐĞŶƐŽƌ͕͙Ϳ
Materials & Circuit
Depends on the device. For example, a sonar sensor might have 3 wires: power, ground & signal. You would
connect the signal wire to one of pins AN0-‐Eϳ͘^ĞĞƚŚĞĚĞǀŝĐĞ͛ƐĚĂƚĂƐŚĞĞƚĨŽƌŚĞůƉ͘
Code
ͬͬƚŚĞƌĞƐƚŽĨƚŚĞĐŽĚĞŝƐŝĚĞŶƚŝĐĂůƚŽĐŽĚĞĨŽƌƉŚŽƚŽƌĞƐŝƐƚŽƌŝŶƉƵƚ͕ĞdžĐĞƉƚŚĞƌĞǁĞĐĂůůŝƚ͚ƐŽŶĂƌ͛
int16 sonar = 0;
set_adc_channel(1);//set the pic to r ead from AN1
delay_us(20);
sonar=read_adc();
Notes
You can use the photoresistor tutorial code to read any sort of analog input device, as long as the device is
designed so you can run a signal wire to the input pin(s).
Remember, only the AN0-‐AN7 pins allow analog input, and you have to call setup_adc functions &
set_adc_channel as in photoresistor tutorial.
Roughly, PIC will read 0 volts as 0, and 5 volts as 255.
Digital Input (Switches)
Materials
CirĐƵŝƚĨƌŽŵ͞/ŶƚƌŽĚƵĐƚŝŽŶƚŽƚŚĞW/ϭϲ&ϴϳϳ͟
LED
Switch
1K ohm resistor
10 or 47K ohm resistor
Wire
Circuit
Pin RB7 to 1Kohm resistor. Resistor to LED. LED to ground.
Pin RD1 to 10/47Kohm resistor. Resistor to ground.
Pin RD1 to switch. Switch to power.
This circuit has Ă͞ƉƵůů-‐ĚŽǁŶƌĞƐŝƐƚŽƌ͘͟ When the switch is closed, the PIC reads 5 volts and when it is open it
ƌĞĂĚƐϬǀŽůƚƐ͘/ĨǁĞŚĂĚŶŽƉƵůůĚŽǁŶƌĞƐŝƐƚŽƌƚŚĞŶƚŚĞƉŝŶǁŽƵůĚďĞŝŶĂ͞ĨůŽĂƚŝŶŐ͟ƐƚĂƚĞǁŚĞŶƚŚĞƐǁŝƚĐŚǁĂƐ
open, meaning that the voltage can fluctuate. We need to connect the pin to ground so the PIC always reads
0 when the switch is open. If there was a wire connected to ground instead of a resistor, it would create a
short that burns up the circuit.
Code
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) // Sets crystal oscillator at 20 megahertz
#use rs232(baud=9600, xmit=PIN_C6, invert) // s erial port output & baud r ate
//close switch to s ee LED turn on
//open switch to see L ED turn off
//if the pin is low (0 v olts) x = 0, or FALSE
//if the pin is high (5 volts) x = 1, or TRUE
void main() {
int x = 0;
while(true){
x = input(PIN_D1);
if(x==1){
output_high(PIN_B7);
}
else{
output_low(PIN_B7);
}
}
}
Output messages to computer screen
I have used 2 methods.
1. PICkit 2 Development Programmer/Debugger. See their instructions in the program or website.
2. Serial port. I used HyperTerminal to read text from the serial port & display on screen, but other programs
can do that too. Make sure the baud rate is 9600 bits / sec, just like in the PIC code: #use rs232(baud=9600, xmit=PIN_C6,
invert)
Serial Port Steps (if using HyperTerminal)
1. Wire serial port pin 2 to pin TX (same as RC6) on the PIC.
2. Wire serial port pin 5 to ground.
3. Connect serial port to computer. Use a serial-‐USB adapter if computer has no serial port.
4. Open HyperTerminal.
5. ŶƚĞƌĂŶLJŶĂŵĞ͕ƐĞůĞĐƚƚŚĞŝĐŽŶƚŚĂƚƐĂLJƐ͞D/͟
6. Connect using COM5/COM13/etc. If there are multiple COMs: pull out the USB, reopen
HyperTerminal, & see which COM has vanisheĚ͘dŚĂƚ͛ƐƚŚĞŽŶĞLJŽƵǁĂŶƚ͘
7. Set bits per second = 9600.
8. HyperTerminal is set up. It will keep reading input until you close the program.
9. Load code onto PIC
10. Hit switch to turn on PIC
Code
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) // Sets crystal oscillator at 20 megahertz
#use rs232(baud=9600, xmit=PIN_C6, invert) //Sets up s erial port output pin & baud rate
void main(){
int x = 0;
while(true){
x = x + 1;
//This is an ordinary C language printf statement that will display on the screen of your PC.
//But, you need to open a special program to read s erial port input, like HyperTerminal.
//DĂŬĞƐƵƌĞƚŚĞďĂƵĚƌĂƚĞŽĨƚŚĞƉƌŽŐƌĂŵŵĂƚĐŚĞƐƚŚŝƐĐŽĚĞ͛ƐďĂƵĚƌĂƚĞ;ϵϲϬϬďŝƚƐͬƐĞĐŽŶĚͿ
printf("hello, x=%d\r\n",x); //send this text to s erial port
delay_ms(100); //wait 100 milliseconds
}
}
Servo Motor Output
Materials
ŝƌĐƵŝƚĨƌŽŵ͞/ŶƚƌŽĚƵĐƚŝŽŶƚŽƚŚĞW/ϭϲ&ϴϳϳ͟
Servo Motor (this tutorial tested on Parallax Standard Servo)
Wire
Circuit
See servo motor datasheet. Most likely it will be
1. Servo power wire to power
2. Servo ground wire to ground
3. Servo signal wire to pin RD2. (could be any I/O pin on the PIC, just change the code)
Code
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Highspeed Osc > 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#use delay(clock=20000000) // Sets crystal oscillator at 20 megahertz
#use rs232(baud=9600, xmit=PIN_C6, invert) //Sets up s erial port output pin & baud rate
//pin R D2 wired to s ervo
//servo c onnected to power, ground, and the signal wire from the PIC
//this program steps the Parallax standard s ervo slowly from 0 to 180 degrees,
//then rushes back to 0 degrees to restart.
//for code readability, c ould use #define SERVO_PIN PIN_D2 -‐-‐> output_high(SERVO_PIN);
void main(){
int16 pulse_width = 1000;
int i;
while(true){
//send short pulse to s ervo indicating which angle it should move to.
//for example, for one type of servo, 1000us=1ms indicates 0 degrees,
//while 2000us=2ms indicates 180 degrees.
output_high(PIN_D2);
delay_us(pulse_width); //sending 5 v olts to s ervo for pulse_width microseconds
output_low(PIN_D2);
delay_ms(20); //wait 20 milliseconds -‐ refresh cycle of typical s ervos
pulse_width = pulse_width + 1; //each time, servo is instructed to move a little farther
if(pulse_width == 2001){ //if s ervo reached a ngle 180, reset: it will rush back to angle 0
pulse_width = 1000;
}
/*
If want servo to go to an angle & stay there, need to send the same pulse s everal times. (50 is good)
If only send 1 pulse, the motor won't get all the way there, a nd it will stop, waiting for
another pulse. Example below s hows how to move servo to 90 degrees.
for(i=1;i<=50; i ++){
output_high(PIN_D2);
delay_us(1500); //want s ervo to move to 90 degrees.
output_low(PIN_D2);
delay_ms(20);
}
*/
}
}
Notes
Other servos may have different pulse width requirements. And, they might go only 90 degrees, or up to 360
degrees. There are also issues where servos draw too much current or waste heat, which is beyond the scope
of this tutorial.
DC Motor Output
Normally, you will use an H bridge for motor control. There are many internet articles about H bridges if you
want to learn how they work.
Materials
ŝƌĐƵŝƚĨƌŽŵ͞/ŶƚƌŽĚƵĐƚŝŽŶƚŽƚŚĞW/ϭϲ&ϴϳϳ͟
DC motor
H bridge chip
Wire
Circuit
Depends on the type of H bridge. I have used an old SN754410 quadruple half-‐h driver to run 2 motors at
once. I also used a custom H bridge circuit board designed by club president Don Crouse. See the data sheet.
Important: whenever you change the direction, you should set all motor signal lines to 0 (output_low). This
prevents short circuits.
Code & Notes
I will not include an entire program here, because each H bridge model has different rules for motor control.
The datasheet will tell you how to set the signal lines.
There are two major types of output signals on most H bridges.
The first signal turns the motor on or off. It is often called the ͞ŶĂďůĞWŝŶ͘͟tŚĞŶĞǀĞƌLJŽƵǁĂŶƚƚŽƚƵƌŶƚŚĞ
motor on, respectively off, type output_high(MOTOR_ENABLE_PIN); and output_low(MOTOR_ENABLE_PIN);
The second type are the signals indicating which direction the motor should turn, whether the motor free
runs, and whether the motor brakes. Send 0 or 1 to those control signals: output_high(MOTOR_DIRECTION_PIN_1), for
example.
A simple illustration:
Direction Pin Enable Pin Results
0 (output_low) 1 (output_high) One direction
1 (output_high) 1 (output_high) Other direction
y;ĚŽĞƐŶ͛ƚŵĂƚƚĞƌͿ 0 (output_low) Free run (slow down & stop)
Some H bridges will only let you set the motor direction. In that case, you brake by reversing the motor
direction for a short time. Beware: the current draw can spike if you do this, and the PIC might black out. But
sometimes sudden direction reversal is OK; you will have to see for yourself.
If you have set the signals to make the motor go left, it will keep going left until you explicitly tell it to stop (or
go right).
IƚŝƐĂŐŽŽĚŝĚĞĂƚŽǁƌŝƚĞĨƵŶĐƚŝŽŶƐůŝŬĞ͞ĨŽƌǁĂƌĚ͕͟͞ďĂĐŬǁĂƌĚ͕͟͞ůĞĨƚ͕͟͞ƌŝŐŚƚ͕͟͞ƐƚŽƉ͘͟dŚĂƚǁĂLJLJŽƵĐĂŶũƵƐƚ
call the function & not have to worry about the details.
Below are code excerpts from motor control for SN754410 H bridge, controlling 2 motors. Your motor control
code will NOT be the same, because you will use a different H bridge or have different needs. But it will be
similar to this.
#define LEFT_CONTROL_2A PIN_C2 //2A
#define LEFT_CONTROL_1A PIN_C1 //1A
#define LEFT_MOTOR PIN_D1 //1,2EN
#define R IGHT_MOTOR PIN_D2 //3,4EN
#define R IGHT_CONTROL_4A PIN_D5 //4A
#define R IGHT_CONTROL_3A PIN_D4 //3A
int going_forward; //current motion
int going_backward;
int going_left;
int going_right;
int going_hard_left;
int going_hard_right;
͙
͙
//brake by reversing motor direction, then turn motors off
void halt(){
if (going_forward)
backward();
else if (going_backward)
forward();
else if (going_left)
right();
else if (going_right)
left();
else if (going_hard_left)
hard_right();
else if (going_hard_right)
hard_left();
delay_ms(50); //reverse direction for 50 ms, enough to make robot stop
//but not l ong enough to make it start moving in opposite direction
resetMotorControl();
}
void forward(){
resetMotorControl();
output_high(LEFT_CONTROL_1A);
output_high(RIGHT_CONTROL_3A);
output_high(LEFT_MOTOR);
output_high(RIGHT_MOTOR);
going_forward=1;
}
void backward(){
resetMotorControl();
output_high(LEFT_CONTROL_2A);
output_high(RIGHT_CONTROL_4A);
output_high(LEFT_MOTOR);
output_high(RIGHT_MOTOR);
going_backward=1;
}
//run only 1 motor for gentler turn
void right(){
resetMotorControl();
output_high(RIGHT_CONTROL_3A);
output_high(RIGHT_MOTOR);
going_right=1;
}
//run only 1 motor for gentler turn
void l eft(){
resetMotorControl();
output_high(LEFT_CONTROL_1A);
output_high(LEFT_MOTOR);
going_left=1;
}
//run one motor forward a nd one backward for sharper turn
void hard_left(){
resetMotorControl();
output_high(LEFT_CONTROL_1A);
output_high(RIGHT_CONTROL_4A);
output_high(LEFT_MOTOR);
output_high(RIGHT_MOTOR);
going_hard_left=1;
}
//run one motor forward a nd one backward for sharper turn
void hard_right(){
resetMotorControl();
output_high(LEFT_CONTROL_2A);
output_high(RIGHT_CONTROL_3A);
output_high(LEFT_MOTOR);
output_high(RIGHT_MOTOR);
going_hard_right=1;
}
//Set all pins to H-‐bridge low to prevent s hort circuits
//Reset "current motion" flags
void r esetMotorControl(){
output_low(LEFT_MOTOR);
output_low(RIGHT_MOTOR);
output_low(LEFT_CONTROL_1A);
output_low(LEFT_CONTROL_2A);
output_low(RIGHT_CONTROL_3A);
output_low(RIGHT_CONTROL_4A);
going_forward=0;
going_backward=0;
going_left=0;
going_right=0;
going_hard_left = 0;
going_hard_right = 0;
delay_us(20);
}
Special Topic: PWM
Now you know how to run the motor at full speed. But a situation might arise where you want half speed, or
¾ speed, etc. In that case, you can send a PWM signal to the H bridge enable pin. There are many internet
articles about PWM if you want to learn how it works.
To use PWM in motor control, replace output_high(MOTOR_ENABLE_PIN) with set_pwm1_duty(x) ͘DĂŬĞƐƵƌĞƚŚĂƚƚŚĞ͞ĞŶĂble
ƉŝŶ͟ǁŝƌĞŐŽĞƐŝŶƚŽƚŚĞW/ƉŝŶWϭŽƌWϮ͘dŚŽƐĞĂƌĞƚŚĞŽŶůLJƉŝŶƐƚŚĂƚĐĂŶŚĂǀĞWtDƐŝŐŶĂů͘
PWM code excerpt:
setup_timer_2(T2_DIV_BY_4,255,1);
setup_ccp1(CCP_PWM); //set pin CCP1 as a PWM pin instead of a regular I/O pin
setup_ccp2(CCP_PWM); //set pin CCP2 as a PWM pin instead of a regular I/O pin
while(true){
//if s etting duty cycle (set_pwm1_duty), only give values between 0 -‐255
//0 means the PWM signal is always at 0 . 128 means it is 1 half the time & 0 the other half
//leading to a n averaged "1/2 power" signal. 255 means it is 1 all the time, motor full speed
set_pwm1_duty(255); //enable pin for motor 1: PIC pin CCP1
set_pwm2_duty(255); //enable pin for motor 2: PIC pin CCP2
delay_ms(3000); //full speed for 3 seconds
set_pwm1_duty(128);
set_pwm2_duty(128);
delay_ms(3000); //half s peed for 3 s econds
set_pwm1_duty(0);
set_pwm2_duty(0);
delay_ms(3000); //motors off for 3 s econds
}
//Use PWM in y our DC motor c ontrol code to c hange the s peed that the robot goes forward/backward/left/right
//call forward(255); to go forward at full s peed. Call forward(128); to g o forward at half speed.
void forward(int speed){
resetMotorControl();
output_high(LEFT_CONTROL_1A);
output_high(RIGHT_CONTROL_3A);
set_pwm1_duty(speed); //wire from PIC pin CCP1 to right motor enable pin
set_pwm2_duty(speed); //wire from PIC pin CCP2 to left motor enable pin c onnected to
going_forward=1;
}
Notes
If you want to use higher voltage DC motors, or run motors at high speeds, you might have to construct a
special circuit to deal with the extra current draw, heat, high voltage batteries, etc. DC motors have their own
set of issues to deal with, which is beyond the scope of this tutorial.