m328 adc-Verständnisproblem

#7930637
Lesenswert?

hallo,

ich verstehe es mal wieder nicht.

an dieser Stelle - lcd_int4(adc_read()); // <== hier mehrere ADC-Auslesungen angezeigt

  • werden mehrere Messungen angezeigt und überschrieben. D.h. die Funktion 'lcd_int4' wird nur einmal ausgeführt da sonst hintereinander geschrieben würde - aber der Wert wird mehrfach angezeigt.

Wie kann das sein? Was mache ich falsch?

1
void main(void)
2
//
3
{
4

5
clock_prescale_set(clock_div_2);//8mhz
6

7
init_timer_smh();
8
led1_init;
9

10
//usart_init;
11
//USART0_init(9600);
12
//relais_init;
13
i2c_init();
14
lcd_init();
15
adc_init();
16

17
lcd_clear(RED);
18
lcd_int4(adc_read());  // <== hier eine ADC-Auslesung
19

20
//while(1);
21

22

23

24

25
timer_smh=0;
26

27
while(1)
28
{
29

30
//------------------- smh timer ------------------
31
  if (timer_smh>99)
32
  {
33
    sec=sec++;
34
    timer_smh=0;
35
      if(sec>59){
36
        sec=sec-60;  min=min+1;
37
        lcd_goto(1,4);//              
38
        lcd_int2(min);//
39
        lcd_write(":");
40
      if(min==60){
41
        min=0; hou++;
42
        if(hou==24)
43
        {
44
        hou=0;
45
        sub_day_start();
46
        
47
        }
48
        lcd_goto(1,1);//              
49
        lcd_int2(hou);//
50
        lcd_write(":");              
51
        }
52
      }
53
    lcd_goto(1,7);//              
54
    lcd_int2(sec);//
55
//    led1_tog;              
56
  }
57
//------------------------------------------------
58

59
if(sec==3 && min%6==0/**/  )//alle sechs Minuten auslesen
60
{
61

62
  if(min==0/**/)//jede Stunde -------
63
  {
64
  }
65

66

67
//wr_read_reg();
68
//sub_regelung();
69
}
70

71

72
if(sec%5==1)
73
{
74
  u8 i=0;
75
  lcd_goto(10+i++,1);
76
lcd_int4(i);// <== hier 1mal angezeigt
77
  
78
lcd_int4(adc_read()); // <== hier mehrere ADC-Auslesungen angezeigt
79

80
}
81

82

83

84

85
 }//while
86
}//main
#7930640
Lesenswert?

Deine Anfrage ist unklar formuliert. Gebe mal Beispiele der fehlerhaften Ausgabe und was du srattdessen erreichen willst.

Korrektes Einrücken hilft ungemein, den Quelltext zu verstehen.

Wo sind die Quelltexte von lcd_int4() und adc_read()? Ich will sie sehen, nicht nur anhand des Namens erraten, was sie tun sollten.

: Bearbeitet durch User
#7930655
Lesenswert?

Nemopuk schrieb:

Wo sind die Quelltexte von lcd_int4() und adc_read()? Ich will sie sehen, nicht nur anhand des Namens erraten, was sie tun sollten.

pardon gehören natürlich dazu:

1
void adc_init()
2
{
3
   // Set the ADC reference to AVcc
4
    ADMUX = (1 << REFS0);
5
    
6
    // Enable ADC and set prescaler to 128 (16 MHz / 128 = 125 kHz)
7
    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
8
}
9

10

11
#define ADCchannel 2
12

13
int16_t adc_read()
14
{
15
    // Select ADC channel (0-7) by setting the lower bits of ADMUX
16
    ADMUX = (ADMUX & 0xF8) | (ADCchannel & 0x07);
17
    
18
    // Start the conversion
19
    ADCSRA |= (1 << ADSC);
20
    
21
    // Wait for the conversion to complete
22
    while (ADCSRA & (1 << ADSC));
23
    
24
    // Return the ADC result
25
    return ADC;
26
}
27

28
 lcd_int4(i16 x){
29
  uint8_t dum[5]={' ',' ',' ','0','\0'};
30
  u8 neg=0;
31
if (x!=0)  
32
{
33
  
34
if (x<0){x=x*-1;neg=1;}
35

36
    if (x>999){
37
        dum[0]=(x/1000+48);
38
        x=x%1000;
39
    dum[1]='0';
40
    dum[2]='0';
41
    
42
        }
43
  
44
    if (x>99){
45
        dum[1]=(x/100+48);
46
        x=x%100;
47
    if(neg){neg=0;dum[0]='-';}
48
     dum[2]='0';
49
       }
50
    if (x>9){
51
        dum[2]=(x/10+48);
52
        x=x%10;
53
    if(neg){neg=0;dum[1]='-';}
54
        }
55
         dum[3]=(x+48);
56
     if(neg){dum[2]='-';}
57

58

59
}
60
    lcd_write(dum);
61
}

Die ADC-Wandlung wird auf dem Bild unten mehrfach ausgeführt und angezeigt - sollte aber nur ein mal ausgeführt und angezeigt werden.

Angehängte Dateien:
#7930683
Lesenswert?

Es ist zwar nicht dein Hauptproblem, ich wollte es aber trotzdem zeigen:

Deine Funktion lcd_int4() gibt positive Zahlen mit bis zu 4 Stellen aus. Negative Zahlen mit mehr als 3 Stellen führen zu einer fehlerhaften Ausgabe:

Der Test

1
int main(void) {
2
    lcd_int4(5);
3
    lcd_int4(50);
4
    lcd_int4(500);
5
    lcd_int4(5000);
6
    lcd_int4(-5);
7
    lcd_int4(-50);
8
    lcd_int4(-500);
9
    lcd_int4(-5000);
10
    return 0;
11
}

erzeugt folgende Ausgabe:

1
   5
2
  50
3
 500
4
5000
5
  -5
6
 -50
7
-500
8
50-0
#7930689
Lesenswert?

So sieht main() einigermaßen ordentlich formatiert aus:

1
void main(void) {
2
    
3
    clock_prescale_set(clock_div_2);  //8mhz  
4
    init_timer_smh();
5
    led1_init;
6
    //usart_init;
7
    //USART0_init(9600);
8
    //relais_init;
9
    i2c_init();
10
    lcd_init();
11
    adc_init();
12
    lcd_clear(RED);
13
    lcd_int4(adc_read());  // <== hier eine ADC-Auslesung
14
       
15
    timer_smh = 0;
16
    while (1) {
17
        
18
        //------------------- smh timer ------------------        
19
        if (timer_smh > 99) {
20
            
21
            sec = sec++;
22
            timer_smh = 0;
23
            
24
            if (sec > 59) {
25
                sec = sec - 60;
26
                min = min + 1;
27
                
28
                lcd_goto(1, 4); 
29
                lcd_int2(min);  
30
                lcd_write(":");
31
                
32
                if (min == 60) {
33
                    
34
                    min = 0;
35
                    hou++;
36
                    
37
                    if (hou == 24) {                        
38
                        hou = 0;                        
39
                        sub_day_start();
40
                    }
41
                    
42
                    lcd_goto(1, 1);                     
43
                    lcd_int2(hou);                     
44
                    lcd_write(":");
45
                }
46
            }
47
            
48
            lcd_goto(1, 7);             
49
            lcd_int2(sec);             
50
        }
51
        
52
        //------------------------------------------------       
53
        if (sec == 3 && min % 6 == 0) { //alle sechs Minuten auslesen
54
            
55
            if (min == 0)  //jede Stunde -------
56
            {
57
                // nichts?
58
            }
59
            
60
            //wr_read_reg();            
61
            //sub_regelung();
62
        }
63
        
64
        if (sec % 5 == 1)   {
65
            
66
            u8 i = 0;            
67
            lcd_goto(10 + i++, 1);
68
            
69
            lcd_int4(i);  // <== hier 1mal angezeigt
70
            lcd_int4(adc_read());  // <== hier mehrere ADC-Auslesungen angezeigt
71
        }
72
        
73
    }  //while
74
    
75
}  //main

Und auf den problematischen Teil reduziert:

1
void main(void) {
2
    
3
    while (1) {
4
        
5
        if (timer_smh > 99) {            
6
            sec = sec++;
7
            timer_smh = 0;
8
        }
9
                   
10
        if (sec % 5 == 1)   {
11
            
12
            u8 i = 0;            
13
            lcd_goto(10 + i++, 1);
14
            
15
            lcd_int4(i);  // <== hier 1mal angezeigt
16
            lcd_int4(adc_read());  // <== hier mehrere ADC-Auslesungen angezeigt
17
        }
18
        
19
    }  //while
20
    
21
}  //main

Hier fallen mir folgende Dinge auf:

Ist timer_smh als "volatile" und 8 Bit Integer deklariert? Wenn nicht, können Lesezugriffe im Hauptprogramm durch Schreibzugriffe in der Timer-ISR gestört werden und zu unerwartetem Verhalten führen. Allerdings nicht zu dem konkreten Verhalten, dass wir hier sehen. Dafür muss es noch einen anderen Grund geben.

lcd_goto(10 + i++

ergibt immer 10, weil i immer 0 ist.

lcd_int4(i);

gibt immer 1 aus, weil i immer 1 ist.

lcd_int4(adc_read());

Gibt mit Sicherheit nur eine ADC Messung aus. Denn etwas anderes kann diese Funtkion nicht tun.

Offenbar kommt die Ausgabe " 1 813" von diesen beiden Aufrufen.

Die unerwarte Ausgabe in der dritten Display-Zeile stimmt nicht mit dem Quellcode überein. Denn er enthält kein "lcd_goto(3,1)". Sie kann auch nicht von "lcd_int4()" kommen, weil diese Funktion immer 4 Ziffern rehtsbündig ausgibt.

Vermutlich ist der Fehler ganz woanders, in einem Stück Code, den du nicht gezeigt hast. Ich würde mal alle andere Display-Ausgaben auskommentieren um auszuschließen, dass sie stören.

: Bearbeitet durch User
#7930724
Lesenswert?

Nemopuk schrieb:

Die unerwarte Ausgabe in der dritten Display-Zeile stimmt nicht mit dem Quellcode überein.

stimmt - "lcd_goto(3,1)" hab ich nachträglich eingefügt weil es in der ersten Zeile sonst blöd aussieht.

lcd_goto(10 + i++ ergibt immer 10, weil i immer 0 ist.

stimmt auch, habe die Deklaration nach oben gezogen. Ergebnis bleibt. Stelle wird nur einmal aufgerufen.

Nemopuk schrieb:

Negative Zahlen mit mehr als 3 Stellen führen zu einer fehlerhaften Ausgabe:

Das ist bewusst in kauf genommen. Die Funktion dient zur Positionierung auf dem Display. Wenn größere negative Werte zu erwarten sind muss man das anders machen.

Nemopuk schrieb:

Allerdings nicht zu dem konkreten Verhalten, dass wir hier sehen. Dafür muss es noch einen anderen Grund geben.

Jedenfalls Dank für dein Bemühen. Es hilft schon weiter zu wissen, dass mein Unverständnis nicht ganz unberechtigt ist.

: Bearbeitet durch User
#7930738
Lesenswert?

Karl K. schrieb:

stimmt - "lcd_goto(3,1)" hab ich nachträglich eingefügt stimmt auch, habe die Deklaration nach oben gezogen

Wenn du hilfreiche Hilfe bekommen willst, solltest du deine Probleme ordentlich beschreiben. Wenn man sich Mühe gibt, erkennt man die Ursache beim verfassen der Problembeschreibung sogar oft selbst.

Ergebnis bleibt. Stelle wird nur einmal aufgerufen.

Damit kann ich nichts anfangen. Ist das jetzt eine gute oder eine schlechte Nachricht?

Nochmal von vorne: Zeige den kompletten Quelltext (ordentlich formatiert!), die dazugehörige Ausgabe, und was du stattdessen erwartest.

: Bearbeitet durch User
#7930855
Lesenswert?

Nemopuk schrieb:

Nochmal von vorne: Zeige den kompletten Quelltext (ordentlich formatiert!), die dazugehörige Ausgabe, und was du stattdessen erwartest.

ich werde den code noch mal komplett neu aufsetzen und dann hier einstellen.

Das Merkwürdige ist, dass die Messwerte überschrieben werden obwohl die Cursorposition bei lcd_write eigentlich mit jedem Wert fortgeschrieben wird:

1
void lcd_write_(uint8_t* str)
2
{
3
while(*str>31)
4
    {
5
        lcd_char_(*str++);
6
        curposx =curposx+ f_width;
7
        if(frame)curposx++;
8
    }
9
}
: Bearbeitet durch User
Beitrag #7930953 wurde von einem Moderator gelöscht.
Beitrag #7930961 wurde von einem Moderator gelöscht.
Beitrag #7930968 wurde von einem Moderator gelöscht.
Beitrag #7930985 wurde von einem Moderator gelöscht.
Beitrag #7931018 wurde von einem Moderator gelöscht.
Beitrag #7931021 wurde von einem Moderator gelöscht.
#7931396
Lesenswert?

Karl K. schrieb:

Die Klammer gehört nach unten

Wie meinst du das?

Ich meine, dass diese beiden Zeilen bereits in Beitrag "Re: m328 adc-Verständnisproblem" nur einmal pro Sekunde ausgeführt werden.

1
        if (timer_smh > 99) {
2

3
            sec = sec++;
4
            timer_smh = 0;
5

6
            if (sec > 59) {
7
               ...
8
            }
9

10
            lcd_goto(1, 7);             
11
            lcd_int2(sec);             
12
        }

Außerdem erklärt das nicht, wieso Ausgaben fälschlicherweise in Zeile 3 landen. lcd_goto(1, 7) geht ja nach Zeile 1.

Zeige mal den vorher/nachher Vergleich mit etwas mehr Kontext. Gerne auch als zwei komplette Dateien (vorher.c, nachher.c) im Anhang.

: Bearbeitet durch User
#7931432
Lesenswert?

Nemopuk schrieb:

Zeige mal den vorher/nachher Vergleich mit etwas mehr Kontext.

1
void main(void)
2
//
3
{
4
u8 i=0;
5
clock_prescale_set(clock_div_2);//8mhz
6

7
init_timer_smh();
8
led1_init;
9
i2c_init();
10
lcd_init();
11
adc_init();
12
lcd_clear(RED);
13

14
while(1)
15
{
16

17
//------------------- smh timer ------------------
18
  if (timer_smh>99)
19
    {
20
      sec=sec+timer_smh/100;
21
      timer_smh=timer_smh%100;
22
      if(sec>59){
23
        sec=sec-60;  min=min+1;
24
        lcd_goto(1,4);//              
25
        lcd_int2(min);//
26
        lcd_write(":");
27
      if(min==60){
28
        min=0; hou++;
29
        if(hou==24)
30
        {
31
        hou=0;
32
        sub_day_start();
33
        
34
        }
35
        lcd_goto(1,1);//              
36
        lcd_int2(hou);//
37
        lcd_write(":");              
38
        }
39
      }
40
    lcd_goto(1,7);//              
41
    lcd_int2(sec);//
42
//    led1_tog;              
43
//} <== Klammer vorher  
44
//------------------------------------------------
45

46
  if(sec==3 && min%6==0/**/  )//alle sechs Minuten auslesen
47
  {
48
  }
49

50
  if(min==0/**/)//jede Stunde -------
51
  {
52
  }
53

54

55

56
  if(sec%5==1)
57
  {
58
    volt=measure_adc();
59
    lcd_goto(10,1);
60
    lcd_int_10(volt);
61
    lcd_write(" V");
62
  }
63

64
  }//sec <== Klammer richtig
65
  }//while
66
}//main

Die while-Schleife soll nur einmal je Sekunde durchlaufen werden. Deswegen war die Klammer falsch.

#7931920
Lesenswert?

Nemopuk schrieb:

falsche Ausgabe in Zeile 3

oben: Nemopuk schrieb:

Die unerwarte Ausgabe in der dritten Display-Zeile stimmt nicht mit dem Quellcode überein.

stimmt - "lcd_goto(3,1)" hab ich nachträglich eingefügt weil es in der ersten Zeile sonst blöd aussieht.

Nochmals Dank für deine kritischen Nachfragen. Man denkt darüber nach und das hilft weiter. So z.B. neue Anzeige-Funktion mit Komma:

1
void lcd_int_10(i16 x){
2
  uint8_t dum[5]={' ','0',',','0','\0'};
3
  uint8_t keinkomma=0;
4
  u8 neg=0;
5
if (x!=0)  
6
{
7
  
8
if (x<0){x=x*-1;neg=1;}
9

10
    if (x>999){
11
        dum[0]=(x/1000+48);
12
        x=x%1000;
13
    dum[1]='0';
14
    dum[2]='0';
15
    keinkomma=1;
16
    
17
        }
18
  
19
    if (x>99){
20
    if(keinkomma==1)
21
      {
22
        dum[1]=(x/100+48);
23
        dum[2]='0';
24
      }
25
      else
26
      {
27
        dum[0]=(x/100+48);
28
        dum[1]='0';
29
      }
30
      
31
        x=x%100;
32
    if(neg){neg=0;dum[0]='-';}
33
       }
34

35
    if (x>9){
36
  
37
    if(keinkomma==1)
38
      {
39
        dum[2]=(x/10+48);
40
      }
41
    else
42
      {
43
        dum[1]=(x/10+48);
44
      }
45
      
46
        x=x%10;
47
    if(neg){neg=0;dum[1]='-';}
48
        }
49
    
50
         dum[3]=(x+48);
51
     if(neg){dum[2]='-';}
52

53

54
}
55
    lcd_write(dum);
56
}
(Firma: Gast) #7931987
Lesenswert?

Ich hätte noch mehr zu sagen.... Was heißt dum, und warum dum?

Wieso ist dum kein C String, obwohl es doch ganz offenbar einer sein soll?

Karl K. schrieb:

x=x*-1

OK, nicht wichtig, aber warum nicht so: x=-x; oder: x=x%100; zu x%=100;

Karl K. schrieb:

i16

Was ist das, und vor allen Dingen, warum ist das?

Für mich wäre an der Stelle der Punkt gekommen, wo ich über sprintf oder seine Brüder nachgedacht hätte, auch wenn es 2k Flash kostet.

: Bearbeitet durch User
#7932024
Lesenswert?

So ist die Formatierung und ein paar Grundlagen OK. Die Funktion ist aber immer noch fehlerhaft. Und wozu soll ein Komma bei Integerzahlen gut sein?

1
void lcd_int_10(i16 x) {
2
    uint8_t dum[5]= " 0,0";
3
    uint8_t keinkomma=0;
4
    u8 neg=0;
5
  
6
    if (x<0) {
7
        x = -x;
8
        neg = 1;
9
    }
10
    if (x>999) {
11
        dum[0] = x/1000+'0';
12
        x=x%1000;
13
        dum[1] = '0';
14
        dum[2] = '0';
15
        keinkomma = 1;
16
    }
17
    if (x>99) {
18
        if(keinkomma) {
19
            dum[1] = x/100 + '0';
20
            dum[2] = '0';
21
        } else {
22
            dum[0] = x/100 + '0';
23
            dum[1] = '0';
24
        }
25
        x=x%100;
26
        if(neg){neg=0;dum[0]='-';}
27
    }
28
    if (x>9) {
29
        if(keinkomma) {
30
            dum[2] = x/10 + '0';
31
        } else {
32
            dum[1] = x/10 + '0';
33
        }      
34
        x=x%10;
35
        if(neg) { neg=0; dum[1]='-';}
36
    }
37
    dum[3]=(x+'0');
38
    if(neg) { dum[2]='-';}
39
    lcd_write(dum);
40
}
#7932115
Lesenswert?

Karl K. schrieb:

gehört schon - gebraucht noch nicht.

Wenn du mal im Detail beschreibst was deine Funktion am LCD machen soll (ich kann es nicht, will es auch nicht verstehen) dann könnte man (jemand hier im Forum) dir mit ein paar "Pinselstrichen" eine vernünftige Funktion mit der Anwendung von sprintf(...) schreiben.

Ich nehme an dass du das nicht alleine schaffst, denn schon der Titel deines Threads hat nichts mit dem zu tun was du wirklich an Problemen hast. Jedenfalls hat das alles (zuerst mal) nichts mit dem ADC zu tun.

#7932143
Lesenswert?

Peter D. schrieb:

#include <stdio.h> #include <stdint.h> void lcd_int4(int16_t val) { char s[] = "Err "; if ( val >= -999 && val <= 9999 ) sprintf(s, "%4d", val); lcd_write(s); }

Schlechter Stil. Bei sowas kann man sich bei C immer wieder leicht in den Fuß schießen. Entweder die Einzelanweisung hinter if() in der gleichen Zeile oder IMMER mit Klammern!

1
    if ( val >= -999 && val <= 9999 ) sprintf(s, "%4d", val);
1
    if ( val >= -999 && val <= 9999 ) {
2
        sprintf(s, "%4d", val);
3
    }
#7933224
Lesenswert?

Falk B. schrieb:

Und wozu soll ein Komma bei Integerzahlen gut sein?

Sorry ich hab noch nie mit Fließkomma gearbeitet und habe das auch im Prinzip nie gebraucht. Der ADC liefert bei 10bit 0-1023. Spannungsmessung Genauigkeit ist 0,1 Volt.

Die Darstellung auf dem lcd muss formatiert sein - sonst sieht es blöd aus. Also immer 4Stellen für eine Zahl.

Die Funktion lcd_int_10() kann auf 4Stellen also Werte von +9999 bis -999 darstellen davon den Zahlenbereich 99,9 bis -9,9 als Dezimalwert mit Komma. Im Prinzip ist das der Sinn meiner Funktion wobei ihr ganz richtig bemerkt habt, dass die Funktion noch nicht ganz fertig ist weil die Anzeige von negativen Zahlen nicht funktioniert und die Ausgaben von Zahlen >99,9 auch noch nicht implementiert ist. (ich muss nur Spannungen von 0 - 60V auswerten - brauch ich also noch nicht)

Ich fand die Idee gut für Dezimalzahlen aus der ADC-Spannungsmessung eine eigene Funktion zu schreiben statt bisher lcd_int2(x/10); lcd_write(","); lcd_int(x%10);

Aber irgendwie versteht mich mal wieder keiner.

#7933228
Lesenswert?

Karl K. schrieb:

Sorry ich hab noch nie mit Fließkomma gearbeitet und habe das auch im Prinzip nie gebraucht. Der ADC liefert bei 10bit 0-1023. Spannungsmessung Genauigkeit ist 0,1 Volt.

Aha, also Festkommaarithmetik. Die ist aus deiner Funktion aber nicht so recht ablesbar.

Aber irgendwie versteht mich mal wieder keiner.

Das könnte an deinen Informationen liegen, welche du nicht lieferst.

#7933247
Lesenswert?

Karl K. schrieb:

Aber irgendwie versteht mich mal wieder keiner.

denn mache es doch richtig, uint16 reicht doch bis 6000centiVolt

Spannungsteiler 0-60V Kennlinie aufnehmen und Steigung m und Konstante b ermitteln mit der Geradengleichung y = m * x + b

hinterher Komma an der richtigen Stelle einsetzen per Stringmanipulation, ganz ohne float.

: Bearbeitet durch User
#7933252
Lesenswert?

Joachim B. schrieb:

hinterher Komma an der richtigen Stelle einsetzen per Stringmanipulation, ganz ohne float.

Man kann auch float Zahlen darstellen ganz ohne float zu benutzen. Karl hat es im Ansatz schon gezeigt.

Karl K. schrieb:

lcd_int2(x/10); lcd_write(","); lcd_int(x%10);

Nur den letzten Schritt, dies vernünftig in einer Funktion zu vereinen, das scheint für ihn zu schwierig zu sein.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren