Forum: Mikrocontroller und Digitale Elektronik PWM funktion OCR1 atmega328p


von Robin G. (gawin)


Angehängte Dateien:

Lesenswert?

Hi leute,
ich brauchte PWM und da ich mich noch nicht so gut damit auskannte, hab 
ich erst mal nen paar tut's gelesen und ne Funktion für den Allgemeinen 
gebrauch geschrieben. Klappt auch alles super nur kommt mir die funktion 
nen bisschen aufgebläht vor und da dachte ich mir, vllt könnte mir 
jemand nen paar Tipps geben um die Funktion zu verkleinern?
Die Funktion ist im anhang.

Danke schonmal ;)
Gruß Robin

edit: seh gerade hab beim if/else "prescaler" einmal noch 64 statt 1024, 
kleiner copy/past fehler halt ;)

von Stefan W. (dl6dx)


Lesenswert?

Hi Robin,

ich hab die Funktion mal etwas umgebaut:
1
void OCR1_PWM(boolean B,
2
        boolean inverted,
3
        boolean fast,
4
        uint8_t bit,
5
        uint16_t prescaler,
6
        uint16_t value)
7
  {
8
  if(!B)
9
    {
10
    DDRB |= (1 << DDB1);
11
    OCR1A = value;
12
    TCCR1A |= inverted ? (1 << COM1A1) | (1 << COM1A0) : (1 << COM1A1);
13
    }
14
  else
15
    {
16
    DDRB |= (1 << DDB2);
17
    OCR1B = value;
18
    TCCR1A |= inverted ? (1 << COM1B1) | (1 << COM1B0) : (1 << COM1B1) ;
19
    }
20
21
  switch(bit)
22
    {
23
    case 8:
24
      TCCR1A |= (1 << WGM10);
25
      break;
26
    case 9:
27
      TCCR1A |= (1 << WGM11);
28
      break;
29
    case 10;
30
      TCCR1A |= (1 << WGM11) | (1 << WGM10);
31
      break;
32
    default:
33
      // hier ggf. eine Fehlerbehandlung für ungültige Parameter einbauen
34
      break;
35
    }
36
    
37
  if(fast)
38
    {
39
    TCCR1A |= (1 << WGM12);
40
    }
41
42
  switch(prescaler)
43
    {
44
    case 0;
45
      TCCR1B |= (1 << CS10);
46
      break;
47
    case 8:
48
      TCCR1B |= (1 << CS11);
49
      break;
50
    case 64:
51
      TCCR1B |= (1 << CS11) | (1 << CS10);
52
      break;
53
    case 256:
54
      TCCR1B |= (1 << CS12);
55
      break;
56
    case 1024:
57
      TCCR1B |= (1 << CS12) | (1 << CS10);
58
      break;
59
    default:
60
      // hier ggf. eine Fehlerbehandlung für ungültige Parameter einbauen
61
      break;
62
    }
63
  }

Der Code sollte nach diff-vergleich äquivalent sein. Falls du Lust hast, 
das mal zu kompilieren, würde mich der Codeumfang im Vergleich 
interessieren.

Grüße

Stefan

von holger (Gast)


Lesenswert?

Das mit den ganzen |= ist gefährlich. Bereits gesetzte
Bits werden so nicht mehr zurückgesetzt.

von Robin G. (gawin)


Lesenswert?

@holger: Wie gehts denn besser?

@Stefan: hab es mal kompiliert und braucht in folgendem beispielt 22byte 
weniger ;)
1
int main(void){
2
  DDRC |= 0x03;
3
  int i;
4
  while(1){
5
    for(i=1;i<=63;i+=1){
6
      OCR1_PWM(0, 0, 0, 10, 64, i);
7
      _delay_ms(40);
8
    }
9
    for(i=63;i>=1;i-=1){
10
      OCR1_PWM(0, 0, 0, 10, 64, i);
11
      _delay_ms(40);
12
    }
13
  }
14
  return 0;
15
}

von Karl H. (kbuchegg)


Lesenswert?

Robin Gerhartz schrieb:
> @holger: Wie gehts denn besser?

Indem du auch die jeweils relevanten Bits auf 0 setzt und nicht nur 
diejenigen auf 1, die du brauchst.

Oder auf Deutsch: Du hast da eine Annahme getroffen. Nämlich die, das 
die anderen Bits automatisch 0 sind. Zb. beim Prescaler. Ist als 
Prescaler 8 angegeben, dann setzt du CS11. Aber was ist mit den anderen 
Bits? In einer allgemein verwendbaren Funktion darfst du nicht davon 
ausgehen, dass diese automatisch 0 wären. Was ist wenn du die Funktion 2 
mal mit verschiedenen Prescalern aufrufst? Passiert dann immer noch das 
richtige?
1
  switch(prescaler)
2
    {
3
    ...
4
5
6
    case 8:
7
      TCCR1B &= ~( ( 1 << CS12 ) | ( 1 << CS10 ) );
8
      TCCR1B |= (1 << CS11);
9
      break;
10
11
    ....

jetzt tut es das.

Auch so

1
  TCCR1B &= ~( ( 1 << CS12 ) | ( 1 << CS11 ) ( 1 << CS10 ) );
2
3
  switch(prescaler)
4
    {
5
    ...
6
7
8
    case 8:
9
      TCCR1B |= (1 << CS11);
10
      break;
11
12
    ....

tut es das, ohne das du dich bei den Einzelfällen damit abquälen musst.

Wenn man einmal weiter denkt, dann wäre es vernünftig
* die 2. Variante zu benutzen
* das Rücksetzen der Prescaler Bits ganz am Anfang der Funktion zu 
machen.

Denn: Wenn der Timer bereits läuft und eine PWM generiert, dann ist 
nicht vorhersehbar was passieren wird, wenn man dem bereits laufenden 
Timer die PWM während des Betriebs umkonfiguriert.

Also
1
void OCR1_PWM(boolean B,
2
        boolean inverted,
3
        boolean fast,
4
        uint8_t bit,
5
        uint16_t prescaler,
6
        uint16_t value)
7
{
8
  TCCR1B &= ~( ( 1 << CS12 ) | ( 1 << CS11 ) ( 1 << CS10 ) );
9
10
  ....

So ist sicher gestellt, dass
* die Bits auf jeden Fall 0 sind
* der Timer angehalten ist, so dass man relativ gefahrlos die
  PWM Einstellung ändern darf.

von Robin G. (gawin)


Angehängte Dateien:

Lesenswert?

Gut hab die Bits vorher mal alle zurück gesetzt.
Und mir is noch auf gefallen das ich den Bit WGM12 im falschen Register 
setzen wollte, wurde auch korrigiert. Es braucht im folgendem Beispiel 
laut AVRDude auf dem µc 502bytes.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <m328p/OCR1.c>
4
5
int main(void){
6
  int i;
7
  while(1){
8
    for(i=1;i<=63;i+=1){
9
      OCR1_PWM(0, 0, 1, 10, 64, i);
10
      _delay_ms(40);
11
    }
12
    for(i=63;i>=1;i-=1){
13
      OCR1_PWM(0, 0, 0, 10, 64, i);
14
      _delay_ms(40);
15
    }
16
  }
17
  return 0;
18
}

Geht da noch was?

Gruß Robin

von Robin G. (gawin)


Angehängte Dateien:

Lesenswert?

So hab auch mal für die beiden anderen Timer auf dem Atmega328p ne 
Funktion geschrieben und default überall "8Bit" oder "ohne Prescaler" 
eingestellt.

Meine frage jetzt sind die so in Ordnung oder sollte ich noch was 
verändern?

Bespiel:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
// #include <m328p/OCR0.c>
4
// #include <m328p/OCR1.c>
5
#include <m328p/OCR2.c>
6
7
int main(void){
8
  int i;
9
  while(1){
10
    for(i=10;i<=70;i+=2){
11
      OCR2_PWM(0, 0, 1, 32, i);
12
      _delay_ms(33);
13
    }
14
    for(i=70;i>=10;i-=2){
15
      OCR2_PWM(0, 0, 1, 32, i);
16
      _delay_ms(33);
17
    }
18
  }
19
  return 0;
20
}

OCR0 - avrdude: 398 bytes of flash verified
OCR1 - avrdude: 498 bytes of flash verified
OCR2 - avrdude: 470 bytes of flash verified

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.