AVR PWM mit beliebigem Output-Pin

Gast #3974021
Lesenswert?

Hallo,
ich versuche schon seit einiger Zeit eine bestimmte Art von PWM zu 
generieren.

Es ist ein Overflow/Compare-Interrupt in dem ich den Timer jedes mal neu 
aufziehe und damit das gewünschte Tastverhältnis einstelle. Eine feste 
Periode ergibt sich ja aus ton+toff, wenn das Verhältnis stimmt. Damit 
soll möglich sein auf beliebigem Output eine PWM auszugeben.

Es funktioniert mit mehreren Timer-Modi gut, solange das Tastverhältnis 
zwischen 10%...90% bleibt.

Meine Frage: Ist es möglich die 10% nach oben und unten zu realisieren?

Es handelt sich um eine BLDC Ansteuerung, d.h. die Hardware steht, 
funktioniert auch - bin halt bei dem Tastverhältnis eingeschränkt. Es 
muss aber unbedingt das Schema beibehalten werden, da der Interrupt den 
Takt vorgibt.

Hier zwei Beispiel mit zwei Modi -> FastPWM, CTC - 20kHz

PS. läuft auf einem ATMEGA328P / Arduino UNO

CTC-Modus:
1
#include <Arduino.h> 
2

3
int flag = 0; 
4

5
unsigned int ton,toff;
6

7
void setup() 
8
{ 
9
    // LED output
10
    DDRD |= (1<<2);
11
    PORTD &= ~(1<<2);
12
    
13
    // Mode 4 - CTC, prescaler 1
14
    TCCR1A = 0x00;
15
    TCCR1B = (1<<WGM12)|(1<<CS10);
16
    
17
    // Capture-Compare - Interrupt
18
    TIMSK1 = (1<<OCIE1A);
19
    sei();
20
    
21
} 
22

23
// 20 kHz PWM
24
ISR (TIMER1_COMPA_vect)
25
{
26
  // ton - Time
27
  if(flag==0) 
28
    { 
29
        // LED on
30
        PORTD |= (1<<2); 
31

32
        // duty-cycle = 25%
33
        ton = 200;
34
        OCR1A = ton;
35
        
36
        flag=1; 
37
    } 
38
    else  // toff - Time
39
    { 
40
        // LED off
41
        PORTD &= ~(1<<2); 
42
        
43
        toff = 799 - ton; 
44
        OCR1A = toff; 
45
        
46
        flag=0; 
47
    } 
48
}
49
 
50
void loop() 
51
{ 
52

53
}

FastPWM
1
#include <Arduino.h> 
2

3
int flag = 0; 
4

5
unsigned int ton, toff;
6

7
void setup() 
8
{ 
9
    // LED output
10
    DDRD |= (1<<2);
11
    PORTD &= ~(1<<2);
12

13
    // Mode 15 - FastPWM, Prescaler 1
14
    TCCR1A = (1<<WGM11)|(1<<WGM10);
15
    TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS10);
16
    
17
    // Overflow Interrupt
18
    TIMSK1 = (1<<TOIE1);
19
    sei();
20
    
21
} 
22

23
// 20 kHz PWM
24
ISR(TIMER1_OVF_vect) 
25
{ 
26
    // ton - Time
27
    if(flag==0) 
28
    { 
29
        // LED on
30
        PORTD |= (1<<2); 
31
        
32
        // duty-cycle = 50%
33
        ton= 400;        
34
        OCR1A = ton;
35

36
        flag=1; 
37
    } 
38
    else // toff - Time
39
    { 
40
        // LED off
41
        PORTD &= ~(1<<2); 
42
 
43
        toff = 799-ton;        
44
        OCR1A = toff;
45

46
        flag=0; 
47
    } 
48
} 
49
void loop() 
50
{ 
51

52
}
#3974047
Lesenswert?

Hi,

der Timer läuft mit Systemtakt, da kann eine Soft-PWM nicht wirklich gut 
funktionieren. Auch mit bestem Assembler nicht.
Die Periode ist doch nur 800 Takte lang.
Mit Soft-PWM kann man vielleicht nebenbei eine LED dimmen oder sowas 
aber doch keine PWM mit 20kHz.
ton immer wieder in der ISR zu setzen ist jetzt auch nicht sonderlich 
effizient aber das macht den Kohl nicht fett.
Überhaupt ginge das etwas besser (ICR1 als Overflow vorher festlegen) 
aber das löst Dein Problem nicht, da das auch nur minimal besser wird.

Einzige Chance wäre Timer mit 8Bit, Teiler 8, PWM-Frequenz 
dementsprechend runter auf ca. 7,8kHz. Darüber wird das Schalten der 
Endstufe auch langsam ein ernsteres Thema.
Damit wird es aber auch noch nicht wirklich gut sondern nur etwas 
weniger schlecht.
Spätestens wenn es noch andere Interrupts gibt kannst Du das komplett 
vergessen.

Das wird nur mit Hardware PWM was werden.

Gruß,
Norbert
(Firma: matzetronics) #3974204
Lesenswert?

Wenn du schon einen Mega328 da hast, ist mir nicht klar, warum du zur 
BLDC Ansteuerung unbedingt Soft PWM nehmen willst. Die 3 Timer mit ihren 
komplementären Ausgängen sind ideal für direkte BLDC Ansteuerung und 
können sogar FOC, wenn du das mal möchtest.
Siehe die Applikation Notes AVR447 und AVR444 bei Atmel. Auch mein 
Frequnezumrichter macht von den Timern regen Gebrauch:
http://www.mikrocontroller.net/articles/3-Phasen_Frequenzumrichter_mit_AVR
#3974329
Lesenswert?

Wowa schrieb:
> Meine Frage: Ist es möglich die 10% nach oben und unten zu realisieren?

Mein uralter WinAVR generiert aus deiner ISR-Roútine (ohne die 
konstanten Zuweisungen, due willst ja sicherlich on/of während der 
Laufzeit verändern) ein Programm, das ca. 75 Zyklen benötigt. Dazu kommt 
noch die Einsprungzeit in die ISR  von minimal 4 Zyklen.

That's it - beantworte deine Frage selber...

Mit handoptimierten Assembler liesse sich noch vier Zyklen für ein 
push/Pop sparen, wenn man zwei Register für die ISR reserviert, nochmals 
8, das bringt dich dann auf ca. 8%. Mehr ist halt nicht drin.

Oliver
Gast #3974655
Lesenswert?

Peter Dannegger schrieb:
> Entwas schneller gehts, wenn man 2 Interrupts (COMPA/B) nimmt zum
> Ein-/Ausschalten und die dann als nacked nur mit SBI/CBI+RETI drin. Dann
> sinds 12 Zyklen.

Danke für den Tipp, habs eben probiert...

bei 20kHz komme ich auf 6% ... 93%
bei 10kHz 3% ... 97%
.
.
.

je größer der Zählerwert (kleinere Frequenz) desto genauer/besser wird 
es.

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