PIC18F14K22 SPI communicatoin with ICM20602

Find out how to setup your programmer's software and how to solve many common problems.

Moderator: phalanx

Post Reply
flopfl
Posts: 2
Joined: Sat Dec 15, 2018 4:45 am

PIC18F14K22 SPI communicatoin with ICM20602

Post by flopfl » Sat Dec 15, 2018 6:07 am

Hello there,

I m trying to communicate using SPI with my PIC18F14K22 as master and ICM20602 as slave to read the accelerometer values.
Currently i m using the c18 compiler on MPLAB IDE and its SPI library.
The Problem is, i need to know if my code should in theory work, since i have no other way of confirming weather the ICM20602 has been properly put on the board(its very tiny). I m very new to all of this so i m having a hard time telling if something should work.

Thanks a lot already for helping me.
datasheet for ICM20602: https://www.invensense.com/products/mot ... icm-20602/
datasheet for PIC18F14K22: https://www.microchip.com/wwwproducts/en/PIC18F14K22

in my Main method i requested the identety which should be 0x12, to check if it works. The result so far has always been 0. I also tried with the adresses for one of the accelerometer values but the returned value is always 0 aswell. Is it even right to assume that having this register:
Register Name: WHO_AM_I
Register Type: READ only
Register Address: 117 (Decimal); 75 (Hex)
means when i write the byte of the adress with the msb beeing set to 1 for read, there will be a byte send for me to read as answer, as soon as i run another byte with the clock?

Code: Select all

void main(void) {
    __init();//initializes some timers and sets the Tris of leds
    spiInit();//code is further down
    for(k=0;k<100000;k++){
            Nop();
        }
    while (1) {
        answ=byte_read(0b11110101);//0b11110101 adress of WHOAMI read
        if(answ!=0){   
            LATCbits.LATC4 = !LATCbits.LATC4; // LED toggle
        }
        for(k=0;k<10000;k++){
            Nop();
        }
        }
my SPI settings are:
-Fosc/4 (Fosc is 4MHz)
-CKE=1 ,CKP=0
-SMP i tried both settings since i m not sure which i need

Code: Select all

void spiInit()
{
    TRISCbits.RC6=0;//CS pin
    SPI_CS=1;//CS Pin Lat
    OpenSPI(SPI_FOSC_4,MODE_00,SMPMID);
        
}
code for read and write method

Code: Select all

void byte_write (unsigned char adress,
unsigned char data)
{
SPI_CS = 0; //assert chip select
var = putcSPI(adress); //send address
var = putcSPI(data); //send data byte
SPI_CS = 1; //negate chip select
}
unsigned char byte_read (unsigned char address)
{
SPI_CS = 0; //assert chip select
var = putcSPI(address); //send  address
var = getcSPI(); //read single byte
SPI_CS = 1;
return (var);
}
library code for read write
READSPI is the same as getcSPI

Code: Select all

#if defined (SPI_V1) || defined (SPI_V4)

unsigned char ReadSPI( void )
{
  unsigned char TempVar;
  TempVar = SSPBUF;        // Clear BF
  PIR1bits.SSPIF = 0;      // Clear interrupt flag
  SSPBUF = 0x00;           // initiate bus cycle
  //while ( !SSPSTATbits.BF );                  // wait until cycle complete
  while(!PIR1bits.SSPIF);  // wait until cycle complete
  return ( SSPBUF );       // return with byte read 
}
#endif
writeSPI is the same then putcSPI

Code: Select all

#if defined (SPI_V1)

signed char WriteSPI( unsigned char data_out )
{
  unsigned char TempVar;  
  TempVar = SSPBUF;           // Clears BF
  PIR1bits.SSPIF = 0;         // Clear interrupt flag
  SSPCON1bits.WCOL = 0;			//Clear any previous write collision
  SSPBUF = data_out;           // write byte to SSPBUF register
  if ( SSPCON1 & 0x80 )        // test if write collision occurred
   return ( -1 );              // if WCOL bit is set return negative #
  else
   // while( !SSPSTATbits.BF );  // wait until bus cycle complete 
    while( !PIR1bits.SSPIF );  // wait until bus cycle complete  
   return ( 0 );                // if WCOL bit is not set return non-negative#
}

#endif

OpenSPI effectivly sets:

SSPSTAT &= 0x3F; // power on state
SSPCON1 = 0x00; // power on state
SSPCON1 |= sync_mode; // select serial mode
SSPSTAT |= smp_phase; // select data input sample phase
SSPSTATbits.CKE = 1; // data transmitted on falling edge
TRISBbits.TRISB6 = 0; // define clock pin as output
TRISCbits.TRISC7 = 0; // define SDO pin as output
TRISBbits.TRISB4 = 1; // define SDI pin as input
SSPCON1 |= SSPENB;

incase you want to doublecheck we have SPI_V1 and SPI_IO_V9

Code: Select all

#if defined (SPI_V1)

void OpenSPI( unsigned char sync_mode, unsigned char bus_mode, unsigned char smp_phase)
{
  SSPSTAT &= 0x3F;                // power on state 
  SSPCON1 = 0x00;                 // power on state
  SSPCON1 |= sync_mode;           // select serial mode 
  SSPSTAT |= smp_phase;           // select data input sample phase

  switch( bus_mode )
  {
    case 0:                       // SPI bus mode 0,0
      SSPSTATbits.CKE = 1;        // data transmitted on falling edge
      break;    
    case 2:                       // SPI bus mode 1,0
      SSPSTATbits.CKE = 1;        // data transmitted on rising edge
      SSPCON1bits.CKP = 1;        // clock idle state high
      break;
    case 3:                       // SPI bus mode 1,1
      SSPCON1bits.CKP = 1;        // clock idle state high
      break;
    default:                      // default SPI bus mode 0,1
      break;
  }

  switch( sync_mode )
  {
    case 4:                       // slave mode w /SS enable
	#if defined SPI_IO_V1 
	 	TRISCbits.TRISC3 = 1;       // define clock pin as input	
      	TRISAbits.TRISA5 = 1;       // define /SS1 pin as input
    #elif defined SPI_IO_V2 || defined SPI_IO_V4 
		TRISFbits.TRISF7 = 1;       // define /SS pin as input
	 	TRISCbits.TRISC3 = 1;       // define clock pin as input
	#elif defined SPI_IO_V3
		TRISAbits.TRISA5 = 1;       // define /SS pin as input
	 	TRISBbits.TRISB1 = 1;       // define clock pin as input
	#elif defined SPI_IO_V5	
		TRISCbits.TRISC6 = 1;       // define /SS pin as input
	 	TRISCbits.TRISC5 = 1;       // define clock pin as input
	#elif defined SPI_IO_V6
	 	TRISCbits.TRISC3 = 1;       // define clock pin as input	
      	TRISAbits.TRISA5 = 1;       // define /SS1 pin as input
    	ANSEL &= 0b11101111;
	#elif defined SPI_IO_V9
		TRISBbits.TRISB6 = 1;		// define clock pin as input
		TRISCbits.TRISC6 = 1;		// define /SS pin as input
	#endif
		break;

case 5:                       // slave mode w/o /SS enable
	#if defined (SPI_IO_V1) || defined (SPI_IO_V6) || defined (SPI_IO_V2) || defined (SPI_IO_V4)
		TRISCbits.TRISC3 = 1;       // define clock pin as input	
    #elif defined SPI_IO_V3
	 	TRISBbits.TRISB1 = 1;       // define clock pin as input
	#elif defined SPI_IO_V5	
	 	TRISCbits.TRISC5 = 1;       // define clock pin as input
	#elif defined SPI_IO_V9
		TRISBbits.TRISB6 = 1;		// define clock pin as input		
	#endif
		break;
    
	default:                      // master mode, define clock pin as output
    #if defined (SPI_IO_V1) || defined (SPI_IO_V6) || defined (SPI_IO_V2) || defined (SPI_IO_V4)
		TRISCbits.TRISC3 = 0;       // define clock pin as output	
    #elif defined SPI_IO_V3
	 	TRISBbits.TRISB1 = 0;       // define clock pin as output
	#elif defined SPI_IO_V5	
	 	TRISCbits.TRISC5 = 0;       // define clock pin as output
	#elif defined SPI_IO_V9
		TRISBbits.TRISB6 = 0;		// define clock pin as output		
	#endif	 
	 
	 break;
  }
	#if defined SPI_IO_V3 
	 	TRISBbits.TRISB0 = 1;       // define SDI pin as input	
      	TRISCbits.TRISC7 = 0;       // define SDO pin as output
    #elif defined SPI_IO_V5
		TRISCbits.TRISC4 = 1;       // define SDI pin as input
	 	TRISCbits.TRISC7 = 0;       // define SDO pin as output
	#elif defined SPI_IO_V9
		TRISCbits.TRISC7 = 0;       // define SDO pin as output
		TRISBbits.TRISB4 = 1;       // define SDI pin as input			
	#else  
		TRISCbits.TRISC4 = 1;       // define SDI pin as input
	 	TRISCbits.TRISC5 = 0;       // define SDO pin as output
	#endif
  
	SSPCON1 |= SSPENB;              // enable synchronous serial port 
}
#endif

User avatar
phalanx
Non-SFE Guru
Posts: 1983
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: PIC18F14K22 SPI communicatoin with ICM20602

Post by phalanx » Sat Dec 15, 2018 2:04 pm

Hi flopfl,

I'm on a business trip at the moment and can't give this the attention it deserves for another several days. I'll try my best to get you up and running but it's pretty clumsy trying to read multiple datasheets on a cell phone.

A couple quick things right off the bat.

First, are you setting the TRIS registers for the pins related to the SPI port? As an SPI master, the module requires the SCK and SDO pins to be outputs and SDI to be an input.

Second, are you reading the SPI input buffer after write to the slave device? Regardless of the direction of your data, an SPI cycle will always shift in a byte. The buffer should be read and the value discarded if not needed in order to prevent errors.

Third, what are you using to program your PIC? Does it have debugging capabilities?

When I have access to my laptop, I'll look over your settings for CKE, CKP, and SMP.

Cheers!

-Bill

User avatar
phalanx
Non-SFE Guru
Posts: 1983
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: PIC18F14K22 SPI communicatoin with ICM20602

Post by phalanx » Sat Dec 15, 2018 2:05 pm

And now that I'm reading your post a little more carefully, I see that the TRIS registers are being set in the OpenSPI() function.

-Bill

flopfl
Posts: 2
Joined: Sat Dec 15, 2018 4:45 am

Re: PIC18F14K22 SPI communicatoin with ICM20602

Post by flopfl » Sat Dec 15, 2018 4:04 pm

Hei Bill,

Thanks a lot for helping me out.
on point 3:
I m using the Pickit3 and i can use 1 breakpoint.

on point 2:
after my Write in WriteSPI() i dont read the spi buffer however i m clearing the Buffer befor reading in ReadSPI().

Code: Select all

signed char WriteSPI( unsigned char data_out )
{
  unsigned char TempVar;  
  TempVar = SSPBUF;           // Clears BF
  PIR1bits.SSPIF = 0;         // Clear interrupt flag
  SSPCON1bits.WCOL = 0;			//Clear any previous write collision
  SSPBUF = data_out;           // write byte to SSPBUF register
  if ( SSPCON1 & 0x80 )        // test if write collision occurred
   return ( -1 );              // if WCOL bit is set return negative #
  else
   // while( !SSPSTATbits.BF );  // wait until bus cycle complete 
    while( !PIR1bits.SSPIF );  // wait until bus cycle complete  
   return ( 0 );                // if WCOL bit is not set return non-negative#
}

Code: Select all

unsigned char ReadSPI( void )
{
  unsigned char TempVar;
  TempVar = SSPBUF;        // Clear BF
  PIR1bits.SSPIF = 0;      // Clear interrupt flag
  SSPBUF = 0x00;           // initiate bus cycle
  //while ( !SSPSTATbits.BF );                  // wait until cycle complete
  while(!PIR1bits.SSPIF);  // wait until cycle complete
  return ( SSPBUF );       // return with byte read 
}

Post Reply