forked from KalicoCrew/kalico
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsensor_mpu9250.c
183 lines (159 loc) · 5.1 KB
/
sensor_mpu9250.c
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
// Support for gathering acceleration data from mpu9250 chip
//
// Copyright (C) 2023 Matthew Swabey <[email protected]>
// Copyright (C) 2022 Harry Beyel <[email protected]>
// Copyright (C) 2020-2023 Kevin O'Connor <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memcpy
#include "board/irq.h" // irq_disable
#include "board/misc.h" // timer_read_time
#include "basecmd.h" // oid_alloc
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_TASK
#include "sensor_bulk.h" // sensor_bulk_report
#include "i2ccmds.h" // i2cdev_oid_lookup
// Chip registers
#define AR_FIFO_COUNT_H 0x72
#define AR_FIFO 0x74
#define AR_INT_STATUS 0x3A
#define FIFO_OVERFLOW_INT 0x10
#define BYTES_PER_FIFO_ENTRY 6
#define BYTES_PER_BLOCK 48
struct mpu9250 {
struct timer timer;
uint32_t rest_ticks;
struct i2cdev_s *i2c;
uint16_t fifo_max, fifo_pkts_bytes;
uint8_t flags;
struct sensor_bulk sb;
};
enum {
AX_PENDING = 1<<0,
};
static struct task_wake mpu9250_wake;
// Event handler that wakes mpu9250_task() periodically
static uint_fast8_t
mpu9250_event(struct timer *timer)
{
struct mpu9250 *ax = container_of(timer, struct mpu9250, timer);
ax->flags |= AX_PENDING;
sched_wake_task(&mpu9250_wake);
return SF_DONE;
}
void
command_config_mpu9250(uint32_t *args)
{
struct mpu9250 *mp = oid_alloc(args[0], command_config_mpu9250
, sizeof(*mp));
mp->timer.func = mpu9250_event;
mp->i2c = i2cdev_oid_lookup(args[1]);
}
DECL_COMMAND(command_config_mpu9250, "config_mpu9250 oid=%c i2c_oid=%c");
// Helper code to reschedule the mpu9250_event() timer
static void
mp9250_reschedule_timer(struct mpu9250 *mp)
{
irq_disable();
mp->timer.waketime = timer_read_time() + mp->rest_ticks;
sched_add_timer(&mp->timer);
irq_enable();
}
static void
read_mpu(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read)
{
int ret = i2c_dev_read(i2c, reg_len, reg, read_len, read);
i2c_shutdown_on_err(ret);
}
// Reads the fifo byte count from the device.
static uint16_t
get_fifo_status(struct mpu9250 *mp)
{
uint8_t reg[] = {AR_FIFO_COUNT_H};
uint8_t msg[2];
read_mpu(mp->i2c, sizeof(reg), reg, sizeof(msg), msg);
uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1];
if (fifo_bytes > mp->fifo_max)
mp->fifo_max = fifo_bytes;
return fifo_bytes;
}
// Query accelerometer data
static void
mp9250_query(struct mpu9250 *mp, uint8_t oid)
{
// If not enough bytes to fill report read MPU FIFO's fill
if (mp->fifo_pkts_bytes < BYTES_PER_BLOCK)
mp->fifo_pkts_bytes = get_fifo_status(mp);
// If we have enough bytes to fill the buffer do it and send report
if (mp->fifo_pkts_bytes >= BYTES_PER_BLOCK) {
uint8_t reg = AR_FIFO;
read_mpu(mp->i2c, sizeof(reg), ®, BYTES_PER_BLOCK, &mp->sb.data[0]);
mp->sb.data_count = BYTES_PER_BLOCK;
mp->fifo_pkts_bytes -= BYTES_PER_BLOCK;
sensor_bulk_report(&mp->sb, oid);
}
// If we have enough bytes remaining to fill another report wake again
// otherwise schedule timed wakeup
if (mp->fifo_pkts_bytes >= BYTES_PER_BLOCK) {
sched_wake_task(&mpu9250_wake);
} else {
mp->flags &= ~AX_PENDING;
mp9250_reschedule_timer(mp);
}
}
void
command_query_mpu9250(uint32_t *args)
{
struct mpu9250 *mp = oid_lookup(args[0], command_config_mpu9250);
sched_del_timer(&mp->timer);
mp->flags = 0;
if (!args[1]) {
// End measurements
// Uncomment and rebuilt to check for FIFO overruns when tuning
//output("mpu9240 fifo_max=%u", mp->fifo_max);
return;
}
// Start new measurements query
mp->rest_ticks = args[1];
sensor_bulk_reset(&mp->sb);
mp->fifo_max = 0;
mp->fifo_pkts_bytes = 0;
mp9250_reschedule_timer(mp);
}
DECL_COMMAND(command_query_mpu9250, "query_mpu9250 oid=%c rest_ticks=%u");
void
command_query_mpu9250_status(uint32_t *args)
{
struct mpu9250 *mp = oid_lookup(args[0], command_config_mpu9250);
// Detect if a FIFO overrun occurred
uint8_t int_reg[] = {AR_INT_STATUS};
uint8_t int_msg;
read_mpu(mp->i2c, sizeof(int_reg), int_reg, sizeof(int_msg), &int_msg);
if (int_msg & FIFO_OVERFLOW_INT)
mp->sb.possible_overflows++;
// Read latest FIFO count (with precise timing)
uint8_t reg[] = {AR_FIFO_COUNT_H};
uint8_t msg[2];
uint32_t time1 = timer_read_time();
read_mpu(mp->i2c, sizeof(reg), reg, sizeof(msg), msg);
uint32_t time2 = timer_read_time();
uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1];
// Report status
sensor_bulk_status(&mp->sb, args[0], time1, time2-time1, fifo_bytes);
}
DECL_COMMAND(command_query_mpu9250_status, "query_mpu9250_status oid=%c");
void
mpu9250_task(void)
{
if (!sched_check_wake(&mpu9250_wake))
return;
uint8_t oid;
struct mpu9250 *mp;
foreach_oid(oid, mp, command_config_mpu9250) {
uint_fast8_t flags = mp->flags;
if (flags & AX_PENDING)
mp9250_query(mp, oid);
}
}
DECL_TASK(mpu9250_task);