Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC SPI and Freescale MMA7455L

Status
Not open for further replies.

WizzBall

New Member
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:
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...
 
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:
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... :eek: 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...
 
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 :D.

Hope this helps! :)
 
Last edited:
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:
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:
@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:
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!!! :D

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:
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! :D).

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
}
 
hi, i used the source code above to use with my csm12c32 dev. board which has mc9s12c32 microcontroller on it. i made the required changes to use my gpio pins. now i can read registers but i can not write to them. i can read i2c address register with correct value 1D. but i can not write to ant r/w register. especially mode register 0x16. i opened a new topic before i have seen this post. there is a detailed explanation there, with my source code.

https://www.electro-tech-online.com/threads/mc9s12c32-to-drive-mma7455l-freescale-accelerometer.101899/

any ideas about what is wrong?

to read a register both spi_read and spi_write functions must work properly. and i can do this. but i can not write to a register which requires multiple write operation.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top