2 * Copyright © 2012 Keith Packard <keithp@keithp.com>
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.
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.
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.
20 #include <ao_mma655x.h>
28 #define PRINTD(l, ...) do { if (DEBUG & (l)) { printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } } while(0)
33 #define AO_MMA655X_SPI_SPEED ao_spi_speed(8333333) /* 120ns clock period */
36 ao_mma655x_start(void) {
37 ao_spi_get_bit(AO_MMA655X_CS_PORT,
40 AO_MMA655X_SPI_SPEED);
44 ao_mma655x_stop(void) {
45 ao_spi_put_bit(AO_MMA655X_CS_PORT,
47 AO_MMA655X_SPI_INDEX);
51 ao_mma655x_restart(void) {
53 ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, 1);
55 /* Emperical testing on STM32L151 at 32MHz for this delay amount */
56 for (i = 0; i < 10; i++)
58 ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, 0);
65 /* down to four bits */
66 p = (v ^ (v >> 4)) & 0xf;
68 /* Cute lookup hack -- 0x6996 encodes the sixteen
69 * even parity values in order.
71 p = (~0x6996 >> p) & 1;
77 ao_mma655x_cmd(uint8_t d[2])
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]);
88 ao_mma655x_reg_read(uint8_t addr)
92 d[0] = addr | (ao_parity(addr) << 7);
94 ao_spi_send(&d, 2, AO_MMA655X_SPI_INDEX);
97 /* Send a dummy read of 00 to clock out the SPI data */
100 ao_spi_duplex(&d, &d, 2, AO_MMA655X_SPI_INDEX);
102 PRINTD(DEBUG_LOW, "read %x = %x %x\n", addr, d[0], d[1]);
107 ao_mma655x_reg_write(uint8_t addr, uint8_t value)
111 PRINTD(DEBUG_LOW, "write %x %x\n", addr, value);
112 addr |= (1 << 6); /* write mode */
113 d[0] = addr | (ao_parity(addr^value) << 7);
116 ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
123 ao_mma655x_value(void)
128 d[0] = ((0 << 6) | /* Axis selection (X) */
129 (1 << 5) | /* Acceleration operation */
130 (1 << 4)); /* Raw data */
131 d[1] = ((1 << 3) | /* must be one */
132 (1 << 2) | /* Unsigned data */
133 (0 << 1) | /* Arm disabled */
134 (1 << 0)); /* Odd parity */
136 PRINTD(DEBUG_LOW, "value SEND %02x %02x\n", d[0], d[1]);
137 ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
138 ao_mma655x_restart();
141 ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
143 PRINTD(DEBUG_LOW, "value RECV %02x %02x\n", d[0], d[1]);
145 v = (uint16_t) d[1] << 2;
147 v |= (uint16_t) (d[0] & 3) << 10;
152 ao_mma655x_reset(void) {
153 PRINTD(DEBUG_HIGH, "reset\n");
154 ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
155 (0 << AO_MMA655X_DEVCTL_RES_1) |
156 (0 << AO_MMA655X_DEVCTL_RES_0));
157 ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
158 (1 << AO_MMA655X_DEVCTL_RES_1) |
159 (1 << AO_MMA655X_DEVCTL_RES_0));
160 ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
161 (0 << AO_MMA655X_DEVCTL_RES_1) |
162 (1 << AO_MMA655X_DEVCTL_RES_0));
165 #define DEVCFG_VALUE (\
166 (1 << AO_MMA655X_DEVCFG_OC) | /* Disable offset cancelation */ \
167 (1 << AO_MMA655X_DEVCFG_SD) | /* Receive unsigned data */ \
168 (0 << AO_MMA655X_DEVCFG_OFMON) | /* Disable offset monitor */ \
169 (AO_MMA655X_DEVCFG_A_CFG_DISABLE << AO_MMA655X_DEVCFG_A_CFG))
171 #define AXISCFG_VALUE (\
172 (0 << AO_MMA655X_AXISCFG_LPF)) /* 100Hz 4-pole filter */
175 #define AO_ST_TRIES 10
176 #define AO_ST_DELAY AO_MS_TO_TICKS(100)
179 ao_mma655x_setup(void)
186 uint8_t s0, s1, s2, s3;
190 for (tries = 0; tries < AO_ST_TRIES; tries++) {
191 ao_delay(AO_MS_TO_TICKS(10));
193 ao_delay(AO_MS_TO_TICKS(10));
195 devstat = ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
196 PRINTD(DEBUG_HIGH, "devstat %x\n", devstat);
198 if (!(devstat & (1 << AO_MMA655X_DEVSTAT_DEVRES)))
201 /* Configure R/W register values.
202 * Most of them relate to the arming feature, which
203 * we don't use, so the only registers we need to
204 * write are DEVCFG and AXISCFG
207 ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
208 DEVCFG_VALUE | (0 << AO_MMA655X_DEVCFG_ENDINIT));
213 ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
215 (1 << AO_MMA655X_AXISCFG_ST));
216 ao_delay(AO_MS_TO_TICKS(10));
218 a_st = ao_mma655x_value();
220 ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
222 (0 << AO_MMA655X_AXISCFG_ST));
224 ao_delay(AO_MS_TO_TICKS(10));
226 a = ao_mma655x_value();
228 st_change = a_st - a;
230 PRINTD(DEBUG_HIGH, "self test %d normal %d change %d\n", a_st, a, st_change);
232 if (AO_ST_MIN <= st_change && st_change <= AO_ST_MAX)
234 ao_delay(AO_ST_DELAY);
236 if (tries == AO_ST_TRIES)
237 AO_SENSOR_ERROR(AO_DATA_MMA655X);
239 ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
240 DEVCFG_VALUE | (1 << AO_MMA655X_DEVCFG_ENDINIT));
242 s0 = ao_mma655x_reg_read(AO_MMA655X_SN0);
243 s1 = ao_mma655x_reg_read(AO_MMA655X_SN1);
244 s2 = ao_mma655x_reg_read(AO_MMA655X_SN2);
245 s3 = ao_mma655x_reg_read(AO_MMA655X_SN3);
246 lot = ((uint32_t) s3 << 24) | ((uint32_t) s2 << 16) |
247 ((uint32_t) s1 << 8) | ((uint32_t) s0);
248 serial = lot & 0x1fff;
250 pn = ao_mma655x_reg_read(AO_MMA655X_PN);
254 uint16_t ao_mma655x_current;
257 ao_mma655x_dump(void)
259 printf ("MMA655X value %d\n", ao_mma655x_current);
262 const struct ao_cmds ao_mma655x_cmds[] = {
263 { ao_mma655x_dump, "A\0Display MMA655X data" },
272 ao_mma655x_current = ao_mma655x_value();
274 AO_DATA_PRESENT(AO_DATA_MMA655X);
280 static struct ao_task ao_mma655x_task;
283 ao_mma655x_init(void)
285 ao_cmd_register(&ao_mma655x_cmds[0]);
286 ao_spi_init_cs(AO_MMA655X_CS_PORT, (1 << AO_MMA655X_CS_PIN));
288 ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x");