« TO-220 Hole Screws | Main | Digital Input to the PIC16F628 in C »

Blinkin' PIC (March 24, 2005)

This is a simple "welcome to the scary world of microcontrollers" circuit that counts up and down between 0 and 0xFF based on a PIC16F628, some LEDs and a 6V battery.

It could serve as a nice "welcome to the fun world of soldering", "welcome to the 2-bit world of binary", or (as I mentioned) "welcome to the scary world of microcontrollers" or, it might only be something with pretty flashing lights.

Usually we use Freescale (formerly known as Motorola) microcontrollers, but Microchip's line of PIC microcontrollers are both more common and cheaper then their Motorola friends, and we found (literally) a bucket of various PIC16F628 chips at an overstock dealer for dirt cheap.

Our application for the chips was handed to us by a higher power: "Make something that counts both up and down from 0 to FF, bring these 8 bits out to a 'standard' 16 pin DIL header." (Around here a 'standard' 16 pin DIL header is always configured to have 8 signal lines across the top, and 8 common ground lines across the bottom.) The DIL header would then be interfaced to other boards that contained such novelties as dual 7 segment displays, and triple 7 segment displays with a binary to BCD encoder, and other such wild and fun ideas. Additionally, the design should be able to drive a bank of LEDs hooked in place of the DIL header and tell the watcher if it's counting up or down.

Assuming power consumption isn't a big deal, the implementation of the code is about as simple as a microcontroller hello world gets. As implemented in C, it looks like the code snippet below. (The line about PORTA= is the result of my inability to address specific I/O pins; It causes pin 0 to output a 1 when counting up, and pin 1 to do the opposite.)

#include <pic.h> #include "delay.h" main (void) { unsigned char j; char ud = 0; TRISB = TRISA = 0; /* all bits output */ j = 0; for (;;) { PORTB = j; PORTA = (ud) | (((!ud) << 1) & 0 b10); DelayMs (200); if (j == 0) ud = 1; if (j == 0xFF) ud = 0; if (ud) j++; else j--; } }

This is all well and good if power consumption is no object, but this code causes the circuit to draw upwards of 120mA @ 6v when driving LEDs. Our 6v batteries top out at a wimpy 15mA. One easy solution is to power the LEDs with a PWM wave with a low duty cycle effectively lowering the amount of 'on time' the LEDs have. The following C code drops the current consumption to around 10mA when driving LEDs without dimming the LEDs too much. The code is identical to the above code, except the delay call has been replaced with a pair of for loops that do the PWM.

#include <pic.h> main (void) { unsigned char i, j, k; char ud = 0; TRISB = TRISA = 0; /* all bits output */ j = 0; for (;;) { for (i = 255; --i;) for (k = 64; --k;) { if ((k & 0 b111) == 0 b111) { /* LEDs on */ PORTB = j; PORTA = (ud) | (((!ud) << 1) & 0 b10); } else { /* LEDs off */ PORTB = 0; PORTA = 0; } } if (j == 0) ud = 1; if (j == 0xFF) ud = 0; if (ud) j++; else j--; } }

This code is all well and good, if you happen to only ever drive LEDs, but that sort of output really doesn't play well with other logic-level devices. A simple kludge would be to simply use a different version of code depending on your desired output, but a better solution would be a jumper allowing you to choose between the two. Because using a jumper requires reading input from the PICs I/O pins and because I couldn't manage to convince the C compiler to read input, the following code block is written in JAL.

-- counts up and down from 0 to 0xFF -- outputs count on PORTB -- outputs up/down status on A.0 and A.1 -- A.2 selects between PWM and DC output for PORTB include f628_4i include jlib var volatile byte CMCON at 0x1F = 0x07 -- disable port a analog functions var byte A is port_b var bit dir is pin_a0 var bit dir2 is pin_a1 var bit out_type is pin_a2 port_b_direction = all_output pin_a0_direction = output pin_a1_direction = output pin_a2_direction = input var byte i = 0 var bit ud forever loop var bit ot = out_type if ud then i = i + 1 if i == 255 then ud = false end if else i = i - 1 if i == 0 then ud = true end if end if if ot then A = i end if var byte oo = 0 for 50 loop for 100 loop if oo != 0 then if ! ot then -- if they're not LEDs, don't PWM them! A = 0 end if dir = false dir2 = false else if ! ot then A = i end if dir = ud dir2 = ! ud end if oo=(oo + 1) & 0b111 end loop end loop end loop

Counter Circuit

With the above code written to a microcontroller, all thats left is some hardware to make it go. The schematic on the right is exactly what's required to bring it all together. Additional resistors aren't needed on the LEDs thanks to the PICs internal current limiting resistors, which source only 2v across the LEDs. For a simple LED output of the count, 8 LEDs (or one LED DIP block) would be soldered across CON1. J2 controls the PWM output on CON1; with pins 2 and 3 connected (short to ground) or with no pins connected, the PIC will use PWM to drive LEDs connected on CON1; with pins 1 and 3 connected (short to VDD), DC voltage will appear on CON1 suitable for connecting to TTL logic level devices. Some sort of DC supply between about 4 and 6 volts should be connected to PWR.

If you can't find a bucket of PIC16F628 chips, they're available at all the usual places:
Mouser - $1.73
Digi-Key - $3.18
Microchip - $1.61


Posted by spiffed at March 24, 2005 9:23 PM