Forum: Mikrocontroller und Digitale Elektronik AD-Wandler Problem mit Datentypen


von Alex S. (alex747)


Lesenswert?

Hallo Zusammen,

habe folgendes Problem. Ich möchte eine LED über ein Potentiometer 
dimmen.
Meine vorgehensweise besteht darin in einer Schleife den aktuellen ADC 
Wert (Potistellung) zu speichern und diesen als Duty-Cycle für die PWM 
zu verwenden. Der ADC Wert liegt im Bereich 0...1023. Diesen will ich 
normieren damit er im Bereich 0...100 liegt. Hier erstmal der gesamte 
Code:
1
#define clearbit(P,BIT)  ((P) &= ~(1<<(BIT)))
2
#define setbit(P,BIT) ((P) |= (1<<(BIT)))
3
4
int main(void)
5
{
6
  unsigned  int adc_value;
7
  
8
  setbit(DDRB,0);  
9
  setbit(PORTB,0);
10
11
  ADMUX = 0b01000000;
12
    ADCSRA  = 0b11100100;
13
14
    uint8_t pwm_soll = 100; // gewünschter Dimmerwert 0..100
15
    uint8_t pwm_phase = 0; // Laufwert der Schleife 0..100
16
 
17
    setbit(DDRB, PB0);  // PORTB PB0 als Ausgang
18
19
    while( 1 )
20
    {
21
    adc_value = ADCL;
22
    adc_value |= (ADCH << 8);
23
24
    pwm_soll = adc_value*100/1023;      //Version 1
25
              //pwm_soll = adc_value*100.0/1023.0;  //Version 2
26
27
              if( pwm_soll == pwm_phase )
28
              {
29
                    setbit(PORTB ,PB0); // LED aus
30
              }
31
32
              pwm_phase++;
33
34
              if( pwm_phase == 100 )
35
              {
36
                     pwm_phase = 0;
37
                     clearbit(PORTB ,PB0); // LED an
38
              }
39
    }
40
41
  return 0;
42
43
}

Von der Logik her müsste die Software ja funktionieren
Das Problem findet bei dieser Zuweisung statt:

pwm_soll = adc_value*100/1023;

Nach längerem forschen habe ich wohl das Problem gefunden.
Falls adc_value den wert 655 überschreitet und mit 100 multipliziert 
wird, wird ein ergebnis von über 16 Bit erzielt. Das überschreitet den 
Bereich des unsigned Int. Der Rest wird einfach abgeschnitten. D.h. 
Falls ich mit meinem Poti diesen Wert überschreite fängt die LED wieder 
von 0 an zu dimmen. Danach habe ich es mit dieser Zuweisung versucht:

pwm_soll = adc_value*100.0/1023.0;

Das funktioniert aber leider überhaupt nicht. Die LED blinkt nur noch. 
Man sieht kein durchgängiges Leuchten mehr. Ist es dann nicht so, dass 
mit double Werten gerechnet wird? Oder wird dann mit float Werten 
gerechnet?
Ein anderer Versuch wäre noch:

pwm_soll = (adc_value*10)/102.3;

Bringt leider genauso wenig.

Ich hoffe ihr könnt mir helfen. Danke im Vorraus

mfg Alex

von Karl H. (kbuchegg)


Lesenswert?

Alex Stei schrieb:

> Nach längerem forschen habe ich wohl das Problem gefunden.
> Falls adc_value den wert 655 überschreitet und mit 100 multipliziert
> wird, wird ein ergebnis von über 16 Bit erzielt. Das überschreitet den
> Bereich des unsigned Int.

Völlig richtig.


> von 0 an zu dimmen. Danach habe ich es mit dieser Zuweisung versucht:
>
> pwm_soll = adc_value*100.0/1023.0;
>
> Das funktioniert aber leider überhaupt nicht. Die LED blinkt nur noch.
> Man sieht kein durchgängiges Leuchten mehr. Ist es dann nicht so, dass
> mit double Werten gerechnet wird? Oder wird dann mit float Werten
> gerechnet?
> Ein anderer Versuch wäre noch:
>
> pwm_soll = (adc_value*10)/102.3;
>
> Bringt leider genauso wenig.

Das funktioniert genauso wie die verhergehende Version.
Nur: du möchtest eigentlich nicht mit Gleitkomma arbeiten. Das belastet 
den µC und bringt dir nichts.

> Ich hoffe ihr könnt mir helfen. Danke im Vorraus

SIcher:
Das Problem wäre keines, wenn die Multiplikation nicht in 16 sondern in 
32 Bit ausgeführt wird.
Dazu genügt es, wenn einer der beiden Multiplikationspartner ein 32 Bit 
INteger ist.

zum Beispiel so

   pwm_soll = adc_value * 100L / 1023;

oder auch ganz banal

 unsigned long adc_value;

einer der beiden muss ein 32 Bit Datentyp sein. Entweder die Variable, 
oder die 100. Das reicht dann schon.

Und lies dir das mal durch, wenn du schon kein C Buch hast:
http://www.mikrocontroller.net/articles/FAQ#Datentypen_in_Operationen

von Peter II (Gast)


Lesenswert?

wenn du es wirklich so machen willst, musst du das ganze als 32bit 
rechnen:

pwm_soll = (uint32_t)adc_value*(uint32_t)100/(uint32_t)1023;

dann sollte es gehen.

Die Frage warum du auf 100 Normieren willst, dann deine PWM geht doch 
auch wieder von 0-255. Es macht damit überhaut keinen sinn erst auf 
0-100 zu gehen.


Dann könnte man es viel einfacher mache:

pwm_soll = adc_value / 4;

Deine 1023 ist auch ungünstig, rechne lieber mit 1024 das ist genauso 
richtig und rechnet sie viel einfacher für die µC.

von Alex S. (alex747)


Lesenswert?

Hallo,

das ist ja klasse wie schnell ihr geantwortet habt, danke :)
Habe eure Varianten ausprobiert und siehe da, die Dimmung funktioniert 
stufenlos. Das Problem ist das ein starkes flackern zu sehen ist. Wenn 
ich allerdingt durch 1024 teile, verschwindet das flackern auch. Es 
funktioniert also wunderbar. Anscheinend tut sich der uC wirklich schwer 
mit dem Rechnen der Fließkommazahlen.
@Peter II
Deinen Vorschlag habe ich dann auch ausprobiert. Funktioniert auch auf 
Anhieb. Gefällt mir gut.
Danke euch beiden nochmal vielmals ;)

von Hubert G. (hubertg)


Lesenswert?

Wenn du mit 256 für die PWM arbeitest, dann kannst du dir das rechnen 
gleich ersparen.
ADC linksbündig ausgeben und nur ADCH abfragen.
Dann kannst du anstelle von unsigned int unsigned char verwenden.

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.