;***************************************************************************: ; BINCNT.ASM Counts in binary on LEDs ( RB0 - RB4 ) ; ;***************************************************************************; ; Circuit: PIC16F84 ; .-----U-----. ; -| RA2 RA1 |- G = Ground ; -| RA3 RA0 |- ; -| RA4 O1 |-X X = 32.768 kHz crystal ; +--| MCLR O2 |-X 33pf caps to + (or GND) ; .-------------GND--| Vss Vdd |--+ ; |-----LED+---R-----| RB0 RB7 |- + = 4.5V battery ; |-----LED+---R-----| RB1 RB6 |- ; |-----LED+---R-----| RB2 RB5 |- R depends on LED's ; '-----LED+---R-----| RB3 RB4 |- ( 470 for normal, ; '-----------' 2.2K for low current) ; the next 5 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 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 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. 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' 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. 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 instructions are the easiest way to set up TRIS registers. Once the TRIS registers 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). The 'clrf PORTB' instruction turns all the LED's off. 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, these instructions are 'clear the indicated register' and 'increment the indicated register'. 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 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. We can slow this down by factors of 2, 4, 8, 16 .... 256 by using what is called a prescalar. This involves setting of yet another register, OPTION. The OPTION register can be set by using the option command. More of this is described in 'TMR0.TXT'. It turns out that if we set the prescalar to 1:32, the rollover happens using a watch crytal at exactly one second. In the program listing, you can see where W is set to a certain value and that value is placed in OPTION. The main program: You can see that 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 cannot start in column one. Another way to jump around is shown with the instruction 'goto $ -1' which means 'go to the location of the current instruction less one'. This is used in waiting on the rollover to happen and T0IF, (timer 0 interrupt flag), to be set. This is because the prior instruction is 'btfss INTCON, T0IF' which in words is 'test the bit called T0IF in the register INTCON and if it is set, skip over the next instruction, ('goto $ -1'). The only instruction we haven't mentioned is 'bcf INTCON, T0IF'. 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. 'bcf' means 'clear the bit indicated in the register indicated'. One final point. 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. It is important to note that if the result is placed in 'W', the original register is not changed. Now it's your turn: 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 How would you make the numbers decrement rather than increment? 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'. 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? 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 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). 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.