File: dsensor.c

package info (click to toggle)
brickos 0.9.0.dfsg-16
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,076 kB
  • sloc: ansic: 9,139; cpp: 860; asm: 693; makefile: 654; sh: 134; perl: 61
file content (620 lines) | stat: -rw-r--r-- 16,347 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
/*! \file   dsensor.c
    \brief  Implementation: direct sensor access
    \author Markus L. Noga <markus@noga.de>
*/

/*
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *  License for the specific language governing rights and limitations
 *  under the License.
 *
 *  The Original Code is legOS code, released October 17, 1999.
 *
 *  The Initial Developer of the Original Code is Markus L. Noga.
 *  Portions created by Markus L. Noga are Copyright (C) 1999
 *  Markus L. Noga. All Rights Reserved.
 *
 *  Contributor(s): Markus L. Noga <markus@noga.de>
 *                  Eric Habnerfeller <ehaberfe@atitech.ca>
 *                  Lou Sortman <lou@sunsite.unc.edu>
 */

/*
 *  2000.03.11 - Paolo Masetti <paolo.masetti@itlug.org>
 *
 *	- Included a fix for rotation sensor posted by "Ben Jackson"
 *        on lugnet.robotics.rcx.legos
 *
 *  2000.04.30 - Paolo Masetti <paolo.masetti@itlug.org>
 *
 *	- ISR Reading routine fix to make read values stable.
 *	- Fixed rotation sensor status table values to avoid offset problems.
 *
 *  2000.09.06 - Jochen Hoenicke <jochen@gnu.org>
 *
 *	- Added velocity calculation for rotation sensor.
 */

#include <dsensor.h>

#ifdef CONF_DSENSOR

#include <sys/h8.h>
#include <sys/irq.h>
#include <sys/bitops.h>
#include <rom/registers.h>
#include <unistd.h>
#include <conio.h>

///////////////////////////////////////////////////////////////////////////////
//
// Definitions
//
///////////////////////////////////////////////////////////////////////////////

#define DS_ALL_ACTIVE         0x07             //!< all sensors active mode
#define DS_ALL_PASSIVE        (~DS_ALL_ACTIVE) //!< all sensors passive mode

///////////////////////////////////////////////////////////////////////////////
//
// Variables
//
///////////////////////////////////////////////////////////////////////////////

volatile unsigned char ds_channel;        //!< current A/D channel

unsigned char ds_activation;              //!< channel bitmask. 1-> active

#ifdef CONF_DSENSOR_ROTATION
unsigned char ds_rotation;                //!< channel bitmask. 1-> rotation

volatile int ds_rotations[3];             //!< sensor revolutions * 16

static signed char rotation_state[3];     //!< rotation state
static signed char rotation_new_state[3]; //!< proposed rotation state
static unsigned int state_duration[3];    //!< proposed rotation state duration

#ifdef CONF_DSENSOR_VELOCITY
volatile int ds_velocities[3];            //!< sensor velocity
static unsigned int last_rotation[3];     //!< last time of rotation signal
static unsigned int next_rotation[3];     //!< rough upper estimatation of next signal time
static signed char rotation_dir[3];       //!< direction of last rotation
#endif



//! convert a/d values to rotation states
/*! Indexed with (value>>12).
    Invalid values yield negative states.
*/
static const signed char ad2state[16]={
  // 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f	// (sensor value>>12)
    -1,-1,-1,-1,-1, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 0	// New values to be used
							// with delayed read

//  -1,-1,-1,-1,-1,-1, 2, 2, 2, 3, 3, 3, 1, 1, 1, 0	// Old values: biased for
							// non-delayed read

};

//! convert state difference to revolution count change
/*! Indexed with (newstate-state)+3.
    Invalid differences yield zero change.
    Differences of magnitude two could have been acheived in either
    rotational sense, so their expected value is zero.
*/
static const signed char diff2change[7]={
  //-3 -2 -1  0  1  2  3      	      	      	      // newstate-state
     1, 0,-1, 0, 1, 0,-1
};

///////////////////////////////////////////////////////////////////////////////
//
// Functions
//
///////////////////////////////////////////////////////////////////////////////

//! set rotation to an absolute value
/*! \param sensor the sensor address, can be &SENSOR_1, &SENSOR_2 or &SENSOR_3
    \param pos    desired absolute position

    axis should be inert during the function call
*/
void ds_rotation_set(volatile unsigned *sensor,int pos) {
  if(sensor>=&AD_A && sensor<=&AD_C) {    // catch range violations
    unsigned channel=(unsigned) (sensor-&AD_A);
    signed char state=ad2state[(*sensor)>>12];

    if(state<0)
      state=0;

    rotation_state[channel]=state;
    rotation_new_state[channel] = -1;
    state_duration[channel]=0;
    ds_rotations[channel]=pos;            // reset counter

  }
}

//! process rotation sensor on current A/D channel
/*! \sa ds_channel current channel (global input value)
*/
void ds_rotation_handler() {
  unsigned    channel =ds_channel;
  unsigned    raw     =(*((&AD_A)+channel));
  signed char newstate=ad2state[raw>>12];

  if (newstate < 0)
    return;

  if (newstate == rotation_new_state[channel]) {
    if (++state_duration[channel] == 2) {
      signed char change = diff2change[newstate - rotation_state[channel] + 3];

      ds_rotations[channel] += change;

#ifdef CONF_DSENSOR_VELOCITY
      {
	/* We only take the lowest 16 bits of sys_time.  We have to be
	 * a bit careful with wraparounds, but this is handled here.
	 */
	unsigned int time = (unsigned int) sys_time;
	if (change != rotation_dir[channel]) {
	  rotation_dir[channel] = change;
	  ds_velocities[channel] = 0;
	  last_rotation[channel] = time;
	  next_rotation[channel] = time + 1000;
	} else {
	  if (time == last_rotation[channel])
	    ds_velocities[channel] = 1000 * change;
	  else {
	    unsigned int time_diff = (time - last_rotation[channel]);
	    if (time_diff > 1000) {
	      rotation_dir[channel] = 0;
	      ds_velocities[channel] = 0;
	    } else {
	      int speed = 1000 / time_diff;
	      ds_velocities[channel] = change > 0 ? speed : -speed;
	      last_rotation[channel] = time;
	      next_rotation[channel] = time + time_diff * 3 / 2;
	    }
	  }
	}
      }
#endif

      rotation_state[channel] = newstate;
      rotation_new_state[channel] = -1;
    }
  } else if (newstate != rotation_state[channel]) {
    rotation_new_state[channel] = newstate;
    state_duration[channel] = 1;
#ifdef CONF_DSENSOR_VELOCITY
  } else {
    /* No rotation change, check if velocity measure timeouts. */
    unsigned int time = (unsigned int) sys_time;
    if (rotation_dir[channel] &&  
	((signed int) (time - next_rotation[channel])) >= 0) {
      unsigned int time_diff = (time - last_rotation[channel]);
      if (time_diff > 1000) {
	rotation_dir[channel] = 0;
	ds_velocities[channel] = 0;
      } else {  
	int speed = 1000 / time_diff;
	ds_velocities[channel] = rotation_dir[channel] > 0 ? speed : -speed;
	next_rotation[channel] = time + time_diff / 2;
      }
    }
#endif
  }

}
#endif // CONF_DSENSOR_ROTATION

#ifdef CONF_DSENSOR_MUX
unsigned char ds_mux;	//!< mux   bitmask

volatile int ds_muxs[3][3];	//!< mux ch values


//width of each mux pulse
#define DS_MUX_PULSE_TM_MS 10



typedef struct {
  unsigned long nextTm;  //timestamp for next pulse
  char remainingEdges;    //edges left in pulse train
  char channel; //current mux sub channel (0,1,2)
  unsigned int attached[3];//what channels are sensors attached to
                            //this also defines the number of ms
                            //to wait before reading the value
  
  enum {ds_mux_prepRead,
	ds_mux_read, 
	ds_mux_pulse_low, 
	ds_mux_pulse_high} action; //specify next action
} ds_mux_data_t;

ds_mux_data_t ds_mux_data[3]; //data on mux

#endif //CONF_DSENSOR_MUX



static inline void ds_power_on(unsigned channel) {
  switch(channel) {
  case 0:
    bit_set(&PORT6,0);
    break;
  case 1:
    bit_set(&PORT6,1);
    break;
  case 2:
    bit_set(&PORT6,2);
    break;
  default:
    //bad
    break;
  }
}//endof ds_power_on

static inline void ds_power_off(unsigned channel) {
  switch(channel) {
  case 0:
    bit_clear(&PORT6,0);
    break;
  case 1:
    bit_clear(&PORT6,1);
    break;
  case 2:
    bit_clear(&PORT6,2);
    break;
  default:
    //bad
    break;
  }
}//endof ds_power_off

#ifdef CONF_DSENSOR_MUX


//! start multiplexing
void ds_mux_on(volatile unsigned *sensor,
	       unsigned int ch1,
	       unsigned int ch2,
	       unsigned int ch3) {
  unsigned char i,j;
  ds_passive(sensor);//powered, but not active in legOS sense


  if(ch1==0 &&
     ch2==0 &&
     ch3==0) {
    //umm this is useless
    //avoid endless cycling
    ds_mux_off(sensor);
    return;
  }

  if (sensor == &SENSOR_3) {
    i=0;
  } else if (sensor == &SENSOR_2) {
    i=1;
  } else if (sensor == &SENSOR_1) {
    i=2;
  } else {
    //bad
    return;
  }

  
  ds_mux_data[i].attached[0]=ch1;
  ds_mux_data[i].attached[1]=ch2;
  ds_mux_data[i].attached[2]=ch3;
  
  //add extended time based on the channel
  //this is required by the mux
  //the user supplies extra time based on the
  //type of sensor they hook up
  //these defaults give enough time to read
  //a light sensor and should be ok for most
  //sensors
  if(ch1)
    ds_mux_data[i].attached[0]+=160;
  if(ch2)
    ds_mux_data[i].attached[1]+=135;
  if(ch3)
    ds_mux_data[i].attached[2]+=25;




  //check if we're just adjusting the ports
  //if so we can return here
  if(i==0 && ds_mux&1)
    return;
  if(i==1 && ds_mux&2)
    return;
  if(i==2 && ds_mux&4)
    return;

  //starting up mux

  //power up
  ds_power_on(i);
  
  //schedule first event
  //find first attached sensor
  for(j=0;j<3 && ds_mux_data[i].attached[j]==0;j++);
    
  ds_mux_data[i].channel=j;
  ds_mux_data[i].remainingEdges=((j+1)*2);
  ds_mux_data[i].action=ds_mux_pulse_low;
  ds_mux_data[i].nextTm=sys_time+DS_MUX_PULSE_TM_MS;

  if (sensor == &SENSOR_3) {
    bit_set(&ds_mux, 0);
  } else if (sensor == &SENSOR_2) {
    bit_set(&ds_mux, 1);
  } else if (sensor == &SENSOR_1) {
    bit_set(&ds_mux, 2);
  } else {
    //bad
    return;
  }

}//endof ds_mux_on



void ds_mux_handler() {
  unsigned sen=ds_channel;


  if(ds_mux_data[sen].nextTm <= sys_time) {
    //we've reached our next scheduled step
    //lcd_int(sys_time-ds_mux_data[sen].nextTm);
    switch(ds_mux_data[sen].action) {
    case ds_mux_prepRead:
      ds_power_off(sen);//power down for read
      ds_mux_data[sen].action=ds_mux_read;
      ds_mux_data[sen].nextTm=sys_time;//do it ASAP, but not now
      break;
    case ds_mux_read:
      //read data
      switch(sen) {
      case 0:
	ds_muxs[sen][(int)ds_mux_data[sen].channel]=SENSOR_3;
	break;
      case 1:
	ds_muxs[sen][(int)ds_mux_data[sen].channel]=SENSOR_2;
	break;
      case 2:
	ds_muxs[sen][(int)ds_mux_data[sen].channel]=SENSOR_1;
	break;
      default:
	//bad
      }

 
      //change channel
      do {
	ds_mux_data[sen].channel++;
	if(ds_mux_data[sen].channel>2/*max chan*/) {
	  ds_mux_data[sen].channel=0;
	}
	//make sure selected channel is marked attached
	//don't worry about an endless loop ds_mux_on makes
	//sure at least one channel is attached
      } while(
	      (ds_mux_data[sen].attached
	       [(int)ds_mux_data[sen].channel])==0);
      
    
      //use this low pulse as the first low pulse of next train

      ds_mux_data[sen].remainingEdges=
	((ds_mux_data[sen].channel+1)*2)-1;

      //schedule next high pulse
      ds_mux_data[sen].action=ds_mux_pulse_high;
      ds_mux_data[sen].nextTm=sys_time+DS_MUX_PULSE_TM_MS;
      break;
    case ds_mux_pulse_low:
      //go low
      ds_power_off(sen);
      //schedule next high pulse
      ds_mux_data[sen].nextTm=sys_time+DS_MUX_PULSE_TM_MS;
      ds_mux_data[sen].remainingEdges--;  
      ds_mux_data[sen].action=ds_mux_pulse_high;
      break;
    case ds_mux_pulse_high:
      //go high
      ds_power_on(sen);
      ds_mux_data[sen].remainingEdges--;      
     
      if(ds_mux_data[sen].remainingEdges==0) {
	//done with train
	//schedule prepRead
	ds_mux_data[sen].action=ds_mux_prepRead;

	//schedule enough time for the mux to make the switch
	//this is scaled because the timeout the mux uses starts
	//when the first pulse comes in, it is around 70ms, so
	//when switching to sensor 1 we must want an additional
	//amount of time before it mux reacts, we wait less for 2
	//and not at all for 3
	//then we wait a little bit before reading the sensor
	//this give the sensor time to power up
	ds_mux_data[sen].nextTm=sys_time+
	  ds_mux_data[sen].attached[(int)ds_mux_data[sen].channel];
	//lcd_int(ds_mux_data[sen].channel+1);

	break;
      } else {
	//schedule next low pulse
	ds_mux_data[sen].action=ds_mux_pulse_low;
	ds_mux_data[sen].nextTm=sys_time+DS_MUX_PULSE_TM_MS;
      }      
      break;
    default:
      //bad
    }

  }
}//endof ds_mux_handler

#endif //CONF_DSENSOR_MUX




//! sensor A/D conversion IRQ handler
//
extern void ds_handler(void);
#ifndef DOXYGEN_SHOULD_SKIP_THIS
__asm__("\n\
.text\n\
.align 1\n\
_ds_handler:\n\
   ; r6 saved by ROM\n\
\n\
   mov.b @_ds_channel,r6l	; r6l = current channel\n\
\n\
   mov.b @_ds_activation,r6h	; r6h = activation bitmask\n\
   btst r6l,r6h			; activate output?\n\
   beq ds_noset\n\
       bset r6l,@_PORT6:8	; activate output of last port scanned\n\
 ds_noset:\n\
        "
#ifdef CONF_DSENSOR_ROTATION
        "\n\
   mov.b @_ds_rotation,r6h	; r6h = rotation bitmask\n\
   btst r6l,r6h			; process rotation sensor?\n\
   beq ds_norot\n\
\n\
     push r0			; save r0..r3\n\
     push r1\n\
     push r2\n\
     push r3			; r4..r6 saved by gcc if necessary\n\
\n\
     jsr _ds_rotation_handler	; process rotation sensor\n\
\n\
     pop r3\n\
     pop r2\n\
     pop r1\n\
     pop r0\n\
 ds_norot:\n\
        "
#endif

#ifdef CONF_DSENSOR_MUX
        "\n\
   mov.b @_ds_mux,r6h	; r6h = mux bitmask\n\
   btst r6l,r6h			; process mux sensor?\n\
   beq ds_nomux\n\
\n\
     push r0			; save r0..r3\n\
     push r1\n\
     push r2\n\
     push r3			; r4..r6 saved by gcc if necessary\n\
\n\
     jsr _ds_mux_handler	; process mux sensor\n\
\n\
     pop r3\n\
     pop r2\n\
     pop r1\n\
     pop r0\n\
 ds_nomux:\n\
        "
#endif
        "\n\
   inc r6l			; next channel\n\
   and #0x03,r6l		; limit to 0-3\n\
\n\
   mov.b @_ds_activation,r6h	; r6h = activation bitmask\n\
   btst r6l,r6h			; activate output?\n\
   beq ds_nounset\n\
      bclr r6l,@_PORT6:8		; set output inactive for reading\n\
 ds_nounset:\n\
\n\
   ; The settle time for reading the value from active sensor start here\n\
\n\
   ; moved here for helping timing problems\n\
   mov.b r6l,@_ds_channel	; store next channel\n\
\n\
   ; Added a delay loop for sensor settle time\n\
\n\
   mov.b #0x04, r6h		; delay loop\n\
settle:\n\
   nop				; each nop is a 2 state clock delay\n\
   dec.b r6h			; 2 states ?\n\
   bne settle			; 4 states\n\
\n\
   ; Total loop delay 32 states (?)\n\
\n\
   mov.b @_AD_CSR:8,r6h		; r6h = A/D CSR\n\
   and.b #0x7c,r6h		; reset scanmode and channel num\n\
   or.b  r6l,r6h		; scan next channel\n\
   mov.b r6h,@_AD_CSR:8		; put r6h back on A/D CSR\n\
\n\
   ; The settle time for reading the value from active sensor finish here\n\
\n\
   bset #0x5,@_AD_CSR:8		; go!\n\
\n\
   rts\n\
");
#endif // DOXYGEN_SHOULD_SKIP_THIS


//! initialize sensor a/d conversion
/*! all sensors set to passive mode
    rotation tracking disabled
*/
void ds_init(void) {
  rom_port6_ddr|=DS_ALL_ACTIVE;         // notify ROM we are using
  PORT6_DDR     =rom_port6_ddr;         // PORT6 bit 0..2 as outputs

  ds_activation=0;                      // all sensors passive
  ds_channel   =0;			// start on channel 0

#ifdef CONF_DSENSOR_ROTATION
  ds_rotation  =0;                      // rotation tracking disabled
#endif

#ifdef CONF_DSENSOR_MUX
  ds_mux=0;                             // muxing disabled
#endif

  ad_vector=&ds_handler;		// setup IRQ handler
  AD_CR &=~ADCR_EXTERN;
  AD_CSR =ADCSR_TIME_266 | ADCSR_GROUP_0 | ADCSR_AN_0  |
          ADCSR_ENABLE_IRQ | ADCSR_START;

#ifdef CONF_CONIO
    delay(10);				// wait for initial A/D
#else
# warning "Rotation initialization might fail."
#endif
}


//! shutdown sensor a/d conversion
/*! all sensors set to passive mode
*/
void ds_shutdown(void) {

  AD_CSR=0x00;
  PORT6        &=DS_ALL_PASSIVE;
  rom_port6_ddr&=DS_ALL_PASSIVE;
  PORT6_DDR     =rom_port6_ddr;
}

#endif  // CONF_DSENSOR