Author Topic: MC3479 Accelerometer Troubles  (Read 2301 times)

0 Members and 1 Guest are viewing this topic.

Offline CMASupraTopic starter

  • Newbie
  • Posts: 9
  • Country: us
MC3479 Accelerometer Troubles
« on: January 20, 2021, 04:39:29 am »
Hello. I'm trying to use an accelerometer (the mCube or MEMSIC MC3479) to detect when a user is shaking my device (a dice) in their hand. This accelerometer supports "shake" interrupts, which is the feature I am having trouble getting working.

I successfully got "tilt" interrupts working (though the interrupts only came when I had the dice flat, not when tilted, which seems backward). The only difference in my code between using "tilt" and "shake" interrupts is in the initialization code.

Getting the "tilt" interrupts working (though backward) makes me confident I'm successfully reading and writing with the accelerometer and that my interrupt line coming from INTN1 on the accelerometer going to the microcontroller is working. It makes me believe that the problem is either in the accelerometer itself or in my accelerometer initialization code.

The accelerometer initialization code for "shake" interrupts (not working):
Code: [Select]
// Set up the accelerometer (ACC) by writing data to it through SPI.
spiWriteToAcc(0x07, 0x00); // enter STANDBY state and disable I2C WDT
while ((spiReadFromAcc(0x05) & 0x03) != 0x00); // wait for the ACC to enter STANDBY state
spiWriteToAcc(0x33, 0x88); // INTN2 = push-pull & active-low. INTN1 = push-pull & active-low.
spiWriteToAcc(0x08, 0x13); // set internal data rate (IDR) to 100Hz
spiWriteToAcc(0x20, 0x20); // 8g range (4096 steps), no low pass filter
spiWriteToAcc(0x2D, 0x08); // FIFO disabled, all interrupts combined onto INTN1
spiWriteToAcc(0x31, 0x00); // don't swap INTN1 and INTN2, 4-wire SPI mode, clear all interrupts together
spiWriteToAcc(0x09, 0x4C); // enable ANYM and SHAKE features, use real-time raw data
spiWriteToAcc(0x46, 0x00); // set SHAKE threshold LSB
spiWriteToAcc(0x47, 0x10); // set SHAKE threshold MSB
spiWriteToAcc(0x48, 0x80); // set SHAKE duration LSB
spiWriteToAcc(0x49, 0x30); // set SHAKE duration MSB and count threshold
spiWriteToAcc(0x06, 0x08); // only enable SHAKE interrupts, don't auto-clear interrupts
spiWriteToAcc(0x07, 0x01); // enter WAKE state and disable I2C WDT
while ((spiReadFromAcc(0x05) & 0x03) != 0x01); // wait for the ACC to enter WAKE state
spiWriteToAcc(0x14, 0x00); // clear any pending interrupts

The accelerometer initialization code for "tilt" interrupts (working but backward):
Code: [Select]
// Set up the accelerometer (ACC) by writing data to it through SPI.
spiWriteToAcc(0x07, 0x00); // enter STANDBY state and disable I2C WDT
while ((spiReadFromAcc(0x05) & 0x03) != 0x00); // wait for the ACC to enter STANDBY state
spiWriteToAcc(0x33, 0x88); // INTN2 = push-pull & active-low. INTN1 = push-pull & active-low.
spiWriteToAcc(0x08, 0x13); // set internal data rate (IDR) to 100Hz
spiWriteToAcc(0x20, 0x20); // 8g range (4096 steps), no low pass filter
spiWriteToAcc(0x2D, 0x08); // FIFO disabled, all interrupts combined onto INTN1
spiWriteToAcc(0x31, 0x00); // don't swap INTN1 and INTN2, 4-wire SPI mode, clear all interrupts together
spiWriteToAcc(0x09, 0x41); // enable TILT/FLIP feature, use real-time raw data
spiWriteToAcc(0x40, 0x20); // set TILT threshold LSB (lower number = device must be flatter to get interrupts)
spiWriteToAcc(0x41, 0x00); // set TILT threshold MSB
spiWriteToAcc(0x42, 0x04); // set TILT debounce time
spiWriteToAcc(0x06, 0x01); // only enable TILT interrupts, don't auto-clear interrupts
spiWriteToAcc(0x07, 0x01); // enter WAKE state and disable I2C WDT
while ((spiReadFromAcc(0x05) & 0x03) != 0x01); // wait for the ACC to enter WAKE state
spiWriteToAcc(0x14, 0x00); // clear any pending interrupts

Main loop code looking for interrupts from the accelerometer:
Code: [Select]
int main(void)
{
/* Replace with your application code */

init();
PORTA.OUT = 0x00; // turn all 7 LEDs on
_delay_ms(500);
PORTA.OUT = 0xFE; // turn all 7 LEDs off

// Main loop.
while (1)
{
if ((PORTB.IN & 0x04) == 0x00) // if an interrupt has occurred
{
PORTA.OUT = nextVal(PORTA.OUT);
spiWriteToAcc(0x14, 0x00); // clear the interrupt
_delay_ms(200);
}
}
}

I should note that a "shake" interrupt seems to often trigger when I turn the dice off, back on, and then shake it. I am unable to get a second interrupt to trigger after that without restarting the dice again. This is only true with the "shake" initialization settings in the code above. If I use the following "shake" initialization settings, I very rarely get a single interrupt, and I never get multiple interrupts.
Code: [Select]
spiWriteToAcc(0x46, 0x00); // set SHAKE threshold LSB
spiWriteToAcc(0x47, 0x10); // set SHAKE threshold MSB
spiWriteToAcc(0x48, 0x02); // set SHAKE duration LSB
spiWriteToAcc(0x49, 0x10); // set SHAKE duration MSB and count threshold

I have tried using "if ((spiReadFromAcc(0x13) & 0x08) != 0x00)" in my main loop instead of using "if ((PORTB.IN & 0x04) == 0x00)", and it seems the "shake" bit in register 0x13 of the accelerometer is reacting to me shaking the dice, but there isn't an interrupt at PORTB if I go back to the larger main loop code I pasted above.

I have attached the relevant section of the schematic for my dice. VCC=3.0V for my testing.1154634-0

I appreciate any help that can be offered. I have access to a good amount of lab equipment if I should test anything, though my soldering skills are very bad so I'd rather not solder anything unless I have to.

Edit:
In case it helps, here are the functions I use to read and write to/from the accelerometer. The microcontroller is an ATtiny406 in a VQFN package.
Code: [Select]
uint8_t inline makeAccWriteCommand(uint8_t addr)
{
return (addr & 0x7F); // clear the MSb to tell the ACC this is a write
}

uint8_t inline makeAccReadCommand(uint8_t addr)
{
return (addr | 0x80); // set the MSb to tell the ACC this is a read
}

/**
 * Writes 1 byte of data to the accelerometer (ACC) over SPI.
 *
 * @param addr Register address to write to on ACC.
 * @param data The byte to write to the register on the ACC.
 */
void spiWriteToAcc(uint8_t addr, uint8_t data)
{
addr = makeAccWriteCommand(addr); // modify addr to be a write
PORTC.OUTCLR = 0x08; // send SS low/active

SPI0.DATA = addr; // send the register address to the ACC
while (!(SPI0.INTFLAGS & 0x80)); // wait for the data to be fully sent
addr = SPI0.DATA; // clear the interrupt flag by reading INTFLAGS and then DATA

SPI0.DATA = data; // send the register data to the ACC
while (!(SPI0.INTFLAGS & 0x80)); // wait for the data to be fully sent
addr = SPI0.DATA; // clear the interrupt flag by reading INTFLAGS and then DATA

PORTC.OUTSET = 0x08; // send SS high/inactive
}

/**
 * Reads 1 byte of data from the accelerometer (ACC) over SPI.
 *
 * @param addr Register address to read on the ACC.
 * @return The byte of data read from the ACC.
 */
uint8_t spiReadFromAcc(uint8_t addr)
{
addr = makeAccReadCommand(addr); // modify addr to be a read
PORTC.OUTCLR = 0x08; // send SS low/active

SPI0.DATA = addr; // send the register address to the ACC
while (!(SPI0.INTFLAGS & 0x80)); // wait for the data to be fully sent
addr = SPI0.DATA; // clear the interrupt flag by reading INTFLAGS and then DATA

SPI0.DATA = 0x00; // send a blank byte while waiting for the ACC to get the data
while (!(SPI0.INTFLAGS & 0x80)); // wait for the data to be fully sent
addr = SPI0.DATA; // clear the interrupt flag by reading INTFLAGS and then DATA

SPI0.DATA = 0x00; // send a blank byte while reading from the ACC
while (!(SPI0.INTFLAGS & 0x80)); // wait for the data to be fully sent
addr = SPI0.DATA; // clear the interrupt flag by reading INTFLAGS and then DATA

PORTC.OUTSET = 0x08; // send SS high/inactive

return addr; // return the data read from the ACC
}
« Last Edit: January 20, 2021, 04:45:44 am by CMASupra »
 

Offline Kappa

  • Newbie
  • Posts: 1
  • Country: za
Re: MC3479 Accelerometer Troubles
« Reply #1 on: March 17, 2022, 01:56:44 pm »
Did you find a solution to the problem? Considering using it.
 

Offline CMASupraTopic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: MC3479 Accelerometer Troubles
« Reply #2 on: March 19, 2022, 05:17:09 pm »
I did not find a solution even after trying the sample code provided by mCube online to use their MC3479A Evaluation Board with an Arduino DUE. What's weird is their sample code writes to registers that don't exist according to the datasheet.
Arduino sample code: https://github.com/mcubemems/mCube_mc34x9_arduino_driver
Instructions for sample code: https://mcubemems.com/wp-content/uploads/2020/02/EV3419A-QSG-and-Demo-APS-045-0033v1.0.pdf
MC3479 datasheet: https://www.mouser.com/datasheet/2/821/20200827164636363-1900743.pdf

Part of the problem is that I just don't understand the descriptions related to SHAKE in the datasheet (ANYMOTION threshold, ANYMOTION debounce, SHAKE threshold, SHAKE duration, SHAKE peak-to-peak). The descriptions are on pages 71-74 of the datasheet.

I'm still open to advice from anyone.
 

Offline tepalia02

  • Regular Contributor
  • *
  • Posts: 100
  • Country: bd
Re: MC3479 Accelerometer Troubles
« Reply #3 on: May 11, 2022, 01:44:51 pm »
Are you applying Kalman filtering? Without that, accelerometers and gyros are more likely to give unrealistic data.
 

Offline CMASupraTopic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: MC3479 Accelerometer Troubles
« Reply #4 on: May 16, 2022, 03:12:48 pm »
I’m not using any kind of filtering because I’m not looking at the raw data. I’m only trying to use the shake feature of the accelerometer, which I assume would do any filtering in hardware.

However, thanks for telling me about that filtering. I do see that I was writing 0 to bit 6 of register 0x9, which means debounce was not being used. I can try enabling debounce and report back. You also gave me something to research if I have to use the raw data instead of the shake feature or a different accelerometer.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf