Poti Wert zu PWM Signal verarbeiten

Gast #2898281
Lesenswert?

Hallo Leute,

ich habe hier so ein schickes atmega8 Board, welches ich als 
"Testobjekt" für ein anderes Projekt nutze. In diesem müssen 3 
Beschleunigungssensoren ausgelesen werden und aus den Daten 3 
verschiedene PWM Signale erzeugt werden, um die einzelnen Pins von 
RGB-LEDs zu dimmen und so ein Farbwechsel beim Drehen der Apparatur 
(wird ein Würfel mit LEDs an der Außenwand) zu ermöglichen.
Da ich leider nicht an die Sensoren komme (noch nicht) muss ich mir mit 
dem Potentiometer auf dem Board, sowie dem Temperatursensor behelfen und 
diese Testweise auslesen, um das dann auch bei den anderen Sensoren 
hinzubekommen.

Und jetzt kommt mein Problem:
Das erzeugen des PWM-Signals erfolgt durch software, d.h. ich lasse 
einen Timer hochzählen und wenn er den Wert, der am Poti gelesen wird, 
erreicht, wird die LED angeschaltet, ansonsten ist sie aus. Soviel zur 
Theorie, aber schon wenn ich eine feste PWM-Auslöse Größe vorgebe, 
klappt das nicht. Hier erstmal der Code:
1
# define F_CPU 8000000UL
2
# include <avr/io.h>
3
# include <avr/interrupt.h>
4
# include <util/delay.h>
5

6
unsigned int sensorWert1 = 512;
7
unsigned int sensorWert2 = 0;
8
unsigned int sensorWert3 = 0;
9

10
volatile double pwm_steps;
11

12
void ADC_Init()
13
{
14
  ADCSRA |= (1<<ADEN);  //Enabling AD-Converter
15
  //ADCSRA |= (1<<ADFR);  //Setting Free-Running Mode
16
  ADCSRA |= (1<<ADIE);  //Enabling ADC Interrupt
17
  ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); //Setting Prescaler to 128
18
  ADCSRA |= (1<<ADSC);  //Start conversions
19

20
  //Setting ADC Input Pin to ADC0
21
  ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
22

23

24
  //Setting reference to Avcc
25
  ADMUX = (1<<REFS0);
26
}
27

28
void ADC_SwitchChannel(int channel)
29
{
30
    if(channel == 0)
31
    {
32
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
33
    }
34
    else if(channel == 1)
35
    {
36
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1));
37
      ADMUX |= (1<<MUX0);
38
    }
39
    else if(channel == 2)
40
    {
41
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX0));
42
      ADMUX |= (1<<MUX1);
43
    }
44
}
45

46

47
void saveAnalogWerte()
48
{
49
  ADC_SwitchChannel(0);
50
  ADCSRA |= (1<<ADSC);
51
  while(!(ADCSRA & (1<<ADSC)))
52
  {
53
  }
54
  sensorWert1 = ADCW;
55
  
56
//   ADC_SwitchChannel(1);
57
//   ADCSRA |= (1<<ADSC);
58
//   while(!(ADCSRA & (1<<ADSC)))
59
//   {
60
//   }
61
//   sensorWert2 = ADCW;
62
    
63
//   ADC_SwitchChannel(2);
64
//   ADCSRA |= (1<<ADSC);
65
//   while(!(ADCSRA & (1<<ADSC)))
66
//   {
67
//   }
68
//   sensorWert3 = ADCW;
69
}
70

71
ISR(TIMER0_OVF_vect)
72
{
73
  pwm_steps += 1;
74
}
75

76

77
int main (void)
78
{
79
  TCCR0 |= /*(1<<CS01) | */(1<<CS00);
80
  TIMSK |= (1<<TOIE0);
81
  sei();
82
  
83
  DDRB |= (1<<PB0) | (1<<PB1);
84
  DDRD |= (1<<PD5);
85
  PORTB |= (1<<PB0) | (1<<PB1);
86
  
87
  ADC_Init();
88

89
  while(1)
90
  {
91
//     saveAnalogWerte();
92
     
93
    if(pwm_steps >= sensorWert1)
94
    {
95
      cli();
96
      pwm_steps = 0;
97
      PORTB &= ~(1<<PB0);
98
      sei();
99
    }
100
    else
101
    {
102
      PORTB |= (1<<PB0);  
103
    }
104
  }
105
}

Das Ausgeklammerte wird später, sobald das erzeugen des pwms überhaupt 
funzt, hinzugefügt.

Wenn ich das laufen lasse und den Wert für die pwm-steps verändere (bspw 
=100) leuchtet die LED mit voller kraft, aber schon bei ca 80 Leuchtet 
sie gar nicht. Dazwischen blitzt sie nur am anfang kurz auf und bleibt 
dann dunkel. Wenn ich den Poti nutze ist etwas ähnliches zu beobachten. 
Bei ganz aufgedrehten leuchtet es stark, geht aber schon nach wenigen 
grad drehung vollkommen ohne übergang (also ohne dimmen) aus. Beim 
Manipulieren der Zahlen schaffe ich es höchstens noch ein Zwischenstatus 
reinzubauen, bei der die LED schwach blinkt, wobei alle Übergänge aber 
abrupt sind und nichts mit dimmen zu tun haben...

Kann mir bitte jemand helfen, ich verzweifel langsam an diesem Problem 
und hab schon das halbe Internet nach einer Lösung abgesucht.
Bitte schickt mir keine Verweise auf Grundlagentutorials, ich bin 
mindestens 20 davon durchgegangen (meine uC Programmierkenntnisse sind 
noch beschränkt), d.h. alles was ich bis jetzt noch nicht verstanden 
habe, werde ich auch beim 100sten Tut noch immer nicht verstehen.

Ich wäre euch unglaublich dankbar für eure Hilfe :))))
Angehängte Dateien:
(Firma: matzetronics) #2898338
Lesenswert?

Antonio schrieb:
> ADCSRA |= (1<<ADSC);  //Start conversions
>
>   //Setting ADC Input Pin to ADC0
>   ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
>
>
>   //Setting reference to Avcc
>   ADMUX = (1<<REFS0);

Schlechte Idee. Setze immer erst die MUX und REF, bevor du die Wandlung 
startest. Steht auch so im Datenblatt.

Antonio schrieb:
> volatile double pwm_steps;

double? Warum sollte das ein Fliesskommawert mit sogar doppelter 
Präzision sein? Meinst du vllt. 'long'? Das wäre dann ein 32-bit Wert, 
aber immer noch viel zu gross. Auch hier reicht dicke ein 'unsigned 
int'. Allerdings tuns Bytes hier genauso, du wirst eine in 256 Stufen 
gedimmte LED nicht von einer in 65536 Stufen gedimmten unterscheiden 
können.

Antonio schrieb:
> unsigned int sensorWert1 = 512;
> unsigned int sensorWert2 = 0;
> unsigned int sensorWert3 = 0;

Auch diese Variablen müssen 'volatile' werden, da sie im Hauptprogramm 
und in der Timer ISR angesprochen werden. Zumindest sensorWert1 im 
Moment.

Antonio schrieb:
> TCCR0 |= /*(1<<CS01) | */(1<<CS00);

Wie jetzt? Wenn du keinen Vorteiler setzt, wird der Timer für immer 
gestoppt sein. Kriegst du bei dieser Syntax keinen Kompilierfehler?
Gast #2898424
Lesenswert?

Erstmal danke für die schnellen Antworten!

Bezgl. Vorteiler:
Da hab ich ein bisschen experimentiert wie schnell er dann hochzählt, 
die dortige Konfiguration bedeutet laut Datenblatt: Kein Prescaler, d.h. 
aber nicht, dass er gestoppt ist (bei gesetztem CS00).

Den Rest werd ich ausprobieren und später nochmal ein Feedback geben, 
dankeschön nochmal :DD
Gast #2898486
Lesenswert?

So, ich hab jetzt den Code wie oben genannt verändert (also das PWM in 
die ISR gelegt und die conversion erst am ende von ADC_Init() 
gestartet), aber das problem liegt weiterhin vor.
Bei einem Wert von 2 für den sensorWert1 (übrigens auch volatile 
gesetzt) leuchtet es mit voller Stärke, aber schon bei 10 blinkt es nur 
kurz auf und ist dann dunkel... bei 5 auch noch volle kraft...

Den Prescaler hab ich auf "No prescaling" also CS00 gesetzt gelassen.

Sonst noch Lösungsvorschläge?
Gast #2898542
Lesenswert?

1
# define F_CPU 8000000UL
2
# include <avr/io.h>
3
# include <avr/interrupt.h>
4
# include <util/delay.h>
5

6
volatile unsigned int sensorWert1 = 9;
7
volatile unsigned int sensorWert2 = 0;
8
volatile unsigned int sensorWert3 = 0;
9

10
void ADC_Init()
11
{
12
  ADCSRA |= (1<<ADEN);  //Enabling AD-Converter
13
  //ADCSRA |= (1<<ADFR);  //Setting Free-Running Mode
14
  ADCSRA |= (1<<ADIE);  //Enabling ADC Interrupt
15
  ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); //Setting Prescaler to 128
16

17
  //Setting ADC Input Pin to ADC0
18
  ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
19

20
  //Setting reference to Avcc
21
  ADMUX = (1<<REFS0);
22
  
23
  ADCSRA |= (1<<ADSC);  //Start conversions
24
}
25

26
void ADC_SwitchChannel(int channel)
27
{
28
    if(channel == 0)
29
    {
30
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
31
    }
32
    else if(channel == 1)
33
    {
34
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1));
35
      ADMUX |= (1<<MUX0);
36
    }
37
    else if(channel == 2)
38
    {
39
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX0));
40
      ADMUX |= (1<<MUX1);
41
    }
42
}
43

44

45
void saveAnalogWerte()
46
{
47
  ADC_SwitchChannel(0);
48
  ADCSRA |= (1<<ADSC);
49
  while(!(ADCSRA & (1<<ADSC)))
50
  {
51
  }
52
  sensorWert1 = ADCW;
53
  
54
//   ADC_SwitchChannel(1);
55
//   ADCSRA |= (1<<ADSC);
56
//   while(!(ADCSRA & (1<<ADSC)))
57
//   {
58
//   }
59
//   sensorWert2 = ADCW;
60
    
61
//   ADC_SwitchChannel(2);
62
//   ADCSRA |= (1<<ADSC);
63
//   while(!(ADCSRA & (1<<ADSC)))
64
//   {
65
//   }
66
//   sensorWert3 = ADCW;
67
}
68

69
ISR(TIMER0_OVF_vect)
70
{
71
  static int pwm_steps = 0;
72
  
73
  PORTB &= ~(1<<PB1);
74
  pwm_steps++;
75
  PORTB |= (1<<PB1);
76
  
77
  if(pwm_steps >= 1024) 
78
  {
79
    pwm_steps = 0;
80
  }
81

82
  if(pwm_steps >= sensorWert1)
83
  {
84
    PORTB &= ~(1<<PB0);
85
    pwm_steps = 0;
86
  }
87
  else
88
  {
89
    PORTB |= (1<<PB0);
90
  }
91
}
92

93

94
int main (void)
95
{
96
  TCCR0 |= (1<<CS01) | (1<<CS00);
97
  TIMSK |= (1<<TOIE0);
98
  
99
  DDRB |= (1<<PB0) | (1<<PB1);
100
  DDRD |= (1<<PD5);
101
  PORTB |= (1<<PB0) | (1<<PB1);
102
  
103
  ADC_Init();
104
  
105
  sei();
106

107
  while(1)
108
  {
109
//     saveAnalogWerte();
110
  }
111
}
Gast #2898561
Lesenswert?

warum macht man sich in dem man overflows zählt eigentlich das leben so 
schwer. nimm einfach timer1 und nutz die compare match interrupts. im 
einfachsten fall hast du dann 0->Compare = LED an und Compare->0 = LED 
aus.

Der Timer läuft dann immer schön mit seinem vorteiler vor sich hin und 
spuckt dir entsprechend des Compare-registers ein PWM-Signal aus. die 
Interruptroutinen enthalten dann jeweils nur noch "LED an" und "LED 
aus"...keine vergleiche mehr, kein zählen mehr.

gruss
TheMiB
#2898571
Lesenswert?

Antonio schrieb:
> //Setting ADC Input Pin to ADC0
>   ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
>
>   //Setting reference to Avcc
>   ADMUX = (1<<REFS0);

Das funktioniert zwar in diesem Fall, weil MUX0 bis MUX3 sowieso 0 sein 
sollen, aber Du meintest vermutlich:

   ADMUX |= (1<<REFS0);

Antonio schrieb:
> if(pwm_steps >= sensorWert1)
>   {
>     PORTB &= ~(1<<PB0);
>     pwm_steps = 0;
>   }
>   else
>   {
>     PORTB |= (1<<PB0);
>   }

Hier solltest Du meiner Meinung nach die Zeile "pwm_steps = 0;" 
weglassen.

Außerdem wird "sensorWert1" nirgendwo gesetzt - oder habe ich etwas 
übersehen? Die Funktion "saveAnalogWerte()" ist jedenfalls 
auskommentiert.
#2898576
Lesenswert?

Wenn du Code übernimmst, dann übernimm ihn richtig!
1
ISR(TIMER0_OVF_vect)
2
{
3
  static int pwm_steps = 0;
4
  
5
  PORTB &= ~(1<<PB1);
6
  pwm_steps++;
7
  PORTB |= (1<<PB1);
8
  
9
  if(pwm_steps >= 1024) 
10
  {
11
    pwm_steps = 0;
12
  }
13

14
  if(pwm_steps >= sensorWert1)
15
  {
16
    PORTB &= ~(1<<PB0);
17
    pwm_steps = 0;
18
...

Nein. Du willst hier nicht pwm_steps auf 0 setzen.
pwm_steps zählt stur von 0 bis 1023 durch. Immer. Ständig. Ohne 
Ausnahme.
Und je nach Vergleichswert ist die LED eben nur bis zu diesem 
Vergleichswert eingeschaltet und den Rest aus. Durch Variation des 
Vergleichswertes variierst du das Verhältnis von Ein zu Aus. That's it. 
Das ist eine PWM.
Gast #2898577
Lesenswert?

Der atmega8 hat laut Datenblatt 2 8-Bit und ein 16-Bit Timer.
Meine Befürchtung beim Nutzen der Compare-Match Funktion ist, dass die 
sich irgendwie nicht vertragen und pwm-signale erzeugen, die ich nicht 
in den griff bekomme.
Wenn du mir vlt einen Codeabschnitt posten könntest, bei dem dieses 
Problem nicht besteht, würde ich es so machen, aber ich persönlich finde 
es einfacher, einen Timer laufen zu lassen und dann eben abzugleichen.
Gast #2898583
Lesenswert?

Ich habe ursprünglich den COde so übernommen, wie er gepostet wurde, 
aber er funktionierte nicht, also änderte ich ihn ab. Die LED an PB1 
diente lediglich dem debuggen, also um zu sehen, ob der da überhaupt 
reingeht.
Der Rest war einfach nur Trial and Error, wobei aber nichts 
funktionierte...
sensorWert1 wird ganz oben auf 9 gesetzt.
1
volatile unsigned int sensorWert1 = 9;

ich werds nochmal abändern, aber es funktioniert nicht, tut mir leid^^
#2898592
Lesenswert?

Antonio schrieb:
> Ich habe ursprünglich den COde so übernommen, wie er gepostet wurde,
> aber er funktionierte nicht

Hat aber einen anderen Grund.
Die ISR war so wie gepostet vollkommen in Ordnung,


Wenn du dir nicht sicher bist, dann mach halt nicht alles auf einmal. 
Wenn du mit PWM und ADC auf einmal überfordert bist, dann mach eines 
nach dem anderen.
Erst die PWM in Betrieb nehmen, testen.
Dann den ADC dazunehmen.

Dann klappts auch.
Gast #2898599
Lesenswert?

Das mit dem Interrupt wars, dankeschön :))

Die Funktionen nutze ich absichtlich nicht, da es so hardwarenah wie 
möglich sein sollte (ist wichtig für das Projekt).

Aber die Frequenz ist nicht hoch genug... die LED flackert (hab auch 
schon den Prescaler auf null gesetzt).... hmmm...
Gast #2898600
Lesenswert?

Matthias Sch. schrieb:
> Allerdings tuns Bytes hier genauso, du wirst eine in 256 Stufen
> gedimmte LED nicht von einer in 65536 Stufen gedimmten unterscheiden
> können.

Da täuscht man sich leicht.
Bei 256 Stufen kann man gerade 8 Helligkeitsstufen realisieren, wenn man 
die Lichtleistung von Stufe zu Stufe gleichmäßig erhöhen möchte, i.e. um 
3dB, vorgegeben durch 1 LSB bei minimaler Helligkeit.
#2898601
Lesenswert?

Karl Heinz Buchegger schrieb:
>>   ADCSRA |= (1<<ADIE);  //Enabling ADC Interrupt
>
> Und wenn die den ADC Interrupt freigibst, brauchst du auch eine
> entsprechende ISR dafür.
> Hast du keine -> wird mit einem µC-Reset bestraft.
>
>
> Mach dir doch nicht das Leben so schwer. Im
> AVR-GCC-Tutorial
> gibt es wunderbare ADC Funktionen, die du nur benutzen musst.

Genauer, sie sind hier

AVR-GCC-Tutorial/Analoge Ein- und Ausgabe: Nutzung des ADC

samt einem beispielhaften main(), das zeigt, wie man sie benutzt.

Aber bring erst mal die PWM alleine zum laufen! Du brauchst dazu erst 
mal keinen ADC.
#2898621
Lesenswert?

Antonio schrieb:

> Aber die Frequenz ist nicht hoch genug... die LED flackert (hab auch
> schon den Prescaler auf null gesetzt).... hmmm...


Wie wärs mal mit etwas rechnen?

Deine µC Taktfrequenz ist 1Mhz
Der Timer zählt bis 256 bis zum Overflow.
Du brauchst 1024 Overflows für einen PWM-Zyklus.

Wieviele PWM-Zyklen schafft der µC pro Sekunde?

1000000 / 256 = 3906
3906 / 1024 = 3.8

Das das flackert, wundert mich nicht.


Selbst wenn du den Mega auf 8Mhz intern geschaltet hast, sinds erst 30 
PWM-Zyklen pro Sekunde. Flackert immer noch. Du brauchst gut und gerne 
mindestens 60 oder 70. 100 wären noch besser.
Gast #2898633
Lesenswert?

1
  outi ICR1H,  0xFF                                        ;TOP im ICR1 
2
  outi ICR1L,  0xFF
3
  outi TCCR1A, (1<<COM1A1)|(0<<COM0A0)|(1<<WGM11)|(0<<WGM10)   ;Mode 14  
4
  outi TCCR1B, (1<<WGM13)|(1<<WGM12)|(1<<CS10)

Für die eingebaute 16 bit PWM brauchst Du nur 4 Register setzen.
Mit 8 MHz und Fast PWM (Mode 14) erreicht man 122 Hz.
Du mußt dann nur noch in einer Schleife das OCR1A oder B Register
verändern.
#2898640
Lesenswert?

Im Code steht: "# define F_CPU 8000000UL". Hast Du die Fuses wirklich 
richtig gesetzt? Läuft der Controller wirklich mit 8 MHz?

Wenn Du die PWM-Frequenz höher brauchst, nimm nicht den 
Timer-Overflow-Interrupt, sondern einen Timer-Compare-Match-Interrupt. 
Da kannst Du dann selber festlegen, bei welchem Timerstand der Interrupt 
ausgelöst werden soll.
Gast #2898642
Lesenswert?

So danke erstmal für all eure Antworten :)

Die Funktionen kann ich nicht benutzen, wenns nur um mich ginge wäre das 
kein Problem, aber derjenige, der den Code dann durchschaut muss auch 
die Registerzugriffe klar erkennen können und nicht nur die funktionen, 
die diese ausführen.

Das Flackern hab ich einfach wegbekommen, pwm_steps bis 512 hochzählen 
lassen und das wars, trotzdem danke ;)
Gast #2898650
Lesenswert?

Joachim schrieb:
> Du mußt dann nur noch in einer Schleife das OCR1A oder B Register
> verändern.

Nur blöd für ihn, dass er drei LEDs ansteuern will. ;-) Das einfachste 
wäre ein Mikrocontroller mit drei 16-Bit-Compare-Registern (bzw. zwei 
Timern mit jeweils zwei). Dann stellt man die Helligkeit einfach mit dem 
OCRxx-Register ein und gut ists.

Mit dem atmega8 muss er etwas mehr Hirnschmalz reinstecken und Compare- 
und Overflow-Interrupt benutzen, um einen (bzw. mehrere) 16-Bit-Timer in 
Software zu bauen.
Gast #2898663
Lesenswert?

Antonio schrieb:
> Das Flackern hab ich einfach wegbekommen, pwm_steps bis 512 hochzählen
> lassen und das wars, trotzdem danke ;)

Naja, wenn 512 Stufen reichen, dann wahrscheinlich auch 256. Also 
könntest Du die Helligkeit direkt ins Compare-Register der 8-Bit-Timer 
schreiben. In die Interrupts muss dann nur noch LED an und LED aus. Wenn 
Du den zum Timer passenden Ausgangs-Pin für die LEDs benutzt nicht mal 
das.
#2898674
Lesenswert?

Dass Antonio nichts daran liegt, die beste PWM-Methode zu nutzen, 
sondern dass er ausdrücklich Software-PWM machen will, ist Euch noch 
nicht aufgefallen? In einem der letzten Posts hat er sogar einen Grund 
dafür angegeben: Offenbar will er jemandem das Prinzip von PWM erklären, 
und dazu ist optimale Ausnutzen von Hardware-Möglichkeiten eher 
ungeeignet. Also warum reitet Ihr immer noch auf der Hardware-PWM herum?
Gast #2898708
Lesenswert?

Also, ich bedanke mich nochmals für all eure Vorschläge, aber ich muss 
Edi recht geben. Ich möchte bevorzugt eine Software-PWM erreichen und 
das hab ich dank eurer Hilfe :D

Jetzt gibt es nur noch eine Sache die ich nicht ganz verstehe und zwar:
Der jetzige Code sieht so aus
1
# define F_CPU 8000000UL
2
# include <avr/io.h>
3
# include <avr/interrupt.h>
4
# include <util/delay.h>
5

6
volatile unsigned int sensorWert1 = 0;
7
volatile unsigned int sensorWert2 = 0;
8
volatile unsigned int sensorWert3 = 510;
9

10
void ADC_Init()
11
{
12
  ADCSRA |= (1<<ADEN);  //Enabling AD-Converter
13
  //ADCSRA |= (1<<ADFR);  //Setting Free-Running Mode
14
  /*ADCSRA |= (1<<ADIE);*/  //Enabling ADC Interrupt
15
  ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); //Setting Prescaler to 128
16

17
  //Setting ADC Input Pin to ADC0
18
  ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
19

20
  //Setting reference to Avcc
21
  ADMUX = (1<<REFS0);
22
  
23
  ADCSRA |= (1<<ADSC);  //Start conversions
24
}
25

26
void ADC_SwitchChannel(int channel)
27
{
28
    if(channel == 0)
29
    {
30
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
31
    }
32
    else if(channel == 1)
33
    {
34
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1));
35
      ADMUX |= (1<<MUX0);
36
    }
37
    else if(channel == 2)
38
    {
39
      ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX0));
40
      ADMUX |= (1<<MUX1);
41
    }
42
}
43

44

45
void saveAnalogWerte()
46
{
47
  ADC_SwitchChannel(0);
48
  ADCSRA |= (1<<ADSC);
49
  while(!(ADCSRA & (1<<ADSC)))
50
  {
51
  }
52
  sensorWert1 = ADCW;
53
  
54
  ADC_SwitchChannel(1);
55
  ADCSRA |= (1<<ADSC);
56
  while(!(ADCSRA & (1<<ADSC)))
57
  {
58
  }
59
  sensorWert2 = ADCW;
60
    
61
//   ADC_SwitchChannel(2);
62
//   ADCSRA |= (1<<ADSC);
63
//   while(!(ADCSRA & (1<<ADSC)))
64
//   {
65
//   }
66
//   sensorWert3 = ADCW;
67
}
68

69
ISR(TIMER0_OVF_vect)
70
{
71
  static int pwm_steps = 0;
72
  
73
  pwm_steps++;
74
  
75
  if(pwm_steps >= 512) 
76
  {
77
    pwm_steps = 0;
78
  }
79

80
  if(pwm_steps >= sensorWert1)
81
  {
82
    PORTB &= ~(1<<PB0);
83
  }
84
  else
85
  {
86
    PORTB |= (1<<PB0);
87
  }
88
  if(pwm_steps >= sensorWert2)
89
  {
90
    PORTB &= ~(1<<PB1);
91
  }
92
  else
93
  {
94
    PORTB |= (1<<PB1);
95
  }
96
  if(pwm_steps >= sensorWert3)
97
  {
98
    PORTB &= ~(1<<PB2);
99
  }
100
  else
101
  {
102
    PORTB |= (1<<PB2);
103
  }
104
}
105

106

107
int main (void)
108
{
109
  TCCR0 |= /*(1<<CS01) |*/ (1<<CS00);
110
  TIMSK |= (1<<TOIE0);
111
  
112
  DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2);
113
  DDRD |= (1<<PD5);
114
  PORTB |= (1<<PB0) | (1<<PB1) | (1<<PB2);
115
  
116
  ADC_Init();
117
  
118
  sei();
119

120
  while(1)
121
  {
122
    saveAnalogWerte();
123
  }
124
}

Die Ansteuerung des Potis funktioniert einwandfrei, aber wenn ich dann 
noch den Temperatursensor (siehe Schaltplan im ersten Post) noch 
zusätzlich als PWM-Frequenz für eine weitere LED nutzen möchte, ändert 
sich die Helligkeit beider (also PB0 und PB1) wenn ich nur den Poti 
verändere... es scheint, als würde er den temp. Sensor gar nicht 
auslesen, da weder Eis noch eine Heizung eine Veränderung der Helligkeit 
bewirkt.
Ausserdem flackern beide schwach wenn ich den poti runterdrehe, aber aus 
geht die erste LED (die vom poti gesteurt wird) gar nicht mehr...
#2898736
Lesenswert?

Antonio schrieb:
> while(!(ADCSRA & (1<<ADSC)))
Andersrum.
while(ADCSRA & (1<<ADSC));
Denn das ADSC-Bit wird gesetzt und wenn er fertig hat, ist es 0.

Antonio schrieb:
> void ADC_SwitchChannel(int channel)
> {
>     if(channel == 0)
>     {
>       ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
>     }
>     else if(channel == 1)
>     {
>       ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1));
>
>       ADMUX |= (1<<MUX0);
>     }
>     else if(channel == 2)
>     {
>       ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX0));
>       ADMUX |= (1<<MUX1);
>     }
> }
Das geht auch mit weniger Aufwand. Nicht ohne Grund liegen die Mux-Bits
auf den unteren Bits im Register.

void AdcChannel(unsigned char channel)
{
   ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
   ADMUX |= channel;
}
Und channel ist unsigned char und nicht int.

mfg.
Gast #2898738
Lesenswert?

Habs denk ich gelöst...

musste die saveAnalogWert Funktion umschreiben in:
1
void saveAnalogWerte()
2
{
3
  ADC_SwitchChannel(0);
4
  ADCSRA |= (1<<ADSC);
5
  while(!(ADCSRA & (1<<ADIF)))
6
  {
7
  }
8
  ADCSRA |= (1<<ADIF);
9
  sensorWert1 = ADCW;
10
  
11
  ADC_SwitchChannel(1);
12
  ADCSRA |= (1<<ADSC);
13
  while(!(ADCSRA & (1<<ADIF)))
14
  {
15
  }
16
  ADCSRA |= (1<<ADIF);
17
  sensorWert2 = ADCW;
18
    
19
//   ADC_SwitchChannel(2);
20
//   ADCSRA |= (1<<ADSC);
21
//   while(!(ADCSRA & (1<<ADIF)))
22
//   {
23
//   }
24
//   ADCSRA |= (1<<ADIF);
25
//   sensorWert3 = ADCW;
26
}

Sodass das ADIF Bit gesetzt (und damit gelöscht) wird und auch dieses 
abgefragt wurde. Das Flackern ist weg und die LED an PB1 ändert sich 
nicht mehr anch dem poti, wobei ich mir aber noch nicht sicher bin, ob 
der temp. Sensor gelesen wird, da ich optisch keinen unterschied 
zwischen dieser LED sehe, wenn ich ein Eis dranhalte oder das teil an 
die heizung halte.

Gibt es eine Möglichkeit den gemessenen Wert auszulesen? Also wirklich 
als Zahl darstellen zu lassen? Ich verwende Atmel Studio 6, vielen Dank 
im voraus :))
Gast #2898754
Lesenswert?

Herr Eckmann, das geht doch auch freundlicher nicht?
Mit meiner Lösung klappt es jedenfalls, mit Ihrer nicht.
Zumindest denke ich das es klappt, wobei ich mir wie gesagt nicht sicher 
bin, ob der Sensor ausgelesen wird oder nicht.
#2898762
Lesenswert?

Antonio schrieb:
> Herr Eckmann, das geht doch auch freundlicher nicht?
Das war freundlich. Lass mich erstmal unfreundlich werden.

Antonio schrieb:
> Mit meiner Lösung klappt es jedenfalls, mit Ihrer nicht.
Was klappt damit nicht?

Das jedenfalls ist totaler Humbug:
Antonio schrieb:
> ADCSRA |= (1<<ADSC);
>   while(!(ADCSRA & (1<<ADIF)))
>   {
>   }
>   ADCSRA |= (1<<ADIF);
>   sensorWert1 = ADCW;

mfg.
Gast #2898801
Lesenswert?

Hab ich doch geschrieben.

"Habs nochmal so umgeändert, wie Edi meinte, aber dann verändert sich 
die
Helligkeit der LED gar nicht... mit meiner Lösung funktioniert es.
Stimmt die so, oder ist das quark?"

Also wenn ich statt ADIF ADSC einsetze, ändert sich die Helligkeit der 
LED beim verstellen des Potis nicht. Bei ADIF geht das, also frag ich 
mich, wieso ist es falsch? Das ADIF Flag wird bei vollendeter Umwandlung 
gesetzt (wie ich das verstanden habe), d.h. solange es nicht gesetzt ist 
(also
1
while(!(ADCSRA & (1<<ADIF)));
) warten und wenn es gesetzt wird gehts weiter. Ausserdem steht im 
Datasheet: "Alternatively, ADIF is cleared by writing a logical one to 
the flag." also wird es 1 gesetzt, damit gelöscht und es geht zur 
zweiten Umwandlung...

Hab ich da was falsch verstanden?
Gast #2898819
Lesenswert?

Hi

>Also wenn ich statt ADIF ADSC einsetze, ändert sich die Helligkeit der
>LED beim verstellen des Potis nicht.

Auch berücksichtigt, das ADSC während der Messung High ist? Bei mir 
klappt das mit der Abfrage von ADSC immer problemlos.

MfG Spess
#2898835
Lesenswert?

Das ADIF braucht man eigentlich nur, wenn ADIE gesetzt ist und der ADC 
über eine ISR, also mit Interrupts behandelt wird. Ansonsten ist das 
ADIF nur ein Hinweis darauf, dass eine ADC-Messung durchgeführt wurde. 
Deshalb ist mir ein wenig schleierhaft, wieso es mit dem Test auf ADIF 
geht, mit ADSC aber nicht, denn wenn ADSC nach dem Messvorgang wieder 
auf 0 geht, wird gleichzeitig ADIF auf 1 gesetzt.

Aber trotzdem habe ich noch zwei Punkte:

Erstens: Um das ADIF-Flag zu löschen, muss eine 1 in das Register 
geschrieben werden. Das geht NICHT mit
1
  ADCSRA |= (1<<ADIF);
sondern mit
1
  ADCSRA = (1<<ADIF);

Bei der ersten Zeile wird nämlich erst aus dem Register gelesen, dann 
der ursprüngliche Inhalt mit der 1 ODER-verknüpft, dann das Ergebnis 
zurückgeschrieben.

Zweitens: Wenn die Fertig-Meldung schon über ADIF gehen soll, würde ich 
die Reihenfolge ändern:

1) ADIF löschen
2) ADC starten
3) Warten bis ADC fertig ist, also bis ADIF 1 ist

Im Code schaut das dann so aus:
1
  ADCSRA = (1<<ADIF);
2
  ADCSRA |= (1<<ADSC);
3
  while(!(ADCSRA & (1<<ADIF)))
4
  {
5
  }
6
  sensorWertx = ADCW;
Gast #2898867
Lesenswert?

Danke nochmal über das große Feedback ;)

Eine eins in ein Register setze ich immer mit |=, und das hat bis jetzt 
funktioniert, sodass ich deinen Punkt nicht ganz verstehe. Ok, vlt geht 
es auch mit deiner Schreibweise, aber da ich den ganzen Code lang nur |= 
genutzt habe, würde es durchgängig benutzt und so verständlicher sein. 
Denn ich muss den Code auch noch jemandem zeigen, weshalb es so 
verständlich wie möglich geschrieben werden sollte.
Trotzdem danke :D

Das wärs fürs erste auch, evtl muss ich in einer Woche nochmal den Code 
umschreiben und werd dann, falls es noch Probleme gibt, auf das Forum 
hier zurückkommen.

Habt noch einen schönen Tag alle miteinander :)

LG
Antonio
#2898911
Lesenswert?

Edi R. schrieb:

> Erstens: Um das ADIF-Flag zu löschen, muss eine 1 in das Register
> geschrieben werden. Das geht NICHT mit
>
1
>   ADCSRA |= (1<<ADIF);
2
>
> sondern mit
>
1
>   ADCSRA = (1<<ADIF);
2
>
>
> Bei der ersten Zeile wird nämlich erst aus dem Register gelesen, dann
> der ursprüngliche Inhalt mit der 1 ODER-verknüpft, dann das Ergebnis
> zurückgeschrieben.


Und genau das will man hier in diesem Fall. Denn im ADCSRA Register sind 
nach andere Bits, die sich eben nicht ändern sollen und die sich nicht 
wie die Interrupt-Flags verhalten.
Also ist |= schon richtig.

Und genau deswegen ist die Abfrage von ADIF eine blöde Sache. Vor allen 
Dingen deshalb, weil es keiner braucht.

Zum starten des ADC setzt man das ADSC Bit und wenn der ADC fertig ist, 
dann zieht er es wieder zurück auf 0. Was könnte einfacher sein?

Aber solange Antonio das Rad ständig neu erfinden will, redet man 
dauernd aneinender vorbei.

(Wenn mir schon einer mit so einem Scheiss kommt, dass er die Funktionen 
aus dem Tutorial nicht benutzen will, dann hab ichs schon dicke. 3 
Stunden Gehampel für nichts und wieder nichts.)
Gast #2899296
Lesenswert?

Antonio schrieb:
> Die Funktionen nutze ich absichtlich nicht, da es so hardwarenah wie
> möglich sein sollte (ist wichtig für das Projekt).

Antonio schrieb:
> Ich möchte bevorzugt eine Software-PWM erreichen

Was ist an einer Software-PWM so besonders hardwarenah?
Ein Hardwaretimer, der passend konfiguriert ist und dann selbständig das 
PWM-Signal ausgibt, ist doch viel naheliegender. Die Ausgabe wird dann 
zum Einzeiler und die Software kann sich ganz auf die Filterung des 
ADC-Ergebnis konzentrieren.
Beitrag #7834649 wurde von einem Moderator gelöscht.
Beitrag #7834653 wurde von einem Moderator gelöscht.

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