SPI Communication - MC9S08QG8 with accelerometer

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPI Communication - MC9S08QG8 with accelerometer

Jump to solution
3,128 Views
victor_carvajal
Contributor I

Hello,

I'm using the following demo board, DEMO9S08QG8, that uses the MC9S08QG8 MCU and I'm trying to talk to a ST accelerometer via SPI,  Attached is the datasheet accelerometer.

 

There is a lot of good existing information on the forums regarding SPI and even some application notes, but i would appreciate another set of eyes into what I could be doing wrong.

 

When I try to communicate w/ the device, i keep getting back 255 as the data.

 

Attached are some code snippets:

 

 

#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */#ifdef __cplusplus extern "C"#endifvoid MCU_init(void); /* Device initialization function declaration */unsigned char memorydata = 0;unsigned char temp = 0;long int address = 0x0F;byte SPI_proc(byte data);void main(void) {  MCU_init(); /* call Device Initialization */  /* include your code here */  for(;;) {    /* __RESET_WATCHDOG(); by default, COP is disabled with device init. When enabling, also reset the watchdog. */        if (PTAD_PTAD2 == 0)  /* read chip */    {      PTBD_PTBD5 = 0;   /* chip select low */      (void)SPI_proc(3);    /* send read instruction to memory */      memorydata = SPI_proc(0x0F);    /* red device id */            PTBD_PTBD5 = 1;   /* chip select high */          } /* loop forever */  /* please make sure that you never leave main */  }}byte SPI_proc(byte data) {    while(!SPIS_SPTEF); /* wait for tX buffer to empty */  SPID = data;        /* send data */  while(!SPIS_SPRF);  /* wait for Rx buffer to empty */  return (SPID);}   

 

 

here's MCU_init()

 

/*** ===================================================================**     Method      :  MCU_init (bean MC9S08QG8_16)****     Description :**         Device initialization code for selected peripherals.** ===================================================================*/void MCU_init(void){  /* ### MC9S08QG8_16 "Cpu" init code ... */  /*  PE initialization code after reset */  /* Common initialization of the write once registers */  /* SOPT1: COPE=0,COPT=1,STOPE=0,BKGDPE=1,RSTPE=0 */  SOPT1 = 0x52;                                        /* SPMSC1: LVDF=0,LVDACK=0,LVDIE=0,LVDRE=1,LVDSE=1,LVDE=1,BGBE=0 */  SPMSC1 = 0x1C;                                        /* SPMSC2: PDF=0,PPDF=0,PPDACK=0,PDC=0,PPDC=0 */  SPMSC2 = 0x00;                                        /* SPMSC3: LVDV=0,LVWV=0 */  SPMSC3 &= (unsigned char)~0x30;                       /*  System clock initialization */  ICSTRM = *(unsigned char*far)0xFFAF; /* Initialize ICSTRM register from a non volatile memory */  ICSSC = *(unsigned char*far)0xFFAE;  /* Initialize ICSSC register from a non volatile memory */  /* ICSC1: CLKS=0,RDIV=0,IREFS=1,IRCLKEN=0,IREFSTEN=0 */  ICSC1 = 0x04;                        /* Initialization of the ICS control register 1 */  /* ICSC2: BDIV=0,RANGE=0,HGO=0,LP=0,EREFS=0,ERCLKEN=0,EREFSTEN=0 */  ICSC2 = 0x00;                        /* Initialization of the ICS control register 2 */  /* Common initialization of the CPU registers */  /* PTASE: PTASE4=1,PTASE3=1,PTASE2=1,PTASE1=1,PTASE0=1 */  PTASE |= (unsigned char)0x1F;                                 /* PTBSE: PTBSE7=1,PTBSE6=1,PTBSE5=1,PTBSE4=1,PTBSE3=1,PTBSE2=1,PTBSE1=1,PTBSE0=1 */  PTBSE = 0xFF;                                        /* PTADS: PTADS5=0,PTADS4=0,PTADS3=0,PTADS2=0,PTADS1=0,PTADS0=0 */  PTADS = 0x00;                                        /* PTBDS: PTBDS7=0,PTBDS6=0,PTBDS5=0,PTBDS4=0,PTBDS3=0,PTBDS2=0,PTBDS1=0,PTBDS0=0 */  PTBDS = 0x00;                                        /* ### Init_SPI init code */  /* SPIC1: SPIE=0,SPE=0,SPTIE=0,MSTR=0,CPOL=0,CPHA=0,SSOE=0,LSBFE=0 */  SPIC1 = 0x00;                        /* The SPRF interrupt flag is cleared when the SPI module is disabled. */  /* SPIC2: MODFEN=0,BIDIROE=0,SPISWAI=0,SPC0=0 */  SPIC2 = 0x00;                                        /* SPIBR: SPPR2=1,SPPR1=1,SPPR0=0,SPR2=1,SPR1=1,SPR0=1 */  SPIBR = 0x67;                                        (void)(SPIS == 0);                   /* Dummy read of the SPIS registr to clear the MODF flag */  /* SPIC1: SPIE=0,SPE=1,SPTIE=0,MSTR=1,CPOL=1,CPHA=1,SSOE=0,LSBFE=0 */  SPIC1 = 0x58;      PTBDD = 0xee;  PTBPE = 0x11;                                        /* ### */  asm CLI;                             /* Enable interrupts */} /*MCU_init*/

 

 

Something that just dawned on me as I write this is that I am not packing the read command, MSB and address field of the register I want into the first byte? Yet I don't see this in other forums, which makes causes me to be suspect.  Figure 7 (attached) from accelerometer datasheet page 24.

 

Any feedback is much appreciated.

Great forums BTW.

 

--Victor

Labels (1)
0 Kudos
1 Solution
852 Views
bigmac
Specialist III

Hello Victor,

 

The accel_comms() function already calls the SPI_proc() function twice, the first time to send the command, and the second to write or read the data byte.  For the read process a dummy byte value of 0x0F is sent, but this could be any 8-bit value.  So the accel_comms() function need be called only once for a single byte.

 

With the function using a pointer to a buffer location, this would also enable the function to be easily adapted to handle successive multiple bytes, by having a buffer array.  You would need to add a further parameter to specify the number of bytes to be read or written.

 

To make the code operation clearer, you might also define the following macros:

#define ACCEL_WR  1

#define ACCEL_RD  3

 

 

Then to read the STATUS_REG_AUX register (also defined by a macro) into the memorydata variable:

accel_comms( STATUS_REG_AUX, ACCEL_RD, &memorydata);

 

 

The dummy read of SPID is not necessary during initialisation, however a read of SPIS status register is necessary prior to sending the first byte after the SPI module is enabled.  This can be done during SPI initialisation with the code:

(void)SPIS;

 

The use of the cast avoids a warning message.  However, since the SPI_proc() function already tests the state of the SPTEF flag within SPIS, prior to sending each byte, in this case the initial read would be redundant.

 

Regards,

Mac

 

 

 

View solution in original post

0 Kudos
11 Replies
852 Views
irob
Contributor V

victor.carvajal wrote:

 

 

SNIP
unsigned char memorydata = 0;
unsigned char temp = 0;
long int address = 0x0F;

SNIP

    if (PTAD_PTAD2 == 0)  /* read chip */
    {

      PTBD_PTBD5 = 0;   /* chip select low */

      (void)SPI_proc(3);    /* send read instruction to memory */
      memorydata = SPI_proc(0x0F);    /* red device id */

      PTBD_PTBD5 = 1;   /* chip select high */
   
    } /* loop forever */
  /* please make sure that you never leave main */
  }
}

What is PTA2 connected to? I see from your comments that PTB5 is connected to chip select.  What is the register address 0x0F?  When you send the read instruction of 0x03 to the device, what happens to that data?  You aren't returning it.  So then you call that function again, but this time with the register address, but without a repeated read instruction.

 

You need to rethink your read function.  I would recommend putting the read command inside the read function.  That should always happen any time you want to read from a register.

 

/* No such thing as overly-commented code! */

 

 

0 Kudos
852 Views
irob
Contributor V

Victor, I recently completed a QG8-based project which used the LIS331DLF (same family I think as yours). Beautiful little part!

 

I opted to use the I2C interface instead of the SPI, so I can't speak to your SPI code. But in practice, your last realization is very poignant!  You must indeed request data from a particular register before reading any data.

0 Kudos
852 Views
sureshee
Contributor I

Hi,

I m working on LIS3DH, i configured it for wakeup interrupt, and it was generating the interrupt for the interrupt threshold which i gave it in INT1_THS.

I made the Accelerometer to be configured for FullScale of 2G, but when i try to retrieve values from X,Y,Z registers through all Axis, the value goes to a maximum range of 1000mg(~1g),

when it is configured for 4G, the maximum range of 90 degree shift is 500mg(~.5g) and for the

8G, it is `250mg and

16g it shows ~80mg.

So when i try to set an interrupt threshold of above this range of value, i could'n able to trigger the interrupt,since it is not going beyond that particular range in that G level. 

Then how will it be more precise for rise in G level. Kindly any one clear me about this doubt.

 

Regards

Suresh

0 Kudos
852 Views
victor_carvajal
Contributor I

hey irob,

 

I modified my original code snippet to follow suit but i still get the response back as 255.

 

 if (PTAD_PTAD2 == 0)  /* read chip */    {      PTBD_PTBD5 = 0;   /* chip select low */      // address: bit 2-7      // bit 0: read command      // bit 1: multiple read      temp = (((address & 0x3F)<<2)|0x80);                                     memorydata = SPI_proc(temp);            PTBD_PTBD5 = 1;   /* chip select high */     } 

 

Are you suggesting that the read command needs to be separate?

 

0 Kudos
852 Views
bigmac
Specialist III

Hello Victor,

 

I cannot see any issues with your original code structure except that you were initially attempting to write to address 0x03, which is a reserved address, according to the datasheet.  The datasheet also gives dire warnings about writing to reserved locations, with the risk of permanent damage to the device.  Perhaps your code might test whether the address is valid to avoid this problem.

 

Your later post seems to be erroneous with respect to placement of the R/W and MS bits - these are at the bit 7 and bit 6 positions respectively.  Additionally, you are only processing a single byte, rather than two bytes minimum.  The following function code attempts to correct these issues, but does not show the test for a valid address.  A common function is used for both read and write.

 

void accel_comms( byte addr, byte ctrl_bits, byte *data)

{

   // Test for valid address value here

 

   PTBD_PTBD5 = 0;   /* chip select low */

   (void)SPI_proc((addr & 0x3F) | ((ctrl_bits & 0x03) << 6)); // send instruction

   if (ctrl_bits < 2)  // Write mode

      (void)SPI_proc( *data);

   else                // Read mode
      *data = SPI_proc(0x0F);
      
   PTBD_PTBD5 = 1;   /* chip select high */

}
 

 

 

Regards,

Mac

 

0 Kudos
852 Views
victor_carvajal
Contributor I

Hi Mac,

 

Thanks for the note about my bit misplacement regarding R/W and MS bits.

I've seem in your responses regarding SPI about having to push dummy bytes in order to get the desired result back.

For instance, incorporating your common function, I have to do the following:

 

 

accel_comms(address, 3, &memorydata); // returns 0accel_comms(address, 3, &memorydata); // returns desired register result

 

 

Which gives me a decimal value that maps to a register value in the datasheet.

Is a good initialization technique to

  (void)(SPID == 0);                   /* Dummy read of the device register */

twice in a init() method?

 

Also, how would one get automated email to replies? I always seem to be having to check back on the forums to see if any response occured.

 

Thanks again everyone, big help.  Free beer if you are even in S.Cal, North County San Diego area.

( to iRob and bigMac atleast ) :smileyhappy:

0 Kudos
852 Views
irob
Contributor V

victor.carvajal wrote:

Thanks again everyone, big help.  Free beer if you are even in S.Cal, North County San Diego area.

( to iRob and bigMac atleast ) :smileyhappy:


I'll hand off my beer to the professionals. Apparently I was way off with my suggestions.  I suppose the I2C method is radically different than SPI.

 

You're in good hands, Victor!

0 Kudos
853 Views
bigmac
Specialist III

Hello Victor,

 

The accel_comms() function already calls the SPI_proc() function twice, the first time to send the command, and the second to write or read the data byte.  For the read process a dummy byte value of 0x0F is sent, but this could be any 8-bit value.  So the accel_comms() function need be called only once for a single byte.

 

With the function using a pointer to a buffer location, this would also enable the function to be easily adapted to handle successive multiple bytes, by having a buffer array.  You would need to add a further parameter to specify the number of bytes to be read or written.

 

To make the code operation clearer, you might also define the following macros:

#define ACCEL_WR  1

#define ACCEL_RD  3

 

 

Then to read the STATUS_REG_AUX register (also defined by a macro) into the memorydata variable:

accel_comms( STATUS_REG_AUX, ACCEL_RD, &memorydata);

 

 

The dummy read of SPID is not necessary during initialisation, however a read of SPIS status register is necessary prior to sending the first byte after the SPI module is enabled.  This can be done during SPI initialisation with the code:

(void)SPIS;

 

The use of the cast avoids a warning message.  However, since the SPI_proc() function already tests the state of the SPTEF flag within SPIS, prior to sending each byte, in this case the initial read would be redundant.

 

Regards,

Mac

 

 

 

0 Kudos
852 Views
peg
Senior Contributor IV

Hello,

 

When you are logged in, up the top of the page "My Settings" then under the subscriptions tab you can setup your email notifications there.

 

Just trying to get a ticket to the free beer.......

 

0 Kudos
852 Views
irob
Contributor V
Again, I used I2C. I'm not all that familiar with SPI transaction protocol. But in the I2C world, the format is: (for writing) Start Command 7-bit base address + !W bit (LSB) Register address Data Stop bit (for reading) Start Command 7-bit base address + !W bit (LSB) Register address to read from Start Command 7-bit base address + R bit (LSB) Data Stop bit
0 Kudos
852 Views
aresmarser
Contributor III

Hi ROB LUND,

I am using NXP NHS3100 kit and added my sensor LIS3DH with I2C interface based on the project "app_demo_dp_tlogger" from the NHS3100 SDK.

I init I2C and use the API functions "Chip_I2C_MasterSend" and "Chip_I2C_MasterRead" but it didn't work properly.

I am looking for some help and found that you have ever use LIS3DH with I2C interface.

Anthing you can share or help please?

Thanks,

Arna

0 Kudos