Program #1 - A Binary Counter

;-----------------------------------------------------------------------;
; BINCNT.ASM         Counts in binary on LEDs ( RB0 - RB4 )             ;
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
;         The next 6 lines are directions to the assembler              ;
;-----------------------------------------------------------------------;
        LIST P=16F84           ;  tells which processor is used
        INCLUDE "p16f84.inc"   ;  defines various registers etc. Look it over.
        ERRORLEVEL -224        ;  supress annoying message because of tris
        __CONFIG _PWRTE_ON & _LP_OSC & _WDT_OFF   ;  configuration switches

          ORG 0              ; start a program memory location zero

;-----------------------------------------------------------------------;
;         First we set up all bits of PORT A and B as outputs           ;
;         and set bits in the OPTION register concerning TMR0           ;
;-----------------------------------------------------------------------;
         movlw B'00000000'    ; all bits low in W
         tris PORTA           ; contents of W copied to PORT A ...
         tris PORTB           ; and PORT B
         movlw B'00000100'    ; pull-ups active
                              ; prescalar assigned to TMR0 and set 1:32
         option               ; rolls over each second
 
;-----------------------------------------------------------------------;
;                      This is the main program                         ;
;-----------------------------------------------------------------------;
         clrf PORTB           ; start with zero
loop:
         incf PORTB, f        ; add 1 to port B
         btfss INTCON, T0IF   ; wait on T0IF to be set
         goto $ -1
         bcf INTCON, T0IF     ; clear the interrupt flag
         goto loop

         end                  ; end of program

PIC16F84 Addresses

There are three types of addresses in the '84 depending on which type of memory we are talking about.

  • Program Memory is like ROM, (but EEPROM). Instructions are 14 bits wide and have addresses from 0 to 1023. Program memory holds these instructions. The MPASM directive 'ORG' can be used to set the program address for subsequent code. On power up the program instructions start executing beginning at address 0.
  • Register Memory is like RAM. Registers are 8 bits wide and have addresses 0-79 decimal. They hold variables or special purpose register information. The address of the registers are included as 7 of the 14 bits in instructions that refer to a register. The first 12 addresses hold registers internal to the machine like PORTA and STATUS.
  • Data Memory is EEPROM which holds values even after power is removed. It requires a complex procedure to read or write and needs 10 milliseconds or so to 'burn' the information into memory. Data addresses run from 0-63 decimal.

    Port Basics:

    The PIC communicates with the outside world through it's pins. The pins are divided into 2 ports, Port A and Port B. Port B controls 8 pins, RB0-RB7,(see top view above). Port A controls 5 pins, RA0-RA4.

    These ports are controlled through port 'registers'. Microchip likes to call these 'file' registers and assigns them the letter 'f', (in general). Learn to manipulate the registers and you learn to program the PIC. Each port has 2 registers associated with it. PORTA and PORTB are the names of the registers that show the state of the pins, high or low. TRISA and TRISB are registers that indicate which pins are inputs and which are outputs.

    The Working Register

    There is also a 'working' register 'W'. Almost everything has to go through the 'W' register to end up anywhere else. It usually take two instructions to do something. First load 'W' with something, (1), and then, (2), put the contents of 'W' into a register.

    This process is used in the above program to set up the ports. In the TRIS registers a 0 represents an output and a 1 an input. We want to set all pins to outputs. Pins set as inputs should always be pulled high or low. An input should never be left floating. Setting all pins as outputs takes care of those pins not connected to anything, (no inputs).

    MOVLW

    'movlw' stands for: move a literal, (number), into the 'W' register. We move the binary number B'00000000' into W in the first instruction. The binary representation lets you see all the bits.

    TRIS

    The 'tris' instruction takes whatever is in W and puts it into the TRIS register associated with the PORT register you specify. 'tris PORTA' puts the 0 into TRISA and 'tris PORTB' puts the same number into TRISB.

    There is another way to set up the TRIS registers that involves 'page flipping', but ignore it for now. The tris instruction is the easiest way to set up TRIS registers.

    Once the TRIS register bits are all set to outputs, the numbers we put in the PORT registers determines what levels are on the pins. 1's will make the pins high and 0's will make them low. We have LEDS on the 4 lowest bits of Port B, (RB0-RB3), so the lowest 4 bits in PORTB will determine if these are on, (1's), or off, (0's).

    CLRF

    The 'clrf PORTB' instruction turns all the LED's off. It 'clears' the register named, (sets all bits to zero). In words: 'clear the indicated register'.

    INCF

    The 'incf PORTB, f' instruction finds out what is in register PORTB, adds one to it and puts that number in PORTB. This makes the LEDs 'count' in binary. In words: 'increment the indicated register'.

    Did you notice the 'f' at the end of 'incf PORTB, f'? Many instructions can do double duty because the result can be placed either in the register indicated in the instruction, ('f'), or in the 'W' register. 'W' or 'f' is referred to as the 'destination' of the instruction. It is important to note that if the result is placed in 'W', the original register is not changed.

    Time delay

    Another register TMR0, (timer zero), is involved in setting the delay between counts. TMR0 continually increments at a certain rate. When it gets to B'11111111' the next count 'rolls over' to B'00000000'. At this time a certain bit in the register INTCON, (T0IF), is 'set', (made 1). We can watch for this to happen and know that 256 counts have passed since TMR0 was at zero. The counts happen at a rate of the crystal frequency divided by four. For the watch crystal this means every 0.122 milliseconds.

    INTCON register and T0IF

    The register INTCON has eight bits dealing with interrupts. The only one we are concerned with now is bit 2 which is the TMR0 overflow interrupt flag bit. If this bit is one, TMR0 has overflowed, if it is zero, it has not. You are responsible for resetting this flag yourself in your program once it has overflowed.

    Testing a bit in a register

    Testing a bit is done using an instruction that skips over then next instruction if the bit has a certain value. 'BTFSS' stands for: 'test the bit designated and skip the next instruction if the bit is 1, (set). If we make the next instruction a 'goto' then the program either goes to this new location if the bit is clear, (0) or continues on if it is 1. There is also the instruction 'BTFSC' which looks for a clear bit rather than a set bit. The designated bit can be a number, (0-7), or a symbol which is equated with a number such as 'T0IF' in this case. The number is assigned to T0IF in the file "p16F84.inc" which was included at the top of the program.

    Jumping around in a program

    Normally instructions are carried out in sequence. To break up this sequence and go to another location we use the 'goto' instruction. This instruction usually contains a label which tells where to go next. A variation on this is the use of '$' which stands for current instruction. Adding a -1 means go to the previous instruction, a +2 would be the one after the next and so on. The '$' convention is useful for short jumps. Longer jumps should use labels.

    The Prescaler and Option Command

    We can effectively slow down the timer by factors of 2, 4, 8, 16 .... 256 by using what is called a prescalar. This involves setting the bits of yet another register, OPTION. The OPTION register can be set by using the option command. The lowest 3 bits of OPTION designate the eight different ratios mentioned. It turns out that if we set the prescalar to 1:32, the rollover happens using a watch crytal at exactly one second. The lowest three bits should be binary '100' to give 1:32.

    On power up, all bits of OPTION are set to 1. A '1' in bit 3, PSA, (prescalar assignment bit), assigns the prescalar to the watchdog timer rather than TMR0. We definately want that one to be '0'. A '1' in bit 5, T0CS, (timer zero clock source select bit), makes the register TMR0 respond to transitions on pin 3 of the PIC, (RA4). We want that one to be zero also. In the program listing, you can see where W is set to B'00000100' and put into OPTION.

    Clearing a bit in a register"

    'bcf' stands for: 'clear a certain bit in the designated register. If we are going to see the next rollover, we have to somehow clear the flag so we can see when it is set again. There is also a 'bsf' to set bits.

    The Main Program

    The program is a continuous loop. You travel down through instructions until you reach the last which is 'goto loop' which sends you back to the instruction just after the label 'loop:'. Labels have to start in column one. Just about everything else can not start in column one.

    Now it's your turn:

    Changing the speed of the counter

    It isn't hard to change the speed of the counter in multiples of 2. Just change the lowest three bits of OPTION. '000' gives a ratio of 1:2, '001' a ratio of 1:4 and so on. Vary these bits and see what happens to the counter.

    The binary numbering system

    Does the pattern of incrementing binary numbers make sense to you? If you need a refresher on numbering systems you could try: http://www.chesworth.com/pv/technical/computing_numbers.htm Watch the counter until you are familiar with the pattern. Can you guess what the pattern of the high 4 bits of PORTB is like? A single resistor and LED can be made into a probe that you can use to examine these port pins. A low current LED is nice for this because you don't have to worry about drawing too much from the pins.

    A decrementing counter

    How would you make the numbers decrement rather than increment? There is an instruction 'decf' which could replace the 'incf'. What happens after you reach zero? Could you add instructions to make the counting start at a particular number, say 10. You can represent a decimal ten as D'10'. The same number in hexidecimal is H'A'. In binary it is B'00001010'. Remember you usually have to go through 'W'.

    Count every other number

    How would you count only even numbers, (the rightmost LED would never come on). See if you can write and run the program. How about only odd numbers?

    Stop the count at a certain number

    You could add four more LED's and count all the way to 255, but four should give you the idea of what is happening. Instead, why not stop when you reach 16, (H'10'), and start decrementing. You have seen the instruction 'btfss (register), (bit)'. See if you can use this to detect when bit 4, ( 5th from the right ), gets set and then start counting down.

    But now, what happens at zero? You then want to start incrementing again. We need an 'btfss' that detects not when a certain bit is set but when no bits are set. There is a register that has such a bit. It is the 'Z ' bit of the STATUS register which is set when an instruction gives a result of zero. So use 'btfss STATUS, Z' and write a program that continually counts up and down between 0 and 15, (or 1 and 16 or whatever).

    The 'Nightrider' display

    What if you want to 'roll' one lit LED across the display, by doing the number sequence 1,2,4,8,1,2,4,8 etc. Could you write a program to do it? There is an instruction that rolls all bits to the left one place. It is called 'rlf (register), f'. Use it to do the roll program. Then try adding btfss instructions to create the sequence 1,2,4,8,4,2,1,2,4 etc.

    Interested:

    Questions:

    clear TMR0 at the start of the interrupt routine and read it when your signal is recieved.
  • see http://www.piclist.com

    See also:

    Comments:

    Questions: