Forum: Mikrocontroller und Digitale Elektronik 3 PWM-Ausgänge am Attiny 2313


von PG (Gast)


Lesenswert?

Guden,

ich möchte mit einem Attiny 2313 eine RGB-LED ansteuern. Es handelt sich 
um eine 10Watt-RGB-LED vom Chinamann, die über drei PWM-fähige KSQs 
getrieben werden soll. Die Auswahl der Farbe soll recht spartanisch über 
DIP-Schalter laufen, ich habe 9 DIP-Schalter vorgesehen, also soll es 4 
Stufen für jede Farbe geben.

Bsp:
0 Schalter für Rot = Rote PWM = 0%
1 Schalter für Rot = Rote PWM = 33%
2 Schalter für Rot = Rote PWM = 66%
3 Schalter für Rot = Rote PWM = 99%
...

Die Schalter werden atm nicht entprellt, das könnte ein Problem sein, 
aber ich denke eher das ich die Register falsch verwende.

Laut Datenblatt hat der µC 4 PWM-Ausgänge, ich bekomme es aber nicht auf 
die Reihe die Register so einzustellen das die 3 PWM-Signale aktiv sind.

Link zum Datenblatt: http://www.atmel.com/images/doc8246.pdf ab S. 99 
geht es um die fastPWM.

Ich möchte, wenn möglich, OC0A, OC1A und OC1B verwenden.

Mein Programmcode bisher:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
//
6
//#ifndef F_CPU
7
//#define F_CPU 1000000000UL
8
//#endif
9
10
11
12
int main(void)
13
{
14
  
15
  DDRB   |= (1 << PB2);                   // PWM output on PB2 - OC0A
16
  DDRB   |= (1 << PB3);                   // PWM output on B3 - OC1A
17
  DDRB   |= (1 << PB4);                   // PWM output on B4 - OC1B
18
  
19
   // Rot
20
   TCCR0A = (1<<COM0A1)|(1<<COM0A0)|(1<<WGM01)|(1<<WGM00);
21
   OCR0A  = 0x10;                          // initial PWM pulse width
22
   
23
   //Blau
24
    DDRB   |= (1 << PB3);                   // PWM output on PB2
25
    TCCR1A = (1 << COM1A1) | (1 << WGM00);  // phase correct PWM mode
26
    OCR1A  = 0x10;                          // initial PWM pulse width
27
   
28
   //Grün
29
    DDRB   |= (1 << PB4);                   // PWM output on PB2
30
    TCCR1B = (1 << COM1B1) | (1 << WGM00);  // phase correct PWM mode
31
    OCR1B  = 0x10;                          // initial PWM pulse width
32
   TCCR0B = (1 << CS01);   // clock source = CLK/8, start PWM
33
   //Rot
34
   PORTB |= (1 << PB1); // initializes the pull-up resistor on AVR pin PB1
35
   PORTB |= (1 << PB0); // initializes the pull-up resistor on AVR pin PB0
36
   PORTD |= (1 << PD6); // initializes the pull-up resistor on AVR pin PD2
37
   
38
   //Blau
39
   PORTB |= (1 << PB7); // initializes the pull-up resistor on AVR pin PB1
40
   PORTB |= (1 << PB6); // initializes the pull-up resistor on AVR pin PB0
41
   PORTD |= (1 << PB5); // initializes the pull-up resistor on AVR pin PD2
42
   
43
   //Grün
44
   PORTB |= (1 << PD3); // initializes the pull-up resistor on AVR pin PB1
45
   PORTB |= (1 << PD4); // initializes the pull-up resistor on AVR pin PB0
46
   PORTD |= (1 << PD5); // initializes the pull-up resistor on AVR pin PD2
47
   
48
   
49
   TCCR0B = (1 << CS01);   // clock source = CLK/8, start PWM
50
  
51
   unsigned char PWM_R = 0;    // 8-bit PWM value
52
   unsigned char PWM_G = 0;    // 8-bit PWM value
53
   unsigned char PWM_B = 0;    // 8-bit PWM value
54
   int Schalter[8] = {0,0,0,0,0,0,0,0};
55
56
     
57
    while(1)
58
    {
59
       // Schalter abfragen und Werte in Array ablegen
60
     // Rot
61
     if (bit_is_clear(PINB, PB1))
62
     { Schalter[0] = 1;}
63
     else { Schalter[0] = 0;}
64
            
65
     if (bit_is_clear(PINB, PB0))
66
     { Schalter[1] = 1;}
67
     else { Schalter[1] = 0;}
68
    
69
     if (bit_is_clear(PIND, PD6))
70
     { Schalter[2] = 1;}
71
     else { Schalter[2] = 0;}          
72
     
73
     //Blau
74
     if (bit_is_clear(PINB, PB1))
75
     { Schalter[0] = 1;}
76
     else { Schalter[0] = 0;}
77
     
78
     if (bit_is_clear(PINB, PB0))
79
     { Schalter[1] = 1;}
80
     else { Schalter[1] = 0;}
81
     
82
     if (bit_is_clear(PIND, PD6))
83
     { Schalter[2] = 1;}
84
     else { Schalter[2] = 0;}
85
     
86
     
87
     //Grün
88
     if (bit_is_clear(PINB, PB1))
89
     { Schalter[0] = 1;}
90
     else { Schalter[0] = 0;}
91
     
92
     if (bit_is_clear(PINB, PB0))
93
     { Schalter[1] = 1;}
94
     else { Schalter[1] = 0;}
95
     
96
     if (bit_is_clear(PIND, PD6))
97
     { Schalter[2] = 1;}
98
     else { Schalter[2] = 0;}
99
     
100
     //Bedingungen abfragen und Ergebnis an PWM übergeben
101
     //Rot     
102
     if (Schalter[0]==0 && Schalter[1]==0 && Schalter[2] == 0)  //000
103
     {  PWM_R = 0;   }
104
       
105
     if (Schalter[0]==1 && Schalter[1]==0 && Schalter[2] == 0)  //100
106
     {  PWM_R = 85;   }
107
     if (Schalter[0]==0 && Schalter[1]==1 && Schalter[2] == 0)  //010
108
     {  PWM_R = 85;   }
109
     if (Schalter[0]==0 && Schalter[1]==0 && Schalter[2] == 1)  //001
110
     {  PWM_R = 85;   }
111
       
112
     if (Schalter[0]==1 && Schalter[1]==1 && Schalter[2] == 0)  //110
113
     {  PWM_R = 170;   }
114
     if (Schalter[0]==0 && Schalter[1]==1 && Schalter[2] == 1)  //011
115
     {  PWM_R = 170;   }
116
     if (Schalter[0]==1 && Schalter[1]==0 && Schalter[2] == 1)  //101
117
     {  PWM_R = 170;   } 
118
       
119
     if (Schalter[0]==1 && Schalter[1]==1 && Schalter[2] == 1)  //111
120
     {  PWM_R = 255;   }  
121
     
122
     OCR0A  = PWM_R;       // write new PWM value
123
     
124
     //Blau
125
     if (Schalter[3]==0 && Schalter[4]==0 && Schalter[5] == 0)  //000
126
     {  PWM_B = 0;   }
127
     
128
     if (Schalter[3]==1 && Schalter[4]==0 && Schalter[5] == 0)  //100
129
     {  PWM_B = 85;   }
130
     if (Schalter[3]==0 && Schalter[4]==1 && Schalter[5] == 0)  //010
131
     {  PWM_B = 85;   }
132
     if (Schalter[3]==0 && Schalter[4]==0 && Schalter[5] == 1)  //001
133
     {  PWM_B = 85;   }
134
     
135
     if (Schalter[3]==1 && Schalter[1]==1 && Schalter[5] == 0)  //110
136
     {  PWM_B = 170;   }
137
     if (Schalter[3]==0 && Schalter[4]==1 && Schalter[5] == 1)  //011
138
     {  PWM_B = 170;   }
139
     if (Schalter[4]==1 && Schalter[4]==0 && Schalter[5] == 1)  //101
140
     {  PWM_B = 170;   }
141
     
142
     if (Schalter[0]==1 && Schalter[4]==1 && Schalter[5] == 1)  //111
143
     {  PWM_B = 255;   }
144
     
145
     OCR0A  = PWM_B;       // write new PWM value
146
     
147
     
148
     //Grün
149
     if (Schalter[6]==0 && Schalter[7]==0 && Schalter[8] == 0)  //000
150
     {  PWM_G = 0;   }
151
     
152
     if (Schalter[6]==1 && Schalter[7]==0 && Schalter[8] == 0)  //100
153
     {  PWM_G = 85;   }
154
     if (Schalter[6]==0 && Schalter[7]==1 && Schalter[8] == 0)  //010
155
     {  PWM_G = 85;   }
156
     if (Schalter[6]==0 && Schalter[7]==0 && Schalter[8] == 1)  //001
157
     {  PWM_G = 85;   }
158
     
159
     if (Schalter[6]==1 && Schalter[7]==1 && Schalter[8] == 0)  //110
160
     {  PWM_G = 170;   }
161
     if (Schalter[6]==0 && Schalter[7]==1 && Schalter[8] == 1)  //011
162
     {  PWM_G = 170;   }
163
     if (Schalter[6]==1 && Schalter[7]==0 && Schalter[8] == 1)  //101
164
     {  PWM_G = 170;   }
165
     
166
     if (Schalter[6]==1 && Schalter[7]==1 && Schalter[8] == 1)  //111
167
     {  PWM_G = 255;   }
168
     
169
     OCR0A  = PWM_G;       // write new PWM value
170
     
171
    }//while
172
}//main

Bin für jede Hilfe dankbar, die Abfrage der Bedingungen für die Schalter 
ist zwar nicht elegant, sollte aber funktionieren, mit 3 Schaltern und 
einer PWM hab ich es schonmal zum Laufen bekommen.

Sollte jemand das komplette Pinout brauchen, bitte melden.
Ich verwende Atmel Studio und den AVR-Dragon zum Programmieren des µC.

Danke für eure Hilfe, Grüße, PG

von Ralf G. (ralg)


Lesenswert?

Was mir als erstes auffällt:

- Die Initialisierung der Timer geht ziemlich durcheinander
- Bit 'CS01' wird an zwei verschiedenen Stellen gesetzt.
- Bit 'COM1B1' ist nicht in 'TCCR1B' ansprechbar. Mit diesem Befehl 
setzt du dort dann das Bit5, welches reserviert ist, und lt. Datenblatt 
immer '0' sein muss.

Die Register 'TCCRxA' und 'TCCRxB' gehören zur Initialisierung des 
Timers! Da gibt es keine Zuordnung zu 'OCxA' und 'OCxB'.

von Εrnst B. (ernst)


Lesenswert?

PG schrieb:
> TCCR1A = (1 << COM1A1) | (1 << WGM00);  // phase correct PWM mode

TCCR1A hat kein "WGM00"-Bit. Und du wolltest Fast PWM, kein phase 
correct?

> TCCR1B = (1 << COM1B1) | (1 << WGM00);

TCCR1B hat weder ein COM1B1 noch ein WGM00 bit.

==> Datenblatt Seite 111 und folgende.

Überprüf doch mal alle deine Register, ob du auch die richtigen Bits 
setzt.

von Wolfgang (Gast)


Lesenswert?

PG schrieb:
> Die Schalter werden atm nicht entprellt, das könnte ein Problem sein, ...

Warum?
Verwendest du irgendwo eine sequentielle Steuerung?

von c-hater (Gast)


Lesenswert?

PG schrieb:

> mit 3 Schaltern und
> einer PWM hab ich es schonmal zum Laufen bekommen.

Kunststück, das war ja wohl genau der Teil des Codes, den du 
"raubkopiert" und offensichtlich komplett nicht verstanden hast.

Was dann praktisch automatisch dazu geführt hat, dass deine Erweiterung 
auf drei Kanäle in jeder Beziehung völliger Bullshit geworden ist.

Also: lies das verdammte Datenblatt und versuche den kopierten Code auch 
zu verstehen. Als Gradmesser für das Wachsen deines Verständnisses 
kannst du das EIGENSTÄNDIGE Erreichen der von dir gewünschten Funktion 
nehmen...

Und wenn du damit durch bist (falls es je so weit kommen sollte), dann 
kannst du auch gleich noch C lernen und den Code mit den Features 
optimieren, die C so bietet, insbesondere der Nutzung von Funktionen für 
ähnliche oder identische Programmteile...

von M. K. (sylaina)


Lesenswert?

PG schrieb:
> Ich möchte, wenn möglich, OC0A, OC1A und OC1B verwenden.

Das ist auf jeden Fall möglich. Hier mal ein Beispiel dazu
1
#include <avr/io.h>
2
#include <stdlib.h>
3
4
int main(void){
5
  // PWM-Portpins auf Ausgang schalten
6
  DDRB |= (1 << PB2)|(1 << PB3)|(1 << PB4);
7
  // Timer0 einstellen fuer Fast-PWM
8
  TCCR0A |= (1 << WGM01)|(1 << WGM00);
9
  // Normaler PWM-Mode, d.h. Ausgang High bis Zaehlwert erreicht, dann Low
10
  TCCR0A |= (1 << COM0A1);
11
  // fest vorgegebene Duty-Cycles
12
  // fuer OC0A
13
  OCR0A = 200;
14
  // Timer1 einstellen fuer PhaseCorrect-PWM
15
  TCCR1A |= (1 << WGM11)|(1 << WGM10);
16
  // Normaler PWM-Mode, d.h. Ausgang High bis Zaehlwert erreicht, darüber Low
17
  TCCR1A |= (1 << COM1A1)|(1 << COM1B1);
18
  // fest vorgegebene Duty-Cycles
19
  // fuer OC1A
20
  OCR1A = 30;
21
  // fuer OC1B
22
  OCR1B = 127;
23
24
  // Timer starten
25
  TCCR0B |= (1 << CS00);
26
  TCCR1B |= (1 << CS12)|(1 << CS01);
27
  for(;;){
28
    // Main-Loop
29
  }
30
  // wird nie erreicht
31
  return 0;
32
}
Das macht drei PWM-Ausgangsignale (ungeprüft, Tippfehler können drin 
sein), vielleicht kannst du darauf aufbauen, TE.

von PG (Gast)


Lesenswert?

Vielen Dank für all die Tipps und Hinweise!
Ich hab schon verstanden das ich die Infos aus dem Datenblatt kriegen 
muss, aber das ist nicht so einfach wenn man wenig Erfahrung damit hat.

Ich schaue mir das Setzen der Register für Timer und Waveforms nochmal 
an und melde mich dann.

Das man den Code mit Funktionen sehr viel kürzer machen kann wenn man 
dreimal das selbe machen will ist klar, aber ich wollte erst die drei 
PWMs zum Laufen kriegen und wissen das es überhaupt das macht was ich 
will, bevor ich ewig Zeit investiere um schöne Funktionen zu schreiben, 
für jemanden der super in C ist mag das unverständlich sein und die 
Lesbarkeit nicht unbedingt erhöhen, aber  ich mache nun mal einen 
Schritt nach dem Anderen.

Grüße, PG

von Feldkurat K. (feldkurat)


Lesenswert?

PG schrieb:
> Ich hab schon verstanden das ich die Infos aus dem Datenblatt kriegen
> muss, aber das ist nicht so einfach wenn man wenig Erfahrung damit hat.

Es gibt in den AVR-Datenblättern den Abschnitt "Register Summary", in 
dem man sämtliche Register und die darin enthaltenen Bits auf einen 
Blick findet. Das ist sehr hilfreich, wenn man alles "von Hand" 
einstellen will, um sicherzugehen, daß der Kontroller auch wirklich das 
macht, was man von ihm erwartet.

-Feldkurat-

von my2ct (Gast)


Lesenswert?

PG schrieb:
> Ich hab schon verstanden das ich die Infos aus dem Datenblatt kriegen
> muss, aber das ist nicht so einfach wenn man wenig Erfahrung damit hat.

Das Wort "Erfahrung" hat was mit erfahren zu tun. Das passiert genau 
dann, wenn man sich selber damit beschäftigt - liest, versteht ein 
bisschen, probiert, versteht.

von Feldkurat K. (feldkurat)


Lesenswert?

my2ct schrieb:
> Das passiert genau
> dann, wenn man sich selber damit beschäftigt - liest, versteht ein
> bisschen, probiert, versteht.

Diese Schleife muß man nur ab und zu verlassen, um ein Polling auf 
Kaffee oder Mittagessen zu machen.

:)

-Feldkurat-

von c-hater (Gast)


Lesenswert?

PG schrieb:

> Ich hab schon verstanden das ich die Infos aus dem Datenblatt kriegen
> muss, aber das ist nicht so einfach wenn man wenig Erfahrung damit hat.

Ja, das ist so. Dagegen hilft aber eben nur eins: LERNEN. Und genau 
das kann man nur selber tun, da helfen keine dümmlichen Anfragen in 
irgendwelchen Foren.

Das heißt natürlich nicht, dass Foren nutzlos sind, denn selbst bei 
tatsächlich vorhandenem Willen und Fähigkeit zum Lernen, stößt man hin 
und wieder auf scheinbar unlösbare Probleme. Meistens dann, wenn 
irgendwelche Dokumentation unvollständig oder gar schlicht falsch ist 
oder man den entscheidenden Knackpunkt einfach nur übersehen hat...
Aber manchmal auch dann, wenn man sich verrannt hat und über die eigenen 
Fehler immer wieder hinwegliest, obwohl sie für praktisch jeden anderen 
sofort sichtbar sind, weil der eben den nötigen Abstand hat...

Solche Probleme zu lösen, dafür sind Foren da. Nicht dafür, dass Dumme 
und Faule ihre Anwendungen für lau programmiert bekommen...

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.