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


von Daniel R. (zerrome)


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

von holger (Gast)


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);
        }

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Was passiert denn genau?

von Falk B. (falk)


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

von Frank B. (frank_b) Benutzerseite


Lesenswert?

@holger:
 delayms ist doch konstant.

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

lg, Frank

von holger (Gast)


Lesenswert?

>@holger:
> delayms ist doch konstant.

Ja, auch gerade gesehen :(

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

von Daniel R. (zerrome)


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...

von Frank B. (frank_b) Benutzerseite


Lesenswert?

...was ist mit den fuses ? richtigt gesetzt ?

von Daniel R. (zerrome)


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?

von Daniel R. (zerrome)


Angehängte Dateien:

Lesenswert?

Da das hochladen der Datei nicht geht, sehe ich mich gezwungen den 
genzen Code zu posten, sorry...
1
#include <avr/io.h>
2
#include <string.h>
3
#include <avr/interrupt.h> 
4
#include <stdlib.h>
5
6
#define F_CPU 16000000
7
#include <util/delay.h>
8
9
#define BAUD 4800l
10
11
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
12
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // reale baudrate
13
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // fehler in promille 
14
#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))
15
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
16
#endif
17
18
#define BUFFER_LEN 100    
19
20
#define delay1ms 1
21
22
void uputc(unsigned char);    //Zeichen in Ring-puffer (trozdem nur über uprints!!)
23
void uputs (char *s);      //String/Zeichen/Daten in ring-puffer
24
void uinit (void);        //usart initial 8n1,tx enable
25
void adcinit(void);        //adc an ,channel adc0 ,AVcc, single conversion
26
char* getAdc(void);        //adc wert auslesen usw.. rückgabe wert als string
27
void waitms (uint16_t);
28
29
volatile char buffer[BUFFER_LEN];    // Ring-Puffer für zu sendende Daten
30
volatile uint8_t writePointer=0;      //schreib zeiger
31
volatile uint8_t readPointer=0;      //lese zeiger
32
volatile uint8_t a=0;            //adc fertig flag
33
34
int main(){
35
  
36
  sei();          //interupts an
37
  DDRD&=~(1<<PD2);    //PD2 ausgang
38
  PORTD=(1<<PD2);    //PD2 an
39
  uinit();          //uart initial (8n1, usw.)
40
  adcinit();        //adc initial mit interrupt, single conversation, mux=adc0  
41
    
42
  uputs("Booting... \n");
43
44
  _delay_ms(10);    
45
  
46
  ADCSRA |=(1<<ADSC);  //wandlung nach init = dump wandlung
47
48
  while (1){      
49
    
50
    if(a==1){              //adc wandlung fertig
51
      uputs(getAdc());        //daten ausgeben/holen      
52
      ADCSRA |= (1<<ADSC);       //neue ADC-Wandlung 
53
      a=0;
54
      }  
55
    
56
    _delay_ms(10);      
57
    //waitms(100);
58
      
59
60
    uputs("bla..");  
61
      
62
  }
63
}
64
65
char* getAdc(void){    //adc wert
66
67
  char *tmp;        //für umwandlung double to string (dtostrf)  
68
  uint16_t adc=0;    //adc wert
69
70
  adc = ADCL;       
71
  adc += (ADCH<<8);         //adc daten register auslesen  L and H            
72
  dtostrf(adc*0.004883,3,3,tmp);  //umwandlung der daten (umrechnugn in volt= (5/1024)*adc)          
73
  return tmp;//dtostrf(adc*0.004883,3,3,NULL);//tmp;
74
}
75
76
77
ISR(ADC_vect){                  // ADC wandlung fertig
78
  a=1;                //wandlung fertig flag
79
}
80
81
82
ISR(USART_UDRE_vect){             // UDR ist leer ISR 
83
  if(writePointer!=readPointer){    //daten in puffer
84
    UDR=buffer[readPointer];      //daten in uart hw puffer schreiben
85
    readPointer++;    
86
    if(readPointer==BUFFER_LEN)readPointer=0;          
87
    }
88
89
  else  UCSRB |= (0<<UDRIE);        // ISR aus, keine daten in puffer    
90
}
91
92
93
uint8_t bufferCeck(void){          //checkt platz in buffer
94
95
  if((readPointer-writePointer)>0){      //noch platz
96
    return (readPointer-writePointer);    //der abstand
97
    }
98
  else return 1;                    //alle anderen zustände
99
100
}
101
102
103
void uputc(unsigned char c){        // Zeichen in ring-puffer --auch ein zeichen nur über uputs !!
104
  
105
    buffer[writePointer]=c;
106
    writePointer++;
107
    if(writePointer==BUFFER_LEN)writePointer=0;
108
  
109
}
110
111
112
void uputs (char *s){            //String/Zeichen in ring-puffer
113
  if(bufferCeck()>=1){            //prüft ob noch plazt
114
    while (*s){            // so lange *s != '\0'        
115
       uputc(*s);
116
        s++;
117
        }
118
    UCSRB |= (1<<UDRIE);      //schaltet uart-hw puffer flag ein
119
    }
120
}
121
122
123
void adcinit(void){              //adc an ,channel adc0 ,AVcc, single conversion
124
  ADMUX |= (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);//ref AVcc,mux0000=adc0
125
  ADCSRA |=(1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);//adc an,interupt an,single conversion,div factor 64
126
}
127
128
129
void uinit (void){              //init usart Tx, 8n1 ,UDRE interupt enable
130
  
131
   UCSRC |= (1<<URSEL)|(3<<UCSZ0)|(0<<USBS);    // URSEL = 1 dann wird UCSRC benutzt sonst UBRRH ; UCSZ0=data register- 8bit; USBS=1 stop bit 
132
133
   UBRRH = UBRR_VAL >> 8;                // baud rate hi & lo
134
  UBRRL=UBRR_VAL & 0xFF;
135
136
  UCSRB |= (1<<TXEN);//|(1<<UDRIE);            // UART TX einschalten,  data register empty interupt enable
137
}
138
139
void waitms (uint16_t ms){        //waitms bis max 65535 ms wegen uint16_t
140
  for(ms;ms>0;ms--)_delay_ms(delay1ms);
141
}


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

von Falk B. (falk)


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.

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

MFG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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!''.

von Daniel R. (zerrome)


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
1
for(; wms>0; wms--)_delay_ms(delay1ms);
geändert.
Wenn ich jetzt in der Eendlosschleife der main
1
waitms(15);
schreibe, macht der Controller einen totalen reset,
was sehr seltsam ist !

Bin ziemlich ratlos!

von Falk B. (falk)


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

von Daniel R. (zerrome)


Lesenswert?

Ich benutze KontrollerLab unter linux...da is mein Atmega8 
eingestellt...

von Daniel R. (zerrome)


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...
1
void waitms (uint16_t ms){    
2
  uint16_t mis=ms;
3
  for(;mis>0;mis--)_delay_ms(delay1ms);
4
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Daniel R. (zerrome)


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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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:
1
#include <stdint.h>
2
3
#define F_CPU 16000000UL
4
#include <util/delay.h>
5
6
void
7
delay_ms(uint16_t ms) {
8
        while (--ms) _delay_ms(1);
9
}

Generierter Code:
1
.global delay_ms
2
        .type   delay_ms, @function
3
delay_ms:
4
/* prologue: frame size=0 */
5
/* prologue end (size=0) */
6
        movw r18,r24
7
        rjmp .L2
8
.L3:
9
        ldi r24,lo8(4000)
10
        ldi r25,hi8(4000)
11
/* #APP */
12
        1: sbiw r24,1
13
        brne 1b
14
/* #NOAPP */
15
.L2:
16
        subi r18,lo8(-(-1))
17
        sbci r19,hi8(-(-1))
18
        brne .L3
19
/* epilogue: frame size=0 */
20
        ret
21
/* epilogue end (size=1) */
22
/* function delay_ms size 14 (13) */

Und mit:
1
#include <stdint.h>
2
3
#define F_CPU 16000000UL
4
#include <util/delay.h>
5
6
void
7
delay_ms(uint16_t ms) {
8
        uint16_t copy = ms;
9
        while (--copy) _delay_ms(1);
10
}
ergibt sich:
1
.global delay_ms
2
        .type   delay_ms, @function
3
delay_ms:
4
/* prologue: frame size=0 */
5
/* prologue end (size=0) */
6
        movw r18,r24
7
        rjmp .L2
8
.L3:
9
        ldi r24,lo8(4000)
10
        ldi r25,hi8(4000)
11
/* #APP */
12
        1: sbiw r24,1
13
        brne 1b
14
/* #NOAPP */
15
.L2:
16
        subi r18,lo8(-(-1))
17
        sbci r19,hi8(-(-1))
18
        brne .L3
19
/* epilogue: frame size=0 */
20
        ret
21
/* epilogue end (size=1) */
22
/* function delay_ms size 14 (13) */

...auffällig dasselbe.

> Kann man irgendwie rausfinden wie voll der Ram ist?

avr-size

von Daniel R. (zerrome)


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...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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. ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.