# Can't get input from SPI on PIC16F18345

#### John P

Joined Oct 14, 2008
1,897
I'm trying to get a PIC processor to communicate over CAN bus, using the MCP2515 interface chip. I have to communicate with the interface over SPI, and I do have it working, but the problem I'm having is that data into the processor always gets read as 0x00. This processor has Peripheral Pin Select, so I've chosen pins for the interface, and I'm pretty sure I got it right, because the SPI clock and data-out lines work fine. I've tried various pins for data-in, but it never works. I have made sure that whichever pin I use, the right TRIS bit is a 1 for an input, and all the ANSEL bits are set to 0. I've got a work-around by bit-banging my own SPI interface, and that works OK, but hardware SPI would be faster.

Here's how I set the PPS register. I've verified that it really does get set, by reading it back.
Code:
pps_unlock();
ra1pps =     0b00011000;         // SPI clock to A1
b5pps =     0b00011001;         // SPI MOSI to B5
ssp1datpps = 0b00010000;     // C0 for SPI MISO
ssp1stat = 0b01000000;
ssp1con1 = 0b00100010;                   // Clock normally low
pps_lock();
Then I write and read the SPI by just doing
ssp1buf = output;
then after a short delay
input = ssp1buf;

Ideas appreciated.

#### Picbuster

Joined Dec 2, 2013
1,027
I'm trying to get a PIC processor to communicate over CAN bus, using the MCP2515 interface chip. I have to communicate with the interface over SPI, and I do have it working, but the problem I'm having is that data into the processor always gets read as 0x00. This processor has Peripheral Pin Select, so I've chosen pins for the interface, and I'm pretty sure I got it right, because the SPI clock and data-out lines work fine. I've tried various pins for data-in, but it never works. I have made sure that whichever pin I use, the right TRIS bit is a 1 for an input, and all the ANSEL bits are set to 0. I've got a work-around by bit-banging my own SPI interface, and that works OK, but hardware SPI would be faster.

Here's how I set the PPS register. I've verified that it really does get set, by reading it back.
Code:
pps_unlock();
ra1pps =     0b00011000;         // SPI clock to A1
b5pps =     0b00011001;         // SPI MOSI to B5
ssp1datpps = 0b00010000;     // C0 for SPI MISO
ssp1stat = 0b01000000;
ssp1con1 = 0b00100010;                   // Clock normally low
pps_lock();
Then I write and read the SPI by just doing
ssp1buf = output;
then after a short delay
input = ssp1buf;

Ideas appreciated.
I do not see:
1:enable (remote device)
2: start clock
4: transmit data
6:disable (remote device)

1:enable (remote device)
3:activate clock by sending dummy char
5:disable (remote device)

Below my working code

Picbuster
void Send_SPI1(int Size)
{
int n;
Enable=0;
for (n=0;n<Size;n++)
{
SSP1CON1bits.WCOL=0;
SSP1BUF = Transmit_Msg[n]; // send
__delay_us(5); //
}
return;
}

Ena_A1=0;
}
__delay_us(200);
{
for (int ip=0;ip<3;ip++) // read three values in Spi_Rec2
{
// test WCOL if collision could occur no need in my case
SSP2BUF = 0; // send sync clock to receive
while (!SSP2STATbits.BF) {} // wait receive
Spi_Rec2[ip]= SSP2BUF;
}

Using the interrupt line will make live easy hence an int will occur when a byte is in (or out).

#### John P

Joined Oct 14, 2008
1,897
Picbuster, thank you for taking an interest in my problem. To respond to the things that you say were missing, I really do have a "wait till receive ready"--I wrote it as
then after a short delay
input = ssp1buf;

And I do enable the peripheral device, but I'm just using the bare processor at present, trying to get the SPI input working. I have the port pin set as an input with the weak pullup enabled, and I short it to ground to generate a low input. But whether the jumper is in place or not, it always reads 0.

I noticed that in your code, you used 2 different methods to time the SPI operation, an explicit 5usec timeout in the transmit routine, and a wait for the SSP2STATbits.BF flag in the receive routine. I don't see why you do this, because SPI receives and transmits simultaneously, so I'd have thought that whatever works for one will work for the other.

Anyway, I rewrote your receive routine into a function that I'm calling repeatedly. It looks like this:
Code:
// receive part
void RecSPI(void)
{
byte ip;
//delay_us(200);
//{
for (ip=0;ip<3;ip++) // read three values in Spi_Rec2
{
SPI_SS = 0;
// test WCOL if collision could occur no need in my case
SSP1BUF = 0x55; // send sync clock to receive
while (!SSP1STAT.F0) ; // wait receive, test BF bit
SPI_SS = 1;
//Spi_Rec1[ip]= SSP1BUF;
util[ip] = SSP1BUF;
}
util[4] = ssp1datpps;
util[7] = portc;
}
My program installs a 10-element array called util[] which I can read out to the computer via the UART. The 3 lowest bytes should be 0 if the jumper to ground is connected, and 0xFF if it's not, but they're always 0. Util[4] correctly shows the value that was loaded into ssp1datpps, and util[7] reads the value on the port pin correctly. It's only the SPI port that fails to receive the correct value. Is it any kind of clue that I can read the port pin as a normal input when I've programmed it to be the input to a peripheral, by setting ssp1datpps? Should using both at the same time be possible? Because it definitely can be read that way.

You can see that I'm sending 0x55 out of the SPI port as a dummy value. That's so that I can verify the output and its relationship to the clock pin with a scope. It doesn't show any problems.

Edited to say that you may notice I changed SSP2STATbits.BF to SSP1STAT.F0. I'm using SPI port 1, and my compiler (MikroE) didn't recognize the BF bit designation, so I had to tell it to look at bit 0, which is the BF bit.

Last edited: