ghoetic
Junior Member level 2
Hey guys and gals!
I want to share my work in progress on space vector modulation ACIM control.
I have become what of a motor control enthusiast , so this is my second motor control project.
I do it because its very rewarding and fun
My first one was based on AN900, written in C on mplab x.
Created a third harmonic injected sine table, used a timer to ramp speed/softstart & stop, etc.
I dont have that code available atm, its on an old harddrive.
But i have two youtube videos for you.
Motor running: https://www.youtube.com/watch?v=A1Ip2bk8uy8
Motor waveform: https://www.youtube.com/watch?v=0gZrBgXn4NI
Now i am at it again but doing SVM this time, basing around AN955 and some other vendors appliaction notes.
The waveform generation works, but i have alot more to do around the project.
E.x. using a timer to check inputs/ramp control and eventually implement a PID routine.
voltage sensing and current sensing etc.
I just made the main state machine before posting.
and i am reiterating all the time, so comments and comments styles come and go.
Here is my excel document, where you can modify the values. the last entry on the Z column you can copy and past right into your own array
https://docs.google.com/spreadsheets/d/1BchXL97TzcaVnA7LcNc9GFQMiEDdUfAeKqaW1kYlN-4/pub?output=xlsx
As i am still learning C i appriciate any helps, tips & tricks you more experienced people have to give!
I want to share my work in progress on space vector modulation ACIM control.
I have become what of a motor control enthusiast , so this is my second motor control project.
I do it because its very rewarding and fun
My first one was based on AN900, written in C on mplab x.
Created a third harmonic injected sine table, used a timer to ramp speed/softstart & stop, etc.
I dont have that code available atm, its on an old harddrive.
But i have two youtube videos for you.
Motor running: https://www.youtube.com/watch?v=A1Ip2bk8uy8
Motor waveform: https://www.youtube.com/watch?v=0gZrBgXn4NI
Now i am at it again but doing SVM this time, basing around AN955 and some other vendors appliaction notes.
The waveform generation works, but i have alot more to do around the project.
E.x. using a timer to check inputs/ramp control and eventually implement a PID routine.
voltage sensing and current sensing etc.
I just made the main state machine before posting.
and i am reiterating all the time, so comments and comments styles come and go.
Here is my excel document, where you can modify the values. the last entry on the Z column you can copy and past right into your own array
https://docs.google.com/spreadsheets/d/1BchXL97TzcaVnA7LcNc9GFQMiEDdUfAeKqaW1kYlN-4/pub?output=xlsx
As i am still learning C i appriciate any helps, tips & tricks you more experienced people have to give!
Code C - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 * * File: main.c * Author: Motherbrain * * Created on den 16 februari 2017, 23:12 */ // PIC18F4431 Configuration Bit Settings // 'C' source line config statements // CONFIG1H #pragma config OSC = IRCIO // Oscillator Selection bits (Internal oscillator block, port function on RA6 and port function on RA7) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled) #pragma config IESO = OFF // Internal External Oscillator Switchover bit (Internal External Switchover mode disabled) // CONFIG2L #pragma config PWRTEN = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = ON // Brown-out Reset Enable bits (Brown-out Reset enabled) // BORV = No Setting // CONFIG2H #pragma config WDTEN = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit)) #pragma config WDPS = 32768 // Watchdog Timer Postscale Select bits (1:32768) #pragma config WINEN = OFF // Watchdog Timer Window Enable bit (WDT window disabled) // CONFIG3L #pragma config PWMPIN = OFF // PWM output pins Reset state control (PWM outputs disabled upon Reset (default)) #pragma config LPOL = HIGH // Low-Side Transistors Polarity (PWM0, 2, 4 and 6 are active-high) #pragma config HPOL = HIGH // High-Side Transistors Polarity (PWM1, 3, 5 and 7 are active-high) #pragma config T1OSCMX = OFF // Timer1 Oscillator MUX (Standard (legacy) Timer1 oscillator operation) // CONFIG3H #pragma config FLTAMX = RC1 // FLTA MUX bit (FLTA input is multiplexed with RC1) #pragma config SSPMX = RC7 // SSP I/O MUX bit (SCK/SCL clocks and SDA/SDI data are multiplexed with RC5 and RC4, respectively. SDO output is multiplexed with RC7.) #pragma config PWM4MX = RB5 // PWM4 MUX bit (PWM4 output is multiplexed with RB5) #pragma config EXCLKMX = RC3 // TMR0/T5CKI External clock MUX bit (TMR0/T5CKI external clock input is multiplexed with RC3) #pragma config MCLRE = ON // MCLR Pin Enable bit (Enabled) // CONFIG4L #pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset) #pragma config LVP = OFF // Low-Voltage ICSP Enable bit (Low-voltage ICSP enabled) // CONFIG5L #pragma config CP0 = OFF // Code Protection bit (Block 0 (000200-000FFFh) not code-protected) #pragma config CP1 = OFF // Code Protection bit (Block 1 (001000-001FFF) not code-protected) #pragma config CP2 = OFF // Code Protection bit (Block 2 (002000-002FFFh) not code-protected) #pragma config CP3 = OFF // Code Protection bit (Block 3 (003000-003FFFh) not code-protected) // CONFIG5H #pragma config CPB = OFF // Boot Block Code Protection bit (Boot Block (000000-0001FFh) not code-protected) #pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected) // CONFIG6L #pragma config WRT0 = OFF // Write Protection bit (Block 0 (000200-000FFFh) not write-protected) #pragma config WRT1 = OFF // Write Protection bit (Block 1 (001000-001FFF) not write-protected) #pragma config WRT2 = OFF // Write Protection bit (Block 2 (002000-002FFFh) not write-protected) #pragma config WRT3 = OFF // Write Protection bit (Block 3 (003000-003FFFh) not write-protected) // CONFIG6H #pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected) #pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block (000000-0001FFh) not write-protected) #pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected) // CONFIG7L #pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000200-000FFFh) not protected from table reads executed in other blocks) #pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (001000-001FFF) not protected from table reads executed in other blocks) #pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (002000-002FFFh) not protected from table reads executed in other blocks) #pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (003000-003FFFh) not protected from table reads executed in other blocks) // CONFIG7H #pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot Block (000000-0001FFh) not protected from table reads executed in other blocks) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include <stdlib.h> #include <stdbool.h> #include <stdint.h> #include <xc.h> #define _XTAL_FREQ 8000000 #define FOSC 8000000 #define FCYC FOSC/4 #define PWM_FREQUENCY 8000 #define DEGREE_CONSTANT 51 // (360 * 256 * Multiplication factor(256) / 60 * Frequency of pwm) > 360*65536 / 60*8000 = ~50,33 #define MAX_FREQUENCY 1023 // Frequency scale factor = 4, 1023/4 = 255 Hz #define MIN_FREQUENCY 2 // Frequency scale factor = 4, 2/4 = 0.5Hz #define MAX_MODULATION 200 // Modulation index max = mains frequency V/Hz => 50*4 #define MIN_MODULATION 60 // Modulation index min = V/Hz equal 15Hz => 15*4 #define PWM_PERIOD 512 // PWM max period, 4xPTPER #define PWM_FREQ_3906 256 // Center aligned pwm frequency PTPER/2 #define PWM_FREQ_7812 128 #define PWM_FREQ_15625 64 #define PWM_FREQ_20000 50 const static uint16_t angle[256]= { 283,282,281,281,280,279,278,278,277,276,276,275,274,273,273,272,271,270,270,269, 268,267,267,266,265,264,263,263,262,261,260,259,259,258,257,256,255,254,254,253, 252,251,250,249,248,248,247,246,245,244,243,242,241,240,240,239,238,237,236,235, 234,233,232,231,230,229,228,227,226,225,224,223,222,221,221,220,219,218,217,216, 215,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195, 194,193,191,190,189,188,187,186,185,184,183,182,180,179,178,177,176,175,174,173, 171,170,169,168,167,166,164,163,162,161,160,159,157,156,155,154,153,152,150,149, 148,147,146,144,143,142,141,140,138,137,136,135,133,132,131,130,129,127,126,125, 124,122,121,120,119,117,116,115,114,112,111,110,109,107,106,105,104,102,101,100, 98,97,96,95,93,92,91,89,88,87,86,84,83,82,80,79,78,77,75,74,73,71,70,69,67,66, 65,63,62,61,59,58,57,56,54,53,52,50,49,48,46,45,44,42,41,40,38,37,36,34,33,32, 30,29,28,26,25,24,22,21,20,18,17,16,14,13,12,10,9,8,6,5,4,2,1,0 }; volatile uint16_t scaled_angle[256]; volatile bool adc_flag = false, pwm_flag = false, pre_scale_done = false; volatile uint8_t sector = 0, adc_count = 0; volatile uint16_t vector_update_step_size = 0, vector_angle = 0, angle_index = 0; volatile uint16_t T1, T2, T0, T7; volatile uint16_t pwm0, pwm1, pwm2; volatile uint16_t frequency = 0, temp_frequency = 0, last_used_frequency = 0, modulation_index = 0, adc_value = 0; bool pre_scale(uint16_t); void process_adc(uint16_t); void setup(void){ /* Initialize system **************************************************************************** **************************************************************************** * OSCCON: Internal oscillator @ 8MHz **************************************************************************** **************************************************************************** */ OSCCON = 0b01110000; /* Initialize I/O **************************************************************************** **************************************************************************** * TRISB: Set B ports to output * PORTB: Set B ports output low * TRISA: Set RA0/AN0 to input * TRISD: Set RD7 to input * ANSEL0: Set RA0/AN0 to analog input channel **************************************************************************** **************************************************************************** */ TRISB = 0; PORTB = 0; TRISAbits.RA0 = 1; TRISDbits.RD7 = 1; ANSEL0bits.ANS0 = 1; /* Initialize ADC module **************************************************************************** **************************************************************************** * ADCON0: Continuos loop, Single channel, Group A, ADC off * ADCON1: Vref = VDD-VSS, FIFO enabled * ADCON2: Right justified, 12 TAD, FOSC/64 * ADCON3: Interrupt on fourth word, AD triggers disabled * ADCHS: Group A = AN0 **************************************************************************** **************************************************************************** */ ADCON0 = 0b00100000; ADCON1 = 0b00010000; ADCON2 = 0b10110110; ADCON3 = 0b10000000; ADCHS = 0b11111100; /* Initialize PWM module **************************************************************************** **************************************************************************** * PWM 0-5 controlled by duty cycle and time base * PWM output active when override bit is cleared * Center aligned PWM Frequency = PWM frequency/Instruction frequency * 2 * Set initial duty cycle registers to zero **************************************************************************** **************************************************************************** */ PTCON0 = 0b00000010; PWMCON0 = 0b01000000; PWMCON1 = 0b00001001; OVDCOND = 0b11111111; OVDCONS = 0b11111111; PTPERL = LOW_BYTE(PWM_FREQ_7812); PTPERH = HIGH_BYTE(PWM_FREQ_7812); PDC0L = 0; PDC0H = 0; PDC1L = 0; PDC1H = 0; PDC2L = 0; PDC2H = 0; /* Initialize Interrupts **************************************************************************** **************************************************************************** * PWM interrupt high priority * Clear PWM interrupt flag * ADC interrupt low priority * Clear ADC interrupt flag * Enable priority levels on interrupts **************************************************************************** **************************************************************************** */ IPR3bits.PTIP = 1; // PWM interrupt high priority PIR3bits.PTIF = 0; // Clear PWM interrupt flag IPR1bits.ADIP = 0; // ADC interrupt low priority PIR1bits.ADIF = 0; // Clear ADC interrupt flag RCONbits.IPEN = 1; // Enable priority levels on interrupts } void high_priority interrupt isr_high(void) { if(PTIF && PTIE) // Check if PWM flag is set, and interrupt enabled { PTIF = false; // Clear interrupt flag vector_angle += vector_update_step_size; // Accumulate step size for angle index if(CARRY) // Check for carry bit set by overflow of vector_angle { sector++; // Increment current SVM sector CARRY = false; // Clear Carry flag } if(sector > 5) // Check if SVM sector is past last valid sector { sector = 0; // Reset SVM sector to 0 } angle_index = vector_angle >> 8; // Assign index variable by shifting right 8 bits (65535 divide by 256) > 0-255 T1 = scaled_angle[angle_index]; // Assign the pre calculated angle T2 = scaled_angle[255-angle_index]; // For the reverse angle T0 = ( PWM_PERIOD - (T1 + T2) ) >>1; // Calculate T0 time and right shift 1 bit (divide by 2) for half T0 time. T7 = (PWM_PERIOD - T0); switch(sector) { // Determine switching pattern based on sector, Standard SVPWM case 0: pwm0 = T0; pwm1 = T0+T1; pwm2 = T7; break; case 1: pwm0 = T0+T2; pwm1 = T0; pwm2 = T7; break; case 2: pwm0 = T7; pwm1 = T0; pwm2 = T0+T1; break; case 3: pwm0 = T7; pwm1 = T0+T2; pwm2 = T0; break; case 4: pwm0 = T0+T1; pwm1 = T7; pwm2 = T0; break; case 5: pwm0 = T0; pwm1 = T7; pwm2 = T0+T2; break; } UDIS = 1; // Disable PWM updates until duty cycles values are loaded into register PDC0L = LOW_BYTE(pwm0) ; PDC0H = HIGH_BYTE(pwm0); PDC1L = LOW_BYTE(pwm1) ; PDC1H = HIGH_BYTE(pwm1); PDC2L = LOW_BYTE(pwm2) ; PDC2H = HIGH_BYTE(pwm2); UDIS = 0; // Enable PWM updates when done } } void low_priority interrupt isr_low(void) { if(ADIF && ADIE) // Check if ADC flag is set, and interrupt enabled { ADIF = false; // Clear interrupt flag adc_value = (ADRESH << 8 | ADRESL); adc_flag = true; } } bool pre_scale(uint16_t amplitude) { uint16_t scaled_value = 0; for (uint16_t index = 0; index < 255; index++) // Step through 256 table values { scaled_value = amplitude * angle[index]; // Scale table value based on modulation index scaled_angle[index] = scaled_value >> 7; // Right shift 7 bits to divide by 128 for max value of ((200*283) / 128) = 442(0,863 * 512) } return true; } void process_adc(uint16_t adc_value) { temp_frequency += adc_value; if(++adc_count >= 4) { adc_count = 0; temp_frequency << 2; frequency = temp_frequency; temp_frequency = 0; if(frequency < MIN_FREQUENCY) // Check if input frequency is below min { frequency = MIN_FREQUENCY; // Limit Frequency to min } if(frequency > MAX_FREQUENCY) // Check if input frequency is above max { frequency = MAX_FREQUENCY; // Limit Frequency to max } if(frequency < last_used_frequency || frequency > last_used_frequency) { last_used_frequency = frequency; vector_update_step_size = ((DEGREE_CONSTANT * frequency) >> 2); // Right shift 2 bits "divide by 4" for scale factor of Frequency/4 modulation_index = frequency; // Assign frequency to modulation index for scaling duty cycle according to V/Hz if(modulation_index > MAX_MODULATION) // Check if modulation index is above max { modulation_index = MAX_MODULATION; // Limit Modulation index max for V/Hz - SVM modulation } if(modulation_index < MIN_MODULATION) // Check if modulation index is below min { modulation_index = MIN_MODULATION; // Limit modulation index for V/Hz min } pre_scale(modulation_index); // Pre scale table values for faster ISR service } } } void main(void) { setup(); enum { idle, motor_start, motor_stop, motor_on, motor_off, }main_state; main_state = idle; while(true) { switch(main_state) { case idle: main_state = motor_start; break; case motor_start: ADCON0bits.ADON = 1; // Enable ADC module __delay_us(10); // Wait recommended startup time ADCON0bits.GO_nDONE = 1; // Start continuous ADC conversion PIE1bits.ADIE = 1; // Enable ADC interrupt PIE3bits.PTIE = 1; // Enable PWM interrupt GIEL = 1; // Enable low priority interrupts GIEH = 1; // Enable high priority interrupts pre_scale(MIN_MODULATION); // Pre_scale for min modulation PTEN = 1; // Enable PWM main_state = motor_on; // Switch state to motor on break; case motor_on: if(adc_flag) { process_adc(adc_value); } break; } } }
Last edited by a moderator: