Hallo zusammen,
der Beitrag ist recht lang geworden, es würde mich trotzdem freuen, wenn sich jemand die Zeit nimmt ihn zu lesen.
Ich versuche gerade einen BLDC-Regler mit feldorientierter Regelung und
singleshunt Strommessung aufzubauen. Allerdings stellen mich die
Messergebnisse nicht ganz zufrieden. Vielleicht hat ja jemand Erfahrung
damit und kann mir dabei etwas helfen. Für die Strommessung verwende ich
einen ACS709 Hall-Effekt-Stromsensor. Im Anhang mal ein Screenshot, wie
die Ströme aktuell aussehen.
Die Ströme werden zweimal pro PWM-Periode gemessen und über das
Kirchhoffsches Gesetz der jeweils fehlende dritte Strom berechnet.
Des Prinzip wird auch hier nochmals erklärt: http://ww1.microchip.com/downloads/e...tes/01299A.pdf
Ich weiß nun nicht, ob der Fehler bereits in der Hardware liegt oder in meiner Software.
Als Mikrocontroller verwende ich einen STM32F103C8.
Timer 1 verwende ich zu PWM Erzeugung. Dieser zählt von 0 bis 2048 und
dann wieder runter auf 0. Timer 3 läuft synchron zählt aber von 0 bis
4095. Damit triggere ich den ADC. Dabei wird in der ISR von Timer 3 der
neue Vergleichswert für die Zweite triggerung des ADCs geladen. In der
ADC-ISR wird dann wieder der Wert für die erste Triggerung geladen.
Init des Timer 3:
Code:
void ADC_Timer_ini()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 4095;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_UpdateRequestConfig(TIM3, TIM_UpdateSource_Global);
TIM_UpdateDisableConfig(TIM3,DISABLE);
TIM_Cmd(TIM3, ENABLE);
}
void ADC_Timer_Compare_ini()
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_Pulse = 2048;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = 1024;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = 3072;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);
TIM_DMACmd(TIM3, TIM_DMA_CC3, ENABLE);
}
void ADC_Timer_Interrupt_ini()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE);
}
Ini des Timer 1:
Code:
void PWMTimer_ini()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
TIM_TimeBaseStructure.TIM_Period = 2048;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM1->CR1 |= TIM_CR1_URS; // Nur Over- bzw. Underflow erzeugt Update event
TIM_UpdateRequestConfig(TIM1, TIM_UpdateSource_Regular);
TIM_UpdateDisableConfig(TIM1,DISABLE);
TIM_Cmd(TIM1, ENABLE);
TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_3Transfers);
}
void PWMChannel_ini()
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM1->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // Compare-Werte erst beim Update-Event übernehmen
TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Compare-Werte erst beim Update-Event übernehmen
}
ADC Ini:
Code:
void ADC_2_ini(void)
{
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC Takt max 14MHz. 72MHz/6 = 12MHz
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADC2, &ADC_InitStructure);
ADC_InjectedSequencerLengthConfig(ADC2, 2);
ADC_InjectedChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);
ADC_InjectedChannelConfig(ADC2, ADC_Channel_1, 2, ADC_SampleTime_7Cycles5);
ADC_InjectedDiscModeCmd(ADC2, ENABLE);
ADC_ExternalTrigInjectedConvCmd(ADC2, ENABLE);
ADC_ExternalTrigInjectedConvConfig(ADC2, ADC_ExternalTrigInjecConv_T3_CC4);
ADC_Cmd(ADC2, ENABLE);
// Starte Kalibierung
ADC_ResetCalibration(ADC2);
while(ADC_GetResetCalibrationStatus(ADC2));
ADC_StartCalibration(ADC2);
while(ADC_GetCalibrationStatus(ADC2));
}
void ADC_2_Interrupt_ini()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_ITConfig(ADC2, ADC_IT_JEOC, ENABLE);
}
ISR Timer 3:
Code:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC4))
{
GPIOB->ODR ^= GPIO_Pin_6;
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
TIM3->CCR4 = pwm_aktuell.adc_comp.adc_triggerung_2;
}
}
ISR ADC:
Code:
void ADC1_2_IRQHandler(void)
{
ADC_ClearITPendingBit(ADC2, ADC_IT_JEOC);
ADC_Strom_1 = ADC_GetInjectedConversionValue(ADC2, ADC_InjectedChannel_1);
ADC_Strom_2 = ADC_GetInjectedConversionValue(ADC2, ADC_InjectedChannel_2);
TIM3->CCR4 = pwm_aktuell.adc_comp.adc_triggerung_1;
flags |= adc_messung_fertig;
strom_auswerten();
}
Funktion zum Auswerten der Strommesswerte:
Code:
void strom_auswerten(void)
{
ACS_ref = gleitender_mittelwert(ACS_ref, ADCBuffer[0], 3);
ADC_Strom_1 -= ACS_ref + ACS_0_Strom_offset;
ADC_Strom_2 -= ACS_ref + ACS_0_Strom_offset;
ADC_Strom_1 = ADC_Strom_1 << 3;
ADC_Strom_2 = ADC_Strom_2 << 3;
if(flags & erste_adc_messung )
{
flags &= ~erste_adc_messung;
flags &= ~adc_messung_fertig;
}
else
{
flags &= ~adc_messung_fertig;
if(counter==2)
{
if(array_counter==2000)
{
asm("nop");
}
else
{
counter = 0;
I_U_array[array_counter] = I_U_flt;//I_U_flt;
I_V_array[array_counter] = I_V_flt;//I_V_flt;
I_W_array[array_counter] = I_W_flt;
//I_U_array[array_counter] = ADC_Strom_1;//I_U_flt;
//I_V_array[array_counter] = - ADC_Strom_2;//I_V_flt;
//I_W_array[array_counter] = -ADC_Strom_1 + ADC_Strom_2;
teta_array[array_counter++] = teta;
}
}
else
{
counter += 1;
}
switch (sektor)
{
case 1: // 0- 60°
{
I_W_raw = - ADC_Strom_2;
I_V_raw = ADC_Strom_1;
I_U_raw = - I_V_raw - I_W_raw;
break;
}
case 2: // 60-120°
{
I_U_raw = -ADC_Strom_2;
I_W_raw = ADC_Strom_1;
I_V_raw = (- I_W_raw - I_U_raw);
break;
}
case 3: // 120-180°
{
I_U_raw = - ADC_Strom_2;
I_W_raw = ADC_Strom_1;
I_V_raw = - I_U_raw - I_W_raw;
break;
}
case 4: //180-240°
{
I_U_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_V_raw = - I_W_raw - I_U_raw;
break;
}
case 5: //240-300°
{
I_U_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_V_raw = - I_W_raw - I_U_raw;
break;
}
case 6: //300-360°
{
I_V_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_U_raw = - I_V_raw - I_W_raw;
break;
}
default: break;
}
}
I_U_flt = gleitender_mittelwert(I_U_flt, I_U_raw, filter_gewichtung_strom);
I_V_flt = gleitender_mittelwert(I_V_flt, I_V_raw, filter_gewichtung_strom);
I_W_flt = gleitender_mittelwert(I_W_flt, I_W_raw, filter_gewichtung_strom);
return;
}
Ich habe nun echt schon viel versucht und gemessen. Wenn ich zum Beispiel nur einen Wechselstrom durch den Hall-Sensor schicken, wird dieser auch korrekt gemessen:
Nur die Messung in Kombination mit dem SVPWM Modul funktioniert überhaupt nicht Mir fällt nichts mehr ein was ich noch versuchen kann. Deshalb wäre ich über eure Hilfe sehr dankbar.
Viele Grüße und vielen Dank im Voraus Michael
Lesezeichen