Aqui os dejo el código que he encontrado en la página de microchip y que probado en PROTEUS funciona perfectamente.
;Ejemplo utilización del módulo SPI en modo master del 877
; grabando / leyendo de una memoria 25LC256
; y mostrando lo leído por el puerto D
; utiliza un xtal de 10mhz
list p=16F877A
; PORTC PIN DESCRIPTION
; SCK bit 3, SDI bit 4, SDO bit 5, CS bit 7
; Fosc = 10.0 MHz, thus each instr. cycle = 400ns
;***************Ram Register Definitions*******************************
;Aqui definimos los registros de variables
cblock 0x20
rxdata
addr
loops
PDel0
PDel1
PDel2
endc
;***************Bit Definitions****************************************
;Aqui definimos los pines del micro
#define CS PORTC,7 ; SPI chip select bit definition
;**********************************************************************
;***************25Cxxx command definitions
;Aqui definimos los bits de comando
#define wren 6 ;write enable latch
#define WRDI 4 ;reset the write enable latch
#define RDSR 5 ;read status register
#define WRSR 1 ;write status register
#define READ 3 ;read data from memory
#define WRITE 2 ;write data to memory
; Bit defines within status register
;Aqui definimos los bits del registro STATUS
#define WIP 0 ;write in progress
#define WEL 1 ;write enable latch
#define BP0 2 ;block protection bit
#define BP1 3 ;block protection bit
;**********************************************************************
include "p16F877A.inc" ; 16F877A include file
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
;**********************************************************************
;Aqui definimos el comienzo de los vectores, no usamos interrupciones
page
org 0x000 ; Reset Vector
clrf PCLATH ; ensure PCLATH bit 3 is cleared
clrf INTCON ; ensure all interrupts are disabled
goto start ; jump to the beginning of the program
org 0x004 ; interrupt vector, do nothing
isr retfie ;goto isr ; do nothing, location just
; identified in code
;***************BEGIN MAIN PROGRAM*************************************
;Aqui comienza el programa, definimos las entradas y salidas y habilitamos el módulo de comunicación SPI
start bcf STATUS,RP0 ; need to set bank 0
clrf PORTC ; initialize port c
clrf PORTD
bsf CS ; make sure cs is set high
bsf STATUS,RP0 ; need to set bank 1
movlw 0x10 ; all bits are outputs except SDI
movwf TRISC ; for SPI setup
movlw 0x00
movwf TRISD ;show on port D
bcf STATUS,RP0 ; need to set bank 0
movlw 0x31 ; SPI master, clk/16, ckp=1
movwf SSPCON ; SSPEN enabled
movlw 0x00 ; *** put beginning address in addr**
movwf addr ; for later use
Main
;Lo primero que hacemos es un WREN
call Wren ; call the write enable routine
;Luego escribimos el registro status para limpiar los bits de protección de bloque
call wrsr ; call the write status routine
;Hacemos un test de ocupado
call busy_test ; test WIP bit in status register
;Y hacemos un WREN antes de escribir en la memoria
call Wren ; call the write enable command
;Después escribimos 0xA5 (o cualquier otro valor ) en 0x10
bcf CS ; set chip select line low
movlw WRITE ; WRITE control byte
call output ; call the output subroutine
movlw b'00000000' ; high addr byte is all 0's
call output ; call the output subroutine
movf addr,w ; low addr byte
call output ; call the output subroutine
movlw b'10100101' ; load 0xA5 as data to be sent out
call output ; call the output subroutine
bcf SSPCON,CKP ; set clock idle low, mode 0,1
bsf CS ; set chip select, begin write cycle
bsf SSPCON,CKP ; set clock idle high, mode 1,1
call busy_test
call rdsr ; call the read status subroutine
;Ahora leemos la direccion 0x10 y guardamos el dato en rxdata
bcf CS ; set chip select line low
movlw READ ; READ control byte
call output ; call the output subroutine
movlw b'00000000' ; high addr byte is all 0's
call output ; call the output subroutine
movf addr,w ; get ready to send next byte
call output ; call the output subroutine
movlw b'01011010' ; move don't care byte of 0x5A into
call output ; call the output subroutine
bsf CS ; bring chip select high end
; terminate read command
; Y lo mostramos por puerto D
movf rxdata,w
movwf PORTD
;El programa hace un loop continuo, así que si lo que muestra por el puerto D es el valor 0xA5 en binario entonces
;es que ha leido correctamente
call wait ; little delay between SPI sequence
call DEMORA1s
goto Main ; do it all over again
; loop can be used to evaluate SPI
; signals on oscilloscope
;********************* BEGIN SUBROUTINES*******************************
;*** DELAY ROUTINE - 400uS ***
; Subrutina de espera 400us
wait movlw .200 ; timing adjustment variable
movwf loops ; move variable into loops
top nop ; sit and wait
nop ; no operation
decfsz loops,f ; loop complete?
goto top ; no, go again
return ; yes, return from sub
;****** This is the OUTPUT transmit/receive subroutine. ***************
;Esta es la subrutina OUTPUT que recive/transmite
output movwf SSPBUF ; place data in buffer to send
loop1 bsf STATUS,RP0 ; specify bank 1
btfss SSPSTAT,BF ; has data been received (xmit done)?
goto loop1 ; not done yet, keep trying
bcf STATUS,RP0 ; specify bank 0
movf SSPBUF,W ; empty receive buffer
movwf rxdata ; put received byte into rxdata
return ; return from subroutine
;*******Write Enable Subroutine****************************************
;Subrutina de habilitación de escritura
Wren bcf CS ; set chip select line low
movlw wren ; WREN control byte
call output ; Call the output subroutine
bcf SSPCON,CKP ; set clock idle low, mode 0,1
bsf CS ; set chip select, begin write
bsf SSPCON,CKP ; set clock idle high, mode 1,1
return ; return from subroutine
;*******Read Status Register Subroutine********************************
;Subrutina de lectura del registro status
rdsr movlw RDSR ; RDSR control byte
call output ; Call the output subroutine
movlw b'00000101' ; this byte is a don't care byte
call output ; status reg data will be in rxdata
bsf CS ; set chip select
return ; return from subroutine
;*******Write Status Register Subroutine*******************************
;Subrutina de escritura del registro status
wrsr bcf CS ; set chip select line low
movlw WRSR ; WRSR control byte
call output ; Call the output subroutine
movlw b'00001000' ; set BP1 bit in status register
call output ; this will clear block protect bits
bcf SSPCON,CKP ; set clock idle low, mode 0,1
bsf CS ; set chip select
bsf SSPCON,CKP ; set clock idle high, mode 1,1
return ; return from subroutine
;*******Busy Test - WIP bit in Status Register*************************
;Testeo del bit WIP del registro status
busy_test
bcf CS ; set chip select line low
movlw RDSR ; RDSR control byte
call output ; Call the output subroutine
movlw b'00000000' ; send dummy byte
call output ; to initiate clock sequence for read
bsf CS ; else, set chip select high
btfsc rxdata,WIP ; test WIP bit read from status register
goto busy_test ; repeat busy test
return ; return from subroutine
;Demora de 1 segundo
DEMORA1s movlw .22 ; 1 set numero de repeticion (C)
movwf PDel0 ; 1 |
PLoop0 movlw .134 ; 1 set numero de repeticion (B)
movwf PDel1 ; 1 |
PLoop1 movlw .211 ; 1 set numero de repeticion (A)
movwf PDel2 ; 1 |
PLoop2 clrwdt ; 1 clear watchdog
decfsz PDel2, 1 ; 1 + (1) es el tiempo 0 ? (A)
goto PLoop2 ; 2 no, loop
decfsz PDel1, 1 ; 1 + (1) es el tiempo 0 ? (B)
goto PLoop1 ; 2 no, loop
decfsz PDel0, 1 ; 1 + (1) es el tiempo 0 ? (C)
goto PLoop0 ; 2 no, loop
PDelL1 goto PDelL2 ; 2 ciclos delay
PDelL2 clrwdt ; 1 ciclo delay
return ; 2+2 Fin.
end