Reputation: 29
I'm trying to get 2 MCUs to communicate using one-way SPI, i.e. Master sends to Slave. The master code appears to block after writing to the output buffer at the point where I am testing for the BF flag set.
I'm using 2 PIC16F1503 MCU's one configured as master, the o upther as slave. The circuit is shown in the following image.Circuit diagram
Apart from the MSSP module specifics, both processors are configured identically
The SPI configuration for the Master is
SPI_init:
clrf INTCON ; Disable all interrupts
banksel TRISC
; set alternative SDO pin
banksel APFCON
bsf APFCON, APFCON_SDOSEL_POSN ; SDO function is on RA4
; complete the rest of SPI setup but don't enable just yet
banksel SSP1STAT
movlw 01000000B
;0------- SMP input data sampled at end of data o/p time
;-1------ CKE xmit occurs on transition from active to idle clock
;--xxxxxx I2C only
movwf SSP1STAT
; set SDO function to alternative RA4 pin
banksel APFCON
bsf APFCON, APFCON_SDOSEL_POSN ; SDO function is on RA4
; set the SPI mode and clock speed
movlw 00110010B
;0------- WCOL write collision bit - no collision
;-0------ SSPOV receive overflow indicator bit - no OF
;--1----- SPEN enable synchronous serial port
;---1---- CKP clock polarity, idle state for clock is high
;----0010 SPI Master mode, Fosc/64
movwf SSP1CON1
; set SS high
banksel LATC
bsf LATC, LATC_LATC3_POSN
call stabilisationWait
return
The SPI configuration for the Slave is
SPI_init:
clrf INTCON ; Disable all interrupts
banksel TRISC
; set alternative SDO pin, even though we won't be sending any data
banksel APFCON
bsf APFCON, APFCON_SDOSEL_POSN ; SDO function is on RA4
; complete the rest of SPI setup but don't enable just yet
banksel SSP1STAT
movlw 01000000B
;0------- SMP input data sampled at end of data o/p time
;-1------ CKE xmit occurs on transition from active to idle clock
;--xxxxxx I2C only
movwf SSP1STAT
; set SDO function to alternative RA4 pin
banksel APFCON
bsf APFCON, APFCON_SDOSEL_POSN ; SDO function is on RA4
movlw 00110001B
;0------- WCOL write collision bit - no collision
;-0------ SSPOV receive overflow indicator bit - no OF
;--0----- SPEN disable synchronous serial port
;---1---- CKP clock polarity, idle state for clock is high
;----0100 SPI Slave mode, SS enabled
movwf SSP1CON1
; now wait for the SS line to go high, meaning SPI master is ready
banksel PORTC
btfss PORTC, PORTC_RC3_POSN
goto $-1
; now enable SPI
banksel SSP1CON1
bsf SSP1CON1, SSP1CON1_SSPEN_POSN ; enable SPI
call stabilisationWait
return
In the Master there's a loop where I write a byte to SPI as shown here.
loop:
movlw 2
call flashLED
call SPI_write
call delay5s
goto loop
SPI_write:
movlw 10101010B
; pull SS low
; banksel LATC
; bcf LATC, LATC_LATC3_POSN
; write the value to SPI transmit buffer
banksel SSP1BUF
movwf SSP1BUF
; wait till BF flag goes high
banksel SSP1STAT
btfss SSP1STAT, SSP1STAT_BF_POSN
goto $-1
; clear the BF flag by reading SSP1BUF
banksel SSP1BUF
movf SSP1BUF, w
; set SS high
; banksel LATC
; bsf LATC, LATC_LATC3_POSN
return
When I run this, the LED flashes twice (expected) and then the program 'hangs'. The only blocking code is the wait for the BF flag to go high.
You will also notice from the commented code that I am not clear on whether I need to explicitly manipulate the SS line or whether this is done by the MSSP module (I suspect the latter) - I have tried the code with/without the SS line setting and the problem remains.
Since SPI has no explicit ACK, I believe the Slave code is likely not relevant, however, the processing loop in the Slave is here for reference.
loop:
; wait for buffer full flag
movlw 1
call flashLED
btfss SSP1STAT, SSP1STAT_BF_POSN
goto $-1
; read the value from the receive buffer
banksel SSP1BUF
movf SSP1BUF, w
movwf bufferValue
goto loop
FWIW, I have managed to configure a PIC16F1503 as a Master driving a DAC (MCP4091) - this is a uni-directional interaction, which worked just fine. However, it was written in C language and when I 'translated' it to ASM, it no longer worked. I thought debugging would be easier if I 'owned' both the Master and Server components, which is why I put this configuration together.
Also, the delay routines work correctly. I've been using them for years now without any problems.
Hopefully someone will be able to spot where I am going wrong.
Thanks @Kozmotronik for your suggestion. I have tried your code and the problem remains. I have ordered a logic analyser and once I have this, I should be able to get a better idea of what signals are on the output lines, if any. Also, I'm still wondering if I need to manually pull down the SS line or whether this is done automatically by the MSSP module?
I have just run my original code under the simulator and found that the BF flag is NOT set after the write to SSP1BUF. Now I don't know if the simulator is behaving incorrectly (i.e. not like real life) or whether something else in my configuration is causing this. However, if the BF flag is not being set after the write, then this explains the hang. Any thoughts?
Upvotes: 0
Views: 153
Reputation: 29
Somewhat embarrassing admission. I just found that SPI was NOT being enabled in the Master. The code below was executing in the wrong bank.
; set the SPI mode and clock speed
movlw 00110010B
;0------- WCOL write collision bit - no collision
;-0------ SSPOV receive overflow indicator bit - no OF
;--1----- SPEN enable synchronous serial port
;---1---- CKP clock polarity, idle state for clock is high
;----0010 SPI Master mode, Fosc/64
movwf SSP1CON1
It should have been
; set the SPI mode and clock speed
movlw 00110010B
;0------- WCOL write collision bit - no collision
;-0------ SSPOV receive overflow indicator bit - no OF
;--1----- SPEN enable synchronous serial port
;---1---- CKP clock polarity, idle state for clock is high
;----0010 SPI Master mode, Fosc/64
banksel SSP1CON1
movwf SSP1CON1
Just to close this out, I received my Logic Analyser yesterday, and captured the following SPI pin states. I also proved that the SS line has to be explicitly set in the code, i.e. it is not automatically pulled low by the MSSP module.
Upvotes: 0
Reputation: 2520
As per the following part of the datasheet:
Any write to the SSPxBUF register during transmission/reception of data will be ignored and the write collision detect bit, WCOL of the SSPxCON1 register, will be set. User software must clear the WCOL bit to allow the following write(s) to the SSPxBUF register to complete successfully.
So before you busy wait for reception, you must ensure that the data is sent so you can have a response from the slave. You need to add some more code if you wanna know whether the shift register is available for transmission before halting the CPU in busy wait for the BF flag.
Here is a slightly modified version of your SPI_write function:
spi1_write:
; wait till transmit buffer to be available if needed
banksel SSP1STAT ; Make sure you are in the SSP registers bank
btfss SSP1STAT, SSP1STAT_BF_POSN ; Make sure there is nothing received in the buffer
goto spi1_send ; Buffer is available for transmission
movf SSP1BUF, W ; There is a received data waiting for handling
movwf ssp1_rx_data ; <- should declare this variable. store the data if needed
spi1_send:
bcf SSP1CON1, SSP1CON1_WCOL_POSN ; Clear the write collision flag before writing to the buffer
movwf SSP1BUF ; Write the data first
btfsc SSP1CON1, SSP1CON1_WCOL_POSN ; Then check whether a write collision has occured
goto spi1_write ; The shift register is still busy, keep checking until the data has been sent
; Now that the data has sent successfully, wait till BF flag goes high
spi1_wait_response:
btfss SSP1STAT, SSP1STAT_BF_POSN
goto spi1_wait_response
; clear the BF flag by reading SSP1BUF
banksel SSP1BUF
movf SSP1BUF, w
return
Try the code to see if it helps you.
However the delay5s call looks suspicious to me on the other hand. Have you tested that function well? Maybe the program flow hangs in that delay function?
Upvotes: 0