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 12000000L // 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 Zyklus(1..256)
|
14 | #define PWM_PORT_1 PORTD // Port für PWM
|
15 | #define PWM_DDR_1 DDRD // Datenrichtungsregister für PWM
|
16 | #define PWM_PORT_2 PORTC // Port für PWM
|
17 | #define PWM_DDR_2 DDRC // Datenrichtungsregister für PWM
|
18 | #define PWM_CHANNELS 11 // 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 PWM-Takt
|
23 | //#define T_PWM 1 //TEST
|
24 |
|
25 | #if ((T_PWM*PWM_PRESCALER)<(111+5))
|
26 | #error T_PWM zu klein, F_CPU muss vergrössert werden oder F_PWM oder PWM_STEPS verkleinert werden
|
27 | #endif
|
28 |
|
29 | #if ((T_PWM*PWM_STEPS)>65535)
|
30 | #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER erhöhen.
|
31 | #endif
|
32 | // includes
|
33 |
|
34 | #include <stdint.h>
|
35 | #include <string.h>
|
36 | #include <avr/io.h>
|
37 | #include <avr/interrupt.h>
|
38 | #include <util/delay.h>
|
39 |
|
40 | // globale Variablen
|
41 |
|
42 | uint16_t PINFreigabe = 0;
|
43 | uint8_t DelayBetweenPINs = 50; // verzögerung zwischen den ausgängen
|
44 |
|
45 | uint16_t pwm_timing[PWM_CHANNELS+1]; // Zeitdifferenzen der PWM Werte
|
46 | uint16_t pwm_timing_tmp[PWM_CHANNELS+1];
|
47 |
|
48 | uint16_t pwm_mask[PWM_CHANNELS+1]; // Bitmaske für PWM Bits, welche gelöscht werden sollen
|
49 | uint16_t pwm_mask_tmp[PWM_CHANNELS+1]; // ändern uint16_t oder uint32_t für mehr Kanäle
|
50 |
|
51 | uint8_t pwm_setting[PWM_CHANNELS]; // Einstellungen für die einzelnen PWM-Kanäle
|
52 | uint8_t pwm_setting_tmp[PWM_CHANNELS+1]; // Einstellungen der PWM Werte, sortiert
|
53 | // ändern auf uint16_t für mehr als 8 Bit Auflösung
|
54 |
|
55 | volatile uint8_t pwm_cnt_max=1; // Zählergrenze, Initialisierung mit 1 ist wichtig!
|
56 | volatile uint8_t pwm_sync; // Update jetzt möglich
|
57 |
|
58 | // Pointer für wechselseitigen Datenzugriff
|
59 |
|
60 | uint16_t *isr_ptr_time = pwm_timing;
|
61 | uint16_t *main_ptr_time = pwm_timing_tmp;
|
62 |
|
63 | uint16_t *isr_ptr_mask = pwm_mask; // Bitmasken fuer PWM-Kanäle
|
64 | uint16_t *main_ptr_mask = pwm_mask_tmp; // ändern uint16_t oder uint32_t für mehr Kanäle
|
65 |
|
66 | // Zeiger austauschen
|
67 | // das muss in einem Unterprogramm erfolgen,
|
68 | // um eine Zwischenspeicherung durch den Compiler zu verhindern
|
69 |
|
70 | void tausche_zeiger(void) {
|
71 | uint16_t *tmp_ptr16;
|
72 | uint16_t *tmp_ptr8; // ändern uint16_t oder uint32_t für mehr Kanäle
|
73 |
|
74 | tmp_ptr16 = isr_ptr_time;
|
75 | isr_ptr_time = main_ptr_time;
|
76 | main_ptr_time = tmp_ptr16;
|
77 | tmp_ptr8 = isr_ptr_mask;
|
78 | isr_ptr_mask = main_ptr_mask;
|
79 | main_ptr_mask = tmp_ptr8;
|
80 | }
|
81 |
|
82 | // PWM Update, berechnet aus den PWM Einstellungen
|
83 | // die neuen Werte für die Interruptroutine
|
84 |
|
85 | void pwm_update(void) {
|
86 |
|
87 | uint8_t i, j, k;
|
88 | uint16_t m1, m2, tmp_mask; // ändern uint16_t oder uint32_t für mehr Kanäle
|
89 | uint8_t min, tmp_set; // ändern auf uint16_t für mehr als 8 Bit Auflösung
|
90 |
|
91 | // PWM Maske für Start berechnen
|
92 | // gleichzeitig die Bitmasken generieren und PWM Werte kopieren
|
93 |
|
94 | m1 = 1;
|
95 | m2 = 0;
|
96 | for(i=1; i<=(PWM_CHANNELS); i++) {
|
97 | main_ptr_mask[i]=~m1; // Maske zum Löschen der PWM Ausgänge
|
98 | pwm_setting_tmp[i] = pwm_setting[i-1];
|
99 | if (pwm_setting_tmp[i]!=0) m2 |= m1; // Maske zum setzen der IOs am PWM Start
|
100 | m1 <<= 1;
|
101 | }
|
102 | main_ptr_mask[0]=m2; // PWM Start Daten
|
103 |
|
104 | // PWM settings sortieren; Einfügesortieren
|
105 |
|
106 | for(i=1; i<=PWM_CHANNELS; i++) {
|
107 | min=PWM_STEPS-1;
|
108 | k=i;
|
109 | for(j=i; j<=PWM_CHANNELS; j++) {
|
110 | if (pwm_setting_tmp[j]<min) {
|
111 | k=j; // Index und PWM-setting merken
|
112 | min = pwm_setting_tmp[j];
|
113 | }
|
114 | }
|
115 | if (k!=i) {
|
116 | // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
|
117 | tmp_set = pwm_setting_tmp[k];
|
118 | pwm_setting_tmp[k] = pwm_setting_tmp[i];
|
119 | pwm_setting_tmp[i] = tmp_set;
|
120 | tmp_mask = main_ptr_mask[k];
|
121 | main_ptr_mask[k] = main_ptr_mask[i];
|
122 | main_ptr_mask[i] = tmp_mask;
|
123 | }
|
124 | }
|
125 |
|
126 | // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
|
127 |
|
128 | k=PWM_CHANNELS; // PWM_CHANNELS Datensätze
|
129 | i=1; // Startindex
|
130 |
|
131 | while(k>i) {
|
132 | while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0)) && (k>i) ) {
|
133 |
|
134 | // aufeinanderfolgende Werte sind gleich und können vereinigt werden
|
135 | // oder PWM Wert ist Null
|
136 | if (pwm_setting_tmp[i]!=0)
|
137 | main_ptr_mask[i+1] &= main_ptr_mask[i]; // Masken vereinigen
|
138 |
|
139 | // Datensatz entfernen,
|
140 | // Nachfolger alle eine Stufe hochschieben
|
141 | for(j=i; j<k; j++) {
|
142 | pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
|
143 | main_ptr_mask[j] = main_ptr_mask[j+1];
|
144 | }
|
145 | k--;
|
146 | }
|
147 | i++;
|
148 | }
|
149 |
|
150 | // letzten Datensatz extra behandeln
|
151 | // Vergleich mit dem Nachfolger nicht möglich, nur löschen
|
152 | // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
|
153 | if (pwm_setting_tmp[i]==0) k--;
|
154 |
|
155 | // Zeitdifferenzen berechnen
|
156 |
|
157 | if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind
|
158 | main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2;
|
159 | main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2;
|
160 | k=1;
|
161 | }
|
162 | else {
|
163 | i=k;
|
164 | main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
|
165 | tmp_set=pwm_setting_tmp[i];
|
166 | i--;
|
167 | for (; i>0; i--) {
|
168 | main_ptr_time[i]=(uint16_t)T_PWM*(tmp_set-pwm_setting_tmp[i]);
|
169 | tmp_set=pwm_setting_tmp[i];
|
170 | }
|
171 | main_ptr_time[0]=(uint16_t)T_PWM*tmp_set;
|
172 | }
|
173 |
|
174 | // auf Sync warten
|
175 |
|
176 | pwm_sync=0; // Sync wird im Interrupt gesetzt
|
177 | while(pwm_sync==0);
|
178 |
|
179 | // Zeiger tauschen
|
180 | cli();
|
181 | tausche_zeiger();
|
182 | pwm_cnt_max = k;
|
183 | sei();
|
184 | }
|
185 |
|
186 | // Timer 1 Output COMPARE A Interrupt
|
187 |
|
188 | ISR(TIMER1_COMPA_vect) {
|
189 | static uint8_t pwm_cnt; // ändern auf uint16_t für mehr als 8 Bit Auflösung
|
190 | uint16_t tmp; // ändern uint16_t oder uint32_t für mehr Kanäle
|
191 |
|
192 | OCR1A += isr_ptr_time[pwm_cnt];
|
193 | tmp = isr_ptr_mask[pwm_cnt];
|
194 |
|
195 | if (pwm_cnt == 0) {
|
196 | tmp &= PINFreigabe;
|
197 | PWM_PORT_1 = tmp; // Ports setzen zu Begin der PWM
|
198 | PWM_PORT_2 = tmp >> 8; // zusätzliche PWM-Ports hier setzen
|
199 |
|
200 | pwm_cnt++;
|
201 | }
|
202 | else {
|
203 | tmp &= PINFreigabe;
|
204 | PWM_PORT_1 &= tmp; // Ports löschen
|
205 | PWM_PORT_2 &= tmp >> 8; // zusätzliche PWM-Ports hier setzen
|
206 |
|
207 | if (pwm_cnt == pwm_cnt_max) {
|
208 | pwm_sync = 1; // Update jetzt möglich
|
209 | pwm_cnt = 0;
|
210 | }
|
211 | else pwm_cnt++;
|
212 | }
|
213 | }
|
214 |
|
215 | int main(void) {
|
216 |
|
217 | // Helligkeit für jeden PIN einstellen
|
218 | // CN14 CN12 CN11 CN10 CN9 CN8 CN7 CN6 CN5 CN3 CN1
|
219 | uint8_t timing[PWM_CHANNELS]={243, 243, 244, 245, 246, 249, 249, 250, 253, 254, 255};
|
220 | uint8_t Anzahl = 9;
|
221 | uint8_t cnt;
|
222 | int i;
|
223 |
|
224 |
|
225 | // PWM Port einstellen
|
226 |
|
227 | PWM_DDR_1 = 0xFF; // Port als Ausgang
|
228 | PWM_DDR_2 = 0x07; // zusätzliche PWM-Ports hier setzen
|
229 | DDRB = 0x00; // Port B als Input
|
230 | PORTB = 0x03; // Pull Up auf PB0 und PB1 aktivieren
|
231 |
|
232 | // Timer 1 OCRA1, als variablen Timer nutzen
|
233 |
|
234 | TCCR1B = 2; // Timer läuft mit Prescaler 8
|
235 | TIMSK1 |= (1<<OCIE1A); // Interrupt freischalten
|
236 |
|
237 | sei(); // Interrupts global einschalten
|
238 |
|
239 | // Abfrage ob 9 oder 11 Pins benutzt werden sollen
|
240 | if ((PINB & (1<<PINB1)) == 0)
|
241 | Anzahl = 11;
|
242 |
|
243 |
|
244 | memcpy(pwm_setting, timing, PWM_CHANNELS);
|
245 | pwm_update();
|
246 |
|
247 | // Abfrage in welche Richtung gezählt werden soll
|
248 | if ((PINB & (1<<PINB0)) == 0)
|
249 | {
|
250 | for (i=0; i<Anzahl; i++)
|
251 | {
|
252 | PINFreigabe |= (1 << i);
|
253 |
|
254 | for (cnt=0;cnt<DelayBetweenPINs;cnt++)
|
255 | {
|
256 | _delay_ms(1);
|
257 | }
|
258 | }
|
259 | }
|
260 | else
|
261 | {
|
262 | for (i=Anzahl-1; i>=0; i--)
|
263 | {
|
264 | PINFreigabe |= (1 << i);
|
265 |
|
266 | for (cnt=0;cnt<DelayBetweenPINs;cnt++)
|
267 | {
|
268 | _delay_ms(1);
|
269 | }
|
270 | }
|
271 | }
|
272 |
|
273 | while(1);
|
274 | }
|