Inbuilt SPI in PIC16F877A

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
I want to send data from master to slave over spi protocol. I have been reading datasheet of pic16f877a. I am not sure if I am configuring the spi register correctly or not. I understood that if I want to use SPI, I have to configure the register below.

• MSSP Control Register (SSPCON)
• MSSP Status Register (SSPSTAT)
• Serial Receive/Transmit Buffer Register(SSPBUF)

Code:
void SPI_Master_Init()
{
  // Configure The IO Pins For SPI Master Mode
  TRISC5 = 0; // SDO -> Output
  TRISC4 = 1; // SDI -> Input
  TRISC3 = 0; // SCK -> Output
  TRISA0  = 0; // Chip select

  // Enables serial port and configures SCK, SDO, SDI, and SS as serial port pins, Idle state for clock is a low level,  0000 = SPI Master mode, clock = FOSC/4,
  SSPCON = 0b0010 0000;

}
1581251792926.png
1581251826596.png
 

Attachments

Last edited by a moderator:

jpanhalt

Joined Jan 18, 2008
11,087
OK, so now what's next? What will your slave be? What mode(s) will the slave accept/work with? What settings will you use? What will your SPI clock speed be?

BTW: A link to the datasheet would suffice. You don't need to upload the entire datasheet.
 

ericgibbs

Joined Jan 29, 2010
18,766
hi G,
This is an example for the Master, check against the 16F877A datasheet.
E

PORTA.4 = 0 'ss
TRISC.3 = 0 'clk
TRISC.4 = 1 'sdi from slave
TRISC.5 = 0 'sdo to slave

'MODE 0,0
ss = 1
SSPSTAT.SMP = 0 'sample at mid data
SSPSTAT.CKE = 1 'idle to active
SSPSTAT.5 = 0 'I2C only
SSPSTAT.4 = 0 'I2C only
SSPSTAT.3 = 0 'I2C only
SSPSTAT.2 = 0 'I2C only
SSPSTAT.1 = 0 'I2C only
SSPSTAT.BF = 0 'If SSPBUF=1=Full


'SSPCON1 = 0x10 'at 8mhz 2mhz operation no SS control
SSPCON1.WCOL = 0 'Collision detect
SSPCON1.SSPOV = 0 'Overflow
SSPCON1.SSPEN = 1 'Configure SCK,SD0,SDI,/SS
SSPCON1.CKP = 0 'Clock Idle Low, Active High
SSPCON1.SSPM3 = 0 '0010 = SPI Master Mode Fosc/64
SSPCON1.SSPM2 = 0
SSPCON1.SSPM1 = 1
SSPCON1.SSPM0 = 0
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
OK, so now what's next? What will your slave be? What mode(s) will the slave accept/work with? What settings will you use? What will your SPI clock speed be?
I am just assuming that I have a slave device that works on the SPI protocol. Slave has six pins CLK, SS, MISO, MOSI, VDD, and GND. Slave running at 400 kHz. Mode 0, 0
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
'SSPCON1 = 0x10 'at 8mhz 2mhz operation no SS control
hi G,
This is an example for the Master, check against the 16F877A datasheet.
E
hi E,

Okay so I have configured the two SPI registers correctly
C:
void SPI_Master_Init()
{
  // Configure The IO Pins For SPI Master Mode
  TRISC5 = 0; // SDO -> Output
  TRISC4 = 1; // SDI -> Input
  TRISC3 = 0; // SCK -> Output

  SSPCON = 0b0010 0000;   // Enables serial port and configures SCK, SDO, SDI, and SS as serial port pins
  SSPCON1 = 0b00100010;   //Configure SCK,SD0,SDI,/SS
}

void SPI_Write(int Data){
  SSPBUF = Data; // Transfer The Data To The Buffer Register
}
int main (void)
{
    SPI_Master_Init();
    SPI_Write(10);

}
 
Last edited:

jpanhalt

Joined Jan 18, 2008
11,087
hi E,

Okay so I have configured the two SPI registers correctly
SSPCON = SSPCON1 It's a bit confusing, but Microchip has not seen to fix it. There is no need to set it twice.

Where are your settings for SSPSTAT? Default? If so, CKE=0 which is not Mode0.

From Wikipedia:
1581260007993.png
 

JohnInTX

Joined Jun 26, 2012
4,787
Yes, does my codes give a basic idea if I want to send data 10 from master to slave ?
Don't forget to drop CE/ to select the slave device before transmitting - unless there is only one slave and you have strapped CE/ down to logic 0 to enable SPI all the time. Even with only one slave, using CE/ is preferred since it will reset the logic of the slave in the event of it picking up noise or other issues.

Also, you should poll for BF==1 in SPI_Write to ensure that SSPBUF can take another character i.e. any previous character has been transmitted on MOSI and a new character has been received on MISO. If you don't you will may get a write collision error - WCOL == 1 - and corrupted data. Example 9-1 in the datasheet is in assembler but is what you have to do:
Code:
LOOP BTFSS SSPSTAT, BF ;Has data been received(transmit complete)?
BRA LOOP ;No
MOVF SSPBUF, W ;WREG reg = contents of SSPBUF
MOVWF RXDATA ;Save in user RAM, if data is meaningful
MOVF TXDATA, W ;W reg = contents of TXDATA
MOVWF SSPBUF ;New data to xmit
In C this would be something like:
C:
unsigned char I2C_Write(unsigned char TXchar)
{
  unsigned char RXchar;
  while (!BF);  // wait for open buffer
  RXchar = SSPBUF;  // read returned char if interested
  SSPBUF = TXchar; // send new character
  return RXchar;
}
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
In C this would be something like:
C:
unsigned char I2C_Write(unsigned char TXchar)
{
  unsigned char RXchar;
  while (!BF);  // wait for open buffer
  RXchar = SSPBUF;  // read returned char if interested
  SSPBUF = TXchar; // send new character
  return RXchar;
}
int main (void)
{
    SPI_Master_Init();
  
return 0
}
I have gone though the datasheet http://ww1.microchip.com/downloads/en/devicedoc/21897b.pdf

What should I send if I want to send data from pic16f877a to SPI MCP4921?
 

jpanhalt

Joined Jan 18, 2008
11,087
It's a 3-wire, write-only interface. Section 5 describes how the two, 8-bit bytes sent to it need to be structured. One detail to note is that after both bytes are sent, LDAC is toggled low to load those bytes into the DAC data latches. The alternative is to tie the lDAC pin low. In that case, data transfer to the DAC latches happens on the rising edge of the !CS signal.

I've never used that chip, but my inclination would be to control data flow with the LDAC pin rather than CS. I don't have any facts to back me up.
 

JohnInTX

Joined Jun 26, 2012
4,787
I've never used that chip, but my inclination would be to control data flow with the LDAC pin rather than CS. I don't have any facts to back me up.
On this one you would want to use CS/. The SPI interface frames the 2 byte data with CS/ and ignores any clocks past 16 until CS/ is raised. Presumably, dropping CS/ also initializes the serial registers so that it knows where the 4 command bits are. LDAC/ is used to inhibit the transfer of the serial data received to the DAC registers to synchronize the output changes i.e. it allows you to write both serial holding registers in two 16bit transactions then transfer those values to to both DACs simultaneously by toggling LDAC/ - but you still need CS/ to frame the two SPI messages. If LDAC/ is strapped low then the effect is that received data is passed to straight to the selected DAC register when CS/ is raised.

What should I send if I want to send data from pic16f877a to SPI MCP4921?
You send 2 bytes (16 bits) for each DAC, framed by CS/. The first 4 bits are COMMAND bits that specify which DAC to write, whether to buffer Vref, the output gain and output control. The remaining 12 bits are the value to be output.
The voltage output is given by: Vout = (Vref * gain) * DAC_value / 2^12
The SPI setup is shown in Figure 1-1.

Good luck!
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
You send 2 bytes (16 bits) for each DAC, framed by CS/.
C:
unsigned char I2C_Write(unsigned char TXchar)
{
  unsigned char RXchar;
  while (!BF);  // wait for open buffer
  RXchar = SSPBUF;  // read returned char if interested
  SSPBUF = TXchar; // send new character
  return RXchar;
}

int main (void)
{
   unsigned char Sendchar,
    SPI_Master_Init();
    
}
I have to pass data from main function

unsigned char Sendchar,

What value should I initialize variable Sendchar?
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
It doesn’t matter since you are not calling any function to send anything.
That was my question, I have to initialize variable Sendchar in main function ? What value should I initialize variable Sendchar?

C:
unsigned char I2C_Write(unsigned char TXchar)
{
  unsigned char RXchar;
  while (!BF);  // wait for open buffer
  RXchar = SSPBUF;  // read returned char if interested
  SSPBUF = TXchar; // send new character
  return RXchar;
}

int main (void)
{
   unsigned char Sendchar,
    SPI_Master_Init();
     I2C_Write(unsigned char Sendchar)
}
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
That depends on what you are trying to do. What are you trying to do?

EDIT: the name of I2C_Write should be SPI_Write, yes?
Yes You are absolutely right SPI_Write,

Well my question might be a little weird, But i think my question is universal

let's suppose I have microcontroller and another device is slave, I can send data from Microcontroller to slave or I can recive data from slave to microcontroller.

Lets assume I have two function

C:
    function to send data to salve
    
    void Sendslave (char data)
    {
        //more  code
    }
        
    void main ()
    {
        char buffer;
        
        Sendslave (char buffer)
        
        ....
        
        //more code
    }
Now suppose I have a different type of slave device and I want to write data to slave so I have two universal function for each device to write data

For example if I have sensor work on SPI protocol so I have to initialize variable char buffer in main function ? What value should I initialize variable char buffer to write sensor?

For example if I have ADC work on SPI protocol so I have to initialize variable char buffer in main function ? What value should I initialize variable char buffer to write ADC?

Just for example I am showing two slave devices

 
Top