Как сгенерировать ШИМ-сигнал для двигателя BLDC с помощью dsPIC33?

Я новичок в микроконтроллерах и работаю над проектом, в котором используется микроконтроллер dsPIC33 для управления двигателем BLDC. У меня возникли проблемы с генерацией выходного сигнала ШИМ. В будущем я буду использовать вход датчика Холла, но, поскольку в данный момент у меня его нет, я хотел бы пока просто смоделировать выход ШИМ. Если у вас есть какие-либо дополнительные вопросы, которые помогут, пожалуйста, не стесняйтесь спрашивать. Любая обратная связь приветствуется и ценится!

Вот скриншот моего вывода сигнала прямо сейчас.  Вместо изменения состояний, как я хочу в StateTableIndex, он сохраняет это одно состояние для всех сигналов.

Вот скриншот моего вывода сигнала прямо сейчас. Вместо изменения состояний, как я хочу в StateTableIndex, он сохраняет это одно состояние для всех сигналов.

  // 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");
    }
}
Я бы посоветовал вам попытаться выяснить, как вы хотите контролировать скорость bldc; а затем прочитайте техническое описание чипа и руководство по компилятору, чтобы узнать, как генерировать ШИМ, если вы пришли к выводу, что хотите управлять двигателем с помощью ШИМ. много раз замедление поможет вам быстрее добраться до конца.
Это не улучшит функциональность, но операции деления занимают больше командных циклов, чем другие операции. В вашем основном цикле было бы лучше увеличивать таблицу состояний, а затем сбрасывать значение, когда оно превышает количество индексов в массиве.

Ответы (1)

Честно говоря, я не уверен, что кто-то проверит ваш код, я бы не стал. Но я хотел бы объяснить, что вам нужно. Вариантов очень много, так что давайте сузим ваш выбор.

Во-первых, чтобы переместить BLDC, вам нужно выбрать метод коммутации. Видите ли, в BLDC магниты перемещаются по катушкам, поэтому вам нужно коммутировать каждую катушку по-разному, чтобы отрегулировать силу, приложенную к магниту. Чтобы усилие было максимальным, вы должны поддерживать «угол коммутации» на уровне 90 градусов. А для этого нужно знать точное положение. Поэтому вам нужна обратная связь по положению. Используйте датчики Холла — они обеспечат шесть положений на пару полюсов (пары полюсов указаны в паспорте двигателя). Другой вариант — энкодер, но он немного сложнее.

Таким образом, имея пары полюсов, вы можете управлять двумя конкретными фазами. Правильно, у вас есть три провода, поэтому для каждой комбинации датчиков Холла вам нужно будет выбрать два провода и одно направление.

Теперь вам нужно посмотреть, что у вас есть в железе. У вас должно быть три полумоста, каждый из которых фактически представляет собой два мосфета. Вам придется управлять каждым мосфетом с помощью собственного ШИМ. Каждая пара получит встречный ШИМ с небольшой паузой-мертвой задержкой. Рабочий цикл будет пропорционален напряжению. Поэтому, если вы хотите управлять фазами A и B, используйте 0 В на C, + V на A и -V на B. Затем переключитесь в соответствии с датчиками эффекта Холла.

Что ж, если все это поможет, я могу продолжить и перейти к петлям положения и скорости, управлению током и т. д.

Спасибо за ответ! Видите ли, я понимаю концепцию ШИМ и, по большей части, то, что я должен сделать, чтобы получить эту коммутацию теоретически. Но где у меня проблемы, так это в моем коде. Все, что я пытаюсь сделать, это смоделировать, как будут выглядеть сигналы ШИМ, потому что у меня еще нет доступа к двигателю BLDC или датчикам эффекта Холла. Я хочу пройтись по массивам StateTableIndex, которые я настроил, чтобы сделать биты ШИМ высокими или низкими и продемонстрировать, как будут выглядеть сигналы ШИМ. В любом случае вы можете быстро просмотреть мой код и посмотреть, что я делаю неправильно? Просто очень много комментариев
Чувак, кроме меня никто не ответил :) я надеялся, что кто-то ответит. Хорошо, я не буду читать код, извините. Но я действительно не понимаю, что не так с выводом, пожалуйста, объясните.
Не беспокойтесь, спасибо, что ответили. По сути, в массивах StateTableIndex[] есть список битов, которые говорят, является ли этот вывод pwm высоким или низким. И в моем цикле while я индексирую эти состояния, за исключением первого и последнего состояний, которые являются неисправностями. Но сигнал PWM не меняется, когда он индексируется через массив, как вы можете видеть на картинке, которую я разместил.
Ладно, извини, но я даже не понимаю. Пожалуйста, начните сначала.