Forum: Mikrocontroller und Digitale Elektronik Xmega PWM zählt nicht richtig(?)


von qzeta5 (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

schreibe gerade ein Programm für den frequenzgeführten Betrieb einer 
Drehfeldmaschine mit Grundfrequenztaktung. Die Frequenz stelle ich über 
die Periodendauer PER des Counters ein.

Das Problem: Wenn ein Ausgang der PWM über mehrere Perioden auf High 
bleiben soll, geht er am Ende (Anfang?) einer Periode kurz (einen 
PWM-Takt?) auf Low. Das, obwohl z.B. TCC0.CCABUF = TCC0.PERBUF gesetzt 
ist und laut Manual hier PIN0 an PORTC über mehrere Perioden 
kontinuierlich auf High bleiben sollte. Das Ganze passiert im statischen 
Betrieb, ohne TCC0.PER zu ändern.
Seltsam ist auch, dass je nach Wert von TCC0.PER das Problem manchmal 
nicht auftritt...?

Wenn ich den Wert für CCABUF unsinniger Weise höher als PERBUF setze, 
habe ich ein durchgängiges High am Pin.

Könnte mir jemand mit seinem Fachwissen weiter helfen, bzw. eine 
elegantere Lösung für die GFT nennen.

Hier ein Auszug au dem Code:
1
#define U TCC0.CCABUF 
2
#define V TCC0.CCBBUF
3
#define W TCC0.CCCBUF
4
.
5
.
6
.
7
void timer3PHASEN_init(void) {
8
  TCC0.INTCTRLA = TC_OVFINTLVL_HI_gc; // Timerüberlauf Interrupt aktivieren Highlevel
9
  TCC0.CTRLB = 0b01110011; // CCA,B,C enable; singleslope
10
  TCC0.CTRLA = TC_CLKSEL_DIV256_gc; // Timer aktivieren
11
  TCC0.PER = PERIODE3PHASE; // Timer Top Wert festlegen
12
  TCC0.CCA = 0;
13
  TCC0.CCB = 0;
14
  TCC0.CCC = 0;
15
}
16
.
17
.
18
.
19
void speedNeu32(void) {
20
.
21
.
22
.
23
  else {
24
    if (speed < 1100 && speed > 33 && speed != speedOld ) {
25
      temp32blockSpeed = ((uint32_t)PerUnter11Hz * 33) / (uint32_t)speed;
26
      TCC0.PERBUF = (uint16_t)temp32blockSpeed;
27
      if (EinmalBlockSpeedWechsel == 0) {
28
        HIRESC.CTRLA = 0b00000000 ;
29
        EinmalBlockSpeedWechsel = 1;
30
      }  
31
    }  
32
    if (speed > 1099 && speed != speedOld)  {
33
      temp32blockSpeed = ((uint32_t)PerUnter11Hz * 33 * 4) / (uint32_t)speed;
34
      TCC0.PERBUF = (uint16_t)temp32blockSpeed;
35
      if (EinmalBlockSpeedWechsel == 1) {
36
        HIRESC.CTRLA = 0b00000001 ; // Timer aktivieren
37
        EinmalBlockSpeedWechsel = 0;
38
      }
39
    }
40
  }    
41
  speedOld = speed;
42
}
43
.
44
.
45
.
46
ISR (TCC0_OVF_vect) {
47
.
48
.
49
.
50
  else { //Blocktaktung
51
      if (spng > 0) {
52
        if (modIn != 3 && speed > 33) {
53
          if (drehwo == 0) {
54
            if (sektor < 5) sektor++; 
55
            else sektor = 0;
56
          }
57
          else {
58
            if (sektor > 0) sektor--; 
59
            else sektor = 5;
60
          }
61
        }
62
        else sektor = speed/(uint16_t)600;        
63
          
64
        switch (sektor) {
65
          case 0: U = TCC0.PERBUF; V = 0; W = TCC0.PERBUF;
66
            break;
67
          case 1: U = TCC0.PERBUF; V = 0; W = 0;
68
            break;
69
          case 2: U = TCC0.PERBUF; V = TCC0.PERBUF; W = 0;
70
            break;
71
          case 3: U = 0; V = TCC0.PERBUF; W = 0;
72
            break;
73
          case 4: U = 0; V = TCC0.PERBUF; W = TCC0.PERBUF;
74
            break;
75
          case 5: U = 0; V = 0; W = TCC0.PERBUF;
76
            break;
77
        }
78
      } else { U = 0; V = 0; W = 0; }                
79
    }
80
}

Dank im Voraus :)

Im Anhang ein Oszillogramm des Problems.

von Chose (Gast)


Lesenswert?

Also ich würd mit den Atmel Lib-Funktionen arbeiten. Was du alles im 
Timer 0 Interrupt machst schreit nach Problemen. Mit welcher PWM 
Frequenz arbeitest du und mit welcher Auflösung?


SG

von qzeta5 (Gast)


Lesenswert?

Was die Lib-Funktionen sind, weiß ich nicht mal. Vieleicht kannst Du mir 
erklären, warum ich diese benutzen sollte. Bin halbwegs Anfänger.

Den TCC0_OVF_vect nutze ich, um zu gewährleisten, dass die 
Compare-Register in der nächsten Periode den "richtigen" Wert haben. 
Übrigens mache ich noch eine ganze menge mehr in dieser ISR. Warum 
schreit das nach Problemen?

Die PWM-Frequenz ist wie erwähnt variabel und bestimmt die Frequenz der 
"Blocktaktung". fBlock = fPWM/6. PER wird über eine Variable gefüttert.
Die Frequenz variiert zwischen 2,1Hz und 65,94Hz bei DIV=256, 
ausgeschaltetem HiRes, ClkCPU=32MHz, ClkPer=ClkCpu, PER=63130 bis 1895.
Und zwischen 66Hz und 600Hz bei DIV=256, HiRes an (4x), ClkCPU=32MHz, 
ClkPer=ClkCpu, ClkPer4=4*ClkCpu, PER=7575 bis 832.
CCA bis CCC sollen immer den aktuellen Wert von PER haben oder Null 
sein. Je nach Sektor halt.

Am liebsten wäre mir eine 32bit-PWM gewesen. Die scheint aber nicht 
möglich mit dem Xmega256A3U. Eine bessere Lösung für die Blocktaktung 
ist mir bisher nicht eingefallen... Vieleicht hast Du einen 
konstruktiven Vorschlag

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

qzeta5 schrieb:
> Das Problem: Wenn ein Ausgang der PWM über mehrere Perioden auf High
> bleiben soll, geht er am Ende (Anfang?) einer Periode kurz (einen
> PWM-Takt?) auf Low. Das, obwohl z.B. TCC0.CCABUF = TCC0.PERBUF gesetzt
> ist und laut Manual hier PIN0 an PORTC über mehrere Perioden
> kontinuierlich auf High bleiben sollte. Das Ganze passiert im statischen
> Betrieb, ohne TCC0.PER zu ändern.
> Seltsam ist auch, dass je nach Wert von TCC0.PER das Problem manchmal
> nicht auftritt...?
>
> Wenn ich den Wert für CCABUF unsinniger Weise höher als PERBUF setze,
> habe ich ein durchgängiges High am Pin.

Das ist völlig richtig und normal so. Die PWM wird, wenn sie nicht 
invertiert ist, beim Übergang von Timer-Top -> 0000 gesetzt und beim 
Compare-Match gelöscht. Im invertierten Modus wird sie beim 
Compare-Match gesetzt und bei Timer-Top -> 0000 gelöscht. Daher erhältst 
du immer einen Spike in der Breite eines ungeteilten Timer-Taktzyklusses 
(CLKPer). Um dies zu verhindern, setzt Du PER genau 1 höher als Deinen 
maximalen Compare-Wert und schon ist alles in Butter.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

qzeta5 schrieb:
> Am liebsten wäre mir eine 32bit-PWM gewesen. Die scheint aber nicht
> möglich mit dem Xmega256A3U.

Auszug aus dem AU-Manual:

Atmel AVR XMEGA devices have a set of flexible, 16-bit timer/counters 
(TC). Their capabilities include accurate program
execution timing, frequency and waveform generation, and input capture 
with time and frequency measurement of digital
signals. Two timer/counters can be cascaded to create a 32-bit 
timer/counter with optional 32-bit capture.

von qzeta5 (Gast)


Lesenswert?

Danke für die hilfreiche Antwort Knut :)
Schon komisch, dass dieses Verhalten nicht aus der zugehörigen "Figure 
14-15" im Manual des Xmega AU hervorgeht. Eher im Gegenteil.

Der Spike ist bei mir übrigens tatsächlich (CLKPer/DIV)^-1 breit, also 
ca. 8us.

Deine Lösung funktioniert (fast) tatsächlich! Nur dass ich den 
Compare-Wert um 1 höher setzen muss als PER.
Mit HiRes muss ich übrigens CCx=PER+4 rechnen...

So ganz verstanden habe ich das immer noch nicht. Da im Manual für 
Single-slope PWM CCx=TOP zu einem verbleibenden High führen soll.
Das Vorgehen jetzt bedeutet ja, dass ich keinen Compare-Match habe und 
somit der CCx-Wert "beliebig" größer als der PER-Wert sein könnte. Habe 
da ein wenig Bedenken wegen unberechenbaren Verhaltens des Programms.

von qzeta5 (Gast)


Lesenswert?

Ja, das habe ich tatsächlich auch gelesen. Das soll wohl mit dem 
Event-System funktionieren. Wie das aber progrsmmiert werden soll, ist 
mir ein Rätsel. Das Event-System habe ich bisher aus Unwissenheit und 
Scheu nicht angefasst. :) Ansonsten steht aber nicht mehr dazu im 
Manual.

Zudem frage ich mich, wie eine 32-Bit PWM an drei Pins damit realisiert 
werden kann. Ich kann ja z.B. TCC0 und TCC1 zusammenschalten. Sagen wir 
der TCC0 würde beim OVF von TCC1 starten. Das hieße doch, dass ich für 
PORTC PIN0 bis PIN2 trotzdem nur Compare-Werte von 0 bis 65536 nutzen 
könnte, somit nur die Frequenz aber nicht die Auflösung eines echten 
32-Bit Counters hätte.(?) Oder verstehe ich das falsch...?

von qzeta5 (Gast)


Lesenswert?

Habe jetzt den Code aus der "void speedNeu32(void)" in die "ISR 
(TCC0_OVF_vect)" verlegt. Das Vorgehen CCx=PER+1 bzw. CCx=PER+4 (HiRes) 
funktioniert soweit.
Das Beschleunigen, Verzögern und der statische Betrieb geben mir ein 
scheinbar einwandfreies Signal aus.

Einziger Knackpunkt ist der Wechsel zwischen HiRes an/aus beim 
Beschleunigen bzw. Verzögern.

Gibt es eine Möglichkeit HiRes exakt zum Beginn einer neuen Periode zu 
aktivieren? So wie ich das jetzt mache geschieht das 
Aktivieren/Deaktivieren von HiRes "irgendwo" zwischendurch.

Dachte hier die ISR des freien TCC0.CCD-Registers zu nutzen, um einige 
Takte vor TOP HiRes ein/auszuschalten, damit HiRes genau zum Beginn der 
nächsten Periode eingeschaltet wird. Mein analoges Oszi eignet sich hier 
leider nicht für die Untersuchung´des Wechsels. Zudem scheint mir dieser 
Ansatz immer noch zu unberechenbar.

von Stephan (Gast)


Lesenswert?

Schau mal hier:
http://www.atmel.com/products/microcontrollers/avr/avr_xmega.aspx?tab=documents

Die Appnote
AVR1001: Getting Started With the XMEGA Event System
erklärt alles :)

von qzeta5 (Gast)


Lesenswert?

Oh je ;) Danke. Habs am liebsten idiotensicher erklärt. Die 
Getting-Started-Blättchen von Atmel verwirren mich manchmal noch mehr, 
als dass sie mir weiterhelfen :) Naja, irgendwann muss ich auch da 
durch...

Weißt Du, ob meine Vermutung mit der 16bit-Auflösung beim 
zusammengesetzten 32bit-Counter riichtig ist? Oder hat jemand eine echte 
32bit-PWM mit dem Zusammenschalten zweier Timer produziert? Mein 
HiRes-Problem habe ich bisher auch nicht lösen können...

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

qzeta5 schrieb:
> Deine Lösung funktioniert (fast) tatsächlich! Nur dass ich den
> Compare-Wert um 1 höher setzen muss als PER.

Meinte ich doch... verdammt ;-)

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.