f53fb59d7ad2147aa04eb2872bd779e706d7ad1a
[fw/altos] / src / drivers / ao_adxl375.c
1 /*
2  * Copyright © 2018 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include <ao.h>
16 #include "ao_adxl375.h"
17
18 #define DEBUG_LOW       1
19 #define DEBUG_HIGH      2
20
21 #define DEBUG           3
22
23 #if DEBUG
24 #define PRINTD(l, ...) do { if (DEBUG & (l)) { printf ("\r%5lu %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } } while(0)
25 #else
26 #define PRINTD(l,...)
27 #endif
28
29 #define AO_ADXL375_SPI_SPEED    ao_spi_speed(5000000)
30
31 struct ao_adxl375_sample        ao_adxl375_current;
32
33 static void
34 ao_adxl375_start(void) {
35         ao_spi_get_bit(AO_ADXL375_CS_PORT,
36                        AO_ADXL375_CS_PIN,
37                        AO_ADXL375_SPI_INDEX,
38                        AO_ADXL375_SPI_SPEED);
39 }
40
41 static void
42 ao_adxl375_stop(void) {
43         ao_spi_put_bit(AO_ADXL375_CS_PORT,
44                        AO_ADXL375_CS_PIN,
45                        AO_ADXL375_SPI_INDEX);
46 }
47
48 static uint8_t
49 ao_adxl375_reg_read(uint8_t addr)
50 {
51         uint8_t d[1];
52
53         d[0] = addr | AO_ADXL375_READ;
54         ao_adxl375_start();
55         ao_spi_send(d, 1, AO_ADXL375_SPI_INDEX);
56         ao_spi_recv(d, 1, AO_ADXL375_SPI_INDEX);
57         ao_adxl375_stop();
58
59         PRINTD(DEBUG_LOW, "read %x = %x\n", addr, d[0]);
60
61         return d[0];
62 }
63
64 static void
65 ao_adxl375_reg_write(uint8_t addr, uint8_t value)
66 {
67         uint8_t d[2];
68
69         PRINTD(DEBUG_LOW, "write %x %x\n", addr, value);
70         d[0] = addr;
71         d[1] = value;
72         ao_adxl375_start();
73         ao_spi_send(d, 2, AO_ADXL375_SPI_INDEX);
74         ao_adxl375_stop();
75
76 #if DEBUG & DEBUG_LOW
77         d[0] = addr | AO_ADXL375_READ;
78         ao_adxl375_start();
79         ao_spi_send(d, 1, AO_ADXL375_SPI_INDEX);
80         ao_spi_recv(d, 1, AO_ADXL375_SPI_INDEX);
81         ao_adxl375_stop();
82         PRINTD(DEBUG_LOW, "readback %x\n", d[0]);
83 #endif
84 }
85
86 static void
87 ao_adxl375_value(struct ao_adxl375_sample *value)
88 {
89         uint8_t d[7];
90
91         d[0] = AO_ADXL375_DATAX0 | AO_ADXL375_READ | AO_ADXL375_MULTI_BYTE;
92         ao_adxl375_start();
93         ao_spi_send(d, 1, AO_ADXL375_SPI_INDEX);
94         ao_spi_recv(value, 6, AO_ADXL375_SPI_INDEX);
95         ao_adxl375_stop();
96 }
97
98 struct ao_adxl375_total {
99         int32_t x;
100         int32_t y;
101         int32_t z;
102 };
103
104 #define AO_ADXL375_SELF_TEST_SAMPLES    10
105 #define AO_ADXL375_SELF_TEST_SETTLE     4
106
107 #define MIN_LSB_G       18.4
108 #define MAX_LSB_G       22.6
109 #define SELF_TEST_MIN_G 5.0
110 #define SELF_TEST_MAX_G 6.8
111
112 #define MIN_SELF_TEST   ((int32_t) (MIN_LSB_G * SELF_TEST_MIN_G * AO_ADXL375_SELF_TEST_SAMPLES + 0.5))
113 #define MAX_SELF_TEST   ((int32_t) (MAX_LSB_G * SELF_TEST_MAX_G * AO_ADXL375_SELF_TEST_SAMPLES + 0.5))
114
115 static void
116 ao_adxl375_total_value(struct ao_adxl375_total *total, int samples)
117 {
118         struct ao_adxl375_sample        value;
119
120         *total = (struct ao_adxl375_total) { 0, 0, 0 };
121         for (int i = 0; i < samples; i++) {
122                 ao_adxl375_value(&value);
123                 total->x += value.x;
124                 total->y += value.y;
125                 total->z += value.z;
126                 ao_delay(AO_MS_TO_TICKS(10));
127         }
128 }
129
130 #define AO_ADXL375_DATA_FORMAT_SETTINGS(self_test) (                    \
131                 AO_ADXL375_DATA_FORMAT_FIXED |                          \
132                 (self_test << AO_ADXL375_DATA_FORMAT_SELF_TEST) |       \
133                 (AO_ADXL375_DATA_FORMAT_SPI_4_WIRE << AO_ADXL375_DATA_FORMAT_SPI_4_WIRE) | \
134                 (0 << AO_ADXL375_DATA_FORMAT_INT_INVERT) |              \
135                 (0 << AO_ADXL375_DATA_FORMAT_JUSTIFY))
136
137 static int32_t  self_test_value;
138
139 static void
140 ao_adxl375_setup(void)
141 {
142         /* Get the device into 4-wire SPI mode before proceeding */
143         ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
144                              AO_ADXL375_DATA_FORMAT_SETTINGS(0));
145
146
147         uint8_t devid = ao_adxl375_reg_read(AO_ADXL375_DEVID);
148         if (devid != AO_ADXL375_DEVID_ID)
149                 AO_SENSOR_ERROR(AO_DATA_ADXL375);
150
151         /* Set the data rate */
152         ao_adxl375_reg_write(AO_ADXL375_BW_RATE,
153                              (0 << AO_ADXL375_BW_RATE_LOW_POWER) |
154                              (AO_ADXL375_BW_RATE_RATE_200 << AO_ADXL375_BW_RATE_RATE));
155
156         /* Set the offsets all to zero */
157         ao_adxl375_reg_write(AO_ADXL375_OFSX, 0);
158         ao_adxl375_reg_write(AO_ADXL375_OFSY, 0);
159         ao_adxl375_reg_write(AO_ADXL375_OFSZ, 0);
160
161         /* Clear interrupts */
162         ao_adxl375_reg_write(AO_ADXL375_INT_ENABLE, 0);
163
164         /* Configure FIFO (disable) */
165         ao_adxl375_reg_write(AO_ADXL375_FIFO_CTL,
166                              (AO_ADXL375_FIFO_CTL_FIFO_MODE_BYPASS << AO_ADXL375_FIFO_CTL_FIFO_MODE) |
167                              (0 << AO_ADXL375_FIFO_CTL_TRIGGER) |
168                              (0 << AO_ADXL375_FIFO_CTL_SAMPLES));
169
170         /* Place part in measurement mode */
171         ao_adxl375_reg_write(AO_ADXL375_POWER_CTL,
172                              (0 << AO_ADXL375_POWER_CTL_LINK) |
173                              (0 << AO_ADXL375_POWER_CTL_AUTO_SLEEP) |
174                              (1 << AO_ADXL375_POWER_CTL_MEASURE) |
175                              (0 << AO_ADXL375_POWER_CTL_SLEEP) |
176                              (AO_ADXL375_POWER_CTL_WAKEUP_8 << AO_ADXL375_POWER_CTL_WAKEUP));
177
178         /* Perform self-test */
179
180         struct ao_adxl375_total self_test_off, self_test_on;
181
182         /* Discard some samples to let it settle down */
183         ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SETTLE);
184
185         /* Get regular values */
186         ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SAMPLES);
187
188         /* Turn on self test */
189         ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
190                              AO_ADXL375_DATA_FORMAT_SETTINGS(1));
191
192         /* Discard at least 4 samples to let the device settle */
193         ao_adxl375_total_value(&self_test_on, AO_ADXL375_SELF_TEST_SETTLE);
194
195         /* Read self test samples */
196         ao_adxl375_total_value(&self_test_on, AO_ADXL375_SELF_TEST_SAMPLES);
197
198         /* Reset back to normal mode */
199
200         ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
201                              AO_ADXL375_DATA_FORMAT_SETTINGS(0));
202
203         /* Verify Z axis value is in range */
204
205         int32_t z_change = self_test_on.z - self_test_off.z;
206
207         self_test_value = z_change;
208
209         if (z_change < MIN_SELF_TEST)
210                 AO_SENSOR_ERROR(AO_DATA_ADXL375);
211
212         /* This check is commented out as maximum self test is unreliable
213
214            if (z_change > MAX_SELF_TEST)
215                 AO_SENSOR_ERROR(AO_DATA_ADXL375);
216
217         */
218
219         /* Discard some samples to let it settle down */
220         ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SETTLE);
221 }
222
223 static void
224 ao_adxl375(void)
225 {
226         ao_adxl375_setup();
227         for (;;) {
228                 ao_adxl375_value(&ao_adxl375_current);
229                 ao_arch_critical(
230                         AO_DATA_PRESENT(AO_DATA_ADXL375);
231                         AO_DATA_WAIT();
232                         );
233         }
234 }
235
236 static struct ao_task ao_adxl375_task;
237
238 static void
239 ao_adxl375_dump(void)
240 {
241         printf ("ADXL375 value %d %d %d self test %ld min %ld max %ld\n",
242                 ao_adxl375_current.x,
243                 ao_adxl375_current.y,
244                 ao_adxl375_current.z,
245                 (long) self_test_value,
246                 (long) MIN_SELF_TEST,
247                 (long) MAX_SELF_TEST);
248 }
249
250 const struct ao_cmds ao_adxl375_cmds[] = {
251         { ao_adxl375_dump,      "A\0Display ADXL375 data" },
252         { 0, NULL },
253 };
254
255 void
256 ao_adxl375_init(void)
257 {
258         ao_cmd_register(ao_adxl375_cmds);
259         ao_spi_init_cs(AO_ADXL375_CS_PORT, (1 << AO_ADXL375_CS_PIN));
260
261         ao_add_task(&ao_adxl375_task, ao_adxl375, "adxl375");
262 }