Forum: Compiler & IDEs Mal wieder 'ne Software PWM Frage


von Flo K. (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

ich hab wie schon viele vor mir eine kleine Frage bezüglich dem
Software PWM Problem. Als Anhang hab ich meinen C-Testcode, der soweit
mit einem PWM-Kanal funktioniert (LED dimmt ohne Flackern auf). Wenn
ich nun aber eine zweite Abfrage in die while-Schleife einfüge klappt
gar nichts mehr. Sollte ich solche Routinen lieber in Assembler
schreiben als in C?
Bzw. Hat jemand vielleicht schon einen Beispielcode mit mehreren PWM
Ausgangskanälen und geringer Systembelastung. (Ich habe übrigens einen
AtMega16 mit einem 10 Mhz Quarz).

von Benedikt (Gast)


Lesenswert?

Pack alles in den Timer Interrupt, dann flackert nichts mehr.

von Flo K. (Gast)


Lesenswert?

Auch schon ausprobiert. Leider das gleiche.

von Benedikt (Gast)


Lesenswert?

Wiso teilst du im Interrupt die Frequenz nochmal durch 256 ?
Stell doch einfach den den Prescaler au 256.

Wenn es im Interrupt immer noch flackert, dann ist der restliche Code
falsch bzw. schlecht programmiert.

von Flo K. (Gast)


Lesenswert?

Ich möchte mehrere PWM's mit 8 bit Auflösung. Dazu benötige ich eine
feste PWM Frequenz die 256 mal die Interruptfrequenz ist. Oder verstehe
ich da etwas falsch, bzw. geht das einfacher?

von Sebastian (Gast)


Lesenswert?

du mußt alle pwm-kanäle in der timer-isr bearbeiten. im hauptprogramm
änderst du dann nur noch die werte der einzelnen kanäle

timer z.b. prescaler 256
in der isr zählst du eine variable hoch und vergleichst diese dann mit
den einzelnen pwm kanälen --> wenn der wert gleich ist setz du den
ausgang auf 1. wenn der zähler überlauft setzt du alle kanäle wieder
auf 0.

von Flo K. (Gast)


Angehängte Dateien:

Lesenswert?

Soweit so gut,
die Idee ist nicht schlecht, danke. Nur hab ich jetzt ein Problem mit
der Frequenz des PWM.

10Mhz /256 (8bit Timer Überlauf) = 40kHz
40kHz /100 (Zählschritte) = 390Hz

Sollte das Oszi eigentlich anzeigen. Nur zeigt es mir jetzt gerade mal
151Hz. Stimmt das was in meiner Rechnung nicht?

von Flo K. (Gast)


Lesenswert?

Lol, habs gerade entdeckt. Die Variable zaehler lief immmer bis 256 und
überschlug dann anstatt nach 100 wieder 0 zu werden.

von Flo K. (Gast)


Lesenswert?

PWM läuft nun einwandfrei sogar mit UART Kommunikation.
1
#include <avr/io.h> //Standard IO Einstellungen
2
#include <avr/iom16.h> //Standard Atmega16 Einstellungen
3
#include <avr/signal.h> //Signal definieren
4
#include <avr/interrupt.h> //Signal definieren
5
6
//Funktionsprototypen
7
void Timerinit(void);
8
void UARTinit(void);
9
10
//globale Variablen
11
volatile char zaehler = 0;
12
volatile char LED = 1;
13
char PWM1 = 100;
14
char PWM2 = 100;
15
char PWM3 = 100;
16
17
int main(void)
18
{
19
cli();
20
PORTB=(7<<PB1)|(1<<PB0);
21
Timerinit();
22
UARTinit();
23
DDRB=0xFF;
24
sei();
25
while(1)
26
  {
27
  }
28
}
29
30
//Timerinitialisierung
31
void Timerinit()
32
{
33
TCCR0 = (1<<CS01);
34
TIMSK = (1<<TOIE0);
35
}
36
37
void UARTinit()
38
{
39
UCSRB |= ( 1 << TXEN )|(1 << RXEN)|(1<<RXCIE);      // UART TX+RX+RXint
40
einschalten
41
  UCSRC |= ( 1 << URSEL )|( 3<<UCSZ0 );          // Asynchron 8N1
42
  UBRRH  = 0;                                   // Highbyte ist 0
43
  UBRRL  = 64;                                  // Lowbyte ist 51 (
44
dezimal )
45
}
46
47
//Interruptroutine PWM
48
SIGNAL (SIG_OVERFLOW0)
49
{
50
if (zaehler == 100) 
51
  {
52
  PORTB = (7 << PB1);
53
  zaehler = 0;
54
  }
55
if (zaehler == PWM1) PORTB &= ~(1<<PB1);
56
if (zaehler == PWM2) PORTB &= ~(1<<PB2);
57
if (zaehler == PWM3) PORTB &= ~(1<<PB3);
58
zaehler++;
59
}
60
61
SIGNAL (SIG_UART_RECV)
62
{
63
switch (LED) {
64
case 1:
65
  PWM1=UDR;
66
  LED=2;
67
  break;
68
case 2:
69
  PWM2=UDR;
70
  LED=3;
71
  break;
72
case 3:
73
  PWM3=UDR;
74
  LED =1;
75
  break;
76
  }
77
UDR=LED;
78
}

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Ohne großartig auf die Funktion eingehen zu wollen:
1
#include <avr/io.h> //Standard IO Einstellungen 
2
#include <avr/iom16.h> //Standard Atmega16 Einstellungen

1. Die Kommentare sind nicht ganz korrekt
2. die avr/io.h bindet die jeweils passende avr/ioXYZ.h bei korrekt
eingestellter -mmcu Option automatisch ein.

von Flo K. (Gast)


Lesenswert?

OK, der Quellcode wahr ein kleiner Schnellschuss.
Aber danke nochmal für die schnelle Hilfe.

von Tobias T. (tobytetzi)


Lesenswert?

Hallo,

Ich habe deinen Code mal aufgegriffen,
um eine PWM Frequenz zu nutzen, u damit eine LED zu dimmen.

Wenn ich PWM1 auf 0 stelle, ist die angeschlossene LED immer noch etwas 
an.
Dürfte doch eigentlich nicht sein, da je kleiner der PWM Wert, desto 
dunkler die LED, nicht?
Also müsste die LED bei einem Wert von 0 doch aus sein!?


Gruß Toby

von Matthias L. (Gast)


Lesenswert?

@ Tobias Tetzlaff:

Das liegt daran, dass bei der HardwarePWM das Setzen des Auganges beim 
PWM-Wert von Null UNTERDRÜCKT wird. Bei PWM Wert gleich Max wird das 
Abschalten unterdrückt. => Datenblatt Atmel, PWM-Beschreibung
Hier in dieser Software ist das nicht der Fall.
Da folgendes geschieht, wenn zähler=100:
1
if (zaehler == 100) 
2
  {
3
  PORTB = (7 << PB1);
4
  zaehler = 0;
5
  }

Das wird ausgeführt, die LEDs werden AUSgeschaltet (LOW-aktiv)
Der Zähler wird auf Null gesetzt.
1
if (zaehler == PWM1) PORTB &= ~(1<<PB1);
Ist auch erfüllt für PWM1=0:
Somit wird die LED fälschlicherweise wieder zugeschaltet.

Es sollte so gemacht werden:
Da folgendes geschieht, wenn zähler=100:
1
..
2
if ( (zaehler == PWM1) && (zaehler  != 0) ) PORTB &= ~(1<<PB1);
3
..
4
if (zaehler++ == 100) 
5
  {
6
  if ( PWM1 != 100 )   PORTB |= (1 << PB1);
7
  ...
8
  zaehler = 0;
9
  }
10
...
11
// das separate zaehler++; kann entfallen!

Die Reihenfolge ist mit Absicht gedreht worden, damit die LED bei 
PWMx=100 auch angeht

von Tobias T. (tobytetzi)


Lesenswert?

Hallo,

ich habe einfach die Led an/aus Befehle getauscht.

Nun ist die LED bei PWM == 100 aus, bei PWM == 0 ganz an.

So gehts auch, danke!

//Interruptroutine PWM
SIGNAL (SIG_OVERFLOW2)
{
  if (zaehler == 100)
  {
    //PORTB = (7 << PB1);
    //Set_All_LEDs(0xFF);
    Clear_All_LEDs();
    zaehler = 0;
  }
  if (zaehler == PWM1)
  {
    //Clear_All_LEDs();
    Set_All_LEDs(0xFF);
  }
//PORTB &= ~(1<<PB1);
//if (zaehler == PWM2) PORTB &= ~(1<<PB2);
//if (zaehler == PWM3) PORTB &= ~(1<<PB3);
zaehler++;
}


Gruß Toby

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.