Forum: Mikrocontroller und Digitale Elektronik PWM an OC1A und OC1B


von Dennis (Gast)


Lesenswert?

Hallo

ich möchte an einem Atmega328P mehere PWMs erzeugen. Komischerweise 
funktioniert in meinem code nur die erste for Schleife. Die zweite hat 
keine Funktion. Sobald ich aber die erste auskommentiere funktioniert 
die zweite. Dachte es hängt vielleicht mit der Funktion _delay_ms 
zusammen. dies ist aber nicht der Fall. Den PWM erzeuge ich im Mode 5 (8 
Bit).
Kann mir jemand weiterhelfen.
1
void mfc_init (void)
2
{
3
  PWM_MFC1_PIN;
4
  PWM_MFC2_PIN;
5
  PWM_MFC3_PIN;
6
  PWM_MFC4_PIN;
7
  PWM_MFC5_PIN;
8
  
9
  /*** Timer 1 ***/  
10
  TCCR1A = 0x00;
11
  TCCR1B = 0x00;
12
  
13
  reg_help  = TCCR1A;
14
  reg_help |= (1<<WGM10);                    //Mode 5                
15
  //reg_help |= ((1<<WGM11) | (1<<WGM10));
16
  TCCR1A    = reg_help;
17
  
18
  reg_help  = TCCR1B;
19
  //reg_help |= ((1<<WGM13) | (1<<WGM12) | (1<<CS10));
20
  //reg_help |= ((1<<WGM12) | (1<<CS10));
21
  reg_help |= ((1<<WGM12) | (1<<CS11) | (1<<CS10));      //Mode 5, prescale 64
22
  TCCR1B    = reg_help;
23
  
24
  //ICR1H = 0x7F;  
25
  //ICR1L = 0x8C;  
26
  //OCR1AH = 0x3F;
27
  //OCR1AL = 0xC6;
28
  
29
  //OCR1AH = 0x00;  //PWM 50:50                       
30
  //OCR1AL = 0x7F;
31
  OCR1AH = 0x00;
32
  OCR1AL = 0xFF;
33
  
34
  OCR1BH = 0x00;
35
  OCR1BL = 0x3F;
36
  
37
  TCCR1A |= (1<<COM1A1);
38
  TCCR1A |= (1<<COM1B1);
39
}
40
41
void mfc_test (void)
42
{  
43
  unsigned int ui_i;
44
  unsigned char uc_i;
45
  
46
      
47
  for (uc_i = 0x00; uc_i <= 0xFF; uc_i++)
48
  {
49
    //OCR1AH = ((unsigned char) (ui_i >> 8));
50
    //OCR1AL = ((unsigned char) (ui_i & 0x00FF));
51
    OCR1AL = uc_i;
52
    //OCR1BL = uc_i;    
53
    //_delay_ms(25);
54
    for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
55
        
56
  }
57
  
58
  
59
  for (uc_i = 0x00; uc_i <= 0xFF; uc_i++)
60
  {
61
    OCR1BL = uc_i;
62
    for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
63
    //_delay_ms(25);
64
  }
65
                  
66
}
67
68
int main (void)
69
{
70
  mfc_init();
71
72
73
  while(1)
74
  {
75
    mfc_test();
76
  }
77
}

von Wolfgang (Gast)


Lesenswert?

Dennis schrieb:
> for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
> ...
> for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);

Bist du dir sicher, dass dein Compiler das nicht wegoptimiert?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Das läuft alles so schnell durch, das du davon ohne Oszilloskop gar 
nichts mitkriegst. Bau also die _delay_ms() wieder ein.

: Bearbeitet durch User
von fop (Gast)


Lesenswert?

1
unsigned char uc_i;

Das gibt oft nur eine 8 Bit breite Variable. Also eine, die Werte von 0 
bis 255 aufnehmen kann.
Damit ist die Bedingung
1
uc_i <= 0xFF
 quasi immer erfüllt. Deine Schleife läuft also länger als Du denkst. Um 
genau zu sein : ewig.

von ich (Gast)


Lesenswert?

fop schrieb:
> Das gibt oft nur eine 8 Bit breite Variable. Also eine, die Werte von 0
> bis 255 aufnehmen kann.
> Damit ist die Bedingunguc_i <= 0xFF quasi immer erfüllt. Deine Schleife
> läuft also länger als Du denkst. Um
> genau zu sein : ewig.

0xFF passt aber auch in 8 Bit...

von Dennis (Gast)


Lesenswert?

Wolfgang schrieb:
> Dennis schrieb:
>> for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
>> ...
>> for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
>
> Bist du dir sicher, dass dein Compiler das nicht wegoptimiert?

Bin mir Sicher. Es läuft ja. Aber eben immer nur eine der beiden for 
Schleifen. Wenn is z.B "//OCR1BL = uc_i;" wieder einkommentiere sehe ich 
wie beide Ausgänge von 0x00 - 0xFF durchlaufen. Habe sicherheitshalber 
aber auch die optimierung auf "none" gesetzt.

Matthias S. schrieb:
> Das läuft alles so schnell durch, das du davon ohne Oszilloskop gar
> nichts mitkriegst. Bau also die _delay_ms() wieder ein.

Ich habe ein Oszi dran. Ich kann sehr schön sehen wie die PWM von 0x00 - 
0xFF durchlaufen wird.

fop schrieb:
> unsigned char uc_i;
> Das gibt oft nur eine 8 Bit breite Variable. Also eine, die Werte von 0
> bis 255 aufnehmen kann.
> Damit ist die Bedingunguc_i <= 0xFF quasi immer erfüllt. Deine Schleife
> läuft also länger als Du denkst. Um
> genau zu sein : ewig.

Wie gesagt, es geht ja.

von Maxim B. (max182)


Lesenswert?

Dennis schrieb:
> reg_help  = TCCR1A;
>   reg_help |= (1<<WGM10);                    //Mode 5
>   //reg_help |= ((1<<WGM11) | (1<<WGM10));
>   TCCR1A    = reg_help;
>
>   reg_help  = TCCR1B;
>   //reg_help |= ((1<<WGM13) | (1<<WGM12) | (1<<CS10));
>   //reg_help |= ((1<<WGM12) | (1<<CS10));
>   reg_help |= ((1<<WGM12) | (1<<CS11) | (1<<CS10));      //Mode 5,
> prescale 64
>   TCCR1B    = reg_help;

Ja, ich verstehe diese Tänze nicht...
Könntest du doch nicht einfach schreiben:
1
TCCR1A |= (1<<WGM10);
2
TCCR1B |= ((1<<WGM12) | (1<<CS11) | (1<<CS10));
 ?

von fop (Gast)


Lesenswert?

ich schrieb:
> 0xFF passt aber auch in 8 Bit...

Aber 8 Bit sind immer kleiner oder gleich 0xFF.

von Dennis (Gast)


Lesenswert?

Maxim B. schrieb:
>
> Ja, ich verstehe diese Tänze nicht...
> Könntest du doch nicht einfach schreiben:TCCR1A |= (1<<WGM10);
> TCCR1B |= ((1<<WGM12) | (1<<CS11) | (1<<CS10));  ?

Für mein Problem nicht relevant...

von Maxim B. (max182)


Lesenswert?

Dennis schrieb:
> Für mein Problem nicht relevant...

Aber deine Schreibweise ist relevant.
Kuck mal weiter: überall gleiche Styl!

Ich würde dir empfehlen, ein bißchen über C-Sprache zu lesen.

Du schreibst auf C so, als ob du Assembler hättest.

Compiler ist kein Narr, er sieht, daß etwas nicht stimmt, und wirkt 
verrückt.

Um Problem besser zu verstehen, kannst du compilierte asm ankucken. Ich 
glaube nach deinem Styl, gerade mit asm hast du kein Problem.

: Bearbeitet durch User
von Dennis (Gast)


Lesenswert?

Maxim B. schrieb:
> Dennis schrieb:
>> Für mein Problem nicht relevant...
>
> Aber deine Schreibweise ist relevant.
> Kuck mal weiter: überall gleiche Styl!
>
> Ich würde dir empfehlen, ein bißchen über C-Sprache zu lesen.
>
> Du schreibst auf C so, als ob du Assembler hättest.
>
> Compiler ist kein Narr, er sieht, daß etwas nicht stimmt, und wirkt
> verrückt.
>
> Um Problem besser zu verstehen, kannst du compilierte asm ankucken. Ich
> glaube nach deinem Styl, gerade mit asm hast du kein Problem.

Also, jetzt habe ich besagte zuweisung direkt gemacht. Siehe da, kein 
Unterschied... Und wo ist "überal gleiche Styl". Deine Vorschläge helfen 
mir nicht weiter.

von S. Landolt (Gast)


Lesenswert?

Liegt es vielleicht daran, dass OC1A/B nicht als Ausgänge definiert 
wurden? Mir ist unklar, warum derzeit überhaupt irgendetwas zu sehen 
ist.

von Maxim B. (max182)


Lesenswert?

Dennis schrieb:
> Deine Vorschläge helfen
> mir nicht weiter.

Auch Vorschlag, ein C-Buch zu lesen?

Bitte begreife: je verständlicher und durchsichtiger dein Code, um so 
weniger wahrscheinlich, daß du Fehler schreibst.

Hast du schon Ergebnis in asm gekuckt?
1
void mfc_test (void)
2
{  
3
  unsigned int ui_i;
4
  unsigned char uc_i;
5
  
6
      
7
  for (uc_i = 0x00; uc_i <= 0xFF; uc_i++)
8
  {
9
    //OCR1AH = ((unsigned char) (ui_i >> 8));
10
    //OCR1AL = ((unsigned char) (ui_i & 0x00FF));
11
    OCR1AL = uc_i;
12
    //OCR1BL = uc_i;    
13
    //_delay_ms(25);
14
    for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
15
        
16
  }
17
  
18
  
19
  for (uc_i = 0x00; uc_i <= 0xFF; uc_i++)
20
  {
21
    OCR1BL = uc_i;
22
    for (ui_i = 0x0000; ui_i <= 0x7FFF; ui_i++);
23
    //_delay_ms(25);
24
  }
25
                  
26
}
Kannst du bitte sagen:
1. warum willst du OSR-Register mal gesamt und mal byteweise schreiben? 
Welche Gründe hast du dafür?
2. kannst du mit einfachen Worten beschreiben, was deine Funktion machen 
sollte?

: Bearbeitet durch User
von Martin H. (horo)


Lesenswert?

Die Lösung kam bereits viel weiter oben, nochmal zum Nachdenken an 
Dennis.

1. Welche Werte kann ui_c annehmen?
2. Wann bricht die Schleife ab?
3. Wie oft wird mach_irgend_was() ausgeführt?
1
   
2
  for (unsigned char uc_i = 0x00; uc_i <= 0xFF; uc_i++) {
3
    mach_irgend_was();
4
  }

von Dennis (Gast)


Lesenswert?

S. Landolt schrieb:
> Liegt es vielleicht daran, dass OC1A/B nicht als Ausgänge
> definiert
> wurden? Mir ist unklar, warum derzeit überhaupt irgendetwas zu sehen
> ist.

Entschuldige, da habe ich defines benutzt.
1
/********************************* MFC ******************************************************************************************************/
2
#define PWM_MFC1_PIN    DDRD |= (1<<DDD3);PORTD |= (1<<PORTD3)    
3
#define PWM_MFC1_HI      TCCR2A &= ~(1<<COM2B1);PORTD |= (1<<PORTD3)  
4
#define PWM_MFC1_LO      TCCR2A &= ~(1<<COM2B1);PORTD &= ~(1<<PORTD3)
5
#define PWM_MFC1_NORM    TCCR2A |= (1<<COM2B1)            
6
#define PWM_MFC1_TOGGLE    PIND |= (1<<PIND3)              
7
8
#define PWM_MFC2_PIN    DDRD |= (1<<DDD5);PORTD |= (1<<PORTD5)    
9
#define PWM_MFC2_HI      TCCR0A &= ~(1<<COM0B1);PORTD |= (1<<PORTD5)  
10
#define PWM_MFC2_LO      TCCR0A &= ~(1<<COM0B1);PORTD &= ~(1<<PORTD5)
11
#define PWM_MFC2_NORM    TCCR0A |= (1<<COM0B1)            
12
#define PWM_MFC2_TOGGLE    PIND |= (1<<PIND5)              
13
14
#define PWM_MFC3_PIN    DDRD |= (1<<DDD6);PORTD |= (1<<PORTD6)    
15
#define PWM_MFC3_HI      TCCR0A &= ~(1<<COM0A1);PORTD |= (1<<PORTD6)              
16
#define PWM_MFC3_LO      TCCR0A &= ~(1<<COM0A1);PORTD &= ~(1<<PORTD6)                
17
#define PWM_MFC3_NORM    TCCR0A |= (1<<COM0B1)                    
18
#define PWM_MFC3_TOGGLE    PIND |= (1<<PIND5)                                      
19
20
#define PWM_MFC4_PIN    DDRB |= (1<<DDB1);PORTB |= (1<<PORTB1)    
21
#define PWM_MFC4_HI      TCCR1A &= ~(1<<COM1A1);PORTB |= (1<<PORTB1)  
22
#define PWM_MFC4_LO      TCCR1A &= ~(1<<COM1A1);PORTB &= ~(1<<PORTB1)
23
#define PWM_MFC4_NORM    TCCR1A |= (1<<COM1A1)            
24
#define PWM_MFC4_TOGGLE    PINB |= (1<<PINB1)              
25
26
#define PWM_MFC5_PIN    DDRB |= (1<<DDB2);PORTB |= (1<<PORTB2)    
27
#define PWM_MFC5_HI      TCCR1A &= ~(1<<COM1B1);PORTB |= (1<<PORTB2)  
28
#define PWM_MFC5_LO      TCCR1A &= ~(1<<COM1B1);PORTB &= ~(1<<PORTB2)
29
#define PWM_MFC5_NORM    TCCR1A |= (1<<COM1B1)            
30
#define PWM_MFC5_TOGGLE    PINB |= (1<<PINB1)            
31
/********************************************************************************************************************************************/

Die Ausgänge funktionieren.

von Dennis (Gast)


Lesenswert?

fop schrieb:
> unsigned char uc_i;
> Das gibt oft nur eine 8 Bit breite Variable. Also eine, die Werte von 0
> bis 255 aufnehmen kann.
> Damit ist die Bedingunguc_i <= 0xFF quasi immer erfüllt. Deine Schleife
> läuft also länger als Du denkst. Um
> genau zu sein : ewig.



Martin H. schrieb:
> Die Lösung kam bereits viel weiter oben, nochmal zum Nachdenken an
> Dennis.
>
> 1. Welche Werte kann ui_c annehmen?
> 2. Wann bricht die Schleife ab?
> 3. Wie oft wird mach_irgend_was() ausgeführt?
>
>   for (unsigned char uc_i = 0x00; uc_i <= 0xFF; uc_i++) {
>     mach_irgend_was();
>   }

Ich honk, vielen Dank euch beiden!

von Jack (Gast)


Lesenswert?

Dennis schrieb:
> Ich honk, vielen Dank euch beiden!

Was gemerkt? Deine Einstellung/Arroganz beeinflusst wesentlich die 
Problemlösung. Du hast es in deiner unendlichen Weisheit drei Mal 
vorgezogen die Hinweise auf das Problem zu ignorieren.

Sogar jetzt ist deine Antwort noch Arrogant. Statt allen zu danken, 
welche dir ehrlich versucht haben zu helfen, kommt diese selektive 
Danksagung.

von Dennis (Gast)


Lesenswert?

Danke euch allen.
Beonders dem Jack!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

> #define PWM_MFC1_PIN    DDRD |= (1<<DDD3);PORTD |= (1<<PORTD3)

So schreibt man kein Makros. Diese gehen bei bestimmten Konstellationen 
voll in die Hose.

Die obige Formulierung funktioniert schon bei:
1
    if (i_want_pwm)
2
        PWM_MFC1_PIN;
nicht mehr.

Dann wird nämlich vom Makro lediglich das erste Statement links vom 
Semikolen bedingt ausgeführt und das zweite unbedingt.

Also wird daraus aus Sicht des Compilers:
1
    if (i_want_pwm)
2
        DDRD |= (1<<DDD3);         // bedingt
3
    PORTD |= (1<<PORTD3)           // unbedingt.

... was bestimmt nicht beabsichtigt ist. Beim nächsten Projekt werden 
diese Makros einfach kopiert und man wundert sich dann, dass das 
Programm nicht das tut, was es tun soll.

Eine kleinere Verbesserung wäre, das Semikolon im Makro durch ein Komma 
zu ersetzen. Dann werden beide Kommandos bedingt ausgeführt, da sie als 
ein Statement gezählt werden.

Optimale Lösung wären:

   a) geschweifte Klammern im Makro
   b) Erweiterung auf do { ... } while.

Aber die Komma-Variante wäre hier schon hilfreich.

von Maxim B. (max182)


Lesenswert?

Frank M. schrieb:
> Eine kleinere Verbesserung wäre, das Semikolon im Makro durch ein Komma
> zu ersetzen. Dann werden beide Kommandos bedingt ausgeführt, da sie als
> ein Statement gezählt werden.

Bei einigen Programmierer wird noch do{...}while(0); in Gebrauch. Dabei 
wird nach jedem Befehl "\" benutzt, etwa so:
1
#define KNOPF_INIT()  do{KN_PLUS_DDR &= ~(1<<KN_PLUS);\
2
      KN_MINUS_DDR &= ~(1<<KN_MINUS);\
3
      KN_M_DDR &= ~(1<<KN_M);\
4
      KN_F_DDR &= ~(1<<KN_F);\
5
      DRKN_DDR &= ~(1<<DRKN);\
6
      KN_PLUS_PORT |= (1<<KN_PLUS);\
7
      KN_MINUS_PORT |= (1<<KN_MINUS);\
8
      KN_M_PORT |= (1<<KN_M);\
9
      KN_F_PORT |= (1<<KN_F);\
10
      DRKN_PORT |= (1<<DRKN);}while(0);

Mir selber scheint aber die Lösung mit inline besser:
1
static inline void knopf_init(void){
2
  KN_PLUS_DDR &= ~(1<<KN_PLUS);
3
  KN_MINUS_DDR &= ~(1<<KN_MINUS);
4
  KN_M_DDR &= ~(1<<KN_M);
5
  KN_F_DDR &= ~(1<<KN_F);
6
  DRKN_DDR &= ~(1<<DRKN);
7
  KN_PLUS_PORT |= (1<<KN_PLUS);
8
  KN_MINUS_PORT |= (1<<KN_MINUS);
9
  KN_M_PORT |= (1<<KN_M);
10
  KN_F_PORT |= (1<<KN_F);
11
  DRKN_PORT |= (1<<DRKN);
12
}

Besser aus zwei Gründen:
1. es wird dem Compiler überlassen, ob er "static inline" wahrnimmt oder 
aus guten Gründen als normale Funktion übersetzt.
2. in Simulator wird Funktion Befehl nach Befehl sichtbar, während Macro 
als einen einzigen Befehl durchgeführt. So ist es leicher Fehler zu 
finden.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Bei einigen Programmierer wird noch do{...}while(0); in Gebrauch.

Schrieb ich doch oben als "Optimale Lösung".

> Dabei
> wird nach jedem Befehl "\" benutzt, etwa so:
1
> #define KNOPF_INIT() do{KN_PLUS_DDR &= ~(1<<KN_PLUS);\
2
>       KN_MINUS_DDR &= ~(1<<KN_MINUS);\
3
>       KN_M_DDR &= ~(1<<KN_M);\
4
>       KN_F_DDR &= ~(1<<KN_F);\
5
>       DRKN_DDR &= ~(1<<DRKN);\
6
>       KN_PLUS_PORT |= (1<<KN_PLUS);\
7
>       KN_MINUS_PORT |= (1<<KN_MINUS);\
8
>       KN_M_PORT |= (1<<KN_M);\
9
>       KN_F_PORT |= (1<<KN_F);\
10
>       DRKN_PORT |= (1<<DRKN);}while(0);

Und schon isses wieder falsch. :(

Am Ende des Makros muss das Semikolon weg. Denn man wendet das Makro 
dann so an:
1
   KNOPF_INIT();              // Hier das Semikolon, nicht ins Makro!
> Mir selber scheint aber die Lösung mit inline besser:

Vollkommen ACK.

: Bearbeitet durch Moderator
von Maxim B. (max182)


Lesenswert?

Frank M. schrieb:
> Am Ende des Makros muss das Semikolon weg. Denn man wendet das Makro
> dann so an:   KNOPF_INIT();              // Hier das Semikolon, nicht
> ins Makro!

Geschmackssache. Wird Semikolon in Macro, so wird Macro ohne Semikolon 
in Programm verwendet. So sieht man sofort: das ist Macro und keine 
Funktion.

Ich habe ein bißchen experimentiert mit solchen Macro. Am Ende aber 
alles auf static inline - Funktionen geändert.
Ich glaube, Macro sind für andere Gebrauch besser: um Ausgänge nach 
Bedarf anders definieren. Werden Macro als Funktionen verwendet, wird 
ganze Text weniger verständlich. Aber Ziel der Sprache von Anfang an 
gerade war, Texte verständlicher und dadurch möglichst fehlerfrei zu 
schreiben.

Aber alles Geschmackssache. Wie sagte ein Pianist: Sie dürfen auch mit 
Ihrer Nase Klavier spielen. Hauptsache Ergebnis stimmt...

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Geschmackssache.

Nein. Fehlerträchtig. Man sollte immer sicherstellen, dass Makros sich 
wie Funktionen verhalten - zumindest von der Syntax her.

Ein simples Beispiel:

Die stdio-Funktion getchar() ist in vielen UNIX-Varianten ein simples 
Makro und keine Funktion. Der Komfort ist hier aber, dass Du das als 
Programmierer überhaupt nicht wissen musst.

von Maxim B. (max182)


Lesenswert?

Ja, ich muß noch vieles lernen... Vielen Dank für Ihre Anweisung!

Also, nach while(0) von nun an schreibe ich nie mehr Semikolon!
Dann hat es auch wenig Sinn, an Regeln zu halten, solche Macros immer 
mit Großbuchstaben zu schreiben - das sollte ja wie eine Funktion 
aussehen.

: Bearbeitet durch User
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.