1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Basic Digital I/O Setup Template for 16F887 in ASM

Discussion in 'Microcontrollers' started by Jon Wilder, Aug 9, 2011.

  1. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    For those who are new to the 16F887, this is an example ASM code template complete with initialization routine, interrupt handler placement, as well as a fixed 100mS and a variable delay routine. Use it and make changes as you see fit. As shown the config bits are set up for an 8MHz internal oscillator. For the sake of the example, I've placed the minimum needed instructions to set up ports A, B and E as digital output with ports C and D as digital input.

    I have also included data EEPROM write, read and verify subroutines as well.

    This init code has been fully tested and debugged. This particular code as shown, while non-functional until you add in your code, has also been test assembled and it assembled with no errors, messages or warnings.

    Code (text):

    ;*********************************************************************************************************
    ;**                                                 **
    ;**         Basic Digital I/O Setup Template for 16F887                 **
    ;**         By: Jon Wilder                                  **
    ;**         Date: 8/9/2011                                  **
    ;**                                                 **
    ;*********************************************************************************************************


    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Header Information                          **
    ;**                                                 **
    ;*********************************************************************************************************

            list        p=16F887, r=dec, w=-302
            include     <P16F887.INC>
            __config    _CONFIG1,b'1110000011100100'
            __config    _CONFIG2,b'1111100011111111'

    ;config word 1_____                                  ___   __         _____
    ;|  -  |  -  |DEBUG|FCMEN|IESO | LVP |BOREN1|BOREN0| CPD | CP  |MCLRE|PWRTE|WDTE |FOSC2|FOSC1|FOSC0|
    ;|  1  |  1  |  1  |  0  |  0  |  0  |  0   |  0   |  1  |  1  |  1  |  0  |  0  |  1  |  0  |  0  |

    ;config word 2
    ;|  -  |  -  |  -  |  -  |  -  |WRT0 | WRT1 |BOR4V |  -  |  -  |  -  |  -  |  -  |  -  |  -  |  -  |
    ;|  1  |  1  |  1  |  1  |  1  |  0  |  0   |  0   |  1  |  1  |  1  |  1  |  1  |  1  |  1  |  1  |

    ;Processor is PIC16F887 (p=16F887)
    ;Default Radix is Decimal (r=dec)
    ;Suppress all bank select assembler messages (w=-302)

    ;In Circuit Debug Mode Off (DEBUG)
    ;Fail Safe Clock Monitor Bit Off (FCMEN)
    ;Internal External Switchover Bit Off (IESO)
    ;Low Voltage Programming Off (LVP)
    ;Brown Out Reset Off (BOREN1-BOREN0)
    ;Data Code Protection Off (CPD)
    ;Program Code Protection Off (CP)
    ;RE3 is External Master Clear (MCLRE)
    ;Power Up Timer On (PWRTE)
    ;Watchdog Timer Off (WDTE)
    ;Internal Oscillator RA6-RA7 digital I/O

    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Variable Declarations                           **
    ;**                                                 **
    ;*********************************************************************************************************

    ;declare variables here

            cblock      0x20
                    COUNT1      ;delay counter 1
                    COUNT2      ;delay counter 2
                                    COUNT3          ;delay counter 3
                    <Delete this line, then and add more variable names if you need more>
            endc

    ;software stack

            cblock      0x70
                    W_TEMP      ;software stack for interrupt handler
                    STATUS_TEMP ;software stack for interrupt handler
                    PCLATH_TEMP ;software stack for interrupt handler
                    DATA_EE_ADDR    ;data EEPROM address buffer
                    DATA_EE_DATA    ;data EEPROM data buffer
            endc


    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Place Non-Relocatable Code                          **
    ;**                                                 **
    ;*********************************************************************************************************

            org     0x000       ;reset vector address
            goto        START
       
            org     0x004       ;interrupt vector address
    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Interrupt Handler                           **
    ;**                                                 **
    ;*********************************************************************************************************

    ISR     ;software stack    
            movwf       W_TEMP      ;back up W
            swapf       STATUS,W    ;back up STATUS
            movwf       STATUS_TEMP
            banksel     0x00        ;bank 0
            movfw       PCLATH      ;back up PCLATH
            movwf       PCLATH_TEMP

    ;       <Delete this line and place rest of Interrupt Handler code here>

    ISRExit     ;software stack
            movfw       PCLATH_TEMP ;restore PCLATH
            movwf       PCLATH
            swapf       STATUS,W    ;restore STATUS
            movwf       STATUS
            swapf       W_TEMP,F    ;restore W
            swapf       W_TEMP,W
            retfie              ;done with interrupt



    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Initialization Routine                          **
    ;**                                                 **
    ;*********************************************************************************************************

    START       clrf        INTCON      ;disable interrupts
            banksel     OSCCON      ;bank 1
            movlw       b'01110000' ;8MHz Internal Oscillator
            movwf       OSCCON
            btfss       OSCCON,HTS  ;is oscillator stable?
            goto        $-1     ;no, check again
            banksel     ANSEL       ;yes, bank 3
            movlw       b'00000000' ;all ports digital I/O
            movwf       ANSEL
            movwf       ANSELH
            banksel     PORTA       ;bank 0
            clrf        PORTA       ;initialize all ports
            clrf        PORTB
            clrf        PORTC
            clrf        PORTD
            clrf        PORTE
            banksel     TRISA       ;bank 1
            movlw       b'00000000' ;ports A, B and E outputs
            movwf       TRISA
            movwf       TRISB
            movwf       TRISE
            movlw       b'11111111' ;ports C and D inputs
            movwf       TRISC
            movwf       TRISD
            banksel     PORTA       ;bank 0

    ;*********************************************************************************************************
    ;**                                                 **
    ;**                  Main Program                           **
    ;**                                                 **
    ;*********************************************************************************************************

    MAIN        <Delete this line and place the first line of your main program code here>
            goto        MAIN        ;last line of code (can change where it jumps to)

    ;*********************************************************************************************************
    ;**                                                 **
    ;**                  Delay Routines                         **
    ;**                                                 **
    ;*********************************************************************************************************

    ;fixed 100mS delay

    Delay_100mS movlw       0xFF        ;init delay counter 1 and 2
            movwf       COUNT1
            movwf       COUNT2
            decfsz      COUNT1,F    ;decrement counter 1 to 0
            goto        $-1     ;continue decrementing counter 1 if counter 1 > 0
            decfsz      COUNT2,F    ;decrement counter 2
            goto        $-3     ;continue decrementing counter 1 if counter 2 > 0
            return              ;done

    ;variable delay - load a value into W prior to calling this delay for a variable delay in 100mS increments
    ;value of 0x05 yields a 500mS delay

    Delay       movwf       COUNT3      ;init delay counter 2
            call        Delay_100mS ;run 100mS delay
            decfsz      COUNT3,F    ;decrement counter 2
            goto        $-2     ;jump back to run 100mS delay if counter 2 > 0
            return              ;done

    ;*********************************************************************************************************
    ;**                                                 **
    ;**             Other Subroutines                           **
    ;**                                                 **
    ;*********************************************************************************************************

    ;write address and data to write should already be loaded into EEADR and EEDATA buffers
    ;prior to calling write

    EEWrite     bcf     INTCON,GIE  ;disable interrupts
            banksel     EEADR       ;bank 3
            movfw       DATA_EE_ADDR    ;move contents of EEADR buffer to W
            movwf       EEADR       ;load address into EEADR
            movfw       DATA_EE_DATA    ;move contents of EEDATA buffer to W
            movwf       EEDAT       ;load data into EEDATA
            banksel     EECON1      ;bank 4
            bcf     EECON1,EEPGD    ;select data EEPROM for write
            bsf     EECON1,WREN ;write mode enable
            movlw       0x55        ;unlock codes
            movwf       EECON2
            movlw       0xAA
            movwf       EECON2
            bsf     EECON1,WR   ;initiate write
            btfsc       EECON1,WR   ;is write complete?
            goto        $-1     ;no, check again
            bcf     EECON1,WREN ;yes, disable write mode
            banksel     PIR2        ;bank 0
            bcf     PIR2,EEIF   ;clear EEPROM write interrupt flag
            bsf     INTCON,GIE  ;enable unmasked interrupts
           

            return              ;done

    ;read address should already be loaded into EEADR buffer prior to calling read

    EERead      bcf     INTCON,GIE  ;disable interrupts
            banksel     EEADR       ;bank 3
            movfw       DATA_EE_ADDR    ;move read address to W
            movwf       EEADR       ;move read address to EEADR
            banksel     EECON1      ;bank 4
            bcf     EECON1,EEPGD    ;select data EEPROM
            bsf     EECON1,RD   ;initiate read
            banksel     EEDAT       ;bank 3
            movfw       EEDAT       ;move EEPROM data into W
            movwf       DATA_EE_DATA    ;move data to EEPROM data buffer
            banksel     0x00        ;bank 0
            bsf     INTCON,GIE  ;enable all unmasked interrupts
            return              ;done

    ;verify address should already be loaded into EEADR prior to calling verify

    EEVerify    bcf     INTCON,GIE  ;disable interrupts
            banksel     EEADR       ;bank 3
            movfw       DATA_EE_ADDR    ;move verify address to W
            movwf       EEADR       ;move verify address to EEADR
            banksel     EECON1      ;bank 4
            bcf     EECON1,EEPGD    ;select data EEPROM
            bsf     EECON1,RD   ;initiate read
            banksel     EEDATA      ;bank 3
            movfw       EEDATA      ;move EEPROM data into W
            xorwf       EEDATA,W    ;compare to data in EEDATA
            btfss       STATUS,Z    ;is data in W equal to data in EEDATA?
            goto        EEErr       ;no, set EEPROM error
            banksel     0x00        ;bank 0
            bsf     INTCON,GIE  ;enable unmasked interrupts
            return              ;done


    EEErr       <Delete this line and place EEPROM Verify error code here (write it yourself)>

            <Delete this line and place all subroutine codes here>

            end             ;end of file
     
     
    Last edited: Aug 13, 2011
  2. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,151
    Likes:
    907
    Location:
    Rochdale UK
    ONLINE
    Excellent Jon... That's good enough for Nigels page... I particularly like the config table / descriptions.

    Good share!!!
     
  3. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    Nice effort Jon.

    I'm on my way out the door so perhaps someone else will jump in and explain why the Microchip template uses the shared memory region at 0x70..0x7F for interrupt context variables.

    Regards, Mike
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA

    More than likely because those registers are mapped in all 4 banks so you can always access them no matter what bank you're in. I always forget about those registers.

    In any event I've changed the code above to move the software stack down to those registers.
     
    Last edited: Aug 10, 2011
  6. Nigel Goodwin

    Nigel Goodwin Super Moderator Most Helpful Member

    Joined:
    Nov 17, 2003
    Messages:
    39,208
    Likes:
    640
    Location:
    Derbyshire, UK
    ONLINE
    As Jon says, so they can be accessed from any bank. It's usual to have an instruction in the ISR to reset to bank 0 - the original bank is restored on exit of course.

    Yet another reason to move the enhanced 16F1627 series - automatic context saving :D
     
  7. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    Heh...a writable stack would also be a nice convenience as you'd be able to push those values onto the stack upon entering the interrupt, then pop them off the stack upon returning like you'd do on the MCS-51 family. Then again vectored interrupts would also be nice (yes yes I know...migrate to the 18F series).
     
    Last edited: Aug 10, 2011
  8. Nigel Goodwin

    Nigel Goodwin Super Moderator Most Helpful Member

    Joined:
    Nov 17, 2003
    Messages:
    39,208
    Likes:
    640
    Location:
    Derbyshire, UK
    ONLINE
    Rather more complicated than the 16F1267 where it just saves and restores them automatically :D
     
  9. JimB

    JimB Super Moderator Most Helpful Member

    Joined:
    Sep 11, 2004
    Messages:
    6,324
    Likes:
    585
    Location:
    Peterhead, Scotland
    ONLINE
    Nice template, but I think that there is a bit of problem here:

    The interupt exit code does not restore the STATUS or W
    Code (text):
     
    ISRExit     ;software stack
      movfw    PCLATH_TEMP       ;restore PCLATH
      movwf    PCLATH
      swapf     STATUS,W            ;restore STATUS
      movwf    STATUS
      movfw    W_TEMP               ;restore W
      retfie                                ;done with interrupt
     
    I think it should be a bit more like this:
    Code (text):
     
      ISRExit                                 ;software stack
      movfw    PCLATH_TEMP           ;restore PCLATH
      movwf    PCLATH
      swapf    STATUS_TEMP, W      ;restore the W and STATUS registers
      movwf   STATUS
      swapf    W_TEMP, W
      retfie                                ;done with interrupt
     
    JimB
     
    Last edited: Aug 11, 2011
  10. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    Actually we are both incorrect. As per Microchip, it should go like this -

    Code (text):

    ISR     ;software stack    
            movwf       W_TEMP      ;back up W
            swapf       STATUS,W    ;back up STATUS
            movwf       STATUS_TEMP
            movfw       PCLATH      ;back up PCLATH
            movwf       PCLATH_TEMP

            <Delete this line and place rest of Interrupt Handler code here>

    ISRExit     ;software stack
            movfw       PCLATH_TEMP ;restore PCLATH
            movwf       PCLATH
            swapf       STATUS_TEMP,W   ;restore STATUS
            movwf       STATUS
            swapf       W_TEMP,F    ;restore W
            swapf       W_TEMP,W   
            retfie              ;done with interrupt


     
    Thanks for the catch. It has been corrected in the template above. The W and STATUS backup/restore as shown above is as per Section 8 of the Microchip PIC Midrange MCU Family reference manual.
     
    Last edited: Aug 11, 2011
  11. JimB

    JimB Super Moderator Most Helpful Member

    Joined:
    Sep 11, 2004
    Messages:
    6,324
    Likes:
    585
    Location:
    Peterhead, Scotland
    ONLINE
    Jon
    Yes I agree with your new version (from Microchip), I have actually been using that in some code which I have been playing with this afternoon.
    I was comparing your code fragment with mine and saw your faux pas and then looked at mine and thought "there is a redundant line in there", forgetting that the SWAPF instruction swaps nibbles and assuming that it swaps bytes.
    I then deleted the "redundant" line and posted the fragment here as a possible improvement.

    Perhaps I should put the redundant line back in my code, it would probably explain some of the odd glitches I have been having!!

    JimB
     
  12. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    The swapf instruction swaps NIBBLES...not bytes. In your code segment, W only gets swapped once whereas in mine it gets swapped twice so that by the time it ends up in W it's back to the original value.

    Say the value in W_TEMP is 0xCA. After the first swapf instruction (swapf W_TEMP,F), the nibbles get swapped but stay in the source file. So the file W_TEMP now contains value 0xAC. Then on the next swapf (swapf W_TEMP,W), it gets swapped again but into W this time so that W now contains the original value of 0xCA.

    Without the second swapf instruction, the nibbles would stay flipped and W would contain the flipped value of 0xAC instead of its original value of 0xCA.

    Make sense?
     
    Last edited: Aug 11, 2011
  13. JimB

    JimB Super Moderator Most Helpful Member

    Joined:
    Sep 11, 2004
    Messages:
    6,324
    Likes:
    585
    Location:
    Peterhead, Scotland
    ONLINE
    Yup, no problem.
    As the man said in the film "I've got my mind right boss".

    JimB
     
  14. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    Made some corrections and improvements to the template. I had forgotten that EECON1 and EECON2 were in different banks than EEDAT and EEADR so that has been corrected. Also, the DATA_EE_ADDR and DATA_EE_DATA buffer registers have been relocated to common RAM space to reduce the required amount of bank selecting.

    I've just tested both the EEWrite and EERead routines on a 16F887 and both work as advertised.
     

Share This Page