A fall detection algorithm can determine if an object has fallen by detecting changes in acceleration and angular velocity. When implementing the MPU6050 fall detection algorithm, the first step is to read the raw data from the MPU6050 sensor.
Then, calculate the rate of change of acceleration and angular velocity to judge if a fall has occurred. Typically, a threshold can be set to determine if acceleration and angular velocity exceed the normal range. If they exceed the threshold, the object is considered to have fallen. Additionally, machine learning algorithms can be used to improve the accuracy of fall detection.
By training on a large amount of data from fall and non-fall states, a model can be built to determine if a fall has occurred. This method better adapts to different objects and environments, enhancing the sensitivity and accuracy of fall detection.
Introduction to the Main Hardware Used in the Device
There are many introductions to the MPU6050 online from various bloggers.https://zhuanlan.zhihu.com/p/346764320
I. Introduction to the MPU6050
The MPU-60X0 is the world's first 9-axis motion processing sensor. It integrates a 3-axis MEMS gyroscope, a 3-axis MEMS accelerometer, and an extensible Digital Motion Processor (DMP).
It can connect to a third-party digital sensor, such as a magnetometer, via the I2C interface. After expansion, it can output a 9-axis signal through its I2C or SPI interface (the SPI interface is only available on the MPU-6000). The MPU-60X0 can also connect to non-inertial digital sensors, such as pressure sensors, via its I2C interface.
The MPU-60X0 uses three 16-bit ADCs (0~65535) for the gyroscope and accelerometer respectively, converting the measured analog quantities into output digital quantities.
To accurately track both fast and slow movements, the sensor's measurement ranges are user-configurable: the gyroscope can measure ±250, ±500, ±1000, ±2000°/s (dps), and the accelerometer can measure ±2, ±4, ±8, ±16g.
The chip measures 4×4×0.9mm, uses a QFN package (Quad Flat No-leads package), can withstand a maximum shock of 10,000g, and features a programmable low-pass filter.
The internal block diagram of the MPU6050 is shown below:
Among the pins to understand: SCL and SDA are the IIC interfaces connecting to the MCU, which controls the MPU6050 through this IIC interface. There is another IIC interface: AUX_CL and AUX_DA, which can be used to connect external slave devices such as a magnetometer, forming a 9-axis sensor.
VLOGIC is the IO port voltage; this pin can go as low as 1.8V, and we usually connect it directly to VDD. AD0 is the address control pin for the slave IIC interface (connected to the MCU), controlling the least significant bit of the IIC address. If connected to GND, the MPU6050's IIC address is 0X68; if connected to VDD, it is 0X69.
Note: The addresses 0x68 and 0x69 here do not include the least significant bit used for data transmission, so they are not 8-bit data. For example, 0x68 represents 110 1000, and 0x69 represents 110 1001. The least significant bit is typically used to indicate the IIC master's read/write mode. Self-test is for self-diagnosis, which tests the sensor's mechanical and electrical structure to check if the chip is damaged. After self-test is activated, the circuit activates the sensor and generates an output signal. Detailed descriptions of self-test can be found in the official chip manual, as follows:
- Gyroscope Self-Test When self-test is activated, the on-board electronics will actuate the appropriate sensor. This actuation will move the sensor’s proof masses over a distance equivalent to a pre-defined Coriolis force. This proof mass displacement results in a change in the sensor output, which is reflected in the output signal. The output signal is used to observe the self-test response. The self-test response is defined as follows: Self-test response = Sensor output with self-test enabled – Sensor output without self-test enabledThe self-test limits for each gyroscope axis is provided in the electrical characteristics tables of the MPU-6000/MPU-6050 Product Specification document. When the value of the self-test response is within the min/max limits of the product specification, the part has passed self test. When the self-test response exceeds the min/max values specified in the document, the part is deemed to have failed self-test.
- Accelerometer Self-Test When self-test is activated, the on-board electronics will actuate the appropriate sensor. This actuation simulates an external force. The actuated sensor, in turn, will produce a corresponding output signal. The output signal is used to observe the self-test response. The self-test response is defined as follows: Self-test response = Sensor output with self-test enabled – Sensor output without self-test enabledThe self-test limits for each accelerometer axis is provided in the electrical characteristics tables of the MPU-6000/MPU-6050 Product Specification document. When the value of the self-test response is within the min/max limits of the product specification, the part has passed self test. When the self-test response exceeds the min/max values specified in the document, the part is deemed to have failed self-test.
II. MPU6050 Related Registers
The official MPU6050 register manual describes the contents and functions of 40 registers. Here, we only select some commonly used and important registers for understanding.
1. Sample Rate Divider Register
Description: This register specifies the divider for the gyroscope output rate, used to generate the sampling rate for the MPU-60X0.
Sensor register output, FIFO output, DMP sampling, motion detection, stationary detection, and free-fall detection are all based on this sampling frequency.
Sampling frequency = Gyroscope output frequency / (1 + SMPLRT_DIV)
When the DLPF (Digital Low-Pass Filter, see Configuration Register) is disabled (DLPF_CFG=0 or 7), the gyroscope output frequency = 8kHz; when DLPF is enabled, the gyroscope output frequency = 1kHz. Note: The accelerometer output frequency is 1kHz. This means that when the sampling frequency is greater than 1kHz, the same accelerometer sample data may be output to the FIFO, DMP, and sensor registers more than once.
2. Configuration Register
Description: This register configures the external frame synchronization (FSYNC) pin sampling and Digital Low-Pass Filter (DLPF) settings for the gyroscope and accelerometer.
The DLPF is configured by DLPF_CFG. The accelerometer and gyroscope are filtered according to the DLPF_CFG values shown in the table below.
Where FS is the gyroscope output frequency. SMPLRT_DIV is calculated from the preset sampling frequency using the formula above. Generally, the DLPF cutoff frequency is half the sampling frequency. For example, if the sampling frequency is set to 50Hz, from the table, when FS is 1kHz, SMPLRT_DIV = 1000/50 - 1 = 19.
3. Gyroscope Configuration Register
Description: This register is used to trigger gyroscope self-test and configure the gyroscope's full-scale range.
XG_ST, YG_ST, ZG_ST are used to set self-test for the gyroscope's X, Y, Z axes respectively; setting to 0 disables self-test. FS_SEL[1:0] is used to set the gyroscope's full-scale range, as shown in the table:
We usually set it to 3, meaning the full-scale range is ±2000°/s. Since a 16-bit ADC (0-65536) is used, the sensitivity G = 65536/4000 = 16.4 LSB/(°/s), where LSB stands for Least Significant Bit, meaning 1°/s corresponds to a digital value of 16.4. Finally, the digital data output by the gyroscope can be converted to angular velocity.
4. Accelerometer Configuration Register
Description: This register is used to trigger accelerometer self-test and configure the accelerometer's full-scale range. It can also be used to configure the Digital High-Pass Filter (DHPF).
XA_ST, YA_ST, ZA_ST are used to set self-test for the accelerometer's X, Y, Z axes respectively; setting to 0 disables self-test. AFS_SEL[1:0] is used to select the accelerometer's full-scale range, as shown in the table:
We usually set it to 0, meaning the full-scale range is ±2g. Since a 16-bit ADC (0-65536) is used, the sensitivity G = 65536/4 = 16384 LSB/g, where LSB stands for Least Significant Bit, meaning 1g corresponds to a digital value of 16384. Finally, the digital data output by the accelerometer can be converted to acceleration.
5. Accelerometer Measurements Register
Description: This register stores the latest accelerometer measurements. The accelerometer writes to these registers according to the sampling frequency (set by the Sample Rate Divider Register). For example, if the sampling frequency is 50Hz, data is written at 0.02s intervals. The accelerometer measurements register, temperature measurements register, gyroscope measurements register, and external sensor data register all consist of two register sets: an internal register set and a user-read register set.
Data in the accelerometer sensor's internal register set is updated according to the sampling frequency. Meanwhile, whenever the serial interface is idle, the user-read register set copies the data values from the internal register set. This ensures that sensor registers can read measurements from the same sampling moment during burst reads. Note: If no burst read is performed, the user is responsible for ensuring that a set of instantaneous single-byte reads corresponds to single-byte sample data by checking the Data Ready interrupt.
Parameters:
· ACCEL_XOUT: 16-bit value consisting of 2 parts, storing the latest X-axis accelerometer measurement.
· ACCEL_YOUT: 16-bit value consisting of 2 parts, storing the latest Y-axis accelerometer measurement.
· ACCEL_ZOUT: 16-bit value consisting of 2 parts, storing the latest Z-axis accelerometer measurement.
6. Gyroscope Measurements Register
Description: This register stores the latest gyroscope measurements. Its structure is roughly the same as the accelerometer measurements register, so it will not be described here. The parameters are GYRO_XOUT, GYRO_YOUT, GYRO_ZOUT.
7. Power Management 1 Register
Description: This register allows the user to configure power modes and clock sources, and also provides bits to reset the entire device and disable the temperature sensor. When the SLEEP bit is set to 1, the MPU-60X0 can enter low-power sleep mode. When SLEEP is disabled and CYCLE is set to 1, the MPU-60X0 enters Cycle Mode. In Cycle Mode, the device cycles between sleep and wake-up to acquire a single data sample from active sensors at a rate determined by LP_WAKE_CTRL (configured by the Power Management 2 Register).
The lowest three bits of this register are used to set the system clock source selection. The default value is 0 (internal 8MHz RC oscillator), but it is generally set to 1 to select the X-axis gyro PLL as the clock source for higher precision. At the same time, the angular velocity sensor and acceleration sensor are enabled by setting the corresponding bits to 0 in the Power Management 2 Register.
Excerpt from the English manual: Upon power up, the MPU-60X0 clock source defaults to the internal oscillator. However, it is highly recommended that the device be configured to use one of the gyroscopes (or an ext ernal clock source) as the clock reference for improved stability.
Other parameters:
· DEVICE_RESET: Setting this bit to 1 resets the internal registers to default values. This bit is automatically cleared to 0 after reset.
· TEMP_DIS: Setting this bit to 1 disables the temperature sensor.
8. Power Management 2 Register
Description: This register allows the user to configure the wake-up frequency in accelerometer low-power mode. It also allows individual axes of the accelerometer and gyroscope to enter standby mode.
Steps to put only the MPU-60X0's accelerometer into low-power mode:
1. Set CYCLE bit to 1
2. Set SLEEP bit to 1
3. Set TEMP_DIS bit to 1
4. Set STBY_XG, STBY_YG, STBY_ZG bits to 1
In this mode, the device shuts down all peripherals except the main I2C interface, and the accelerometer only wakes up at fixed intervals to take a single measurement. The wake-up frequency can be configured via LP_WAKE_CTRL as follows:
Parameters:
· LP_WAKE_CTRL: 2-bit unsigned value. Specifies the accelerometer's wake-up frequency in low-power mode.
· STBY_XA: Setting this bit to 1 puts the accelerometer's X-axis into standby mode.
· STBY_YA: Setting this bit to 1 puts the accelerometer's Y-axis into standby mode.
· STBY_ZA: Setting this bit to 1 puts the accelerometer's Z-axis into standby mode.
· STBY_XG: Setting this bit to 1 puts the gyroscope's X-axis into standby mode.
· STBY_YG: Setting this bit to 1 puts the gyroscope's Y-axis into standby mode.
· STBY_ZG: Setting this bit to 1 puts the gyroscope's Z-axis into standby mode.
We have provided a brief introduction to the MPU6050, gaining some understanding of this module.
mpu6050.h
#ifndef __MPU6050_H #define __MPU6050_H #include <stdlib.h> #include <stdbool.h> #include <stdarg.h> #include <stddef.h> #include <string.h> #include <math.h> #include <stdint.h> //Added for uint_t #include <stdio.h> #include "bflb_mtimer.h" #define MPU6050_ADDRESS_AD0_LOW 0x68 // address pin low (GND), default for InvenSense evaluation board #define MPU6050_ADDRESS_AD0_HIGH 0x69 // address pin high (VCC) #define MPU6050_DEFAULT_ADDRESS MPU6050_ADDRESS_AD0_LOW #define MPU6050_RA_XG_OFFS_TC 0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD #define MPU6050_RA_YG_OFFS_TC 0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD #define MPU6050_RA_ZG_OFFS_TC 0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD #define MPU6050_RA_X_FINE_GAIN 0x03 //[7:0] X_FINE_GAIN #define MPU6050_RA_Y_FINE_GAIN 0x04 //[7:0] Y_FINE_GAIN #define MPU6050_RA_Z_FINE_GAIN 0x05 //[7:0] Z_FINE_GAIN #define MPU6050_RA_XA_OFFS_H 0x06 //[15:0] XA_OFFS #define MPU6050_RA_XA_OFFS_L_TC 0x07 #define MPU6050_RA_YA_OFFS_H 0x08 //[15:0] YA_OFFS #define MPU6050_RA_YA_OFFS_L_TC 0x09 #define MPU6050_RA_ZA_OFFS_H 0x0A //[15:0] ZA_OFFS #define MPU6050_RA_ZA_OFFS_L_TC 0x0B #define MPU6050_RA_XG_OFFS_USRH 0x13 //[15:0] XG_OFFS_USR #define MPU6050_RA_XG_OFFS_USRL 0x14 #define MPU6050_RA_YG_OFFS_USRH 0x15 //[15:0] YG_OFFS_USR #define MPU6050_RA_YG_OFFS_USRL 0x16 #define MPU6050_RA_ZG_OFFS_USRH 0x17 //[15:0] ZG_OFFS_USR #define MPU6050_RA_ZG_OFFS_USRL 0x18 #define MPU6050_RA_SMPLRT_DIV 0x19 #define MPU6050_RA_CONFIG 0x1A #define MPU6050_RA_GYRO_CONFIG 0x1B #define MPU6050_RA_ACCEL_CONFIG 0x1C #define MPU6050_RA_FF_THR 0x1D #define MPU6050_RA_FF_DUR 0x1E #define MPU6050_RA_MOT_THR 0x1F #define MPU6050_RA_MOT_DUR 0x20 #define MPU6050_RA_ZRMOT_THR 0x21 #define MPU6050_RA_ZRMOT_DUR 0x22 #define MPU6050_RA_FIFO_EN 0x23 #define MPU6050_RA_I2C_MST_CTRL 0x24 #define MPU6050_RA_I2C_SLV0_ADDR 0x25 #define MPU6050_RA_I2C_SLV0_REG 0x26 #define MPU6050_RA_I2C_SLV0_CTRL 0x27 #define MPU6050_RA_I2C_SLV1_ADDR 0x28 #define MPU6050_RA_I2C_SLV1_REG 0x29 #define MPU6050_RA_I2C_SLV1_CTRL 0x2A #define MPU6050_RA_I2C_SLV2_ADDR 0x2B #define MPU6050_RA_I2C_SLV2_REG 0x2C #define MPU6050_RA_I2C_SLV2_CTRL 0x2D #define MPU6050_RA_I2C_SLV3_ADDR 0x2E #define MPU6050_RA_I2C_SLV3_REG 0x2F #define MPU6050_RA_I2C_SLV3_CTRL 0x30 #define MPU6050_RA_I2C_SLV4_ADDR 0x31 #define MPU6050_RA_I2C_SLV4_REG 0x32 #define MPU6050_RA_I2C_SLV4_DO 0x33 #define MPU6050_RA_I2C_SLV4_CTRL 0x34 #define MPU6050_RA_I2C_SLV4_DI 0x35 #define MPU6050_RA_I2C_MST_STATUS 0x36 #define MPU6050_RA_INT_PIN_CFG 0x37 #define MPU6050_RA_INT_ENABLE 0x38 #define MPU6050_RA_DMP_INT_STATUS 0x39 #define MPU6050_RA_INT_STATUS 0x3A #define MPU6050_RA_ACCEL_XOUT_H 0x3B #define MPU6050_RA_ACCEL_XOUT_L 0x3C #define MPU6050_RA_ACCEL_YOUT_H 0x3D #define MPU6050_RA_ACCEL_YOUT_L 0x3E #define MPU6050_RA_ACCEL_ZOUT_H 0x3F #define MPU6050_RA_ACCEL_ZOUT_L 0x40 #define MPU6050_RA_TEMP_OUT_H 0x41 #define MPU6050_RA_TEMP_OUT_L 0x42 #define MPU6050_RA_GYRO_XOUT_H 0x43 #define MPU6050_RA_GYRO_XOUT_L 0x44 #define MPU6050_RA_GYRO_YOUT_H 0x45 #define MPU6050_RA_GYRO_YOUT_L 0x46 #define MPU6050_RA_GYRO_ZOUT_H 0x47 #define MPU6050_RA_GYRO_ZOUT_L 0x48 #define MPU6050_RA_EXT_SENS_DATA_00 0x49 #define MPU6050_RA_EXT_SENS_DATA_01 0x4A #define MPU6050_RA_EXT_SENS_DATA_02 0x4B #define MPU6050_RA_EXT_SENS_DATA_03 0x4C #define MPU6050_RA_EXT_SENS_DATA_04 0x4D #define MPU6050_RA_EXT_SENS_DATA_05 0x4E #define MPU6050_RA_EXT_SENS_DATA_06 0x4F #define MPU6050_RA_EXT_SENS_DATA_07 0x50 #define MPU6050_RA_EXT_SENS_DATA_08 0x51 #define MPU6050_RA_EXT_SENS_DATA_09 0x52 #define MPU6050_RA_EXT_SENS_DATA_10 0x53 #define MPU6050_RA_EXT_SENS_DATA_11 0x54 #define MPU6050_RA_EXT_SENS_DATA_12 0x55 #define MPU6050_RA_EXT_SENS_DATA_13 0x56 #define MPU6050_RA_EXT_SENS_DATA_14 0x57 #define MPU6050_RA_EXT_SENS_DATA_15 0x58 #define MPU6050_RA_EXT_SENS_DATA_16 0x59 #define MPU6050_RA_EXT_SENS_DATA_17 0x5A #define MPU6050_RA_EXT_SENS_DATA_18 0x5B #define MPU6050_RA_EXT_SENS_DATA_19 0x5C #define MPU6050_RA_EXT_SENS_DATA_20 0x5D #define MPU6050_RA_EXT_SENS_DATA_21 0x5E #define MPU6050_RA_EXT_SENS_DATA_22 0x5F #define MPU6050_RA_EXT_SENS_DATA_23 0x60 #define MPU6050_RA_MOT_DETECT_STATUS 0x61 #define MPU6050_RA_I2C_SLV0_DO 0x63 #define MPU6050_RA_I2C_SLV1_DO 0x64 #define MPU6050_RA_I2C_SLV2_DO 0x65 #define MPU6050_RA_I2C_SLV3_DO 0x66 #define MPU6050_RA_I2C_MST_DELAY_CTRL 0x67 #define MPU6050_RA_SIGNAL_PATH_RESET 0x68 #define MPU6050_RA_MOT_DETECT_CTRL 0x69 #define MPU6050_RA_USER_CTRL 0x6A #define MPU6050_RA_PWR_MGMT_1 0x6B #define MPU6050_RA_PWR_MGMT_2 0x6C #define MPU6050_RA_BANK_SEL 0x6D #define MPU6050_RA_MEM_START_ADDR 0x6E #define MPU6050_RA_MEM_R_W 0x6F #define MPU6050_RA_DMP_CFG_1 0x70 #define MPU6050_RA_DMP_CFG_2 0x71 #define MPU6050_RA_FIFO_COUNTH 0x72 #define MPU6050_RA_FIFO_COUNTL 0x73 #define MPU6050_RA_FIFO_R_W 0x74 #define MPU6050_RA_WHO_AM_I 0x75 #define MPU6050_TC_PWR_MODE_BIT 7 #define MPU6050_TC_OFFSET_BIT 6 #define MPU6050_TC_OFFSET_LENGTH 6 #define MPU6050_TC_OTP_BNK_VLD_BIT 0 #define MPU6050_VDDIO_LEVEL_VLOGIC 0 #define MPU6050_VDDIO_LEVEL_VDD 1 #define MPU6050_CFG_EXT_SYNC_SET_BIT 5 #define MPU6050_CFG_EXT_SYNC_SET_LENGTH 3 #define MPU6050_CFG_DLPF_CFG_BIT 2 #define MPU6050_CFG_DLPF_CFG_LENGTH 3 #define MPU6050_EXT_SYNC_DISABLED 0x0 #define MPU6050_EXT_SYNC_TEMP_OUT_L 0x1 #define MPU6050_EXT_SYNC_GYRO_XOUT_L 0x2 #define MPU6050_EXT_SYNC_GYRO_YOUT_L 0x3 #define MPU6050_EXT_SYNC_GYRO_ZOUT_L 0x4 #define MPU6050_EXT_SYNC_ACCEL_XOUT_L 0x5 #define MPU6050_EXT_SYNC_ACCEL_YOUT_L 0x6 #define MPU6050_EXT_SYNC_ACCEL_ZOUT_L 0x7 #define MPU6050_DLPF_BW_256 0x00 #define MPU6050_DLPF_BW_188 0x01 #define MPU6050_DLPF_BW_98 0x02 #define MPU6050_DLPF_BW_42 0x03 #define MPU6050_DLPF_BW_20 0x04 #define MPU6050_DLPF_BW_10 0x05 #define MPU6050_DLPF_BW_5 0x06 #define MPU6050_GCONFIG_FS_SEL_BIT 4 #define MPU6050_GCONFIG_FS_SEL_LENGTH 2 #define MPU6050_GYRO_FS_250 0x00 #define MPU6050_GYRO_FS_500 0x01 #define MPU6050_GYRO_FS_1000 0x02 #define MPU6050_GYRO_FS_2000 0x03 #define MPU6050_ACONFIG_XA_ST_BIT 7 #define MPU6050_ACONFIG_YA_ST_BIT 6 #define MPU6050_ACONFIG_ZA_ST_BIT 5 #define MPU6050_ACONFIG_AFS_SEL_BIT 4 #define MPU6050_ACONFIG_AFS_SEL_LENGTH 2 #define MPU6050_ACONFIG_ACCEL_HPF_BIT 2 #define MPU6050_ACONFIG_ACCEL_HPF_LENGTH 3 #define MPU6050_ACCEL_FS_2 0x00 #define MPU6050_ACCEL_FS_4 0x01 #define MPU6050_ACCEL_FS_8 0x02 #define MPU6050_ACCEL_FS_16 0x03 #define MPU6050_DHPF_RESET 0x00 #define MPU6050_DHPF_5 0x01 #define MPU6050_DHPF_2P5 0x02 #define MPU6050_DHPF_1P25 0x03 #define MPU6050_DHPF_0P63 0x04 #define MPU6050_DHPF_HOLD 0x07 #define MPU6050_TEMP_FIFO_EN_BIT 7 #define MPU6050_XG_FIFO_EN_BIT 6 #define MPU6050_YG_FIFO_EN_BIT 5 #define MPU6050_ZG_FIFO_EN_BIT 4 #define MPU6050_ACCEL_FIFO_EN_BIT 3 #define MPU6050_SLV2_FIFO_EN_BIT 2 #define MPU6050_SLV1_FIFO_EN_BIT 1 #define MPU6050_SLV0_FIFO_EN_BIT 0 #define MPU6050_MULT_MST_EN_BIT 7 #define MPU6050_WAIT_FOR_ES_BIT 6 #define MPU6050_SLV_3_FIFO_EN_BIT 5 #define MPU6050_I2C_MST_P_NSR_BIT 4 #define MPU6050_I2C_MST_CLK_BIT 3 #define MPU6050_I2C_MST_CLK_LENGTH 4 #define MPU6050_CLOCK_DIV_348 0x0 #define MPU6050_CLOCK_DIV_333 0x1 #define MPU6050_CLOCK_DIV_320 0x2 #define MPU6050_CLOCK_DIV_308 0x3 #define MPU6050_CLOCK_DIV_296 0x4 #define MPU6050_CLOCK_DIV_286 0x5 #define MPU6050_CLOCK_DIV_276 0x6 #define MPU6050_CLOCK_DIV_267 0x7 #define MPU6050_CLOCK_DIV_258 0x8 #define MPU6050_CLOCK_DIV_500 0x9 #define MPU6050_CLOCK_DIV_471 0xA #define MPU6050_CLOCK_DIV_444 0xB #define MPU6050_CLOCK_DIV_421 0xC #define MPU6050_CLOCK_DIV_400 0xD #define MPU6050_CLOCK_DIV_381 0xE #define MPU6050_CLOCK_DIV_364 0xF #define MPU6050_I2C_SLV_RW_BIT 7 #define MPU6050_I2C_SLV_ADDR_BIT 6 #define MPU6050_I2C_SLV_ADDR_LENGTH 7 #define MPU6050_I2C_SLV_EN_BIT 7 #define MPU6050_I2C_SLV_BYTE_SW_BIT 6 #define MPU6050_I2C_SLV_REG_DIS_BIT 5 #define MPU6050_I2C_SLV_GRP_BIT 4 #define MPU6050_I2C_SLV_LEN_BIT 3 #define MPU6050_I2C_SLV_LEN_LENGTH 4 #define MPU6050_I2C_SLV4_RW_BIT 7 #define MPU6050_I2C_SLV4_ADDR_BIT 6 #define MPU6050_I2C_SLV4_ADDR_LENGTH 7 #define MPU6050_I2C_SLV4_EN_BIT 7 #define MPU6050_I2C_SLV4_INT_EN_BIT 6 #define MPU6050_I2C_SLV4_REG_DIS_BIT 5 #define MPU6050_I2C_SLV4_MST_DLY_BIT 4 #define MPU6050_I2C_SLV4_MST_DLY_LENGTH 5 #define MPU6050_MST_PASS_THROUGH_BIT 7 #define MPU6050_MST_I2C_SLV4_DONE_BIT 6 #define MPU6050_MST_I2C_LOST_ARB_BIT 5 #define MPU6050_MST_I2C_SLV4_NACK_BIT 4 #define MPU6050_MST_I2C_SLV3_NACK_BIT 3 #define MPU6050_MST_I2C_SLV2_NACK_BIT 2 #define MPU6050_MST_I2C_SLV1_NACK_BIT 1 #define MPU6050_MST_I2C_SLV0_NACK_BIT 0 #define MPU6050_INTCFG_INT_LEVEL_BIT 7 #define MPU6050_INTCFG_INT_OPEN_BIT 6 #define MPU6050_INTCFG_LATCH_INT_EN_BIT 5 #define MPU6050_INTCFG_INT_RD_CLEAR_BIT 4 #define MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT 3 #define MPU6050_INTCFG_FSYNC_INT_EN_BIT 2 #define MPU6050_INTCFG_I2C_BYPASS_EN_BIT 1 #define MPU6050_INTCFG_CLKOUT_EN_BIT 0 #define MPU6050_INTMODE_ACTIVEHIGH 0x00 #define MPU6050_INTMODE_ACTIVELOW 0x01 #define MPU6050_INTDRV_PUSHPULL 0x00 #define MPU6050_INTDRV_OPENDRAIN 0x01 #define MPU6050_INTLATCH_50USPULSE 0x00 #define MPU6050_INTLATCH_WAITCLEAR 0x01 #define MPU6050_INTCLEAR_STATUSREAD 0x00 #define MPU6050_INTCLEAR_ANYREAD 0x01 #define MPU6050_INTERRUPT_FF_BIT 7 #define MPU6050_INTERRUPT_MOT_BIT 6 #define MPU6050_INTERRUPT_ZMOT_BIT 5 #define MPU6050_INTERRUPT_FIFO_OFLOW_BIT 4 #define MPU6050_INTERRUPT_I2C_MST_INT_BIT 3 #define MPU6050_INTERRUPT_PLL_RDY_INT_BIT 2 #define MPU6050_INTERRUPT_DMP_INT_BIT 1 #define MPU6050_INTERRUPT_DATA_RDY_BIT 0 // TODO: figure out what these actually do // UMPL source code is not very obivous #define MPU6050_DMPINT_5_BIT 5 #define MPU6050_DMPINT_4_BIT 4 #define MPU6050_DMPINT_3_BIT 3 #define MPU6050_DMPINT_2_BIT 2 #define MPU6050_DMPINT_1_BIT 1 #define MPU6050_DMPINT_0_BIT 0 #define MPU6050_MOTION_MOT_XNEG_BIT 7 #define MPU6050_MOTION_MOT_XPOS_BIT 6 #define MPU6050_MOTION_MOT_YNEG_BIT 5 #define MPU6050_MOTION_MOT_YPOS_BIT 4 #define MPU6050_MOTION_MOT_ZNEG_BIT 3 #define MPU6050_MOTION_MOT_ZPOS_BIT 2 #define MPU6050_MOTION_MOT_ZRMOT_BIT 0 #define MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT 7 #define MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT 4 #define MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT 3 #define MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT 2 #define MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT 1 #define MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT 0 #define MPU6050_PATHRESET_GYRO_RESET_BIT 2 #define MPU6050_PATHRESET_ACCEL_RESET_BIT 1 #define MPU6050_PATHRESET_TEMP_RESET_BIT 0 #define MPU6050_DETECT_ACCEL_ON_DELAY_BIT 5 #define MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH 2 #define MPU6050_DETECT_FF_COUNT_BIT 3 #define MPU6050_DETECT_FF_COUNT_LENGTH 2 #define MPU6050_DETECT_MOT_COUNT_BIT 1 #define MPU6050_DETECT_MOT_COUNT_LENGTH 2 #define MPU6050_DETECT_DECREMENT_RESET 0x0 #define MPU6050_DETECT_DECREMENT_1 0x1 #define MPU6050_DETECT_DECREMENT_2 0x2 #define MPU6050_DETECT_DECREMENT_4 0x3 #define MPU6050_USERCTRL_DMP_EN_BIT 7 #define MPU6050_USERCTRL_FIFO_EN_BIT 6 #define MPU6050_USERCTRL_I2C_MST_EN_BIT 5 #define MPU6050_USERCTRL_I2C_IF_DIS_BIT 4 #define MPU6050_USERCTRL_DMP_RESET_BIT 3 #define MPU6050_USERCTRL_FIFO_RESET_BIT 2 #define MPU6050_USERCTRL_I2C_MST_RESET_BIT 1 #define MPU6050_USERCTRL_SIG_COND_RESET_BIT 0 #define MPU6050_PWR1_DEVICE_RESET_BIT 7 #define MPU6050_PWR1_SLEEP_BIT 6 #define MPU6050_PWR1_CYCLE_BIT 5 #define MPU6050_PWR1_TEMP_DIS_BIT 3 #define MPU6050_PWR1_CLKSEL_BIT 2 #define MPU6050_PWR1_CLKSEL_LENGTH 3 #define MPU6050_CLOCK_INTERNAL 0x00 #define MPU6050_CLOCK_PLL_XGYRO 0x01 #define MPU6050_CLOCK_PLL_YGYRO 0x02 #define MPU6050_CLOCK_PLL_ZGYRO 0x03 #define MPU6050_CLOCK_PLL_EXT32K 0x04 #define MPU6050_CLOCK_PLL_EXT19M 0x05 #define MPU6050_CLOCK_KEEP_RESET 0x07 #define MPU6050_PWR2_LP_WAKE_CTRL_BIT 7 #define MPU6050_PWR2_LP_WAKE_CTRL_LENGTH 2 #define MPU6050_PWR2_STBY_XA_BIT 5 #define MPU6050_PWR2_STBY_YA_BIT 4 #define MPU6050_PWR2_STBY_ZA_BIT 3 #define MPU6050_PWR2_STBY_XG_BIT 2 #define MPU6050_PWR2_STBY_YG_BIT 1 #define MPU6050_PWR2_STBY_ZG_BIT 0 #define MPU6050_WAKE_FREQ_1P25 0x0 #define MPU6050_WAKE_FREQ_2P5 0x1 #define MPU6050_WAKE_FREQ_5 0x2 #define MPU6050_WAKE_FREQ_10 0x3 #define MPU6050_BANKSEL_PRFTCH_EN_BIT 6 #define MPU6050_BANKSEL_CFG_USER_BANK_BIT 5 #define MPU6050_BANKSEL_MEM_SEL_BIT 4 #define MPU6050_BANKSEL_MEM_SEL_LENGTH 5 #define MPU6050_WHO_AM_I_BIT 6 #define MPU6050_WHO_AM_I_LENGTH 6 #define MPU6050_DMP_MEMORY_BANKS 8 #define MPU6050_DMP_MEMORY_BANK_SIZE 256 #define MPU6050_DMP_MEMORY_CHUNK_SIZE 16 // 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];") #define I2CDEV_DEFAULT_READ_TIMEOUT 1000 void MPU6050(); // SMPLRT_DIV register uint8_t getRate(); void setRate(uint8_t rate); uint8_t getClockSource(); void setClockSource(uint8_t source); // GYRO_CONFIG register uint8_t getFullScaleGyroRange(); void setFullScaleGyroRange(uint8_t range); uint8_t getFullScaleAccelRange(); void setFullScaleAccelRange(uint8_t range); uint8_t getDHPFMode(); void setDHPFMode(uint8_t mode); // PWR_MGMT_1 register void reset(); bool getSleepEnabled(); void setSleepEnabled(bool enabled); bool getTempSensorEnabled(); void setTempSensorEnabled(bool enabled); uint8_t getMasterClockSpeed(); void setMasterClockSpeed(uint8_t speed); int8_t writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); int8_t writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); int8_t writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); int8_t writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data); int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data); int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data); int getState(); // WHO_AM_I register uint8_t getDeviceID(); void getAcceleration(int16_t* x, int16_t* y, int16_t* z); int16_t getAccelerationX(); int16_t getAccelerationY(); int16_t getAccelerationZ(); // TEMP_OUT_* registers int16_t getTemperature(); // GYRO_*OUT_* registers void getRotation(int16_t* x, int16_t* y, int16_t* z); int16_t getRotationX(); int16_t getRotationY(); int16_t getRotationZ(); void getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz); #endif</stdio.h></stdint.h></math.h></string.h></stddef.h></stdarg.h></stdbool.h></stdlib.h>
mpu6050.c
#include "Wire.h"
#include "MPU6050.h"
#include "log.h"
#define MPU6050_ADDR 0x68
uint8_t buffer[14];
int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data) {
beginTransmission(devAddr);
write_char(regAddr);
requestFrom(devAddr, length);
endTransmission();
int8_t count = 0;
while (available()) {
data[count] = readI2c();
count++;
}
LOG_E("=============================\r\n");
for (size_t i = 0; i < count; i++)
{
LOG_E("%2x data%d: %2x \r\n", regAddr, i, data[0]);
}
LOG_E("==============================\r\n");
return count;
}
int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data) {
return readBytes(devAddr, regAddr, 1, data);
}
/** Read a single bit from an 8-bit device register.
* @param devAddr I2C slave device address
* @param regAddr Register regAddr to read from
* @param bitNum Bit position to read (0-7)
* @param data Container for single bit value
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
* @return Status of read operation (true = success)
*/
int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data) {
uint8_t b;
uint8_t count = readByte(devAddr, regAddr, &b);
*data = b & (1 << bitNum);
return count;
}
/** Read multiple bits from an 8-bit device register.
* @param devAddr I2C slave device address
* @param regAddr Register regAddr to read from
* @param bitStart First bit position to read (0-7)
* @param length Number of bits to read (not more than 8)
* @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05)
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
* @return Status of read operation (true = success)
*/
int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data) {
// 01101001 read byte
// 76543210 bit numbers
// xxx args: bitStart=4, length=3
// 010 masked
// -> 010 shifted
uint8_t count, b;
if ((count = readByte(devAddr, regAddr, &b)) != 0) {
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
b &= mask;
b >>= (bitStart - length + 1);
*data = b;
}
return count;
}
int8_t writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) {
beginTransmission(devAddr);
write_char((uint8_t) regAddr); // send address
for (uint8_t i = 0; i < length; i++) {
write_char((uint8_t) data[i]);
}
endTransmission();
return 0;
}
int8_t writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
return writeBytes(devAddr, regAddr, 1, &data);
}
int8_t writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) {
// 010 value to write
// 76543210 bit numbers
// xxx args: bitStart=4, length=3
// 00011100 mask byte
// 10101111 original value (sample)
// 10100011 original & ~mask
// 10101011 masked | value
uint8_t b;
if(readByte(devAddr, regAddr, &b)!=0){
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
data <<= (bitStart - length + 1); // shift data into correct position
data &= mask; // zero all non-important bits in data
b &= ~(mask); // zero all important bits in existing byte
b |= data; // combine data with existing byte
return writeByte(devAddr, regAddr, b);
}
return 0;
}
int8_t writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) {
uint8_t b;
readByte(devAddr, regAddr, &b);
b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
return writeByte(devAddr, regAddr, b);
}
/** Get clock source setting.
* @return Current clock source setting
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_CLKSEL_BIT
* @see MPU6050_PWR1_CLKSEL_LENGTH
*/
uint8_t getClockSource() {
readBits(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, buffer);
return buffer[0];
}
/** Set clock source setting.
* An internal 8MHz oscillator, gyroscope based clock, or external sources can
* be selected as the MPU-60X0 clock source. When the internal 8 MHz oscillator
* or an external source is chosen as the clock source, the MPU-60X0 can operate
* in low power modes with the gyroscopes disabled.
*
* Upon power up, the MPU-60X0 clock source defaults to the internal oscillator.
* However, it is highly recommended that the device be configured to use one of
* the gyroscopes (or an external clock source) as the clock reference for
* improved stability. The clock source can be selected according to the following table:
*
* <pre> * CLK_SEL | Clock Source
* --------+--------------------------------------
* 0 | Internal oscillator
* 1 | PLL with X Gyro reference
* 2 | PLL with Y Gyro reference
* 3 | PLL with Z Gyro reference
* 4 | PLL with external 32.768kHz reference
* 5 | PLL with external 19.2MHz reference
* 6 | Reserved
* 7 | Stops the clock and keeps the timing generator in reset
* </pre>
*
* @param source New clock source setting
* @see getClockSource()
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_CLKSEL_BIT
* @see MPU6050_PWR1_CLKSEL_LENGTH
*/
void setClockSource(uint8_t source) {
writeBits(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, source);
}
// GYRO_CONFIG register
/** Get full-scale gyroscope range.
* The FS_SEL parameter allows setting the full-scale range of the gyro sensors,
* as described in the table below.
*
* <pre> * 0 = +/- 250 degrees/sec
* 1 = +/- 500 degrees/sec
* 2 = +/- 1000 degrees/sec
* 3 = +/- 2000 degrees/sec
* </pre>
*
* @return Current full-scale gyroscope range setting
* @see MPU6050_GYRO_FS_250
* @see MPU6050_RA_GYRO_CONFIG
* @see MPU6050_GCONFIG_FS_SEL_BIT
* @see MPU6050_GCONFIG_FS_SEL_LENGTH
*/
uint8_t getFullScaleGyroRange() {
readBits(MPU6050_ADDR, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, buffer);
return buffer[0];
}
/** Set full-scale gyroscope range.
* @param range New full-scale gyroscope range value
* @see getFullScaleRange()
* @see MPU6050_GYRO_FS_250
* @see MPU6050_RA_GYRO_CONFIG
* @see MPU6050_GCONFIG_FS_SEL_BIT
* @see MPU6050_GCONFIG_FS_SEL_LENGTH
*/
void setFullScaleGyroRange(uint8_t range) {
writeBits(MPU6050_ADDR, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, range);
}
/** Get full-scale accelerometer range.
* The FS_SEL parameter allows setting the full-scale range of the accelerometer
* sensors, as described in the table below.
*
* <pre> * 0 = +/- 2g
* 1 = +/- 4g
* 2 = +/- 8g
* 3 = +/- 16g
* </pre>
*
* @return Current full-scale accelerometer range setting
* @see MPU6050_ACCEL_FS_2
* @see MPU6050_RA_ACCEL_CONFIG
* @see MPU6050_ACONFIG_AFS_SEL_BIT
* @see MPU6050_ACONFIG_AFS_SEL_LENGTH
*/
uint8_t getFullScaleAccelRange() {
readBits(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, buffer);
return buffer[0];
}
/** Set full-scale accelerometer range.
* @param range New full-scale accelerometer range setting
* @see getFullScaleAccelRange()
*/
void setFullScaleAccelRange(uint8_t range) {
writeBits(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range);
}
/** Get the high-pass filter configuration.
* The DHPF is a filter module in the path leading to motion detectors (Free
* Fall, Motion threshold, and Zero Motion). The high pass filter output is not
* available to the data registers (see Figure in Section 8 of the MPU-6000/
* MPU-6050 Product Specification document).
*
* The high pass filter has three modes:
*
* <pre> * Reset: The filter output settles to zero within one sample. This
* effectively disables the high pass filter. This mode may be toggled
* to quickly settle the filter.
*
* On: The high pass filter will pass signals above the cut off frequency.
*
* Hold: When triggered, the filter holds the present sample. The filter
* output will be the difference between the input sample and the held
* sample.
* </pre>
*
* <pre> * ACCEL_HPF | Filter Mode | Cut-off Frequency
* ----------+-------------+------------------
* 0 | Reset | None
* 1 | On | 5Hz
* 2 | On | 2.5Hz
* 3 | On | 1.25Hz
* 4 | On | 0.63Hz
* 7 | Hold | None
* </pre>
*
* @return Current high-pass filter configuration
* @see MPU6050_DHPF_RESET
* @see MPU6050_RA_ACCEL_CONFIG
*/
uint8_t getDHPFMode() {
readBits(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, buffer);
return buffer[0];
}
/** Set the high-pass filter configuration.
* @param bandwidth New high-pass filter configuration
* @see setDHPFMode()
* @see MPU6050_DHPF_RESET
* @see MPU6050_RA_ACCEL_CONFIG
*/
void setDHPFMode(uint8_t bandwidth) {
writeBits(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, bandwidth);
}
/** Get I2C master clock speed.
* I2C_MST_CLK is a 4 bit unsigned value which configures a divider on the
* MPU-60X0 internal 8MHz clock. It sets the I2C master clock speed according to
* the following table:
*
* <pre> * I2C_MST_CLK | I2C Master Clock Speed | 8MHz Clock Divider
* ------------+------------------------+-------------------
* 0 | 348kHz | 23
* 1 | 333kHz | 24
* 2 | 320kHz | 25
* 3 | 308kHz | 26
* 4 | 296kHz | 27
* 5 | 286kHz | 28
* 6 | 276kHz | 29
* 7 | 267kHz | 30
* 8 | 258kHz | 31
* 9 | 500kHz | 16
* 10 | 471kHz | 17
* 11 | 444kHz | 18
* 12 | 421kHz | 19
* 13 | 400kHz | 20
* 14 | 381kHz | 21
* 15 | 364kHz | 22
* </pre>
*
* @return Current I2C master clock speed
* @see MPU6050_RA_I2C_MST_CTRL
*/
uint8_t getMasterClockSpeed() {
readBits(MPU6050_ADDR, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, buffer);
return buffer[0];
}
/** Set I2C master clock speed.
* @reparam speed Current I2C master clock speed
* @see MPU6050_RA_I2C_MST_CTRL
*/
void setMasterClockSpeed(uint8_t speed) {
writeBits(MPU6050_ADDR, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, speed);
}
/** Get 3-axis accelerometer readings.
* These registers store the most recent accelerometer measurements.
* Accelerometer measurements are written to these registers at the Sample Rate
* as defined in Register 25.
*
* The accelerometer measurement registers, along with the temperature
* measurement registers, gyroscope measurement registers, and external sensor
* data registers, are composed of two sets of registers: an internal register
* set and a user-facing read register set.
*
* The data within the accelerometer sensors' internal register set is always
* updated at the Sample Rate. Meanwhile, the user-facing read register set
* duplicates the internal register set's data values whenever the serial
* interface is idle. This guarantees that a burst read of sensor registers will
* read measurements from the same sampling instant. Note that if burst reads
* are not used, the user is responsible for ensuring a set of single byte reads
* correspond to a single sampling instant by checking the Data Ready interrupt.
*
* Each 16-bit accelerometer measurement has a full scale defined in ACCEL_FS
* (Register 28). For each full scale setting, the accelerometers' sensitivity
* per LSB in ACCEL_xOUT is shown in the table below:
*
* <pre> * AFS_SEL | Full Scale Range | LSB Sensitivity
* --------+------------------+----------------
* 0 | +/- 2g | 8192 LSB/mg
* 1 | +/- 4g | 4096 LSB/mg
* 2 | +/- 8g | 2048 LSB/mg
* 3 | +/- 16g | 1024 LSB/mg
* </pre>
*
* @param x 16-bit signed integer container for X-axis acceleration
* @param y 16-bit signed integer container for Y-axis acceleration
* @param z 16-bit signed integer container for Z-axis acceleration
* @see MPU6050_RA_GYRO_XOUT_H
*/
void getAcceleration(int16_t* x, int16_t* y, int16_t* z) {
readBytes(MPU6050_ADDR, MPU6050_RA_ACCEL_XOUT_H, 6, buffer);
*x = (((int16_t)buffer[0]) << 8) | buffer[1];
*y = (((int16_t)buffer[2]) << 8) | buffer[3];
*z = (((int16_t)buffer[4]) << 8) | buffer[5];
}
/** Get X-axis accelerometer reading.
* @return X-axis acceleration measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_ACCEL_XOUT_H
*/
int16_t getAccelerationX() {
readBytes(MPU6050_ADDR, MPU6050_RA_ACCEL_XOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
/** Get Y-axis accelerometer reading.
* @return Y-axis acceleration measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_ACCEL_YOUT_H
*/
int16_t getAccelerationY() {
readBytes(MPU6050_ADDR, MPU6050_RA_ACCEL_YOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
/** Get Z-axis accelerometer reading.
* @return Z-axis acceleration measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_ACCEL_ZOUT_H
*/
int16_t getAccelerationZ() {
readBytes(MPU6050_ADDR, MPU6050_RA_ACCEL_ZOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
// TEMP_OUT_* registers
/** Get current internal temperature.
* @return Temperature reading in 16-bit 2's complement format
* @see MPU6050_RA_TEMP_OUT_H
*/
int16_t getTemperature() {
readBytes(MPU6050_ADDR, MPU6050_RA_TEMP_OUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
// GYRO_*OUT_* registers
/** Get 3-axis gyroscope readings.
* These gyroscope measurement registers, along with the accelerometer
* measurement registers, temperature measurement registers, and external sensor
* data registers, are composed of two sets of registers: an internal register
* set and a user-facing read register set.
* The data within the gyroscope sensors' internal register set is always
* updated at the Sample Rate. Meanwhile, the user-facing read register set
* duplicates the internal register set's data values whenever the serial
* interface is idle. This guarantees that a burst read of sensor registers will
* read measurements from the same sampling instant. Note that if burst reads
* are not used, the user is responsible for ensuring a set of single byte reads
* correspond to a single sampling instant by checking the Data Ready interrupt.
*
* Each 16-bit gyroscope measurement has a full scale defined in FS_SEL
* (Register 27). For each full scale setting, the gyroscopes' sensitivity per
* LSB in GYRO_xOUT is shown in the table below:
*
* <pre> * FS_SEL | Full Scale Range | LSB Sensitivity
* -------+--------------------+----------------
* 0 | +/- 250 degrees/s | 131 LSB/deg/s
* 1 | +/- 500 degrees/s | 65.5 LSB/deg/s
* 2 | +/- 1000 degrees/s | 32.8 LSB/deg/s
* 3 | +/- 2000 degrees/s | 16.4 LSB/deg/s
* </pre>
*
* @param x 16-bit signed integer container for X-axis rotation
* @param y 16-bit signed integer container for Y-axis rotation
* @param z 16-bit signed integer container for Z-axis rotation
* @see getMotion6()
* @see MPU6050_RA_GYRO_XOUT_H
*/
void getRotation(int16_t* x, int16_t* y, int16_t* z) {
readBytes(MPU6050_ADDR, MPU6050_RA_GYRO_XOUT_H, 6, buffer);
*x = (((int16_t)buffer[0]) << 8) | buffer[1];
*y = (((int16_t)buffer[2]) << 8) | buffer[3];
*z = (((int16_t)buffer[4]) << 8) | buffer[5];
}
/** Get X-axis gyroscope reading.
* @return X-axis rotation measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_GYRO_XOUT_H
*/
int16_t getRotationX() {
readBytes(MPU6050_ADDR, MPU6050_RA_GYRO_XOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
/** Get Y-axis gyroscope reading.
* @return Y-axis rotation measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_GYRO_YOUT_H
*/
int16_t getRotationY() {
readBytes(MPU6050_ADDR, MPU6050_RA_GYRO_YOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
/** Get Z-axis gyroscope reading.
* @return Z-axis rotation measurement in 16-bit 2's complement format
* @see getMotion6()
* @see MPU6050_RA_GYRO_ZOUT_H
*/
int16_t getRotationZ() {
readBytes(MPU6050_ADDR, MPU6050_RA_GYRO_ZOUT_H, 2, buffer);
return (((int16_t)buffer[0]) << 8) | buffer[1];
}
// PWR_MGMT_1 register
/** Trigger a full device reset.
* A small delay of ~50ms may be desirable after triggering a reset.
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_DEVICE_RESET_BIT
*/
void reset() {
writeBit(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_DEVICE_RESET_BIT, true);
}
/** Get sleep mode status.
* Setting the SLEEP bit in the register puts the device into very low power
* sleep mode. In this mode, only the serial interface and internal registers
* remain active, allowing for a very low standby current. Clearing this bit
* puts the device back into normal mode. To save power, the individual standby
* selections for each of the gyros should be used if any gyro axis is not used
* by the application.
* @return Current sleep mode enabled status
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_SLEEP_BIT
*/
bool getSleepEnabled() {
readBit(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, buffer);
return buffer[0];
}
/** Set sleep mode status.
* @param enabled New sleep mode enabled status
* @see getSleepEnabled()
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_SLEEP_BIT
*/
void setSleepEnabled(bool enabled) {
writeBit(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled);
}
/** Get temperature sensor enabled status.
* Control the usage of the internal temperature sensor.
*
* Note: this register stores the *disabled* value, but for consistency with the
* rest of the code, the function is named and used with standard true/false
* values to indicate whether the sensor is enabled or disabled, respectively.
*
* @return Current temperature sensor enabled status
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_TEMP_DIS_BIT
*/
bool getTempSensorEnabled() {
readBit(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, buffer);
return buffer[0] == 0; // 1 is actually disabled here
}
/** Set temperature sensor enabled status.
* Note: this register stores the *disabled* value, but for consistency with the
* rest of the code, the function is named and used with standard true/false
* values to indicate whether the sensor is enabled or disabled, respectively.
*
* @param enabled New temperature sensor enabled status
* @see getTempSensorEnabled()
* @see MPU6050_RA_PWR_MGMT_1
* @see MPU6050_PWR1_TEMP_DIS_BIT
*/
void setTempSensorEnabled(bool enabled) {
// 1 is actually disabled here
writeBit(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, !enabled);
}
uint8_t getDeviceID() {
uint8_t buf[1];
readByte(MPU6050_ADDR, MPU6050_RA_WHO_AM_I, buf);
LOG_E("MPU6050 getDeviceID : %2x!\r\n", buf[0]);
return buf[0];
}
// SMPLRT_DIV register
/** Get gyroscope output rate divider.
* The sensor register output, FIFO output, DMP sampling, Motion detection, Zero
* Motion detection, and Free Fall detection are all based on the Sample Rate.
* The Sample Rate is generated by dividing the gyroscope output rate by
* SMPLRT_DIV:
*
* Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
*
* where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or
* 7), and 1kHz when the DLPF is enabled (see Register 26).
*
* Note: The accelerometer output rate is 1kHz. This means that for a Sample
* Rate greater than 1kHz, the same accelerometer sample may be output to the
* FIFO, DMP, and sensor registers more than once.
*
* For a diagram of the gyroscope and accelerometer signal paths, see Section 8
* of the MPU-6000/MPU-6050 Product Specification document.
*
* @return Current sample rate
* @see MPU6050_RA_SMPLRT_DIV
*/
uint8_t getRate() {
readByte(MPU6050_ADDR, MPU6050_RA_SMPLRT_DIV, buffer);
return buffer[0];
}
/** Set gyroscope sample rate divider.
* @param rate New sample rate divider
* @see getRate()
* @see MPU6050_RA_SMPLRT_DIV
*/
void setRate(uint8_t rate) {
writeByte(MPU6050_ADDR, MPU6050_RA_SMPLRT_DIV, rate);
}
void getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) {
LOG_E("MPU6050 getMotion6!\r\n");
readBytes(MPU6050_ADDR, MPU6050_RA_ACCEL_XOUT_H, 14, buffer);
*ax = (((int16_t)buffer[0]) << 8) | buffer[1];
*ay = (((int16_t)buffer[2]) << 8) | buffer[3];
*az = (((int16_t)buffer[4]) << 8) | buffer[5];
*gx = (((int16_t)buffer[8]) << 8) | buffer[9];
*gy = (((int16_t)buffer[10]) << 8) | buffer[11];
*gz = (((int16_t)buffer[12]) << 8) | buffer[13];
}
void MPU6050() {
begin();
// setClockSource(MPU6050_CLOCK_PLL_XGYRO);
// setFullScaleGyroRange(MPU6050_GYRO_FS_1000);
// setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
// setSleepEnabled(false);
writeByte(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, 0x01);
writeByte(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_2, 0x00);
writeByte(MPU6050_ADDR, MPU6050_RA_GYRO_CONFIG, 0x18);
writeByte(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, 0x18);
}main.c
Message push functionality hasn't been perfected yet; PushPlus is used.Official address: https://www.pushplus.plus/. After registration, obtain a token and use HTTP requests to implement message push.
For pushing messages to WeChat, refer to:
#!/bin/sh
This is a script written for intranet penetration, as the free version of Natapp always changes addresses.
It sends the new domain name regularly. Logs are cleaned, only the INFO section is retained, so they won't be too large.Messages can be pushed directly to WeChat.
Ai-Thinker