ASURO Library  2.80
asuro.c
gehe zur Dokumentation dieser Datei
1 /****************************************************************************/
59 /*****************************************************************************
60 * *
61 * This program is free software; you can redistribute it and/or modify *
62 * it under the terms of the GNU General Public License as published by *
63 * the Free Software Foundation; either version 2 of the License, or *
64 * any later version. *
65 * *
66 *****************************************************************************/
67 #include "asuro.h"
68 #include "myasuro.h"
69 
70 #define BAUD_RATE 2400
71 
72 
73 /***************************************************************************
74  \brief
75  Speicher für die ADC Werte aus IsrStandard().
76  Hierauf greifen die nichtinterrupt-Funktionen PollSwitch(), LineData(..) etc. zu.
77  */
78 volatile unsigned int adcValue[6];
79 
80 /***************************************************************************
81  \brief
82  Interrupt-Funktion fuer den AD-Wandler. Kann auch, ueber autoencode gesteuert,
83  die Odometrie-Zaehler in encoder hochzaehlen.
84  Bemerkung:
85  Alle ADC Werte kommen durch die ADC hardware pipeline 2 Interrupts später an.
86 
87  \param
88  keine
89 
90  \return
91  nichts
92 
93  \see
94  Die globale Variable autoencode wird hier ausgewertet. Ist sie nicht FALSE,\n
95  dann wird der AD-Wandler-Wert zum Zaehlen der Odometriewerte in der globalen\n
96  Variablen encoder benutzt.\n
97  Es wird auch der AD-Wandler-Kanal auf die 'andere' Seite der Odometrie\n
98  umgeschaltete und der AD-Wandler neu gestartet.\n
99  Somit wird erreicht, dass zumindest die Odometriemessung automatisch erfolgt.
100  Anmerkungen zu encoder:
101  1)
102  In avg wird ein gleitender Mittelwert mitgeführt: a(n+1) = (a(n)+sensor)/4
103  (Ich shifte um 2 und habe damit einen Teiler von 4.)
104 
105  2)
106  Ich wollte der Interruptroutine Platz und Zeit ersparen und habe deshalb
107  auf eine Initialisierung von avg verzichtet. Durch den kleinen Teiler 4
108  passt sich avg sehr rapide den ADC-Werten an. Und verliert in der Realität
109  auch "nie" den ersten Tick. (So muss ASURO auch meist erst mal anfahren.)
110 
111  3)
112  Allerdings - und hier habe ich noch nicht probiert - könnte es mit ein Teiler von 8
113  zum ersten mal gelingen, bei Tageslicht, auch ohne eingeschaltetem Encoder-LED,
114  Ticks zu sammeln (allerdings verrauscht). Und das spart immerhin Energie -
115  falls es wirklich darauf ankommt.
116 
117  * ADC_vect: handles all ADC and odometry ticking
118 
119  \par Beispiel:
120 int main(void) {
121  int lSoll=60, rSoll=30, lPwm=lSoll, rPwm=rSoll, delta=50;
122  Init();
123  EncoderInit();
124  EncoderStart();
125 
126  while(1) {
127  int lIst=encoder[LEFT], rIst=encoder[RIGHT];
128  EncoderSet(0, 0);
129 
130  lPwm+=(delta*lSoll - 1000*lIst)/300;
131  rPwm+=(delta*rSoll - 1000*rIst)/300;
132 
133  SetMotorPower(lPwm, rPwm);
134  Msleep(delta);
135  }
136 }
137 *****************************************************************************/
138 void IsrStandard(void) {
139  static int sign[2]={1,1}, avg[2], adc_cnt = 0;
140  static unsigned char mux[]={
141  _BV(ADLAR) | _BV(REFS0) | IR_RIGHT, // AVCC reference with external capacitor
142  _BV(ADLAR) | _BV(REFS0) | IR_LEFT, // AVCC reference with external capacitor
143  _BV(ADLAR) | _BV(REFS0) | SWITCH,
144  _BV(ADLAR) | _BV(REFS0) | BATTERIE | _BV(REFS1), // internal 2.56V reference with external capacitor
145  _BV(ADLAR) | _BV(REFS0) | WHEEL_RIGHT, // AVCC reference with external capacitor
146  _BV(ADLAR) | _BV(REFS0) | WHEEL_LEFT, // AVCC reference with external capacitor
147  };
148  // Vielleicht hat Rakke ja Recht:
149  // ADCL muss zuerst gelesen werden! Sonst können sich zwei Wandlungen überschneiden.
150  unsigned int sensor = ADCL | (ADCH << 8);
151  adcValue[adc_cnt]=sensor;
152 
153  if(autoencode) // Aus Kompatibilitätsgründen wird autoencode weiter benutzt
154  {
155  if(adc_cnt<2) { // WHEEL_RIGHT || WHEEL_LEFT
156  int s=(sensor >> 8);
157  // In avg wird ein gleitender Mittelwert mitgeführt: a(n+1) = 0.75*a(n)+0.25*s
158  avg[adc_cnt] += (s-avg[adc_cnt])>>2;
159 
160  // Schneidet die aktuelle Sensorkurve den gleitenden Mittelwert? Konkret:
161  // Weicht der aktuelle Sensorwert um mehr als +/- 2 vom gleitenden Mittelwert ab?
162  if (sign[adc_cnt]*(s-avg[adc_cnt]) > 2)
163  {
164  // Dann zähle einen Tick weiter.
165  // Und nächster Tick erst wieder bei -/+ 2 Abweichung vom gleitenden Mittelwert.
166  encoder[adc_cnt^RIGHT]++;
167  sign[adc_cnt] = -sign [adc_cnt];
168  }
169  }
170  }
171 
172  if(adc_cnt==SWITCH) {
173  switched=switched || sensor<(MY_SWITCH_THRESHHOLD<<6); // Es wurde (ganz sicher) eine Taste gedrückt
174  }
175 
176  ADMUX = mux[adc_cnt];
177  adc_cnt=(adc_cnt+1) % sizeof(mux);
178 }
179 
180 /****************************************************************************/
228 void Init (
229  void)
230 {
231  /*
232  Timer2, zum Betrieb mit der seriellen Schnittstelle, fuer die
233  IR-Kommunikation auf 36 kHz eingestellt.
234  */
235 #if defined(__AVR_ATmega168__)
236  // fast PWM, set OC2A on compare match, clear OC2A at bottom, clk/1
237  TCCR2A = _BV(WGM20) | _BV(WGM21) | _BV(COM2A0) | _BV(COM2A1);
238  TCCR2B = _BV(CS20);
239  // interrupt on timer overflow
240  TIMSK2 |= _BV(TOIE2);
241 #else
242  // fast PWM, set OC2A on compare match, clear OC2A at bottom, clk/1
243  TCCR2 = _BV(WGM20) | _BV(WGM21) | _BV(COM20) | _BV(COM21) | _BV(CS20);
244  // interrupt on timer overflow
245  TIMSK |= _BV(TOIE2);
246 #endif
247  // 36kHz carrier/timer
248  OCR2 = 0x91;
249 
250  /*
251  Die serielle Schnittstelle wurde waerend der Boot-Phase schon
252  programmiert und gestartet. Hier werden die Parameter auf 2400 1N8 gesetzt.
253  */
254 #if defined(__AVR_ATmega168__)
255  UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
256  UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
257  UCSR0B = (1<<RXEN0) | (1<<TXEN0);
258  UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);
259 #else
260  UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8; // set baud rate
261  UBRRL = (((F_CPU/BAUD_RATE)/16)-1);
262  UCSRB = (1<<RXEN)|(1<<TXEN); // enable Rx & Tx
263  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // config USART; 8N1
264 #endif
265 
266  /*
267  Datenrichtung der I/O-Ports festlegen. Dies ist durch die Beschaltung der
268  Asuro-Hardware nicht aenderbar.
269  Port B: Seriell Senden; Richtungsvorgabe Motor links; Takt fuer die
270  Geschwindigkeit beider Motoren; Grueneanteil-Status-LED
271  Port D: Richtungsvorgabe Motor rechts; Vordere LED;
272  Odometrie-LED (Radsensor); Rotanteil-Status-LED
273  */
274  DDRB = IRTX | RIGHT_DIR | PWM | GREEN_LED;
275  DDRD = LEFT_DIR | FRONT_LED | ODOMETRIE_LED | RED_LED | SWITCHES; // Port-Bits als Output
276 
277 
278  /*
279  PWM-Kanaele OC1A und OC1B auf 8-Bit einstellen.
280  Sie werden fuer die Geschwindigkeitsvorgaben der Motoren benutzt.
281  */
282  TCCR1A = _BV(WGM10) | _BV(COM1A1) | _BV(COM1B1);
283  TCCR1B = _BV(CS11); // tmr1-Timer mit MCU-Takt/8 betreiben.
284 
285  /*
286  Sonstige Vorbereitungen.
287  - Alle LED's ausschalten
288  - Motoren stoppen und schon mal auf Vorwaerts einstellen.
289  - Globale Variable autoencoder ausschalten.
290  */
291  FrontLED (OFF);
292  BackLED (ON, ON);
293  BackLED (OFF, OFF);
294  StatusLED (GREEN);
295  SWITCH_ON;
296 
297  MotorDir (FWD, FWD);
298  MotorSpeed (0, 0);
299 
300  autoencode = FALSE;
301 
302  Ovr2IntFunc = 0;
304 
305  /*
306  * Vorbereitung für WHEEL-Interrupts (Encoder)
307  Port C als Input => dadurch gehen die Back-LED aus ...
308  */
309  DDRC &= ~ (_BV(PC0) | _BV(PC1));
310 
311  /*
312  ... aber nun koennen die LED's am Rad eingeschaltet werden, und die
313  Sensoren koennen gemessen werden.
314  */
316 
317  /*
318  Linken Odometrie-Sensor auswaehlen. (AVCC ref. with external capacitor)
319  */
320  ADMUX = _BV(ADLAR) | _BV(REFS0) | WHEEL_LEFT;
321 
322  /*
323  AD-Wandler einschalten, Parameter einstellen und Starten. (clk/128)
324  Startet den ADC im 'free running'-Mode (ADFR). Das heisst, der Wandler
325  nach einer Messung automatisch wieder neu startet.
326  */
327  ADCSRA = _BV(ADEN) | _BV(ADFR) | _BV(ADIE) | _BV(ADSC) | _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2);
328 
329  /*
330  Funktion zum ALLGEMEINEN ZULASSEN von Interrupts.
331  */
332  sei ();
333 }
334 
335 /****************************************************************************/
336 /*
337  \brief
338  Interrupt-Funktion fuer Timer-2-Ueberlauf.
339 
340  \see
341  count36kHz, timebase
342 
343  \par
344  Der zum Timer gehoerende Zaehler TCNT2 wird so justiert, dass damit die\n
345  gewuenschten 36 kHz erreicht werden.\n
346  Fuer die Zeitfunktionen werden die globalen Variablen count36kHz und\n
347  timebase hochgezaehlt.
348 
349  \par
350  Die Variable Ovr2IntFunc kann als Zeiger auf eine User Funktion benutzt werden\n
351  und wird dann, falls ungleich 0, von der Interrupt Funktion aus angesprungen.
352 
353  \par Beispiel:
354  (Nicht vorhanden)
355 *****************************************************************************/
356 SIGNAL (TIMER2_OVF_vect)
357 {
358  TCNT2 += 0x25;
359  count36kHz ++;
360  if (!count36kHz)
361  timebase ++;
362  if (Ovr2IntFunc)
363  Ovr2IntFunc();
364 }
365 
366 
370 #if defined(__AVR_ATmega168__)
371 SIGNAL(TIMER2_COMPA_vect)
372 #else
373 SIGNAL(TIMER2_COMP_vect)
374 #endif
375 {
376  count36kHz++;
377  if (!count36kHz)
378  timebase ++;
379 }
380 
381 
382 /****************************************************************************/
383 /*
384  \brief
385  Interrupt-Funktion fuer den AD-Wandler. Kann ueber autoencode gesteuert\n
386  die Odometrie-Zaehler in encoder hochzaehlen.
387 
388  \see
389  Die globale Variable autoencode wird hier ausgewertet. Ist sie nicht FALSE,\n
390  dann wird der AD-Wandler-Wert zum Zaehlen der Odometriewerte in der globalen\n
391  Variablen encoder benutzt.\n
392  Es wird auch der AD-Wandler-Kanal auf die 'andere' Seite der Odometrie\n
393  umgeschaltete und der AD-Wandler neu gestartet.\n
394  Somit wird erreicht, dass zumindest die Odometriemessung automatisch erfolgt.
395 
396  \par
397  Die Variable AdcIntFunc kann als Zeiger auf eine User Funktion benutzt werden\n
398  und wird dann, falls ungleich 0, von der Interrupt Funktion aus angesprungen.
399 
400  \par Beispiel:
401  (Nicht vorhanden)
402 *****************************************************************************/
403 SIGNAL (ADC_vect)
404 {
405  AdcIntFunc();
406 }
407