Hallo,
Hier erst mal meine ADC Routine:
Ist leicht abgeändert vom Original aber trotsdem danke an Rakke. Sein Code hat den Vorteil, das die Odometriesenoren im vergleich zu den anderen häufiger abgefragt werden und daher die Messung genauer wird.
Und hier Bilder von meiner Linienverfolgungseinheit:Code:/*************************************************************************** SIGNAL (SIG_ADC) Interuptfunktion zum Auswerten mehrerer ADC-Kanäle nach einer vorgebbaren Konfigurationsliste. Zugriff auf global definierte Variablen für die ADC-Kanäle. last modification: Ver. Date Author Comments ------- ---------- -------------- --------------------------------- beta1 31.03.2005 Robotrixer counting odometrie tics sto1 29.07.2005 stochri Wheelspeed,hysteresis rakke 20.12.2005 Rakke Konfigliste, glob. Var für Line, Bat etc ------- ---------- -------------- --------------------------------- ***************************************************************************/ #define ADC_CTRL_MANYS 12 ///>Zahl der Stützpunkte der configliste #define ADC_CTRL_ODO_LEFT 1 #define ADC_CTRL_ODO_RIGHT 0 #define ADC_CTRL_LINE_LEFT 3 #define ADC_CTRL_LINE_RIGHT 2 #define ADC_CTRL_SWITCHES 4 #define ADC_CTRL_BATTERY 5 // der folgende define erleichtert das berechnen des Wertes ADMUX. // admux_pre_calc = (0 <<REFS1) // AVCC with external ... // | (1 <<REFS0) // ... capacitor at AREF pin; // | (1 <<ADLAR); // Ergebnis in ADCH, ADCL links ausrichten #define ADMUX_PRE_CALC 0x60 const unsigned char adc_mux_config_list[ADC_CTRL_MANYS] = { WHEEL_RIGHT, WHEEL_LEFT, IR_RIGHT, WHEEL_RIGHT, WHEEL_LEFT, IR_LEFT, WHEEL_RIGHT, WHEEL_LEFT, SWITCH, WHEEL_RIGHT, WHEEL_LEFT, BATTERIE }; SIGNAL (SIG_ADC) { // für die Odometrie: static unsigned char flag[2]; ///> merkt sich für jede Seite, ob steigende oder fallende Flanke static unsigned char adc_cnt=0; ///> Merker für die Auswertung unsigned char adc_channel; ///> Kanal für die Auswertung unsigned char side; ///> side = 0: linkes, side = 1: rechtes Rad unsigned char adc_lo; ///> zum Zwischenspeichern des ADC_lo_Wertes unsigned char adc_hi; ///> zum Zwischenspeichern des ADC_hi_Wertes unsigned int temp; ///> Hilfsvariable const unsigned char grenzlow[2]={167,142}; // {LL,RL} const unsigned char grenzhigh[2]={173,148}; // {LH,RH} static unsigned int switcheshist[4]={0,0,0,0}; // ADCL muss zuerst gelesen werden! Sonst können sich zwei Wandlungen überschneiden. adc_lo= ADCL; // Zwischenspeichern des ADC-Wertes in temp. Variable adc_hi= ADCH; // Zwischenspeichern des ADC-Wertes in temp. Variable if(autoencode==ADC_RUN) // Aus Kompatibilitätsgründen und weil sehr praktisch für debugging wird autoencode weiter benutzt { adc_channel = adc_mux_config_list[adc_cnt]; // nachgucken: welcher Kanal wurde gerade gewandelt? Fürs bessere Verständnis als eigene Variable eingeführt. Wenns mal knapp wird, lässt sich hiereine sparen... // Für die Auswertung je nach gewähltem Kanal unterschiedlich verfahren switch(adc_channel) { // Odometriesensoren. Code von stochri weitgehend übernommen. case ADC_CTRL_ODO_LEFT: case ADC_CTRL_ODO_RIGHT: /* Unterscheidung nach steigender und fallender Flanke. Bei steigender Flanke wird nur der encoder_counter der jeweiligen Seite hochgesetzt und das Flag für "gestiegene Flanke" gesetzt. Die Seite bestimmt sich aus "adc_channel". Da encoder[0] aus AD-Eingang=1 und encoder[1] aus AD-Eingang=0 kommen, muss die Seite über XOR getauscht werden, um mit WEJA/stochri kompatibel zu bleiben. Wenn die Bytes mal ganz arg knapp werden, lässt sich hier eins sparen... */ side = adc_channel ^ 1; // Hysteresis included 25.6.2005 stochri if ( (adc_hi < grenzlow[side]) && (flag[side] == TRUE)) // falling edge { encoder[side] ++; flag[side] = FALSE; } if ( (adc_hi > grenzhigh[side]) && (flag[side] == FALSE)) // rising edge { encoder[side] ++; flag[side] = TRUE; } break; // Liniensensoren. Liefert die gleichen Werte wie "LineData" case ADC_CTRL_LINE_LEFT: case ADC_CTRL_LINE_RIGHT: side = (adc_channel & 0x01)^ 1; // nur das kleinste Bit vom adc_channel auswerten temp = (((unsigned int)adc_hi)<<8) + ((unsigned int)adc_lo); line[side] = temp>>6; // Ergebnis ist links ausgerichtet break; // Kollisionstaster. Liefert die gleichen Werte wie "Pollswitch" und funktioniert genauso gut. Hm. case ADC_CTRL_SWITCHES: temp = ((((unsigned int)adc_hi)<<8) + ((unsigned int)adc_lo))>>6; // align right temp = (unsigned char)(((10240000L/(long)temp-10000L)*62L+5000L)/10000); switcheshist[3] = switcheshist[2]; // shiftregister switcheshist[2] = switcheshist[1]; // shiftregister switcheshist[1] = switcheshist[0]; // shiftregister switcheshist[0] = temp; if (switcheshist[0] == switcheshist[1] && switcheshist[0] == switcheshist[2] && switcheshist[0] == switcheshist[3]) // nur wenn alle werte gelich sind, wird wert übernommen. switches = temp; // ansonsten bleibt switches beim alten wert. break; // Batteriespannung. Liefert die gleichen Werte wie "Batterie" und funktioniert genauso gut. Der Vollständigkeit halber aufgeführt. case ADC_CTRL_BATTERY: u_Bat = (((unsigned int)adc_hi)<<8) + ((unsigned int)adc_lo); u_Bat = u_Bat >>6; break; } /* ADC-MUX für die nächste AD-Wandlung auf den nächsten Kanal der Konfigliste umschalten. */ adc_cnt = adc_cnt + 1; // nächstes Listenelement if (adc_cnt >= ADC_CTRL_MANYS) // Listenende erreicht? adc_cnt = 0; // Dann von vorne anfangen! ADMUX = ADMUX_PRE_CALC + adc_mux_config_list[adc_cnt];// aus der config-Liste den nächsten Kanal holen } /* Schließlich noch die nächste Konversion starten: das ADSC-Bit wird nach Beendigung der Wandlung durch den ATmega auf null gesetzt. Dies muss außerhalb der "if(autoencode)" erfolgen, sonst wird der Interrupt nicht mehr ausgelöst! Soll also - z. B. wenn die Zeit mal knapp ist - der Code "gekürzt" werden, dann über autoencode = 0 ausschalten. */ ADCSRA = ADCSRA | (1<<ADSC); }// Ende der rakke'schen ADC-Interruptroutine
Damit bin ich eigentlich sehr zufrieden.







Zitieren

Lesezeichen