Forum: Mikrocontroller und Digitale Elektronik Fragen zu extrem langsamer PWM und Prescaler, PREA, DIVA


von Buddler B. (buddler) Benutzerseite


Lesenswert?

Hallo,

ich versuche mittels PWM ein Ventil regelmäßig zu schalten. Die 
Periodenlängen dabei sind bis zu 300 sek und einstellbar von 10 bis 
300sek.
Mein AVR32 UC3A1512 läuft auf 12 MHz und der Vorteiler für den PWM-Kanal 
(CPRE aus dem CMR-Register des Kanals) steht auf 1024 (0xA).
Damit komme ich dann bei 16 bit max auf eine Periode von 5.59 sek. 
Deshalb dachte ich, dass ich noch eine 2. Vorteiler nutzen kann und habe 
das so angestellt:
1
void PWM2(int channel, int duty, int period)
2
{
3
  AVR32_GPIO.port[1].gperc = 1 << (19+channel);              // PB20 = PWM Channel[1]
4
  AVR32_GPIO.port[1].pmr0c = 1 << (19+channel);              // PB20 = Function A
5
  AVR32_GPIO.port[1].pmr1c = 1 << (19+channel);              // PB20 = Function A
6
7
8
  AVR32_PWM.mr =     0 << AVR32_PWM_PREB_OFFSET |        // PWM PREB Divider Input clock
9
            0xa << AVR32_PWM_PREA_OFFSET |        // PWM PREA Divider Input clock
10
            0 << AVR32_PWM_DIVB_OFFSET |        // PWM CLKB Divider factor
11
            0xff << AVR32_PWM_DIVA_OFFSET ;        // PWM CLKA Divider factor
12
13
14
  AVR32_PWM.channel[channel].cmr =
15
                0 << AVR32_PWM_CPD_OFFSET |    // Writing to the CUPD1 will modify the duty cycle at the next period start event.
16
                0 << AVR32_PWM_CPOL_OFFSET |  // The output waveform starts at a Low level.
17
                0 << AVR32_PWM_CALG_OFFSET |   // The period is left aligned.
18
                0x3 << AVR32_PWM_CPRE_OFFSET ;  // PWM CLKA Divider factor
19
20
  AVR32_PWM.channel[channel].cprd = period;          // max pwm Value
21
  AVR32_PWM.channel[channel].cdty = duty;            // duty
22
23
  AVR32_PWM.ena |= 1 << channel;                // Enable PWM Channel[1]
24
}

Leider stellt sich nicht der gewünschte Effekt ein (MCK/(PREA*DIVA)).

Wer weiß rat oder hat selbiges schon probiert?

von Hans M. (hansilein)


Lesenswert?

Warum willst Du für etwas so langsames Hardware-PWM nutzen?

von Buddler B. (buddler) Benutzerseite


Lesenswert?

Weil ich zur Zeit PWM besser verstehe als Timer, d.h. ich schaffe es 
noch nicht mit Timer/counter und Interrupts zu arbeiten. Wie wäre da die 
herangehensweise (und letztendlich funktioniert das doch ähnlich, 
oder?)?

von Buddler B. (buddler) Benutzerseite


Lesenswert?

So, wie ich nochmal nachgelesen habe, ist der interne Counter 20bit 
groß, d.h. ich kann bis 1048575 zählen, was mir bei maximalen Vorteiler 
und 12 MHz 89.4784 s Periodenlänge (sogar selbst gemessen) ermöglicht. 
Schon etwas näher dran, aber immer noch daneben. Wie löst man das 
Problem vielleicht eleganter?

Als Notlösung könnte ich den Chiptakt auf z.B. 3 MHz reduzieren, was 
aber Nachteile bringt.

von Hans M. (hansilein)


Lesenswert?

sich jede sekunde einen interrupt geben lassen, zähler inkrementieren, 
wenn er eine bestimmte zahl, z.b. 300 überschritten hat ausgang 
schalten?

von Buddler B. (buddler) Benutzerseite


Lesenswert?

Ich habe noch eine Frage:

Standardmäßig wird beim AVR32 bei Änderungen der Periodenlänge oder der 
Pulsbreite erst im nächsten Periodendurchlauf der Parameter geändert.
Da ich aber sofort eine Änderung möchte, dachte ich, es wäre möglich die 
PWM des Kanals zu dekativieren und einfach neu zu initialisieren.
Allerdings setzt sich der µC dort mit dem A**** drauf und macht selbst 
bei folgender Variante Änderungen erst im nächsten Periodendurchlauf:
1
if (buttons[1] == 1){
2
      up++;
3
      if(up > 80){up = 80;}
4
      AVR32_PWM.dis = (1 << 2);             //PWM for channel 2 disabled
5
      PWM2(2, 2343, 11718+up*11718); //11718 entspricht einer Sekunde
6
    }

Code der Funktion PWM2:
1
void PWM2(int channel, int duty, int period)
2
{
3
  AVR32_GPIO.port[1].gperc = 1 << (19+channel);              // PB20 = PWM Channel[1]
4
  AVR32_GPIO.port[1].pmr0c = 1 << (19+channel);              // PB20 = Function A
5
  AVR32_GPIO.port[1].pmr1c = 1 << (19+channel);              // PB20 = Function A
6
  AVR32_PWM.channel[channel].cmr =
7
                0 << AVR32_PWM_CPD_OFFSET |    // Writing to the CUPD1 will modify the duty cycle at the next period start event.
8
                1 << AVR32_PWM_CPOL_OFFSET |  // The output waveform starts at a Low level.
9
                0 << AVR32_PWM_CALG_OFFSET |   // The period is left aligned.
10
                0xa << AVR32_PWM_CPRE_OFFSET ;  // PWM CLKA Divider factor
11
12
  AVR32_PWM.channel[channel].cprd = period;          // max pwm Value
13
  AVR32_PWM.channel[channel].cdty = duty;            // duty
14
  AVR32_PWM.ena |= 1 << channel;                // Enable PWM Channel[1]
15
}

Wie gesagt, über CUPD lässt sich auch erst in der nächsten Periode etwas 
ändern.

Wie kann man PWM sofort stoppen und wieder neu starten lassen?

Das Manual sagt:
"CNT: Channel Counter Register
Internal counter value. This register is reset when:
• the channel is enabled (writing CHIDx in the ENA register).
• the counter reaches CPRD value defined in the CPRDx register if the 
waveform is left aligned."

Das entsprechende Bit wird ja auch gesetzt ... :-/

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.