First PICMicro Project


First PICMicro Project

first.jpg (23199 bytes)
In the past two weeks, I discussed the prerequisits for starting your first application and the issues surrounding PICMicro programmers and what to look for. With this background, I am now ready to present what I think is a good first application for somebody learning about the PICMicro. This circuit is not as simple as what I started with in "Programming and Customizing the PIC Microcontroller", but can be used in the same way to experiment with different ideas and trying out new concepts.
The application itself consists of the 16F84 PICMicro (with power, reset and clocking "core") with two LEDs and a button. One of the LEDs is lit by the PICMicro while the other is lit when the button is pressed.
         Circuit

Bill of Materials
Part Number

R1
R2-R3
C1
C2
U1
U2
CR1-CR2
SW1
--
--
--

Y1
Value/Comments

4.7K 0.25 Watt Resistor
220 Ohm 0.25 Watt Resistor
10 uF 25 Volt Electrolytic Capacitor
0.1 uF 16 Volt Tantalum Capacitor
78L05 Voltage Regulator
PIC16F84 Microcontroller
5 mm Red LEDs
"Momentary On" SPST Pushbutton
Prototype Card
Wiring
Power Supply (> 6 Volts if Voltage Regulator used)
4MHz Ceramic Resonator (With Internal Capacitors)
When I built the circuit, I put it on a spring loaded "Breadboard" as is shown at the top of the article. I was able to put on the Voltage regulator at one end of the breadboard with the "red" commoning bar as positive voltage and the blue bar as ground. I used a +12 Volt "Wall Wart" for powering my prototype, but this circuit could be powered by a 9 Volt Alkaline radio battery. As I pointed out in the earlier articles, three "AA" 1.5 Volt Alkaline batteries could be wired in series to provide power as well.

For my prototype, I used a 4 MHz "Ceramic Resonator" for clocking. For this circuit, the operating frequency is not important. If you haven't used a Ceramic Resonator before, I highly recommend that you look into them because they are a) easier to wire than a crystal and a couple of capacitors and b) are much more robust than a crystal, which means that you will be able to abuse them more and not loose your investment.
The LEDs are lit when the Microcontroller pins that they are connected to are pulled to ground. This is traditional in Microcontroller applications because in most devices, the "0" can "sink" more current than the "1" can "source". The PICmicro is capable of sourcing enough current to drive an LED, but I prefer using the sink feature because this is the only way that the 8051 can light LEDs directly and this eliminates the need for me to convert mentally how applications work for different devices.
The button connection probably seems unreasonably simple because I have not provided a "pull up" for the switch to pull down. In the interests of keeping the circuit simple, I rely on the internal pull ups in "PORTB" of the PICMicro to provide this function for me. A 4.7K Resistor connected between Vdd and the PICMicro's I/O pin could also be used if the internal pull ups are not used.
There is one point to make about the layout that you should always keep in mind when developing a circuit with a microcontroller or other programmable device; you should always design the circuit in such a way that the chip can be pulled easily. This means that there should be space at either end for prying the chip up out of its socket or, as I like to do, slide a small screwdriver underneath the PICMicro and "pop" it up simply by twisting it. I know a lot of people that end up cursing themselves because they've forgotten to remember this basic operation. In keeping with this theme, when using a "breadboard", I like to put in a connection between the row of holes above the Microcontroller. This allows me to pull the part without having to figure out where it goes back each time.
The application code is quite simple and can be blocked out as:

main()              // First Application, Light 1st LED and 
{                   // Light 2nd LED when Button Pressed

 LED_1 = on;        // Turn on the 1st LED

 while ( 1 == 1 )   // Loop forever
 if ( Button == Pushed )
 LED_2 = on;
 else
 LED_2 = off;

}                   // End First Application

                                                       
To provide this function, I created the PICMicro Assembly code:



 title  "First Application - Light an LED and Poll a Button."
;
;  This Code Simply Lights an LED and polls a button and turns
;  on another LED when it is pressed.
;
;
;  Hardware Notes:
;   Reset is tied directly to Vcc and PWRT is Enabled.
;   The PIC is a 16F84 Running at any speed.
;   PortB.0 is Connected to a Button that pulls down to Ground when Pressed
;   PortB.1 is Connected to an LED which is Pulled to Ground ("On" LED)
;   PortB.2 is Connected to an LED which is Pulled to Ground ("Button" LED)
;
;   Myke Predko
;   99.05.18
;
  LIST P=16F84, R=DEC
  errorlevel 0,-305
  INCLUDE "p16f84.inc"

;  Register Usage
 CBLOCK 0x00C                   ;  Start Registers at End of the Values
 ENDC


 PAGE
 __CONFIG _CP_OFF & _XT_OSC & _PWRTE_ON  & _WDT_OFF
                                ;  Note that the WatchDog Timer is OFF
;  First Application - Turn on LED and poll the Button.

  org    0

  movlw  0x0FD                  ;  Turn on LED on RB1 When Output Enabled
  movwf  PORTB

  bsf    STATUS, RP0
  bcf    TRISB ^ 0x080, 1       ;  Enable RB1 for Output
  bcf    TRISB ^ 0x080, 2       ;  Enable RB2 for Output
  movlw  0x07F                  ;  Enable Internal Pull-Ups
  movwf  OPTION_REG ^ 0x080
  bcf    STATUS, RP0

Loop

  btfsc  PORTB, 0               ;  Is the Button Pressed?
  goto  ButtonUp                ;   No, Light Off

  bcf    PORTB, 2               ;  Turn Light On

  goto   Loop

ButtonUp                        ;  Button Up, Turn Light Off

  bsf    PORTB, 2

  goto   Loop


  end

                                                       
This code can be downloaded by clicking HERE.

The body of the assembler code should match the "C" pseudo code given above, but the initial code,

 
  movlw  0x0FD                  :   Turn on LED on RB1 When Output Enabled
  movwf  PORTB

  bsf    STATUS, RP0
  bcf    TRISB ^ 0x080, 1       ;   Enable RB1 for Output
  bcf    TRISB ^ 0x080, 2       ;   Enable RB2 for Output
  movlw  0x07F                  ;   Enable Internal Pull-Ups
  movwf  OPTION_REG ^ 0x080
  bcf    STATUS, RP0

                             

is probably a bit confusing. To help explain what I have done, I wanted to explain a few things. The first is, in the PICMicro, upon power up, the "TRIS" ("TRI-State Enable" registers, which control the Input/Output operation of the PICmicro's I/O registers) have all their bits "set" (equal to "1") which puts the I/O pins into "input" mode. To put the pins into "output" mode, I simply "reset" (make equal to "0") the appropriate pins of the TRIS registers.
The TRIS (and "Option") Registers are located in "Bank 1" of the PICMicro 16F84's register space. Instead of resetting individual bits, I could have written full bytes to the registers like:


  bsf    STATUS  RP0         ;  Point to "Bank 1"

  movlw  0x0F9               ;  Make RB1 and RB2 output
  movwf  TRISB ^ 0x080

  movlw  0x07F               ;  Enable the Internal Pull Ups
  movwf  OPTION_REG ^ 0x080 

  bcf    STATUS, RP0         ;  Return to "Bank 0"

                                                       
This works just as well. When the PICMicro first boots up, the "OPTION" register has all its bits set (like the TRIS registers) which means that to just enable the internal pull ups the instruction:

  bcf    OPTION_REG ^ 0x080, 7
                                                       
could be used.
A common question from people just learning about the PICMicro and see other people's code is the use of "^ 0x080" when writing to "Bank 1" registers. In the mid-range PICMicros, up to 128 addresses can be accessed, which requires seven addressing bits. A byte is eight bits which leaves an additional bit for selecting between "Bank 0" and "Bank 1". Because the TRIS and OPTION registers are in Bank 1, this bit is normally set in the register addresses in the Microchip provided register definition files.
If bit seven of the address is set with an instruction, then the MPLAB assembler returns a "warning" to the user. This warning can be masked out using the "error" directive (like I use in the code above), but the accepted way of eliminating this problem is to set "RP0" of the STATUS register (making accesses access Bank 1 registers) and then XOR ("^") bit seven.
The reason for this convention is to add the "^ 0x080" characters to all addressed instructions between the "bsf STATUS, RP0" and "bcf STATUS, RP0" instructions and if any registers which are not in Bank 1 are accessed, the assembler will come back with a warning indicating that there is a register which is incorrectly accessed.
Another option is to use the "TRIS" and "OPTION" instructions which move the data bytes in the "w" (accumulator) register directly in to the TRIS and OPTION registers. Using these instructions would eliminate the need for the "bsf/bcf STATUS, RP0" instructions that I use above. The equivalent code would become:

  movlw  0x0F9   ;  Make RB1 and RB2 output
  TRIS   PORTB

  movlw  0x07F   ;  Enable the Internal Pull Ups
  OPTION 

                                                       
These instructions are carry overs into the "mid-range" architecture from the "low-end" PICMicro architecture and Microchip indicates that these instructions may disappear in future products. The only real disadvantage to using these instructions is that the contents of the TRIS and OPTION registers cannot be read back. There is some debate over whether or not the instructions will ever disappear, so whether or not you use them, is up to you.
To develop the application, I used "MPLAB". If you haven't downloaded this excellent tool yet, then I highly recommend that you do so. Along with providing a simple "Windows-style" editor and and assembler, the tool also provides a source-code compatible simulator that is very easy to work with.

Even though this application is quite simple, I did set up a "watch" window" along with the "asynchronous inputs" to test out the application before I burned it into a 16F84.
Simulating an application before burning a part is probably the most effective way of ensuring the application will work when you first apply power to it. By simulating the application and testing out its function, you are eliminating one variable in the list of possible problems why an application won't work.
MPLAB is pretty easy to figure out how it works and includes a simple tutorial. If you are interested in learning more about it and how I work with it, please send me an email and I can answer the questions in a future article.
I wanted to end up this discussion on a developer's first application by discussing different ways the code could be written. I think I've beaten the different ways of setting the TRIS and OPTION registers appropriately, but there is also a number of different ways in which the mainline code could be written.
For example, instead of:

Loop

  btfsc  PORTB, 0          ;  Is the Button Pressed?
  goto   ButtonUp          ;   No, Light Off

  bcf    PORTB, 2          ;  Turn Light On

  goto   Loop

ButtonUp                   ;  Button Up, Turn Light Off

  bsf    PORTB, 2

  goto   Loop

                                                       
which sets or resets the button state LED according to the button's state, I could have coded this section as:

Loop

  movf   PORTB, w          ;  Set/Reset RB2 According to Button
  iorlw  0x004

  btfss  PORTB, 0
  andlw  0x0FB             ;  Turn on the LED if the Button is Low

  movwf  PORTB

  goto   Loop

                                                       
Which executes in the same number of cycles for each loop (the original doesn't).
You might be tempted to simplify this to:

Loop

  bsf    PORTB, 2          ;  Set the Bit Assuming Button is up

  btfss  PORTB, 0
  bcf    PORTB, 2          ;  Reset the Bit if Button Pressed
  
  goto   Loop

                                                       
which will give you some interesting and probably unexpected results. Why don't you try it and figure out what is actually happening here?
In the PICMicro, like all programming situations, there are a lot of different ways to do something. My way of doing something is really my way, based on previous experiences and my way of thinking. Your way of thinking and previous experiences may be different and lead you to different or better ways of doing things. I highly recommend that when you start out in any new programming situation, that you try different ways of attacking a problem and writing your application code.
Not only will you learn much faster what is the best way to do things in a given situation but you will become an expert in finding problems much faster.
Just to follow up on last week's article about programmers and to show how an in-circuit programmer, such as my "YAP" can be of advantage. Using the YAP, Just the two LEDs and button have to be wired into the circuit because the YAP provides the "core" (power, clocking and reset) functions. This simplifies the circuit assembly by quite a bit as the following picture shows:

yap_ex.jpg (35591 bytes)
For this circuit, I was able to wire up the two LEDs, button and Vcc/Ground connections in literally two minutes. While this isn't a huge savings in effort (it probably took me ten minutes to wire the circuit into the bread board as shown at the top of this article), it does have the advantage of not requiring the developer to pull the device (and create a circuit which has access to be able to pull the device) every time they want to try something new (like I presented above). Unless you have a "ZIF" ("Zero Insertion Force") socket, this operation can be tedious and potentially damaging to the chip. Because the "YAP" leaves the PICMicro in place, there is less opportunity for the PICMicro to be damaged.
The simple circuit and code that I've shown here should really be the culmination of a lot of work. To get to this point, you should have become familiar with the following topics:
1. PICMicro device execution with power, clocking and reset
2. The PICMicro processor architecture and instruction operation
3. Code development tools and application simulating
4. device programming
5. PICMicro application debug
This is not a trivial amount of learning to do before you can develop your own application successfully. As well, depending on the complexity of the application, the amount of information to be worked through can be considerably more.
The circuit provided in this article can be used as a basis for other applications. For example, the execution "core" used, tested and wired in this circuit could be used for the LCD applications (the 2-wire interface and walking man) I presented in the first two articles.
email Questions and Comments
I received a couple of questions and comments this week about my LCD articles that I wanted to discuss. The first is a pointer from the PICList about the operation of Hitachi 44780 based LCDs. In my article (and on my LCD web page), I indicated that the 44780 was level triggered and the "E" Strobe line (used to latch in data/start an LCD operation) had to be "high" (logic "1") for at least 450 nsecs.
This seems to be not quite true. The comments both on the PICList and from Tom McGahee indicate that the Strobe is actually negative level (from "high" to "low") active. This makes sense with the 2-wire LCD interface I presented a few weeks ago because when the least significant bit is set, then the "E" Strobe will be high without ever going low before the data is strobed in.
This information doesn't change the code presented for the 2-wire LCD interface, but helps explain why there isn't a problem. To be on the safe side, always make sure that there is a 450 nsec or more delay between a line changing state and the "E" Strobe going low.
The other question, from Marcelo Cesar Asti was based on the 2-wire LCD code and how to use the "#DEFINE" information to reset the TRIS register bits for the output bits. If you look at "2wirelcd.asm", I "hardcoded" the TRIS bit writes with:

   movlw  0x01C                ;  Enable PA0 & PA1 for Output
   bsf    STATUS, RP0
   movwf  TRISA ^ 0x080
   bcf    STATUS, RP0
  
                                                       
This code will not allow the "#DEFINES" to be changed for arbitrary I/O bits to be used for the 2-wire LCD interface.
Instead, the code:

   bsf    STATUS, RP0
   bcf    Data                  ;  Clear the "Data" & "Clock" bits 
   bcf    Clock                 ;   in the TRIS Registers
   bcf    STATUS, RP0

                                                        
should be used. As you probably know, setting the "RP0" bit of the "STATUS" Register will move all the register accesses into the "Page 1" set, where the "TRIS" Registers reside. This means that the 16F84's "TRIS" Registers can be accessed directly, similarly to the I/O bits. Sorry for missing this very important point.
Next week, I will take a look at "debouncing" button input for both the PICMicro as well as the Basic Stamp.
Myke
Copyright and Warranty Statement

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Engr.Arif Naseem | Bloggerized by Naseem - | Affiliate Network Reviews