Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Sharing my work in progress, space vector modulation PIC18F4431

Status
Not open for further replies.

ghoetic

Junior Member level 2
Joined
Nov 9, 2014
Messages
21
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,283
Activity points
1,655
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!




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:

That's very nice.
You could only move that enum outside main function so it's more readable, enums are usually put outside functiions. And maybe use typedef.
Can you also share the schematic?
 

That's very nice.
You could only move that enum outside main function so it's more readable, enums are usually put outside functiions. And maybe use typedef.
Can you also share the schematic?

Sure thing!
Added timer0 now, added a three second speed ramp, exponential moving average filter and made changes to comments.
I have no current schematic for my first build, believe it or not but i cut the tracks and soldered everything on the vero board straight from my mind.
Not a single fault :grin:, but i can do a makeshift schematic later of it.

heres the new code
Code:
/*
 * 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 INSTRUCTION_CYCLE _XTAL_FREQ/4
#define _5_milliseconds 56255 // Timer0 16bit Reload value = 65535 - T/Tcyc = (0,005/0,0000005) => 65535-1000 = 55535
#define _1_second 200 // 1 Second divided by 5 miliseconds => 1/0,005 = 200



#define DEGREE_CONSTANT 51 // ((360 * 256 * Multiplication factor(256)) / (60 * Frequency of pwm)) > ((360*65536) / (60*7812)) = ~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
#define angle_index_reverse (255-angle_index)

const static uint16_t angle[256]= //60-0 Degrees, resolution: 0,234375. Scaled for max multiply by 200 and divide by power of 2. ~0,86366 * 65535
{
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;
volatile uint8_t sector = 0, adc_count = 0, timer0_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, speed_ramp, speed_count = 0;

void pre_scale(uint16_t);
void process_adc(uint16_t);

    typedef enum //define and declare finite state machine          
    {
        idle,
        motor_start,
        motor_stop,
        motor_on,
        motor_off,         
    }state;
    state main_state = idle;

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;
    TRISA0 = 1;
    TRISD7 = 0;
    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
     * ADC interrupt low priority
     * Timer0 interrupt low priority
     * Clear interrupt flags.
     * Enable priority levels on interrupts
     *
     */
      
    PTIP = 1; 
    PTIF = 0; 
    ADIP = 0; 
    ADIF = 0; 
    T0IP = 0; 
    T0IF = 0; 
    IPEN = 1;
    
    /*  
     * Initialize Timer0
     * Timer0 16bit mode
     * Internal clock Fosc/4 increment
     * No prescaler
     * 
     */
    T016BIT = 0; 
    T0CS = 0; 
    PSA = 1;
}



void high_priority interrupt isr_high(void)
{

    if(PTIF && PTIE) 
    {
        PTIF = false;
        vector_angle += vector_update_step_size; 
        if(CARRY) // Check for carry bit set by overflow of vector_angle
        {
            sector++; 
            CARRY = false;
        }
        if(sector > 5)
        {
            sector = 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]; // angle 60-0
        T2 = scaled_angle[angle_index_reverse]; // angle in reverse 0-60
        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); // Calculate the reverse of half T0 time;
        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)
    {
        ADIF = false;
        adc_value = (ADRESH << 8 | ADRESL);  // Assign the high and low result of ADC registers - 10bit Value     
        speed_ramp = (int16_t)last_used_frequency + (((int16_t)adc_value - (int16_t)last_used_frequency) >> 1); // Exponential moving average filter by power of 2
        adc_flag = true;
    }
    if(T0IF && T0IE)
    {
        T0IF = false;
        ++timer0_count;
        ++speed_count;
        if(timer0_count == _1_second) // Check if N number of counts has elapsed for desired time
        {
            timer0_count = 0;
            LATDbits.LATD7 ^= 1; // Toggle LED
        }
        if(speed_count >= 10 && speed_ramp < last_used_frequency)
        {
            speed_ramp -= 17;
            if(speed_ramp - 17 < last_used_frequency)
            {
                speed_ramp -= last_used_frequency - speed_ramp;
            }
            speed_count = 0;
        }
        if(speed_count >= 10 && speed_ramp > last_used_frequency)
        {
            speed_ramp += 17;
            if(speed_ramp + 17 > last_used_frequency)
            {
                speed_ramp += last_used_frequency - speed_ramp;
            }
            speed_count = 0;
        }
        WRITETIMER0(_5_milliseconds);
    }
}

void pre_scale(uint16_t amplitude)
{
    for (uint16_t index = 0; index <= 255; index++)
    {
        scaled_angle[index] = (amplitude * angle[index]) >> 7; // Scale with modulation index and right shift to divide by power of 2 e.x. ((200*283) / 128) = 442(0,863 * 512)
    }
}

    void process_adc(uint16_t adc_value)
{
    frequency = speed_ramp;

    if(frequency < MIN_FREQUENCY) 
    {
        frequency = MIN_FREQUENCY;
    }
    if(frequency > MAX_FREQUENCY)
    {
        frequency = MAX_FREQUENCY;
    }
        
    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) 
     {
         modulation_index = MAX_MODULATION;
     }
     if(modulation_index < MIN_MODULATION)
     {
         modulation_index = MIN_MODULATION;
     }
     pre_scale(modulation_index); // Pre scale table values for faster ISR service
    }      
}

void main(void)
{
    setup();
    while(true)
    {
        switch(main_state)
        {
            case idle:
                T0IE = 1; // Enable Timer0 interrupt
                WRITETIMER0(_5_milliseconds);
                TMR0ON = 1; // Start timer 0
                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);
                    adc_flag = false;
                }
                break;
                
        }        
    }
}
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top