altos: Re-enable the ms5607 and mma655x acquisition threads
[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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_mma655x.h>
20
21 #if HAS_MMA655X
22
23 #if 0
24 #define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); } while(0)
25 #else
26 #define PRINTD(...) 
27 #endif
28
29 static uint8_t  mma655x_configured;
30
31 uint8_t ao_mma655x_spi_index = AO_MMA655X_SPI_INDEX;
32
33 static void
34 ao_mma655x_start(void) {
35         ao_spi_get_bit(AO_MMA655X_CS_PORT,
36                        AO_MMA655X_CS_PIN,
37                        AO_MMA655X_CS,
38                        AO_MMA655X_SPI_INDEX,
39                        AO_SPI_SPEED_FAST);
40 }
41
42 static void
43 ao_mma655x_stop(void) {
44         ao_spi_put_bit(AO_MMA655X_CS_PORT,
45                        AO_MMA655X_CS_PIN,
46                        AO_MMA655X_CS,
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, AO_MMA655X_CS, 1);
54
55         /* Emperical testing on STM32L151 at 32MHz for this delay amount */
56         for (i = 0; i < 9; i++)
57                 ao_arch_nop();
58         ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, AO_MMA655X_CS, 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 static void
76 ao_mma655x_cmd(uint8_t d[2])
77 {
78         ao_mma655x_start();
79         PRINTD("\tSEND %02x %02x\n", d[0], d[1]);
80         ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
81         PRINTD("\t\tRECV %02x %02x\n", d[0], d[1]);
82         ao_mma655x_stop();
83 }
84
85 static uint8_t
86 ao_mma655x_reg_read(uint8_t addr)
87 {
88         uint8_t d[2];
89         ao_mma655x_start();
90         d[0] = addr | (ao_parity(addr) << 7);
91         d[1] = 0;
92         ao_spi_send(&d, 2, AO_MMA655X_SPI_INDEX);
93         ao_mma655x_restart();
94
95         /* Send a dummy read of 00 to clock out the SPI data */
96         d[0] = 0x80;
97         d[1] = 0x00;
98         ao_spi_duplex(&d, &d, 2, AO_MMA655X_SPI_INDEX);
99         ao_mma655x_stop();
100         return d[1];
101 }
102
103 static void
104 ao_mma655x_reg_write(uint8_t addr, uint8_t value)
105 {
106         uint8_t d[2];
107
108         addr |= (1 << 6);       /* write mode */
109         d[0] = addr | (ao_parity(addr^value) << 7);
110         d[1] = value;
111         ao_mma655x_start();
112         ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
113         ao_mma655x_stop();
114
115         addr &= ~(1 << 6);
116         PRINTD("write %x %x = %x\n",
117                addr, value, ao_mma655x_reg_read(addr));
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("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("value RECV %02x %02x\n", d[0], d[1]);
142
143         v = (uint16_t) d[1] << 2;
144         v |= d[0] >> 6;
145         v |= (uint16_t) (d[0] & 3) << 10;
146         return v;
147 }
148
149 static void
150 ao_mma655x_reset(void) {
151         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
152                              (0 << AO_MMA655X_DEVCTL_RES_1) |
153                              (0 << AO_MMA655X_DEVCTL_RES_1));
154         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
155                              (1 << AO_MMA655X_DEVCTL_RES_1) |
156                              (1 << AO_MMA655X_DEVCTL_RES_1));
157         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
158                              (0 << AO_MMA655X_DEVCTL_RES_1) |
159                              (1 << AO_MMA655X_DEVCTL_RES_1));
160 }
161
162 #define DEVCFG_VALUE    (\
163         (1 << AO_MMA655X_DEVCFG_OC) |           /* Disable offset cancelation */ \
164         (1 << AO_MMA655X_DEVCFG_SD) |           /* Receive unsigned data */ \
165         (0 << AO_MMA655X_DEVCFG_OFMON) |        /* Disable offset monitor */ \
166         (AO_MMA655X_DEVCFG_A_CFG_DISABLE << AO_MMA655X_DEVCFG_A_CFG))
167
168 #define AXISCFG_VALUE   (\
169                 (0 << AO_MMA655X_AXISCFG_LPF))  /* 100Hz 4-pole filter */
170
171
172 static void
173 ao_mma655x_setup(void)
174 {
175         uint8_t         v;
176         uint16_t        a, a_st;
177         uint8_t         stdefl;
178         uint8_t         i;
179         uint8_t s0, s1, s2, s3;
180         uint8_t pn;
181         uint32_t        lot;
182         uint16_t        serial;
183
184
185         if (mma655x_configured)
186                 return;
187         mma655x_configured = 1;
188         ao_delay(AO_MS_TO_TICKS(10));   /* Top */
189         ao_mma655x_reset();
190         ao_delay(AO_MS_TO_TICKS(10));   /* Top */
191         (void) ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
192         v = ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
193
194         /* Configure R/W register values.
195          * Most of them relate to the arming feature, which
196          * we don't use, so the only registers we need to
197          * write are DEVCFG and AXISCFG
198          */
199
200         ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
201                              DEVCFG_VALUE | (0 << AO_MMA655X_DEVCFG_ENDINIT));
202
203         /* Test X axis
204          */
205         
206         ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
207                              AXISCFG_VALUE |
208                              (1 << AO_MMA655X_AXISCFG_ST));
209         for (i = 0; i < 10; i++) {
210                 a_st = ao_mma655x_value();
211                 printf ("SELF-TEST %2d = %6d\n", i, a_st);
212         }
213
214         stdefl = ao_mma655x_reg_read(AO_MMA655X_STDEFL);
215
216         ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
217                              AXISCFG_VALUE |
218                              (0 << AO_MMA655X_AXISCFG_ST));
219         a = ao_mma655x_value();
220
221         for (i = 0; i < 10; i++) {
222                 a = ao_mma655x_value();
223                 printf("NORMAL   %2d = %6d\n", i, a);
224         }
225
226         ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
227                              DEVCFG_VALUE | (1 << AO_MMA655X_DEVCFG_ENDINIT));
228         s0 = ao_mma655x_reg_read(AO_MMA655X_SN0);
229         s1 = ao_mma655x_reg_read(AO_MMA655X_SN1);
230         s2 = ao_mma655x_reg_read(AO_MMA655X_SN2);
231         s3 = ao_mma655x_reg_read(AO_MMA655X_SN3);
232         lot = ((uint32_t) s3 << 24) | ((uint32_t) s2 << 16) |
233                 ((uint32_t) s1 << 8) | ((uint32_t) s0);
234         serial = lot & 0x1fff;
235         lot >>= 12;
236         pn = ao_mma655x_reg_read(AO_MMA655X_PN);
237         printf ("MMA655X lot %d serial %d number %d\n", lot, serial, pn);
238
239 }
240
241 static void
242 ao_mma655x_dump(void)
243 {
244         ao_mma655x_setup();
245         printf ("MMA655X value %d\n", ao_mma655x_value());
246 }
247
248 __code struct ao_cmds ao_mma655x_cmds[] = {
249         { ao_mma655x_dump,      "A\0Display MMA655X data" },
250         { 0, NULL },
251 };
252
253 static void
254 ao_mma655x(void)
255 {
256         ao_mma655x_setup();
257         for (;;) {
258                 ao_data_ring[ao_data_head].mma655x = ao_mma655x_value();
259                 ao_arch_critical(
260                         AO_DATA_PRESENT(AO_DATA_MMA655X);
261                         AO_DATA_WAIT();
262                         );
263         }
264 }
265
266 static __xdata struct ao_task ao_mma655x_task;
267
268 void
269 ao_mma655x_init(void)
270 {
271         mma655x_configured = 0;
272
273         ao_cmd_register(&ao_mma655x_cmds[0]);
274         ao_spi_init_cs(AO_MMA655X_CS_PORT, (1 << AO_MMA655X_CS_PIN));
275
276         ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x");
277 }
278
279 #endif