Я новичок в микроконтроллерах и работаю над проектом, в котором используется микроконтроллер dsPIC33 для управления двигателем BLDC. У меня возникли проблемы с генерацией выходного сигнала ШИМ. В будущем я буду использовать вход датчика Холла, но, поскольку в данный момент у меня его нет, я хотел бы пока просто смоделировать выход ШИМ. Если у вас есть какие-либо дополнительные вопросы, которые помогут, пожалуйста, не стесняйтесь спрашивать. Любая обратная связь приветствуется и ценится!
Вот скриншот моего вывода сигнала прямо сейчас. Вместо изменения состояний, как я хочу в 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, вам нужно выбрать метод коммутации. Видите ли, в BLDC магниты перемещаются по катушкам, поэтому вам нужно коммутировать каждую катушку по-разному, чтобы отрегулировать силу, приложенную к магниту. Чтобы усилие было максимальным, вы должны поддерживать «угол коммутации» на уровне 90 градусов. А для этого нужно знать точное положение. Поэтому вам нужна обратная связь по положению. Используйте датчики Холла — они обеспечат шесть положений на пару полюсов (пары полюсов указаны в паспорте двигателя). Другой вариант — энкодер, но он немного сложнее.
Таким образом, имея пары полюсов, вы можете управлять двумя конкретными фазами. Правильно, у вас есть три провода, поэтому для каждой комбинации датчиков Холла вам нужно будет выбрать два провода и одно направление.
Теперь вам нужно посмотреть, что у вас есть в железе. У вас должно быть три полумоста, каждый из которых фактически представляет собой два мосфета. Вам придется управлять каждым мосфетом с помощью собственного ШИМ. Каждая пара получит встречный ШИМ с небольшой паузой-мертвой задержкой. Рабочий цикл будет пропорционален напряжению. Поэтому, если вы хотите управлять фазами A и B, используйте 0 В на C, + V на A и -V на B. Затем переключитесь в соответствии с датчиками эффекта Холла.
Что ж, если все это поможет, я могу продолжить и перейти к петлям положения и скорости, управлению током и т. д.
Дэнниф
в действительности