Electronic Projects, forums and more.

Go Back   Electronic Circuits Projects Diagrams Free > Electronics Categories > Micro Controllers


Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc.

Reply
 
Tools
Old 12th June 2009, 10:49 PM   #1
Default PIC SPI and Freescale MMA7455L

Hi there!

It's been a while since I bugged you people with a question and now I have another one. It relates to SPI communication. I am trying to read values from a Freescale accelerometer (MMA7455L) using a PIC 18F4550 and SPI.

To that end, I wrote some simple code. Now, it doesn't seems to work as the program seems stuck when trying to read the value I just wrote into a register of the MMA7455L. What I would like you to do is take a look at my code and tell me if you think it's valid. If you think it's Ok then I can start thinking about bugging the Freescale people. Many thanks!

- Florian

Edit - just to add a few details. Because MMA7455L operates in slave mode, I am using bit 6 on PORTD to toggle SPI transmission. Also, the write command seems to work as when I am debugging it exits the loop, however the code gets stuck when trying to read the value written into the register (testing to see if the code works).

The clock is FOSC/4, so about 5MHz as I am using a 20MHz crystal (that speed is Ok for the MMA7455L if the VDD is > 2.4 and it is in my case).

Code:
#include <p18f4550.h>
#include <delays.h>

#pragma config WDT = OFF, FOSC = INTOSC_HS, LVP=OFF

unsigned char val=0;

void picdelay (unsigned char n);
void ledonoff ();
unsigned char spi_r(unsigned char reg);
void spi_w(unsigned char regaddr, unsigned char regdata);

void main (void)
{

 // Make all bits on the Port D (LEDs) output bits.
 TRISD = 0;

//initializing SPI operation
SSPSTAT=0xC0; //11xxxxxx  (x = don't care)
SSPCON1=0x30; //0011xxxx  (x = don't care)

//Enable I/O
//SDO must have TRISC<7> bit cleared
TRISCbits.TRISC7=0;
//SCK must have TRISB<1> bit cleared.
TRISBbits.TRISB1=0;

//Set up MMA7455L operation mode by writing to hex addr 16, hex value 05 (2g, 4 wires)
spi_w(22,5); // 22 = 0x16, 5 = 0x05
 
 
while (1) {
	val = spi_r(22); // read value back from register and if expected value flash the LED
	if(val==5){ledonoff();}
			};

}// End of the main program


//SPI write command function reg - 1rrrrrrx (bit 7 is 1, next 6 bits reg address and a don't care)
void spi_w(unsigned char regaddr, unsigned char regdata)
{
	unsigned char tmp;
	PORTDbits.RD6 = 0; // pull the CS low to start SPI transmission
	SSPBUF=(((regaddr&0x3F)<<1)|0x80); // send register address to the accelerometer with write command
	while(!SSPSTATbits.BF);// wait for transfer
	tmp=SSPBUF; // reading SSPBUF clears the BF bit
	SSPBUF=regdata; // send data into the accelerometer register
	while(!SSPSTATbits.BF);
	tmp=SSPBUF; // reading SSPBUF clears the BF bit
	PORTDbits.RD6 = 1; // pull the CS high to stop SPI transmission
}


//SPI read function (send read register commands to the accelerometer and output resultant read byte)
unsigned char spi_r(unsigned char reg)
{
	unsigned char temp;
	PORTDbits.RD6 = 0; // pull the CS low to start transmission
	SSPBUF=((reg&0x3F)<<1); // // send register affress to the accelerometer with read command
	while(!SSPSTATbits.BF);// wait for transmit to finish
	temp=SSPBUF; // reading SSPBUF clears the BF bit
	while(!SSPSTATbits.BF); // wait for receive to finish
	temp=SSPBUF; // reading SSPBUF clears the BF bit
	PORTDbits.RD6 = 1; // pull the CS high to stop transmission
	return temp;

}

Last edited by WizzBall; 12th June 2009 at 11:01 PM.
WizzBall is offline  
Old 3rd July 2009, 09:23 AM   #2
Default

Hello i think you don't use the good register:
SSPSTATbits.BF but SSPIF on PIR1bits.SSPIF
I have the same accelerometer (MMA7455L) but using a PIC 18F24K20 and with this code i can read, what i write on SPI...

Code:
//SPI write command function
void spi_w(unsigned char reg, unsigned char data)
{ unsigned char x;
  CS=0;
  SSPBUF=(((reg &0x3F)<<1)|0x80);
  while (!SSPIF);  //wait for transmission complete
  x=SSPBUF;  //dummy read
  SSPBUF=data;
  while (!SSPIF);  //wait for transmission complete
  x=SSPBUF;
  CS=1;
}

//SPI read function

unsigned char spi_r(unsigned char reg)
{ unsigned char x;
  CS=0;
  SSPBUF = 0x00;                  // initiate bus cycle
  while ( !SSPIF );      // wait until cycle complete
  CS=1;
  return ( SSPBUF );              // return with byte read 
}
But now i want make a level for LED on but not working

Code:
char Xdata
while (1) {
  Xdata=spi_r(0x06); //Read X output from Sensor
  DelayUs(10);
if(Xdata<=0x00)LED=1;
else LED=0;
			}
}
Tell me if is good and if you have an idea for my problem...

sorry for my english...
MacArell is offline  
Old 8th July 2009, 02:54 AM   #3
Default

Many thanks MacArell! That was indeed the problem! Looks like it's just the two of us on this thing but I think I know why your code is not working. I believe it is purely a programming issue.

One thing to keep in mind is that when the 'if' condition is not satisfied and is not followed by an 'else if' statement, the PIC will exit the loop. So in your case, when your 'if' statement is not true, it will never get to evaluate your 'else' statement.

You can do it this way and it works just fine for me:

Code:
while (1) {
	val=0;// reset value
	PORTDbits.RD7 = 0;// turn LED ON
	val = spi_r(22); // read value from register
	if(val==5){PORTDbits.RD7 = 1;}// turn LED OFF
	else if(val!=5) {PORTDbits.RD7 = 0;}// turn LED ON
			};
I hope this helps you and thanks again for pointing out my mistake!


- Florian

Last edited by WizzBall; 8th July 2009 at 03:02 AM.
WizzBall is offline  
Old 8th July 2009, 08:26 AM   #4
Default

Thank Florian for your reply!
But i think my last post wasn't good, because on my SDO (MISO, pin 12 connected to my SDO pin PIC) and SDI (MOSI, pin 13 connect to my SDI pin PIC) i have nothing with an oscilloscope (it's good on SCL...) and when i unconnect the MMA7455L my LED work like the MMA7455L was connected... My PIC is master and with SPI 4 wire mode...

For information:
Code:
void initialise_SPI()
{
   SSPEN = 0;            //Enable bit must be cleared to configure SPI
   
   TRISC = TRISC | 0b00010000;   //Setup the TRIS register
   
   CKP = 0;            //IDLE state for clock is a low level
   CKE = 1;            //Output data changes on clock transition from active to idle
   SMP = 0;            //Input data sampled at the middle of data output time
   
   SSPCON1 = SSPCON1 | 0b00000010;   //Speed: Fosc/64
   
   SSPEN = 1;            //Enable the serial port 
}
Code:
//SPI write command function
void spi_w(unsigned char reg, unsigned char data){
  unsigned char x;
  CS=lo;
  SSPBUF=(((reg &0x3F)<<1)|0x80); 
  while (!SSPIF);  //wait for transmission complete
  x=SSPBUF;  //dummy read
  SSPBUF=data;
  while (!SSPIF);  //wait for transmission complete
  x=SSPBUF;
  CS=hi;
}

//SPI read function

unsigned char spi_r(unsigned char reg){
  CS=lo;
  SSPBUF = 0x00;                  // initiate bus cycle
  while ( !BF );      // wait until cycle complete
  CS=hi;
  return ( SSPBUF );              // return with byte read 
}
PS:
the 'if' condition is satisfied when followed by an 'else' alone...
MacArell is offline  
Old 8th July 2009, 08:57 AM   #5
Default

Hi MacArell, please look at my first post to see the proper way of doing a read on the MMA7455L. You are first writing to the MMA7455L to tell it what register to read from and then you are reading the value sent back from MMA7455L. I am not sure if your problem stems from that but try it this way, see if it works as it seems to work for me.

As to the 'else' working for you instead of 'else if', well I'm glad it works, it didn't do it for me .

Hope this helps!

Last edited by WizzBall; 8th July 2009 at 09:02 AM.
WizzBall is offline  
Old 8th July 2009, 11:48 AM   #6
Default

heh WizzBall is correct... have you notice you are not using the reg char from:
"unsigned char spi_r(unsigned char reg)"

Code:
unsigned char spi_r(unsigned char reg){
  CS=lo;
  SSPBUF = 0x00;                  // initiate bus cycle
  while ( !BF );      // wait until cycle complete
  CS=hi;
  return ( SSPBUF );              // return with byte read 
}
EDIT:

i made changes for you from the first posters code here is what yours should look like:
Code:
unsigned char spi_r(unsigned char reg)
{
    unsigned char temp;
    CS=lo;
    SSPBUF=((reg&0x3F)<<1); // send register affress to the accelerometer with read command
    while(!SSPSTATbits.BF); // wait for transmit to finish
    temp=SSPBUF;            // reading SSPBUF clears the BF bit
    while(!SSPSTATbits.BF); // wait for receive to finish
    temp=SSPBUF;            // reading SSPBUF clears the BF bit
    CS=hi;                  // pull the CS high to stop transmission
    return temp;
}

Last edited by AtomSoft; 8th July 2009 at 11:53 AM.
AtomSoft is online now  
Old 8th July 2009, 12:18 PM   #7
Default

Hi AtomSoft, thank's

On my "SPI read function" code, i do a mistake when i cut and copy code, now my code it's:

Code:
//SPI read function (send read register commands to the accelerometer and output resultant read byte)
unsigned char spi_r(unsigned char reg)
{
	unsigned char temp;
	CS = lo; // pull the CS low to start transmission
	SSPBUF=((reg&0x3F)<<1); // // send register address to the accelerometer with read command
	while(!SSPIF);// wait for transmit to finish
	temp=SSPBUF; // reading SSPBUF clears the BF bit
	while(!SSPIF); // wait for receive to finish
	temp=SSPBUF; // reading SSPBUF clears the BF bit
	CS = hi; // pull the CS high to stop transmission
	return temp;

}

Last edited by MacArell; 8th July 2009 at 03:00 PM.
MacArell is offline  
Old 8th July 2009, 02:50 PM   #8
Default

That should work, just correct "'affress"' back to "'address", I made a typo .
WizzBall is offline  
Old 8th July 2009, 02:59 PM   #9
Default

OK Florian //No comment

Last edited by MacArell; 8th July 2009 at 03:01 PM.
MacArell is offline  
Old 8th July 2009, 04:47 PM   #10
Default

@Florian or other:
How you connect you PIC (mine: 18F24K20) to Accelerometer? for me it's 1rst case:

PIC ---> MMA7455L
SCL (14) --> SCL (14)
SDO (16) --> SDO (12)
SDI (15) --> SDI (13)
RB3 (24) --> CS (7)

or you think:

PIC ---> MMA7455L
SCL (14) --> SCL (14)
SDO (16) --> SDI (13)
SDI (15) --> SDO (12)
RB3 (24) --> CS (7)

Thank's

Last edited by MacArell; 8th July 2009 at 04:48 PM.
MacArell is offline  
Old 8th July 2009, 09:21 PM   #11
Default

Hi MacArell,

If it's the first case, it is wrong. Output from the master (PIC) should go into the input of the slave and viceversa. So you should connect pins as per your second arrangement.

Here is a link to the Freescale document that explains in detail how to connect the accelerometer when using SPI:

http://www.freescale.com/files/senso...ote/AN3468.pdf

- Florian
WizzBall is offline  
Old 16th July 2009, 03:54 PM   #12
Talking

Thank you Florian,

I change my SDI and SDO pin but don't working again

In your first Code, where do you make SDI as input (for you SDI is RB0 with TRISBbits.TRISB0=1; )?
For me the SDI input don't reply... no signal on pin from Accelerometer.
my init code:

Code:
void init_ports()
{	ANSEL=0;
	ANSELH=0;
	//Ports Direction:	 "1" inputs and "0" outputs
	TRISA=0b11111000;  //config portA   
	TRISB=0b11110111;  //config portB  RB3=0 (CS SPI)
	TRISC=0b00010000;  //config portC  SCK(RC3)=0, SDI(RC4)=1, SDO(RC5)=0
}

void init_SPI()
{
OpenSPI(FOSC_4,MODE_00,SMPEND); //Mode 00 & SMPEND: CKE=1, CKP=0 & SMP=1
}
I try all init of SPI (CKP=1 like you...) but don't working

EDIT:
I found solution!!!

With all init upstair...
With new code for read and write on SPI:
Code:
//SPI write command function reg
void spi_w(unsigned char regaddr, unsigned char regdata)
{
unsigned char tmp;
CS = lo;	// pull the CS low to start SPI transmission
SSPBUF=(((regaddr&0x3F)<<1)|0x80);	// send register address to the accelerometer with write command
while(!BF);	// wait for transfer
tmp=SSPBUF;	// reading SSPBUF clears the BF bit
SSPBUF=regdata;	// send data into the accelerometer register
while(!BF);
tmp=SSPBUF;	// reading SSPBUF clears the BF bit
CS = hi;	// pull the CS high to stop SPI transmission
}


//SPI read function
unsigned char spi_r(unsigned char reg)
{
unsigned char temp;
CS=lo;
SSPBUF=((reg&0x3F)<<1);	// send register address to the accelerometer with read command
while(!BF);		// wait for transmit to finish
SSPBUF = 0x00;		// reading SSPBUF clears the BF bit
while(!BF);		// wait for receive to finish
temp=SSPBUF;		// reading SSPBUF clears the BF bit
CS=hi;			// pull the CS high to stop transmission
return temp;
}

Last edited by MacArell; 17th July 2009 at 07:59 AM.
MacArell is offline  
Old 22nd July 2009, 06:47 PM   #13
Default

Thank you MacArell!

Here is the solution I developed using some of the Tips and Tricks from AtomSoft's document which I modified for my needs (thanks again Jason! ).

I became frustrated with the 'support' from both Microchip and Freescale so this is SPI done in software. The good thing is it can be used with almost any PIC or even other micros with a bit of adjustment.

I will try your solution and post some feedback.


Code:
#include <p18f4550.h>
#include <delays.h>

#pragma config FOSC = HS // use High Speed crystal (20MHz) - not using PLL right now
#pragma config CPUDIV = OSC4_PLL6 // Set speed for 5MHz (divide crystal speed by 4, PLL by 6)
#pragma config PWRT = ON //Power up timer is ON
#pragma config WDT = OFF // Watchdog timer is OFF
#pragma config LVP = ON // Low Voltage Programming ON
#pragma config BOR = OFF //Brown out reset is OFF
#pragma config DEBUG = ON //Debug is ON
#pragma config VREGEN = OFF //Internal voltage regulator is off (not using USB yet)

#define CS LATDbits.LATD0 //Chip Select Latch
#define SCL LATCbits.LATC0 //Clock Latch
#define SDO LATCbits.LATC1 //Data Out Latch
#define SDI PORTCbits.RC2 //Data In Port

#define CST TRISDbits.TRISD0 //Chip Select TRIS
#define SCLT TRISCbits.TRISC0 //Clock TRIS
#define SDOT TRISCbits.TRISC1 //Data Out TRIS
#define SDIT TRISCbits.TRISC2 //Data In TRIS


unsigned char val=0;

void main(void);
void initSPI(void);
unsigned char ReadSPI(unsigned char address);
void WriteSPI(unsigned char address, unsigned char data);
unsigned char SPI_RByte(void);
void SPI_WByte(unsigned char data);


void main (void)
{

 // Make bit 7 the Port D (LED) output bit.
 TRISDbits.TRISD7 = 0;

 //SPI init
 initSPI();

while (1) {
	val=0;// reset value
	LATDbits.LATD7 = 0;// turn LED OFF
	//Set up MMA7455L operation mode by writing to hex addr 16, hex value 05 (2g, 4 wires)
	WriteSPI(0x16,0x05);
	val = ReadSPI(0x16); // read value back from register and if expected value turn ON the LED
	if(val==0x05){LATDbits.LATD7 = 1;Delay10KTCYx(200);}// turn LED ON
	else if(val!=0x05) {LATDbits.LATD7 = 0;}// turn LED ON
			};


}// End of the main program


void initSPI(void){
	SCLT = 0; //Set the SCL (CLOCK) pin to output
	SDOT = 0; //Set the SDO (DATA OUT) pin to output
	SDIT = 1; //Set the SDI (DATA IN) pin to input
	CST = 0; //Set the CS (Chip Select) pin to output
	CS = 0; //Set Chip Select Low
	SDO = 0; //Set Serial Data Out Low
	SCL = 0; //Set Serial Clock Low
}

void SPI_WByte(unsigned char data){
char x;
	for(x=0;x<8;x++){ //Loop 8 bits
		SCL = 0; //Clock Low
		SDO = 0; //Data Low
		if((data & 0x80) != 0) //Clear entire byte except bit 7 and check if byte is greater than 0
			SDO = 1; //Set the Data Out to HIGH
			Delay10TCY(); //Delay 5uS
			SCL = 1; //Clock High
			Delay10TCY(); //Delay 5uS
			data <<= 1; //Shift data bit out to the right
	}
}

unsigned char SPI_RByte(void){
char x,data;
	for(x=0;x<8;x++){ //Loop 8 bits
		SCL = 0; //Clock Low
		Delay10TCY(); //5uS delay
		data <<= 1; //Shift 1 bit to the left
		data &= 0xFE; //Clear BIT 0 from byte without touching the rest
		if(SDI) //if the Serial Data In is HIGH do below if not then leave it 0
			data |= 1; //OR in a 1 into out byte
			Delay10TCY(); //delay 5uS
			SCL = 1; //Clock High
	}
	return data; //return our data
}

void WriteSPI(unsigned char address, unsigned char data){
	SCL = 1; //Clock High
	CS = 0; //Chip Select Low
	address=(((address&0x3F)<<1)|0x80);
	SPI_WByte(address); //Write our address
	SPI_WByte(data); //Write our data
	CS = 1; //Chip Select High
}

unsigned char ReadSPI(unsigned char address){
unsigned char tmp;
	SCL = 1; //Clock High
	CS = 0; //Chip Select Low
	address=((address<<1)&0x7f);// Send read command together with reg address
	SPI_WByte(address); //Write our address to read from
	tmp = SPI_RByte(); //Get the byte from the device
	CS = 1; //Chip Select High
	return tmp; //Return tmp
}
__________________
Florian
WizzBall is offline  
Reply

Tags
freescale, mma7455l, pic, spi

Thread Tools
Display Modes


Similar
Title Starter Forum Replies Latest
Does Freescale MPC561 drive Data pins 23-0 when instructed to write a byte? naseeam Micro Controllers 0 2nd September 2008 05:11 PM
Freescale Semiconductors shermaine General Electronics Chat 2 19th September 2005 07:16 PM
Keyscanning Routine Problem with Freescale Motorola HCS08. GraveYard_Killer Micro Controllers 0 30th March 2005 02:41 PM



All times are GMT. The time now is 01:27 AM.


Electronic Circuits  |  Learning Electronics
eXTReMe Tracker