Wie erzeuge ich mit dsPIC33 ein PWM-Signal für einen BLDC-Motor?

Ich bin ein Anfänger mit Mikrocontrollern und arbeite an einem Projekt, das den dsPIC33-Mikrocontroller zur Steuerung eines BLDC-Motors verwendet. Ich habe Probleme beim Generieren des PWM-Signalausgangs. Ich werde in Zukunft den Eingang des Hall-Effekt-Sensors verwenden, aber da ich diesen im Moment nicht zur Verfügung habe, möchte ich vorerst nur den PWM-Ausgang simulieren. Wenn Sie weitere Fragen haben, die Ihnen weiterhelfen, können Sie diese gerne stellen. Jedes Feedback ist willkommen und wird geschätzt!

Hier ist ein Screenshot meiner aktuellen Signalausgabe.  Anstatt Zustände zu ändern, wie ich es im StateTableIndex möchte, behält es diesen einen Zustand für alle Signale bei.

Hier ist ein Screenshot meiner aktuellen Signalausgabe. Anstatt Zustände zu ändern, wie ich es im StateTableIndex möchte, behält es diesen einen Zustand für alle Signale bei.

  // DSPIC33EP256MC506 Configuration Bit Settings

// 'C' source line config statements

// FICD
#pragma config ICS = PGD2               // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF             // JTAG Enable bit (JTAG is disabled)

// FPOR
#pragma config ALTI2C1 = ON             // Alternate I2C1 pins (I2C1 mapped to ASDA1/ASCL1 pins)
#pragma config ALTI2C2 = ON             // Alternate I2C2 pins (I2C2 mapped to ASDA2/ASCL2 pins)
#pragma config WDTWIN = WIN25           // Watchdog Window Select bits (WDT Window is 25% of WDT period)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128           // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON              // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)

// FOSC
#pragma config POSCMD = XT              // Primary Oscillator Mode Select bits (XT Crystal Oscillator Mode)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = OFF            // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD           // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)

// FOSCSEL
#pragma config FNOSC = FRC              // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config PWMLOCK = ON             // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = ON                // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)

    // FGS
    #pragma config GWRP = OFF               // General Segment Write-Protect bit (General Segment may be written)
    #pragma config GCP = OFF                // General Segment Code-Protect bit (General Segment Code protect is Disabled)
    // #pragma config statements should precede project file includes.
    // Use project enums instead of #define for ON and OFF.

    #include <xc.h>
    #include <p33Exxxx.h>
    #include <stdio.h>

#define SYS_FREQ        50000000L
#define FCY             SYS_FREQ/2
/****************************CONFIGURATION****************************/

unsigned int StateIndexTable1[] = {0xC00C, 0xC00C, 0xC004, 0xC00C, 0xC00C, 0xC00C, 0xC004, 0xC00C};
unsigned int StateIndexTable2[] = {0xC00C, 0xC00C, 0xC00C, 0xC00C, 0xC004, 0xC004, 0xC00C, 0xC00C};
unsigned int StateIndexTable3[] = {0xC00C, 0xC004, 0xC00C, 0xC004, 0xC00C, 0xC00C, 0xC00C, 0xC00C};

long unsigned int pwmOutput = 0;
int indexx = 0;

void initAdc1(void);
void Init_Timers(void);
void Delay_us(unsigned int);
void Delay_ms(unsigned int);
int  ADCValue, i;
int  main(void)
{
    // Configure the device PLL to obtain 40 MIPS operation. The crystal frequency is 8 MHz.
    // Divide 8 MHz by 2, multiply by 40 and divide by 2. This results in Fosc of 80 MHz.
    // The CPU clock frequency is Fcy = Fosc/2 = 40 MHz.
    PLLFBD = 0x0030;                    /* M  = 40 */
    CLKDIVbits.PLLPOST = 1;         /* N1 = 2  */
    CLKDIVbits.PLLPRE = 0;          /* N2 = 2  */
    OSCTUN = 0;

    /* Initiate Clock Switch to Primary Oscillator with PLL (NOSC = 0x3) */
    __builtin_write_OSCCONH(0x03);
    __builtin_write_OSCCONL(0x01);

    while (OSCCONbits.COSC != 0x3);
    while (_LOCK == 0);             /* Wait for PLL lock at 40 MIPS */

    initAdc1();
    Init_Timers();

    while(1)
    {
        /*
        IOCON1 = 0xC004;
        IOCON2 = 0xC00C;
        IOCON3 = 0xC00C;
        */
        Delay_us(100);
        pwmOutput = (indexx % 6) + 1;
        IOCON1 = StateIndexTable1[pwmOutput];
        IOCON2 = StateIndexTable2[pwmOutput];
        IOCON3 = StateIndexTable3[pwmOutput];
        indexx++;
        Delay_ms(1000);

        /*
        AD1CON1bits.SAMP = 1;         // Start sampling
        Delay_us(10);                 // Wait for sampling time (10 us)
        AD1CON1bits.SAMP = 0;         // Start the conversion
        while (!AD1CON1bits.DONE);    // Wait for the conversion to complete
        ADCValue = ADC1BUF0;          // Read the ADC conversion result
        */
    }


}
void initAdc1(void) {

    TRISB = 0x01FF;     //Set PWM as outputs 

    /* Set port configuration */ 
    ANSELA = ANSELB = ANSELC  = ANSELE = 0x0000;

    ANSELEbits.ANSE13 = 1; //Set pot to analog 
    TRISEbits.TRISE13 = 1; //Set pot to input 

    /* Initialize and enable ADC module */

    AD1CON1 = 0x0000;
    AD1CON2 = 0x0000;
    AD1CON3 = 0x000F;
    AD1CON4 = 0x0000;
    AD1CHS0 = 0x000D;
    AD1CHS123 = 0x0000;
    AD1CSSH = 0x0000;
    AD1CSSL = 0x0000;
    AD1CON1bits.ADON = 1;
    Delay_us(20);


    // select master duty cycle MDC
    PWMCON1 = 0x0000;
    PWMCON2 = 0x0000;
    PWMCON3 = 0x0000;
    // initialize PWMxH/L in override low state
    IOCON1 = 0xC300;
    IOCON2 = 0xC300;
    IOCON3 = 0xC300;
    // PWM fault configuration
    FCLCON1 = 0x03;
    FCLCON2 = 0x03;
    FCLCON3 = 0x03; 

    PTPER = 4999;         // (FOSC/FPWM - 1)
    SEVTCMP = PTPER;        // PWM period is special event trigger

    PDC1 = PDC2 = PDC3 = 499 ;           // Initialize Duty Cycles @ 50% 

    PTCON = 0x8000;



}

void Init_Timers(void){


    //Timer 4&5
    T4CON = 0x0038;      //32 bit timer, pre-scaler of 256
    T5CONbits.TSIDL = 0; // Timer to operate during idle
    TMR5HLD = 0;         // MSB (write to MSW first then LSW)
    TMR4 = 0;            // LSB
    PR5 = 0xFFFF;        // Period of MSB
    PR4 = 0xFFFF;        // Period of LSB
}

// [TMR5][TMR4] holds up to 2147483648 decimal
// max value for compare = 214783648 / 97
// DELAY UP TO 22139006 ms
void Delay_ms(unsigned int delay) {
    TMR5HLD = 0;            // Reset timer values
    TMR4 = 0;
    T4CONbits.TON = 1;      // Start 32 bit timer

    unsigned long timer_4_ticks = 97UL * delay;    // Calculate clock ticks to wait
    unsigned long tmp = 0;

    while(tmp < timer_4_ticks) {              
        tmp = TMR4;
        tmp |= (unsigned long) TMR5HLD << 16;
    }

    T4CONbits.TON = 0;
}

void Delay_us(unsigned int delay)
{
    for (i = 0; i < delay; i++)
    {
        __asm__ volatile ("repeat #39");
        __asm__ volatile ("nop");
    }
}
Ich würde vorschlagen, dass Sie versuchen, herauszufinden, wie Sie die bldc-Geschwindigkeit steuern möchten; und lesen Sie dann das Datenblatt und das Compiler-Handbuch des Chips, um zu sehen, wie Sie PWM generieren, wenn Sie zu dem Schluss kommen, dass Sie den Motor über PWM steuern möchten. Wenn Sie langsamer werden, gelangen Sie oft schneller ans Ende.
Dies hilft nicht bei der Funktionalität, aber Divisionsoperationen benötigen mehr Befehlszyklen als andere Operationen. In Ihrer Hauptschleife wäre es besser, die Zustandstabelle zu inkrementieren und dann den Wert zurückzusetzen, wenn er die Anzahl der Indizes im Array überschreitet.

Antworten (1)

Ehrlich gesagt bin ich mir nicht sicher, ob jemand Ihren Code überprüfen wird, würde ich nicht. Aber ich möchte erklären, was Sie brauchen. Es gibt viele, viele Optionen, also grenzen wir Ihre Auswahl ein.

Um BLDC zu verschieben, müssen Sie zunächst die Kommutierungsmethode festlegen. Sie sehen, in BLDC bewegen sich die Magnete über Spulen, sodass Sie jede Spule anders kommutieren müssen, um die auf den Magneten ausgeübte Kraft anzupassen. Um die Kraft maximal zu halten, müssen Sie den "Kommutierungswinkel" bei 90 Grad halten. Und dafür müssen Sie die genaue Position kennen. Sie brauchen also Positionsrückmeldung. Verwenden Sie Hall-Effekt-Sensoren - sie bieten sechs Positionen pro Polpaar (Polpaare sind im Motordatenblatt angegeben). Eine andere Option ist Encoder, aber es ist ein wenig komplizierter.

Mit den Polpaaren können Sie also gezielt zwei Phasen ansteuern. Richtig, Sie haben drei Drähte, also müssen Sie für jede Kombination von Hall-Effekt-Sensoren zwei Drähte und eine Richtung auswählen.

Jetzt müssen Sie sehen, was Sie an Hardware haben. Sie sollten drei Halbbrücken haben, jede ist tatsächlich zwei Mosfets. Sie müssen jeden Mosfet mit einem eigenen PWM ansteuern. Jedes Paar erhält entgegengesetzte PWM mit einer kleinen Pausen-Totzeit. Das Tastverhältnis ist proportional zur Spannung. Wenn Sie also die Phasen A und B ansteuern möchten, verwenden Sie 0 V auf C, + V auf A und -V auf B. Schalten Sie dann entsprechend den Halleffektsensoren um.

Nun, wenn das alles hilfreich ist, kann ich weitermachen und Positions- und Geschwindigkeitsschleifen, Stromsteuerung usw. erreichen ...

Danke für die Rückmeldung! Sehen Sie, ich verstehe das Konzept von PWM und zum größten Teil, was ich tun muss, um diese Kommutierung theoretisch zu erreichen. Aber wo ich Probleme habe, ist in meinem Code. Ich versuche nur zu simulieren, wie die PWM-Signale aussehen werden, da ich noch keinen Zugriff auf den BLDC-Motor oder die Hall-Effekt-Sensoren habe. Ich möchte die von mir eingerichteten StateTableIndex-Arrays durchlaufen, die die PWM-Bits hoch oder niedrig machen, und demonstrieren, wie die PWM-Signale aussehen werden. Wie können Sie meinen Code ganz schnell durchsuchen und sehen, was ich falsch mache? Es gibt wirklich nur viele Kommentare
Mann, außer mir hat niemand geantwortet :) Ich hatte gehofft, jemand wird es tun. OK, ich werde den Code nicht lesen, sorry. Aber ich verstehe wirklich nicht, was mit der Ausgabe falsch ist, bitte erklären Sie es.
Keine Sorge, danke für deine Antwort. Grundsätzlich gibt es in den StateTableIndex[]-Arrays eine Liste von Bits, die angeben, ob dieser PWM-Ausgang hoch oder niedrig ist. Und in meiner While-Schleife indiziere ich diese Zustände, mit Ausnahme des ersten und letzten Zustands, die Fehler sind. Aber das PWM-Signal ändert sich nicht, wenn es durch das Array indiziert wird, wie Sie auf dem Bild sehen können, das ich gepostet habe.
Ok, tut mir leid, aber ich verstehe es nicht einmal. Bitte fangen Sie von vorne an.