root / branches / init_refactor / code / projects / libdragonfly / encoders.c @ 1543
History | View | Annotate | Download (11.3 KB)
1 | 1462 | dsschult | /**
|
---|---|---|---|
2 | * Copyright (c) 2007 Colony Project
|
||
3 | *
|
||
4 | * Permission is hereby granted, free of charge, to any person
|
||
5 | * obtaining a copy of this software and associated documentation
|
||
6 | * files (the "Software"), to deal in the Software without
|
||
7 | * restriction, including without limitation the rights to use,
|
||
8 | * copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
9 | * copies of the Software, and to permit persons to whom the
|
||
10 | * Software is furnished to do so, subject to the following
|
||
11 | * conditions:
|
||
12 | *
|
||
13 | * The above copyright notice and this permission notice shall be
|
||
14 | * included in all copies or substantial portions of the Software.
|
||
15 | *
|
||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||
18 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||
20 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
21 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
23 | * OTHER DEALINGS IN THE SOFTWARE.
|
||
24 | **/
|
||
25 | |||
26 | |||
27 | /**
|
||
28 | * @file encoders.c
|
||
29 | * @brief Encoders
|
||
30 | *
|
||
31 | * Implementation of functions for encoders.
|
||
32 | *
|
||
33 | * @author Colony Project, CMU Robotics Club
|
||
34 | **/
|
||
35 | |||
36 | #include <avr/io.h> |
||
37 | |||
38 | #include "dragonfly_defs.h" |
||
39 | 868 | justin | #include "spi.h" |
40 | #include "ring_buffer.h" |
||
41 | 1462 | dsschult | #include "encoders.h" |
42 | 868 | justin | |
43 | 1461 | bneuman | unsigned char encoders_initd=0; |
44 | |||
45 | 868 | justin | unsigned int left_data_buf; |
46 | unsigned int right_data_buf; |
||
47 | char encoder_buf_index;
|
||
48 | |||
49 | unsigned int left_data; |
||
50 | unsigned int right_data; |
||
51 | |||
52 | unsigned int left_data_array[BUFFER_SIZE]; |
||
53 | unsigned int right_data_array[BUFFER_SIZE]; |
||
54 | int left_data_idx;
|
||
55 | int right_data_idx;
|
||
56 | |||
57 | int left_dx;
|
||
58 | int right_dx;
|
||
59 | long int timecount; |
||
60 | |||
61 | volatile short int data_ready; |
||
62 | |||
63 | 1461 | bneuman | int encoder_recv(char data); |
64 | 868 | justin | |
65 | //Helper Function Prototypes
|
||
66 | inline void left_data_array_put(unsigned short int value); |
||
67 | inline unsigned int left_data_array_top(void); |
||
68 | inline unsigned int left_data_array_prev(void); |
||
69 | inline unsigned int left_data_array_bottom(void); |
||
70 | |||
71 | inline void right_data_array_put(unsigned short int value); |
||
72 | inline unsigned int right_data_array_top(void); |
||
73 | inline unsigned int right_data_array_prev(void); |
||
74 | inline unsigned int right_data_array_bottom(void); |
||
75 | |||
76 | 1418 | chihsiuh | //encoder_get_v helper function prototypes
|
77 | int left_data_at(int index); |
||
78 | int right_data_at(int index); |
||
79 | int get_dx(char encoder, int index); |
||
80 | 868 | justin | |
81 | 890 | justin | void encoder_recv_complete(void){ |
82 | 868 | justin | encoder_buf_index = 0;
|
83 | data_ready++; |
||
84 | |||
85 | spi_transfer(5);
|
||
86 | } |
||
87 | |||
88 | /**
|
||
89 | * @brief Initializes encoder variables and the hardware interface.
|
||
90 | */
|
||
91 | 1461 | bneuman | int encoders_init(void){ |
92 | int i;
|
||
93 | 868 | justin | |
94 | 1543 | bneuman | if(encoders_initd) {
|
95 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders already init'd\r\n");
|
||
96 | 1461 | bneuman | return ERROR_INIT_ALREADY_INITD;
|
97 | 1543 | bneuman | } |
98 | 868 | justin | |
99 | 1461 | bneuman | data_ready=0;
|
100 | |||
101 | 1543 | bneuman | if(spi_init(encoder_recv, encoder_recv_complete)) {
|
102 | DRAGONFLY_DEBUG_PRINT("ERROR: SPI was already init'd, bailing out\r\n");
|
||
103 | 1461 | bneuman | return -1; //if spi was inited for something else, bail out |
104 | 1543 | bneuman | } |
105 | 1461 | bneuman | encoder_buf_index = 0;
|
106 | left_data_buf = 0;
|
||
107 | right_data_buf= 0;
|
||
108 | left_data = -1;
|
||
109 | right_data = -1;
|
||
110 | |||
111 | //RING_BUFFER_INIT(enc_buffer,BUFFER_SIZE);
|
||
112 | left_data_idx = 0;
|
||
113 | right_data_idx = 0;
|
||
114 | |||
115 | for(i = 0; i < BUFFER_SIZE; i++) { |
||
116 | left_data_array[i] = 0;
|
||
117 | } |
||
118 | for(i = 0; i < BUFFER_SIZE; i++) { |
||
119 | right_data_array[i] = 0;
|
||
120 | } |
||
121 | |||
122 | spi_transfer(5);
|
||
123 | |||
124 | encoders_initd=1;
|
||
125 | return 0; |
||
126 | 868 | justin | } |
127 | |||
128 | /**
|
||
129 | * @brief Returns the specified encoders value
|
||
130 | *
|
||
131 | * @param encoder this is the encoder that you want to read. Valid arguments
|
||
132 | * are LEFT and RIGHT
|
||
133 | *
|
||
134 | 1418 | chihsiuh | * @return the value of the specified encoder.
|
135 | * -1 usually means low battery.
|
||
136 | 1461 | bneuman | * -2 means the library was not properly initialized
|
137 | 1418 | chihsiuh | * values above ENCODER_MAX usually means phyiscal problems with
|
138 | * the encoder.
|
||
139 | 868 | justin | **/
|
140 | int encoder_read(char encoder){ |
||
141 | 1543 | bneuman | if(!encoders_initd) {
|
142 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders not init'd\r\n");
|
||
143 | 1461 | bneuman | return -2; |
144 | 1543 | bneuman | } |
145 | 1461 | bneuman | |
146 | if(encoder==LEFT) {
|
||
147 | return left_data;
|
||
148 | } |
||
149 | else if(encoder==RIGHT){ |
||
150 | return right_data;
|
||
151 | } |
||
152 | else{
|
||
153 | return -1; |
||
154 | } |
||
155 | 868 | justin | } |
156 | |||
157 | /**
|
||
158 | 1418 | chihsiuh | * @brief Get total distance travelled by the specified encoder (in encoder ticks)
|
159 | *
|
||
160 | * @param encoder the encoder that you want to read, either LEFT or RIGHT
|
||
161 | *
|
||
162 | * @return The distance covered by the specified encoder.
|
||
163 | *
|
||
164 | * @note Simply calls encoder_get_dx.
|
||
165 | **/
|
||
166 | int encoder_get_x(char encoder) { |
||
167 | return encoder_get_dx(encoder);
|
||
168 | } |
||
169 | |||
170 | /**
|
||
171 | 868 | justin | * Gets the total distance covered by the specified encoder (in encoder clicks)
|
172 | *
|
||
173 | * @param encoder the encoder that you want to read, use LEFT or RIGHT
|
||
174 | *
|
||
175 | 1461 | bneuman | * @return The distance covered by the specified encoder, -2 if the library is not initialized
|
176 | 868 | justin | **/
|
177 | int encoder_get_dx(char encoder) { |
||
178 | 1461 | bneuman | if(!encoders_initd)
|
179 | return -2; |
||
180 | |||
181 | if(encoder==LEFT)
|
||
182 | return left_dx;
|
||
183 | else if(encoder==RIGHT) |
||
184 | return right_dx;
|
||
185 | else
|
||
186 | return -1; |
||
187 | 868 | justin | } |
188 | |||
189 | 1418 | chihsiuh | /**
|
190 | * @brief Returns the approximated instantaneous velocity of the robot
|
||
191 | * in terms of encoder clicks.
|
||
192 | *
|
||
193 | * @param encoder RIGHT or LEFT - the wheel you want the velocity for.
|
||
194 | *
|
||
195 | * @return The instantaneous velocity for the given wheel or twice the ERR_VEL
|
||
196 | * if an error occurs (1024 * 2 = 2048)
|
||
197 | *
|
||
198 | * @bug This uses hard coded values and results are inconsistent.
|
||
199 | * Use at your own risk.
|
||
200 | */
|
||
201 | int encoder_get_v(char encoder){ |
||
202 | int vel1, vel2;
|
||
203 | 1461 | bneuman | |
204 | 1418 | chihsiuh | vel1 = get_dx(encoder, 0);
|
205 | vel2 = get_dx(encoder, 1);
|
||
206 | |||
207 | if (vel1 == ERR_VEL && vel2 == ERR_VEL)
|
||
208 | return ERR_VEL << 1; |
||
209 | else if (vel2 == ERR_VEL) |
||
210 | return vel1 << 1; |
||
211 | else if (vel1 == ERR_VEL) |
||
212 | return vel2 << 1; |
||
213 | else
|
||
214 | return vel1 + vel2;
|
||
215 | } |
||
216 | |||
217 | 868 | justin | /**
|
218 | * Resets the distance accumulator for the specified
|
||
219 | * encoder.
|
||
220 | *
|
||
221 | * @param encoder the encoder that you want to reset distance for
|
||
222 | **/
|
||
223 | 1461 | bneuman | int encoder_rst_dx(char encoder) { |
224 | |||
225 | 1543 | bneuman | if(!encoders_initd) {
|
226 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders not init'd\r\n");
|
||
227 | 1461 | bneuman | return ERROR_LIBRARY_NOT_INITD;
|
228 | 1543 | bneuman | } |
229 | 1461 | bneuman | |
230 | if(encoder==LEFT)
|
||
231 | left_dx = 0;
|
||
232 | else if(encoder==RIGHT) |
||
233 | right_dx = 0;
|
||
234 | |||
235 | return 0; |
||
236 | 868 | justin | } |
237 | |||
238 | /**
|
||
239 | * @brief Returns the number of encoder reads that have occurred.
|
||
240 | *
|
||
241 | * @return The time count.
|
||
242 | */
|
||
243 | int encoder_get_tc(void) { |
||
244 | return timecount;
|
||
245 | } |
||
246 | |||
247 | /**
|
||
248 | * @brief Resets the encoder read counter.
|
||
249 | 1461 | bneuman | * @return 0 if init succesfull, an error code otherwise
|
250 | 868 | justin | */
|
251 | 1461 | bneuman | int encoder_rst_tc(void) { |
252 | 1543 | bneuman | if(!encoders_initd) {
|
253 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders not init'd\r\n");
|
||
254 | 1461 | bneuman | return ERROR_LIBRARY_NOT_INITD;
|
255 | 1543 | bneuman | } |
256 | 1461 | bneuman | |
257 | timecount = 0;
|
||
258 | |||
259 | return 0; |
||
260 | 868 | justin | } |
261 | |||
262 | /**
|
||
263 | * @brief Waits until n encoder reads have occurred.
|
||
264 | * Counter is reset on functions exit.
|
||
265 | *
|
||
266 | * @param n
|
||
267 | 1461 | bneuman | *
|
268 | * @return 0 if init succesfull, an error code otherwise
|
||
269 | 868 | justin | */
|
270 | 1461 | bneuman | int encoder_wait(int n){ |
271 | 1543 | bneuman | if(!encoders_initd) {
|
272 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders not init'd\r\n");
|
||
273 | 1461 | bneuman | return ERROR_LIBRARY_NOT_INITD;
|
274 | 1543 | bneuman | } |
275 | 1461 | bneuman | |
276 | while(data_ready<n);
|
||
277 | data_ready=0;
|
||
278 | |||
279 | return 0; |
||
280 | 868 | justin | } |
281 | |||
282 | 1461 | bneuman | /**
|
283 | * @brief This functions recieves data from the encoders directly
|
||
284 | *
|
||
285 | * @note Full reads occur every 40 microseconds. This function should be called every 8 microseconds.
|
||
286 | *
|
||
287 | * @return 0 if init succesfull, an error code otherwise
|
||
288 | */
|
||
289 | int encoder_recv(char data){ |
||
290 | short int dx; |
||
291 | 868 | justin | |
292 | 1543 | bneuman | if(!encoders_initd) {
|
293 | DRAGONFLY_DEBUG_PRINT("ERROR: encoders not init'd\r\n");
|
||
294 | 1461 | bneuman | return ERROR_LIBRARY_NOT_INITD;
|
295 | 1543 | bneuman | } |
296 | 1461 | bneuman | |
297 | //Parse the encoder data, comes in over 5 bytes 16 bits per encoder,
|
||
298 | // second is offset by 1 bit.
|
||
299 | switch(encoder_buf_index){
|
||
300 | case 0: |
||
301 | right_data_buf |= ((short)data)<<8 & 0xff00; |
||
302 | break;
|
||
303 | case 1: |
||
304 | right_data_buf |= ((short)data) & 0xff; |
||
305 | break;
|
||
306 | case 2: |
||
307 | left_data_buf |= (((short)data) << 9) & (0x7F << 9); |
||
308 | break;
|
||
309 | case 3: |
||
310 | left_data_buf |= (((short)data) << 1) & (0xFF<<1); |
||
311 | break;
|
||
312 | case 4: left_data_buf |= (((short)data)>>7) & 0x1; |
||
313 | } |
||
314 | 868 | justin | |
315 | 1461 | bneuman | encoder_buf_index = (encoder_buf_index + 1) % 5; |
316 | 868 | justin | |
317 | 1461 | bneuman | if(encoder_buf_index==0) { |
318 | 868 | justin | |
319 | 1461 | bneuman | /*Error handling for the left encoder*/
|
320 | if(!(left_data_buf & OCF))
|
||
321 | left_data = ENCODER_DATA_NOT_READY; |
||
322 | if(left_data_buf & (COF | LIN))
|
||
323 | left_data = ENCODER_MISALIGNED; |
||
324 | else if((left_data_buf & MagINCn) && (left_data_buf & MagDECn)) |
||
325 | left_data = ENCODER_MAGNET_FAILURE; |
||
326 | else left_data = (left_data_buf>>5) & 1023; |
||
327 | 868 | justin | |
328 | 1461 | bneuman | /*Error handling for the right encoder*/
|
329 | if(!(right_data_buf & OCF))
|
||
330 | right_data = ENCODER_DATA_NOT_READY; |
||
331 | if(right_data_buf & (COF | LIN))
|
||
332 | right_data = ENCODER_MISALIGNED; |
||
333 | else if ((right_data_buf & MagINCn) && (right_data_buf & MagDECn)) |
||
334 | right_data = ENCODER_MAGNET_FAILURE; |
||
335 | else right_data = (right_data_buf>>5) & 1023; |
||
336 | 868 | justin | |
337 | 1461 | bneuman | left_data_buf = 0;
|
338 | right_data_buf = 0;
|
||
339 | 868 | justin | |
340 | 1461 | bneuman | /*Note: Above 1023 is invalid data*/
|
341 | if(!(left_data > 1023)) { |
||
342 | //Reverse the left wheel since encoders are necessarily mounted backwards.
|
||
343 | left_data = 1023 - left_data;
|
||
344 | left_data_array_put(left_data); |
||
345 | 868 | justin | |
346 | 1461 | bneuman | //Adjust left accumulator
|
347 | dx = left_data - left_data_array_prev(); |
||
348 | 868 | justin | |
349 | 1461 | bneuman | if(left_data_array_prev()==0) dx=0; |
350 | 868 | justin | |
351 | 1461 | bneuman | if(dx > 512) left_dx += dx - 1023; //Underflow |
352 | else if(dx < -512) left_dx += dx + 1023; //Overflow |
||
353 | else left_dx += dx;
|
||
354 | } |
||
355 | 868 | justin | |
356 | 1461 | bneuman | /*Above 1023 is invalid data*/
|
357 | if(!(right_data > 1023)) { |
||
358 | right_data_array_put(right_data); |
||
359 | 868 | justin | |
360 | 1461 | bneuman | //Adjust right accumulator
|
361 | dx = right_data - right_data_array_prev(); |
||
362 | 868 | justin | |
363 | 1461 | bneuman | if(right_data_array_prev()==0) dx=0; |
364 | 868 | justin | |
365 | 1461 | bneuman | if(dx > 512) right_dx += dx - 1023; //underflow |
366 | else if(dx < -512) right_dx += dx + 1023; //overflow |
||
367 | else right_dx += dx;
|
||
368 | } |
||
369 | } |
||
370 | 868 | justin | |
371 | 1461 | bneuman | //Increment timecount accumulator
|
372 | timecount++; |
||
373 | |||
374 | return 0; |
||
375 | 868 | justin | } |
376 | |||
377 | |||
378 | //Helper Functions
|
||
379 | inline void left_data_array_put(unsigned short int value) { |
||
380 | if(left_data_idx == BUFFER_SIZE-1) |
||
381 | left_data_idx = 0;
|
||
382 | else
|
||
383 | left_data_idx++; |
||
384 | left_data_array[left_data_idx] = value; |
||
385 | } |
||
386 | |||
387 | inline unsigned int left_data_array_top(void) { |
||
388 | return left_data_array[left_data_idx];
|
||
389 | } |
||
390 | |||
391 | inline unsigned int left_data_array_prev(void) { |
||
392 | if(left_data_idx == 0) |
||
393 | return left_data_array[BUFFER_SIZE-1]; |
||
394 | else
|
||
395 | return left_data_array[left_data_idx - 1]; |
||
396 | } |
||
397 | |||
398 | inline unsigned int left_data_array_bottom(void) { |
||
399 | if(left_data_idx == BUFFER_SIZE-1) |
||
400 | return left_data_array[0]; |
||
401 | else
|
||
402 | return left_data_array[left_data_idx + 1]; |
||
403 | } |
||
404 | |||
405 | inline void right_data_array_put(unsigned short int value) { |
||
406 | if(right_data_idx == BUFFER_SIZE-1) |
||
407 | right_data_idx = 0;
|
||
408 | else
|
||
409 | right_data_idx++; |
||
410 | right_data_array[right_data_idx] = value; |
||
411 | } |
||
412 | |||
413 | inline unsigned int right_data_array_top(void) { |
||
414 | return right_data_array[right_data_idx];
|
||
415 | } |
||
416 | |||
417 | inline unsigned int right_data_array_prev(void) { |
||
418 | if(right_data_idx == 0) |
||
419 | return right_data_array[BUFFER_SIZE-1]; |
||
420 | else
|
||
421 | return right_data_array[right_data_idx - 1]; |
||
422 | } |
||
423 | |||
424 | inline unsigned int right_data_array_bottom(void) { |
||
425 | if(right_data_idx == BUFFER_SIZE-1) |
||
426 | return right_data_array[0]; |
||
427 | else
|
||
428 | return right_data_array[right_data_idx + 1]; |
||
429 | } |
||
430 | |||
431 | 1418 | chihsiuh | /* Helper functions for encoder_get_v */
|
432 | int left_data_at(int index) { |
||
433 | int tmp_idx = left_data_idx - index;
|
||
434 | if (tmp_idx < 0) |
||
435 | tmp_idx += BUFFER_SIZE; |
||
436 | return left_data_array[tmp_idx];
|
||
437 | } |
||
438 | |||
439 | int right_data_at(int index) { |
||
440 | int tmp_idx = right_data_idx - index;
|
||
441 | if (tmp_idx < 0) |
||
442 | tmp_idx += BUFFER_SIZE; |
||
443 | return right_data_array[tmp_idx];
|
||
444 | } |
||
445 | |||
446 | int get_dx(char encoder, int index) { |
||
447 | int dx, ctr;
|
||
448 | ctr = 0;
|
||
449 | dx = 1024;
|
||
450 | do {
|
||
451 | if (encoder == LEFT)
|
||
452 | dx = left_data_at(index+ctr) - left_data_at(index+ctr+38);
|
||
453 | else
|
||
454 | dx = right_data_at(index+ctr) - right_data_at(index+ctr+38);
|
||
455 | ctr++; |
||
456 | } while ((dx > 30 || dx < -30) && ctr < 3); |
||
457 | if (dx > 30 || dx < -30) |
||
458 | return ERR_VEL;
|
||
459 | return dx;
|
||
460 | } |