Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2194 | - | 1 | /* |
2 | MPU6050 lib 0x02 |
||
3 | |||
4 | copyright (c) Davide Gironi, 2012 |
||
5 | |||
6 | Released under GPLv3. |
||
7 | Please refer to LICENSE file for licensing information. |
||
8 | */ |
||
9 | |||
10 | |||
11 | #include <stdlib.h> |
||
12 | #include <string.h> |
||
13 | #include <avr/io.h> |
||
14 | #include <avr/pgmspace.h> |
||
15 | #include <avr/interrupt.h> |
||
16 | #include <util/delay.h> |
||
17 | |||
18 | #include "mpu6050.h" |
||
19 | |||
20 | #if MPU6050_GETATTITUDE == 1 || MPU6050_GETATTITUDE == 2 |
||
21 | #include <math.h> //include libm |
||
22 | #endif |
||
23 | |||
24 | //path to i2c fleury lib |
||
25 | #include "i2cmaster.h" |
||
26 | |||
27 | volatile uint8_t buffer[14]; |
||
28 | |||
29 | /* |
||
30 | * read bytes from chip register |
||
31 | */ |
||
32 | int8_t mpu6050_readBytes(uint8_t regAddr, uint8_t length, uint8_t *data) { |
||
33 | uint8_t i = 0; |
||
34 | int8_t count = 0; |
||
35 | if(length > 0) { |
||
36 | //request register |
||
37 | i2c_start(MPU6050_ADDR | I2C_WRITE); |
||
38 | i2c_write(regAddr); |
||
39 | _delay_us(10); |
||
40 | //read data |
||
41 | i2c_start(MPU6050_ADDR | I2C_READ); |
||
42 | for(i=0; i<length; i++) { |
||
43 | count++; |
||
44 | if(i==length-1) |
||
45 | data[i] = i2c_readNak(); |
||
46 | else |
||
47 | data[i] = i2c_readAck(); |
||
48 | } |
||
49 | i2c_stop(); |
||
50 | } |
||
51 | return count; |
||
52 | } |
||
53 | |||
54 | /* |
||
55 | * read 1 byte from chip register |
||
56 | */ |
||
57 | int8_t mpu6050_readByte(uint8_t regAddr, uint8_t *data) { |
||
58 | return mpu6050_readBytes(regAddr, 1, data); |
||
59 | } |
||
60 | |||
61 | /* |
||
62 | * write bytes to chip register |
||
63 | */ |
||
64 | void mpu6050_writeBytes(uint8_t regAddr, uint8_t length, uint8_t* data) { |
||
65 | if(length > 0) { |
||
66 | //write data |
||
67 | i2c_start(MPU6050_ADDR | I2C_WRITE); |
||
68 | i2c_write(regAddr); //reg |
||
69 | for (uint8_t i = 0; i < length; i++) { |
||
70 | i2c_write((uint8_t) data[i]); |
||
71 | } |
||
72 | i2c_stop(); |
||
73 | } |
||
74 | } |
||
75 | |||
76 | /* |
||
77 | * write 1 byte to chip register |
||
78 | */ |
||
79 | void mpu6050_writeByte(uint8_t regAddr, uint8_t data) { |
||
80 | return mpu6050_writeBytes(regAddr, 1, &data); |
||
81 | } |
||
82 | |||
83 | /* |
||
84 | * read bits from chip register |
||
85 | */ |
||
86 | int8_t mpu6050_readBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data) { |
||
87 | // 01101001 read byte |
||
88 | // 76543210 bit numbers |
||
89 | // xxx args: bitStart=4, length=3 |
||
90 | // 010 masked |
||
91 | // -> 010 shifted |
||
92 | int8_t count = 0; |
||
93 | if(length > 0) { |
||
94 | uint8_t b; |
||
95 | if ((count = mpu6050_readByte(regAddr, &b)) != 0) { |
||
96 | uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); |
||
97 | b &= mask; |
||
98 | b >>= (bitStart - length + 1); |
||
99 | *data = b; |
||
100 | } |
||
101 | } |
||
102 | return count; |
||
103 | } |
||
104 | |||
105 | /* |
||
106 | * read 1 bit from chip register |
||
107 | */ |
||
108 | int8_t mpu6050_readBit(uint8_t regAddr, uint8_t bitNum, uint8_t *data) { |
||
109 | uint8_t b; |
||
110 | uint8_t count = mpu6050_readByte(regAddr, &b); |
||
111 | *data = b & (1 << bitNum); |
||
112 | return count; |
||
113 | } |
||
114 | |||
115 | /* |
||
116 | * write bit/bits to chip register |
||
117 | */ |
||
118 | void mpu6050_writeBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) { |
||
119 | // 010 value to write |
||
120 | // 76543210 bit numbers |
||
121 | // xxx args: bitStart=4, length=3 |
||
122 | // 00011100 mask byte |
||
123 | // 10101111 original value (sample) |
||
124 | // 10100011 original & ~mask |
||
125 | // 10101011 masked | value |
||
126 | if(length > 0) { |
||
127 | uint8_t b = 0; |
||
128 | if (mpu6050_readByte(regAddr, &b) != 0) { //get current data |
||
129 | uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); |
||
130 | data <<= (bitStart - length + 1); // shift data into correct position |
||
131 | data &= mask; // zero all non-important bits in data |
||
132 | b &= ~(mask); // zero all important bits in existing byte |
||
133 | b |= data; // combine data with existing byte |
||
134 | mpu6050_writeByte(regAddr, b); |
||
135 | } |
||
136 | } |
||
137 | } |
||
138 | |||
139 | /* |
||
140 | * write one bit to chip register |
||
141 | */ |
||
142 | void mpu6050_writeBit(uint8_t regAddr, uint8_t bitNum, uint8_t data) { |
||
143 | uint8_t b; |
||
144 | mpu6050_readByte(regAddr, &b); |
||
145 | b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum)); |
||
146 | mpu6050_writeByte(regAddr, b); |
||
147 | } |
||
148 | |||
149 | #if MPU6050_GETATTITUDE == 2 |
||
150 | /* |
||
151 | * write word/words to chip register |
||
152 | */ |
||
153 | void mpu6050_writeWords(uint8_t regAddr, uint8_t length, uint16_t* data) { |
||
154 | if(length > 0) { |
||
155 | uint8_t i = 0; |
||
156 | //write data |
||
157 | i2c_start(MPU6050_ADDR | I2C_WRITE); |
||
158 | i2c_write(regAddr); //reg |
||
159 | for (i = 0; i < length * 2; i++) { |
||
160 | i2c_write((uint8_t)(data[i++] >> 8)); // send MSB |
||
161 | i2c_write((uint8_t)data[i]); // send LSB |
||
162 | } |
||
163 | i2c_stop(); |
||
164 | } |
||
165 | } |
||
166 | |||
167 | /* |
||
168 | * set a chip memory bank |
||
169 | */ |
||
170 | void mpu6050_setMemoryBank(uint8_t bank, uint8_t prefetchEnabled, uint8_t userBank) { |
||
171 | bank &= 0x1F; |
||
172 | if (userBank) bank |= 0x20; |
||
173 | if (prefetchEnabled) bank |= 0x40; |
||
174 | mpu6050_writeByte(MPU6050_RA_BANK_SEL, bank); |
||
175 | } |
||
176 | |||
177 | /* |
||
178 | * set memory start address |
||
179 | */ |
||
180 | void mpu6050_setMemoryStartAddress(uint8_t address) { |
||
181 | mpu6050_writeByte(MPU6050_RA_MEM_START_ADDR, address); |
||
182 | } |
||
183 | |||
184 | /* |
||
185 | * read a memory block |
||
186 | */ |
||
187 | void mpu6050_readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address) { |
||
188 | mpu6050_setMemoryBank(bank, 0, 0); |
||
189 | mpu6050_setMemoryStartAddress(address); |
||
190 | uint8_t chunkSize; |
||
191 | for (uint16_t i = 0; i < dataSize;) { |
||
192 | // determine correct chunk size according to bank position and data size |
||
193 | chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE; |
||
194 | |||
195 | // make sure we don't go past the data size |
||
196 | if (i + chunkSize > dataSize) chunkSize = dataSize - i; |
||
197 | |||
198 | // make sure this chunk doesn't go past the bank boundary (256 bytes) |
||
199 | if (chunkSize > 256 - address) chunkSize = 256 - address; |
||
200 | |||
201 | // read the chunk of data as specified |
||
202 | mpu6050_readBytes(MPU6050_RA_MEM_R_W, chunkSize, data + i); |
||
203 | |||
204 | // increase byte index by [chunkSize] |
||
205 | i += chunkSize; |
||
206 | |||
207 | // uint8_t automatically wraps to 0 at 256 |
||
208 | address += chunkSize; |
||
209 | |||
210 | // if we aren't done, update bank (if necessary) and address |
||
211 | if (i < dataSize) { |
||
212 | if (address == 0) bank++; |
||
213 | mpu6050_setMemoryBank(bank, 0, 0); |
||
214 | mpu6050_setMemoryStartAddress(address); |
||
215 | } |
||
216 | } |
||
217 | } |
||
218 | |||
219 | /* |
||
220 | * write a memory block |
||
221 | */ |
||
222 | uint8_t mpu6050_writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address, uint8_t verify, uint8_t useProgMem) { |
||
223 | mpu6050_setMemoryBank(bank, 0, 0); |
||
224 | mpu6050_setMemoryStartAddress(address); |
||
225 | uint8_t chunkSize; |
||
226 | uint8_t *verifyBuffer = 0; |
||
227 | uint8_t *progBuffer = 0; |
||
228 | uint16_t i; |
||
229 | uint8_t j; |
||
230 | if (verify) verifyBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE); |
||
231 | if (useProgMem) progBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE); |
||
232 | for (i = 0; i < dataSize;) { |
||
233 | // determine correct chunk size according to bank position and data size |
||
234 | chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE; |
||
235 | |||
236 | // make sure we don't go past the data size |
||
237 | if (i + chunkSize > dataSize) chunkSize = dataSize - i; |
||
238 | |||
239 | // make sure this chunk doesn't go past the bank boundary (256 bytes) |
||
240 | if (chunkSize > 256 - address) chunkSize = 256 - address; |
||
241 | |||
242 | if (useProgMem) { |
||
243 | // write the chunk of data as specified |
||
244 | for (j = 0; j < chunkSize; j++) progBuffer[j] = pgm_read_byte(data + i + j); |
||
245 | } else { |
||
246 | // write the chunk of data as specified |
||
247 | progBuffer = (uint8_t *)data + i; |
||
248 | } |
||
249 | |||
250 | mpu6050_writeBytes(MPU6050_RA_MEM_R_W, chunkSize, progBuffer); |
||
251 | |||
252 | // verify data if needed |
||
253 | if (verify && verifyBuffer) { |
||
254 | mpu6050_setMemoryBank(bank, 0, 0); |
||
255 | mpu6050_setMemoryStartAddress(address); |
||
256 | mpu6050_readBytes(MPU6050_RA_MEM_R_W, chunkSize, verifyBuffer); |
||
257 | if (memcmp(progBuffer, verifyBuffer, chunkSize) != 0) { |
||
258 | free(verifyBuffer); |
||
259 | if (useProgMem) free(progBuffer); |
||
260 | return 0; // uh oh. |
||
261 | } |
||
262 | } |
||
263 | |||
264 | // increase byte index by [chunkSize] |
||
265 | i += chunkSize; |
||
266 | |||
267 | // uint8_t automatically wraps to 0 at 256 |
||
268 | address += chunkSize; |
||
269 | |||
270 | // if we aren't done, update bank (if necessary) and address |
||
271 | if (i < dataSize) { |
||
272 | if (address == 0) bank++; |
||
273 | mpu6050_setMemoryBank(bank, 0, 0); |
||
274 | mpu6050_setMemoryStartAddress(address); |
||
275 | } |
||
276 | } |
||
277 | if (verify) free(verifyBuffer); |
||
278 | if (useProgMem) free(progBuffer); |
||
279 | return 1; |
||
280 | } |
||
281 | |||
282 | /* |
||
283 | * write a dmp configuration set |
||
284 | */ |
||
285 | uint8_t mpu6050_writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, uint8_t useProgMem) { |
||
286 | uint8_t *progBuffer = 0; |
||
287 | uint8_t success, special; |
||
288 | uint16_t i, j; |
||
289 | if (useProgMem) { |
||
290 | progBuffer = (uint8_t *)malloc(8); // assume 8-byte blocks, realloc later if necessary |
||
291 | } |
||
292 | |||
293 | // config set data is a long string of blocks with the following structure: |
||
294 | // [bank] [offset] [length] [byte[0], byte[1], ..., byte[length]] |
||
295 | uint8_t bank, offset, length; |
||
296 | for (i = 0; i < dataSize;) { |
||
297 | if (useProgMem) { |
||
298 | bank = pgm_read_byte(data + i++); |
||
299 | offset = pgm_read_byte(data + i++); |
||
300 | length = pgm_read_byte(data + i++); |
||
301 | } else { |
||
302 | bank = data[i++]; |
||
303 | offset = data[i++]; |
||
304 | length = data[i++]; |
||
305 | } |
||
306 | |||
307 | // write data or perform special action |
||
308 | if (length > 0) { |
||
309 | // regular block of data to write |
||
310 | if (useProgMem) { |
||
311 | if (sizeof(progBuffer) < length) progBuffer = (uint8_t *)realloc(progBuffer, length); |
||
312 | for (j = 0; j < length; j++) progBuffer[j] = pgm_read_byte(data + i + j); |
||
313 | } else { |
||
314 | progBuffer = (uint8_t *)data + i; |
||
315 | } |
||
316 | success = mpu6050_writeMemoryBlock(progBuffer, length, bank, offset, 1, 0); |
||
317 | i += length; |
||
318 | } else { |
||
319 | // special instruction |
||
320 | // NOTE: this kind of behavior (what and when to do certain things) |
||
321 | // is totally undocumented. This code is in here based on observed |
||
322 | // behavior only, and exactly why (or even whether) it has to be here |
||
323 | // is anybody's guess for now. |
||
324 | if (useProgMem) { |
||
325 | special = pgm_read_byte(data + i++); |
||
326 | } else { |
||
327 | special = data[i++]; |
||
328 | } |
||
329 | if (special == 0x01) { |
||
330 | // enable DMP-related interrupts |
||
331 | |||
332 | //mpu6050_writeBit(MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_ZMOT_BIT, 1); //setIntZeroMotionEnabled |
||
333 | //mpu6050_writeBit(MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, 1); //setIntFIFOBufferOverflowEnabled |
||
334 | //mpu6050_writeBit(MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DMP_INT_BIT, 1); //setIntDMPEnabled |
||
335 | mpu6050_writeByte(MPU6050_RA_INT_ENABLE, 0x32); // single operation |
||
336 | |||
337 | success = 1; |
||
338 | } else { |
||
339 | // unknown special command |
||
340 | success = 0; |
||
341 | } |
||
342 | } |
||
343 | |||
344 | if (!success) { |
||
345 | if (useProgMem) free(progBuffer); |
||
346 | return 0; // uh oh |
||
347 | } |
||
348 | } |
||
349 | if (useProgMem) free(progBuffer); |
||
350 | return 1; |
||
351 | } |
||
352 | |||
353 | /* |
||
354 | * get the fifo count |
||
355 | */ |
||
356 | uint16_t mpu6050_getFIFOCount(void) { |
||
357 | mpu6050_readBytes(MPU6050_RA_FIFO_COUNTH, 2, (uint8_t *)buffer); |
||
358 | return (((uint16_t)buffer[0]) << 8) | buffer[1]; |
||
359 | } |
||
360 | |||
361 | /* |
||
362 | * read fifo bytes |
||
363 | */ |
||
364 | void mpu6050_getFIFOBytes(uint8_t *data, uint8_t length) { |
||
365 | mpu6050_readBytes(MPU6050_RA_FIFO_R_W, length, data); |
||
366 | } |
||
367 | |||
368 | /* |
||
369 | * get the interrupt status |
||
370 | */ |
||
371 | uint8_t mpu6050_getIntStatus(void) { |
||
372 | mpu6050_readByte(MPU6050_RA_INT_STATUS, (uint8_t *)buffer); |
||
373 | return buffer[0]; |
||
374 | } |
||
375 | |||
376 | /* |
||
377 | * reset fifo |
||
378 | */ |
||
379 | void mpu6050_resetFIFO(void) { |
||
380 | mpu6050_writeBit(MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_RESET_BIT, 1); |
||
381 | } |
||
382 | |||
383 | /* |
||
384 | * get gyro offset X |
||
385 | */ |
||
386 | int8_t mpu6050_getXGyroOffset(void) { |
||
387 | mpu6050_readBits(MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, (uint8_t *)buffer); |
||
388 | return buffer[0]; |
||
389 | } |
||
390 | |||
391 | /* |
||
392 | * set gyro offset X |
||
393 | */ |
||
394 | void mpu6050_setXGyroOffset(int8_t offset) { |
||
395 | mpu6050_writeBits(MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset); |
||
396 | } |
||
397 | |||
398 | /* |
||
399 | * get gyro offset Y |
||
400 | */ |
||
401 | int8_t mpu6050_getYGyroOffset(void) { |
||
402 | mpu6050_readBits(MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, (uint8_t *)buffer); |
||
403 | return buffer[0]; |
||
404 | } |
||
405 | |||
406 | /* |
||
407 | * set gyro offset Y |
||
408 | */ |
||
409 | void mpu6050_setYGyroOffset(int8_t offset) { |
||
410 | mpu6050_writeBits(MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset); |
||
411 | } |
||
412 | |||
413 | /* |
||
414 | * get gyro offset Z |
||
415 | */ |
||
416 | int8_t mpu6050_getZGyroOffset(void) { |
||
417 | mpu6050_readBits(MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, (uint8_t *)buffer); |
||
418 | return buffer[0]; |
||
419 | } |
||
420 | |||
421 | /* |
||
422 | * set gyro offset Z |
||
423 | */ |
||
424 | void mpu6050_setZGyroOffset(int8_t offset) { |
||
425 | mpu6050_writeBits(MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset); |
||
426 | } |
||
427 | #endif |
||
428 | |||
429 | /* |
||
430 | * set sleep disabled |
||
431 | */ |
||
432 | void mpu6050_setSleepDisabled() { |
||
433 | mpu6050_writeBit(MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, 0); |
||
434 | } |
||
435 | |||
436 | /* |
||
437 | * set sleep enabled |
||
438 | */ |
||
439 | void mpu6050_setSleepEnabled(void) { |
||
440 | mpu6050_writeBit(MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, 1); |
||
441 | } |
||
442 | |||
443 | |||
444 | /* |
||
445 | * test connectino to chip |
||
446 | */ |
||
447 | uint8_t mpu6050_testConnection(void) { |
||
448 | mpu6050_readBits(MPU6050_RA_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_LENGTH, (uint8_t *)buffer); |
||
449 | if(buffer[0] == 0x34) |
||
450 | return 1; |
||
451 | else |
||
452 | return 0; |
||
453 | } |
||
454 | |||
455 | /* |
||
456 | * initialize the accel and gyro |
||
457 | */ |
||
458 | void mpu6050_init(void) { |
||
459 | //allow mpu6050 chip clocks to start up |
||
460 | _delay_ms(100); |
||
461 | |||
462 | //set sleep disabled |
||
463 | mpu6050_setSleepDisabled(); |
||
464 | //wake up delay needed sleep disabled |
||
465 | _delay_ms(10); |
||
466 | |||
467 | //set clock source |
||
468 | // it is highly recommended that the device be configured to use one of the gyroscopes (or an external clock source) |
||
469 | // as the clock reference for improved stability |
||
470 | mpu6050_writeBits(MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, MPU6050_CLOCK_PLL_XGYRO); |
||
471 | //set DLPF bandwidth to 42Hz |
||
472 | mpu6050_writeBits(MPU6050_RA_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_LENGTH, MPU6050_DLPF_BW_42); |
||
473 | //set sampe rate |
||
474 | mpu6050_writeByte(MPU6050_RA_SMPLRT_DIV, 4); //1khz / (1 + 4) = 200Hz |
||
475 | //set gyro range |
||
476 | mpu6050_writeBits(MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, MPU6050_GYRO_FS); |
||
477 | //set accel range |
||
478 | mpu6050_writeBits(MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, MPU6050_ACCEL_FS); |
||
479 | |||
480 | #if MPU6050_GETATTITUDE == 1 |
||
481 | #error "Do not enable timer 0 it is in use elsewhere!" |
||
482 | //MPU6050_TIMER0INIT |
||
483 | #endif |
||
484 | } |
||
485 | |||
486 | //can not accept many request if we alreay have getattitude requests |
||
487 | /* |
||
488 | * get raw data |
||
489 | */ |
||
490 | void mpu6050_getRawData(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) { |
||
491 | mpu6050_readBytes(MPU6050_RA_ACCEL_XOUT_H, 14, (uint8_t *)buffer); |
||
492 | |||
493 | *ax = (((int16_t)buffer[0]) << 8) | buffer[1]; |
||
494 | *ay = (((int16_t)buffer[2]) << 8) | buffer[3]; |
||
495 | *az = (((int16_t)buffer[4]) << 8) | buffer[5]; |
||
496 | *gx = (((int16_t)buffer[8]) << 8) | buffer[9]; |
||
497 | *gy = (((int16_t)buffer[10]) << 8) | buffer[11]; |
||
498 | *gz = (((int16_t)buffer[12]) << 8) | buffer[13]; |
||
499 | } |
||
500 | |||
501 | /* |
||
502 | * get raw data converted to g and deg/sec values |
||
503 | */ |
||
504 | void mpu6050_getConvData(double* axg, double* ayg, double* azg, double* gxds, double* gyds, double* gzds) { |
||
505 | int16_t ax = 0; |
||
506 | int16_t ay = 0; |
||
507 | int16_t az = 0; |
||
508 | int16_t gx = 0; |
||
509 | int16_t gy = 0; |
||
510 | int16_t gz = 0; |
||
511 | mpu6050_getRawData(&ax, &ay, &az, &gx, &gy, &gz); |
||
512 | |||
513 | #if MPU6050_CALIBRATEDACCGYRO == 1 |
||
514 | *axg = (double)(ax-MPU6050_AXOFFSET)/MPU6050_AXGAIN; |
||
515 | *ayg = (double)(ay-MPU6050_AYOFFSET)/MPU6050_AYGAIN; |
||
516 | *azg = (double)(az-MPU6050_AZOFFSET)/MPU6050_AZGAIN; |
||
517 | *gxds = (double)(gx-MPU6050_GXOFFSET)/MPU6050_GXGAIN; |
||
518 | *gyds = (double)(gy-MPU6050_GYOFFSET)/MPU6050_GYGAIN; |
||
519 | *gzds = (double)(gz-MPU6050_GZOFFSET)/MPU6050_GZGAIN; |
||
520 | #else |
||
521 | *axg = (double)(ax)/MPU6050_AGAIN; |
||
522 | *ayg = (double)(ay)/MPU6050_AGAIN; |
||
523 | *azg = (double)(az)/MPU6050_AGAIN; |
||
524 | *gxds = (double)(gx)/MPU6050_GGAIN; |
||
525 | *gyds = (double)(gy)/MPU6050_GGAIN; |
||
526 | *gzds = (double)(gz)/MPU6050_GGAIN; |
||
527 | #endif |
||
528 | } |
||
529 | |||
530 | #if MPU6050_GETATTITUDE == 1 |
||
531 | |||
532 | volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; |
||
533 | volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; |
||
534 | /* |
||
535 | * Mahony update function (for 6DOF) |
||
536 | */ |
||
537 | void mpu6050_mahonyUpdate(float gx, float gy, float gz, float ax, float ay, float az) { |
||
538 | float norm; |
||
539 | float halfvx, halfvy, halfvz; |
||
540 | float halfex, halfey, halfez; |
||
541 | float qa, qb, qc; |
||
542 | |||
543 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) |
||
544 | if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { |
||
545 | |||
546 | // Normalise accelerometer measurement |
||
547 | norm = sqrt(ax * ax + ay * ay + az * az); |
||
548 | ax /= norm; |
||
549 | ay /= norm; |
||
550 | az /= norm; |
||
551 | |||
552 | // Estimated direction of gravity and vector perpendicular to magnetic flux |
||
553 | halfvx = q1 * q3 - q0 * q2; |
||
554 | halfvy = q0 * q1 + q2 * q3; |
||
555 | halfvz = q0 * q0 - 0.5f + q3 * q3; |
||
556 | |||
557 | // Error is sum of cross product between estimated and measured direction of gravity |
||
558 | halfex = (ay * halfvz - az * halfvy); |
||
559 | halfey = (az * halfvx - ax * halfvz); |
||
560 | halfez = (ax * halfvy - ay * halfvx); |
||
561 | |||
562 | // Compute and apply integral feedback if enabled |
||
563 | if(mpu6050_mahonytwoKiDef > 0.0f) { |
||
564 | integralFBx += mpu6050_mahonytwoKiDef * halfex * (1.0f / mpu6050_mahonysampleFreq); // integral error scaled by Ki |
||
565 | integralFBy += mpu6050_mahonytwoKiDef * halfey * (1.0f / mpu6050_mahonysampleFreq); |
||
566 | integralFBz += mpu6050_mahonytwoKiDef * halfez * (1.0f / mpu6050_mahonysampleFreq); |
||
567 | gx += integralFBx; // apply integral feedback |
||
568 | gy += integralFBy; |
||
569 | gz += integralFBz; |
||
570 | } else { |
||
571 | integralFBx = 0.0f; // prevent integral windup |
||
572 | integralFBy = 0.0f; |
||
573 | integralFBz = 0.0f; |
||
574 | } |
||
575 | |||
576 | // Apply proportional feedback |
||
577 | gx += mpu6050_mahonytwoKpDef * halfex; |
||
578 | gy += mpu6050_mahonytwoKpDef * halfey; |
||
579 | gz += mpu6050_mahonytwoKpDef * halfez; |
||
580 | } |
||
581 | |||
582 | // Integrate rate of change of quaternion |
||
583 | gx *= (0.5f * (1.0f / mpu6050_mahonysampleFreq)); // pre-multiply common factors |
||
584 | gy *= (0.5f * (1.0f / mpu6050_mahonysampleFreq)); |
||
585 | gz *= (0.5f * (1.0f / mpu6050_mahonysampleFreq)); |
||
586 | qa = q0; |
||
587 | qb = q1; |
||
588 | qc = q2; |
||
589 | q0 += (-qb * gx - qc * gy - q3 * gz); |
||
590 | q1 += (qa * gx + qc * gz - q3 * gy); |
||
591 | q2 += (qa * gy - qb * gz + q3 * gx); |
||
592 | q3 += (qa * gz + qb * gy - qc * gx); |
||
593 | |||
594 | // Normalise quaternion |
||
595 | norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); |
||
596 | q0 /= norm; |
||
597 | q1 /= norm; |
||
598 | q2 /= norm; |
||
599 | q3 /= norm; |
||
600 | } |
||
601 | |||
602 | /* |
||
603 | * update quaternion |
||
604 | */ |
||
605 | void mpu6050_updateQuaternion(void) { |
||
606 | int16_t ax = 0; |
||
607 | int16_t ay = 0; |
||
608 | int16_t az = 0; |
||
609 | int16_t gx = 0; |
||
610 | int16_t gy = 0; |
||
611 | int16_t gz = 0; |
||
612 | double axg = 0; |
||
613 | double ayg = 0; |
||
614 | double azg = 0; |
||
615 | double gxrs = 0; |
||
616 | double gyrs = 0; |
||
617 | double gzrs = 0; |
||
618 | |||
619 | //get raw data |
||
620 | while(1) { |
||
621 | mpu6050_readBit(MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_DATA_RDY_BIT, (uint8_t *)buffer); |
||
622 | if(buffer[0]) |
||
623 | break; |
||
624 | _delay_us(10); |
||
625 | } |
||
626 | |||
627 | mpu6050_readBytes(MPU6050_RA_ACCEL_XOUT_H, 14, (uint8_t *)buffer); |
||
628 | ax = (((int16_t)buffer[0]) << 8) | buffer[1]; |
||
629 | ay = (((int16_t)buffer[2]) << 8) | buffer[3]; |
||
630 | az = (((int16_t)buffer[4]) << 8) | buffer[5]; |
||
631 | gx = (((int16_t)buffer[8]) << 8) | buffer[9]; |
||
632 | gy = (((int16_t)buffer[10]) << 8) | buffer[11]; |
||
633 | gz = (((int16_t)buffer[12]) << 8) | buffer[13]; |
||
634 | |||
635 | #if MPU6050_CALIBRATEDACCGYRO == 1 |
||
636 | axg = (double)(ax-MPU6050_AXOFFSET)/MPU6050_AXGAIN; |
||
637 | ayg = (double)(ay-MPU6050_AYOFFSET)/MPU6050_AYGAIN; |
||
638 | azg = (double)(az-MPU6050_AZOFFSET)/MPU6050_AZGAIN; |
||
639 | gxrs = (double)(gx-MPU6050_GXOFFSET)/MPU6050_GXGAIN*0.01745329; //degree to radians |
||
640 | gyrs = (double)(gy-MPU6050_GYOFFSET)/MPU6050_GYGAIN*0.01745329; //degree to radians |
||
641 | gzrs = (double)(gz-MPU6050_GZOFFSET)/MPU6050_GZGAIN*0.01745329; //degree to radians |
||
642 | #else |
||
643 | axg = (double)(ax)/MPU6050_AGAIN; |
||
644 | ayg = (double)(ay)/MPU6050_AGAIN; |
||
645 | azg = (double)(az)/MPU6050_AGAIN; |
||
646 | gxrs = (double)(gx)/MPU6050_GGAIN*0.01745329; //degree to radians |
||
647 | gyrs = (double)(gy)/MPU6050_GGAIN*0.01745329; //degree to radians |
||
648 | gzrs = (double)(gz)/MPU6050_GGAIN*0.01745329; //degree to radians |
||
649 | #endif |
||
650 | |||
651 | //compute data |
||
652 | mpu6050_mahonyUpdate(gxrs, gyrs, gzrs, axg, ayg, azg); |
||
653 | } |
||
654 | |||
655 | /* |
||
656 | * update timer for attitude |
||
657 | */ |
||
658 | ISR(TIMER0_OVF_vect) { |
||
659 | mpu6050_updateQuaternion(); |
||
660 | } |
||
661 | |||
662 | /* |
||
663 | * get quaternion |
||
664 | */ |
||
665 | void mpu6050_getQuaternion(double *qw, double *qx, double *qy, double *qz) { |
||
666 | *qw = q0; |
||
667 | *qx = q1; |
||
668 | *qy = q2; |
||
669 | *qz = q3; |
||
670 | } |
||
671 | |||
672 | /* |
||
673 | * get euler angles |
||
674 | * aerospace sequence, to obtain sensor attitude: |
||
675 | * 1. rotate around sensor Z plane by yaw |
||
676 | * 2. rotate around sensor Y plane by pitch |
||
677 | * 3. rotate around sensor X plane by roll |
||
678 | */ |
||
679 | void mpu6050_getRollPitchYaw(double *roll, double *pitch, double *yaw) { |
||
680 | *yaw = atan2(2*q1*q2 - 2*q0*q3, 2*q0*q0 + 2*q1*q1 - 1); |
||
681 | *pitch = -asin(2*q1*q3 + 2*q0*q2); |
||
682 | *roll = atan2(2*q2*q3 - 2*q0*q1, 2*q0*q0 + 2*q3*q3 - 1); |
||
683 | } |
||
684 | |||
685 | #endif |
||
686 |