summaryrefslogtreecommitdiff
path: root/fw/mpu6050.c
blob: dc374d86b53fb78a9b994550c2e940025fbc438e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/* MPU6050 device I2C library code for ARM STM32F103xx is placed under the MIT license
   Copyright (c) 2012 Harinadha Reddy Chintalapalli

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#include "mpu6050.h"
#include "i2c.h"

/** @defgroup MPU_Library
 * @{
 */

/** Power on and prepare for general usage.
 * This will activate the device and take it out of sleep mode (which must be done
 * after start-up). This function also sets both the accelerometer and the gyroscope
 * to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets
 * the clock source to use the X Gyro for reference, which is slightly better than
 * the default internal clock source.
 */
void mpu_init()
{
    mpu_reg_write(MPU_RA_PWR_MGMT1, MPU_CLOCK_PLL_XGYRO);
    mpu_set_gyro_fs(MPU_GYRO_FS_250);
    mpu_set_accel_fs(MPU_ACCEL_FS_2);
}

void mpu_init_low_power(uint8_t wake_freq, bool enable_interrupt)
{
    mpu_set_gyro_fs(MPU_GYRO_FS_250);
    mpu_set_accel_fs(MPU_ACCEL_FS_2);
    /* For these two regs, see pwr mgmt reg 2 desc in reg map ref manual, pg. 42 */
    mpu_reg_write(MPU_RA_PWR_MGMT1, MPU_PWR_MGMT1_CYCLE | MPU_PWR_MGMT1_TEMP_DIS | MPU_CLOCK_INTERNAL);
    mpu_reg_write(MPU_RA_PWR_MGMT2, MPU_PWR_MGMT2_STBY_XG | MPU_PWR_MGMT2_STBY_YG | MPU_PWR_MGMT2_STBY_ZG | wake_freq);

    if (enable_interrupt) {
        mpu_reg_write(MPU_RA_INT_PIN_CFG, MPU_INT_PIN_CFG_RD_CLEAR);
        mpu_reg_write(MPU_RA_INT_ENABLE, MPU_INT_ENABLE_DATA_RDY);
    }
}

/** Verify the I2C connection.
 * Make sure the device is connected and responds as expected.
 * @return True if connection is valid, FALSE otherwise
 */
bool mpu_test_connection()
{
    return mpu_device_id() == MPU_DEVICE_ID;
}

/** Get Device ID.
 * This register is used to verify the identity of the device (0b110100).
 * @return Device ID (should be 0x68, 104 dec, 150 oct)
 * @see MPU_RA_WHO_AM_I
 * @see MPU_WHO_AM_I_BIT
 * @see MPU_WHO_AM_I_LENGTH
 */
uint8_t mpu_device_id()
{
    return mpu_reg_read(MPU_RA_WHO_AM_I) >> MPU_WHO_AM_I_Pos;
}

/** Set full-scale gyroscope range.
 * @param range New full-scale gyroscope range value
 * @see MPU_GetFullScaleGyroRange()
 * @see MPU_GYRO_FS_250
 * @see MPU_RA_GYRO_CONFIG
 * @see MPU_GCONFIG_FS_SEL_BIT
 * @see MPU_GCONFIG_FS_SEL_LENGTH
 */
void mpu_set_gyro_fs(uint8_t range)
{
    mpu_reg_write(MPU_RA_GYRO_CONFIG, range);
}


/** 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 MPU_GYRO_FS_250
 * @see MPU_RA_GYRO_CONFIG
 * @see MPU_GCONFIG_FS_SEL_BIT
 * @see MPU_GCONFIG_FS_SEL_LENGTH
 */
uint8_t mpu_get_gyro_fs()
{
    return mpu_reg_read(MPU_RA_GYRO_CONFIG) & MPU_GYRO_FS_SEL_Msk;
}

/** 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 MPU_ACCEL_FS_2
 * @see MPU_RA_ACCEL_CONFIG
 */
uint8_t mpu_get_accel_fs()
{
    return mpu_reg_read(MPU_RA_ACCEL_CONFIG) & MPU_ACCEL_CONFIG_FS_SEL_Msk;
}

/** Set full-scale accelerometer range.
 * @param range New full-scale accelerometer range setting
 * @see MPU_GetFullScaleAccelRange()
 */
void mpu_set_accel_fs(uint8_t range)
{
    mpu_reg_write(MPU_RA_ACCEL_CONFIG, range);
}

/** 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 bit
 * @see MPU_RA_PWR_MGMT_1
 * @see MPU_PWR1_SLEEP_BIT
 */
bool mpu_get_sleep_mode()
{
    return mpu_reg_read(MPU_RA_PWR_MGMT1) & MPU_PWR_MGMT1_SLEEP;
}

/** Set sleep mode status.
 * @param enabled New sleep mode enabled status
 * @see MPU_GetSleepModeStatus()
 * @see MPU_RA_PWR_MGMT_1
 * @see MPU_PWR1_SLEEP_BIT
 */
void mpu_set_sleep_mode(bool val)
{
    int tmp = mpu_reg_read(MPU_RA_PWR_MGMT1);
    tmp = (tmp & ~MPU_PWR_MGMT1_SLEEP) | (val ? MPU_PWR_MGMT1_SLEEP : 0);
    mpu_reg_write(MPU_RA_PWR_MGMT1, tmp);
}

/** Get raw 6-axis motion sensor readings (accel/gyro).
 * Retrieves all currently available motion sensor values.
 * @see MPU_RA_ACCEL_XOUT_H
 */
void mpu_read_accel_gyro(struct mpu_raw_data *out)
{
    struct mpu_raw_data buf;
    mpu_reg_read_multiple(MPU_RA_ACCEL_XOUT_H, (uint8_t *)&buf, sizeof(buf));

    for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++)
        out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]);
}

int16_t mpu_read_temp() {
    uint16_t buf;
    mpu_reg_read_multiple(MPU_RA_TEMP_OUT_H, (uint8_t *)&buf, sizeof(buf));
    return (int16_t)be16toh(buf);
}

void mpu_read_accel(struct mpu_accel_data *out)
{
    struct mpu_accel_data buf;
    mpu_reg_read_multiple(MPU_RA_ACCEL_XOUT_H, (uint8_t *)&buf, sizeof(buf));

    for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++)
        out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]);
}

void mpu_read_gyro(struct mpu_gyro_data *out)
{
    struct mpu_gyro_data buf;
    mpu_reg_read_multiple(MPU_RA_GYRO_XOUT_H, (uint8_t *)&buf, sizeof(buf));

    for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++)
        out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]);
}

/**
 * @brief  Writes one byte to the  MPU6050.
 * @param  reg : address of the register in which the data will be written
 * @param  val : the data to be written to the MPU6050.
 * @return None
 */
void mpu_reg_write(uint8_t reg, uint8_t val)
{
    uint8_t tx[2] = {reg, val};
    i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, tx, sizeof(tx), I2C_GENSTOP_YES);
}

/**
 * @brief  Reads a block of data from the MPU6050.
 * @param  buf : pointer to the buffer that receives the data read from the MPU6050.
 * @param  addr : MPU6050's internal address to read from.
 * @param  len : number of bytes to read from the MPU6050
 * @return None
 */
void mpu_reg_read_multiple(uint8_t addr, uint8_t* buf, size_t len)
{
    i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &addr, 1, I2C_GENSTOP_NO);
    i2c_receive(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, buf, len);
}

uint8_t mpu_reg_read(uint8_t addr)
{
    uint8_t buf;
    i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &addr, 1, I2C_GENSTOP_NO);
    i2c_receive(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &buf, 1);
    return buf;
}
/**
 * @}
 *//* end of group MPU_Library */