1 | //---------------------------------------------------------------------
|
2 | //
|
3 | // Software License Agreement
|
4 | //
|
5 | // The software supplied herewith by Microchip Technology Incorporated
|
6 | // (the ?Company?) for its PICmicro® Microcontroller is intended and
|
7 | // supplied to you, the Company?s customer, for use solely and
|
8 | // exclusively on Microchip PICmicro Microcontroller products. The
|
9 | // software is owned by the Company and/or its supplier, and is
|
10 | // protected under applicable copyright laws. All rights are reserved.
|
11 | // Any use in violation of the foregoing restrictions may subject the
|
12 | // user to criminal sanctions under applicable laws, as well as to
|
13 | // civil liability for the breach of the terms and conditions of this
|
14 | // license.
|
15 | //
|
16 | // THIS SOFTWARE IS PROVIDED IN AN ?AS IS? CONDITION. NO WARRANTIES,
|
17 | // WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
|
18 | // TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
19 | // PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
|
20 | // IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
|
21 | // CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
|
22 | //
|
23 | //---------------------------------------------------------------------
|
24 | // File: sinusoidal_6015.c
|
25 | //
|
26 | // Written By: Jorge Zambada, Microchip Technology
|
27 | //
|
28 | // The following files should be included in the MPLAB project:
|
29 | //
|
30 | // sinusoidal_6015.c -- Main source code file
|
31 | // SVM.c -- Space Vector Modulation file
|
32 | // SVM.h
|
33 | // p30f6015.gld -- Linker script file
|
34 | //---------------------------------------------------------------------
|
35 |
|
36 | #include "p30f6015.h"
|
37 | #include "svm.h"
|
38 |
|
39 | //--------------------------Device Configuration------------------------
|
40 | _FOSC(FRC_PLL8 & CSW_FSCM_OFF);
|
41 | _FWDT(WDT_OFF);
|
42 | _FBORPOR(PBOR_OFF & BORV27 & PWRT_16 & MCLR_DIS & PWMxH_ACT_HI & PWMxL_ACT_HI & RST_IOPIN);
|
43 | _FGS(GSS_OFF & GWRP_OFF);
|
44 | _FBS(NO_BOOT_RAM & NO_BOOT_EEPROM & NO_BOOT_CODE & WR_PROTECT_BOOT_OFF);
|
45 | _FSS(NO_SEC_RAM & NO_SEC_EEPROM & NO_SEC_CODE & WR_PROT_SEC_OFF);
|
46 | _ICD(ICS_PGD);
|
47 |
|
48 | //----------------------------------------------------------------------
|
49 |
|
50 | // Hurst Motor Terminals | MC LV PICDEM Board Connection
|
51 | // -----------------------|---------------------------------
|
52 | // Ground Phase ---------|-- G
|
53 | // Phase Red ---------|-- M1
|
54 | // Phase Black ---------|-- M2
|
55 | // Phase White ---------|-- M3
|
56 | // Hall White ---------|-- HA
|
57 | // Hall Brown ---------|-- HB
|
58 | // Hall Green ---------|-- HC
|
59 |
|
60 | typedef signed int SFRAC16;
|
61 |
|
62 | //#define CLOSED_LOOP // if defined the speed controller will be enabled //dis 29.10.2023
|
63 | #define PHASE_ADVANCE // for extended speed ranges this should be defined
|
64 |
|
65 | #define FCY 20000000 // xtal = 5Mhz; PLLx16 -> 20 MIPS
|
66 | #define FPWM 20000 // 20 kHz, so that no audible noise is present.
|
67 | #define _10MILLISEC 10 // Used as a timeout with no hall effect sensors
|
68 | // transitions and Forcing steps according to the
|
69 | // actual position of the motor
|
70 | #define _100MILLISEC 1000 // after this time has elapsed, the motor is
|
71 | // consider stalled and it's stopped
|
72 |
|
73 | // These Phase values represent the base Phase value of the sinewave for each
|
74 | // one of the sectors (each sector is a translation of the hall effect sensors
|
75 | // reading
|
76 | #define PHASE_ZERO 57344
|
77 | #define PHASE_ONE ((PHASE_ZERO + 65536/6) % 65536)
|
78 | #define PHASE_TWO ((PHASE_ONE + 65536/6) % 65536)
|
79 | #define PHASE_THREE ((PHASE_TWO + 65536/6) % 65536)
|
80 | #define PHASE_FOUR ((PHASE_THREE + 65536/6) % 65536)
|
81 | #define PHASE_FIVE ((PHASE_FOUR + 65536/6) % 65536)
|
82 |
|
83 | #define MAX_PH_ADV_DEG 60 // This value represents the maximum allowed phase
|
84 | // advance in electrical degrees. Set a value from
|
85 | // 0 to 60. This value will be used to calculate
|
86 | // phase advance only if PHASE_ADVANCE is defined
|
87 |
|
88 | // This is the calculation from the required phase advance to the actual
|
89 | // value to be multiplied by the speed of the motor. So, if PHASE_ADVANCE is
|
90 | // enabled, a certain amount of shit angle will be added to the generated
|
91 | // sine wave, up to a maximum of the specified value on MAX_PH_ADV_DEG. This
|
92 | // maximum phase shift will be present when the MeasuredSpeed variable is a
|
93 | // fractional 1.0 (for CW) or -1.0 (for CCW).
|
94 | #define MAX_PH_ADV (int)(((float)MAX_PH_ADV_DEG / 360.0) * 65536.0)
|
95 |
|
96 | #define HALLA 1 // Connected to RB3
|
97 | #define HALLB 2 // Connected to RB4
|
98 | #define HALLC 4 // Connected to RB5
|
99 | #define CW 0 // Counter Clock Wise direction
|
100 | #define CCW 1 // Clock Wise direction //03.04.2024
|
101 | #define SWITCH_S2 (!PORTCbits.RC14) // Push button S2
|
102 |
|
103 | // Period Calculation
|
104 | // Period = (TMRClock * 60) / (RPM * Motor_Poles)
|
105 | // For example>
|
106 | // Motor_Poles = 10
|
107 | // RPM = 6000 (Max Speed)
|
108 | // Period = ((20,000,000 / 64) * 60) / (6000 * 10) = 312.5
|
109 | // RPM = 60 (Min Speed)
|
110 | // Period = ((20,000,000 / 64) * 60) / (60 * 10) = 31250
|
111 |
|
112 | #define MINPERIOD 156 // For 6000 max rpm and 10 poles motor // vorher 312 29.10.2023
|
113 | #define MAXPERIOD 51250 // For 60 min rpm and 10 poles motor //31250
|
114 |
|
115 | // Use this MACRO when using floats to initialize signed 16-bit fractional
|
116 | // variables
|
117 |
|
118 | #define SFloat_To_SFrac16(Float_Value) \
|
119 | ((Float_Value < 0.0) ? (SFRAC16)(32768 * (Float_Value) - 0.5) \
|
120 | : (SFRAC16)(32767 * (Float_Value) + 0.5))
|
121 |
|
122 | void InitADC10(void); // Initialization of ADC used for Speed Command
|
123 | void InitMCPWM(void); // Initialization for PWM at 20kHz, Center aligned,
|
124 | // Complementary mode with 500 ns of deadtime
|
125 | void InitTMR1(void); // Initialization for TIMER1 used for speed control
|
126 | // and motor stalled protection
|
127 | void InitTMR3(void); // Initialization for TIMER3 used as a timebase
|
128 | // for the two input capture channels
|
129 | void InitUserInt(void); // This function initializes all ports
|
130 | // (inputs and outputs) for the application
|
131 | void InitICandCN(void); // Initializes input captures and change notification,
|
132 | // used for the hall sensor inputs
|
133 | void RunMotor(void); // This function initializes all variables
|
134 | // and interrupts used for starting and running
|
135 | // the motor
|
136 | void StopMotor(void); // This function clears all flags, and stops anything
|
137 | // related to motor control, and also disables PWMs
|
138 | void SpeedControl(void); // This function contains all ASM and C operations
|
139 | // for doing the PID Control loop for the speed
|
140 | void ForceCommutation(void); // When motor is to slow to generate interrupts
|
141 | // on halls, this function forces a commutation
|
142 | void ChargeBootstraps(void); // At the begining of the motor operation, the
|
143 | // bootstrap caps are charged with this function
|
144 |
|
145 | // Constants used for properly energizing the motor depending on the
|
146 | // rotor's position
|
147 | const int PhaseValues[6] =
|
148 | {PHASE_ZERO, PHASE_ONE, PHASE_TWO, PHASE_THREE, PHASE_FOUR, PHASE_FIVE};
|
149 |
|
150 | // In the sinewave generation algorithm we need an offset to be added to the
|
151 | // pointer when energizing the motor in CCW. This is done to compensate an
|
152 | // asymetry of the sinewave
|
153 | int PhaseOffset = 4100;
|
154 |
|
155 | // Flags used for the application
|
156 | struct
|
157 | {
|
158 | unsigned MotorRunning :1; // This bit is 1 if motor running
|
159 | unsigned unused :15;
|
160 | }Flags;
|
161 |
|
162 |
|
163 | unsigned int Phase; // This variable is incremented by the PWM interrupt
|
164 | // in order to generate a proper sinewave. Its value
|
165 | // is incremented by a value of PhaseInc, which
|
166 | // represents the frequency of the generated sinewave
|
167 | signed int PhaseInc; // Delta increments of the Phase variable, calculated
|
168 | // in the TIMER1 interrupt (each 1 ms) and used in
|
169 | // the PWM interrupt (each 50 us)
|
170 | signed int PhaseAdvance; // Used for extending motor speed range. This value
|
171 | // is added directly to the parameters passed to the
|
172 | // SVM function (the sine wave generation subroutine)
|
173 | unsigned int HallValue; // This variable holds the hall sensor input readings
|
174 | unsigned int Sector; // This variables holds present sector value, which is
|
175 | // the rotor position
|
176 | unsigned int LastSector; // This variable holds the last sector value. This
|
177 | // is critical to filter slow slew rate on the Hall
|
178 | // effect sensors hardware
|
179 | unsigned int MotorStalledCounter = 0; // This variable gets incremented each
|
180 | // 1 ms, and is cleared everytime a new
|
181 | // sector is detected. Used for
|
182 | // ForceCommutation and MotorStalled
|
183 | // protection functions
|
184 |
|
185 | // This array translates the hall state value read from the digital I/O to the
|
186 | // proper sector. Hall values of 0 or 7 represent illegal values and therefore
|
187 | // return -1.
|
188 | char SectorTable[] = {-1,4,2,3,0,5,1,-1};
|
189 |
|
190 | unsigned char Current_Direction; // Current mechanical motor direction of
|
191 | // rotation Calculated in halls interrupts
|
192 | unsigned char Required_Direction; // Required mechanical motor direction of
|
193 | // rotation, will have the same sign as the
|
194 | // ControlOutput variable from the Speed
|
195 | // Controller
|
196 |
|
197 | // Variables containing the Period of half an electrical cycle, which is an
|
198 | // interrupt each edge of one of the hall sensor input
|
199 | unsigned int PastCapture, ActualCapture, Period;
|
200 | // Used as a temporal variable to perform a fractional divide operation in
|
201 | // assembly
|
202 | SFRAC16 _MINPERIOD = MINPERIOD - 1;
|
203 |
|
204 | SFRAC16 MeasuredSpeed, RefSpeed; // Actual and Desired speeds for the PID
|
205 | // controller, that will generate the error
|
206 | SFRAC16 ControlOutput = 0; // Controller output, used as a voltage output,
|
207 | // use its sign for the required direction
|
208 |
|
209 | // Absolute PID gains used by the controller. Position form implementation of
|
210 | // a digital PID. See SpeedControl subroutine for details
|
211 | SFRAC16 Kp = SFloat_To_SFrac16(0.1); // P Gain //0,1
|
212 | SFRAC16 Ki = SFloat_To_SFrac16(0.01); // I Gain //0,01
|
213 | SFRAC16 Kd = SFloat_To_SFrac16(0.000); // D Gain //0,000
|
214 |
|
215 | // Constants used by the PID controller, since a MAC operation is used, the
|
216 | // PID structure is changed (See SpeedControl() Comments)
|
217 | SFRAC16 ControlDifference[3] \
|
218 | __attribute__((__space__(xmemory), __aligned__(4)));
|
219 | SFRAC16 PIDCoefficients[3] \
|
220 | __attribute__((__space__(ymemory), __aligned__(4)));
|
221 |
|
222 | // Used as a temporal variable to perform a fractional divide operation in
|
223 | // assembly
|
224 | SFRAC16 _MAX_PH_ADV = MAX_PH_ADV;
|
225 |
|
226 | /*********************************************************************
|
227 | Function: void __attribute__((__interrupt__)) _T1Interrupt (void)
|
228 |
|
229 | PreCondition: The motor is running and is generating hall effect sensors
|
230 | interrupts. Also, the actual direction of the motor used
|
231 | in this interrupt is assumed to be previously calculated.
|
232 |
|
233 | Input: None.
|
234 |
|
235 | Output: None.
|
236 |
|
237 | Side Effects: None.
|
238 |
|
239 | Overview: In this ISR the Period, Phase Increment and MeasuredSpeed
|
240 | are calculated based on the input capture of one of the
|
241 | halls. The speed controller is also called in this ISR
|
242 | to generate a new output voltage (ControlOutput). The
|
243 | Phase Advance is calculated based on the maximum allowed
|
244 | phase advance (MAX_PH_ADV) and the actual speed of the
|
245 | motor. The last thing done in this ISR is the forced
|
246 | commutation, which happens each time the motor doesn't
|
247 | generate a new hall interrupt after a programmed period
|
248 | of time. If the timeout for generating hall ISR is too much
|
249 | (i.e. 100 ms) the motor is then stopped.
|
250 |
|
251 | Note: The MeasuredSpeed Calculation is made in assembly to take
|
252 | advantage of the signed fractional division.
|
253 | ********************************************************************/
|
254 |
|
255 | void __attribute__((interrupt, no_auto_psv)) _T1Interrupt (void)
|
256 | {
|
257 | IFS0bits.T1IF = 0;
|
258 | Period = ActualCapture - PastCapture; // This is an UNsigned substraction
|
259 | // to get the Period between one
|
260 | // hall effect sensor transition
|
261 |
|
262 | // These operations limit the Period value to a range from 60 to 6000 rpm
|
263 | if (Period < (unsigned int)MINPERIOD) // MINPERIOD or 6000 rpm
|
264 | Period = MINPERIOD;
|
265 | else if (Period > (unsigned int)MAXPERIOD) // MAXPERIOD or 60 rpm
|
266 | Period = MAXPERIOD;
|
267 |
|
268 | // PhaseInc is a value added to the Phase variable to generate the sine
|
269 | // voltages. 1 electrical degree corresponds to a PhaseInc value of 184,
|
270 | // since the pointer to the sine table is a 16bit value, where 360 Elec
|
271 | // Degrees represents 65535 in the pointer.
|
272 | // __builtin_divud(Long Value, Int Value) is a function of the compiler
|
273 | // to do Long over Integer divisions.
|
274 | PhaseInc = __builtin_divud(512000UL, Period); // Phase increment is used
|
275 | // by the PWM isr (SVM)
|
276 |
|
277 | // This subroutine in assembly calculates the MeasuredSpeed using
|
278 | // fractional division. These operations in assembly perform the following
|
279 | // formula:
|
280 | // MINPERIOD (in fractional)
|
281 | // MeasuredSpeed = ---------------------------
|
282 | // Period (in fractional)
|
283 | //
|
284 |
|
285 | __asm__ volatile("repeat #17\n\t"
|
286 | "divf %1,%2\n\t"
|
287 | "mov w0,%0" : /* output */ "=g"(MeasuredSpeed)
|
288 | : /* input */ "r"(_MINPERIOD),
|
289 | "e"(Period)
|
290 | : /* clobber */ "w0");
|
291 |
|
292 | // MeasuredSpeed sign adjustment based on current motor direction of
|
293 | // rotation
|
294 | if (Current_Direction == CCW)
|
295 | MeasuredSpeed = -MeasuredSpeed;
|
296 |
|
297 | // The following values represent the MeasuredSpeed values from the
|
298 | // previous operations:
|
299 | //
|
300 | // CONDITION RPM SFRAC16 SINT HEX
|
301 | // Max Speed CW -> 6000 RPM -> 0.996805 -> 32663 -> 0x7F97
|
302 | // Min Speed CW -> 60 RPM -> 0.009984 -> 327 -> 0x0147
|
303 | // Min Speed CCW -> -60 RPM -> -0.009984 -> -327 -> 0xFEB9
|
304 | // Max Speed CCW -> -6000 RPM -> -0.996805 -> -32663 -> 0x8069
|
305 |
|
306 | SpeedControl(); // Speed PID controller is called here. It will use
|
307 | // MeasuredSpeed, RefSpeed, some buffers and will generate
|
308 | // the new ControlOutput, which represents a new amplitude
|
309 | // of the sinewave that will be generated by the SVM
|
310 | // subroutine.
|
311 |
|
312 | #ifdef PHASE_ADVANCE
|
313 | // Calculate Phase Advance Based on Actual Speed and MAX_PH_ADV define
|
314 | // The following assembly instruction perform the following formula
|
315 | // using fractional multiplication:
|
316 | //
|
317 | // PhaseAdvance = MAX_PH_ADV * MeasuredSpeed
|
318 | //
|
319 |
|
320 | register int a_reg asm("A");
|
321 | a_reg = __builtin_mpy(_MAX_PH_ADV, MeasuredSpeed, 0,0,0,0,0,0);
|
322 | PhaseAdvance = __builtin_sac(a_reg,0);
|
323 |
|
324 | #endif
|
325 |
|
326 | // MotorStalledCounter++; // We increment a timeout variable to see if the////////////////////////04.03.2024
|
327 | // motor is too slow (not generating hall effect
|
328 | // sensors interrupts frequently enough) or if
|
329 | // the motor is stalled. This variable is cleared
|
330 | // in halls ISRs
|
331 | if ((MotorStalledCounter % _10MILLISEC) == 0)
|
332 | {
|
333 | ForceCommutation(); // Force Commutation if no hall sensor changes
|
334 | // have occured in specified timeout.
|
335 | }
|
336 | else if (MotorStalledCounter >= _100MILLISEC)
|
337 | {
|
338 | StopMotor(); // Stop motor is no hall changes have occured in
|
339 | // specified timeout
|
340 | }
|
341 | return;
|
342 | }
|
343 |
|
344 | /*********************************************************************
|
345 | Function: void __attribute__((__interrupt__)) _CNInterrupt (void)
|
346 |
|
347 | PreCondition: The inputs of the hall effect sensors should have low pass
|
348 | filters. A simple RC network works.
|
349 |
|
350 | Input: None.
|
351 |
|
352 | Output: None.
|
353 |
|
354 | Side Effects: None.
|
355 |
|
356 | Overview: This interrupt represent Hall A ISR. Hall A -> RB3 -> CN5.
|
357 | This is generated by the input change notification CN5.
|
358 | The purpose of this ISR is to Calculate the actual
|
359 | mechanical direction of rotation of the motor, and to adjust
|
360 | the Phase variable depending on the sector the rotor is in.
|
361 |
|
362 | Note 1: The sector is validated in order to avoid any spurious
|
363 | interrupt due to a slow slew rate on the halls inputs due to
|
364 | hardware filtering.
|
365 |
|
366 | Note 2: For Phase adjustment in CCW, an offset is added to
|
367 | compensate non-symetries in the sine table used.
|
368 | ********************************************************************/
|
369 |
|
370 | void __attribute__((interrupt, no_auto_psv)) _CNInterrupt (void)
|
371 | {
|
372 | IFS0bits.CNIF = 0; // Clear interrupt flag
|
373 | HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
|
374 | Sector = SectorTable[HallValue]; // Get Sector from table
|
375 |
|
376 | // This MUST be done for getting around the HW slow rate
|
377 | if (Sector != LastSector)
|
378 | {
|
379 | // Since a new sector is detected, clear variable that would stop
|
380 | // the motor if stalled.
|
381 | MotorStalledCounter = 0;
|
382 |
|
383 | // Motor current direction is computed based on Sector
|
384 | if ((Sector == 5) || (Sector == 2))
|
385 | Current_Direction = CCW;
|
386 | else
|
387 | Current_Direction = CW;
|
388 |
|
389 | // Motor commutation is actually based on the required direction, not
|
390 | // the current dir. This allows driving the motor in four quadrants
|
391 | if (Required_Direction == CW)
|
392 | {
|
393 | Phase = PhaseValues[Sector];
|
394 | }
|
395 | else
|
396 | {
|
397 | // For CCW an offset must be added to compensate difference in
|
398 | // symmetry of the sine table used for CW and CCW
|
399 | Phase = PhaseValues[(Sector + 3) % 6] + PhaseOffset;
|
400 | }
|
401 | LastSector = Sector; // Update last sector
|
402 | }
|
403 |
|
404 | return;
|
405 | }
|
406 |
|
407 | /*********************************************************************
|
408 | Function: void __attribute__((__interrupt__)) _IC7Interrupt (void)
|
409 |
|
410 | PreCondition: The inputs of the hall effect sensors should have low pass
|
411 | filters. A simple RC network works.
|
412 |
|
413 | Input: None.
|
414 |
|
415 | Output: None.
|
416 |
|
417 | Side Effects: None.
|
418 |
|
419 | Overview: This interrupt represent Hall B ISR. Hall B -> RB4 -> IC7.
|
420 | This is generated by the input Capture Channel IC7.
|
421 | The purpose of this ISR is to Calculate the actual Period
|
422 | between hall effect sensor transitions, calculate the actual
|
423 | mechanical direction of rotation of the motor, and also to
|
424 | adjust the Phase variable depending on the sector the rotor
|
425 | is in.
|
426 |
|
427 | Note 1: The sector is validated in order to avoid any spurious
|
428 | interrupt due to a slow slew rate on the halls inputs due to
|
429 | hardware filtering.
|
430 |
|
431 | Note 2: For Phase adjustment in CCW, an offset is added to
|
432 | compensate non-symetries in the sine table used.
|
433 | ********************************************************************/
|
434 |
|
435 | void __attribute__((interrupt, no_auto_psv)) _IC7Interrupt (void)
|
436 | {
|
437 | IFS1bits.IC7IF = 0; // Cleat interrupt flag
|
438 | HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
|
439 | Sector = SectorTable[HallValue]; // Get Sector from table
|
440 |
|
441 | // This MUST be done for getting around the HW slow rate
|
442 | if (Sector != LastSector)
|
443 | {
|
444 | // Calculate Hall period corresponding to half an electrical cycle
|
445 | PastCapture = ActualCapture;
|
446 | ActualCapture = IC7BUF;
|
447 | IC7BUF;
|
448 | IC7BUF;
|
449 | IC7BUF;
|
450 |
|
451 | // Since a new sector is detected, clear variable that would stop
|
452 | // the motor if stalled.
|
453 | MotorStalledCounter = 0;
|
454 |
|
455 | // Motor current direction is computed based on Sector
|
456 | if ((Sector == 3) || (Sector == 0))
|
457 | Current_Direction = CCW;
|
458 | else
|
459 | Current_Direction = CW;
|
460 |
|
461 | // Motor commutation is actually based on the required direction, not
|
462 | // the current dir. This allows driving the motor in four quadrants
|
463 | if (Required_Direction == CW)
|
464 | {
|
465 | Phase = PhaseValues[Sector];
|
466 | }
|
467 | else
|
468 | {
|
469 | // For CCW an offset must be added to compensate difference in
|
470 | // symmetry of the sine table used for CW and CCW
|
471 | Phase = PhaseValues[(Sector + 3) % 6] + PhaseOffset;
|
472 | }
|
473 | LastSector = Sector; // Update last sector
|
474 | }
|
475 |
|
476 | return;
|
477 | }
|
478 |
|
479 | /*********************************************************************
|
480 | Function: void __attribute__((__interrupt__)) _IC8Interrupt (void)
|
481 |
|
482 | PreCondition: The inputs of the hall effect sensors should have low pass
|
483 | filters. A simple RC network works.
|
484 |
|
485 | Input: None.
|
486 |
|
487 | Output: None.
|
488 |
|
489 | Side Effects: None.
|
490 |
|
491 | Overview: This interrupt represent Hall C ISR. Hall C -> RB5 -> IC8.
|
492 | This is generated by the input Capture Channel IC8.
|
493 | The purpose of this ISR is to Calculate the actual
|
494 | mechanical direction of rotation of the motor, and to adjust
|
495 | the Phase variable depending on the sector the rotor is in.
|
496 |
|
497 | Note 1: The sector is validated in order to avoid any spurious
|
498 | interrupt due to a slow slew rate on the halls inputs due to
|
499 | hardware filtering.
|
500 |
|
501 | Note 2: For Phase adjustment in CCW, an offset is added to
|
502 | compensate non-symetries in the sine table used.
|
503 | ********************************************************************/
|
504 |
|
505 | void __attribute__((interrupt, no_auto_psv)) _IC8Interrupt (void)
|
506 | {
|
507 | IFS1bits.IC8IF = 0; // Cleat interrupt flag
|
508 | HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
|
509 | Sector = SectorTable[HallValue]; // Get Sector from table
|
510 |
|
511 | // This MUST be done for getting around the HW slow rate
|
512 | if (Sector != LastSector)
|
513 | {
|
514 | // Since a new sector is detected, clear variable that would stop
|
515 | // the motor if stalled.
|
516 | MotorStalledCounter = 0;
|
517 |
|
518 | // Motor current direction is computed based on Sector
|
519 | if ((Sector == 1) || (Sector == 4))
|
520 | Current_Direction = CCW;
|
521 | else
|
522 | Current_Direction = CW;
|
523 |
|
524 | // Motor commutation is actually based on the required direction, not
|
525 | // the current dir. This allows driving the motor in four quadrants
|
526 | if (Required_Direction == CW)
|
527 | {
|
528 | Phase = PhaseValues[Sector];
|
529 | }
|
530 | else
|
531 | {
|
532 | // For CCW an offset must be added to compensate difference in
|
533 | // symmetry of the sine table used for CW and CCW
|
534 | Phase = PhaseValues[(Sector + 3) % 6] + PhaseOffset;
|
535 | }
|
536 | LastSector = Sector; // Update last sector
|
537 | }
|
538 |
|
539 | return;
|
540 | }
|
541 |
|
542 | /*********************************************************************
|
543 | Function: void __attribute__((__interrupt__)) _PWMInterrupt (void)
|
544 |
|
545 | PreCondition: None.
|
546 |
|
547 | Input: None.
|
548 |
|
549 | Output: None.
|
550 |
|
551 | Side Effects: None.
|
552 |
|
553 | Overview: in this ISR the sinewave is generated. If the current motor
|
554 | direction of rotation is different from the required
|
555 | direction then the motor is operated in braking mode and
|
556 | step commutation is performed. Once both directions are
|
557 | equal then the sinewave is fed into the motor windings.
|
558 | If PHASE_ADVANCE is defined, a value corresponding to the
|
559 | multiplication of the actual speed * maximum phase advance
|
560 | is added to the sine wave phase to produce the phase shift
|
561 |
|
562 | Note: None.
|
563 | ********************************************************************/
|
564 |
|
565 | void __attribute__((interrupt, no_auto_psv)) _PWMInterrupt (void)
|
566 | {
|
567 | IFS2bits.PWMIF = 0; // Clear interrupt flag
|
568 |
|
569 | if (Required_Direction == CW)
|
570 | {
|
571 | if (Current_Direction == CW)
|
572 | Phase += PhaseInc; // Increment Phase if CW to generate the
|
573 | // sinewave only if both directions are equal
|
574 | // If Required_Direction is CW (forward) POSITIVE voltage is applied
|
575 | #ifdef PHASE_ADVANCE
|
576 | SVM(ControlOutput, Phase + PhaseAdvance); // PhaseAdvance addition
|
577 | // produces the sinewave
|
578 | // phase shift
|
579 | #else
|
580 | SVM(ControlOutput, Phase);
|
581 | #endif
|
582 | }
|
583 | else
|
584 | {
|
585 | if (Current_Direction == CCW)
|
586 | Phase -= PhaseInc; // Decrement Phase if CCW to generate
|
587 | // the sinewave only if both
|
588 | // directions are equal
|
589 | // If Required_Direction is CCW (reverse) NEGATIVE voltage is applied
|
590 | #ifdef PHASE_ADVANCE
|
591 | SVM(-(ControlOutput+1), Phase + PhaseAdvance);// PhaseAdvance addition
|
592 | // produces the sinewave
|
593 | // phase shift
|
594 | #else
|
595 | SVM(-(ControlOutput+1), Phase);
|
596 | #endif
|
597 | }
|
598 | return;
|
599 | }
|
600 |
|
601 | /*********************************************************************
|
602 | Function: void __attribute__((__interrupt__)) _ADCInterrupt (void)
|
603 |
|
604 | PreCondition: None.
|
605 |
|
606 | Input: None.
|
607 |
|
608 | Output: None.
|
609 |
|
610 | Side Effects: None.
|
611 |
|
612 | Overview: The ADC interrupt loads the reference speed (RefSpeed) with
|
613 | the respective value of the POT. The value will be a signed
|
614 | fractional value, so it doesn't need any scaling.
|
615 |
|
616 | Note: None.
|
617 | ********************************************************************/
|
618 |
|
619 | void __attribute__((interrupt, no_auto_psv)) _ADCInterrupt (void)
|
620 | {
|
621 | IFS0bits.ADIF = 0; // Clear interrupt flag
|
622 | RefSpeed = ADCBUF0/2; //(ADCBUF0/2)+0x8000; // Read POT value to set Reference Speed //03.04.2024
|
623 | RefSpeed = RefSpeed + 0x8000; //rückwärts 06.05.2024
|
624 | /*
|
625 | __asm ("push W0");
|
626 | __asm ("push W1");
|
627 | __asm ("mov #0xffc0,W0");
|
628 | __asm ("mov _RefSpeed,W1");
|
629 | __asm ("com W1,W0") ;
|
630 | __asm ("and W1,W0,W1") ;
|
631 | __asm ("mov W1, _RefSpeed") ;
|
632 | __asm ("pop W0");
|
633 | __asm ("pop W1");
|
634 | RefSpeed = RefSpeed + 0x8000;
|
635 | */
|
636 | return;
|
637 | }
|
638 |
|
639 | /*********************************************************************
|
640 | Function: int main(void)
|
641 |
|
642 | PreCondition: None.
|
643 |
|
644 | Input: None.
|
645 |
|
646 | Output: None.
|
647 |
|
648 | Side Effects: None.
|
649 |
|
650 | Overview: main function of the application. Peripherals are
|
651 | initialized, and then, depending on the motor status
|
652 | (running or stopped) and if the push button is pressed,
|
653 | the motor is started or stopped. All other operations and
|
654 | state machines are performed with interrupts.
|
655 |
|
656 | Note: None.
|
657 | ********************************************************************/
|
658 |
|
659 | int main(void)
|
660 | {
|
661 | InitUserInt(); // Initialize User Interface I/Os
|
662 | InitADC10(); // Initialize ADC to be signed fractional
|
663 | InitTMR1(); // Initialize TMR1 for 1 ms periodic ISR
|
664 | InitTMR3(); // Initialize TMR3 for timebase of capture
|
665 | InitICandCN(); // Initialize Hall sensor inputs ISRs
|
666 | InitMCPWM(); // Initialize PWM @ 20 kHz, center aligned, 500 ns of
|
667 | // deadtime
|
668 | for(;;)
|
669 | {
|
670 |
|
671 | if ((SWITCH_S2) && (!Flags.MotorRunning))
|
672 | {
|
673 | while(SWITCH_S2);
|
674 | RunMotor(); // Run motor if push button is pressed and motor is
|
675 | // stopped
|
676 | }
|
677 | else if ((SWITCH_S2) && (Flags.MotorRunning))
|
678 | {
|
679 | while(SWITCH_S2);
|
680 | StopMotor();// Stop motor if push button is pressed and motor is
|
681 | // running
|
682 | }
|
683 |
|
684 | }
|
685 | return 0;
|
686 | }
|
687 |
|
688 | /*********************************************************************
|
689 | Function: void ChargeBootstraps(void)
|
690 |
|
691 | PreCondition: None.
|
692 |
|
693 | Input: None.
|
694 |
|
695 | Output: None.
|
696 |
|
697 | Side Effects: None.
|
698 |
|
699 | Overview: In the topology used, it is necessary to charge the
|
700 | bootstrap caps each time the motor is energized for the
|
701 | first time after an undetermined amount of time.
|
702 | ChargeBootstraps subroutine turns ON the lower transistors
|
703 | for 10 ms to ensure voltage on these caps, and then it
|
704 | transfers the control of the outputs to the PWM module.
|
705 |
|
706 | Note: None.
|
707 | ********************************************************************/
|
708 |
|
709 | void ChargeBootstraps(void)
|
710 | {
|
711 | unsigned int i;
|
712 | OVDCON = 0x0015; // Turn ON low side transistors to charge
|
713 | for (i = 0; i < 33330; i++) // 10 ms Delay at 20 MIPs
|
714 | ;
|
715 | PWMCON2bits.UDIS = 1;
|
716 | PDC1 = PTPER; // Initialize as 0 voltage
|
717 | PDC2 = PTPER; // Initialize as 0 voltage
|
718 | PDC3 = PTPER; // Initialize as 0 voltage
|
719 | OVDCON = 0x3F00; // Configure PWM0-5 to be governed by PWM module
|
720 | PWMCON2bits.UDIS = 0;
|
721 | return;
|
722 | }
|
723 |
|
724 | /*********************************************************************
|
725 |
|
726 | Function: void RunMotor(void)
|
727 |
|
728 | PreCondition: None.
|
729 |
|
730 | Input: None.
|
731 |
|
732 | Output: None.
|
733 |
|
734 | Side Effects: None.
|
735 |
|
736 | Overview: Call this subroutine when first trying to run the motor and
|
737 | the motor is previously stopped. RunMotor will charge
|
738 | bootstrap caps, will initialize application variables, and
|
739 | will enable all ISRs.
|
740 |
|
741 | Note: None.
|
742 | ********************************************************************/
|
743 |
|
744 | void RunMotor(void)
|
745 | {
|
746 | ChargeBootstraps();
|
747 | // init variables
|
748 | ControlDifference[0] = 0; // Error at K (most recent)
|
749 | ControlDifference[1] = 0; // Error at K-1
|
750 | ControlDifference[2] = 0; // Error at K-2 (least recent)
|
751 | PIDCoefficients[0] = Kp + Ki + Kd; // Modified coefficient for using MACs
|
752 | PIDCoefficients[1] = -(Kp + 2*Kd); // Modified coefficient for using MACs
|
753 | PIDCoefficients[2] = Kd; // Modified coefficient for using MACs
|
754 |
|
755 | TMR1 = 0; // Reset timer 1 for speed control
|
756 | TMR3 = 0; // Reset timer 3 for speed measurement
|
757 | ActualCapture = MAXPERIOD; // Initialize captures for minimum speed
|
758 | //(60 RPMs)
|
759 | PastCapture = 0;
|
760 |
|
761 | // Initialize direction with required direction
|
762 | // Remember that ADC is not stopped.
|
763 | HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
|
764 | LastSector = Sector = SectorTable[HallValue]; // Initialize Sector
|
765 |
|
766 |
|
767 | // RefSpeed's sign will determine if the motor should be run at CW
|
768 | // (+RefSpeed) or CCW (-RefSpeed) ONLY at start up, since when the motor
|
769 | // has started, the required direction will be set by the control output
|
770 | // variable to be able to operate in the four quadrants
|
771 | if (RefSpeed < 0)
|
772 | {
|
773 | ControlOutput = 0; // Initial output voltage
|
774 | Current_Direction = Required_Direction = CCW;
|
775 | Phase = PhaseValues[(Sector + 3) % 6] + PhaseOffset;
|
776 | }
|
777 | else
|
778 | {
|
779 | ControlOutput = 0; // Initial output voltage
|
780 | Current_Direction = Required_Direction = CW;
|
781 | Phase = PhaseValues[Sector];
|
782 | }
|
783 |
|
784 | MotorStalledCounter = 0; // Reset motor stalled protection counter
|
785 | // Set initial Phase increment with minimum value. This will change if a
|
786 | // costing operation is required by the application
|
787 | PhaseInc = __builtin_divud(512000UL, MAXPERIOD);
|
788 |
|
789 | // Clear all interrupts flags
|
790 | IFS0bits.T1IF = 0; // Clear timer 1 flag
|
791 | IFS0bits.CNIF = 0; // Clear interrupt flag
|
792 | IFS1bits.IC7IF = 0; // Clear interrupt flag
|
793 | IFS1bits.IC8IF = 0; // Clear interrupt flag
|
794 | IFS2bits.PWMIF = 0; // Clear interrupt flag
|
795 |
|
796 | // enable all interrupts
|
797 | __asm__ volatile ("DISI #0x3FFF");
|
798 | IEC0bits.T1IE = 1; // Enable interrupts for timer 1
|
799 | IEC0bits.CNIE = 1; // Enable interrupts on CN5
|
800 | IEC1bits.IC7IE = 1; // Enable interrupts on IC7
|
801 | IEC1bits.IC8IE = 1; // Enable interrupts on IC8
|
802 | IEC2bits.PWMIE = 1; // Enable PWM interrupts
|
803 | DISICNT = 0;
|
804 |
|
805 | Flags.MotorRunning = 1; // Indicate that the motor is running
|
806 | return;
|
807 | }
|
808 |
|
809 | /*********************************************************************
|
810 | Function: void StopMotor(void)
|
811 |
|
812 | PreCondition: None.
|
813 |
|
814 | Input: None.
|
815 |
|
816 | Output: None.
|
817 |
|
818 | Side Effects: None.
|
819 |
|
820 | Overview: Call this subroutine whenever the user want to stop the
|
821 | motor. This subroutine will clear interrupts properly, and
|
822 | will also turn OFF all PWM channels.
|
823 |
|
824 | Note: None.
|
825 | ********************************************************************/
|
826 | void StopMotor(void)
|
827 | {
|
828 | OVDCON = 0x0000; // turn OFF every transistor
|
829 |
|
830 | // disable all interrupts
|
831 | __asm__ volatile ("DISI #0x3FFF");
|
832 | IEC0bits.T1IE = 0; // Disable interrupts for timer 1
|
833 | IEC0bits.CNIE = 0; // Disable interrupts on CN5
|
834 | IEC1bits.IC7IE = 0; // Disable interrupts on IC7
|
835 | IEC1bits.IC8IE = 0; // Disable interrupts on IC8
|
836 | IEC2bits.PWMIE = 0; // Disable PWM interrupts
|
837 | DISICNT = 0;
|
838 |
|
839 | Flags.MotorRunning = 0; // Indicate that the motor has been stopped
|
840 | return;
|
841 | }
|
842 |
|
843 | /*********************************************************************
|
844 | Function: void SpeedControl(void)
|
845 |
|
846 | PreCondition: None.
|
847 |
|
848 | Input: None.
|
849 |
|
850 | Output: None.
|
851 |
|
852 | Side Effects: None.
|
853 |
|
854 | Overview: This subroutine implements a PID in assembly using the MAC
|
855 | instruction of the dsPIC.
|
856 |
|
857 | Note: None.
|
858 | ********************************************************************/
|
859 |
|
860 | /*
|
861 |
|
862 | ---- Proportional
|
863 | | | Output
|
864 | ---------------| Kp |-----------------
|
865 | | | | |
|
866 | | ---- |
|
867 | Reference | ---
|
868 | Speed --- | -------------- Integral | + | Control -------
|
869 | --------| + | Error | | Ki | Output | | Output | |
|
870 | | |----------|----------| ------------ |----------|+ |----------| Plant |--
|
871 | -----| - | | | 1 - Z^(-1) | | | | | |
|
872 | | --- | -------------- | + | ------- |
|
873 | | | --- |
|
874 | | Measured | ------------------- Deriv | |
|
875 | | Speed | | | Output | |
|
876 | | --------| Kd * (1 - Z^(-1)) |--------- |
|
877 | | | | |
|
878 | | ------------------- |
|
879 | | |
|
880 | | |
|
881 | -----------------------------------------------------------------------------------
|
882 |
|
883 | ControlOutput(K) = ControlOutput(K-1)
|
884 | + ControlDifference(K) * (Kp + Ki + Kd)
|
885 | + ControlDifference(K-1) * (-Ki - 2*Kd)
|
886 | + ControlDifference(K-2) * Kd
|
887 |
|
888 | Using PIDCoefficients:
|
889 | PIDCoefficients[0] = Kp + Ki + Kd
|
890 | PIDCoefficients[1] = -(Kp + 2*Kd)
|
891 | PIDCoefficients[2] = Kd
|
892 | and leting:
|
893 | ControlOutput -> ControlOutput(K) and ControlOutput(K-1)
|
894 | ControlDifference[0] -> ControlDifference(K)
|
895 | ControlDifference[1] -> ControlDifference(K-1)
|
896 | ControlDifference[2] -> ControlDifference(K-2)
|
897 |
|
898 | ControlOutput = ControlOutput
|
899 | + ControlDifference[0] * PIDCoefficients[0]
|
900 | + ControlDifference[1] * PIDCoefficients[1]
|
901 | + ControlDifference[2] * PIDCoefficients[2]
|
902 |
|
903 | This was implemented using Assembly with signed fractional and saturation enabled
|
904 | with MAC instruction
|
905 | */
|
906 |
|
907 | void SpeedControl(void) {
|
908 | SFRAC16 *ControlDifferencePtr = ControlDifference;
|
909 | SFRAC16 *PIDCoefficientsPtr = PIDCoefficients;
|
910 | SFRAC16 x_prefetch;
|
911 | SFRAC16 y_prefetch;
|
912 |
|
913 | register int reg_a asm("A");
|
914 | register int reg_b asm("B");
|
915 |
|
916 | CORCONbits.SATA = 1; // Enable Saturation on Acc A
|
917 |
|
918 | #if __C30_VERSION__ == 320
|
919 | #error "This Demo is not supported with v3.20"
|
920 | #endif
|
921 |
|
922 | #if __C30_VERSION__ < 320
|
923 |
|
924 | reg_a = __builtin_lac(RefSpeed,0);
|
925 | reg_b = __builtin_lac(MeasuredSpeed,0);
|
926 | reg_a = __builtin_subab();
|
927 | *ControlDifferencePtr = __builtin_sac(reg_a,0);
|
928 | reg_a = __builtin_movsac(&ControlDifferencePtr, &x_prefetch, 2,
|
929 | &PIDCoefficientsPtr, &y_prefetch, 2, 0);
|
930 | reg_a = __builtin_lac(ControlOutput, 0);
|
931 | reg_a = __builtin_mac(x_prefetch,y_prefetch,
|
932 | &ControlDifferencePtr, &x_prefetch, 2,
|
933 | &PIDCoefficientsPtr, &y_prefetch, 2, 0);
|
934 | reg_a = __builtin_mac(x_prefetch,y_prefetch,
|
935 | &ControlDifferencePtr, &x_prefetch, 2,
|
936 | &PIDCoefficientsPtr, &y_prefetch, 2, 0);
|
937 | reg_a = __builtin_mac(x_prefetch,y_prefetch,
|
938 | &ControlDifferencePtr, &x_prefetch, 2,
|
939 | &PIDCoefficientsPtr, &y_prefetch, 2, 0);
|
940 | ControlOutput = __builtin_sac(reg_a, 0);
|
941 |
|
942 | #else
|
943 |
|
944 | reg_a = __builtin_lac(RefSpeed,0);
|
945 | reg_b = __builtin_lac(MeasuredSpeed,0);
|
946 | reg_a = __builtin_subab(reg_a,reg_b);
|
947 | *ControlDifferencePtr = __builtin_sac(reg_a,0);
|
948 | reg_a = __builtin_movsac(&ControlDifferencePtr, &x_prefetch, 2,
|
949 | &PIDCoefficientsPtr, &y_prefetch, 2, 0, reg_b);
|
950 | reg_a = __builtin_lac(ControlOutput, 0);
|
951 |
|
952 | reg_a = __builtin_mac(reg_a,x_prefetch,y_prefetch,
|
953 | &ControlDifferencePtr, &x_prefetch, 2,
|
954 | &PIDCoefficientsPtr, &y_prefetch, 2, 0, reg_b);
|
955 | reg_a = __builtin_mac(reg_a,x_prefetch,y_prefetch,
|
956 | &ControlDifferencePtr, &x_prefetch, 2,
|
957 | &PIDCoefficientsPtr, &y_prefetch, 2, 0, reg_b);
|
958 | reg_a = __builtin_mac(reg_a,x_prefetch,y_prefetch,
|
959 | &ControlDifferencePtr, &x_prefetch, 2,
|
960 | &PIDCoefficientsPtr, &y_prefetch, 2, 0, reg_b);
|
961 |
|
962 | ControlOutput = __builtin_sac(reg_a, 0);
|
963 |
|
964 | #endif
|
965 |
|
966 | CORCONbits.SATA = 0; // Disable Saturation on Acc A
|
967 | // Store last 2 errors
|
968 | ControlDifference[2] = ControlDifference[1];
|
969 | ControlDifference[1] = ControlDifference[0];
|
970 |
|
971 | // If CLOSED_LOOP is undefined (running open loop) overide ControlOutput
|
972 | // with value read from the external potentiometer
|
973 | #ifndef CLOSED_LOOP
|
974 | ControlOutput = RefSpeed;
|
975 | #endif
|
976 |
|
977 | // ControlOutput will determine the motor required direction
|
978 | if (ControlOutput < 0)
|
979 | Required_Direction = CCW;
|
980 | else
|
981 | Required_Direction = CW;
|
982 | return;
|
983 | }
|
984 |
|
985 | /*********************************************************************
|
986 | Function: void ForceCommutation(void)
|
987 |
|
988 | PreCondition: None.
|
989 |
|
990 | Input: None.
|
991 |
|
992 | Output: None.
|
993 |
|
994 | Side Effects: None.
|
995 |
|
996 | Overview: This function is called each time the motor doesn't
|
997 | generate hall change interrupt, which means that the motor
|
998 | running too slow or is stalled. If it is stalled, the motor
|
999 | is stopped, but if it is only slow, this function is called
|
1000 | and forces a commutation based on the actual hall sensor
|
1001 | position and the required direction of rotation.
|
1002 |
|
1003 | Note: None.
|
1004 | ********************************************************************/
|
1005 |
|
1006 | void ForceCommutation(void)
|
1007 | {
|
1008 | HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
|
1009 | Sector = SectorTable[HallValue]; // Read sector based on halls
|
1010 | if (Sector != -1) // If the sector is invalid don't do anything
|
1011 | {
|
1012 | // Depending on the required direction, a new phase is fetched
|
1013 | if (Required_Direction == CW)
|
1014 | {
|
1015 | // Motor is required to run forward, so read directly the table
|
1016 | Phase = PhaseValues[Sector];
|
1017 | }
|
1018 | else
|
1019 | {
|
1020 | // Motor is required to run reverse, so calculate new phase and
|
1021 | // add offset to compensate asymmetries
|
1022 | Phase = PhaseValues[(Sector + 3) % 6] + PhaseOffset;
|
1023 | }
|
1024 | }
|
1025 | return;
|
1026 | }
|
1027 |
|
1028 | /*********************************************************************
|
1029 | Function: void InitADC10(void)
|
1030 |
|
1031 | PreCondition: None.
|
1032 |
|
1033 | Input: None.
|
1034 |
|
1035 | Output: None.
|
1036 |
|
1037 | Side Effects: None.
|
1038 |
|
1039 | Overview: Below is the code required to setup the ADC registers for:
|
1040 | 1. 1 channel conversion (in this case RB2/AN2)
|
1041 | 2. PWM trigger starts conversion
|
1042 | 3. Pot is connected to CH0 and RB2/AN2
|
1043 | 4. The data format will be signed fractional
|
1044 |
|
1045 | Note: None.
|
1046 | ********************************************************************/
|
1047 |
|
1048 | void InitADC10(void)
|
1049 | {
|
1050 | ADPCFG = 0x0038; // RB3, RB4, and RB5 are digital //04.05.2024 vorher 0x2038
|
1051 |
|
1052 |
|
1053 | ADCON3 = 0x0003;
|
1054 | ADCON2 = 0x0404;
|
1055 | ADCHS = 0;
|
1056 | ADCON1 = 0x8066;
|
1057 | ADCON1 = 0x0266;
|
1058 | ADCSSL = 0x2004;
|
1059 | IFS0bits.ADIF = 0;
|
1060 | IEC0bits.ADIE = 1; // Enable interrupts
|
1061 | /*
|
1062 | ADCON1 = 0x026E; // PWM starts conversion //03.04.2024
|
1063 | // Signed fractional conversions
|
1064 | ADCON2 = 0x000;
|
1065 | ADCHS = 0x0002; // Pot is connected to AN2
|
1066 |
|
1067 | ADCON3 = 0x0003;
|
1068 | IFS0bits.ADIF = 0; // Clear ISR flag
|
1069 | IEC0bits.ADIE = 1; // Enable interrupts
|
1070 | */
|
1071 | ADCON1bits.ADON = 1; // turn ADC ON
|
1072 | return;
|
1073 | }
|
1074 |
|
1075 | /*********************************************************************
|
1076 | Function: void InitMCPWM(void)
|
1077 |
|
1078 | PreCondition: None.
|
1079 |
|
1080 | Input: None.
|
1081 |
|
1082 | Output: None.
|
1083 |
|
1084 | Side Effects: None.
|
1085 |
|
1086 | Overview: InitMCPWM, intializes the PWM as follows:
|
1087 | 1. FPWM = 20000 hz
|
1088 | 2. Complementary PWMs with center aligned
|
1089 | 3. Set Duty Cycle to 0 for complementary, which is half the
|
1090 | period
|
1091 | 4. Set ADC to be triggered by PWM special trigger
|
1092 | 5. Configure deadtime to be 500 ns
|
1093 |
|
1094 | Note: None.
|
1095 | ********************************************************************/
|
1096 |
|
1097 | void InitMCPWM(void)
|
1098 | {
|
1099 | TRISE = 0x0100; // PWM pins as outputs, and FLTA as input
|
1100 | PTPER = (FCY/FPWM - 1) >> 1; // Compute Period based on CPU speed and
|
1101 | // required PWM frequency (see defines)
|
1102 | OVDCON = 0x0000; // Disable all PWM outputs.
|
1103 | DTCON1 = 0x0008; // ~500 ns of dead time
|
1104 | PWMCON1 = 0x0077; // Enable PWM output pins and configure them as
|
1105 | // complementary mode
|
1106 | PDC1 = PTPER; // Initialize as 0 voltage
|
1107 | PDC2 = PTPER; // Initialize as 0 voltage
|
1108 | PDC3 = PTPER; // Initialize as 0 voltage
|
1109 | SEVTCMP = 1; // Enable triggering for ADC
|
1110 | PWMCON2 = 0x0F02; // 16 postscale values, for achieving 20 kHz
|
1111 | PTCON = 0x8002; // start PWM as center aligned mode
|
1112 | return;
|
1113 | }
|
1114 |
|
1115 | /*********************************************************************
|
1116 | Function: void InitICandCN(void)
|
1117 |
|
1118 | PreCondition: None.
|
1119 |
|
1120 | Input: None.
|
1121 |
|
1122 | Output: None.
|
1123 |
|
1124 | Side Effects: None.
|
1125 |
|
1126 | Overview: Configure Hall sensor inputs, one change notification and
|
1127 | two input captures. on IC7 the actual capture value is used
|
1128 | for further period calculation
|
1129 |
|
1130 | Note: None.
|
1131 | ********************************************************************/
|
1132 |
|
1133 | void InitICandCN(void)
|
1134 | {
|
1135 | //Hall A -> CN5. Hall A is only used for commutation.
|
1136 | //Hall B -> IC7. Hall B is used for Speed measurement and commutation.
|
1137 | //Hall C -> IC8. Hall C is only used for commutation.
|
1138 |
|
1139 | // Init Input change notification 5
|
1140 | TRISB |= 0xffff; // Ensure that hall connections are inputs 7.8.2023 vorher; 0x38
|
1141 | CNPU1 = 0; // Disable all CN pull ups
|
1142 | CNPU1 = 0x00e0; //03.03.2024
|
1143 | CNEN1 = 0x0020; // Enable CN5
|
1144 | IFS0bits.CNIF = 0; // Clear interrupt flag
|
1145 |
|
1146 | // Init Input Capture 7
|
1147 | IC7CON = 0x0001; // Input capture every edge with interrupts and TMR3
|
1148 | IFS1bits.IC7IF = 0; // Clear interrupt flag
|
1149 |
|
1150 | // Init Input Capture 8
|
1151 | IC8CON = 0x0001; // Input capture every edge with interrupts and TMR3
|
1152 | IFS1bits.IC8IF = 0; // Clear interrupt flag
|
1153 |
|
1154 | return;
|
1155 | }
|
1156 |
|
1157 | /*********************************************************************
|
1158 | Function: void InitTMR1(void)
|
1159 |
|
1160 | PreCondition: None.
|
1161 |
|
1162 | Input: None.
|
1163 |
|
1164 | Output: None.
|
1165 |
|
1166 | Side Effects: None.
|
1167 |
|
1168 | Overview: Initialization of timer 1 as a periodic interrupt each 1 ms
|
1169 | for speed control, motor stalled protection which includes:
|
1170 | forced commutation if the motor is too slow, or motor
|
1171 | stopped if the motor is stalled.
|
1172 |
|
1173 | Note: None.
|
1174 | ********************************************************************/
|
1175 |
|
1176 | void InitTMR1(void)
|
1177 | {
|
1178 | T1CON = 0x0020; // internal Tcy/64 clock
|
1179 | TMR1 = 0;
|
1180 | PR1 = 313; // 1 ms interrupts for 20 MIPS
|
1181 | T1CONbits.TON = 1; // turn on timer 1
|
1182 | return;
|
1183 | }
|
1184 |
|
1185 | /*********************************************************************
|
1186 | Function: void InitTMR3(void)
|
1187 |
|
1188 | PreCondition: None.
|
1189 |
|
1190 | Input: None.
|
1191 |
|
1192 | Output: None.
|
1193 |
|
1194 | Side Effects: None.
|
1195 |
|
1196 | Overview: Initialization of timer 3 as the timebase for the capture
|
1197 | channels for calculating the period of the halls.
|
1198 |
|
1199 | Note: None.
|
1200 | ********************************************************************/
|
1201 |
|
1202 | void InitTMR3(void)
|
1203 | {
|
1204 | T3CON = 0x0020; // internal Tcy/64 clock
|
1205 | TMR3 = 0;
|
1206 | PR3 = 0xFFFF;
|
1207 | T3CONbits.TON = 1; // turn on timer 3
|
1208 | return;
|
1209 | }
|
1210 |
|
1211 | /*********************************************************************
|
1212 | Function: void InitUserInt(void)
|
1213 |
|
1214 | PreCondition: None.
|
1215 |
|
1216 | Input: None.
|
1217 |
|
1218 | Output: None.
|
1219 |
|
1220 | Side Effects: None.
|
1221 |
|
1222 | Overview: Initialization of the IOs used by the application. The IOs
|
1223 | for the PWM and for the ADC are initialized in their
|
1224 | respective peripheral initialization subroutine.
|
1225 |
|
1226 | Note: None.
|
1227 | ********************************************************************/
|
1228 |
|
1229 | void InitUserInt(void)
|
1230 | {
|
1231 | TRISC |= 0x4000; // S2/RC14 as input
|
1232 | // Analog pin for POT already initialized in ADC init subroutine
|
1233 | PORTF = 0x0008; // RS232 Initial values
|
1234 | TRISF = 0xFFF7; // TX as output
|
1235 | return;
|
1236 | }
|
1237 |
|
1238 | // End of SinusoidalBLDC v1.2.c
|