Skip to content

Code Snippets and Tricks

Marco Migliorini edited this page Oct 14, 2024 · 26 revisions

This page lists the necessary informations to explain the working logic of the proposed setup.

This system is designed to remain in an ultra low-power state while the device is at rest. The MCU stays in standby mode, and the IMU monitors for motion, generating an interrupt when movement is detected. Once motion is detected, the device is reconfigured: the IMU switches to measuring mode and stores all measurements in its internal FIFO buffer, while the MCU transitions to stop mode 2. This enables the MCU to quickly perform a burst read of the stored measurements.

Measurements are initially logged into an SRAM region on the MCU, providing a local buffer that minimizes writing operations to external permanent memory (to be added in future work). The MPU6050 also generates a zero-motion interrupt when movement ends, allowing the device to return to standby mode when measurements are no longer required.

MPU6050 features

Motion Interrupt

During motion interrupt the MPU6050 ticks the accelerometer axes at a configurable frequency (down to 1.25 Hz) while keeping all the other components into a low power state, reducing the necessary current to ~10 uA (plus 70-80 uA required by the voltage regulator).
The correct procedure to setup the motion interrupt is implemented by the corresponding function located in the source file Core/Src/mpu6050.c .

void MPU6050_setupMotionInt(I2C_HandleTypeDef *I2Cx, uint8_t duration, uint8_t threshold, int16_t* offsets)

The motion duration (1 LSB = 1 ms) and motion threshold (1 LSB = 2 mg) can be configured by the user. From an experimental perspective, I recommend keeping the duration short (1-2 ms) and increasing the threshold parameter to around 30-40 mg for better results.

Zero Motion Interrupt

Zero motion interrupt is used to generate an interrupt to revert the device operation when motion is no longer detected.
The setup procedure is similar to "Motion Interrupt", see the source file Core/Src/mpu6050.c .

void MPU6050_setupZeroMotionInt(I2C_HandleTypeDef *I2Cx, uint8_t duration, uint8_t threshold, int16_t* offsets)

In this case zero_motion_duration (1 LSB = 64 ms) and zero_motion_threshold (1 LSB = 2 mg) should be configured for longer duration (few seconds) and higher threshold to avoid entering sleep state while the device is still moving. The main difference between these two motion interrupts is related to the DHPF (digital high pass filter configuration): while the motion interrupt uses "hold-setting", meaning that the DHPF output is given by the difference between the current sample and the sample stored in the MPU6050 before entering sleep state, the zero motion interrupt is based on "High pass filtering" to remove the bias due to gravity and avoid erroneous trigger signals.

Internal FIFO buffer

The MPU6050 internal FIFO (1024 bytes) is used to store sensors readings and avoid to continually read the outputs which would require to keep the MCU active. FIFO can be configured using the following code:

void MPU6050_setupFifoBuffer(I2C_HandleTypeDef *I2Cx, uint8_t dlpfMode, uint8_t freqDivider, bool overflowEnabled) {
  // force gyro output rate to 1kHz
   MPU6050_setDLPFMode(I2Cx, dlpfMode);

   // frequency divider:
   MPU6050_setRate(I2Cx, freqDivider);

   // enable sensors writing to FIFO:
   MPU6050_setXGyroFIFOEnabled(I2Cx, true);
   MPU6050_setYGyroFIFOEnabled(I2Cx, true);
   MPU6050_setZGyroFIFOEnabled(I2Cx, true);
   MPU6050_setAccelFIFOEnabled(I2Cx, true);

   // enable interrupt generation when the FIFO overflows:
   MPU6050_setIntFIFOBufferOverflowEnabled(I2Cx, overflowEnabled);

   //set trigger event: Active high until interrupt status register is cleared, push-pull configuration
   MPU6050_setInterruptLatch(I2Cx, 1);
   MPU6050_setInterruptLatchClear(I2Cx, 0);
   MPU6050_setInterruptDrive(I2Cx, 0);
   MPU6050_setInterruptMode(I2Cx, 0);

   // Enable the FIFO here:
   MPU6050_setFIFOEnabled(I2Cx, true);

}

The FIFO filling rate depends on the gyroscope output rate, which is configured by setting the DLPF (digital low pass filter, output rate can be 8 kHz or 1 kHz) and the frequency divider (1 - 255). In my application I used the lowest possible frequency: DLPF set to BW = 188 Hz which results in a gyro frequency of 1 kHz, and divider equals to 255, meaning that the sensors sampling frequency was set to ~4 Hz. In my case only accelerometer and gyroscope readings were selected, corresponding to 12 bytes (6 for accel. 6 for gyro.) added to the FIFO each time. The interrupt latch was configured to remain high until the status reg is cleared. This is used to correctly detect the interrupt in the case the MCU is busy while interrupt is generated.

Problem with overflow interrupt
MPU6050 provides the possibility to generate an interrupt when the FIFO overflows. This would be useful to trigger the MCU activation, however there are few problems related to this feature as widely described here. Briefly, the fifo count register updates even if a full packet has not been written in the FIFO yet, potentially resulting in data corruption if the FIFO is read during this "unlucky" cases. The correct fifo sequence should be as follows (if we are storing accel. and gyro. readings):

Expected correct FIFO readings:
| Ax Ay Az Gx Gy Gz Ax Ay Az Gx Gy Gz ... Ax Ay Az Gx Gy Gz Ax Ay Az Gx Gy Gz |

however, sometimes a full 12-byte packet is not written in a single shot, resulting in a potential data corruption:

| Ax Ay Az Gx Gy Gz Ax Ay Az Gx Gy Gz ... Ax Ay Az Gx Gy Gz Ax Ay (Az Gx Gy Gz not written yet) |

If the FIFO overflows with partially written packet:
                    
| Gy Ay Az Gx Gy Gz Ax Ay Az Gx Gy Gz ... Ax Ay Az Gx Gy Gz Ax Ay Az Gx|
  ^                 ^   1st entire  ^     ^ last complete ^                          
Overflow byte              packet               packet
(Gz missing)
(Ax overwritten)

In this situation the first fifo byte would be Ay. There wouldn't be any way to skip the corrupted packet (Ay Az Gx Gy Gz) and move to the first entire packet if we are not sure that the overflowed packet was entirely written. Notice that fifo count stucks at 1024 even if the buffer has overflowed.

This issue makes FIFO overflow useless in my application, given that there wouldn't be any way to reconstruct the measurements without knowing the exact position of the 1st byte in the FIFO. As an alternative solution I used a time based approach, keeping the MCU into stop mode 2 for the time necessary to completely fill the FIFO using 12-byte long packets and 4 Hz sampling rate. This can be done by configuring the RTC to generate an interrupt event to wake the MCU before the fifo overflows.

Clone this wiki locally