blob: ad4965835d7f09a210c4c8e9c5e08e6574eb7c66 [file] [log] [blame]
Brian Silverman49876942013-10-11 17:50:26 -07001#include "gyro.h"
2
3#include <stdio.h>
4#include <inttypes.h>
5
6#include "FreeRTOS.h"
7#include "task.h"
8#include "partest.h"
9
10struct GyroOutput gyro_output;
11
12static void gyro_disable_csel(void) {
13 // Set the CSEL pin high to deselect it.
14 GPIO0->FIOSET = 1 << 16;
15}
16
17static void gyro_enable_csel(void) {
18 // Clear the CSEL pin to select it.
19 GPIO0->FIOCLR = 1 << 16;
20}
21
22// Blocks until there is data available.
23static uint16_t spi_read(void) {
24 while (!(SSP0->SR & (1 << 2))) {}
25 return SSP0->DR;
26}
27
28// Blocks until there is space to enqueue data.
29static void spi_write(uint16_t data) {
30 while (!(SSP0->SR & (1 << 1))) {}
31 SSP0->DR = data;
32}
33
34static uint32_t do_gyro_read(uint32_t data, int *parity_error) {
35 *parity_error = 0;
36
37 gyro_enable_csel();
38 spi_write(data >> 16);
39 if (__builtin_parity(data & ~1) == 0) data |= 1;
40 spi_write(data);
41
42 uint16_t high_value = spi_read();
43 if (__builtin_parity(high_value) != 1) {
44 printf("high value 0x%"PRIx16" parity error\n", high_value);
45 *parity_error = 1;
46 }
47 uint16_t low_value = spi_read();
48 gyro_disable_csel();
Brian Silvermand36b7d32013-10-24 15:56:47 -070049 uint32_t r = high_value << 16 | low_value;
50 if (__builtin_parity(r) != 1) {
51 printf("low value 0x%"PRIx16" parity error (r=%"PRIx32")\n", low_value, r);
Brian Silverman49876942013-10-11 17:50:26 -070052 *parity_error = 1;
53 }
54
Brian Silvermand36b7d32013-10-24 15:56:47 -070055 return r;
Brian Silverman49876942013-10-11 17:50:26 -070056}
57
58// Returns all of the non-data bits in the "header" except the parity from
59// value.
60static uint8_t gyro_status(uint32_t value) {
61 return (value >> 26) & ~4;
62}
63
64// Returns all of the error bits in the "footer" from value.
65static uint8_t gyro_errors(uint32_t value) {
66 return (value >> 1) & 0x7F;
67}
68
69// Performs a read from the gyro.
70// Sets *bad_reading to 1 if the result is potentially bad and *bad_gyro to 1 if
71// the gyro is bad and we're not going to get any more readings.
72static int16_t gyro_read(int *bad_reading, int *bad_gyro) {
73 *bad_reading = *bad_gyro = 0;
74
75 int parity_error;
76 uint32_t value = do_gyro_read(0x20000000, &parity_error);
77
78 if (parity_error) {
79 *bad_reading = 1;
80 return 0;
81 }
82
83 // This check assumes that the sequence bits are all 0, but they should be
84 // because that's all we send.
85 if (gyro_status(value) != 1) {
86 uint8_t status = gyro_status(value);
87 if (status == 0) {
88 printf("gyro says sensor data is bad\n");
89 } else {
90 printf("gyro gave weird status 0x%"PRIx8"\n", status);
91 }
92 *bad_reading = 1;
93 }
94
95 if (gyro_errors(value) != 0) {
96 uint8_t errors = gyro_errors(value);
97 if (errors & ~(1 << 1)) {
98 *bad_reading = 1;
99 // Error 1 (continuous self-test error) will set status to 0 if it's bad
100 // enough by itself.
101 }
102 if (errors & (1 << 6)) {
103 printf("gyro PLL error\n");
104 }
105 if (errors & (1 << 5)) {
106 printf("gyro quadrature error\n");
107 }
108 if (errors & (1 << 4)) {
109 printf("gyro non-volatile memory error\n");
110 *bad_gyro = 1;
111 }
112 if (errors & (1 << 3)) {
113 printf("gyro volatile memory error\n");
114 *bad_gyro = 1;
115 }
116 if (errors & (1 << 2)) {
117 printf("gyro power error\n");
118 }
119 if (errors & (1 << 1)) {
120 printf("gyro continuous self-test error\n");
121 }
122 if (errors & 1) {
123 printf("gyro unexpected self check mode\n");
124 }
125 }
126 if (*bad_gyro) {
127 *bad_reading = 1;
128 return 0;
129 } else {
130 return -(int16_t)(value >> 10 & 0xFFFF);
131 }
132}
133
134// Returns 1 if the setup failed or 0 if it succeeded.
135static int gyro_setup(void) {
136 for (int i = 0; i < 100; ++i) {
137 portTickType wait_time = xTaskGetTickCount();
138 int parity_error;
139
140 // Wait for it to start up.
141 vTaskDelayUntil(&wait_time, 100 / portTICK_RATE_MS);
142 // Get it started doing a check.
143 uint32_t value = do_gyro_read(0x20000003, &parity_error);
144 if (parity_error) continue;
145 // Its initial response is hardcoded to 1.
146 if (value != 1) {
147 printf("gyro unexpected initial response 0x%"PRIx32"\n", value);
148 // There's a chance that we're retrying because of a parity error
149 // previously, so keep going.
150 }
151
152 // Wait for it to assert the fault conditions.
153 vTaskDelayUntil(&wait_time, 50 / portTICK_RATE_MS);
154 // Dummy read to clear the old latched state.
155 do_gyro_read(0x20000000, &parity_error);
156 if (parity_error) continue;
157
158 // Wait for it to clear the fault conditions.
159 vTaskDelayUntil(&wait_time, 50 / portTICK_RATE_MS);
160 value = do_gyro_read(0x20000000, &parity_error);
161 if (parity_error) continue;
162 // If it's not reporting self test data.
163 if (gyro_status(value) != 2) {
164 printf("gyro first value 0x%"PRIx32" not self test data\n", value);
165 continue;
166 }
167 // If we don't see all of the errors.
168 if (gyro_errors(value) != 0x7F) {
169 printf("gyro self test value 0x%"PRIx32" is bad\n", value);
170 return 1;
171 }
172
173 // Wait for the sequential transfer delay.
174 vTaskDelayUntil(&wait_time, 1 / portTICK_RATE_MS);
175 value = do_gyro_read(0x20000000, &parity_error);
176 if (parity_error) continue;
177 // It should still be reporting self test data.
178 if (gyro_status(value) != 2) {
179 printf("gyro second value 0x%"PRIx32" not self test data\n", value);
180 continue;
181 }
182 return 0;
183 }
184 return 1;
185}
186
187static portTASK_FUNCTION(gyro_read_task, pvParameters) {
Brian Silvermand36b7d32013-10-24 15:56:47 -0700188 SC->PCONP |= PCONP_PCSSP0;
Brian Silverman74acd622013-10-26 14:47:14 -0700189
190 // Make sure that the clock is set up right.
Brian Silvermand36b7d32013-10-24 15:56:47 -0700191 SC->PCLKSEL1 &= ~(3 << 10);
192 SC->PCLKSEL1 |= 1 << 10;
193
194 // Set up SSEL.
195 // It's is just a GPIO pin because we're the master (it would be special if we
196 // were a slave).
197 gyro_disable_csel();
198 GPIO0->FIODIR |= 1 << 16;
199 PINCON->PINSEL1 &= ~(3 << 0);
200 PINCON->PINSEL1 |= 0 << 0;
201
202 // Set up MISO0 and MOSI0.
203 PINCON->PINSEL1 &= ~(3 << 2 | 3 << 4);
204 PINCON->PINSEL1 |= 2 << 2 | 2 << 4;
205
206 // Set up SCK0.
207 PINCON->PINSEL0 &= ~(3 << 30);
208 PINCON->PINSEL0 |= (2 << 30);
209
210 // Make sure it's disabled.
211 SSP0->CR1 = 0;
212 SSP0->CR0 =
213 0xF /* 16 bit transfer */ |
214 0 << 4 /* SPI mode */ |
215 0 << 6 /* CPOL = 0 */ |
216 0 << 7 /* CPHA = 0 */;
217 // 14 clocks per cycle. This works out to a ~7.2MHz bus.
218 // The gyro is rated for a maximum of 8.08MHz.
219 SSP0->CPSR = 14;
220 // Finally, enable it.
221 // This has to be done after we're done messing with everything else.
222 SSP0->CR1 |= 1 << 1;
223
224 if (gyro_setup()) {
225 printf("gyro setup failed. deleting task\n");
226 gyro_output.angle = 0;
227 gyro_output.last_reading_bad = gyro_output.gyro_bad = 1;
228 gyro_output.initialized = 1;
229 vTaskDelete(NULL);
230 return;
231 } else {
232 gyro_output.initialized = 1;
233 }
234
235 gyro_output.angle = 0;
236 gyro_output.last_reading_bad = 1; // until we're started up
237 gyro_output.gyro_bad = 0;
238
Brian Silverman49876942013-10-11 17:50:26 -0700239 // How many times per second to read the gyro value.
240 static const int kGyroReadFrequency = 200;
241 // How many times per second to flash the LED.
242 // Must evenly divide kGyroReadFrequency.
243 static const int kFlashFrequency = 10;
244
245 static const int kStartupCycles = kGyroReadFrequency * 2;
246 static const int kZeroingCycles = kGyroReadFrequency * 6;
247
248 // An accumulator for all of the values read while zeroing.
249 int32_t zero_bias = 0;
250
251 int startup_cycles_left = kStartupCycles;
252 int zeroing_cycles_left = kZeroingCycles;
253
254 // These are a pair that hold the offset calculated while zeroing.
255 // full_units_ is the base (in ticks) and remainder_ ranges between 0 and
256 // kZeroingCycles (like struct timespec). remainder_ is used to calculate which
257 // cycles to add an additional unit to the result.
258 int32_t full_units_offset = 0;
259 int32_t remainder_offset = 0;
260 // This keeps track of when to add 1 to the read value (using _offset).
261 int32_t remainder_sum = 0;
262
263 int32_t led_flash = 0;
264 vParTestSetLED(0, 0);
265
266 portTickType xLastGyroReadTime = xTaskGetTickCount();
267
268 for (;;) {
269 ++led_flash;
270 if (led_flash < kGyroReadFrequency / kFlashFrequency / 2) {
271 vParTestSetLED(1, 0);
272 } else {
273 vParTestSetLED(1, 1);
274 }
275 if (led_flash >= kGyroReadFrequency / kFlashFrequency) {
276 led_flash = 0;
277 }
278
279 vTaskDelayUntil(&xLastGyroReadTime,
280 1000 / kGyroReadFrequency / portTICK_RATE_MS);
281
282 int bad_reading, bad_gyro;
283 int16_t gyro_value = gyro_read(&bad_reading, &bad_gyro);
284 if (bad_gyro) {
285 // We're just going to give up if this happens (write out that we're
286 // giving up and then never run anything else in this task).
287 vParTestSetLED(0, 1);
288 printf("gyro read task giving up because of bad gyro\n");
289 portENTER_CRITICAL();
290 gyro_output.gyro_bad = 1;
291 gyro_output.last_reading_bad = 1;
292 gyro_output.angle = 0;
293 portEXIT_CRITICAL();
294 vTaskDelete(NULL);
295 while (1) {}
296 }
297
298 if (startup_cycles_left) {
299 vParTestSetLED(2, 0);
300 --startup_cycles_left;
301 if (bad_reading) {
302 printf("gyro retrying startup wait because of bad reading\n");
303 startup_cycles_left = kStartupCycles;
304 }
305 } else if (zeroing_cycles_left) {
306 vParTestSetLED(2, 1);
307 --zeroing_cycles_left;
308 if (bad_reading) {
309 printf("gyro restarting zeroing because of bad reading\n");
310 zeroing_cycles_left = kZeroingCycles;
311 zero_bias = 0;
312 } else {
313 zero_bias -= gyro_value;
314 if (zeroing_cycles_left == 0) {
315 // Do all the nice math
316 full_units_offset = zero_bias / kZeroingCycles;
317 remainder_offset = zero_bias % kZeroingCycles;
318 if (remainder_offset < 0) {
319 remainder_offset += kZeroingCycles;
320 --full_units_offset;
321 }
322 }
323 }
324 } else {
325 vParTestSetLED(2, 0);
326
327 int64_t new_angle = gyro_output.angle;
328 if (!bad_reading) new_angle += gyro_value + full_units_offset;
329 if (remainder_sum >= kZeroingCycles) {
330 remainder_sum -= kZeroingCycles;
331 new_angle += 1;
332 }
333 portENTER_CRITICAL();
334 gyro_output.angle = new_angle;
335 gyro_output.last_reading_bad = bad_reading;
336 portEXIT_CRITICAL();
337 remainder_sum += remainder_offset;
338 }
339 }
340}
341
342void gyro_init(void) {
Brian Silvermand36b7d32013-10-24 15:56:47 -0700343 gyro_output.initialized = 0;
Brian Silverman49876942013-10-11 17:50:26 -0700344
345 xTaskCreate(gyro_read_task, (signed char *) "gyro",
346 configMINIMAL_STACK_SIZE + 100, NULL,
347 tskIDLE_PRIORITY + 2, NULL);
348}