m328 adc-Verständnisproblem

OP #7930637

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

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.

OP

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:

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

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.

OP

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.

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.

OP

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

Problem gelöst:

1
lcd_goto(1,7);//              
2
lcd_int2(sec);//

// led1_tog;
} <=== //------------------------------------------------

Die Klammer gehört nach unten damit die Schleife nur einmal je Sekunde durchlaufen wird. Eigentlich ganz einfach.

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.

OP

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.

OP

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)

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.

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
}

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.

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
    }
OP

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.

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.

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.

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

Eine eigene Frage? Dieser Thread ist über ein Jahr alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.