mikrocontroller.net

Forum: Compiler & IDEs delay Problem mit delay.h und eigener schleife


Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich benutze die util/delay.h in meinem Code.

Sieht so aus:

    #define delayms 1

    void waitms (uint16_t ms){
        for(ms;ms>0;ms--)_delay_ms(delayms);
        }

Aufruf im Programm:

    waitms(100);


Wieso verdammt funktioniert die waitms Schleife nicht richtig?!?
Wenn ich nur z.B:_delay_ms(10); benutze geht es ja auch...(im zulässigen 
rahmen von 16mhz F_CPU)

Ich verstehe nicht wie es sein kann, dass ich nicht einfach 100 mal eine 
milli Sekunde warten kann. 1 milli Sekunde wird ja voll von dem Makro in 
der delay.h abgedeckt...

Hat da vielleicht einer eien Tipp?

Danke schonmal

Grüße Daniel

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>_delay_ms(delayms);

_delay_ms() funktioniert nur mit Konstanten.
Mit Variablen gibt es unangenehme Nebeneffekte.

Nur so geht das:

    void waitms (uint16_t ms){
        for(ms;ms>0;ms--)_delay_ms(1);
        }

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

Bewertung
0 lesenswert
nicht lesenswert
Was passiert denn genau?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ holger (Gast)

>_delay_ms() funktioniert nur mit Konstanten.
>Mit Variablen gibt es unangenehme Nebeneffekte.

Guten Morgen. delayms ist eine Konstante über #define.
Das Problem liegt woanders. Vollständiger Quelltext wäre hilfreich.

MFG
Falk

Autor: Frank B_. (frank_b) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@holger:
 delayms ist doch konstant.

Schliesse mich der Frage meine Vorredners an: Was funktioniert nicht ?

lg, Frank

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>@holger:
> delayms ist doch konstant.

Ja, auch gerade gesehen :(

F_CPU im makefile oder vor #include <util/delay.h> definiert ?

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So definiere ich F_CPU

#define F_CPU 16000000
#include <util/delay.h>

Quelltext als Anhang.

Äh wieso kann ich keine zip Datei anhängen?!?

Mann, klappt auch garnix grad !

Na ja, also der ganze Controller bleibt irgedwie hängen, als wenn das 
delay einfach mal so 3-10 Sekunden braucht, sehr seltsam...

Autor: Frank B_. (frank_b) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...was ist mit den fuses ? richtigt gesetzt ?

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also denke schon,
da wenn ich z.B. _delay_ms(10); benutze,
alles funktioniert, der ADC der USART und die Ports...

Also eigentlich sollte der Code doch funktionieren oder?

Autor: Daniel R. (zerrome)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Da das hochladen der Datei nicht geht, sehe ich mich gezwungen den 
genzen Code zu posten, sorry...

#include <avr/io.h>
#include <string.h>
#include <avr/interrupt.h> 
#include <stdlib.h>

#define F_CPU 16000000
#include <util/delay.h>

#define BAUD 4800l

#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // reale baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // fehler in promille 
#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
#endif

#define BUFFER_LEN 100    

#define delay1ms 1

void uputc(unsigned char);    //Zeichen in Ring-puffer (trozdem nur über uprints!!)
void uputs (char *s);      //String/Zeichen/Daten in ring-puffer
void uinit (void);        //usart initial 8n1,tx enable
void adcinit(void);        //adc an ,channel adc0 ,AVcc, single conversion
char* getAdc(void);        //adc wert auslesen usw.. rückgabe wert als string
void waitms (uint16_t);

volatile char buffer[BUFFER_LEN];    // Ring-Puffer für zu sendende Daten
volatile uint8_t writePointer=0;      //schreib zeiger
volatile uint8_t readPointer=0;      //lese zeiger
volatile uint8_t a=0;            //adc fertig flag

int main(){
  
  sei();          //interupts an
  DDRD&=~(1<<PD2);    //PD2 ausgang
  PORTD=(1<<PD2);    //PD2 an
  uinit();          //uart initial (8n1, usw.)
  adcinit();        //adc initial mit interrupt, single conversation, mux=adc0  
    
  uputs("Booting... \n");

  _delay_ms(10);    
  
  ADCSRA |=(1<<ADSC);  //wandlung nach init = dump wandlung

  while (1){      
    
    if(a==1){              //adc wandlung fertig
      uputs(getAdc());        //daten ausgeben/holen      
      ADCSRA |= (1<<ADSC);       //neue ADC-Wandlung 
      a=0;
      }  
    
    _delay_ms(10);      
    //waitms(100);
      

    uputs("bla..");  
      
  }
}

char* getAdc(void){    //adc wert

  char *tmp;        //für umwandlung double to string (dtostrf)  
  uint16_t adc=0;    //adc wert

  adc = ADCL;       
  adc += (ADCH<<8);         //adc daten register auslesen  L and H            
  dtostrf(adc*0.004883,3,3,tmp);  //umwandlung der daten (umrechnugn in volt= (5/1024)*adc)          
  return tmp;//dtostrf(adc*0.004883,3,3,NULL);//tmp;
}


ISR(ADC_vect){                  // ADC wandlung fertig
  a=1;                //wandlung fertig flag
}


ISR(USART_UDRE_vect){             // UDR ist leer ISR 
  if(writePointer!=readPointer){    //daten in puffer
    UDR=buffer[readPointer];      //daten in uart hw puffer schreiben
    readPointer++;    
    if(readPointer==BUFFER_LEN)readPointer=0;          
    }

  else  UCSRB |= (0<<UDRIE);        // ISR aus, keine daten in puffer    
}


uint8_t bufferCeck(void){          //checkt platz in buffer

  if((readPointer-writePointer)>0){      //noch platz
    return (readPointer-writePointer);    //der abstand
    }
  else return 1;                    //alle anderen zustände

}


void uputc(unsigned char c){        // Zeichen in ring-puffer --auch ein zeichen nur über uputs !!
  
    buffer[writePointer]=c;
    writePointer++;
    if(writePointer==BUFFER_LEN)writePointer=0;
  
}


void uputs (char *s){            //String/Zeichen in ring-puffer
  if(bufferCeck()>=1){            //prüft ob noch plazt
    while (*s){            // so lange *s != '\0'        
       uputc(*s);
        s++;
        }
    UCSRB |= (1<<UDRIE);      //schaltet uart-hw puffer flag ein
    }
}


void adcinit(void){              //adc an ,channel adc0 ,AVcc, single conversion
  ADMUX |= (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);//ref AVcc,mux0000=adc0
  ADCSRA |=(1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);//adc an,interupt an,single conversion,div factor 64
}


void uinit (void){              //init usart Tx, 8n1 ,UDRE interupt enable
  
   UCSRC |= (1<<URSEL)|(3<<UCSZ0)|(0<<USBS);    // URSEL = 1 dann wird UCSRC benutzt sonst UBRRH ; UCSZ0=data register- 8bit; USBS=1 stop bit 

   UBRRH = UBRR_VAL >> 8;                // baud rate hi & lo
  UBRRL=UBRR_VAL & 0xFF;

  UCSRB |= (1<<TXEN);//|(1<<UDRIE);            // UART TX einschalten,  data register empty interupt enable
}

void waitms (uint16_t ms){        //waitms bis max 65535 ms wegen uint16_t
  for(ms;ms>0;ms--)_delay_ms(delay1ms);
}



äh gut ok. Man kann nur Dateianhänge machen wenn man neue Posts macht, 
mit Bearbeiten geht das nicht...

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Daniel Rau (zerrome)

>Also eigentlich sollte der Code doch funktionieren oder?

Ein paar Anmerkungen.

sei() als ersten Befehl in main ist sehr gewagt. ZUERST muss man alles 
initialisieren, sei() kammt dann am Schluss! Kurz vor der Endlosschleife 
von main(). Ausserdem bin ich nicht ganz sicher, wie der Compiler das 
statement ms interpretiert. Die Variable Warteschleife im Tutorila ist 
anders. Nämlich so.

void waitms (uint16_t ms){        //waitms bis max 65535 ms wegen uint16_t
  for(; ms>0; ms--)_delay_ms(delay1ms);
  //  KEINE zusätzliche Initialisierung von ms
}

MFG
Falk

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

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:

> Ausserdem bin ich nicht ganz sicher, wie der Compiler das
> statement ms interpretiert.

Ich würde vermuten, dass er beim Einschalten aller Warnungen etwas
von sich gibt wie "Statement has no effect".  Stört aber auch nicht.

Daniel, wenn du schreibst, dass _delay_ms(10) funktioniert: hast du
die Zeit dabei auch nachgemessen?  Oder könnten das auch 160 ms
sein?

Ich fürchte einfach mal, dein AVR läuft noch mit 1 MHz und du hast
jeweils schon vor Ablauf von 16 Sekunden (ist eine verdammt lange
Zeit, wenn man auf etwas wartet ;-) kalte Füße bekommen und gesagt:
,,Geht nicht!''.

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
1.
also der AVR läuft auf jeden fall mit externem 16mhz Quarz, die fuses 
sind so gesetzt und der uart funktioniert, was für mich eigentlich auch 
ein Beweis dafür ist, dass die 16 mhz eingestellt sind (die Baudrate 
errechnet sich ja anhand des Taktes).

2.
nein hab die 10 ms nicht gemessen, da ich kein Oszi oder Frequenz-messer 
habe, sind mehr gefühlte 10 ms (160ms wären ja schon weniger als 10 hz 
das könnte man mit ner led sogar sehen, was eideutig nicht so ist)

3.
sei() an anderer Stelle macht schon mehr sinn,
wenn man mal drüber nachdenkt :)

4.
hab die waitms schleife mal auf
for(; wms>0; wms--)_delay_ms(delay1ms);
geändert.
Wenn ich jetzt in der Eendlosschleife der main
waitms(15);
schreibe, macht der Controller einen totalen reset,
was sehr seltsam ist !

Bin ziemlich ratlos!

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Daniel Rau (zerrome)

>schreibe, macht der Controller einen totalen reset,
>was sehr seltsam ist !

Hast du im AVR-Studio den richtigen Prozessortyp eingestellt? Das klingt 
nach Stackproblemen.

MfG
Falk

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich benutze KontrollerLab unter linux...da is mein Atmega8 
eingestellt...

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab grade was rumprobiert,
wenn ich in der waitms Schleife die übergebene Variable nochmal quasi 
lokal in der Funktion speichere macht der Controller keinen reset, 
kleiner Vortschritt...
void waitms (uint16_t ms){    
  uint16_t mis=ms;
  for(;mis>0;mis--)_delay_ms(delay1ms);
}

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

Bewertung
0 lesenswert
nicht lesenswert
Ich vermute, dein RAM ist einfach zu voll.

Das Runterzählen des Parameters (statt einer lokalen Variable) muss
auch funktionieren.  Ich staune sogar, dass der Compiler nicht
überhaupt gleichen Code aus beiden generiert.

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab mich da an eine lang vergangene c Programmier Stunde erinnert, es 
ist wegen was auch immer, wohl ein Unterschied ob die Variable übergeben 
wird oder übergeben wird und nochmal gespeichert (außer dem 
verschwendeten Platz)...

Kann man irgendwie rausfinden wie voll der Ram ist? Oder muss ich 
variablen zählen?

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

Bewertung
0 lesenswert
nicht lesenswert
Daniel Rau wrote:

> Hab mich da an eine lang vergangene c Programmier Stunde erinnert, es
> ist wegen was auch immer, wohl ein Unterschied ob die Variable übergeben
> wird oder übergeben wird und nochmal gespeichert (außer dem
> verschwendeten Platz)...

Nein, ein Funktionsparameter ist effektiv eine lokale Variable
innerhalb der Funktion.  Hier der Vergleich:
#include <stdint.h>

#define F_CPU 16000000UL
#include <util/delay.h>

void
delay_ms(uint16_t ms) {
        while (--ms) _delay_ms(1);
}

Generierter Code:
.global delay_ms
        .type   delay_ms, @function
delay_ms:
/* prologue: frame size=0 */
/* prologue end (size=0) */
        movw r18,r24
        rjmp .L2
.L3:
        ldi r24,lo8(4000)
        ldi r25,hi8(4000)
/* #APP */
        1: sbiw r24,1
        brne 1b
/* #NOAPP */
.L2:
        subi r18,lo8(-(-1))
        sbci r19,hi8(-(-1))
        brne .L3
/* epilogue: frame size=0 */
        ret
/* epilogue end (size=1) */
/* function delay_ms size 14 (13) */

Und mit:
#include <stdint.h>

#define F_CPU 16000000UL
#include <util/delay.h>

void
delay_ms(uint16_t ms) {
        uint16_t copy = ms;
        while (--copy) _delay_ms(1);
}
ergibt sich:
.global delay_ms
        .type   delay_ms, @function
delay_ms:
/* prologue: frame size=0 */
/* prologue end (size=0) */
        movw r18,r24
        rjmp .L2
.L3:
        ldi r24,lo8(4000)
        ldi r25,hi8(4000)
/* #APP */
        1: sbiw r24,1
        brne 1b
/* #NOAPP */
.L2:
        subi r18,lo8(-(-1))
        sbci r19,hi8(-(-1))
        brne .L3
/* epilogue: frame size=0 */
        ret
/* epilogue end (size=1) */
/* function delay_ms size 14 (13) */

...auffällig dasselbe.

> Kann man irgendwie rausfinden wie voll der Ram ist?

avr-size

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja ist gleich.

Danke für die Hilfe. Traurig das es nicht gefruchtet hat...

Ich werd jetzt mal sehn ob ich nicht einfach nen timer nehme...

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

Bewertung
0 lesenswert
nicht lesenswert
Daniel Rau wrote:

> Ich werd jetzt mal sehn ob ich nicht einfach nen timer nehme...

Das ist ultimativ sowieso die beste Methode, hilft aber auch nicht
gegen zu vollen RAM. ;-)

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.