Here are some PIC assembly codes I have compiled over the years. If you'd like some explanation of how these codes work, check out my tutorials page.
Beginner:
Blink One LED
Toggle the state of RA0.
; Blink LED Example ; Simple LED blinking program for PIC16F84A ; LED connected to RA0, blinks every 500ms list p=16f84a include <p16f84a.inc> ; Configuration bits __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF ; Variables cblock 0x20 delay1 delay2 endc org 0x00 goto start org 0x04 ; Interrupt vector (not used in this example) retfie start: ; Set RA0 as output bsf STATUS, RP0 ; Select Bank 1 bcf TRISA, 0 ; Set RA0 as output bcf STATUS, RP0 ; Select Bank 0 ; Main loop main_loop: bsf PORTA, 0 ; Turn LED on call delay_500ms ; Wait 500ms bcf PORTA, 0 ; Turn LED off call delay_500ms ; Wait 500ms goto main_loop ; Repeat delay_500ms: ; 500ms delay at 4MHz crystal movlw 0x82 movwf delay1 movlw 0x7A movwf delay2 delay_loop: decfsz delay1, f goto delay_loop decfsz delay2, f goto delay_loop return end
Blink All LED
All pins of PORTB simultaneously turn on and then turn off on a loop.
; Blink LED Example ; Simple LED blinking program for PIC16F84A ; LED connected to PORTB pins, blinks every 200ms list p=16f84a include <p16f84a.inc> ; Configuration bits __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF ; Variables cblock 0x20 delay1 delay2 endc org 0x00 goto start org 0x04 ; Interrupt vector (not used in this example) retfie start: ; Set RA0 as output bsf STATUS, RP0 ; Select Bank 1 movlw 0x00 movwf TRISB ; Make all PORTB pins output bcf STATUS, RP0 ; Select Bank 0 ; Main loop main_loop: movlw 0xFF movwf PORTB ; Make all PORTB pins high call delay_500ms ; Wait 500ms movlw 0x00 movwf PORTB ; Make all PORTB pins low call delay_500ms ; Wait 500ms goto main_loop ; Repeat delay_500ms: ; 500ms delay at 4MHz crystal movlw 0x82 movwf delay1 movlw 0x7A movwf delay2 delay_loop: decfsz delay1, f goto delay_loop decfsz delay2, f goto delay_loop return end
Using a Switch
A switch is connected normally high at RA0. If the switch is pressed, RB0 is pulled low, otherwise, RB0 stays high.
![]()
list p=16f84a include <p16f84a.inc> ; Configuration bits __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF ; Variables cblock 0x20 delay1 delay2 endc org 0x00 start start: bsf STATUS,RP0 ;bank 1 movlw 0x01 movwf TRISA ;make all PORTA output except RA0 movlw 0x00 movwf TRISB ;make all PORTB output bcf STATUS,RP0 ;bank 0 main_loop: btfss PORTA,0 ;check RA0 if high, if it is, skip the next line goto sub1 ;program goes here if RA0 is low bcf PORTB,0 ;program goes here if RA0 is high; make RBO low call delay_loop ;delay subroutine goto main_loop sub1: bsf PORTB,0 ;make RB0 high call delay_loop goto main_loop delay_loop: loop1 decfsz delay1,1 ;decrement COUNT1 variable until zero goto delay_loop decfsz delay2,1 ;decrement COUNT2, if not zero, go back to loop1 goto delay_loop return end
Count Button Press (w/ Seven Segment Display):
Display on a common cathode seven segment display the number of button presses on RA0. If the number of presses exceeds nine, the counter goes back to zero.
![]()
list p=16f84a include <p16f84a.inc> ; bit order: 0b0gfedcba ; RB0=a, RB1=b, RB2=c, RB3=d, RB4=e, RB5=f, RB6=g ; Configuration bits __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF ; Variables cblock 0x20 delay1 delay2 endc org 0x00 goto start start: bsf STATUS, 5 ;goto bank 1 movlw 0x00 ;clear TRISB - set all PORTB as output movwf TRISB movlw 0x1F ;set all PORTA as input movwf TRISA bcf STATUS, 5 ;goto bank 0 clrf PORTB ; initialize PORTB to 0 main_loop: btfsc PORTA,0 ; check if RA0 button is pressed goto main_loop ; if not pressed, go back to main_loop call delay ; debounce call delay call delay btfsc PORTA,0 ; check again goto main_loop ; if not pressed, go back to main_loop ; Check current display pattern and move to next digit movf PORTB, W xorlw b'00000000' ; blank / initial state btfsc STATUS, Z goto show_0 movf PORTB, W xorlw b'00111111' ; currently 0 btfsc STATUS, Z goto show_1 movf PORTB, W xorlw b'00000110' ; currently 1 btfsc STATUS, Z goto show_2 movf PORTB, W xorlw b'01011011' ; currently 2 btfsc STATUS, Z goto show_3 movf PORTB, W xorlw b'01001111' ; currently 3 btfsc STATUS, Z goto show_4 movf PORTB, W xorlw b'01100110' ; currently 4 btfsc STATUS, Z goto show_5 movf PORTB, W xorlw b'01101101' ; currently 5 btfsc STATUS, Z goto show_6 movf PORTB, W xorlw b'01111101' ; currently 6 btfsc STATUS, Z goto show_7 movf PORTB, W xorlw b'00000111' ; currently 7 btfsc STATUS, Z goto show_8 movf PORTB, W xorlw b'01111111' ; currently 8 btfsc STATUS, Z goto show_9 movf PORTB, W xorlw b'01101111' ; currently 9 btfsc STATUS, Z goto show_0 goto show_0 ; fallback if PORTB has unknown value show_0: movlw b'00111111' movwf PORTB goto wait_release show_1: movlw b'00000110' movwf PORTB goto wait_release show_2: movlw b'01011011' movwf PORTB goto wait_release show_3: movlw b'01001111' movwf PORTB goto wait_release show_4: movlw b'01100110' movwf PORTB goto wait_release show_5: movlw b'01101101' movwf PORTB goto wait_release show_6: movlw b'01111101' movwf PORTB goto wait_release show_7: movlw b'00000111' movwf PORTB goto wait_release show_8: movlw b'01111111' movwf PORTB goto wait_release show_9: movlw b'01101111' movwf PORTB goto wait_release wait_release: btfss PORTA,0 ; wait until button is released goto wait_release call delay ; debounce release call delay goto main_loop delay: loop1 decfsz delay1,1 ;decrement COUNT1 variable until zero goto loop1 decfsz delay2,1 ;decrement COUNT2, if not zero, go back to loop1 goto loop1 return end
Intermediate
Timer Interrupt
Strobe lights from RB7 to RB0 and back using timer overflow interrupt.
![]()
list p=16f84a include <p16f84a.inc> ; Variables cblock 0x20 COUNT1 COUNT2 W_T ST_T endc org 0x00 goto start org 0x04 goto isr start: bsf STATUS, RP0 ; Switch to Bank 1 movlw 0x00 movwf TRISA ; PORTA all output movlw 0x00 movwf TRISB ; PORTB all output movlw 07h movwf OPTION_REG ; Timer0 internal clock, prescaler assigned to TMR0, 1:256 bcf STATUS, RP0 ; Switch to Bank 0 bcf INTCON,T0IF ; Clear Timer0 overflow flag first bsf INTCON,GIE ; Enable all interrupts bsf INTCON,T0IE ; Enable timer overflow interrupt movlw 0x80 movwf PORTB goto main ;---------------interrupt handler isr: bcf INTCON,GIE ;Disable interrupt inside handler movwf W_T ; Save W and STATUS reg inside the ISR swapf STATUS,W movwf ST_T btfss INTCON,T0IF goto isr_done bcf STATUS,C ; clear Carry first btfsc PORTB,0 ; check if RB0 is 1 bsf STATUS,C ; if yes, wrap it back to RB7 rrf PORTB,1 ; rotate PORTB right bcf INTCON,T0IF ; clear Timer0 overflow flag isr_done: swapf ST_T,W movwf STATUS swapf W_T, 1 swapf W_T,W bsf INTCON,GIE ;Re-enable interrupts retfie ;---------------main routine main: goto main ;---------------delay subroutine delay: movlw d'255' movwf COUNT1 movlw d'255' movwf COUNT2 loop1: decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end
RBO Interrupt:
Turn off blinking when RB0 is pressed.
![]()
list p=16f84a include <p16f84a.inc> cblock 0x20 COUNT1 COUNT2 endc org 0x00 goto start org 0x04 goto isr start: bsf STATUS, RP0 ; bank 1 movlw 0x00 movwf TRISA ; make all PORTA output movlw 0x01 movwf TRISB ; make all PORTB output except RB0 bcf STATUS, RP0 ; bank 0 movlw b'10010000' movwf INTCON ; enable GIE, INTE goto main ;-----------interrupt handler isr: bcf INTCON, GIE ; disable all interrupt inside handler bcf PORTA,0 ; make RA0 low call delay ; multiple calls to delay for longer pause call delay call delay bcf INTCON,INTF ; clear the interrupt flag bsf INTCON, GIE ; enable interrupt retfie ;-----------main routine main: call delay bsf PORTA,0 ;make RA0 high call delay bcf PORTA,0 ;make RA0 low call delay goto main ;-----------delay subroutine delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end
RB Change Interrupt:
Similar to the code above except interrupt is triggered when changes to RB4, RB5, RB6 or RB7 is made.
![]()
list p=16f84a include <p16f84a.inc> cblock 0x20 COUNT1 COUNT2 endc org 0x00 goto start org 0x04 goto isr start: bsf STATUS, RP0 ; bank 1 movlw 0x00 movwf TRISA ; make all PORTA output movlw 0x01 movwf TRISB ; make all PORTB output except RB0 bcf STATUS, RP0 ; bank 0 movlw b'10001000' movwf INTCON ; enable GIE, RBIE goto main ;-----------interrupt handler isr: bcf INTCON, GIE ; disable all interrupt inside handler bcf PORTA,0 ; make RA0 low call delay ; multiple calls to delay for longer pause call delay call delay bcf INTCON, RBIF ; clear the interrupt flag bsf INTCON, GIE ; enable interrupt retfie ;-----------main routine main: call delay bsf PORTA,0 ; make RA0 high call delay bcf PORTA,0 ; make RA0 low call delay goto main ;-----------delay subroutine delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end
EEPROM Write Interrupt:
Triggers an interrupt when the data to EEPROM has been written.
![]()
list p=16f84a include <p16f84a.inc> org 0x00 goto start org 0x04 goto isr start: bsf STATUS, RP0 ; Bank 1 clrf TRISA bsf EECON1, WREN ; Enable Write ; this is a required sequence according to the datasheet movlw 55h movwf EECON2 ; Write 55h movlw AAh ; movwf EECON2 ; Write AAh bsf EECON1,WR ; Set WR bit ; end of required sequence bcf STATUS, RP0 ; Bank 0 bcf PORTA,0 ;clear RA0 movlw 0FFh ;write 0FFh to EEDATA movwf EEDATA movlw b'11000000' ;enable EEPROM write interrupt movwf INTCON goto main ;-----------interrupt handler isr: bcf INTCON, GIE bcf INTCON, EEIE bsf PORTA,0 bsf INTCON, EEIE bsf INTCON, GIE retfie end
PWM:
Generate hardware PWM at RB3 using PIC16F628A.
![]()
list p=16F628A include <p16f628a.inc> cblock 0x20 COUNT1 COUNT2 endc org 0x00 goto init init: movlw .50 movwf COUNT1 movwf COUNT2 ;-------SET PWM FREQUENCY bsf STATUS, RP0 ; Bank 1 movlw .64 ; Set PR2 to 64 decimal; PWM frequency = 961 Hz at 4 MHz movwf PR2 bcf STATUS, RP0 ; Bank 0 movlw .32 ; Set PWM starting duty cycle to 50% movwf CCPR1L movlw b'00101100' movwf CCP1CON bsf STATUS, RP0 ; Bank 1 bcf TRISB, 3 ; Set RB3 as output for PWM bcf STATUS, RP0 ; Bank 0 movlw b'00000010' ; Set Timer2 prescale value to 16 ; so the PWM period = 2064uS => PWM frequency = 484Hz movwf T2CON clrf TMR2 ; Clear Timer2 module bsf T2CON, TMR2ON ; Enable Timer2 module main: call delay goto main delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end
Advanced
USART Communication:
Send "!" character to serial port.
![]()
list p = 16F628A include <p16f628a.inc> cblock 0x20 char0 COUNT1 COUNT2 endc org 0x00 goto init init: clrf PORTB clrf PORTA bsf STATUS, RP0 ; Bank 1 clrf TRISB ; all PORTB pins are output movlw 01h movwf TRISA ; make all PORTA pins output except RA0 movlw 07h movwf CMCON ; disable comparator modules ;---CONFIGURE SPBRG FOR DESIRED BAUD RATE movlw d'25' ; baud rate = 9600bps movwf SPBRG ; at 4MHZ ;---CONFIGURE TXSTA movlw b'00100100' movwf TXSTA ; Configures TXSTA as 8 bit transmission, transmit enabled, async mode, high speed baud rate bcf STATUS, RP0 ; Bank 0 movlw b'10000000' movwf RCSTA ; Enable serial port receive movlw 0x21 movwf char0 ; Put ! (ascii code 21) character to char0 register main: btfsc PORTA, 0 ; Check if button at RA0 is pressed goto main ; If not, wait ; Else transmit a byte movf char0, W movwf TXREG ; Place the ! character to TXREG goto wthere call delay goto main wthere: btfss TXSTA, TRMT ; Check if TRMT is empty goto wthere ; If not, check again bcf STATUS, RP0 ; Bank 0, if TRMT is empty then the character has been sent return delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end
USART - Display a Rabbit:
list p = 16F628A include <p16f628a.inc> cblock 0x20 charOpPa charClPa charBlash charSlash charUnSc charEq charSQ charDQ charPrd spc lf COUNT1 COUNT2 endc org 0x00 goto init init: clrf PORTB clrf PORTA bsf STATUS, RP0 ;bank 1 clrf TRISB ;all PORTB pins are output movlw 01h movwf TRISA ;make all PORTA pins output except RA0 movlw 07h movwf CMCON ;disable comparator modules ;---CONFIGURE SPBRG FOR DESIRED BAUD RATE movlw D'25' ;baud rate = 9600bps movwf SPBRG ;at 4MHZ ;---CONFIGURE TXSTA movlw B'00100100' movwf TXSTA ;Configures TXSTA as 8 bit transmission, transmit enabled, async mode, high speed baud rate bcf STATUS, RP0 ;bank 0 movlw B'10000000' movwf RCSTA ;enable serial port receive movlw .40 ;( movwf charOpPa movlw .41 ;) movwf charClPa movlw .92 ;\ movwf charBlash movlw .47 ;/ movwf charSlash movlw .95 ;_ movwf charUnSc movlw .61 ;= movwf charEq movlw .39 ;' movwf charSQ movlw .34 movwf charDQ movlw .46 ;. movwf charPrd movlw .32 movwf spc movlw .13 movwf lf main: btfsc PORTA, 0 ;check if button at RA0 is pressed goto main ;if not, wait ;else transmit a byte ;------------line1 movf spc, W movwf TXREG ;TO TXREG call wthere movf spc, W movwf TXREG ;TO TXREG call wthere movf charOpPa, W movwf TXREG ;TO TXREG call wthere movf charBlash, W movwf TXREG ;TO TXREG call wthere movf charUnSc, W movwf TXREG ;TO TXREG call wthere movf charUnSc, W movwf TXREG ;TO TXREG call wthere movf charUnSc, W movwf TXREG ;TO TXREG call wthere movf charSlash, W movwf TXREG ;TO TXREG call wthere movf charClPa, W movwf TXREG call wthere movf lf, W ;line feed movwf TXREG ;TO TXREG call wthere ;-------------line2 movf spc, W movwf TXREG ;TO TXREG call wthere movf spc, W movwf TXREG ;TO TXREG call wthere movf charOpPa, W movwf TXREG ;TO TXREG call wthere movf charEq, W movwf TXREG ;TO TXREG call wthere movf charSQ, W movwf TXREG ;TO TXREG call wthere movf charPrd, W movwf TXREG ;TO TXREG call wthere movf charSQ, W movwf TXREG ;TO TXREG call wthere movf charEq, W movwf TXREG ;TO TXREG call wthere movf charClPa, W movwf TXREG call wthere movlw .13 ;line feed movwf TXREG ;TO TXREG call wthere ;------------line3 movf spc, W movwf TXREG ;TO TXREG call wthere movf spc, W movwf TXREG ;TO TXREG call wthere movf charOpPa, W movwf TXREG ;TO TXREG call wthere movf charDQ, W movwf TXREG ;TO TXREG call wthere movf charClPa, W movwf TXREG ;TO TXREG call wthere movf charUnSc, W movwf TXREG ;TO TXREG call wthere movf charOpPa, W movwf TXREG ;TO TXREG call wthere movf charDQ, W movwf TXREG ;TO TXREG call wthere movf charClPa, W movwf TXREG ;TO TXREG call wthere movlw .13 ;line feed movwf TXREG ;TO TXREG call wthere call delay goto last wthere: loop22 bsf STATUS,RP0 btfss TXSTA, TRMT ;check if TRMT is empty goto loop22 ;if not, check again bcf STATUS, RP0 return delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return last nop ;so that the display will stay nop sleep end
Using Parallel LCD:
Displays HELLO on a parallel LCD (16x2). Data pins are connected to PORTB. E=RA2, RW=RA1, RS=RA0
![]()
list p=16f84a include <p16f84a.inc> cblock 0x0c TEMP TEMP2 DADDR COUNT1 COUNT2 CHAR1 CHAR2 CHAR3 CHAR4 CHAR5 CHAR_COUNT endc LCD_DATA equ 0x06 ; LCD_DATA wired to PORTB org 0x00 goto init init: movlw 0x05 movwf CHAR_COUNT movlw 0x48 ;H movwf CHAR1 movlw 0x45 ;E movwf CHAR2 movlw 0x4C ;L movwf CHAR3 movlw 0x4C ;L movwf CHAR4 movlw 0x4F ;O movwf CHAR5 bsf STATUS, RP0 clrf TRISB clrf TRISA bcf STATUS, RP0 call LCD_prep call delay movlw 0x80 call LCD_cmd call delay main: call LCD_sendchr goto end_here LCD_sendchr: movf CHAR1, W call send_it movf CHAR2, W call send_it movf CHAR3, W call send_it movf CHAR4, W call send_it movf CHAR5, W call send_it return send_it: call delay call LCD_putch call delay return LCD_poll: bsf STATUS, RP0 clrf TRISB comf TRISB, F bcf STATUS, RP0 bcf PORTA, 0 bsf PORTA, 1 bsf PORTA, 2 movf LCD_DATA, W movwf TEMP2 nop bcf PORTA, 2 btfsc TEMP2,7 goto LCD_poll bsf STATUS, RP0 clrf TRISB bcf STATUS, RP0 return LCD_cmd: movwf TEMP call LCD_poll bcf PORTA, 0 bcf PORTA, 1 bsf PORTA, 2 movf TEMP, W movwf LCD_DATA nop bcf PORTA, 2 return LCD_prep: clrf PORTA movlw 0x3C ;Configure to 2 x 40, 8-bit mode call LCD_cmd movlw 0x0F ;Turn on display and cursor call LCD_cmd movlw 0x14 ;Shift cursor right call LCD_cmd movlw 0x01 ;clear cursor and return to home position call LCD_cmd return get_DDRAM: bsf STATUS, RP0 clrf TRISB comf TRISB, F bcf STATUS, RP0 bcf PORTA, 0 bsf PORTA, 1 bsf PORTA, 2 movf LCD_DATA, W nop bcf PORTA, 2 movwf DADDR return LCD_putch: movwf TEMP call LCD_poll movf TEMP,W bsf PORTA, 0 bcf PORTA, 1 bsf PORTA, 2 movwf LCD_DATA nop bcf PORTA, 2 return chk_0x53: movlw 0x53 subwf DADDR, F btfsc STATUS,Z goto chk_0x27 movlw 0x94 ;Set DDRAM address to 0x14 (start of 3rd line) call LCD_cmd call LCD_poll movf TEMP,W bsf PORTA, 0 bcf PORTA, 1 bsf PORTA, 2 movwf LCD_DATA nop bcf PORTA, 2 return chk_0x27: movlw 0x27 subwf DADDR, F btfsc STATUS,Z return movlw 0xD4 call LCD_cmd call LCD_poll movf TEMP, W bsf PORTA, 0 bcf PORTA, 1 bsf PORTA, 2 movwf LCD_DATA nop bcf PORTA, 2 return delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return end_here: nop nop end
Dual Seven Segment w/ Lookup:
Uses persistence of vision (POV) to digits on a dual seven segment display.
![]()
list p=16f84a include <p16f84a.inc> cblock 0x20 COUNT1 COUNT2 endc org 0x00 goto start start: bsf STATUS,5 movlw 0x00 movwf TRISA movlw 0x00 movwf TRISB bcf STATUS,5 movlw .100 movwf COUNT1 movwf COUNT2 main: movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x00 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x01 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x02 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x03 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x04 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x05 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x06 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x07 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x08 call table movwf PORTB call delay call delay call delay call delay movlw 0x02 movwf PORTA movlw 0x00 call table movwf PORTB call delay movlw 0x01 movwf PORTA movlw 0x09 call table movwf PORTB call delay call delay call delay call delay goto main delay: loop1 decfsz COUNT1,1 goto loop1 decfsz COUNT2,1 goto loop1 return table: addwf PC,F retlw b'00111111' ;digit 0 retlw b'00000110' ;digit 1 retlw b'01011011' ;digit 2 retlw b'01001111' ;digit 3 retlw b'01100110' ;digit 4 retlw b'01101101' ;digit 5 retlw b'01111101' ;digit 6 retlw b'00000111' ;digit 7 retlw b'01111111' ;digit 8 retlw b'01101111' ;digit 9 end




