Forum: Compiler & IDEs Einstellung Tastgrad bei Fast PWM


von Daniel (Gast)


Lesenswert?

Hallo,

ich habe ein kleines Problem und hoffe, mit der Anfängerfrage nicht zu 
sehr zu nerven:

Ich habe eine Fast PWM programmiert, funktioniert auch wunderbar:
1
int main (void)
2
{
3
  DDRD |= (1 << PD5); // PWM Ausgang
4
  DDRB = 0x00;
5
  DDRA = 0xff;
6
7
  TIMSK |= (1<<OCIE1A); //   Interrupt bei Compare A
8
     TCCR1A |= (1 << COM1A1); //set am Periodenanfang, clear bei Vergleich
9
  TCCR1B |= (1 << WGM12); 
10
     TCCR1A |= ( 1 << WGM10  |1<<WGM11);  // FastPWM 10-Bit
11
  OCR1A   = (200);  // Anfangsvergleichwert
12
  
13
     TCCR1B |= ((1 << CS10)); // Starte Timer mit Fcpu/1
14
15
  sei();
16
17
     while(1)
18
     {
19
     }
20
21
22
23
return 1;
24
};


Jetzt würde ich das ganze gerne so einstellen, dass der Tastgrad auf 
Knopfdruck erhöht wird, und setze dafür folgenden Code in die while 
Schleife:
1
  if(debounce(&PINB,PB0))
2
  {
3
  OCR1A += 100;  //Erhöhe Vergleichswert
4
  }

mit
1
uint8_t debounce(volatile uint8_t *port, uint8_t pin)
2
3
{    
4
5
if (  ! (*port & (1 << pin)) )
6
    {
7
    cli();
8
      _delay_us(1);  
9
    if ( ! (*port & (1 << pin)) )
10
    {
11
    _delay_ms(200);
12
    return 1;
13
        }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
14
    }
15
    sei();
16
  return 0;
17
}

Man sieht dann auf dem Oszi, dass der Tastgrad erhöht wird, solange ich 
die Taste gedrückt halte, aber sobald ich sie loslasse, hüpft er wieder 
auf den Anfangswert zurück. Als nächstes habe ich versucht, vor der 
Neuzuweisung von OCR1A die Interrupts global auszuschalten und danach 
wieder einzuschalten, das führt aber nur dazu, dass sich der Tastgrad 
auch beim Halten der Taste nicht mehr verändert.

Sieht jemand von euch den Fehler?

Es geht übrigens um eine ATMega 32 auf einem STK 500, programmiert wird 
mit WinAVR / AVRStudio 4.

Gruß, Daniel

von Helmut L. (helmi1)


Lesenswert?

uint8_t debounce(volatile uint8_t *port, uint8_t pin)

{

if (  ! (*port & (1 << pin)) )
    {
    cli();
      _delay_us(1);
    if ( ! (*port & (1 << pin)) )
    {
    _delay_ms(200);
    return 1;             <<<< Wenn der hier rausspringt bleiben
                               die Interrupts gesperrt
        }
    }
    sei();
  return 0;
}


Gruss Helmi

von Daniel (Gast)


Lesenswert?

Hi,

ich habe grade eine Lösung gefunden:

Ich schalte den OCIE1A vor dem setzen von OCR1A ab, setze den neuen Wert 
und schalte den Interrupt danach nicht wieder explizizt an, und jetzt 
funktioniert. Könnte man natürlich sagen toll, hat funktioniert, aber 
ich würde schon gerne wissen, warum es funktioniert ;) Kann mir jemand 
helfen?

Gruß, Daniel

von Daniel (Gast)


Lesenswert?

Hi,

danke, stimmt, das war ein Fehler. Allerdings bringt nur das leider 
nicht die Lösung, im Gegenteil, jetzt funktioniert meine Lösung (siehe 
Post vorher) nicht mehr.

Gruß, Daniel

von Klaus (Gast)


Lesenswert?

Wie sieht deine Interruptroutine aus?

von Daniel (Gast)


Lesenswert?

Hallo Klaus,

ich hab keine, durch

    TCCR1A |= (1 << COM1A1);

macht das der Timer selbst. Falls du das Datenblatt 
(http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf) zur Hand 
hast, das steht in Tabelle 45.

von Helmut L. (helmi1)


Lesenswert?

Kann es sein das die Routine _delay_ms() auch auf Interrupts angewiesen 
ist ?  Wenn du die Interrupst vorher sperrst kommt der Prozesoor da 
niemehr raus.

von Daniel (Gast)


Lesenswert?

Das scheint in die richtige Richtung zu gehen...

Nur mal als Zwischenstand, hier der aktuelle Code:
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
uint8_t debounce(volatile uint8_t *port, uint8_t pin)
8
9
{    
10
11
if (  ! (*port & (1 << pin)) )
12
    {
13
    TIMSK &= ~(1<<OCIE1A);
14
      _delay_us(1);  
15
    if ( ! (*port & (1 << pin)) )
16
    {
17
    _delay_ms(200);
18
    TIMSK |= (1<<OCIE1A);
19
    return 1;
20
        }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
21
    }
22
    TIMSK |= (1<<OCIE1A);
23
  return 0;
24
}
25
26
int main (void)
27
{
28
  DDRD |= (1 << PD5); // PWM Ausgang
29
     DDRB = 0x00;
30
  DDRA = 0xff;
31
32
  TIMSK |= (1<<OCIE1A); //   Interrupt bei Compare A
33
     TCCR1A |= (1 << COM1A1); //set am Periodenanfang, clear bei Vergleich
34
  TCCR1B |= (1 << WGM12 | 1<<WGM13); 
35
     TCCR1A |= (1<<WGM11);  // FastPWM 10-Bit
36
  ICR1 = 128;
37
  OCR1A   = (32);  // Anfangsvergleichwert
38
  
39
     TCCR1B |= ((1 << CS10)); // Starte Timer mit Fcpu/1
40
41
  TIMSK |= (1<<OCIE1A);  //bzw. sei();
42
43
     while(1)
44
     {
45
  if(debounce(&PINB,PB0))
46
  {
47
  OCR1A += 10;  //Erhöhe Vergleichswert
48
  }
49
  }
50
51
52
return 1;
53
};

So funktioniert es wunderbar. Wenn ich aber vor der while Schleife 
sei(); schreibe, habe ich wieder das alte Problem, dass er den Tastgrad 
setzt, wenn ich die Taste halte, aber danach wieder zurück hüpft. Das 
ist mir noch etwas unverständelich... Insbesondere, wo er den alten Wert 
wieder her nimmt.

von Helmut L. (helmi1)


Lesenswert?

>So funktioniert es wunderbar. Wenn ich aber vor der while Schleife
>sei(); schreibe, habe ich wieder das alte Problem, dass er den Tastgrad

  TIMSK |= (1<<OCIE1A); //   Interrupt bei Compare A

Wo ist denn die dazu gehoereige Interruptservice Routine ?
So springt er irgendwo hin.

von Daniel (Gast)


Lesenswert?

Die wäre doch eh leer. Also ISR(TIMER1_COMPA_vect){}; Muss sie trotzdem 
da stehen?

von Klaus (Gast)


Lesenswert?

Die Frage ist, muss der Interrupt überhaupt freigegeben werden, wenn du 
keine Interrutserviceroutine brauchst?

von Daniel (Gast)


Lesenswert?

Tatsache, das wars... Vielen Dank! Ich hatte halt gedacht, dass ich die 
ISR auch weglassen kann, wenn sie eh nichts tut ;)

von Daniel (Gast)


Lesenswert?

Oh, hatte eigentlich gedacht, dass sie das müssen, weil der Pin ja bei 
Compare umgesetzt werden muss. Aber habs grade mal ausprobiert, das geht 
ja auch ohne... Dann hatte ich da wohl was falsch verstanden ;)

Vielen Dank nochmal!

von Helmut L. (helmi1)


Lesenswert?

>Tatsache, das wars... Vielen Dank! Ich hatte halt gedacht, dass ich die
>ISR auch weglassen kann, wenn sie eh nichts tut ;)

Auch wenn sie scheinbar nichts tut , tut sie doch etwas .
Zumindest ist dort ein Return Befehl der ins normale Programm 
zurueckspringt. Auch werden Hardwaremaessig einige Flags geloescht.

von gast (Gast)


Lesenswert?

hallo, zur pwm existieren bereits so viele threads, dass ich mir mit 
meiner frage diesem thread anschließen möchte.
ich habe, das als erste gepostete programm von daniel leicht für meine 
bedürfnisse abgeändert:
1
                
2
int main (void)
3
{initRP6Control();
4
5
6
  DDRD |= (1 << PD4); // PWM Ausgang
7
  
8
  
9
     TCCR1A |= (1 << COM1A1); //set am Periodenanfang, clear bei Vergleich
10
   
11
  TCCR1A |= (1 << WGM12);
12
     TCCR1A |= ( 1 << WGM10  |1<<WGM11);  // FastPWM 10-Bit
13
   
14
  
15
     TCCR1B |= ((1 << CS10)); // Vorteiler= 1
16
   
17
OCR1A =(900);
18
  
19
     while(1)
20
     {
21
     }
22
23
return 1;
24
};

dazu hab ich jetzt das TCCR1B |= (1 << WGM12); mit TCCR1A |= (1 << 
WGM12); abgeändert, da es meiner meinung nach laut datenblatt so richtig 
ist.
kann mir vielleicht jemand sagen, ob ich mit dieser einschätzung richtig 
liege, bzw ob dieses programm so funktionieren kann?

gruß

von gast (Gast)


Lesenswert?

kann mir denn gar niemand weiterhelfen?

von Karl H. (kbuchegg)


Lesenswert?

gast schrieb:
> kann mir denn gar niemand weiterhelfen?

Die WGM Bits sind bei unterschiedlichen Porzessoren in unterschiedlichen 
Registern. Wenn im Datenblatt deines Prozessors steht, dass es im TCCR1A 
Register ist, dann wird das schon stimmen. Das Datenblatt hat immer 
recht.

> kann mir denn gar niemand weiterhelfen?

Soll das heißen, du hast jetzt 2 Tage auf Antwort gewartet ohne die 
Veränderung wenigstens einmal auszuprobieren?
Die Frage kannst du dir doch in 3 Minuten selbst beantworten. Probiers 
so aus wie es Daniel gepostet hat, probier deine Version aus. Welche 
funktioniert? Zur Sicherheit nochmal im Datenblatt nachgesehen, ob du 
dich mit der Interpretation des Datenblattes nicht vertan hast, und dann 
weißt du es mit Sicherheit.

von gast (Gast)


Lesenswert?

also, jetzt hab ich beides ausprobiert und muss sagen, dass beides geht. 
es scheint also egal ob man TCCR1B |= (1 << WGM12) oder TCCR1A |= (1 << 
WGM12) setzt. ich konnte im datenblatt zwar nur WGM12 in verbindung mit 
TCCR1A finden, aber es funktioniert auch so wie es daniel oben gemacht 
hat.

vielen dank trotzdem

von Karl H. (kbuchegg)


Lesenswert?

gast schrieb:
> also, jetzt hab ich beides ausprobiert und muss sagen, dass beides geht.
> es scheint also egal ob man TCCR1B |= (1 << WGM12) oder TCCR1A |= (1 <<
> WGM12) setzt. ich konnte im datenblatt zwar nur WGM12 in verbindung mit
> TCCR1A finden,

Gut.
Jetzt siehst du nochmal im Datenblatt nach, welches Bit im TCCR1A das 
Bit WGM12 ist.
Mit dieser Bitnummer suchst du dann die Beschreibung vom TCCR1B Register 
auf und siehst nach, was das Bit mit der gleichen Nummer in diesem 
Register macht.

Damit weist du dann auch, welche Funktion des Timers du noch 
eingeschaltet hast ohne es zu merken.

Hinweis:
Kleb nicht an der Bezeichnung WGM12. Die wird vom Compiler einfach nur 
in eine Zahl übertragen. Und dem Compiler ist es völlig egal, ob es im 
betreffenden Register ein Bit dieses Namens gibt. Denn wie gesagt, die 
Bezeichnung WGM12 steht einfach nur für eine Zahl.

Letztendlich steht bei

   TCCR1B |= ( 1 << WGM12 );

im Grunde nur da: Setzte Bit Nr 3 (3 jetzt mal als Hausnummer genommen) 
im Register TCCR1B.
Du hättest genausogut auch schreiben können
   TCCR1B |= ( 1 << PD3 );

Würde genau das gleiche machen (wenn WGM12 wirklich 3 ist), oder 
irgendeinen anderen Ausdruck der 3 ergibt

   TCCR1B |= ( 1 << ( 9 - 6 ) );

oder TCCR1B |= ( 1 << ( 6 / 2 ) );

Ist alles das Gleiche.

Daher ist es wichtig, sich im Datenblatt seines Prozessors anzusehen, 
welches Bit in welchem Register was macht und nicht einfach blind 
irgendwelche Bits in irgendwelchen Registern zu setzen, nur weil 
irgendeine literarische Vorlage das auch so macht. Du musst 
Funktionalität von einem Prozessor zu einem anderen Prozessor 
übertragen. Aber kein Mensch garantiert dir, dass auf einem anderen 
Prozessor dieselbe Funktionalität mit den gleichen Bits in den gleichen 
Registern erreicht wird.

Und daher ist es auch wichtig, dass du bei solchen Fragen im Forum immer 
auch deinen Prozessor angibst, damit die Helfenden das richtige 
Datenblatt herausholen können um darin nachzusehen, welche Bits du 
setzen musst.

von gast (Gast)


Lesenswert?

also,
 jetzt hab ich nochmal nachgeschaut. das WGM 12 Bit bei TCCR1A schaltet 
meiner meinung nach den CTC modus des timers 1 ein.
da ich diesen modus nicht brauche sondern lediglich eine LED mit PWM 
ansteuern will, brauche ich das WGM12 bit auch nicht setzen.
habs jetzt mal ohne probiert und funktioniert genauso.
mein prozessor ist der gleiche wie der von daniel, nämlich ein Atmel 
Atmega 32.

gruß

von Karl H. (kbuchegg)


Lesenswert?

gast schrieb:

> mein prozessor ist der gleiche wie der von daniel, nämlich ein Atmel
> Atmega 32.

Ich weiß nicht, wo du ständig nachsiehst.
Wenn ich in meinem Datenblatt die Register Description vom Timer 1 
aufschlage und dort unter TCCR1A nachsehe, dann existiert in TCCR1A ganz 
einfach kein Bit mit dem Namen WGM12

Es gibt aber sehr wohl in TCCR1B das entsprechende Bit.

Geh ich in die Tabelle "Waveform Generation Mode Bit Description", dann 
findet sich dort der Modus 7 "Fast PWM,10Bit" bei dem WGM12, WGM11 und 
WGM10 gesetzt sind. Achtung: Nur weil diese Bits in dieser Tabelle 
zusammengefasst sind bedeutet das nicht, dass sie im selben Register 
sind! Die können über TCCR1A bzw. TCCR1B verstreut sein. Aufschluss 
darüber gibt nur die entsprechende Beschreibung des jeweiligen 
Registers.

"Fast PWM, 10 Bit" stimmt auch auffallend mit dem Kommentar in Daniels 
Programm überein.


Edit:
Die Beschreibung des TCCR1A Registers ist im Datenblatt der Abschnitt 
16.10.1 auf Datenblattseite 110.
Gleich zu Beginn des Abschnitts ist eine Grafik, in der alle Bits dieses 
Registers aufgeführt sind. WGM10 und WGM11 sind da drinnen. Aber von 
WGM12 fehlt jede Spur

Die bewusste Modus Tabelle ist auf Seite 114 und gleich darunter beginnt 
auch die Beschriebung von TCCR1B mit der gleichwertigen Grafik, in der 
sich WGM12 und WGM13 wiederfinden.

Wenn du selbst einen bestimmten Modus brauchst, dann gehst du in diese 
Tabelle und wählst den entsprechenden Modus aus. Hast du den Modus, 
siehst du in der Tabelle nach, welche Bits dafür gesetzt werden müssen. 
Und dann sieht man nach in welchem Register, TCCR1A oder TCCR1B, das 
entsprechende Bit zu finden ist.

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.