Hallo ihr lieben, hab da mal ein kleines Problem und zwar wollte ich an meinem ATMega 32 16Hz extern getaktet eine PWM ausprobieren. Leider blinken die 8 an PortD angeschlossenen LED's nur einmal kurz auf und dann habe ich eine konstant leuchtende an Pin D0. Seh da nicht so ganz durch hab den Code über das GCC Tutorial im Mikrocontroller.net gefunden und dann an meinen Controller angepasst. Würde halt gerne diese Software PWM benutzen da man durch diese mit einem Controller 32 Kanäle ansprechen kann. Kann mir jemand sagen was ich falsch mache, wollte eine art soft fading effekt haben das die LED's sich langsam an und ab dimmen. /* Eine 8-kanalige PWM mit intelligentem Lösungsansatz ATmega32 @ 8 MHz */ // Defines an den Controller und die Anwendung anpassen #define F_CPU 16000000L // Systemtakt in Hz #define F_PWM 100L // PWM-Frequenz in Hz #define PWM_PRESCALER 8 // Vorteiler für den Timer #define PWM_STEPS 256 // PWM-Schritte pro Zyklus(1..256) #define PWM_PORT PORTD // Port für PWM #define PWM_DDR DDRD // Datenrichtungsregister für PWM #define PWM_CHANNELS 8 // Anzahl der PWM-Kanäle // ab hier nichts ändern, wird alles berechnet #define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt //#define T_PWM 1 //TEST #if ((T_PWM*PWM_PRESCALER)<(111+5)) #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden #endif #if ((T_PWM*PWM_STEPS)>65535) #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER erhöhen. #endif // includes #include <stdint.h> #include <string.h> #include <avr/io.h> #include <avr/interrupt.h> // globale Variablen uint16_t pwm_timing[PWM_CHANNELS+1]; // Zeitdifferenzen der PWM Werte uint16_t pwm_timing_tmp[PWM_CHANNELS+1]; uint8_t pwm_mask[PWM_CHANNELS+1]; // Bitmaske für PWM Bits, welche gelöscht werden sollen uint8_t pwm_mask_tmp[PWM_CHANNELS+1]; // ändern uint16_t oder uint32_t für mehr Kanäle uint8_t pwm_setting[PWM_CHANNELS]; // Einstellungen für die einzelnen PWM-Kanäle uint8_t pwm_setting_tmp[PWM_CHANNELS+1]; // Einstellungen der PWM Werte, sortiert // ändern auf uint16_t für mehr als 8 Bit Auflösung volatile uint8_t pwm_cnt_max=1; // Zählergrenze, Initialisierung mit 1 ist wichtig! volatile uint8_t pwm_sync; // Update jetzt möglich // Pointer für wechselseitigen Datenzugriff uint16_t *isr_ptr_time = pwm_timing; uint16_t *main_ptr_time = pwm_timing_tmp; uint8_t *isr_ptr_mask = pwm_mask; // Bitmasken fuer PWM-Kanäle uint8_t *main_ptr_mask = pwm_mask_tmp; // ändern uint16_t oder uint32_t für mehr Kanäle // Zeiger austauschen // das muss in einem Unterprogramm erfolgen, // um eine Zwischenspeicherung durch den Compiler zu verhindern void tausche_zeiger(void) { uint16_t *tmp_ptr16; uint8_t *tmp_ptr8; // ändern uint16_t oder uint32_t für mehr Kanäle tmp_ptr16 = isr_ptr_time; isr_ptr_time = main_ptr_time; main_ptr_time = tmp_ptr16; tmp_ptr8 = isr_ptr_mask; isr_ptr_mask = main_ptr_mask; main_ptr_mask = tmp_ptr8; } // PWM Update, berechnet aus den PWM Einstellungen // die neuen Werte für die Interruptroutine void pwm_update(void) { uint8_t i, j, k; uint8_t m1, m2, tmp_mask; // ändern uint16_t oder uint32_t für mehr Kanäle uint8_t min, tmp_set; // ändern auf uint16_t für mehr als 8 Bit Auflösung // PWM Maske für Start berechnen // gleichzeitig die Bitmasken generieren und PWM Werte kopieren m1 = 1; m2 = 0; for(i=1; i<=(PWM_CHANNELS); i++) { main_ptr_mask[i]=~m1; // Maske zum Löschen der PWM Ausgänge pwm_setting_tmp[i] = pwm_setting[i-1]; if (pwm_setting_tmp[i]!=0) m2 |= m1; // Maske zum setzen der IOs am PWM Start m1 <<= 1; } main_ptr_mask[0]=m2; // PWM Start Daten // PWM settings sortieren; Einfügesortieren for(i=1; i<=PWM_CHANNELS; i++) { min=PWM_STEPS-1; k=i; for(j=i; j<=PWM_CHANNELS; j++) { if (pwm_setting_tmp[j]<min) { k=j; // Index und PWM-setting merken min = pwm_setting_tmp[j]; } } if (k!=i) { // ermitteltes Minimum mit aktueller Sortiertstelle tauschen tmp_set = pwm_setting_tmp[k]; pwm_setting_tmp[k] = pwm_setting_tmp[i]; pwm_setting_tmp[i] = tmp_set; tmp_mask = main_ptr_mask[k]; main_ptr_mask[k] = main_ptr_mask[i]; main_ptr_mask[i] = tmp_mask; } } // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden k=PWM_CHANNELS; // PWM_CHANNELS Datensätze i=1; // Startindex while(k>i) { while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0)) && (k>i) ) { // aufeinanderfolgende Werte sind gleich und können vereinigt werden // oder PWM Wert ist Null if (pwm_setting_tmp[i]!=0) main_ptr_mask[i+1] &= main_ptr_mask[i]; // Masken vereinigen // Datensatz entfernen, // Nachfolger alle eine Stufe hochschieben for(j=i; j<k; j++) { pwm_setting_tmp[j] = pwm_setting_tmp[j+1]; main_ptr_mask[j] = main_ptr_mask[j+1]; } k--; } i++; } // letzten Datensatz extra behandeln // Vergleich mit dem Nachfolger nicht möglich, nur löschen // gilt nur im Sonderfall, wenn alle Kanäle 0 sind if (pwm_setting_tmp[i]==0) k--; // Zeitdifferenzen berechnen if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2; main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2; k=1; } else { i=k; main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]); tmp_set=pwm_setting_tmp[i]; i--; for (; i>0; i--) { main_ptr_time[i]=(uint16_t)T_PWM*(tmp_set-pwm_setting_tmp[i]); tmp_set=pwm_setting_tmp[i]; } main_ptr_time[0]=(uint16_t)T_PWM*tmp_set; } // auf Sync warten pwm_sync=0; // Sync wird im Interrupt gesetzt while(pwm_sync==0); // Zeiger tauschen cli(); tausche_zeiger(); pwm_cnt_max = k; sei(); } // Timer 1 Output COMPARE A Interrupt ISR(TIMER1_COMPA_vect) { static uint8_t pwm_cnt; // ändern auf uint16_t für mehr als 8 Bit Auflösung uint8_t tmp; // ändern uint16_t oder uint32_t für mehr Kanäle OCR1A += isr_ptr_time[pwm_cnt]; tmp = isr_ptr_mask[pwm_cnt]; if (pwm_cnt == 0) { PWM_PORT = tmp; // Ports setzen zu Begin der PWM // zusätzliche PWM-Ports hier setzen pwm_cnt++; } else { PWM_PORT &= tmp; // Ports löschen // zusätzliche PWM-Ports hier setzen if (pwm_cnt == pwm_cnt_max) { pwm_sync = 1; // Update jetzt möglich pwm_cnt = 0; } else pwm_cnt++; } } int main(void) { // PWM Port einstellen PWM_DDR = 0xff; // Port als Ausgang // zusätzliche PWM-Ports hier setzen // Timer 1 OCRA1, als variablen Timer nutzen TCCR1B = 2; // Timer läuft mit Prescaler 8 TIMSK |= (1<<OCIE1A); // Interrupt freischalten sei(); // Interrupts gloabl einschalten /******************************************************************/ // nur zum testen, in der Anwendung entfernen // Test values volatile uint8_t tmp; const uint8_t t1[8]={255, 40, 3, 17, 150, 99, 5, 9}; const uint8_t t2[8]={27, 40, 3, 0, 150, 99, 5, 9}; const uint8_t t3[8]={27, 40, 3, 17, 3, 99, 3, 0}; const uint8_t t4[8]={0, 0, 0, 0, 0, 0, 0, 0}; const uint8_t t5[8]={9, 1, 1, 1, 1, 1, 1, 1}; const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33}; const uint8_t t7[8]={0, 0, 0, 0, 0, 0, 0, 88}; // Messung der Interruptdauer tmp =1; tmp =2; tmp =3; // Debug memcpy(pwm_setting, t1, 8); pwm_update(); memcpy(pwm_setting, t2, 8); pwm_update(); memcpy(pwm_setting, t3, 8); pwm_update(); memcpy(pwm_setting, t4, 8); pwm_update(); memcpy(pwm_setting, t5, 8); pwm_update(); memcpy(pwm_setting, t6, 8); pwm_update(); memcpy(pwm_setting, t7, 8); pwm_update(); /******************************************************************/ while(1); return 0; } Hab hier auch mal den Link: http://www.mikrocontroller.net/articles/Soft-PWM wäre super wenn mir jemand helfen könnte Gruß Andi
Andi schrieb: > ATmega32 @ 8 MHz > #define F_CPU 16000000L // Systemtakt in Hz Was jetzt? längeren Code bitte als Anhang, mit dieser Zeilenlängenbegrenzung will das hier keiner lesen. :-)
Sorry kenn mich in diesem Forum noch nicht so wirklich aus. Ich versuche es jetzt mal so:
1 | /*
|
2 | Eine 8-kanalige PWM mit intelligentem Lösungsansatz
|
3 | |
4 | ATmega32 @ 8 MHz
|
5 | |
6 | */
|
7 | |
8 | // Defines an den Controller und die Anwendung anpassen
|
9 | |
10 | #define F_CPU 16000000L // Systemtakt in Hz
|
11 | #define F_PWM 100L // PWM-Frequenz in Hz
|
12 | #define PWM_PRESCALER 8 // Vorteiler für den Timer
|
13 | #define PWM_STEPS 256 // PWM-Schritte pro
|
14 | Zyklus(1..256) |
15 | #define PWM_PORT PORTD // Port für PWM
|
16 | #define PWM_DDR DDRD // Datenrichtungsregister für
|
17 | PWM
|
18 | #define PWM_CHANNELS 8 // Anzahl der PWM-Kanäle
|
19 | |
20 | // ab hier nichts ändern, wird alles berechnet
|
21 | |
22 | #define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro
|
23 | PWM-Takt |
24 | //#define T_PWM 1 //TEST
|
25 | |
26 | #if ((T_PWM*PWM_PRESCALER)<(111+5))
|
27 | #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder
|
28 | PWM_STEPS verkleinert werden |
29 | #endif
|
30 | |
31 | #if ((T_PWM*PWM_STEPS)>65535)
|
32 | #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER
|
33 | erhöhen. |
34 | #endif
|
35 | // includes
|
36 | |
37 | #include <stdint.h> |
38 | #include <string.h> |
39 | #include <avr/io.h> |
40 | #include <avr/interrupt.h> |
41 | |
42 | // globale Variablen
|
43 | |
44 | uint16_t pwm_timing[PWM_CHANNELS+1]; // Zeitdifferenzen der PWM |
45 | Werte
|
46 | uint16_t pwm_timing_tmp[PWM_CHANNELS+1]; |
47 | |
48 | uint8_t pwm_mask[PWM_CHANNELS+1]; // Bitmaske für PWM Bits, |
49 | welche gelöscht werden sollen |
50 | uint8_t pwm_mask_tmp[PWM_CHANNELS+1]; // ändern uint16_t oder |
51 | uint32_t für mehr Kanäle |
52 | |
53 | uint8_t pwm_setting[PWM_CHANNELS]; // Einstellungen für die |
54 | einzelnen PWM-Kanäle |
55 | uint8_t pwm_setting_tmp[PWM_CHANNELS+1]; // Einstellungen der PWM |
56 | Werte, sortiert |
57 | // ändern auf uint16_t für
|
58 | mehr als 8 Bit Auflösung |
59 | |
60 | volatile uint8_t pwm_cnt_max=1; // Zählergrenze, |
61 | Initialisierung mit 1 ist wichtig! |
62 | volatile uint8_t pwm_sync; // Update jetzt möglich |
63 | |
64 | // Pointer für wechselseitigen Datenzugriff
|
65 | |
66 | uint16_t *isr_ptr_time = pwm_timing; |
67 | uint16_t *main_ptr_time = pwm_timing_tmp; |
68 | |
69 | uint8_t *isr_ptr_mask = pwm_mask; // Bitmasken fuer |
70 | PWM-Kanäle |
71 | uint8_t *main_ptr_mask = pwm_mask_tmp; // ändern uint16_t oder |
72 | uint32_t für mehr Kanäle |
73 | |
74 | // Zeiger austauschen
|
75 | // das muss in einem Unterprogramm erfolgen,
|
76 | // um eine Zwischenspeicherung durch den Compiler zu verhindern
|
77 | |
78 | void tausche_zeiger(void) { |
79 | uint16_t *tmp_ptr16; |
80 | uint8_t *tmp_ptr8; // ändern uint16_t oder |
81 | uint32_t für mehr Kanäle |
82 | |
83 | tmp_ptr16 = isr_ptr_time; |
84 | isr_ptr_time = main_ptr_time; |
85 | main_ptr_time = tmp_ptr16; |
86 | tmp_ptr8 = isr_ptr_mask; |
87 | isr_ptr_mask = main_ptr_mask; |
88 | main_ptr_mask = tmp_ptr8; |
89 | }
|
90 | |
91 | // PWM Update, berechnet aus den PWM Einstellungen
|
92 | // die neuen Werte für die Interruptroutine
|
93 | |
94 | void pwm_update(void) { |
95 | |
96 | uint8_t i, j, k; |
97 | uint8_t m1, m2, tmp_mask; // ändern uint16_t oder |
98 | uint32_t für mehr Kanäle |
99 | uint8_t min, tmp_set; // ändern auf uint16_t |
100 | für mehr als 8 Bit Auflösung |
101 | |
102 | // PWM Maske für Start berechnen
|
103 | // gleichzeitig die Bitmasken generieren und PWM Werte kopieren
|
104 | |
105 | m1 = 1; |
106 | m2 = 0; |
107 | for(i=1; i<=(PWM_CHANNELS); i++) { |
108 | main_ptr_mask[i]=~m1; // Maske zum Löschen |
109 | der PWM Ausgänge |
110 | pwm_setting_tmp[i] = pwm_setting[i-1]; |
111 | if (pwm_setting_tmp[i]!=0) m2 |= m1; // Maske zum setzen |
112 | der IOs am PWM Start |
113 | m1 <<= 1; |
114 | }
|
115 | main_ptr_mask[0]=m2; // PWM Start Daten |
116 | |
117 | // PWM settings sortieren; Einfügesortieren
|
118 | |
119 | for(i=1; i<=PWM_CHANNELS; i++) { |
120 | min=PWM_STEPS-1; |
121 | k=i; |
122 | for(j=i; j<=PWM_CHANNELS; j++) { |
123 | if (pwm_setting_tmp[j]<min) { |
124 | k=j; // Index und |
125 | PWM-setting merken |
126 | min = pwm_setting_tmp[j]; |
127 | }
|
128 | }
|
129 | if (k!=i) { |
130 | // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
|
131 | tmp_set = pwm_setting_tmp[k]; |
132 | pwm_setting_tmp[k] = pwm_setting_tmp[i]; |
133 | pwm_setting_tmp[i] = tmp_set; |
134 | tmp_mask = main_ptr_mask[k]; |
135 | main_ptr_mask[k] = main_ptr_mask[i]; |
136 | main_ptr_mask[i] = tmp_mask; |
137 | }
|
138 | }
|
139 | |
140 | // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls
|
141 | vorhanden
|
142 | |
143 | k=PWM_CHANNELS; // PWM_CHANNELS Datensätze |
144 | i=1; // Startindex |
145 | |
146 | while(k>i) { |
147 | while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || |
148 | (pwm_setting_tmp[i]==0)) && (k>i) ) { |
149 | |
150 | // aufeinanderfolgende Werte sind gleich und können
|
151 | vereinigt werden |
152 | // oder PWM Wert ist Null
|
153 | if (pwm_setting_tmp[i]!=0) |
154 | main_ptr_mask[i+1] &= main_ptr_mask[i]; // Masken |
155 | vereinigen
|
156 | |
157 | // Datensatz entfernen,
|
158 | // Nachfolger alle eine Stufe hochschieben
|
159 | for(j=i; j<k; j++) { |
160 | pwm_setting_tmp[j] = pwm_setting_tmp[j+1]; |
161 | main_ptr_mask[j] = main_ptr_mask[j+1]; |
162 | }
|
163 | k--; |
164 | }
|
165 | i++; |
166 | }
|
167 | |
168 | // letzten Datensatz extra behandeln
|
169 | // Vergleich mit dem Nachfolger nicht möglich, nur löschen
|
170 | // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
|
171 | if (pwm_setting_tmp[i]==0) k--; |
172 | |
173 | // Zeitdifferenzen berechnen
|
174 | |
175 | if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind |
176 | main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2; |
177 | main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2; |
178 | k=1; |
179 | }
|
180 | else { |
181 | i=k; |
182 | main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]); |
183 | tmp_set=pwm_setting_tmp[i]; |
184 | i--; |
185 | for (; i>0; i--) { |
186 | main_ptr_time[i]=(uint16_t)T_PWM*(tmp_set-pwm_setting_tmp[i]); |
187 | tmp_set=pwm_setting_tmp[i]; |
188 | }
|
189 | main_ptr_time[0]=(uint16_t)T_PWM*tmp_set; |
190 | }
|
191 | |
192 | // auf Sync warten
|
193 | |
194 | pwm_sync=0; // Sync wird im Interrupt gesetzt |
195 | while(pwm_sync==0); |
196 | |
197 | // Zeiger tauschen
|
198 | cli(); |
199 | tausche_zeiger(); |
200 | pwm_cnt_max = k; |
201 | sei(); |
202 | }
|
203 | |
204 | // Timer 1 Output COMPARE A Interrupt
|
205 | |
206 | ISR(TIMER1_COMPA_vect) { |
207 | static uint8_t pwm_cnt; // ändern auf uint16_t |
208 | für mehr als 8 Bit Auflösung |
209 | uint8_t tmp; // ändern uint16_t oder |
210 | uint32_t für mehr Kanäle |
211 | |
212 | OCR1A += isr_ptr_time[pwm_cnt]; |
213 | tmp = isr_ptr_mask[pwm_cnt]; |
214 | |
215 | if (pwm_cnt == 0) { |
216 | PWM_PORT = tmp; // Ports setzen zu Begin |
217 | der PWM |
218 | // zusätzliche PWM-Ports
|
219 | hier setzen |
220 | pwm_cnt++; |
221 | }
|
222 | else { |
223 | PWM_PORT &= tmp; // Ports löschen |
224 | // zusätzliche PWM-Ports
|
225 | hier setzen |
226 | if (pwm_cnt == pwm_cnt_max) { |
227 | pwm_sync = 1; // Update jetzt möglich |
228 | pwm_cnt = 0; |
229 | }
|
230 | else pwm_cnt++; |
231 | }
|
232 | }
|
233 | |
234 | int main(void) { |
235 | |
236 | // PWM Port einstellen
|
237 | |
238 | PWM_DDR = 0xff; // Port als Ausgang |
239 | // zusätzliche PWM-Ports hier setzen
|
240 | |
241 | // Timer 1 OCRA1, als variablen Timer nutzen
|
242 | |
243 | TCCR1B = 2; // Timer läuft mit Prescaler 8 |
244 | TIMSK |= (1<<OCIE1A); // Interrupt freischalten |
245 | |
246 | sei(); // Interrupts gloabl einschalten |
247 | |
248 | |
249 | /******************************************************************/
|
250 | // nur zum testen, in der Anwendung entfernen
|
251 | |
252 | // Test values
|
253 | volatile uint8_t tmp; |
254 | const uint8_t t1[8]={255, 40, 3, 17, 150, 99, 5, 9}; |
255 | const uint8_t t2[8]={27, 40, 3, 0, 150, 99, 5, 9}; |
256 | const uint8_t t3[8]={27, 40, 3, 17, 3, 99, 3, 0}; |
257 | const uint8_t t4[8]={0, 0, 0, 0, 0, 0, 0, 0}; |
258 | const uint8_t t5[8]={9, 1, 1, 1, 1, 1, 1, 1}; |
259 | const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33}; |
260 | const uint8_t t7[8]={0, 0, 0, 0, 0, 0, 0, 88}; |
261 | |
262 | |
263 | // Messung der Interruptdauer
|
264 | tmp =1; |
265 | tmp =2; |
266 | tmp =3; |
267 | |
268 | // Debug
|
269 | |
270 | memcpy(pwm_setting, t1, 8); |
271 | pwm_update(); |
272 | |
273 | memcpy(pwm_setting, t2, 8); |
274 | pwm_update(); |
275 | |
276 | memcpy(pwm_setting, t3, 8); |
277 | pwm_update(); |
278 | |
279 | memcpy(pwm_setting, t4, 8); |
280 | pwm_update(); |
281 | |
282 | memcpy(pwm_setting, t5, 8); |
283 | pwm_update(); |
284 | |
285 | memcpy(pwm_setting, t6, 8); |
286 | pwm_update(); |
287 | |
288 | memcpy(pwm_setting, t7, 8); |
289 | pwm_update(); |
290 | |
291 | |
292 | /******************************************************************/
|
293 | |
294 | while(1); |
295 | return 0; |
296 | }
|
Wer soll denn das lesen. Bitte hänge doch den Quellcode als Datei über Dateianhang dran. (Taste /Durchsuchen.../ unter Betreff). Zu deinem Problem: Läuft denn im Debugger alles sauber durch? D.h. sind die vorberechneten Werte wie erwartet, passiert im IRQ das richtige und stimmen beim Port die DDR und PORT-Werte? Gruß Rainer
Ok ich versuche es nochmal :) Zu deinen Fragen: - Der Debugger bringt keinerlei Fehlermeldungen oder Warnungen - Die Ports sind auch korrekt PortD als Ausgang und dieser auch gesetzt Ehrlich gesagt kann ich dir mit der IRQ Frage nicht folgen, was ist ein IRQ? Hab jetzt mal alles angehängt:)
Mit dem Anhang sieht das doch viel besser aus. Anscheinend kommt das Forum nur mit den Projektdateien nicht zurecht, keine Ahnung wieso. In solch einem Fall hilft das Verpacken in eine ZIP-Datei. Aber egal. > - Der Debugger bringt keinerlei Fehlermeldungen oder Warnungen Du meinst wahrscheinlich nicht Debugger sondern Compiler und Linker oder? Mit dem Debugger von AVR-Studio kannst du dem Prozessor in der Simulation des Programmablaufes Schritt für Schritt auf die Finger gucken und verfolgen was passiert. Dafür mußt du natürlich erstmal wissen, was passieren soll. Aber dazu ist ja in dem Artikel Soft-PWM einiges geschrieben. Den Debugger würde ich erstmal mit einem weniger komplexen Programm ausprobieren. > Ehrlich gesagt kann ich dir mit der IRQ Frage nicht folgen, was ist > ein IRQ? IRQ ist die Abkürzung von Interrupt, d.h. eine Unterbrechung des gerade laufenden Programmteils. Gruß Rainer
Erstmal danke Rainer, werde jetzt noch mal den Bericht in aller Ruhe 2-3 mal lesen und hoffen das ich irgendwas versteh und nebenbei das ganze mit dem Debugger versuchen, dann ist der debugger also das Simulationstool :) Gruß Andi
Hallo Andi, beim Simulieren wirst du sehen, dass im Hauptprogramm gleich nachdem Port, Timer und Interruptflag (OCIE1A) gesetzt sind, über sei() die Interruptbearbeitung freigegeben wird. Die Steuerung fängt also an zu laufen, bevor die Daten zum Test und debuggen geladen und vorbereitet worden sind. Das geht auch mit dem Programm nicht anders, weil die Routine pwm_update(), die das Vorbereiten übernimmt, über das Flag pwm_sync mit der InterruptServiceRoutine ISR(TIMER1_COMPA_vect) zusammenarbeitet. Das stört aber nicht weiter. Das Hauptprogramm macht dann nach dem Start eigentlich gar nichts mehr und kreist in "while (1);" (ganz am Ende) nur um sich selbst. In der Simulation sieht man das dann gar nicht. Rainer P.S. IRQ heißt richtig Interrupt Request, d.h. das ist die Unterbrechungsanforderung, die dann zum Aufruf der InterruptServiceRoutine führt.
So, hab jetzt mal den kleineren Code aus dem Beitrag zerstückelt und so weit ich es verstanden hab kommentiert. Ab dem Moment wo jedoch die Interrupts ins Spiel kommen steig ich aus. Hab jetzt mal gegoogelt und verstehe auch das Prinzip des ganzen. Ich denke das mit den Interrupts ist auch nicht das Problem warum meine 8 LED's an Port D nicht leuchten :( Ich frage mich nur ob ich im main nicht irgendwo die Ausgänge aktivieren muss ich setze dort ja nur das Datenrichtungsregister. Was auch ganz unverständlich ist, was dieser Programmteil hier macht: memcpy(pwm_setting, t1, 8); memcpy(pwm_setting, t2, 8); memcpy(pwm_setting, t3, 8); memcpy(pwm_setting, t4, 8); memcpy(pwm_setting, t5, 8); memcpy(pwm_setting, t6, 8); Hab im Anhang mal den kommentierten Code, was mit ????? gekennzeichnet ist ist eine Vermutung und ich hab keine Ahnung ob das so passt.
Hallo Andi, um die Fragezeichen würde ich mich später kümmer. Verstehe erstmal die 8-Kanal Version. Der ATmega32 mit 16 MHz schafft die auf jeden Fall. Mit der Definition #define PWM_DDR DDRD // Datenrichtungsregister für PWM und der Zeile PWM_DDR = 0xFF; // Port als Ausgang werden alle 8 Anschlüsse vom Port D als Ausgang programmiert und daran wird während des Programmlaufs auch nicht mehr gerüttelt. Die Ausgangswerte, d.h. das Ein- und Ausschalten der LEDs passiert dann in der IRS-Routine mit "PWM_PORT = tmp;" bzw. "PWM_PORT &= tmp;" Während der Programmteil mit den memcpy Aufrufen läuft, müßte sich dauern die Helligkeit ändern und dann bleiben sie auf einem Endwert stehen, den du siehst. Die Zeit, die die Steuerung auf jeder einzelnen Helligkeitsstufe läuft, kannst du dir mit dem Debugger ansehen, wenn du auf die Zeilen mit den memcpy-Aufrufen jeweils einen Breakpoint legst, so dass das Programm dort anhält, und du dir dann links in dem Fenster "Prozessor" die unter "Stop-Watch" angezeigte Zeit ansiehst. In den Konstanten-Arrays T1 bis T7 stehen jeweils komplette Sätze von neuen Helligkeitswerten, die während der laufenden PWM-Ausgabe mit memcpy nach pwm_setting kopiert werden und dann mit pwm_update aufbereitet (so wie in dem Artikel beschrieben) und am Ende mit tausche_zeiger an die Interruptgesteuerte PWM-Erzeugung übergeben werden. Da kommt die im Artikel beschrieben Pufferung ins Spiel, die erlaubt, dass die PWM weiterläuft, während mit pwm_update schon die neuen Steuerwerte erzeugt werden. Damit die einzelnen Helligkeitsstufen länger zu sehen sind, könntest du vor den Aufrufen von memcpy jeweils die Verzögerungsfunktion _delay_ms (1000); // 1000 ms warten einbauen und müßtest dann mehr sehen können. Damit der Compiler die Funktion _delay_ms finden kann muß dazu die passende Bibliothek mit der Zeile #include <util/delay.h> mit eingebunden werden, z.B. ziemlich oben nach "#include <avr/interrupt.h>". Die Funktionsbeschreibung von memcpy findest du z.B. in http://de.wikibooks.org/wiki/C-Programmierung unter Referenzen Standardbibliotheken string.h beschrieben. Der ganze Knackpunkt an dem Algorithmus ist die Art, wie in pwm_update aus den Helligkeitswerten die Zeiten für den Timer und die Bitmuster für den Ausgang erzeugt werden. Da steckt schon einiges dahinter. Vielleicht fängst du nicht mit der kompliziertesten Version an, sondern probierst die einfachern Versionen aus dem Artikel mal der Reihe nach mit Verstehen (!)durch. Dann ist der Schritt nicht gleich so riesengroß. Viel Spaß Rainer
Ja hab mir jetzt auch mal die erste Version vorgeknüpft. Viellen Dank für deine umfassende Hilfe bin dir wirklich zu tiefem Dank verpflichtet. So werd mich jetzt mal dran machen dies nachvollziehen zu können :) Wenn es was neues gibt melde ich mich. Gruß Andi
So lieber Rainer, dein Tip mit der delay war Gold wert, hab auf diese Weise das meißte verstehen können und die PWM läuft jetzt auch, auch konnte ich sie inzwischen meinen Anforderungen nach anpassen. Jetzt habe ich nur noch ein Problem, ich lege Spannungsversorgung an den Controller und er dimmt die LED's entsprechend der 7 voreingestellten Stufen. Hat er Stufe 7 erreicht tut sich nichts mehr die LED's leuchten nun mit dem für Schritt 7 eingestellten Wert. Ich denke das soll alles ja auch so, aber wie bekomm ich jetzt zum Beispiel das ganze erneut gestartet? Normalerweise würde ja eine While Schleife ausreichen um die LED's wieder von vorne dimmen zu lassen. Aber selbst wenn ich über den Gesamten Inhalt des "mains" ne Schleife lege und die while vor dem return Befehl am Ende des Programms raushaue bleibt der Controller in Stufe 7 stehen :( Kanns aber auch nicht nachvollziehen :( Gruß Andi
Hi Andi, na klasse. Dann wird das mit der Schleife auch noch hinzukriegen sein. Eigentlich sollte das mit der While-Schleife ok sein, am besten nicht über das ganze main, sondern nur über den Bereich wo die Helligkeitsvektoren geladen werden, da die Initialierung ja erledigt ist und sich nur die Helligkeitswerte wiederholen sollen, also: // Debug #define ZEITms 200 // Zeitdauer der PWM-Muster T1..T7 while (1) { memcpy(pwm_setting, t1, 8); pwm_update(); _delay_ms(ZEITms); ... memcpy(pwm_setting, t7, 8); pwm_update(); _delay_ms(ZEITms); } Dann muß das eigenlich funktionieren. Probier mal... Das bisherige while(1); am Ende ist egal, weil das Programm da sowieso nicht mehr hin kommt, wenn es in der neuen Schleife festhängt. Gruß Rainer
So hab jetzt mal das ganze ausprobiert, klappt jetzt auch mit der Schleife. Was mich jedoch sehr verwundert hat es funktioniert nur wenn ich die andere While(1); und den return Befehl rausschmeiße. Jetzt bin ich grad an der Auflösung dran bekomme das aber noch nicht wirklich hin. Wollte die Auflösung von 8 auf 16 ändern um mehr Stufen zum dimmen zu erhalten. Der Schreiber des Codes hat die Stellen ja kommentiert die man ändern soll, leider bringt er mir Warnhinweise sobald ich in der Liste in der "main" weitere Zeilen einfüge. Die Warnhinweise lauten wie folgt: unused variable 't8' usw. Vielleicht weiß ja jemand die Lösung... Gruß Andi
@ Andi (Gast) >Jetzt bin ich grad an der Auflösung dran bekomme das aber noch nicht >wirklich hin. Wollte die Auflösung von 8 auf 16 ändern um mehr Stufen >zum dimmen zu erhalten. Lesen und Verstehen scheinen nicht zu deinen Kernkompetenzen zugehören. Die Angabe bezieht sich nicht auf die STUFEN, sondern BITBREITE! 8 Bit = 256 Stufen. Mehr als 10 Bit =1024 Stufen sind nicht drin. MFG Falk
Ne sind wirklich nicht so meins, bin noch Frischling in C und in Mikrocontrollern und versuch grad durch lerning by doing mir das Ganze anzueignen. Ich lerne auf diese Weise wohl am schnellsten :) Klappt jetzt auf jeden Fall, hatte ganz vergessen das ich unterhalb der Liste die Variablen erst einmal aufrufen muss. Doch wenn ich dich jetzt richtig verstanden habe kann ich mit 8-Bit meine Dimmung über 1024 Stufen laufen lassen??? Hab jetzt ein Long für die Auflösung verwendet falls das so richtig ist, weil ich dachte ich könne nur auf diese Weise 32 Stufen zum dimmen erhalten. Gruß Andi
Andi schrieb: > Doch wenn ich dich jetzt richtig verstanden habe kann ich mit 8-Bit > meine Dimmung über 1024 Stufen laufen lassen??? 8-Bit Helligkeitswerte und 1024 Stufen paßt irgendwie nicht zusammen, oder? Da würd' ich nochmal nachzählen, was in dem Programm die Amplituden- und was die Zeitauflösung bestimmt. Rainer
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.