altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / drivers / ao_hmc5883.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_hmc5883.h>
21 #include <ao_exti.h>
22
23 #if HAS_HMC5883
24
25 static uint8_t  ao_hmc5883_configured;
26
27 static uint8_t  ao_hmc5883_addr;
28
29 static void
30 ao_hmc5883_reg_write(uint8_t addr, uint8_t data)
31 {
32         uint8_t d[2];
33
34         d[0] = addr;
35         d[1] = data;
36         ao_i2c_get(AO_HMC5883_I2C_INDEX);
37         ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_WRITE);
38         ao_i2c_send(d, 2, AO_HMC5883_I2C_INDEX, true);
39         ao_i2c_put(AO_HMC5883_I2C_INDEX);
40         ao_hmc5883_addr = addr + 1;
41 }
42
43 static void
44 ao_hmc5883_read(uint8_t addr, uint8_t *data, uint8_t len)
45 {
46         ao_i2c_get(AO_HMC5883_I2C_INDEX);
47         if (addr != ao_hmc5883_addr) {
48                 ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_WRITE);
49                 ao_i2c_send(&addr, 1, AO_HMC5883_I2C_INDEX, false);
50         }
51         ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_READ);
52         ao_i2c_recv(data, len, AO_HMC5883_I2C_INDEX, true);
53         ao_i2c_put(AO_HMC5883_I2C_INDEX);
54         ao_hmc5883_addr = 0xff;
55 }
56
57 static uint8_t ao_hmc5883_done;
58
59 static void
60 ao_hmc5883_isr(void)
61 {
62         ao_exti_disable(AO_HMC5883_INT_PORT, AO_HMC5883_INT_PIN);
63         ao_hmc5883_done = 1;
64         ao_wakeup(&ao_hmc5883_done);
65 }
66
67 static uint32_t ao_hmc5883_missed_irq;
68
69 static void
70 ao_hmc5883_sample(struct ao_hmc5883_sample *sample)
71 {
72         uint16_t        *d = (uint16_t *) sample;
73         int             i = sizeof (*sample) / 2;
74
75         ao_hmc5883_done = 0;
76         ao_exti_enable(AO_HMC5883_INT_PORT, AO_HMC5883_INT_PIN);
77         ao_hmc5883_reg_write(HMC5883_MODE, HMC5883_MODE_SINGLE);
78
79         ao_arch_block_interrupts();
80         while (!ao_hmc5883_done)
81                 if (ao_sleep_for(&ao_hmc5883_done, AO_MS_TO_TICKS(10)))
82                         ++ao_hmc5883_missed_irq;
83         ao_arch_release_interrupts();
84
85         ao_hmc5883_read(HMC5883_X_MSB, (uint8_t *) sample, sizeof (struct ao_hmc5883_sample));
86 #if __BYTE_ORDER == __LITTLE_ENDIAN
87         /* byte swap */
88         while (i--) {
89                 uint16_t        t = *d;
90                 *d++ = (uint16_t) ((t >> 8) | (t << 8));
91         }
92 #endif
93 }
94
95 static uint8_t
96 ao_hmc5883_setup(void)
97 {
98         uint8_t d;
99         uint8_t present;
100
101         if (ao_hmc5883_configured)
102                 return 1;
103
104         ao_i2c_get(AO_HMC5883_I2C_INDEX);
105         present = ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_READ);
106         ao_i2c_recv(&d, 1, AO_HMC5883_I2C_INDEX, true);
107         ao_i2c_put(AO_HMC5883_I2C_INDEX);
108
109         if (!present)
110                 ao_panic(AO_PANIC_SELF_TEST_HMC5883);
111
112         ao_hmc5883_reg_write(HMC5883_CONFIG_A,
113                              (HMC5883_CONFIG_A_MA_8 << HMC5883_CONFIG_A_MA) |
114                              (HMC5883_CONFIG_A_DO_15 << HMC5883_CONFIG_A_DO) |
115                              (HMC5883_CONFIG_A_MS_NORMAL << HMC5883_CONFIG_A_MS));
116
117         ao_hmc5883_reg_write(HMC5883_CONFIG_B,
118                              (HMC5883_CONFIG_B_GN_1_3 << HMC5883_CONFIG_B_GN));
119
120         ao_hmc5883_configured = 1;
121         return 1;
122 }
123
124 struct ao_hmc5883_sample ao_hmc5883_current;
125
126 static void
127 ao_hmc5883(void)
128 {
129         struct ao_hmc5883_sample        sample;
130         ao_hmc5883_setup();
131         for (;;) {
132                 ao_hmc5883_sample(&sample);
133                 ao_arch_block_interrupts();
134                 ao_hmc5883_current = sample;
135                 AO_DATA_PRESENT(AO_DATA_HMC5883);
136                 AO_DATA_WAIT();
137                 ao_arch_release_interrupts();
138         }
139 }
140
141 static struct ao_task ao_hmc5883_task;
142
143 static void
144 ao_hmc5883_show(void)
145 {
146         printf ("X: %d Z: %d Y: %d missed irq: %lu\n",
147                 ao_hmc5883_current.x, ao_hmc5883_current.z, ao_hmc5883_current.y, ao_hmc5883_missed_irq);
148 }
149
150 static const struct ao_cmds ao_hmc5883_cmds[] = {
151         { ao_hmc5883_show,      "M\0Show HMC5883 status" },
152         { 0, NULL }
153 };
154
155 void
156 ao_hmc5883_init(void)
157 {
158         ao_hmc5883_configured = 0;
159
160         ao_enable_port(AO_HMC5883_INT_PORT);
161         ao_exti_setup(AO_HMC5883_INT_PORT,
162                       AO_HMC5883_INT_PIN,
163                       AO_EXTI_MODE_FALLING | AO_EXTI_MODE_PULL_UP,
164                       ao_hmc5883_isr);
165
166         ao_add_task(&ao_hmc5883_task, ao_hmc5883, "hmc5883");
167         ao_cmd_register(&ao_hmc5883_cmds[0]);
168 }
169
170 #endif