Forum: Mikrocontroller und Digitale Elektronik Soft PWM viel zu langsam


von Thomas (Gast)


Lesenswert?

Hallo Ihr,

ich versuche seit gestern ein Soft-PWM Programm nach Vorbild aus dem 
Artikel hier, auf meinem Atmega 328p mit 16MHz zum Laufen zu bringen. 
Vorerst sollte lediglich ein PWM Signal mit jeweils unterschiedlichem 
Duty Cycle an 3 Pins anliegen.
Um zu sehen was an den Ausgängen passiert schließe ich dort testweise 
eine LED an.
Da alte Programme einwandfrei laufen und auch die Fuses richtig gesetzt 
sind, liegt es mit 95 prozentiger Sicherheit an Codefehlern.

Das Problem ist, dass an den Ausgängen zwar ein PWM Signal anliegt 
jedoch mit einer Frequenz im ein- bis zweistelligen Hertz Bereich. Das 
heißt etwa 100mal zu langsam.

Ich hoffe Ihr findet meinen dämlichen Fehler wieder mal und helft mir 
aus der Patsche.

Danke schonmal für die Mühe!

Hier mal der Code:
1
#define F_CPU 16000000UL 
2
3
#define F_PWM 100                       // PWM-Frequenz in Hz
4
5
#define PWM_STEPS 256UL                   // PWM-Schritte pro Zyklus(1..256)
6
7
#define PWM_PORT PORTD                  // Port für PWM
8
#define PWM_DDR DDRD                    // Datenrichtungsregister für PWM
9
10
11
#define T_PWM (F_CPU/(F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
12
13
14
#include <avr/io.h>
15
#include <avr/interrupt.h>
16
17
volatile uint8_t red1=30;
18
volatile uint8_t green1=100;
19
volatile uint8_t blue1=200;
20
21
void timer_init(){
22
  
23
  //Timer 1 in CTC Mode with F_CPU
24
  TCCR1B |= (1<<WGM12) | (1<<CS10);
25
  TIMSK1 |= (1<<OCIE1A);   // Interrupt freischalten
26
  OCR1A=T_PWM;
27
  sei();                  // Interrupts global einschalten
28
  }
29
30
void io_init(){
31
  PWM_DDR = 0xff;  
32
  PWM_PORT=0;
33
  }
34
35
36
37
ISR(TIMER1_COMPA_vect) {
38
    static uint8_t pwm_cnt=0;
39
    uint8_t tmp=0;
40
 
41
    OCR1A += (uint16_t)T_PWM;
42
  
43
 
44
    if (red1 > pwm_cnt) tmp |= (1<<2);
45
    if (green1 > pwm_cnt) tmp |= (1<<3);
46
    if (blue1 > pwm_cnt) tmp |= (1<<4);
47
48
    PWM_PORT = tmp;                         // PWMs auf Port schreiben
49
  
50
    if (pwm_cnt==(uint8_t)(PWM_STEPS-1))
51
        pwm_cnt=0;
52
    else
53
        pwm_cnt++;
54
    
55
}
56
 
57
int main(void)
58
{  
59
  io_init();
60
  timer_init();
61
  
62
    while(1)
63
    {
64
        
65
    }
66
  
67
  return 0;
68
}

von Patrick B. (p51d)


Lesenswert?

Du änderst deine PWM-Frequenz jedesmal, durch anpassen des OCR1A 
Registers (also den Max-Wert des Timers). Beim ersten Durchlauf zählt er 
bis T_PWM, dann wird der Timer auf 0 gesetzt und zählt beim 2. Interrupt 
auf 2*T_PWM, usw...

Ausserdem würdest du so die "unbenützen" oder anders verwendeten Pins 
von PortD ebenfalls manipulieren. Desswegen musst du zuerst den 
Registerwet speichern, und nur deine PWM-Pins behandeln.

Das sollte klappen:
1
#define PWM_PORT_VAL  PIND
2
#define PWM_PIN_1    2
3
#define PWM_PIN_2    3
4
#define PWM_PIN_3    4
5
ISR(TIMER1_COMPA_vect) {
6
    static uint8_t pwm_cnt=0;
7
    uint8_t tmp=PWM_PORT_VAL;        // Register retten
8
  
9
  // Löschen der Werte (nur PWM)
10
  temp &= ~((1<<PWM_PIN_1) | (1<<PWM_PIN_2) | (1<<PWM_PIN_3));
11
 
12
  // PWM Pins setzen
13
    if (red1 > pwm_cnt) tmp |= (1<<PWM_PIN_1);
14
    if (green1 > pwm_cnt) tmp |= (1<<PWM_PIN_2);
15
    if (blue1 > pwm_cnt) tmp |= (1<<PWM_PIN_3);
16
17
    PWM_PORT = tmp;                         // PWMs auf Port schreiben
18
  
19
    if (pwm_cnt==(uint8_t)(PWM_STEPS-1))    pwm_cnt=0;
20
    else                  pwm_cnt++;    
21
}

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Dickes Dankeschön!!

Mir kam das doch gleich sehr merwürdig vor, dass ich in jedem Interrupt 
den Compare Wert addieren muss. Da will man einmal was genau nach 
Anleitung machen und das hat man dann davon.. :D

Das mit den Pins ist in meinem Anwendungsfall nicht besonders tragisch, 
da noch drei Weitere Pins für PWM benutzt werden und ich somit der Port 
nur rein zur PWM Erzeugung genutzt wird.
Aber danke für den Hinweis, kann ich bestimmt mal brauchen ;)

Läuft jetzt übrigens einwandfrei. Dankeschööön!!!!

von Patrick B. (p51d)


Lesenswert?

Thomas schrieb:
> Mir kam das doch gleich sehr merwürdig vor, dass ich in jedem Interrupt
> den Compare Wert addieren muss. Da will man einmal was genau nach
> Anleitung machen und das hat man dann davon.. :D

Du hast auch den Timer anders initialisiert (CTC)... Im Beispielt zählt 
der Timer bis 0xFFFF, da macht das inkrementieren bedingt Sinn (der 
letzte Wert kann zufällig genau passen, muss aber nicht). Dorst sollte 
man darauf achten, dass T_PWM ein gerader Teiler von 256 oder 65536 ist. 
Ist dies nicht der Fall, dann ist die CTC Variante eindeutig die bessere 
und hier ist OCR fix.

von Axel S. (a-za-z0-9)


Lesenswert?

Patrick B. schrieb:
> Du hast auch den Timer anders initialisiert (CTC)... Im Beispielt zählt
> der Timer bis 0xFFFF, da macht das inkrementieren bedingt Sinn (der
> letzte Wert kann zufällig genau passen, muss aber nicht). Dorst sollte
> man darauf achten, dass T_PWM ein gerader Teiler von 256 oder 65536 ist.

Nein. T_PWM muß kein Teiler des Zählumfangs sein. Denn bei der Addition 
auf das OCR passiert genau der gleiche wohldefinierte Überlauf wie beim 
Timer selber.

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.