www.mikrocontroller.net

Forum: Compiler & IDEs wie daraus 10 PWM channals machen


Autor: sv (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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;
}

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: sv (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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++;
    }

}

Autor: AVR-Frickler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
2ten Port nicht als Ausgang geschaltet?

>int main(void) {
>
>   // PWM Port einstellen
>
>    PWM_DDR = 0xFF;         // Port als Ausgang

MfG
AVR-Frickler

Autor: sv (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 !

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  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. ;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: sv (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schönen Dank erstmal !

Dann werde ich mal etwas genauer mir die Sache anschauen!

Wäre aber für jeden Lösungsansatz Dankbar!

Autor: sv (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.