Wie kann mann daraus am besten 10 pwm Kanäle machen wäre sehr dankbar für Hilfe , komm einfach nicht drauf! /* Eine 8-kanalige PWM mit intelligentem Lösungsansatz ATmega32 @ 8 MHz */ // Defines an den Controller und die Anwendung anpassen #define F_CPU 8000000L // 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 PORTB // Port für PWM #define PWM_DDR DDRB // 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]; 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 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; uint8_t * main_ptr_mask = pwm_mask_tmp; // 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; 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, min; uint8_t tmp; // PWM Maske für Start berechnen // gleichzeitig die Bitmasken generieren und PWM Werte kopieren tmp=0; j = 1; for(i=1; i<=(PWM_CHANNELS); i++) { main_ptr_mask[i]=~j; // Maske zum Löschen der PWM Ausgänge pwm_setting_tmp[i] = pwm_setting[i-1]; if (pwm_setting_tmp[i]!=0) tmp |= j; // Maske zum setzen der IOs am PWM Start j <<= 1; } main_ptr_mask[0]=tmp; // PWM Start Daten // PWM settings sortieren; Einfügesortieren for(i=1; i<=PWM_CHANNELS; i++) { min=255; 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 = pwm_setting_tmp[k]; pwm_setting_tmp[k] = pwm_setting_tmp[i]; pwm_setting_tmp[i] = tmp; tmp = main_ptr_mask[k]; main_ptr_mask[k] = main_ptr_mask[i]; main_ptr_mask[i] = tmp; } } // 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]); j=pwm_setting_tmp[i]; i--; for (; i>0; i--) { main_ptr_time[i]=(uint16_t)T_PWM*(j-pwm_setting_tmp[i]); j=pwm_setting_tmp[i]; } main_ptr_time[0]=(uint16_t)T_PWM*j; } // 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; uint8_t tmp; 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 pwm_cnt++; } else { PWM_PORT &= tmp; // Ports löschen 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 // 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; }
@sv (Gast) >Wie kann mann daraus am besten 10 pwm Kanäle machen wäre sehr dankbar >für Hilfe , komm einfach nicht drauf! Lies vorher mal was über Netiquette. Das aufbohren auf 10 Kanäle ist recht einfach, #define PWM_CHANNELS 10 // Anzahl der PWM-Kanäle uint16_t * isr_ptr_mask = pwm_mask; uint16_t * main_ptr_mask = pwm_mask_tmp; uint16_t * tmp_ptr8; uint16_t tmp; Und halt in der ISR den Zugriff von einem Port auf zwei Ports aufbohren. Alles in Allem nicht viel. MFG Falk
schönen Dank ! Das hatte ich schon probiert das klappte leider nicht wie gewünscht ! Beim ersten PORT gehts ja aber beim zweiten tut sich nichts. MFG hier noch die ISR ISR(TIMER1_COMPA_vect) { static uint8_t pwm_cnt; uint16_t tmp ; OCR1A += isr_ptr_time [pwm_cnt]; tmp = isr_ptr_mask [pwm_cnt]; if (pwm_cnt == 0) { PWM_PORT = tmp; PWM_PORT2 = tmp>>8; //hier der zweite PORT pwm_cnt++; } else { PWM_PORT &= tmp; PWM_PORT2 &= tmp>>8; //hier der zweite PORT if (pwm_cnt == pwm_cnt_max) { pwm_sync = 1; pwm_cnt = 0; } else pwm_cnt++; } }
2ten Port nicht als Ausgang geschaltet? >int main(void) { > > // PWM Port einstellen > > PWM_DDR = 0xFF; // Port als Ausgang MfG AVR-Frickler
Ist auf Ausgang gestellt, es kommt mir so vor als ob in der isr_ptr_mask nur 8 Bits sind da ich an PWM_PORT2 nichts rausbekomme !
@ sv (Gast) >Ist auf Ausgang gestellt, es kommt mir so vor als ob in der isr_ptr_mask >nur 8 Bits sind da ich an PWM_PORT2 nichts rausbekomme ! Stimmt, denn die wird in der Funktion über j generiert, und das ist im Moment noch 8 Bit. ;-)
Da ist noch mehr, was auf 16 Bit aufgebohrt werden muss. Ich dachte auch zuerst, das geht sehr simpel. Aber da hängt schon noch mehr drann. Ohne Codeanalyse kommt man nicht weit.
Schönen Dank erstmal ! Dann werde ich mal etwas genauer mir die Sache anschauen! Wäre aber für jeden Lösungsansatz Dankbar!
So geht jetzt hab den Fehler gefunden. memcpy(pwm_setting, t7, 8); die acht muss ne 10 sein. memcpy(pwm_setting, t7,10); dann gings wie gewünscht!
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.