mikrocontroller.net

Forum: Compiler & IDEs Probleme nach Optimierung


Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe ein Programm geschrieben das bis jetzt ohne Optimierung (-O0) super 
lief, jetzt muss ich es aber zwecks erweiterung Optimieren, also im Avr 
Studio auf -Os gestellt, jetzt läuft das Programm aber nicht mehr wie 
gewollt. Ist das mehr ein Problem mit einem Fehler im Code oder eher ein 
grundsätzliches Problem, das zuviel optimiert wird?

Autor: michel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
könnte es evtl sein, dass der compiler eine leere schleife 
"wegoptimiert" hat?
meine gelesen zu haben, dass sowas vorkommen kann.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder ein fehlendes "volatile".

Oliver

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es liegt zu 99.9% an dem Code. Es war bisher eben nur Zufall, dass es 
funktioniert hat.

Autor: Bernhard M. (boregard)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...oder man hat einen GCC 4.3.0-2 (debian), der optimiert schon mal 
uint32_t Operationen (z.B. division) weg...

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also könnte es sein das er folgende Schleife aus der ReadChannel 
Funktion aus dem AVR-GCC Tutorial wegoptimiert?
while(!(MyADCSRA & (1 << ADIF))) 
{
} 

was kann ich dagegen tun?

Autor: lkmiller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
MyADCSRA gibts in dem Tutorial nicht!!

Abhilfe zu deinem MyADCSRA:
volatile char MyADCSRA;

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh, was vergessen, das MyADCSRA ist als volatile uint8_t MyADCSRA 
deklariert und in einem Interupt verwurschtelt:
ISR(ADC_vect)
{
  MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
}

hier noch die komplette ReadChannel Funktion:
uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result = 0;

  MyADCSRA = 0; 

  ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);  

  ADMUX = mux; // Kanal waehlen

    ADCSRA |= (1 << ADSC);              
    while ( ADCSRA & (1 << ADSC)) 
  {
    }

    result |= ADCL + (ADCH << 8); 

    result = 0; 

    for(i = 0; i < 64; i++)
    {
    ADCSRA |= (1 << ADSC); 
    MyADCSRA &= ~(1 << ADIF); 

    while(!(MyADCSRA & (1 << ADIF))) 
    {
    }
          
    result += ADCL + (ADCH << 8);  
    }

  ADCSRA &= ~(1 << ADEN); 
 
    result /= 64; 
   return result;
}

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gibt es irgendeinen Grund für das MyADCSRA anstelle von dem normalen 
ADCSRA ?
PS: ADCL + ADCH kennt der Compiler als ADC

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe alle MyADCSRA in ADCSRA umgewandelt, problem besteht aber 
weiterhin.
Ich lese ADCL + ADCH getrennt ein weil der Atmega8 den wert der ADC 
Wandlung in zwei register packt, oder liege ich da falsch?

Autor: Jo Sen (tigermatze)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Register mit dem Ergebnis kannst du getrennt auslesen.
Versuch doch mal:
while ((ADCSRA & (1 << ADIF))==0);
oder mach ein "nop" in die Schleife:
while(!(MyADCSRA & (1 << ADIF))) 
{
  __asm("NOP");
}
Dann sollte diese nicht mehr wegoptimiert werden.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leider tritt der Fehler immer noch auf, ich werde wohl mal alle 
Interrupt Routinen checken müssen, vermute das sich dort ein Fehler 
befindet.
Werde später mal das ganze c File posten.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn das Teil `volatile' deklariert ist, wird der Zugriff darauf nicht
wegoptimiert.

Der Bug wird also waonders sein.

Wenn ich zu obigem Sourcecodeschnipsel noch
#include <avr/io.h>

volatile uint8_t MyADCSRA;

voranstelle und das Ganze für einen ATmega8 compiliere, erhalte ich:
.L4:
        sbi 38-0x20,6
        lds r24,MyADCSRA
        andi r24,lo8(-17)
        sts MyADCSRA,r24
.L5:
        lds r24,MyADCSRA
        sbrs r24,4
        rjmp .L5
        in r18,36-0x20
        in r24,37-0x20
        clr r25
        mov r25,r24
        clr r24
        add r24,r18
        adc r25,__zero_reg__
        add r20,r24
        adc r21,r25
        subi r19,lo8(-(1))
        cpi r19,lo8(64)
        brne .L4

Gut erkennbar, dass in MyADCSRA zuerst Bit 4 gelöscht wird (das ist
die ominöse -17, entspricht 0xef) und dann auf Bit 4 darin getestet
wird.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo Sen wrote:
> Die Register mit dem Ergebnis kannst du getrennt auslesen.
Schon, aber sie müssen unbedingt in der richtigen Reihenfolge ausgelesen 
werden (grundsätzlich zuerst ADCL, dann ADCH). Und bei dem oben 
gezeigten Konstrukt ist nicht gewährleistet, dass das auch der Fall ist. 
Dafür gibt es eben in C extra die 16-Bit-Zugriffe über ADC bzw. ADCW, 
bei denen der Compiler das selber in der richtigen Reihenfolge macht.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, die Ergebnissreister werden natürlich in der richtigen Reihenfolge 
ausgelesen:
uint16_t x

x = ADCL;       
x += (ADCH << 8); 

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Ja, die Ergebnissreister werden natürlich in der richtigen Reihenfolge
> ausgelesen:
Oben aber nicht (Posting von 13:42)! Und wie gesagt: Spar Dir den ganzen 
Kram und lies das ADCW aus. Da ist alles drin.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ich poste hier mal den ganzen Code vllt. befindet sich dort ja ein 
fehler der erst durch die optimierung auftritt:
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define BAUD 9600UL // Baudrate für serielle Kommunikation
#define F_CPU 4000000UL // Systemtakt in Hz
#define uart_buffer_size 32    
#define MYUBRR F_CPU/16/BAUD-1 // Takt für den USART

char uart_tx_buffer[uart_buffer_size]; // Sendepuffer TX USART
unsigned char counter; // Anzeige die Angeschaltet werden soll
unsigned char status[2] = {0x01, 0x02}; // Status LED's (Rot, Grün)
unsigned char digit[10] = {0x82, 0xF3, 0x4A, 0x62, 0x33, 0x26, 0x06, 0xF2, 0x00, 0x20}; // Bitmaske für die Zahlen
volatile unsigned char display[2] = {0x10, 0x08}; // Siebensegmentanzeige (Einer- , Zehnerstelle)
volatile unsigned char segment[2];
volatile uint8_t uart_tx_flag = 1; // Flag, String komplett gesendet

uint8_t MyADCSRA;

ISR(TIMER0_OVF_vect) // Aufruf bei Interrupt von Timer0 (8 Bit)
{
  PORTC |= 0xFF; // Die bisherige Anzeige ausschalten

    counter++; // Welches ist die nächste Stelle, um Eins erhöhen

    counter &= 0x01; // Wenn Stelle 2 erreicht, zurücksetzen

    PORTD = segment[counter]; // Jeweiligen Wert ausgeben
    PORTC = display[counter]; // Jeweilige Stelle einschalten
}

ISR(ADC_vect)
{
  MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
}

ISR(USART_UDRE_vect) 
{
  static char* uart_tx_p = uart_tx_buffer;   
  uint8_t data;
 
  data = *uart_tx_p++;
  
  if(data == 0) 
  {        
        UCSRB &= ~(1<<UDRIE); // UDRE Interrupt ausschalten        
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen
        uart_tx_flag = 1; // Flag setzen, Übertragung beeendet
    }

    else 
  {
    UDR = data; // Daten senden
    UDR = 0x0A;
  }
}

uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result = 0;

  MyADCSRA = 0; // Interrupt Variable Funktion 
  cli(); // Global Interrupt Disabled
  
  // AD-Wandler wird eingeschaltet
   // Frequenzvorteiler Teilungsfaktor 32
  // bei 4Mhz Taktfrequenz der CPU 
  //
  //  AD_TAKT = CPU_TAKT / TEILUNGSFAKTOR = 4Mhz / 32 = |125kHz|
  //  PERIODENDAUER = 1 / 125kHz = |8 mikroSec.|
  //
  // Es werden alle 8 mikroSec. neue werte vom AD-Wandler eingelesen.
  // Zusätzlich noch den ADC Interrupt (ADIE) einschalten zur
  // weiterverarbeitung für Sleep mode (ADC Noise Canceler)
  ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);  
  
  set_sleep_mode(SLEEP_MODE_ADC); // ADC Sleep mode vorwählen
  //sleep_mode();
  sei(); // Global Interrupt Enabled

  ADMUX = mux; // Kanal waehlen

    // Nach Aktivieren des AD-Wandlers wird ein "Dummy-Readout" empfohlen, 
  // man liest also einen Wert und verwirft diesen, 
  // um den AD-Wandler "warmlaufen zu lassen". 
    ADCSRA |= (1 << ADSC);              
    while ( ADCSRA & (1 << ADSC)) 
  {
  
  }
  
  // 10Bit Ergenisregister ADCL und ADCH einlesen, und als 16Bit großen 
  // Integer Wert speichern. Werte aus ADCL und ADCH rechtsbündig 
    result |= ADCL + (ADCH << 8); 

  // Messung - Arith. Mittelwert aus 64 aufeinanderfolgenden Wandlungen 
    result = 0; 

    for(i = 0; i < 64; i++)
    {
    ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
    MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten  

    while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
    {

    }
          
    result += ADCL + (ADCH << 8);  // Nach jeder Wandlung Ergebnisse aufaddieren
    }

  ADCSRA &= ~(1 << ADEN); // Nach Wandlungen AD-Wandler deaktivieren 
 
    result /= 64; // Summe der Wandlungen durch 64 teilen = arith. Mittelwert
   return result; // den arith. Mittelwert als Rückgabewert der Funktion ReadChannel
}

void status_LED(uint8_t color) // Zeigt den Zustand an
{
  DDRB = 0x03; // Port (B) 1-3 als Ausgang  
  PORTB = 0xFF; // Alle Led'S aus

  switch(color) // Variable "color" abfragen
  {
    case 1:
      // Rote LED leuchtet - Alarmzustand
      PORTB = status[0];
    break;

    case 2:
      // Grüne LED Leuchtet - Alles Super! 
      PORTB = status[1];
    break;
  }
}

void _error(void)
{
  //reti(); TODO: Ordentliche Fehlerroutine die Interrupts berücksichtigt
  PORTC = 0x10; // Einerstelle einschalten
  PORTD = 0x06; // E (ERROR) Ausgeben
  status_LED(1); // Status LED auf Rot (Fehler aufgetretet)
}

void PrintNumber(int number)
{
  if(number < 10) // Einstellig
  {
    if(number < 0) // Wenn Zahl Negativ
    {
        number = -number; // Wenn Zahl negativ, durch Multiplizieren mit (-1) positiv machen
    }

    segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
    segment[1] = 0xFF;  // Zehnerstelle leer lassen
  }

  if(number > 9) // Zweistellig
  {  
    
    if(number > 99) // Wenn Zahl größer als 99, also dreistellig
    {
      _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
    } 
    
    else
    {
      segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
        segment[1] = digit[ number / 10 ]; // Zehnerstelle bestimmen
    }
  }
}

double Angle(uint16_t x, uint16_t y, uint16_t z)
{
  double tilt; // Winkel in Degree (°)

  //  x-Value | z-Value | Degree
  //   409.6  |  614.4  |    0°
  //   614.4  |  409.6  |   90°

  //tilt = (asin((x - 512.0) / 102.4)) * (180 / M_PI); // Einachsige Winkelbestimmung
  tilt = (atan((x - 512.0) / 102.4) / (((z - 512.0) / 102.4))) * (180 / M_PI); // Zweiachsige Winkelbestimmung
  
  return tilt; // Rückgabewert (Winkel) der Funktion
}

void USART_Init(unsigned int ubrr) // USART initialisieren
{
  UBRRH = (unsigned char) (ubrr >> 8); // Baudrate in Register schreiben
  UBRRL = (unsigned char) ubrr;

  UCSRB = (1 << TXEN) | (1 << RXCIE); // UART TX einschalten, UART RX Complete Interrupt einschalten
  UCSRC = (1 << URSEL) | (3 << UCSZ0); // Frame Format einstellen (Asynchron 8N1)
}

void USART_puts(char *data_) 
{
    if(uart_tx_flag == 1) 
  {
      strcpy(uart_tx_buffer, data_); // String Daten in den Sendepuffer kopieren
         uart_tx_flag = 0; // Flag für 'Senden ist komplett' löschen,
        UCSRB |= (1<<UDRIE); // UDRE Interrupt einschalten, los gehts
    }
}

int main(void)
{
  char CSV[7];
  double ADC_angle;

  uint16_t accelx; // Messergebnisse des Kanals C0 (X) (0-1024)
  uint16_t accely; // Messergebnisse des Kanals C1 (Y) (0-1024)
  uint16_t accelz; // Messergebnisse des Kanals C2 (Z) (0-1024)

  // Eigenschaften der Ports C und D definieren
  DDRC = 0x18; // Nur die Ports 3-5 (C) auf Ausgänge schalten
  DDRD = 0xFD; // Port D Komplett als Ausgang für 7 Segment

  USART_Init(MYUBRR); // USART initialisieren
  cli(); // Global Interrupt Disabled

  wdt_enable(5); // Watchdog hält Wache im 500ms Zyklus

    TCCR0 |= (1 << CS00); // Vorteiler 1 (Takt 4Mhz)
    TIMSK |= (1 << TOIE0); // Timer 0 Overflow Interrupt

  sei(); // Globale Interrupts einschalten

  while(1)
  {
    wdt_reset(); // Watchdog Timer zurücksetzen
    status_LED(2); // Status LED auf Grün (Alles OK)
    
    accelx = ReadChannel(0); // 10Bit - Messwert der x-Achse 
    accely = ReadChannel(1); // 10Bit - Messwert der y-Achse
    accelz = ReadChannel(2); // 10Bit - Messwert der z-Achse

    ADC_angle = Angle(accelx, accely, accelz); // Winkel in Variable verstauen
  
    PrintNumber(ADC_angle); // Wert ausgeben, ganzzahlig nicht negativ
    USART_puts(itoa(ADC_angle, CSV, 10)); // Daten auch seriell rausschicken
  }

  return 0; // Wird niemals erreicht
}



Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> result |= ADCL + (ADCH << 8);
> result += ADCL + (ADCH << 8);
Liest Du auch, was ich schreibe? Genau das ist Murks! Und Du hast grad 
noch behauptet, dass Du es jetzt in der richtigen Reihenfolge machst. 
Lies noch mal mein Posting von 15:03 und denk drüber nach! Außerdem 
gehört Code von dem Umfang in den Anhang.

Autor: lkmiller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und was steht da:
[/c]
uint8_t MyADCSRA;
[c]
Wo ist das jetzt volatile?

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, mein Fehler, allerdings hat das nichts an dem Hauptproblem geändert 
wenn ich jetzt die zeilen
result |= ADCL + (ADCH << 8); 

in
result = ADCL;
result += (ADCH << 8); 

umänder funktioniert der Code auch nicht mehr wenn ich die Optimierung 
austelle.
Ok das volatile habe ich drangehängt allerdings bringt, das keine 
veränderung, ist aber ja an dieser stelle richtig da ich MyADCSRA ja 
global in der Interupt Routine und in den Funktionen nutze.
Vllt. ist da ein grundlegender Fehler mit den Interupt Routinen bin mir 
nicht so ganz sicher wo ein cli() und wo ein sei() hinkommen muss.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ISR(USART_UDRE_vect) 
...
    UDR = data; // Daten senden
    UDR = 0x0A;
Da würde ich auch noch mal drüber nachdenken. ;-)

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Ok, mein Fehler, allerdings hat das nichts an dem Hauptproblem geändert
> wenn ich jetzt die zeilen
>
>
> result |= ADCL + (ADCH << 8);
> 
>
> in
>
>
> result = ADCL;
> result += (ADCH << 8);
> 
>
> umänder funktioniert der Code auch nicht mehr wenn ich die Optimierung
> austelle.
Himmelarsch, ist das denn so schwer zu begreifen?
result += ADCW;
Und sonst nichts!

> Vllt. ist da ein grundlegender Fehler mit den Interupt Routinen bin mir
> nicht so ganz sicher wo ein cli() und wo ein sei() hinkommen muss.
Nirgends. Das ist schon richtig so.

Dringende Literaturempfehlung:
AVR-GCC-Tutorial

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Himmelarsch, ist das denn so schwer zu begreifen?
>result += ADCW;
>Und sonst nichts!

Doch. result läuft irgendwann über. Dieses += kommt doch noch vom 
getrennten Auslesen.

result = ADCW;

MW

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael Wilhelm wrote:
>>Himmelarsch, ist das denn so schwer zu begreifen?
>>result += ADCW;
>>Und sonst nichts!
>
> Doch. result läuft irgendwann über. Dieses += kommt doch noch vom
> getrennten Auslesen.
>
> result = ADCW;
An einer Stelle braucht er es aber für die Mittelwertbildung mit dem 
"+=". Und das "|=" bei dem ersten Einlesen ist sowieso Käse, weil das 
nur ein Dummy-Read ist und result sofort danach eh wieder gelöscht 
wird. Da kann tatsächlich ein einfaches "=" hin.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>...problem besteht aber weiterhin...
>...nichts am Hauptproblem geändert...

Nur mal angenommen, wir wissen gar nicht so richtig, was das Problem 
ist....

Dann können wir lang an irgendwelchen AD-Registern und volatile 
Deklarationen rumdiskutieren. Oder habe ich was überlesen?

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übrigens, nach wie vor stehen im ADMUX noch ein paar Steuerbits 
zusätzlich zu den Multiplexer-Bits, die jedes Mal beim Kanal-Wählen zu 
Null gesetzt werden. Hast Du tatsächlich ne externe Referenz?

Außerdem, was soll eigentlich der Quatsch da:
> MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten  
>
> while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
?

Umständlicher geht es wohl kaum. Wenn Du schon in einer Schleife 
einliest, dann schalt den Interrupt komplett aus und frag entweder das 
ADSC oder das ADIF direkt ab. Beim Dummy-Read ist es doch genau so 
gemacht, warum also dieser Umstand?

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So lese habe das jetzt über result = ADCW gemacht, ich dachte das das 
nicht funzt deswegen hatte ich es in der vergangenheit immer einzeln 
über die ADCL und ADCH gemacht. Nachdem ich das geändert habe funzt es 
auch mit der Optimierung also danke am Johannes für seine Geduld.

Jetzt noch was zu den Fragen, ja ich benutze eine externe Referenzspg.,
zu MyADCSRA, habe auch schon versucht das durch das normale ADCSRA zu 
ersetzen mit dem ergebnis das das Programm dann gar nicht mehr lief.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Jetzt noch was zu den Fragen, ja ich benutze eine externe Referenzspg.,
> zu MyADCSRA, habe auch schon versucht das durch das normale ADCSRA zu
> ersetzen mit dem ergebnis das das Programm dann gar nicht mehr lief.
Du kannst entweder das Flag per Software abfragen oder das ganze per 
Interrupt machen, aber nicht beides gleichzeitig! Wenn der Interrupt 
Handler aufgerufen wird, dann wird das Flag automatisch gelöscht. Das 
Hauptprogramm kann dann lange warten. Warum machst Du es nicht einfach 
so, wie Du es bei der ersten Wandlung doch schon korrekt geschrieben 
hast, indem Du ADSC abfragst?

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm, habe jetzt beschriebene stelle so umgeändert wie du es gesagt hast:
for(i = 0; i < 64; i++)
{
    ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
    
    while ( ADCSRA & (1 << ADSC)) 
    {

    }
    result += ADCW; // Nach jeder Wandlung Ergebnisse aufaddieren
}

das funktioniert auch super, allerdings bin ich jetzt ein wenig confused 
wegen meine ISR, die ich wie folgt geändert habe:
ISR(ADC_vect)
{
    ADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
}

muss die ISR überhaupt noch vorhanden sein, da diese ja aufgerufen wird 
wenn ADIF gesetzt ist, was ja jetzt nicht mehr der fall sein kann, habe 
glaube ich ein kleines verständnis Problem, wenn ich sie allerdings ganz 
weglasse funzt es ja auch nicht?

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Hmm, habe jetzt beschriebene stelle so umgeändert wie du es gesagt hast:
>
>
> for(i = 0; i < 64; i++)
> {
>     ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
> 
>     while ( ADCSRA & (1 << ADSC))
>     {
> 
>     }
>     result += ADCW; // Nach jeder Wandlung Ergebnisse aufaddieren
> }
> 
So weit, so gut...

>
> das funktioniert auch super, allerdings bin ich jetzt ein wenig confused
> wegen meine ISR, die ich wie folgt geändert habe:
>
>
> ISR(ADC_vect)
> {
>     ADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
> }
> 
>
> muss die ISR überhaupt noch vorhanden sein, da diese ja aufgerufen wird
> wenn ADIF gesetzt ist, was ja jetzt nicht mehr der fall sein kann,
Wieso sollte das nicht mehr der Fall sein? Das Flag wird immer 
gesetzt, wenn eine Wandlung beendet wird, egal, ob der Interrupt 
freigegeben ist oder nicht. Außerdem kann man ein Interrupt Flag nicht 
setzen, sondern nur löschen. Wenn Du das ADSC abfragst, was in Deinem 
Fall sinnvoll ist, dann deaktiviere den Interrupt, schmeiß den 
Interrupt Handler (ISR) weg und auch das ganze Trara mit dem myADCSRA.

> habe glaube ich ein kleines verständnis Problem, wenn ich sie
> allerdings ganz weglasse funzt es ja auch nicht?
Ein Interrupt Flag wird immer gesetzt, wenn das entsprechende 
Hardware-Ereignis auftritt, ganz unabhängig davon, ob der betreffende 
Interrupt freigegeben ist oder nicht. Dadurch ist es eben auch möglich, 
Hardware-Ereignisse ohne Interrupt abzufragen (Polling). Allerdings 
muss man, wenn man ein solches Flag abfragt, dieses nach der Abfrage 
auch löschen, weil das dann nicht automatisch durch die Hardware gemacht 
wird. Und Interrupt Flags werden beim AVR nicht durch Hineinschreiben 
einer 0 gelöscht, sondern mit einer 1. Das, was Du oben in Deiner ISR 
geschrieben hast, würde deshalb lediglich das Flag löschen (wenn es 
nicht vorher schon durch die Hardware gelöscht worden wäre).

Beim ADC macht Polling über das Interrupt Flag aber nicht so viel Sinn, 
weil es da einfacher über das ADSC-Bit geht. Das wird nämlich generell 
automatisch gelöscht, wenn die Wandlung abgeschlossen ist und muss 
nicht, wie das Flag, von Hand gelöscht werden.

Wenn man einen Interrupt freigibt, bedeutet das nur, dass die 
Bearbeitung des dazugehörenden Hardware-Ereignisses per Interrupt 
durchgeführt wird (m.a.W., dass das Auftreten des Hardware-Ereignisses 
zu einer Unterbrechung des normalen Programmablaufs führt). Und immer 
drauf achten, dass eine Interrupt-Freigabe aus zwei Stufen besteht: 
lokale Freigabe (über das jeweilige Enable-Bit, im Falle des ADC ist 
das ADIE) und globale Freigabe über das I-Bit im Statusregister SREG 
(in WINAVR-C über die Funktion "sei()"). Wenn ein Interrupt freigegeben 
ist und es keine ISR dazu gibt, führt das zum Reset ("Absturz") des µC, 
wenn das entsprechende Hardware-Ereignis auftritt.

Zu einem freigegebenen Interrupt gehört immer eine ISR, wenn man das 
Flag per Polling abfragen will, muss man den Interrupt über das lokale 
Enable-Bit deaktivieren, sonst knallt's.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.