forked from proactivity-lab-study/esw-digital-sensor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmma8653fc_driver.c
327 lines (280 loc) · 10.4 KB
/
mma8653fc_driver.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
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/**
* @file mma8653fc_driver.c
*
* @note I2C set-up must be done separately and before the usage of this driver.
* GPIO interrupt set-up must be done separately if MMA8653FC interrupts are used.
*
* @author Johannes Ehala, ProLab.
* @license MIT
*
* Copyright ProLab, TTÜ. 2021
*/
#include "cmsis_os2.h" // For osDelay() in sensor_reset() function.
#include "mma8653fc_reg.h"
#include "mma8653fc_driver.h"
#include "em_i2c.h"
#include "i2c_handler.h"
#include "loglevels.h"
#define __MODUUL__ "sdrv"
#define __LOG_LEVEL__ (LOG_LEVEL_mmadrv & BASE_LOG_LEVEL)
#include "log.h"
static uint8_t read_registry(uint8_t regAddr);
static void read_multiple_registries(uint8_t startRegAddr, uint8_t *rxBuf, uint16_t rxBufLen);
static void write_registry(uint8_t regAddr, uint8_t regVal);
/**
* @brief Reset MMA8653FC sensor (software reset).
*/
void sensor_reset(void)
{
uint8_t regVal;
regVal = read_registry(MMA8653FC_REGADDR_CTRL_REG2);
regVal = (regVal & ~MMA8653FC_CTRL_REG2_SOFTRST_MASK) | (MMA8653FC_CTRL_REG2_SOFTRST_EN << MMA8653FC_CTRL_REG2_SOFTRST_SHIFT);
write_registry(MMA8653FC_REGADDR_CTRL_REG2, regVal);
osDelay(5 * osKernelGetTickFreq() / 1000); // Wait a little for reset to finish.
}
/**
* @brief Sets sensor to active mode.
*/
void set_sensor_active()
{
uint8_t reg_val;
reg_val = read_registry(MMA8653FC_REGADDR_CTRL_REG1);
reg_val = (reg_val & ~MMA8653FC_CTRL_REG1_SAMODE_MASK) | (MMA8653FC_CTRL_REG1_SAMODE_ACTIVE << MMA8653FC_CTRL_REG1_SAMODE_SHIFT);
// Change mode to ACTIVE
write_registry(MMA8653FC_REGADDR_CTRL_REG1, reg_val);
}
/**
* @brief Sets sensor to standby mode. Sensor must be in standby mode when writing to
* different config registries.
*/
void set_sensor_standby()
{
// Change mode to STANDBY
uint8_t reg_val;
reg_val = read_registry(MMA8653FC_REGADDR_CTRL_REG1);
reg_val = (reg_val & ~MMA8653FC_CTRL_REG1_SAMODE_MASK) | (MMA8653FC_CTRL_REG1_SAMODE_STANDBY << MMA8653FC_CTRL_REG1_SAMODE_SHIFT);
// Change mode to ACTIVE
write_registry(MMA8653FC_REGADDR_CTRL_REG1, reg_val);
}
uint8_t read_whoami()
{
return read_registry(MMA8653FC_REGADDR_WHO_AM_I);
}
/**
* @brief Configures MMA8653FC sensor to start collecting xyz acceleration data.
*
* @param dataRate Set data rate (1.56, 6.26, 12, 50, 100, 200, 400, 800 Hz)
* @param range Set dynamic range (+- 2g, +- 4g, +- 8g)
* @param powerMod Set power mode (normal, low-noise-low-power, highres, low-power)
*
* @return -1 if sensor is not in standby mode
* 0 if configuration succeeded (no check)
*/
int8_t configure_xyz_data(uint8_t dataRate, uint8_t range, uint8_t powerMod)
{
// Check if sensor is in standby mode, control registers can only be modified in standby mode.
uint8_t reg_val;
reg_val = read_registry(MMA8653FC_REGADDR_SYSMOD);
if (reg_val == MMA8653FC_SYSMOD_MOD_STANDBY)
{
// Set data rate.
uint8_t reg_val_data;
reg_val_data = read_registry(MMA8653FC_REGADDR_CTRL_REG1);
reg_val_data = (reg_val_data & ~MMA8653FC_CTRL_REG1_DATA_RATE_MASK) | (dataRate << MMA8653FC_CTRL_REG1_DATA_RATE_SHIFT);
// Set dynamic range.
uint8_t reg_val_range;
reg_val_range = read_registry(MMA8653FC_REGADDR_XYZ_DATA_CFG);
reg_val_range = (reg_val_range & ~MMA8653FC_XYZ_DATA_CFG_RANGE_MASK) | (range << MMA8653FC_XYZ_DATA_CFG_RANGE_SHIFT);
// Set power mode (oversampling)
uint8_t reg_val_power;
reg_val_power = read_registry(MMA8653FC_REGADDR_CTRL_REG2);
reg_val_power = (reg_val_power & ~MMA8653FC_CTRL_REG2_ACTIVEPOW_MASK) | (powerMod << MMA8653FC_CTRL_REG2_ACTIVEPOW_SHIFT);
write_registry(MMA8653FC_REGADDR_CTRL_REG1, reg_val_data);
write_registry(MMA8653FC_REGADDR_XYZ_DATA_CFG, reg_val_range);
write_registry(MMA8653FC_REGADDR_CTRL_REG2, reg_val_power);
return 0;
}
return -1;
}
/**
* @brief Configures MMA8653FC sensor to start collecting xyz acceleration data.
*
* @param polarity Set interrupt pin polarity.
* @param pinmode Set interrupt pin pinmode.
* @param interrupt Set interrupts to use.
* @param int_select Route interrupts to selected pin.
*
* @return -1 if sensor is not in standby mode
* 0 if configuration succeeded (no check)
*/
int8_t configure_interrupt(uint8_t polarity, uint8_t pinmode, uint8_t interrupt, uint8_t int_select)
{
// Check if sensor is in standby mode, control registers can only be modified in standby mode.
uint8_t reg_val;
reg_val = read_registry(MMA8653FC_REGADDR_SYSMOD);
if (reg_val == MMA8653FC_SYSMOD_MOD_STANDBY)
{
// Configure interrupt pin pinmode and interrupt transition direction
uint8_t reg_val_int_pin;
reg_val_int_pin = read_registry(MMA8653FC_REGADDR_CTRL_REG3);
reg_val_int_pin = (reg_val_int_pin & ~MMA8653FC_CTRL_REG3_POLARITY_MASK) | (polarity << MMA8653FC_CTRL_REG3_POLARITY_SHIFT);
reg_val_int_pin = (reg_val_int_pin & ~MMA8653FC_CTRL_REG3_PINMODE_MASK) | (pinmode << MMA8653FC_CTRL_REG3_PINMODE_SHIFT);
// Enable data ready interrupt
uint8_t reg_val_int_enable;
reg_val_int_enable = read_registry(MMA8653FC_REGADDR_CTRL_REG4);
reg_val_int_enable = (reg_val_int_enable & ~MMA8653FC_CTRL_REG4_DRDY_INT_MASK) | (interrupt << MMA8653FC_CTRL_REG4_DRDY_INT_SHIFT);
// Route data ready interrupt to sensor INT1 output pin (connected to port PA1 on the TTTW uC)
uint8_t reg_val_int_route;
reg_val_int_route = read_registry(MMA8653FC_REGADDR_CTRL_REG5);
reg_val_int_route = (reg_val_int_route & ~MMA8653FC_CTRL_REG5_DRDY_INTSEL_MASK) | (int_select << MMA8653FC_CTRL_REG5_DRDY_INTSEL_SHIFT);
write_registry(MMA8653FC_REGADDR_CTRL_REG3, reg_val_int_pin);
write_registry(MMA8653FC_REGADDR_CTRL_REG4, reg_val_int_enable);
write_registry(MMA8653FC_REGADDR_CTRL_REG5, reg_val_int_route);
return 0;
}
return -1;
}
/**
* @brief Reads MMA8653FC STATUS and data registries.
*
* @return Returns value of STATUS registry and x, y, z, 10 bit raw values (left-justified 2's complement)
*/
xyz_rawdata_t get_xyz_data()
{
xyz_rawdata_t data;
uint8_t rx_buf[7];
// Read multiple registries for status and x, y, z raw data
read_multiple_registries(MMA8653FC_REGADDR_STATUS, rx_buf, 7);
data.status = rx_buf[0];
data.out_x = (uint16_t)(rx_buf[1] << 8) | (0x0000 | rx_buf[2]);
data.out_y = (uint16_t)(rx_buf[3] << 8) | (0x0000 | rx_buf[4]);
data.out_z = (uint16_t)(rx_buf[5] << 8) | (0x0000 | rx_buf[6]);
return data;
}
/**
* @brief Read value of one registry of MMA8653FC.
*
* @param regAddr Address of registry to read.
*
* @return value of registry with address regAddr.
*/
static uint8_t read_registry(uint8_t regAddr)
{
uint8_t reg;
I2C_TransferSeq_TypeDef *ret, seq;
static uint8_t tx_buf[1], rx_buf[1];
// Configure I2C_TransferSeq_TypeDef
seq.addr = MMA8653FC_SLAVE_ADDRESS_READ;
tx_buf[0] = regAddr;
seq.buf[0].data = tx_buf;
seq.buf[0].len = 1;
rx_buf[0] = 0;
seq.buf[1].data = rx_buf;
seq.buf[1].len = 1;
seq.flags = I2C_FLAG_WRITE_READ;
// Read a value from MMA8653FC registry
ret = i2c_transaction(&seq);
reg = ret->buf[1].data[0];
return reg;
}
/**
* @brief Write a value to one registry of MMA8653FC.
*
* @param regAddr Address of registry to read.
* @param regVal Value to write to MMA8653FC registry.
*
* @note rxBuf is not used I think, maybe replace with NULL pointer.
*/
static void write_registry(uint8_t regAddr, uint8_t regVal)
{
uint8_t reg;
I2C_TransferSeq_TypeDef *ret, seq;
static uint8_t tx_buf[2], rx_buf[1];
// Configure I2C_TransferSeq_TypeDef
seq.addr = MMA8653FC_SLAVE_ADDRESS_WRITE;
tx_buf[0] = regAddr;
tx_buf[1] = regVal;
seq.buf[0].data = tx_buf;
seq.buf[0].len = 2;
rx_buf[0] = 0;
seq.buf[1].data = rx_buf;
seq.buf[1].len = 1;
seq.flags = I2C_FLAG_WRITE_WRITE;
// Read a value from MMA8653FC registry
ret = i2c_transaction(&seq);
return;
}
/**
* @brief Read multiple registries of MMA8653FC in one go.
* @note MMA8653FC increments registry pointer to read internally according to its own logic.
* Registries next to each other are not necessarily read in succession. Check MMA8653FC
* datasheet to see how registry pointer is incremented.
*
* @param startRegAddr Address of registry to start reading from.
* @param *rxBuf Pointer to memory area where read values are stored.
* @param rxBufLen Length/size of rxBuf memory area.
*/
static void read_multiple_registries(uint8_t startRegAddr, uint8_t *rxBuf, uint16_t rxBufLen)
{
uint8_t loopVar, regAddr, regData;
regAddr = startRegAddr;
loopVar = 0;
while (loopVar < rxBufLen)
{
regData = read_registry(regAddr);
rxBuf[loopVar] = regData;
regAddr++;
loopVar++;
}
return;
}
/**
* @brief Converts MMA8653FC sensor output value (left-justified 10-bit 2's complement
* number) to decimal number representing MMA8653FC internal ADC read-out
* (including bias).
*
* @param raw_val is expected to be left-justified 10-bit 2's complement number
*
* @return decimal number ranging between -512 ... 511
*/
int16_t convert_to_count(uint16_t raw_val)
{
// TODO Convert raw sensor data to ADC readout (count) value
int16_t count = raw_val;
count >>= 6;
return count;
}
/**
* @brief Converts MMA8653FC sensor output value (left-justified 10-bit 2's complement
* number) to floating point number representing acceleration rate in g.
*
* @param raw_val is expected to be left-justified 10-bit 2's complement number
* @param sensor_scale sensor scale 2g, 4g or 8g
*
* @return floating point number, value depending on chosen sensor range
* +/- 2g -> range -2 ... 1.996
* +/- 4g -> range -4 ... 3.992
* +/- 8g -> range -8 ... 7.984
*/
float convert_to_g(uint16_t raw_val, uint8_t sensor_scale)
{
float res;
// TODO Convert raw sensor data to g-force acceleration value
if (sensor_scale == 2)
{
res = (1000 * raw_val + 1024) >> 6;
}
else if (sensor_scale == 4)
{
res = (1000 * raw_val + 512) >> 5;
}
else if (sensor_scale == 8)
{
res = (1000 * raw_val + 256) >> 4;
}
else
{
res = 0;
}
return res / 1000;
}