MC9S12ZVML128_BLDC_Sensorless adding ADC

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MC9S12ZVML128_BLDC_Sensorless adding ADC

Jump to solution
3,573 Views
sohyunjang
Contributor III

Hi.

I use MC9S12ZVML128_BLDC_Sensorless example project.

I want to add ADC (AN0_3(position sensor (sin)), AN1_3(position sensor (cos)).

And then, I modified a code.

PR_SECTION(ptuTrigE)
  volatile short PTUTriggerEventList[PTU_TOT_TRIGGERs_GEN_NO][PTU_TOT_LISTS_NO][4] = {
                                              {   /*DelayT0 */
                                                  {0x0100,0x0200,0x0300,0x0000},{0x0000,0x0000,0x0000,0x0000}
                                         
                                              },
                                              {
                                                {0x0100,0x0200,0x0000,0x0000},{0x0000,0x0000,0x0000,0x0000}
                                               
                                              }
                                            };

volatile char ADC0CommandList[6][4] = {
   {0x40,0xD0,0x00,0x00}, // current sense channel, end of sequence [D0]
   {0x40,0xCB,0x00,0x00}, // end of list + no int [C0], HD voltage [CB], 4 clock cycles sample time [00], reserved [00]
   {0xC0,0xD3,0x00,0x00},
   {0x00,0x00,0x00,0x00},
   {0x00,0x00,0x00,0x00},
      {0x00,0x00,0x00,0x00}
};

PR_SECTION(adcLists)
  volatile char ADC1CommandList[6][4] = {
   {0x40,0xCA,0x00,0x00}, // [end of list] + no int [C0], phase voltage [CA], 4 clock cycles sample time [00], reserved [00]
   {0xC0,0xD3,0x00,0x00},
   {0x00,0x00,0x00,0x00},
   {0x00,0x00,0x00,0x00},
   {0x00,0x00,0x00,0x00},
      {0x00,0x00,0x00,0x00}
  };

Getting ADC value is OK, but BLDC motor is not ruuing.

how can I modify the code?

Labels (1)
Tags (2)
1 Solution
2,071 Views
pachamatej
NXP Employee
NXP Employee

Hello,

You did it well, however, there is one thing you need to do to make it run. You have set up both the trigger list and the command lists correctly, but please note that the trigger list is intended to be changed in runtime based on the PWM edges. You may find the UpdateDutycycle() function, which updates triggers based on the PMFVAL0. Thanks to that, the DC link current is always measured in the center of the PWM pulse and the backEMF is sensed at a certain percentage of the duty cycle (see the formula in the code). In your code, these two values were updated correctly, but the additional triggers remained static, occasionally placed before the backEMF triggers, which resulted in misplaced measurement of backEMF. What you need to do is to update your additional triggers with a new value as follows:

Code
void UpdateDutycycle(void)
{
tU16 delay1, delay2;

PMFVAL0 = duty_cycle;

delay1 = duty_cycle >> 2; // middle of the pulse - DC Bus current
 if (delay1 < MIN_ADC_TRIGGER_FIRST) delay1 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[0][0][0] = delay1;

delay2 = MLIB_Mul(duty_cycle,BEMF_SENSING_POINT_FRAC,F16); // end of the pulse
 PTUTriggerEventList[1][0][0] = delay2; // ADC1 - phase voltage

if (delay2 < (delay1 + MIN_ADC_TRIGGER_SECOND)) delay2 = delay1 + MIN_ADC_TRIGGER_SECOND;
 PTUTriggerEventList[0][0][1] = delay2; // ADC0 second sample - DC Bus Voltage

 PTUTriggerEventList[0][0][2] = delay2 + 35; // Update trigger for resolver sin-channel
 PTUTriggerEventList[1][0][1] = delay2 + 35; // Update trigger for resolver cos-channel

PTUC_PTULDOK = 1;
}

Please note that the " + 35" is directly connected with the bus frequency and it should be at least 2.5 us @ 8.3 MHz sampling frequency as required by ADC in terms of the time between two samples (see the reference manual for more details).

I hope it helps. In case of any additional questions, please place a comment.

Best regards,

Matej

View solution in original post

12 Replies
2,072 Views
pachamatej
NXP Employee
NXP Employee

Hello,

You did it well, however, there is one thing you need to do to make it run. You have set up both the trigger list and the command lists correctly, but please note that the trigger list is intended to be changed in runtime based on the PWM edges. You may find the UpdateDutycycle() function, which updates triggers based on the PMFVAL0. Thanks to that, the DC link current is always measured in the center of the PWM pulse and the backEMF is sensed at a certain percentage of the duty cycle (see the formula in the code). In your code, these two values were updated correctly, but the additional triggers remained static, occasionally placed before the backEMF triggers, which resulted in misplaced measurement of backEMF. What you need to do is to update your additional triggers with a new value as follows:

Code
void UpdateDutycycle(void)
{
tU16 delay1, delay2;

PMFVAL0 = duty_cycle;

delay1 = duty_cycle >> 2; // middle of the pulse - DC Bus current
 if (delay1 < MIN_ADC_TRIGGER_FIRST) delay1 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[0][0][0] = delay1;

delay2 = MLIB_Mul(duty_cycle,BEMF_SENSING_POINT_FRAC,F16); // end of the pulse
 PTUTriggerEventList[1][0][0] = delay2; // ADC1 - phase voltage

if (delay2 < (delay1 + MIN_ADC_TRIGGER_SECOND)) delay2 = delay1 + MIN_ADC_TRIGGER_SECOND;
 PTUTriggerEventList[0][0][1] = delay2; // ADC0 second sample - DC Bus Voltage

 PTUTriggerEventList[0][0][2] = delay2 + 35; // Update trigger for resolver sin-channel
 PTUTriggerEventList[1][0][1] = delay2 + 35; // Update trigger for resolver cos-channel

PTUC_PTULDOK = 1;
}

Please note that the " + 35" is directly connected with the bus frequency and it should be at least 2.5 us @ 8.3 MHz sampling frequency as required by ADC in terms of the time between two samples (see the reference manual for more details).

I hope it helps. In case of any additional questions, please place a comment.

Best regards,

Matej

2,071 Views
subinkim
Contributor II

Hi,

Can you explain more about the '+35' under here??

PTUTriggerEventList[0][0][2] = delay2 + 35; // Update trigger for resolver sin-channel
PTUTriggerEventList[1][0][1] = delay2 + 35; // Update trigger for resolver cos-channel

What's the meaning of this code and why is it '+35'??

I want to use ADC module to get AMP0 and AN1_0 in same example code.

My codes are like this,

void initGDU()

{
 GDUE_GCSE0 = 1; // enable Current Sense Amplifier 0

}

void initGPIO(void)
{
 DDRADH_DDRADH0 = 1; /*AN1_3 set*/
 PTAD_PTADH0 = 1;

}

volatile short PTUTriggerEventList[PTU_TOT_TRIGGERs_GEN_NO][PTU_TOT_LISTS_NO][4] ={
  { /*DelayT0 */
    {0x0100, 0x0200, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000, 0x0000}
  },
  {
    {0x0100, 0x0200, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000, 0x0000}
  }
};

volatile char ADC0CommandList[6][4] = {
  { 0x40, 0xD0, 0x00, 0x00 }, // current sense channel, end of sequence [D0]
  { 0xC0, 0xCB, 0x00, 0x00 }, // end of list + no int [C0], HD voltage [CB], 4 clock cycles sample time [00], reserved [00]
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 } };
PR_SECTION(adcLists)

volatile char ADC1CommandList[6][4] = {
  { 0x40, 0xCA, 0x00, 0x00 }, // [end of list] + no int [C0], phase voltage [CA], 4 clock cycles sample time [00], reserved [00]
  { 0xC0, 0x50, 0x00, 0x00 }, // ADC1 AN1_0
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 } };

volatile unsigned short ADC0ResultList[6] = { 0, 0, 0, 0, 0, 0 };
volatile unsigned short ADC1ResultList[6] = { 0, 0, 0, 0, 0, 0 };

void UpdateDutycycle(void) {
 tU16 delay1, delay2;

 PMFVAL0 = duty_cycle;

 delay1 = duty_cycle >> 2;    // middle of the pulse - DC Bus current
 if (delay1 < MIN_ADC_TRIGGER_FIRST)
  delay1 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[0][0][0] = delay1;

 delay2 = MLIB_Mul(duty_cycle,BEMF_SENSING_POINT_FRAC,F16); // end of the pulse
 if (delay2 < MIN_ADC_TRIGGER_FIRST)
  delay2 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[1][0][0] = delay2;     // ADC1 - phase voltage

 if (delay2 < (delay1 + MIN_ADC_TRIGGER_SECOND))
  delay2 = delay1 + MIN_ADC_TRIGGER_SECOND;
 PTUTriggerEventList[0][0][1] = delay2; // ADC0 second sample - DC Bus Voltage

 
 PTUTriggerEventList[1][0][1] = delay2+35;

 PTUC_PTULDOK = 1;
}

It looks like that the ADC values are measured, but the BLDC motor is not rotating normally(repeat stop and go).

what's the problem of my code?? I beg your opinion.

0 Kudos
2,071 Views
pachamatej
NXP Employee
NXP Employee

Hello,

please see the comment in my first response:

Please note that the " + 35" is directly connected with the bus frequency and it should be at least 2.5 us @ 8.3 MHz sampling frequency as required by ADC in terms of the time between two samples (see the reference manual for more details).

It is related to a mandatory delay between two samples of ADC, which should be at least 2.5us if 12bit conversion and 8.3MHz clock is used with ADC.

 

If the motor is not running, please test it step by step from the original code, through the changes to the final code. If the reason is the ADC error, then the timing of the ADC sampling should checked and changed. Please use the PTU trigger debug signals to investigate the distance between triggers (see PTUDEBUG description in the reference manual).

Please let me know if it helps.

Matej

0 Kudos
2,071 Views
sohyunjang
Contributor III

pachamatej

Thanks your comment,

I running MC9S12ZVML128_BLDC_Sensorless adding ADC by bleow setting.

PR_SECTION(adcLists)volatile char ADC0CommandList[6][4] = { 

  { 0x40, 0xD0, 0x00, 0x00 }, // cu{ 0x40, 0xD0, 0x00, 0x00 }, // current sense channel, end of sequence [D0]rrent sense channel, end of sequence [D0]
  { 0x00, 0xCB, 0x00, 0x00 }, // end of list + no int [C0], HD voltage [CB], 4 clock cycles sample time [00], reserved [00]
  { 0xC0, 0xD3, 0x00, 0x00 }, //Linear
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 }};

PR_SECTION(adcLists)volatile char ADC1CommandList[6][4] = { 
  { 0x00, 0xCA, 0x00, 0x00 }, // [end of list] + no int [C0], phase voltage [CA], 4 clock cycles sample time [00], reserved [00]
  { 0x00, 0xD0, 0x00, 0x00 }, //압력 D0
  { 0x00, 0xD1, 0x00, 0x00 }, //온도 D1
  { 0xC0, 0xD3, 0x00, 0x00 }, //Linear
  { 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00 }};

PR_SECTION(ptuTrigE)volatile short PTUTriggerEventList[PTU_TOT_TRIGGERs_GEN_NO][PTU_TOT_LISTS_NO][4] =
  { { /*DelayT0 */
  { 0x0100, 0x0200, 0x0000, 0x0000 }, { 0x0000, 0x0000, 0x0000, 0x0000 } },
  { { 0x0100,0x0000, 0x0000, 0x0000 }, { 0x0000, 0x0000, 0x0000, 0x0000 } } };

Motor running is OK.

But, there are problem when motor is not runnig, stop status.

I can only get ADC chennel 0 while motor is stop status.

I cannot get ADC chennel 1 while motor is not running.

what is problem?

how can I fix the code?

I wait your comment.

0 Kudos
2,071 Views
pachamatej
NXP Employee
NXP Employee

Hello,

The issue is clear to me. The thing is, the demo code was created long time ago and there has been some workaround to suppress the ADC error when the motor is stopped. In this mode, the ADC1 result is not needed in the demo code.

Let me describe what is happening:

The ADC is triggered by the PTU based on the duty cycle setting in UpdateDutycycle() function.

Before
void UpdateDutycycle(void)
{
 tU16 delay1, delay2;
 PMFVAL0 = duty_cycle; // Set PMF value0 duty cycle sets value1 to value5 as well
 
 // Trigger calculation to capture DC bus current
 delay1 = duty_cycle >> 2; // middle of the pulse - DC Bus current
 if (delay1 < MIN_ADC_TRIGGER_FIRST)
     delay1 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[0][0][0] = delay1;
 
 // Trigger calculation to capture phase voltage
 delay2 = MLIB_Mul(duty_cycle,BEMF_SENSING_POINT_FRAC,F16); // end of the pulse
 PTUTriggerEventList[1][0][0] = delay2; // ADC1 - phase voltage
 
 // Trigger calculation to capture DC bus voltage
 if (delay2 < (delay1 + MIN_ADC_TRIGGER_SECOND))
     delay2 = delay1 + MIN_ADC_TRIGGER_SECOND;
 PTUTriggerEventList[0][0][1] = delay2; // ADC0 second sample - DC Bus Voltage
 PTUC_PTULDOK = 1; // Reload PTU triggers
}

The very first PTU trigger should be at least 16 PTU ticks to let the PTU and ADC settle after the restart. If this condition is not met, the ADC RSTAR error occurs.

The ADC0 triggering is already protected from a very low values - see "Before", line 8-9 and 17-18. However, the ADC1 triggering is not protected and the trigger = 0 occurs whenever the duty cycle is zero.

To fix this issue, the code should look like the following (see lines 14-15):

After
void UpdateDutycycle(void)
{
 tU16 delay1, delay2;
 PMFVAL0 = duty_cycle; // Set PMF value0 duty cycle sets value1 to value5 as well
 
 // Trigger calculation to capture DC bus current
 delay1 = duty_cycle >> 2; // middle of the pulse - DC Bus current
 if (delay1 < MIN_ADC_TRIGGER_FIRST)
     delay1 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[0][0][0] = delay1;
 
 // Trigger calculation to capture phase voltage
 delay2 = MLIB_Mul(duty_cycle,BEMF_SENSING_POINT_FRAC,F16); // end of the pulse
 if(delay2 < MIN_ADC_TRIGGER_FIRST)
    delay2 = MIN_ADC_TRIGGER_FIRST;
 PTUTriggerEventList[1][0][0] = delay2; // ADC1 - phase voltage
 
 // Trigger calculation to capture DC bus voltage
 if (delay2 < (delay1 + MIN_ADC_TRIGGER_SECOND))
     delay2 = delay1 + MIN_ADC_TRIGGER_SECOND;
 PTUTriggerEventList[0][0][1] = delay2; // ADC0 second sample - DC Bus Voltage

 PTUC_PTULDOK = 1; // Reload PTU triggers
}

I hope it will help. Please let me know how it goes.

Best regards,

Matej

0 Kudos
2,071 Views
sohyunjang
Contributor III

Hi,

I try the way that you recomand.

But, It doesn't work.  I get only ADC ch 0.

So, I solve the problem by other way.

I add the code below.

void AppStop(void)

{

   ADC0FLWCTL_TRIG = 1;

}

After adding the code, I get ADC ch 0 and ch 1 when motor is stop state.

But, I dont know it is appropriate way.

I want your comment.

Thank you.

0 Kudos
2,071 Views
pachamatej
NXP Employee
NXP Employee

Hi,

In my opinion, once it works with no consequences, it's ok to do it. However, it's always a good idea to watch for errors. Please enable the ADC error interrupts in the initADC()

ADC1EIE = 0xEE;      // enable all error interrupts‍

and watch for the AdcErrorXXXX variables. If they are not changing, then you did well.

Just a note: I have tried my previous solution and it works. Maybe we have a different configuration - my is based on the original BLDC devKit software.

Regards,

Matej

0 Kudos
2,071 Views
sohyunjang
Contributor III

Thank you for your answer.

So, I have one more question.

I also use MTRCKTSPNZVM128 (single shunt, AN5327) SW.

and I use same way to add adc value (AN0_1(sensor1), AN0_3(position sensor (sin)), AN1_3(position sensor (cos), AN1_2(sensor)).

and then, I modified code :

PR_SECTION(adcLists)
  volatile char ADC0CommandList[COMMAND_NO][COMMAND_LENGTH] = {
   {0x40,0xD0,0x00,0x00}, // end of sequence [40], current sense channel [D0] - dc bus current on op-amp0
   {0x40,0xD0,0x00,0x00}, // end of sequence [40], current sense channel [D0] - dc bus current on op-amp0
   {0x40,0xD0,0x00,0x00}, // end of sequence [40], current sense channel [D0] - dc bus current on op-amp0
   {0x40,0xD0,0x00,0x00}, // end of list + no int [C0], current sense channel [D0] - dc bus current on op-amp0
   {0x40,0xD1,0x00,0x00}, // sensor1
   {0xC0,0xD30x00,0x00}, // position sensor (sin)
   {0x00,0x00,0x00,0x00},
      {0x00,0x00,0x00,0x00}
  };

  volatile char ADC1CommandList[COMMAND_NO][COMMAND_LENGTH] = {
   {0x40,0xCB,0x00,0x00}, // end of sequence + no int [40], DC-Link VOltage
   {0x40,0xC9,0x00,0x00}, // end of List + no int [C0], TEMP [C9], 4 clock cycles sample time [00], reserved [00] 
   {0x40,0xD2,0x00,0x00}, //sensor2
   {0xC0,0xD3,0x00,0x00}, // position sensor (cos)
   {0x00,0x00,0x00,0x00}, 
   {0x00,0x00,0x00,0x00},
   {0x00,0x00,0x00,0x00},
   {0x00,0x00,0x00,0x00}
  };

volatile short ptuTriggerList0[PTU_LISTS_NO][7] =  {{125,250,375,500,625, 750, 0x0000},{200,400,600,800, 1000, 1200, 0x0000}}; // !_! for 50 MHz bus clock
volatile short ptuTriggerList1[PTU_LISTS_NO][7] =  {{150,500, 600, 700, 0x0000,0x0000,0x0000},{150,500, 600, 700,0x0000,0x0000,0x0000}};

is it right? there ara ADC fault.

How can I change the code additionally?

Waiting for your answer.

Th

0 Kudos
2,070 Views
sohyunjang
Contributor III

thanks your commend.

But, there are ADC Interrupt Flag problem.

ADC 0 Error Interrupt Flag Register (ADCEIF)


 RSTAR_EIF bits[  2:2  ] = 1 Restart request error situation occurred.

ADC 0 Interrupt Flag Register (ADCIF)

 SEQAD_IF  bits[  7:7  ] = 1 A conversion sequence abort request occurred.

ADC 1 Error Interrupt Flag Register (ADCEIF)


 TRIG_EIF  bits[  3:3  ] = 1 A trigger error occurred.

It is very helpfult to your answer.

Thank you.

Waiting for your answer.

0 Kudos
2,070 Views
pachamatej
NXP Employee
NXP Employee

Hello,

these error flags indicate that the sequence of ADC conversions is longer than planned by the ADC-command-list-restart event. In other words, if you put additional commands after the last PTU trigger, it might happen that these additional triggers are placed after the PWM reload, when a new set of triggers is loaded. If a restart event occurs while the sequence is not finished, the RSTAR_EIF is set and the sequence is aborted indicated by SEQAD_IF.

Please use FreeMASTER or in-the-code watch for maximum of the last trigger in the list. It should be less than 2500 (which is the period of PWM).

Regarding the ADC1 TRIG_EIF, it usually occurs if a new trigger is asserted while previous conversion is still ongoing. I can see that your triggers in the ptuTriggerList1 are only 100 ptu ticks apart. For 50 MHz bus and 8.33 MHz ADC clock with 12bit conversion, the maximal ADC conversion time is 2.5us, in terms of PTU ticks it is 125. Please set the PTU triggers as follows:

PTU trigger list 1
volatile short ptuTriggerList1[PTU_LISTS_NO][PTU_COMMANDS] =  {{150,500, 625, 750, 0x0000,0x0000,0x0000},{150,500, 625, 750, 0x0000,0x0000,0x0000}};

Best regards

Matej

2,070 Views
sohyunjang
Contributor III

So, if I don't want to modify the motor runing algorithm,

Can I modify HW :

AN0_1(sensor1), AN0_3(position sensor (sin)) -> AN1 ch ?

thank you.

0 Kudos
2,070 Views
pachamatej
NXP Employee
NXP Employee

If you are using our S12ZVMLEVBLIN (or devKit), it is easy to reroute the signals to the ADC1. For example POS_SIN can be routed to PAD7 (jumper J46 and J51) and POS_COS (jumper J50 and J52) can be routed to PAD8. Then, you have to set the triggers for ADC1 (ADC0 changes will be reverted to the original version).

Since the SIN and COS signals will be sampled with a delay of 2.5us, some error in the position sensing will be introduced. However, this error should not be significant to the control algorithm (depending on the motor properties and the control dynamics).

Best regards

Matej