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.
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
Und hier Bilder von meiner Linienverfolgungseinheit:
Damit bin ich eigentlich sehr zufrieden.
Lesezeichen