Forum: Compiler & IDEs Timer0 Umschaltung "clear" auf "set" FastPWM


von Gero G. (jumbo)


Angehängte Dateien:

Lesenswert?

Hallo,

Ich habe folgendes Problem. Ich möchte in 8 verschiedenen 
Timer-durchläufen bei einem Atmega644 unterschiedliche Pulslängen und 
Pegelflanken am OC0A Pin erzeugen. Initialisiert habe ich Timer0 mit 
FastPWM, von 0-0xFF und Overflow interrupt. Bei jedem Interrupt lade ich 
einen neuen Wert ins OCR0A Register und sage ob der Pin bei match 
gesetzt "set" werden soll oder gelöscht "clear" werden soll. Es 
funktioniert eigentlich alles, ausser der letzte Durchgang an dem ich 
den Pin löschen möchte funktioniert nicht. Der Timer läuft immer voll 
(high) durch. Umgekehrt, der letzte "set" Durchgang funktionert wenn ich 
danach COM0A0 auf "1" setzte. Hier mal die ISR:
/*********************************************************************** 
******/
ISR(TIMER0_OVF_vect)
{
  static unsigned char CycleCount_uc=0,CycleRest_uc=0,ReloadValue_uc=0;
  static unsigned int Temp_ui;
  static unsigned int CycleTime_ui = 5760;
  static unsigned int ServoValue1_ui=432;


  if(CycleCount_uc==0)
  {
    sbi(TIFR0,OCF0A);
    sbi(TCCR0A,COM0A0);
    //sbi(TCCR0A,COM0A1);
    OCR0A=255;
  }

  if(CycleCount_uc==1)
  {
    sbi(TIFR0,OCF0A);
    sbi(TCCR0A,COM0A0);
    //sbi(TCCR0A,COM0A1);
    OCR0A=255;
  }

  if(CycleCount_uc==2)
  {
    sbi(TIFR0,OCF0A);
    sbi(TCCR0A,COM0A0);
    //sbi(TCCR0A,COM0A1);
    OCR0A=255;
  }

  if(CycleCount_uc==3)
  {
    sbi(TIFR0,OCF0A);
    sbi(TCCR0A,COM0A0);
    //sbi(TCCR0A,COM0A1);
    OCR0A=255;
  }

  if(CycleCount_uc==4)
  {
    sbi(TIFR0,OCF0A);
    cbi(TCCR0A,COM0A0);
    //sbi(TCCR0A,COM0A1);
    OCR0A=150;
  }

  if(CycleCount_uc==5)
  {
    sbi(TIFR0,OCF0A);
    cbi(TCCR0A,COM0A0);
    sbi(TCCR0A,COM0A1);
    OCR0A=100;// DIESER Durchgang geht nicht!! Warum immer 0xFF
  }
  if(CycleCount_uc==6)
  {
    sbi(TIFR0,OCF0A);
    sbi(TCCR0A,COM0A0);
    sbi(TCCR0A,COM0A1);
    OCR0A=255;
  }

if(CycleCount_uc==7)
{
  sbi(TIFR0,OCF0A);
  sbi(TCCR0A,COM0A0);
  //sbi(TCCR0A,COM0A1);
  OCR0A=255;
  /**********************/
  CycleCount_uc=255;
  /**********************/
}

CycleCount_uc++;

PORTB ^= 0x10;

}
/*******************************/
Anbei auch noch ein Oszi-bild (habe Kanal 2 mit einem 1:10 Tastkopf 
gemessen, also nicht am Pegel stören)

Ich habe im Datenblatt keine Einschränkung dazu gefunden und auch sonst 
keinen Rat mehr. Ich benutze den aktuellen GCC (20100110). Hat jemand 
eine Idee? Ich hoffe ich habe mein Problem halbwegs verständlich rüber 
gebracht.

Danke

von Jörg G. (joergderxte)


Lesenswert?

Ist die compiler optimierung an?
Läuft der Timer mit prescaler=1, d.h. vollem Takt?
 Dann sind bis zu deinem Test auf 5 womöglich schon 100 Takte vergangen.

Und:
- die PWM-pins funktionieren auch, wenn die flags dazu die ganze zeit 
gesetzt sind
- C kennt switch/case (und ggf. else!):
1
/*Alles ungetestet*/
2
ISR(TIMER0_OVF_vect)
3
{
4
    // static-Variablen werden automatisch mit 0 initialisiert
5
    static unsigned char CycleCount_uc, CycleRest_uc, ReloadValue_uc;
6
    static unsigned int Temp_ui;
7
    static unsigned int CycleTime_ui = 5760;
8
    static unsigned int ServoValue1_ui = 432;
9
    switch(CycleCount_uc) {
10
    case 0: // Fall through
11
    case 1:
12
    case 2:
13
    case 3:
14
        OCR0A = 0xFF;
15
        break;
16
    case 4:
17
        OCR0A = 150;
18
        break;
19
    case 5:
20
        OCR0A = 100;// geht's nun?
21
        break;
22
    case 6: //Koennte man mit dem ersten zusammenlegen
23
        OCR0A = 0xFF;
24
        break;
25
    case 7:
26
        OCR0A = 0xFF;
27
        CycleCount_uc = (unsigned char)-1; // Wird noch einmal erhoeht
28
    }
29
    CycleCount_uc++;
30
    PORTB ^= (1<<PB4);
31
}
- Für deine vielen Fälle bietet sich eine 'enum' an

hth, Jörg

von Jörg G. (joergderxte)


Lesenswert?

'Tschuldige bitte - ich hab da ein bisschen zuviel gekürzt, das switch() 
sollte effektiv so aussehen (um das gleiche zu tun wie deine if's):
1
switch(CycleCount_uc) {
2
    case 0:
3
    case 1:
4
    case 2:
5
    case 3:
6
        TCCR0A |= (1<<COM0A0);
7
        OCR0A = 0xFF;
8
        break;
9
    case 4:                     // Wenn der Timer nicht mit
10
        TCCR0A &= ~(1<<COM0A0); // gesetzem COM0A1-Bit initialisiert
11
        OCR0A = 150;            // wurde, ist die PWM jetzt aus !
12
        break;
13
    case 5:
14
        OCR0A = 100;
15
        TCCR0A |= (1<<COM0A1);
16
        break;
17
    case 6:
18
        OCR0A = 0xFF;
19
        TCCR0A |= (1<<COM0A0);
20
        break;
21
    case 7:
22
        OCR0A = 0xFF;
23
        CycleCount_uc = (unsigned char)-1;
24
    }
sry, Jörg

von Gero G. (jumbo)


Lesenswert?

Hallo Jörg,

danke für die schnelle Rückmeldung.

Zu den Punkten. Pre-scaler steht auf 64. Ich denke langsam genug.

Optimierung auf -O2. Was könnte aber hier wegoptimiert werden?

Der code wie ich ihn hier gepostet habe ist das Frust-Ergebnis nach 
unzähligen Versuche. Das ständige zurücksetzen der Flags ist wirklich 
nicht nötig, aber ich wollte nichts unversucht lassen. Auch die ganzen 
If-Dinger. Nicht schön aber ich wollte endlich verstehen wo es 
eigentlich hängt. Jetzt weiss ich zwar wo, aber immer noch nicht warum?

Ich verstehe es halt nicht warum der umgekehrt weg (erst fallende Flanke 
und dann Steigende) funktioniert. Da ist in deinem Beispiel code gar 
nicht enthalten. (je nach dem ob COM0A0 gesetzt oder nicht gesetzt ist)

ups.. habe gerade gesehen du hast schon ein update ... werde ich mal 
ausprobieren

Gruß
Christian

von Gero G. (jumbo)


Lesenswert?

Hallo Joerg,

habe gerade mal deinen Code-Vorschlag getestet. Leider das gleiche 
Ergebnis. d.h. case 4 geht und case 5 wird mit 0xff durchlaufen.

Ich habe auch noch die anderen Compiler-Optimierungen ausprobiert. 
Bringt alle nichts.

Ich weiss nicht mehr weiter, habe keine Idee mehr?

Kann das der AVR in die Richtung nicht schnell genug? Kann doch 
eigentlich nicht sein.

Hat keiner noch eine Idee?

Danke und Gruß
Christian

von Jörg G. (joergderxte)


Lesenswert?

Funktioniert es, wenn du den Pin komplett von Hand steuerst? 
(TIMER0_COMPA-Interrupt dazunehmen)

Sonst:
 - poste mal deine Initialisierung (besser: ein minimales Testprogramm 
als Anhang)
 - Beschreibe mal genauer was du machen willst.

Im Datenblatt ist zwar immer nur von set (oder clear) on match die 
Rede, aber der Pin wird ja auch beim Überlauf geschaltet, soll heißen, 
wenn du irgendwo von clear-on-match auf set-on-match umschaltest, muss 
es einen Durchlauf geben, bei dem sich der Pegel nicht ändert - oder 
schalten die COMxyz-bits den Pin auch sofort, wenn man die ändert (und 
ich bin fehl-/unterinformiert) ?.

hth, Jörg

von Juergen (Gast)


Lesenswert?

So wie ich den Timer0 verstehe, werden die OCR0x Register in den PWM 
Modes nur bei TOP übernommen. Damit wird erreicht, dass es keine 
Glitches gibt.
Der TIMER0_OVF wird aber (kurz) nach TOP ausgeführt, d.h. für den gerade 
begonnenen Durchlauf ist es zu spät.

Volle Kontrolle hat man im CTC Mode:
(
  - OCR0B auf den gewünschten Zeitpunkt setzen
  - TCCR0?:COM0B auf die gewünschte Flanke setzen
  - OC0B-Int enablen
  - bei OC0B-Int von vorne mit den dann aktuellen Zeiten/Flanken
)
aber keine automatische "Rück-Flanke" bei TOP.

Gruß,
Jürgen

von Gero G. (jumbo)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

zu den Vorschlägen:

@Juergen:

Die OCR0x Register werden bei FastPWM bei Bottom aktualisiert. 
(Datenblatt)
Bei PWM (Phase correct) ist Top.

CTC geht nicht, da ich sowohl OCRA und OCRB brauche, und im CTC Mode 
OCRA als Timer reload verwendet wird.

@Joerg:
Ich möchte auf den OC0A / OC0B Pins ein Jitter-freies PM Signal 
erzeugen. Daher kann ich es nicht mit "Handgesteuerten" Ports machen. 
Jedes einzelne PWM Signal soll eine individuelle Zeit bekommen. Auch 
über Timerüberläufe hinweg. Ich habe trotzdem mal deinen Rat befolgt und 
es von Hand gemacht (Ovflow + TIMER0_COMPA-Interrupt) und bin auf mehr 
Fragen als Antworten gestoßen. Hier mal der Code und ein Oszi-bild.

/***********************************************/
void Timer0_Init(void)
{
  TCCR0A=(1<<WGM01)|(1<<WGM00);
  TCCR0B=(1<<CS01)|(1<<CS00);

  sbi(TIMSK0,TOIE0);
  sbi(TIMSK0,OCIE0A);

  cbi(TCCR0A,COM0A0);
  cbi(TCCR0A,COM0A1);
  cbi(TCCR0A,COM0B0);
  cbi(TCCR0A,COM0B1);
  OCR0A=255;
}


ISR(TIMER0_OVF_vect)
{

switch(CycleCount_uc) {
    case 0:
    PORTB &= ~(1<<PB3);
    OCR0A = 0xFF;
    break;
    case 1:
    PORTB &= ~(1<<PB3);
    OCR0A = 0xFF;
    break;
    case 2:

    PORTB &= ~(1<<PB3);
    OCR0A = 0xFF;
    break;
    case 3:
    PORTB &= ~(1<<PB3);
          OCR0A = 0xFF;
          break;
    case 4:
    PORTB |= (1<<PB3);
    OCR0A = 180;
          break;
    case 5:
    PORTB |= (1<<PB3);  // Diese Hochsetzen wird nicht ausgeführt,
    OCR0A = 80;      // obwohl OCR0A richtig geladen und ausgeführt wird
    break;
    case 6:
    PORTB &= ~(1<<PB3);
    OCR0A = 0xFF;
    break;
    case 7:
    PORTB &= ~(1<<PB3);
    OCR0A = 0xFF;
        CycleCount_uc = (unsigned char)-1;
    }
    CycleCount_uc++;
    PORTB ^= (1<<PB4);
}


/*********************************************************************** 
******/
ISR(TIMER0_COMPA_vect)
{

  PORTB &= ~(1<<PB3);
    PORTB ^= (1<<PB4);
}
/**************************************************/

In Case 5 wird die Port-high Schaltung einfach nicht ausgeführt (Oszi 
1,85ms). Stattdessen wird schon bei case 3 der Port high geschaltet 
obwohl ich das gar nicht will. Die COMPA_ISR wird aber immer korrekt 
ausgeführt. (siehe rotes Signal).Zur Erklärung Rot ist PB4 und Blau PB3.
Irgendwie ist immer ein Versatz drin. Diesmal nur ein "Task" nach vorne.

Nebenbei wie kann man code so schön wie du reinkopieren?

Gruß
Christian

von Jörg G. (joergderxte)


Angehängte Dateien:

Lesenswert?

Wie gut funktioniert das Testprogramm im Anhang?

Und deine Schaltung ist in Ordnung (kein anderer Ausgang gegen PB3, kein 
Pull-up vergessen, kalte Lötstelle etc.)?

>Nebenbei wie kann man code so schön wie du reinkopieren?
[c ] ... [/ c] -- steht auch direkt über dem Eingabefenster...

Könntest du das Signal, dass du erzeugen willst genauer beschreiben?

hth, Jörg

von Gero G. (jumbo)


Lesenswert?

Hallo Joerg,

danke für den entscheidenden Tip. Ich habe mal eine andere Hardware 
genommen und da tut alles was ich will. Es ist zum verrückt werden. An 
dem Port hängt nichts dran. Ich habe die gleiche SW auf eine identische 
Platine geflasht und es hat sofort getan. Wie gesagt, keine externe 
Beschaltung auch eine kalte Lötstelle kann ich zu 99% ausschließen. Kann 
es sein das im AVR nur beim Timer0 irgendwas im Eimer ist? So ein Fehler 
ist mir wirklich noch nicht bei den AVR´s untergekommen. Irgendwie bin 
ich jetzt ein bischen verunsichert.

Ich danke für die gute Unterstützung.

Gruß
Christian

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.