cancel
Showing results for 
Search instead for 
Did you mean: 

Converting ADC Bit Value to q1.15 Current in "per Unit"?

Daniel Studer
Associate II

Hi,

I'm reviewing a code for PMSM motor control with FOC. The library used is an older one and described in UM0492 "STM32F103xx permanent-magnet synchronous motor FOC software library V2.0" (attached file)

I'm wondering how the software know what current is flowing through motor.

There is one define MAX_CURRENT, which does not change any current value when changing this constant.

The current read through an ICS is just the ADC raw bit value, but in documentation it says this is q1.15 format in "per Unit". (File stm32f10x_svpwm_ics.c, function SVPWM_IcsGetPhaseCurrentValues). I can't see any conversion to correct per Unit value. Measuring current with an oscilloscope and looking to variables current does not seem to be correct.

Where is this conversion made in code to per Unit value?

Thanks for any help.

Regards,

Dani

4 REPLIES 4
Ozone
Lead II

Such conversion functions used to be part of the CMSIS DSP library, originating from ARM (and adapted by the vendor).

Not sure if and where this DSP code ended up in the current "Cube" mania.

But for the F103, you can still download "old" DSP libs and SPL-based firmware libs.

As a last resort, you could code it yourself.

cedric h1
Associate III

Hello Daniel,

I am not sure I fully understand your question, but I will try to give you some clues.

Note that my analysis is done based on the 5.3 source code. I do not know the 2.0 version, and how far it is from the 5.3.

"I'm wondering how the software know what current is flowing through motor."

No magic here, the current is measured with an ADC. ADC voltage value is proportional to current flowing to each phases.

The currents read from the hardware are read inside PWMC_GetPhaseCurrents function called from FOC_CurrController:

inline uint16_t FOC_CurrController(uint8_t bMotor)

{

 Curr_Components Iab, Ialphabeta, Iqd;

 Volt_Components Valphabeta, Vqd;

 int16_t hElAngle;

 uint16_t hCodeError;

 hElAngle = SPD_GetElAngle(STC_GetSpeedSensor(pSTC[bMotor]));

 PWMC_GetPhaseCurrents(pwmcHandle[bMotor], &Iab);

 Ialphabeta = MCM_Clarke(Iab);

 Iqd = MCM_Park(Ialphabeta, hElAngle);

 Vqd.qV_Component1 = PI_Controller(pPIDIq[bMotor],

           (int32_t)(FOCVars[bMotor].Iqdref.qI_Component1) - Iqd.qI_Component1);

 Vqd.qV_Component2 = PI_Controller(pPIDId[bMotor],

           (int32_t)(FOCVars[bMotor].Iqdref.qI_Component2) - Iqd.qI_Component2);

 FOCVars[bMotor].Vqd = Vqd;

 Vqd = Circle_Limitation(pCLM[bMotor], Vqd);

 Valphabeta = MCM_Rev_Park(Vqd, hElAngle);

 hCodeError = PWMC_SetPhaseVoltage(pwmcHandle[bMotor], Valphabeta);

 FOCVars[bMotor].Iab = Iab;

 FOCVars[bMotor].Ialphabeta = Ialphabeta;

 FOCVars[bMotor].Iqd = Iqd;

 FOCVars[bMotor].Valphabeta = Valphabeta;

 FOCVars[bMotor].hElAngle = hElAngle;

 return(hCodeError);

}

Currents read are stored inside Iab structure. Ia and Ib can be easily send to an oscilloscope thanks to the DAC. The MotorControl monitor allows you to select which information you send to the DAC in the advanced tab.

From this Raw Iab current, we do mathematical transformation into Torque and Flux (Iq and Id) thanks to Clarke and Park transforms.

Iq and Id are then compared to Iqdref, and the error is the entry of the current PID controller.

IqdRef is computed inside the speed regulation loop.

IqdRef.qI_Component1 = STC_CalcTorqueReference( pSTC[M1] );

The torque computation depends on the control mode. In speed mode, the torque is the output of the speed PID. The entry of the PID is the speed delta error.

In torque mode, the iqdref can be set by the motor control monitor or by the MC_ProgramTorqueRampMotor1( int16_t hFinalTorque, uint16_t hDurationms ) API.

In both cases, the current input is expressed in digit.

During open loop start, the reference torque is computed by the workbench based on the different values set in the start-up parameters. The current conversion from amps to digit is done by the workbench.

Hope it helps

Cedric

If you agree with my answer, please accept it by clicking on 'Accept as solution'."

Hi Cedric,

Thanks for your reply.

So I understand, that ADC voltage is proportional to current flowing through phases.

I just can't see, how the library does conversion from ADC voltage to q1.15 "per Unit" value.

Function "SVPWM_IcsGetPhaseCurrentValues()" gets the ADC voltage in bits directliy from ADC register and subtracts offsets (bit value) all left aligned:

wAux = ((ADC1->JDR1)<<1)-(s32)(hPhaseAOffset);

Local_Stator_Currents.qI_Component1= wAux;

This value is then stored in variables for further claculations (clarke, park transformation).

 Stat_Curr_a_b = SVPWM_IcsGetPhaseCurrentValues();

 Stat_Curr_alfa_beta = Clarke(Stat_Curr_a_b);

 Stat_Curr_q_d = Park(Stat_Curr_alfa_beta,GET_ELECTRICAL_ANGLE);

But I cant see any conversion or calculation to "per Unit" value. This is just the ADC bit value. (As I can see, this is the same in version 5.3.3).

Unfortunately, I cant use DAC because our design does not support that. But I store values in arrays and watch them by debugger and graphs.

How does the library then knows what current is corresponding to what ADC bit value?

Regards,

Dani

cedric h1
Associate III

Hello Daniel,

There is actually no conversion. The target torque is already expressed in digit.

Cedric

If you agree with my answer, please accept it by clicking on 'Accept as solution'."