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).
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.
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?
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.
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?
Lol, habs gerade entdeckt. Die Variable zaehler lief immmer bis 256 und überschlug dann anstatt nach 100 wieder 0 zu werden.
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 | }
|
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.
OK, der Quellcode wahr ein kleiner Schnellschuss. Aber danke nochmal für die schnelle Hilfe.
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
@ 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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.