1 | #define F_CPU 4000000L // Systemtakt in Hz
|
2 | #define F_PWM 100L // PWM-Frequenz in Hz
|
3 | #define PWM_PRESCALER 8 // Vorteiler für den Timer
|
4 | #define PWM_STEPS 256 // PWM-Schritte pro Zyklus(1..256)
|
5 | #define PWM_PORT PORTB // Port für PWM
|
6 | #define PWM_DDR DDRB // Datenrichtungsregister für PWM
|
7 | #define PWM_CHANNELS 8 // Anzahl der PWM-Kanäle
|
8 |
|
9 | // ab hier nichts ändern, wird alles berechnet
|
10 |
|
11 | #define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
|
12 | //#define T_PWM 1 //TEST
|
13 |
|
14 | #if ((T_PWM*PWM_PRESCALER)<(111+5))
|
15 | #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden
|
16 | #endif
|
17 |
|
18 | #if ((T_PWM*PWM_STEPS)>65535)
|
19 | #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER erhöhen.
|
20 | #endif
|
21 | // includes
|
22 |
|
23 | #include <stdint.h>
|
24 | #include <string.h>
|
25 | #include <avr/io.h>
|
26 | #include <avr/interrupt.h>
|
27 | #include <util/delay.h>
|
28 |
|
29 | // globale Variablen
|
30 |
|
31 | uint16_t pwm_timing[PWM_CHANNELS+1]; // Zeitdifferenzen der PWM Werte
|
32 | uint16_t pwm_timing_tmp[PWM_CHANNELS+1];
|
33 | uint8_t pwm_mask[PWM_CHANNELS+1]; // Bitmaske für PWM Bits, welche gelöscht werden sollen
|
34 | uint8_t pwm_mask_tmp[PWM_CHANNELS+1];
|
35 | uint8_t pwm_setting[PWM_CHANNELS]; // Einstellungen für die einzelnen PWM-Kanäle
|
36 | uint8_t pwm_setting_tmp[PWM_CHANNELS+1]; // Einstellungen der PWM Werte, sortiert
|
37 |
|
38 | volatile uint8_t pwm_cnt_max=1; // Zählergrenze, Initialisierung mit 1 ist wichtig!
|
39 | volatile uint8_t pwm_sync; // Update jetzt möglich
|
40 |
|
41 | // Pointer für wechselseitigen Datenzugriff
|
42 |
|
43 | uint16_t * isr_ptr_time = pwm_timing;
|
44 | uint16_t * main_ptr_time = pwm_timing_tmp;
|
45 | uint8_t * isr_ptr_mask = pwm_mask;
|
46 | uint8_t * main_ptr_mask = pwm_mask_tmp;
|
47 |
|
48 | // Zeiger austauschen
|
49 | // das muss in einem Unterprogramm erfolgen,
|
50 | // um eine Zwischenspeicherung durch den Compiler zu verhindern
|
51 |
|
52 | void tausche_zeiger(void) {
|
53 | uint16_t * tmp_ptr16;
|
54 | uint8_t * tmp_ptr8;
|
55 |
|
56 | tmp_ptr16 = isr_ptr_time;
|
57 | isr_ptr_time = main_ptr_time;
|
58 | main_ptr_time = tmp_ptr16;
|
59 | tmp_ptr8 = isr_ptr_mask;
|
60 | isr_ptr_mask = main_ptr_mask;
|
61 | main_ptr_mask = tmp_ptr8;
|
62 | }
|
63 |
|
64 | // PWM Update, berechnet aus den PWM Einstellungen
|
65 | // die neuen Werte für die Interruptroutine
|
66 |
|
67 | void pwm_update(void) {
|
68 | uint8_t i, j, k, min;
|
69 | uint8_t tmp;
|
70 |
|
71 | // PWM Maske für Start berechnen
|
72 | // gleichzeitig die Bitmasken generieren und PWM Werte kopieren
|
73 |
|
74 | tmp=0;
|
75 | j = 1;
|
76 | for(i=1; i<=(PWM_CHANNELS); i++) {
|
77 | main_ptr_mask[i]=~j; // Maske zum Löschen der PWM Ausgänge
|
78 | pwm_setting_tmp[i] = pwm_setting[i-1];
|
79 | if (pwm_setting_tmp[i]!=0) tmp |= j; // Maske zum setzen der IOs am PWM Start
|
80 | j <<= 1;
|
81 | }
|
82 | main_ptr_mask[0]=tmp; // PWM Start Daten
|
83 |
|
84 | // PWM settings sortieren; Einfügesortieren
|
85 |
|
86 | for(i=1; i<=PWM_CHANNELS; i++) {
|
87 | min=255;
|
88 | k=i;
|
89 | for(j=i; j<=PWM_CHANNELS; j++) {
|
90 | if (pwm_setting_tmp[j]<min) {
|
91 | k=j; // Index und PWM-setting merken
|
92 | min = pwm_setting_tmp[j];
|
93 | }
|
94 | }
|
95 | if (k!=i) {
|
96 | // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
|
97 | tmp = pwm_setting_tmp[k];
|
98 | pwm_setting_tmp[k] = pwm_setting_tmp[i];
|
99 | pwm_setting_tmp[i] = tmp;
|
100 | tmp = main_ptr_mask[k];
|
101 | main_ptr_mask[k] = main_ptr_mask[i];
|
102 | main_ptr_mask[i] = tmp;
|
103 | }
|
104 | }
|
105 |
|
106 | // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
|
107 |
|
108 | k=PWM_CHANNELS; // PWM_CHANNELS Datensätze
|
109 | i=1; // Startindex
|
110 |
|
111 | while(k>i) {
|
112 | while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0)) && (k>i) ) {
|
113 |
|
114 | // aufeinanderfolgende Werte sind gleich und können vereinigt werden
|
115 | // oder PWM Wert ist Null
|
116 | if (pwm_setting_tmp[i]!=0)
|
117 | main_ptr_mask[i+1] &= main_ptr_mask[i]; // Masken vereinigen
|
118 |
|
119 | // Datensatz entfernen,
|
120 | // Nachfolger alle eine Stufe hochschieben
|
121 | for(j=i; j<k; j++) {
|
122 | pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
|
123 | main_ptr_mask[j] = main_ptr_mask[j+1];
|
124 | }
|
125 | k--;
|
126 | }
|
127 | i++;
|
128 | }
|
129 |
|
130 | // letzten Datensatz extra behandeln
|
131 | // Vergleich mit dem Nachfolger nicht möglich, nur löschen
|
132 | // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
|
133 | if (pwm_setting_tmp[i]==0) k--;
|
134 |
|
135 | // Zeitdifferenzen berechnen
|
136 |
|
137 | if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind
|
138 | main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2;
|
139 | main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2;
|
140 | k=1;
|
141 | }
|
142 | else {
|
143 | i=k;
|
144 | main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
|
145 | j=pwm_setting_tmp[i];
|
146 | i--;
|
147 | for (; i>0; i--) {
|
148 | main_ptr_time[i]=(uint16_t)T_PWM*(j-pwm_setting_tmp[i]);
|
149 | j=pwm_setting_tmp[i];
|
150 | }
|
151 | main_ptr_time[0]=(uint16_t)T_PWM*j;
|
152 | }
|
153 |
|
154 | // auf Sync warten
|
155 |
|
156 | pwm_sync=0; // Sync wird im Interrupt gesetzt
|
157 | while(pwm_sync==0);
|
158 |
|
159 | // Zeiger tauschen
|
160 | cli();
|
161 | tausche_zeiger();
|
162 | pwm_cnt_max = k;
|
163 | sei();
|
164 | }
|
165 |
|
166 | // Timer 1 Output COMPARE A Interrupt
|
167 |
|
168 | ISR(TIMER1_COMPA_vect) {
|
169 | static uint8_t pwm_cnt;
|
170 | uint8_t tmp;
|
171 |
|
172 | OCR1A += isr_ptr_time[pwm_cnt];
|
173 | tmp = isr_ptr_mask[pwm_cnt];
|
174 |
|
175 | if (pwm_cnt == 0) {
|
176 | PWM_PORT = tmp; // Ports setzen zu Begin der PWM
|
177 | pwm_cnt++;
|
178 | }
|
179 | else {
|
180 | PWM_PORT &= tmp; // Ports löschen
|
181 | if (pwm_cnt == pwm_cnt_max) {
|
182 | pwm_sync = 1; // Update jetzt möglich
|
183 | pwm_cnt = 0;
|
184 | }
|
185 | else pwm_cnt++;
|
186 | }
|
187 | }
|
188 |
|
189 | int main(void) {
|
190 |
|
191 | PWM_DDR = 0xFF; // Port als Ausgang
|
192 |
|
193 | // Timer 1 OCRA1, als variablem Timer nutzen
|
194 | TCCR1B = 2; // Timer läuft mit Prescaler 8
|
195 | TIMSK |= (1<<OCIE1A); // Interrupt freischalten
|
196 | sei(); // Interrupts gloabl einschalten
|
197 |
|
198 | int i1,i2,i3,i4,i5,i6,i7,i8;
|
199 |
|
200 | //for (x=1,x=PWM_CHANNELS,x++)
|
201 | while (1)
|
202 | {
|
203 |
|
204 | if (i1 > rand()%235)
|
205 | {
|
206 | i1 = i1 - rand()%20;
|
207 | }
|
208 | else
|
209 | {
|
210 | i1 = i1 + rand()%20;
|
211 | };
|
212 |
|
213 | if (i2 > rand()%235)
|
214 | {
|
215 | i2 = i2 - rand()%20;
|
216 | }
|
217 | else
|
218 | {
|
219 | i2 = i2 + rand()%20;
|
220 | };
|
221 |
|
222 | if (i3 > rand()%235)
|
223 | {
|
224 | i3 = i3 - rand()%20;
|
225 | }
|
226 | else
|
227 | {
|
228 | i3 = i3 + rand()%20;
|
229 | };
|
230 |
|
231 | if (i4 > rand()%235)
|
232 | {
|
233 | i4 = i4 - rand()%20;
|
234 | }
|
235 | else
|
236 | {
|
237 | i4 = i4 + rand()%20;
|
238 | };
|
239 |
|
240 | if (i5 > rand()%235)
|
241 | {
|
242 | i5 = i5 - rand()%20;
|
243 | }
|
244 | else
|
245 | {
|
246 | i5 = i5 + rand()%20;
|
247 | };
|
248 |
|
249 | if (i6 > rand()%235)
|
250 | {
|
251 | i6 = i6 - rand()%20;
|
252 | }
|
253 | else
|
254 | {
|
255 | i6 = i6 + rand()%20;
|
256 | };
|
257 |
|
258 | if (i7 > rand()%235)
|
259 | {
|
260 | i7 = i7 - rand()%20;
|
261 | }
|
262 | else
|
263 | {
|
264 | i7 = i7 + rand()%20;
|
265 | };
|
266 |
|
267 | if (i8 > rand()%235)
|
268 | {
|
269 | i8 = i8 - rand()%20;
|
270 | }
|
271 | else
|
272 | {
|
273 | i8 = i8 + rand()%20;
|
274 | };
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | const uint8_t t1[8] = {i1, i2, i3, i4, i5, i6, i7, i8};
|
280 | memcpy(pwm_setting, t1, 8);
|
281 | pwm_update();
|
282 | _delay_ms(rand()%50+50);
|
283 |
|
284 |
|
285 | };
|
286 |
|
287 | return 0;
|
288 | }
|