Merge remote-tracking branch 'mjb/master'
[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 static uint8_t  mma655x_configured;
24
25 static void
26 ao_mma655x_start(void) {
27         ao_spi_get_bit(AO_MMA655X_CS_GPIO,
28                        AO_MMA655X_CS,
29                        AO_MMA655X_CS_PIN,
30                        AO_MMA655X_SPI_INDEX,
31                        AO_SPI_SPEED_FAST);
32 }
33
34 static void
35 ao_mma655x_stop(void) {
36         ao_spi_put_bit(AO_MMA655X_CS_GPIO,
37                        AO_MMA655X_CS,
38                        AO_MMA655X_CS_PIN,
39                        AO_MMA655X_SPI_INDEX);
40 }
41
42 static uint8_t
43 ao_parity(uint8_t v)
44 {
45         /* down to four bits */
46         v = (v ^ (v >> 4)) & 0xf;
47
48         /* Cute lookup hack -- 0x6996 encodes the sixteen
49          * even parity values in order.
50          */
51         return (~0x6996 >> v) & 1;
52 }
53
54 static void
55 ao_mma655x_cmd(uint8_t d[2])
56 {
57         ao_mma655x_start();
58         ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
59         ao_spi_recv(d, 2, AO_MMA655X_SPI_INDEX);
60         ao_mma655x_stop();
61 }
62
63 static uint8_t
64 ao_mma655x_reg_write(uint8_t addr, uint8_t value)
65 {
66         uint8_t d[2];
67
68         addr |= (1 << 6);       /* write mode */
69         d[0] = addr | (ao_parity(addr^value) << 7);
70         d[1] = value;
71         ao_mma655x_cmd(d);
72         return d[1];
73 }
74
75 static uint8_t
76 ao_mma655x_reg_read(uint8_t addr)
77 {
78         uint8_t d[2];
79
80         d[0] = addr | (ao_parity(addr) << 7);
81         d[1] = 0;
82         ao_mma655x_cmd(d);
83         return d[1];
84 }
85
86 static uint16_t
87 ao_mma655x_value(void)
88 {
89         uint8_t         d[2];
90         uint16_t        v;
91
92         d[0] = ((0 << 7) |      /* Axis selection (X) */
93                 (1 << 6) |      /* Acceleration operation */
94                 (1 << 5));      /* Raw data */
95         d[1] = ((1 << 3) |      /* must be one */
96                 (1 << 2) |      /* Unsigned data */
97                 (0 << 1) |      /* Arm disabled */
98                 (1 << 0));      /* Odd parity */
99         ao_mma655x_cmd(d);
100         v = (uint16_t) d[1] << 2;
101         v |= d[0] >> 6;
102         v |= (uint16_t) (d[0] & 3) << 10;
103         return v;
104 }
105
106 static void
107 ao_mma655x_reset(void) {
108         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
109                              (0 << AO_MMA655X_DEVCTL_RES_1) |
110                              (0 << AO_MMA655X_DEVCTL_RES_1));
111         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
112                              (1 << AO_MMA655X_DEVCTL_RES_1) |
113                              (1 << AO_MMA655X_DEVCTL_RES_1));
114         ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
115                              (0 << AO_MMA655X_DEVCTL_RES_1) |
116                              (1 << AO_MMA655X_DEVCTL_RES_1));
117 }
118
119 #define DEVCFG_VALUE    (\
120         (1 << AO_MMA655X_DEVCFG_OC) |           /* Disable offset cancelation */ \
121         (1 << AO_MMA655X_DEVCFG_SD) |           /* Receive unsigned data */ \
122         (0 << AO_MMA655X_DEVCFG_OFMON) |        /* Disable offset monitor */ \
123         (AO_MMA655X_DEVCFG_A_CFG_DISABLE << AO_MMA655X_DEVCFG_A_CFG))
124
125 #define AXISCFG_VALUE   (\
126                 (0 << AO_MMA655X_AXISCFG_LPF))  /* 100Hz 4-pole filter */
127
128
129 static void
130 ao_mma655x_setup(void)
131 {
132         uint8_t         v;
133         uint16_t        a, a_st;
134         uint8_t         stdefl;
135
136         if (mma655x_configured)
137                 return;
138         mma655x_configured = 1;
139         ao_delay(AO_MS_TO_TICKS(10));   /* Top */
140         ao_mma655x_reset();
141         ao_delay(AO_MS_TO_TICKS(10));   /* Top */
142         (void) ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
143         v = ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
144
145         /* Configure R/W register values.
146          * Most of them relate to the arming feature, which
147          * we don't use, so the only registers we need to
148          * write are DEVCFG and AXISCFG
149          */
150
151         ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
152                              DEVCFG_VALUE | (0 << AO_MMA655X_DEVCFG_ENDINIT));
153
154         /* Test X axis
155          */
156         
157         ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
158                              AXISCFG_VALUE |
159                              (1 << AO_MMA655X_AXISCFG_ST));
160         a_st = ao_mma655x_value();
161
162         stdefl = ao_mma655x_reg_read(AO_MMA655X_STDEFL);
163
164         ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
165                              AXISCFG_VALUE |
166                              (0 << AO_MMA655X_AXISCFG_ST));
167         a = ao_mma655x_value();
168         printf ("normal: %u self_test: %u stdefl: %u\n",
169                 a, a_st, stdefl);
170
171         ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
172                              DEVCFG_VALUE | (1 << AO_MMA655X_DEVCFG_ENDINIT));
173 }
174
175 static void
176 ao_mma655x_dump(void)
177 {
178         uint8_t s0, s1, s2, s3;
179         uint32_t        lot;
180         uint16_t        serial;
181
182         ao_mma655x_setup();
183
184         s0 = ao_mma655x_reg_read(AO_MMA655X_SN0);
185         s1 = ao_mma655x_reg_read(AO_MMA655X_SN1);
186         s2 = ao_mma655x_reg_read(AO_MMA655X_SN2);
187         s3 = ao_mma655x_reg_read(AO_MMA655X_SN3);
188         lot = ((uint32_t) s3 << 24) | ((uint32_t) s2 << 16) |
189                 ((uint32_t) s1 << 8) | ((uint32_t) s0);
190         serial = lot & 0x1fff;
191         lot >>= 12;
192         printf ("MMA655X lot %d serial %d\n", lot, serial);
193         mma655x_configured = 0;
194 }
195
196 __code struct ao_cmds ao_mma655x_cmds[] = {
197         { ao_mma655x_dump,      "A\0Display MMA655X data" },
198         { 0, NULL },
199 };
200
201 static void
202 ao_mma655x(void)
203 {
204         ao_mma655x_setup();
205         for (;;) {
206                 ao_data_ring[ao_data_head].mma655x = ao_mma655x_value();
207                 ao_arch_critical(
208                         AO_DATA_PRESENT(AO_DATA_MMA655X);
209                         AO_DATA_WAIT();
210                         );
211         }
212 }
213
214 static __xdata struct ao_task ao_mma655x_task;
215
216 void
217 ao_mma655x_init(void)
218 {
219         mma655x_configured = 0;
220
221         ao_cmd_register(&ao_mma655x_cmds[0]);
222         ao_spi_init_cs(AO_MMA655X_CS_GPIO, (1 << AO_MMA655X_CS));
223
224         ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x");
225 }
226
227 #endif