Forum: Mikrocontroller und Digitale Elektronik Poti Wert zu PWM Signal verarbeiten


von Antonio (Gast)


Angehängte Dateien:

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 :))))

von Juergen G. (jup)


Lesenswert?

1
ISR(TIMER0_OVF_vect)
2
{
3
   static uint16_t pwmcnt = 0;
4
5
   pwmcnt++;
6
   if(pwmcnt >= 1024) {
7
      pwmcnt = 0;
8
   }
9
10
   if(pwmcnt >= sensorWert1)
11
   {
12
      PORTB &= ~(1<<PB0);
13
   }
14
   else
15
   {
16
      PORTB |= (1<<PB0);  
17
   }
18
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


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?

von Antonio (Gast)


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

von Antonio (Gast)


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?

von Edi R. (edi_r)


Lesenswert?

Kannst Du dann nochmal den Code posten, so wie er jetzt ist?

von Antonio (Gast)


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
}

von TheMiB (Gast)


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

von Edi R. (edi_r)


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.

von Karl H. (kbuchegg)


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.

von Antonio (Gast)


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.

von Antonio (Gast)


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

von Karl H. (kbuchegg)


Lesenswert?

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

von Edi R. (edi_r)


Lesenswert?

Antonio schrieb:
> sensorWert1 wird ganz oben auf 9 gesetzt.
> volatile unsigned int sensorWert1 = 9;

Stimmt, habe ich übersehen. Sorry.

von Karl H. (kbuchegg)


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.

von Antonio (Gast)


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

von Michael (Gast)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


Lesenswert?

Antonio schrieb:
> 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).

kopfschüttel.

Denkst du, die Funktionen machen irgendwas anderes, als das Chaos, das 
du da veranstaltet hast?

von Karl H. (kbuchegg)


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.

von Joachim (Gast)


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.

von Edi R. (edi_r)


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.

von Antonio (Gast)


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

von xfr (Gast)


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.

von xfr (Gast)


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.

von Edi R. (edi_r)


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?

von Antonio (Gast)


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

von Thomas E. (thomase)


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.

von Edi R. (edi_r)


Lesenswert?

Antonio schrieb:
> ADCSRA |= (1<<ADSC);
>   while(!(ADCSRA & (1<<ADSC)))
>   {
>   }

Hier liegt vermutlich der Wurm. ADSC ist 1, solange die Messung 
durchgeführt wird. Also müsstest Du die Abfrage so machen:
1
  while(ADCSRA & (1<<ADSC))
2
  {
3
  }

von Antonio (Gast)


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 :))

von Edi R. (edi_r)


Lesenswert?

Da war ich jetzt ein paar Sekunden zu langsam ;-)

von Thomas E. (thomase)


Lesenswert?

Antonio schrieb:
> Habs denk ich gelöst...
Gar nichts hast du gelöst.

Antonio schrieb:
> while(!(ADCSRA & (1<<ADIF)))

mfg.

von Antonio (Gast)


Lesenswert?

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?

von Antonio (Gast)


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.

von Thomas E. (thomase)


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.

von Antonio (Gast)


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?

von Spess53 (Gast)


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

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Antonio schrieb:
> Hab ich da was falsch verstanden?

Nee, ist schon richtig. Alternativ könntest du ADSC solange abfragen, 
bis es auf Null geht, dann ist die Wandlung auch fertig. Das hätte den 
Vorteil (nicht wirklich ein grosser), das du nicht extra ADIF löschen 
musst, spielt aber wirklich keine Rolle.

von Thomas E. (thomase)


Lesenswert?

Antonio schrieb:
> while(!(ADCSRA & (1<<ADIF)));
Sorry. Das hatte ich übersehen.
Ja. Damit geht es auch.

mfg.

von Edi R. (edi_r)


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;

von Antonio (Gast)


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

von Karl H. (kbuchegg)


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

von Michael (Gast)


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.

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.