first cut at turnon scripts for EasyTimer v2
[fw/altos] / src / drivers / ao_mma655x.c
1 /*
2  * Copyright © 2012 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  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_mma655x.h>
21
22 #if HAS_MMA655X
23
24 #define DEBUG           0
25 #define DEBUG_LOW       1
26 #define DEBUG_HIGH      2
27 #if 1
28 #define PRINTD(l, ...) do { if (DEBUG & (l)) { printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } } while(0)
29 #else
30 #define PRINTD(l,...) 
31 #endif
32
33 #define AO_MMA655X_SPI_SPEED    ao_spi_speed(8333333)   /* 120ns clock period */
34
35 static void
36 ao_mma655x_start(void) {
37         ao_spi_get_bit(AO_MMA655X_CS_PORT,
38                        AO_MMA655X_CS_PIN,
39                        AO_MMA655X_SPI_INDEX,
40                        AO_MMA655X_SPI_SPEED);
41 }
42
43 static void
44 ao_mma655x_stop(void) {
45         ao_spi_put_bit(AO_MMA655X_CS_PORT,
46                        AO_MMA655X_CS_PIN,
47                        AO_MMA655X_SPI_INDEX);
48 }
49
50 static void
51 ao_mma655x_restart(void) {
52         uint8_t i;
53         ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, 1);
54
55         /* Emperical testing on STM32L151 at 32MHz for this delay amount */
56         for (i = 0; i < 10; i++)
57                 ao_arch_nop();
58         ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, 0);
59 }
60
61 static uint8_t
62 ao_parity(uint8_t v)
63 {
64         uint8_t p;
65         /* down to four bits */
66         p = (v ^ (v >> 4)) & 0xf;
67
68         /* Cute lookup hack -- 0x6996 encodes the sixteen
69          * even parity values in order.
70          */
71         p = (~0x6996 >> p) & 1;
72         return p;
73 }
74
75 #if 0
76 static void
77 ao_mma655x_cmd(uint8_t d[2])
78 {
79         ao_mma655x_start();
80         PRINTD(DEBUG_LOW, "\tSEND %02x %02x\n", d[0], d[1]);
81         ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
82         PRINTD(DEBUG_LOW, "\t\tRECV %02x %02x\n", d[0], d[1]);
83         ao_mma655x_stop();
84 }
85 #endif
86
87 static uint8_t
88 ao_mma655x_reg_read(uint8_t addr)
89 {
90         uint8_t d[2];
91         ao_mma655x_start();
92         d[0] = (uint8_t) (addr | (ao_parity(addr) << 7));
93         d[1] = 0;
94         ao_spi_send(&d, 2, AO_MMA655X_SPI_INDEX);
95         ao_mma655x_restart();
96
97         /* Send a dummy read of 00 to clock out the SPI data */
98         d[0] = 0x80;
99         d[1] = 0x00;
100         ao_spi_duplex(&d, &d, 2, AO_MMA655X_SPI_INDEX);
101         ao_mma655x_stop();
102         PRINTD(DEBUG_LOW, "read %x = %x %x\n", addr, d[0], d[1]);
103         return d[1];
104 }
105
106 static void
107 ao_mma655x_reg_write(uint8_t addr, uint8_t value)
108 {
109         uint8_t d[2];
110
111         PRINTD(DEBUG_LOW, "write %x %x\n", addr, value);
112         addr |= (1 << 6);       /* write mode */
113         d[0] = (uint8_t) (addr | (ao_parity(addr^value) << 7));
114         d[1] = value;
115         ao_mma655x_start();
116         ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
117         ao_mma655x_stop();
118 }
119
120 static uint16_t
121 ao_mma655x_value(void)
122 {
123         uint8_t         d[2];
124         uint16_t        v;
125
126         d[0] = ((0 << 6) |      /* Axis selection (X) */
127                 (1 << 5) |      /* Acceleration operation */
128                 (1 << 4));      /* Raw data */
129         d[1] = ((1 << 3) |      /* must be one */
130                 (1 << 2) |      /* Unsigned data */
131                 (0 << 1) |      /* Arm disabled */
132                 (1 << 0));      /* Odd parity */
133         ao_mma655x_start();
134         PRINTD(DEBUG_LOW, "value SEND %02x %02x\n", d[0], d[1]);
135         ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
136         ao_mma655x_restart();
137         d[0] = 0x80;
138         d[1] = 0x00;
139         ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
140         ao_mma655x_stop();
141         PRINTD(DEBUG_LOW, "value RECV %02x %02x\n", d[0], d[1]);
142
143         v = (uint16_t) d[1] << 2;
144         v = (uint16_t) (v | (d[0] >> 6));
145         v = (uint16_t) (v | ((d[0] & 3) << 10));
146         return v;
147 }
148
149 static void
150 ao_mma655x_reset(void) {
151         PRINTD(DEBUG_HIGH, "reset\n");
152         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
153                              (0 << AO_MMA655X_DEVCTL_RES_1) |
154                              (0 << AO_MMA655X_DEVCTL_RES_0));
155         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
156                              (1 << AO_MMA655X_DEVCTL_RES_1) |
157                              (1 << AO_MMA655X_DEVCTL_RES_0));
158         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
159                              (0 << AO_MMA655X_DEVCTL_RES_1) |
160                              (1 << AO_MMA655X_DEVCTL_RES_0));
161 }
162
163 #define DEVCFG_VALUE    (\
164         (1 << AO_MMA655X_DEVCFG_OC) |           /* Disable offset cancelation */ \
165         (1 << AO_MMA655X_DEVCFG_SD) |           /* Receive unsigned data */ \
166         (0 << AO_MMA655X_DEVCFG_OFMON) |        /* Disable offset monitor */ \
167         (AO_MMA655X_DEVCFG_A_CFG_DISABLE << AO_MMA655X_DEVCFG_A_CFG))
168
169 #define AXISCFG_VALUE   (\
170                 (0 << AO_MMA655X_AXISCFG_LPF))  /* 100Hz 4-pole filter */
171
172
173 #define AO_ST_TRIES     10
174 #define AO_ST_DELAY     AO_MS_TO_TICKS(100)
175
176 static void
177 ao_mma655x_setup(void)
178 {
179         uint16_t        a, a_st;
180         int16_t         st_change;
181         int             tries;
182         uint8_t         devstat;
183 #if 0
184         uint8_t s0, s1, s2, s3;
185         uint32_t        lot;
186 #endif
187
188         for (tries = 0; tries < AO_ST_TRIES; tries++) {
189                 ao_delay(AO_MS_TO_TICKS(10));
190                 ao_mma655x_reset();
191                 ao_delay(AO_MS_TO_TICKS(10));
192
193                 devstat = ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
194                 PRINTD(DEBUG_HIGH, "devstat %x\n", devstat);
195
196                 if (!(devstat & (1 << AO_MMA655X_DEVSTAT_DEVRES)))
197                         continue;
198
199                 /* Configure R/W register values.
200                  * Most of them relate to the arming feature, which
201                  * we don't use, so the only registers we need to
202                  * write are DEVCFG and AXISCFG
203                  */
204
205                 ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
206                                      DEVCFG_VALUE | (0 << AO_MMA655X_DEVCFG_ENDINIT));
207
208                 /* Test X axis
209                  */
210         
211                 ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
212                                      AXISCFG_VALUE |
213                                      (1 << AO_MMA655X_AXISCFG_ST));
214                 ao_delay(AO_MS_TO_TICKS(10));
215
216                 a_st = ao_mma655x_value();
217
218                 ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
219                                      AXISCFG_VALUE |
220                                      (0 << AO_MMA655X_AXISCFG_ST));
221
222                 ao_delay(AO_MS_TO_TICKS(10));
223
224                 a = ao_mma655x_value();
225
226                 st_change = (int16_t) (a_st - a);
227
228                 PRINTD(DEBUG_HIGH, "self test %d normal %d change %d\n", a_st, a, st_change);
229
230                 if (AO_ST_MIN <= st_change && st_change <= AO_ST_MAX)
231                         break;
232                 ao_delay(AO_ST_DELAY);
233         }
234         if (tries == AO_ST_TRIES)
235                 AO_SENSOR_ERROR(AO_DATA_MMA655X);
236
237         ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
238                              DEVCFG_VALUE | (1 << AO_MMA655X_DEVCFG_ENDINIT));
239 #if 0
240         s0 = ao_mma655x_reg_read(AO_MMA655X_SN0);
241         s1 = ao_mma655x_reg_read(AO_MMA655X_SN1);
242         s2 = ao_mma655x_reg_read(AO_MMA655X_SN2);
243         s3 = ao_mma655x_reg_read(AO_MMA655X_SN3);
244         lot = ((uint32_t) s3 << 24) | ((uint32_t) s2 << 16) |
245                 ((uint32_t) s1 << 8) | ((uint32_t) s0);
246         serial = lot & 0x1fff;
247         lot >>= 12;
248         pn = ao_mma655x_reg_read(AO_MMA655X_PN);
249 #endif
250 }
251
252 uint16_t        ao_mma655x_current;
253
254 static void
255 ao_mma655x_dump(void)
256 {
257         printf ("MMA655X value %d\n", ao_mma655x_current);
258 }
259
260 const struct ao_cmds ao_mma655x_cmds[] = {
261         { ao_mma655x_dump,      "A\0Display MMA655X data" },
262         { 0, NULL },
263 };
264
265 static void
266 ao_mma655x(void)
267 {
268         ao_mma655x_setup();
269         for (;;) {
270                 ao_mma655x_current = ao_mma655x_value();
271                 ao_arch_critical(
272                         AO_DATA_PRESENT(AO_DATA_MMA655X);
273                         AO_DATA_WAIT();
274                         );
275         }
276 }
277
278 static struct ao_task ao_mma655x_task;
279
280 void
281 ao_mma655x_init(void)
282 {
283         ao_cmd_register(&ao_mma655x_cmds[0]);
284         ao_spi_init_cs(AO_MMA655X_CS_PORT, (1 << AO_MMA655X_CS_PIN));
285
286         ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x");
287 }
288
289 #endif