This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hello,
Sometimes, after a power cycle, as soon as the FOC is started and the SpinTAC Velocity Control block is regulating at 0 speed, one or more motors automatically start spinning at full speed and cannot be controlled anymore. The only way to stop the motor is to disable the FOC. "Automatically" means that the motor starts spinning even if the speed setpoint fed into the function CTRL_setSpd_ref_krpm is null.
We are running out of ideas now and we need your help to avoid having this uncontrollable behavior that is not acceptable.
Our system is composed by:
Here is the process that is done the first time the system is powered on in order to align the motor with the encoder:
if (EST_State_Rs == EST_getState(m_ctrlHandle->estHandle)) { HAL_resetQepPosnCounts(m_halHandle); }
Here is the normal process after each power cycle (once the encoder are aligned with the motors):
Uncontrollable issues have been seen in both first time process and normal process as soon as the position control is enabled.
To already answer potential questions on your side:
Could you please help us on this subject?
I have attached our user.h file. Please note that this file is shared among the two motors types this is why we have replaced MOTOR_ definitions with our own definitions (motor parameters are loaded at startup based on an address).
This post has been created based on my previous question:
https://e2e.ti.com/support/microcontrollers/c2000/f/171/t/900637
Thank you in advance.
Regards,
Johann
I think you should add much more codes in the control ISR, is it possible to move some codes to the main background loop and use a higher ISR frequency like 9kHz, even 18kHz. It seems like 6Khz is too low for current control to achieve good velocity and position control, the control interrupt should be set to the highest priority and nest other interrupts without delay to complete the current close loop control.
You may disable other interrupts, just enable the control ISR and debug the project with CCS to monitor some control variables like feedback speed and currents. I suppose the feedback speed should be not correct.
You might have a look at the following two links that should be helpful for you.
https://e2e.ti.com/support/microcontrollers/c2000/f/171/p/526272/1917534
https://e2e.ti.com/support/microcontrollers/c2000/f/171/p/378088/1871253
I think you should add much more codes in the control ISR, is it possible to move some codes to the main background loop
Here is the code of the motor control ISR. As you can see, we use current reconstruction since we have high-speed motors.
interrupt void motorCtrl_isr (void) { SVGENCURRENT_MeasureShunt_e measShunt = immeasureable; static uint16_t stCnt = 0U; static uint16_t stPosCnt = 0U; uint32_t posCnt = 0U; IER |= 0x0100; IER &= 0x0100; asm(" NOP"); EINT; posCnt = HAL_getQepPosnCounts(m_halHandle); measShunt = SVGENCURRENT_getMode(m_svGenCurHandle); calcAbsPos(posCnt); // Compute the electrical angle. ENC_calcElecAngle(m_encHandle, posCnt); // Acknowledge the ADC interrupt. HAL_acqAdcInt(m_halHandle, ADC_IntNumber_1); // Convert the ADC data. HAL_readAdcData( m_halHandle, &m_adcData, (sdw_adcData_t *)&m_sdwAdcData, m_adcFullScaleCurrent ); // Check for an overcurrent on the motor. checkMotOvercurrent(); if (stCnt++ >= ISR_TICKS_PER_SPINTAC_TICK) { ST_runPosConv(m_stHandle, m_encHandle, m_ctrlHandle); ST_runVelCtl(m_stHandle, m_ctrlHandle); stCnt = 1U; } // Run the current reconstruction algorithm. SVGENCURRENT_RunRegenCurrent( m_svGenCurHandle, (MATH_vec3 *)(m_adcData.I.value) ); m_iAvg.value[0] += (m_adcData.I.value[0] - m_iAvg.value[0]) >> m_iAvgShift; m_iAvg.value[1] += (m_adcData.I.value[1] - m_iAvg.value[1]) >> m_iAvgShift; m_iAvg.value[2] += (m_adcData.I.value[2] - m_iAvg.value[2]) >> m_iAvgShift; if (measShunt > two_phase_measurable) { m_adcData.I.value[0] = m_iAvg.value[0]; m_adcData.I.value[1] = m_iAvg.value[1]; m_adcData.I.value[2] = m_iAvg.value[2]; } // Run the controller. CTRL_run( m_ctrlHandle, m_halHandle, &m_adcData, &m_pwmData, ENC_getElecAngle(m_encHandle) ); // Run the PWM compensation and ignore current algorithm. SVGENCURRENT_compPwmData( m_svGenCurHandle, &(m_pwmData.Tabc), &m_prevPwmData ); // Set PWMs. HAL_writePwmData(m_halHandle, &m_pwmData); { SVGENCURRENT_IgnoreShunt_e ignoreShuntNextCycle = SVGENCURRENT_getIgnoreShunt(m_svGenCurHandle); SVGENCURRENT_VmidShunt_e midVolShunt = SVGENCURRENT_getVmid(m_svGenCurHandle); // Set trigger point in the middle of the low side pulse HAL_setTrigger(m_halHandle, ignoreShuntNextCycle, midVolShunt); } // Setup the controller. CTRL_setup(m_ctrlHandle); if (EST_State_Rs == EST_getState(m_ctrlHandle->estHandle)) { HAL_resetQepPosnCounts(m_halHandle); } DINT; } static void calcAbsPos (const uint32_t qepPos) { int32_t encTurnCnt = m_oldEncTurnCnt; if ((m_oldEncCnt > 3800) && (qepPos < 200)) { // Rollover detected (overflow). encTurnCnt++; } else if ((m_oldEncCnt < 200) && (qepPos > 3800)) { // Rollover detected (underflow). encTurnCnt--; } else { ; } m_absPos += (encTurnCnt - m_oldEncTurnCnt) * (int32_t)m_encCntPerTurn; m_absPos += qepPos - m_oldEncCnt; m_oldEncTurnCnt = encTurnCnt; m_oldEncCnt = qepPos; } static void checkMotOvercurrent (void) { float_t ovcThresholdA_neg = -m_ovcThresholdA; _iq iq = _IQ(0.0f); float_t iq_A = 0.0f; iq = _IQmpy( CTRL_getIq_in_pu(m_ctrlHandle), _IQ(m_iqFullScaleCurrent) ); iq_A = _IQ24toF(iq); if ((iq_A > m_ovcThresholdA) || (iq_A < ovcThresholdA_neg)) { if (true == mb_ovcThresholdReached) { m_ovcTimeCnt++; } else { mb_ovcThresholdReached = true; } } else { if ((m_ovcTimeCnt > 0) && (false == mb_ovcThresholdReached)) { m_ovcTimeCnt--; } else { mb_ovcThresholdReached = false; } } } void ST_runPosConv ( ST_Handle handle, ENC_Handle encHandle, CTRL_Handle ctrlHandle ) { ST_Obj *stObj = (ST_Obj *)handle; // Get the electrical angle from the ENC module. STPOSCONV_setElecAngle_erev( stObj->posConvHandle, ENC_getElecAngle(m_encHandle) ); // Run the SpinTAC Position Converter. STPOSCONV_run(stObj->posConvHandle); } void ST_runVelCtl (ST_Handle handle, CTRL_Handle ctrlHandle) { _iq speedFeedback, iqReference; ST_Obj *stObj = (ST_Obj *)handle; CTRL_Obj *ctrlObj = (CTRL_Obj *)m_ctrlHandle; // Get the mechanical speed in pu. speedFeedback = STPOSCONV_getVelocityFiltered(stObj->posConvHandle); // Run the SpinTAC Controller // Note that the library internal ramp generator is used to set the speed reference STVELCTL_setVelocityReference( stObj->velCtlHandle, TRAJ_getIntValue(ctrlObj->trajHandle_spd) ); // Internal ramp generator does not provide Acceleration Reference STVELCTL_setAccelerationReference(stObj->velCtlHandle, _IQ(0.0)); STVELCTL_setVelocityFeedback(stObj->velCtlHandle, speedFeedback); STVELCTL_run(stObj->velCtlHandle); // select SpinTAC Velocity Controller iqReference = STVELCTL_getTorqueReference(stObj->velCtlHandle); if (MOT_OP_MODE_Current != m_curMode) { // Set the Iq reference that came out of SpinTAC Velocity Control. CTRL_setIq_ref_pu(m_ctrlHandle, iqReference); } }
...use a higher ISR frequency like 9kHz, even 18kHz. It seems like 6Khz is too low for current control to achieve good velocity and position control, the control interrupt should be set to the highest priority and nest other interrupts without delay to complete the current close loop control.
The problem is that our system relies on a high-speed communication UART (3.75Mbps) that cannot be interrupted by any other interrupt otherwise overrun errors occur (this is the reason why EINT/DINT instructions are used in the motor control ISR). 18 bytes are received followed by 12 bytes sent every 2ms. This is why we had to slow down the motor control timings in order to be sure that we do not miss an ADC sample because of the communication interrupts.
We plan in the near future to use McBSP with DMA in order to avoid interrupting the motor control ISR anymore.
Regarding the InstaSPIN execution frequency which is 6kHz, we followed the following recommendation from the InstaSPIN user guide:
The highest frequency among our motors is 333.33Hz, which means that with 6kHz we are far above the recommendations.
The duration of the motor control interruption without SpinTAC is already almost 90us (this time is mainly equal to the CTRL execution time) which would cause overruns at 18kHz. We saw that when the code is executed exclusively from RAM using the debugger, this time is reduced from about 90us to about 40us.
You might have a look at the following two links that should be helpful for you.
I've already seen these post and they haven't helped me to solve the issue. The first post is talking about a certain speed above which the motor has the same uncontrollable behavior as we have. However, in our case, the issue occurs when the motor is not spinning at all.
Q1: What current sensor are you using on your board? 1. Define all of the functions in ISR to inline to reduce the function calling branch time. 2. Copy all ISR codes from flash to RAM during running by using "#pragma CODE_SECTION(function_name,"ramfuncs");" and call "memCopy()" as the example lab. 3. Move or remove checkMotOvercurrent() that seems like the function is useless to implement the over-current protection.
The recommended control frequency (at least 10 times electric frequency) is just for typical InstaSPIN-FOC control, it's better to use a far higher frequency for fast response, high speed, and position control.
Don't set the USER_MAX_VS_MAG_PU to a value is higher than 0.5774 unless the current sensing is using an inline shunt resistor or hall sensor.
Q2: All are too slow since the performances of the speed and position loop are based on the frequency of the current loop.
Q3: That depends on the control bandwidth and required performance. I don't think the bandwidth for SpinTAC Control can be achieved by using such current loop and speed loop frequency. Generally, the frequency of the current loop should be at least 10 times the speed loop.
Q4: As answers to Q1.
Hi Seydoux,
Any updates? Do you have a chance to optimize the ISR code to use a higher frequency ISR for motor control?
Hi Yanming,
What current sensor are you using on your board?
Do you mean the HW configuration of the phases currents measurement? If so, we are measuring the current using 3 low-side resistors through a DRV8353 connected to three ADC channels.
We finally did optimize the communication link (we did fight a lot with McBSP SPI DMA implementation until now to make it work).
I am now able to focus on the motor control ISR execution time optimization.
I'll come back to you as soon as I do progress on that subject.
Hi Yanming,
As discussed, can you please:
Q1. Check why the following code is taking 365 cycles (~4us @90MHz) and how can I reduce this execution time (or improve it)? By the way, counts were measured using timer 0 as shown in the code snippet below. For your information, _IQmpy takes 305 cycles (~3.4us) of the 365 cycles.
float_t ovcThresholdA_neg = -m_ovcThresholdA; _iq iq = _IQ(0.0f); float_t iq_A = 0.0f; m_halHandle->timerHandle[0]->TCR |= (uint16_t)TIMER_TCR_TRB_BITS; iq = _IQmpy( CTRL_getIq_in_pu(m_ctrlHandle), _IQ(m_iqFullScaleCurrent) ); iq_A = _IQ24toF(iq); g_dbgIsrExecTimeCnt = 0xFFFFFFFF - m_halHandle->timerHandle[0]->TIM;
Assembly code is below:
342 iq = _IQmpy( 008aa4: 5CAD MOVZ AR4, @SP 341 m_halHandle->timerHandle[0]->TCR |= (uint16_t)TIMER_TCR_TRB_BITS; 008aa5: 8B12 MOVL XAR1, @0x12 008aa6: D07E MOVB XAR0, #0x7e 342 iq = _IQmpy( 008aa7: DC8C SUBB XAR4, #12 341 m_halHandle->timerHandle[0]->TCR |= (uint16_t)TIMER_TCR_TRB_BITS; 008aa8: 8391 MOVL XAR5, *+XAR1[AR0] 342 iq = _IQmpy( 008aa9: 88A4 MOVZ AR6, @AR4 008aaa: E2AF0024 MOV32 R0H, @0x24, UNCF 337 float_t ovcThresholdA_neg = -m_ovcThresholdA; 008aac: E2AF0426 MOV32 R4H, @0x26, UNCF 341 m_halHandle->timerHandle[0]->TCR |= (uint16_t)TIMER_TCR_TRB_BITS; 008aae: 1AE50020 OR *+XAR5[4], #0x0020 342 iq = _IQmpy( 008ab0: 767F5997 LCR FS$$TOFD 008ab2: 5CAD MOVZ AR4, @SP 008ab3: 88AD MOVZ AR6, @SP 008ab4: 8F7F5CE8 MOVL XAR5, #0x3f5ce8 008ab6: DC8C SUBB XAR4, #12 008ab7: DE88 SUBB XAR6, #8 008ab8: 5CA4 MOVZ AR4, @AR4 008ab9: 88A6 MOVZ AR6, @AR6 008aba: 767F52B6 LCR FD$$MPY 008abc: 5CAD MOVZ AR4, @SP 008abd: DC88 SUBB XAR4, #8 008abe: 5CA4 MOVZ AR4, @AR4 008abf: 767F5902 LCR FD$$TOL 008ac1: 761F0240 MOVW DP, #0x240 008ac3: 1EA6 MOVL @XAR6, ACC 008ac4: 02B4 MOVB ACC, #180 008ac5: 0714 ADDL ACC, @0x14 008ac6: 8AA9 MOVL XAR4, @ACC 346 g_dbgIsrExecTimeCnt = 0xFFFFFFFF - m_halHandle->timerHandle[0]->TIM; 008ac7: D07E MOVB XAR0, #0x7e 342 iq = _IQmpy( 008ac8: 87C4 MOVL XT, *+XAR4[0] 008ac9: 560500A6 IMPYL P, XT, @XAR6 008acb: 566300A6 QMPYL ACC, XT, @XAR6 346 g_dbgIsrExecTimeCnt = 0xFFFFFFFF - m_halHandle->timerHandle[0]->TIM; 008acd: 8A91 MOVL XAR4, *+XAR1[AR0] 342 iq = _IQmpy( 008ace: 56A7 LSL64 ACC:P, 8 008acf: 1EA6 MOVL @XAR6, ACC 346 g_dbgIsrExecTimeCnt = 0xFFFFFFFF - m_halHandle->timerHandle[0]->TIM; 008ad0: 0200 MOVB ACC, #0 008ad1: 1901 SUBB ACC, #1 008ad2: 03C4 SUBL ACC, *+XAR4[0] 008ad3: 1E3E MOVL @0x3e, ACC 008ad4: 06A6 MOVL ACC, @XAR6 348 iq_A = _IQ24toF(iq); 008ad5: 764084C8 LCR $..\src\IQNtoF_fpu32.asm:118:261$
Q2. Check why function HAL_readAdcData cannot be inlined in the motor control ISR even if #pragam FUNC_ALWAYS_INLINE(HAL_readAdcData) is used and compiler optimization level is set to 2?
I also measured with timer0 the execution time of function STPOSCONV_run and STVELCTL_run while the SpinTAC is OFF (RES = 0, ENB = 0) and compared the measurements with the ones in Table 8-23 of InstaSPIN User's Guide.
According to the user guide:
In my case:
Q3. Why are those functions executed so slowly compared to the values in the User's Guide (STPOSCONV_run is about 3 times slower)? In my case, those functions are running from RAM.
Q4. We are using the function CTRL_setSpd_ref_krpm outside the motor control ISR as it is done in the labs. There is a probability that the motor control ISR triggers while this function is being executed by the background code: in other words, spd_ref variable could be corrupted and used in the motor control ISR leading to a potential misbehavior.
How can we protect the access to this function? Why is it not protected in the labs?
Q5. We need to share variables between the motor control ISR which has the highest priority and low priority interrupt. In the low priority interrupt which nests motor control ISR, is it safe to disable global interrupts while accessing shared variables as follows if we make sure that variables access are as atomic as possible? Or is there a better way to avoid reading potentially "corrupted" variables in the low priority interrupt?
interrupt void lowPriorityInterrupt (void) { int16_t speed; uint32_t position; float_t speedRef; // Nest higher priority interrupt (INT1.1). IER |= 0x0001; IER &= 0x0001; asm(" NOP"); EINT; //... // getMotSpeed() is inline and returns a variable set by higher priority ISR (INT1.1). DINT speed = getMotSpeed(); EINT //... // getMotPosition() is inline and returns a variable set by higher priority ISR (INT1.1). DINT position = getMotPosition(); EINT // Compute new speed reference. speedRef = ...; // Apply speedRef safely to the FOC controller executed in the higher priority ISR (INT1.1). DINT CTRL_setSpd_ref_krpm(...); EINT // exit low priority interrupt PIE_clearInt(...); DINT }
Q1. What type is m_iqFullScaleCurrent? A variable? or a constant? The _IQ() will take a lot of CPU cycles if the m_iqFullScaleCurrent is a variable.
Q2. The HAL_readAdcData can be embedded into the motor control ISR if using the inline definition in the example lab of motorWare. Maybe, there are some configuration are not right in your project.
Q3. The executing time in User's guide is based on using SpinTAC.lib with fixed point IQMath library, we don't have the benchmarks for SpinTAC_fpu32.lib which still needs to use the fixed point IQMath library.
You might use the SpinTAC.lib with fixed point IQMath library if there are not too many floating-point operations in your project.
Q4. The frequency of the speed loop is far lower than the current loop, so it should be fine for InstaSPIN-FOC if this variable is updated in background loop. You can execute this function in ISR if you use the spinTAC.
Q5. You might enable nesting interrupt after these variables are updated.
Q1. What type is m_iqFullScaleCurrent? A variable? or a constant? The _IQ() will take a lot of CPU cycles if the m_iqFullScaleCurrent is a variable.
Yes, this is a floating point variable (static float_t m_iqFullScaleCurrent).
Q2. The HAL_readAdcData can be embedded into the motor control ISR if using the inline definition in the example lab of motorWare. Maybe, there are some configuration are not right in your project.
The project has been shared with you so that you can check on your side.
Q3. The executing time in User's guide is based on using SpinTAC.lib with fixed point IQMath library, we don't have the benchmarks for SpinTAC_fpu32.lib which still needs to use the fixed point IQMath library. You might use the SpinTAC.lib with fixed point IQMath library if there are not too many floating-point operations in your project.
SDW_Q1: For my understanding, if I enable the FPU32 support (--float_support=fpu32), can I use a mix of FPU32 and non-FPU32 libraries?
For example, can I use:
As I understand, as soon as the FPU32 is enabled, we need to use FPU32 libraries. But maybe I am wrong.
Q4. The frequency of the speed loop is far lower than the current loop, so it should be fine for InstaSPIN-FOC if this variable is updated in background loop. You can execute this function in ISR if you use the spinTAC.
Maybe my question was not clear enough.
The function to set the speed reference used by the SpinTAC Velocity Controller (which runs in the motor control ISR) is the following:
void CTRL_setSpd_ref_krpm(CTRL_Handle handle,const _iq spd_ref_krpm) { CTRL_Obj *obj = (CTRL_Obj *)handle; _iq krpm_to_pu_sf = EST_get_krpm_to_pu_sf(obj->estHandle); _iq spd_ref_pu = _IQmpy(spd_ref_krpm,krpm_to_pu_sf); obj->spd_ref = spd_ref_pu; return; } // end of CTRL_setSpd_ref_krpm() function
Since this function run in the background code, it could be a situation where the motor control ISR occurs during instruction obj->spd_ref = spd_ref_pu which could potentially corrupt spd_ref (this variable is used by the SpinTAC Velocity Controller).
SDW_Q2: Am I right?
SDW_Q3: How can I protect this access?
Q5. You might enable nesting interrupt after these variables are updated.
SDW_Q4: You mean that, before each EINT, I need to write this instructions, right?
IER |= 0x0001;
IER &= 0x0001;
asm(
" NOP"
);
Q1: Yes, you can only use _fpu32.lib if you enable the FPU32 support (--float_support=fpu32), otherwise, you need to use the non-fpu32 library.
Q2: The data/pointer type of the arguments of the function must be the same in its definition and calling code. It's not better to use the "volatile" variables as the arguments. Maybe the static and volatile definitions are necessary for almost all variables in a project.
Q3. Executing obj->spd_ref = spd_ref_pu just needs one CPU cycle if the variable is 32-bit. And the SpinTAC Velocity Controller doesn't use the reference value of obj->spd_ref directly, which uses the value from the speed trajectory (ctrlObj->trajHandle_spd). Of course, the target value of the speed trajectory is the obj->spd_ref.
Q5. Maybe, you don't need to enable nesting interrupt immediately, which depends on the control logic you want. I don't think you need to add the EINT/DINT for theses code to read/write the variables if these variables are 32-bit and are defined in a struct object.
Any updates? It's been a week since we heard from you, so I am assuming you no longer need clarification for your question that will be marking this thread as closed. You can reply with a post or create a new thread if you have any further questions on this topic. Thanks.
So, if I summarize, writing 32-bit variables should be atomic - 1 CPU cycle - (as described in C2000 C28x Optimization Guide, Section 5.3).
Q1: Is this the same for reading a 32-bit variable? Is it atomic?
Q2: How can I ensure in assembly code that the access is atomic? Do you have any example?
Q1: Yes.
Q2: You have to check the related assembly instructions or codes to ensure that. Or you might use EINT&DINT if there are not too many variables for reading/writing and you don't want to check it.