New approach to MPP tracking

Test multiple points for PWM, switch to the one which gives the most
power, repeat. If two consecutive tests give the same point, hold that
for some time or until power changes too much.
This commit is contained in:
Thomas Kolb 2016-08-18 22:05:18 +02:00
parent 473253f5ab
commit 9dc1ab928c
2 changed files with 146 additions and 110 deletions

2
fxplib

@ -1 +1 @@
Subproject commit 035e7ecda497b408a5791ba20a0906b913dfef45
Subproject commit 3bd86806d72a6caed2b604a0ba4c6bf344f72b1a

View File

@ -325,22 +325,30 @@ static void init_systick(int freq)
}
#endif
static void report_status(fxp_t vin, fxp_t vout, fxp_t current,
struct PowerState {
fxp_t vin, vin_avg;
fxp_t vout, vout_avg;
fxp_t current, current_avg;
fxp_t power_avg;
};
static void report_status(struct PowerState *power_state,
int32_t pwm, enum OperState operState)
{
char number[FXP_STR_MAXLEN];
debug_send_string("DATA:");
fxp_format(vin, number, 3);
fxp_format(power_state->vin, number, 3);
debug_send_string(number);
debug_send_string(":");
fxp_format(vout, number, 3);
fxp_format(power_state->vout, number, 3);
debug_send_string(number);
debug_send_string(":");
fxp_format(current, number, 3);
fxp_format(power_state->current, number, 3);
debug_send_string(number);
debug_send_string(":");
@ -353,6 +361,94 @@ static void report_status(fxp_t vin, fxp_t vout, fxp_t current,
debug_send_string("\r\n");
}
fxp_t MPP_MAX_POWER_CHANGE_FACTOR;
struct MPPState {
int32_t testIdx; /* -1, if test requested, 0..MPP_TEST_STEPS-1 if running, >=MPP_TEST_STEPS if finished */
uint32_t nextTestStepTime;
fxp_t refPower;
int32_t refPWM;
fxp_t maxPower;
int32_t maxPWM;
};
#define MPP_TEST_DURATION 25 /* ms */
#define MPP_TEST_STEPS 4
const int32_t mpp_pwm_offsets[MPP_TEST_STEPS] = {-51, -4, 5, 50};
static void mpp_run(
uint32_t time_in_state, struct MPPState *mpp_state,
struct PowerState *power_state, int32_t *pwm)
{
fxp_t power_change;
fxp_t power_change_factor;
if(mpp_state->testIdx == -1) {
/* initiate new test */
mpp_state->refPower = power_state->power_avg;
mpp_state->nextTestStepTime = time_in_state + MPP_TEST_DURATION; /* time to give averagers to settle */
mpp_state->testIdx = 0;
mpp_state->refPWM = *pwm;
mpp_state->maxPower = 0;
mpp_state->maxPWM = *pwm;
*pwm = *pwm + mpp_pwm_offsets[0];
} else if(mpp_state->testIdx < MPP_TEST_STEPS) {
/* test running */
if(time_in_state > mpp_state->nextTestStepTime) {
if(power_state->power_avg > mpp_state->maxPower) {
mpp_state->maxPower = power_state->power_avg;
mpp_state->maxPWM = *pwm;
}
mpp_state->testIdx++;
if(mpp_state->testIdx < MPP_TEST_STEPS) {
*pwm = mpp_state->refPWM + mpp_pwm_offsets[mpp_state->testIdx];
mpp_state->nextTestStepTime = time_in_state + MPP_TEST_DURATION;
}
}
} else if(mpp_state->testIdx == MPP_TEST_STEPS) {
/* finalize test */
if(mpp_state->maxPower > mpp_state->refPower) {
/* better PWM value found -> initiate new test to reach MPP */
mpp_state->testIdx = -1;
mpp_state->refPower = mpp_state->maxPower;
*pwm = mpp_state->maxPWM;
} else {
/* We were already at the maximum power point */
*pwm = mpp_state->refPWM;
/* test again after some time */
mpp_state->testIdx++;
mpp_state->nextTestStepTime = time_in_state + 5000;
}
} else {
/* no test active, just holding PWM */
/* initiate new test after defined time */
if(time_in_state > mpp_state->nextTestStepTime) {
mpp_state->testIdx = -1;
}
/* initiate new test if power changes too much */
power_change = fxp_abs(fxp_sub(mpp_state->refPower, power_state->power_avg));
power_change_factor = fxp_div(power_change, mpp_state->refPower);
if(power_change_factor > MPP_MAX_POWER_CHANGE_FACTOR) {
mpp_state->testIdx = -1;
}
}
}
int main(void)
{
uint32_t cpuload = 0;
@ -362,27 +458,18 @@ int main(void)
char number[FXP_STR_MAXLEN];
uint8_t sentSomething = 0;
fxp_t prevPower = 0;
int32_t prevPWM = 0;
int32_t pwm = 0;
int32_t mpp_step = 5;
enum OperState operState = Bootstrap;
enum OperState nextState = ConvConstVoltage;
enum OperState lastState = operState;
fxp_t vin, vout, current;
fxp_t vin_avg = 0, vout_avg = 0, current_avg = 0;
struct PowerState power_state;
struct MPPState mpp_state;
fxp_t pErr = 0, iErr = 0;
fxp_t controlAction = 0;
fxp_t power_avg;
fxp_t testPower[2];
int32_t testPWM[2];
int testPWMStep = 5;
uint32_t next_power_test = 0;
uint32_t sleep_time = 10;
uint64_t force_display_update_time = 1000;
@ -420,6 +507,13 @@ int main(void)
fxp_t CURRENT_OFFSET = fxp_from_float(0.049);
/* if power changes by more than this factor, MPP is tested again */
MPP_MAX_POWER_CHANGE_FACTOR = fxp_from_float(0.1f);
power_state.vin_avg = 0;
power_state.vout_avg = 0;
power_state.current_avg = 0;
init_clock();
init_rtc();
init_gpio();
@ -446,13 +540,13 @@ int main(void)
if(timebase_ms == force_display_update_time) {
lcd_set_cursor_pos(1, 0);
fxp_format(vin_avg, number, 1);
fxp_format(power_state.vin_avg, number, 1);
fxp_right_align(number, msg, 4, ' ');
lcd_send_string("I:");
lcd_send_string(msg);
lcd_send_string("V ");
fxp_format(vout_avg, number, 2);
fxp_format(power_state.vout_avg, number, 2);
fxp_right_align(number, msg, 5, ' ');
lcd_send_string("O:");
lcd_send_string(msg);
@ -460,7 +554,7 @@ int main(void)
lcd_set_cursor_pos(0, 10);
fxp_format(power_avg, number, 2);
fxp_format(power_state.power_avg, number, 2);
fxp_right_align(number, msg, 5, ' ');
lcd_send_string(msg);
lcd_send_string("W");
@ -488,25 +582,25 @@ int main(void)
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL1, DMA_TCIF);
// convert read values
vin = fxp_mult(fxp_from_int(adc_values[0]), VIN_SCALE);
vout = fxp_mult(fxp_from_int(adc_values[1]), VOUT_SCALE);
current = fxp_mult(fxp_from_int(adc_values[2]), CURRENT_SCALE);
current = fxp_sub(current, CURRENT_OFFSET);
power_state.vin = fxp_mult(fxp_from_int(adc_values[0]), VIN_SCALE);
power_state.vout = fxp_mult(fxp_from_int(adc_values[1]), VOUT_SCALE);
power_state.current = fxp_mult(fxp_from_int(adc_values[2]), CURRENT_SCALE);
power_state.current = fxp_sub(power_state.current, CURRENT_OFFSET);
vin_avg = fxp_add(fxp_mult(vin, AVG_FACT), fxp_mult(vin_avg, AVG_FACT_INV));
vout_avg = fxp_add(fxp_mult(vout, AVG_FACT), fxp_mult(vout_avg, AVG_FACT_INV));
current_avg = fxp_add(fxp_mult(current, AVG_FACT), fxp_mult(current_avg, AVG_FACT_INV));
power_state.vin_avg = fxp_add(fxp_mult(power_state.vin, AVG_FACT), fxp_mult(power_state.vin_avg, AVG_FACT_INV));
power_state.vout_avg = fxp_add(fxp_mult(power_state.vout, AVG_FACT), fxp_mult(power_state.vout_avg, AVG_FACT_INV));
power_state.current_avg = fxp_add(fxp_mult(power_state.current, AVG_FACT), fxp_mult(power_state.current_avg, AVG_FACT_INV));
power_avg = fxp_mult(vout_avg, current_avg);
power_state.power_avg = fxp_mult(power_state.vout_avg, power_state.current_avg);
// Main FSM
if(timebase_ms >= 1000) {
switch(operState) {
case Bootstrap:
// enable bootstrap pulse with very low duty cycle
// disable converter
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 48);
// enable bootstrap pulse with very low duty cycle
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 48);
if(time_in_state >= 5) { // bootstrap duration in ms
iErr = fxp_div(IERR_LIMIT, fxp_from_int(2));
@ -519,7 +613,7 @@ int main(void)
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
// calculate error values
pErr = fxp_sub(CONST_VOLTAGE, vout_avg);
pErr = fxp_sub(CONST_VOLTAGE, power_state.vout_avg);
iErr = fxp_add(iErr, pErr);
// limit integral error range
@ -541,20 +635,20 @@ int main(void)
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
}
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && current_avg < CURRENT_THRESHOLD) {
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && power_state.current_avg < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvConstVoltage;
}
if(time_in_state > 1000 && vout_avg < MPP_VOLTAGE_THR) {
if(time_in_state > 1000 && power_state.vout_avg < MPP_VOLTAGE_THR) {
operState = ConvMPP;
}
if(current_avg > MAX_CURRENT) {
if(power_state.current_avg > MAX_CURRENT) {
operState = ConvConstCurrent;
}
if(vin_avg < vout_avg) {
if(power_state.vin_avg < power_state.vout_avg) {
operState = Idle;
}
break;
@ -564,7 +658,7 @@ int main(void)
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
// calculate error values
pErr = fxp_sub(MAX_CURRENT, current_avg);
pErr = fxp_sub(MAX_CURRENT, power_state.current_avg);
iErr = fxp_add(iErr, pErr);
// limit integral error range
@ -586,16 +680,16 @@ int main(void)
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
}
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && current_avg < CURRENT_THRESHOLD) {
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && power_state.current_avg < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvConstCurrent;
}
if(time_in_state > 1000 && current_avg < MPP_CURRENT_THR) {
if(time_in_state > 1000 && power_state.current_avg < MPP_CURRENT_THR) {
operState = ConvMPP;
}
if(vout_avg > MAX_VOLTAGE) {
if(power_state.vout_avg > MAX_VOLTAGE) {
operState = ConvConstVoltage;
}
break;
@ -604,86 +698,31 @@ int main(void)
// bootstrap off
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
if((time_in_state % 10) == 0) {
switch(testPWMStep) {
case 0:
case 2:
pwm = testPWM[testPWMStep/2];
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
break;
mpp_run(time_in_state, &mpp_state, &power_state, &pwm);
case 1:
case 3:
testPower[testPWMStep/2] = power_avg;
break;
case 4:
if(testPower[1] > prevPower) {
pwm = testPWM[1];
next_power_test = time_in_state + 10;
mpp_step++;
} else if(testPower[0] > prevPower) {
pwm = testPWM[0];
next_power_test = time_in_state + 10;
mpp_step++;
} else {
pwm = prevPWM;
next_power_test = time_in_state + 200;
mpp_step--;
}
if(mpp_step > 30) {
mpp_step = 30;
} else if(mpp_step < 1) {
mpp_step = 1;
}
if(pwm < CONV_PWM_MAX / 10) {
pwm = CONV_PWM_MAX / 10;
} else if(pwm > CONV_PWM_MAX) {
pwm = CONV_PWM_MAX;
}
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
break;
case 5:
// initiate a new power test
if(time_in_state >= next_power_test) {
testPWM[0] = pwm + mpp_step + 1;
testPWM[1] = pwm - mpp_step;
testPWMStep = 0;
prevPower = power_avg;
prevPWM = pwm;
}
break;
default:
testPWMStep = 4;
break;
}
if(testPWMStep < 5) {
testPWMStep++;
}
if(pwm > CONV_PWM_MAX) {
pwm = CONV_PWM_MAX;
} else if(pwm < CONV_PWM_PERIOD/100) {
pwm = CONV_PWM_PERIOD/100;
}
if(time_in_state > 5000 && current_avg < CURRENT_THRESHOLD) {
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
if(time_in_state > 5000 && power_state.current_avg < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvMPP;
pwm = 8 * CONV_PWM_PERIOD / 10;
next_power_test = 0;
mpp_state.testIdx = -1;
}
if(vout_avg > MAX_VOLTAGE) {
if(power_state.vout_avg > MAX_VOLTAGE) {
operState = ConvConstVoltage;
}
if(current_avg > MAX_CURRENT) {
if(power_state.current_avg > MAX_CURRENT) {
operState = ConvConstCurrent;
}
if(vin_avg < vout_avg) {
if(power_state.vin_avg < power_state.vout_avg) {
operState = Idle;
}
@ -692,9 +731,6 @@ int main(void)
debug_send_string("PWM: ");
fxp_format_int(pwm, msg);
debug_send_string(msg);
debug_send_string(" Step: ");
fxp_format_int(mpp_step, msg);
debug_send_string(msg);
sentSomething = 1;
}
#endif
@ -706,7 +742,7 @@ int main(void)
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
if(time_in_state > 1000 && vin_avg > fxp_add(vout_avg, WAKEUP_OFFSET_VOLTAGE)) {
if(time_in_state > 1000 && power_state.vin_avg > fxp_add(power_state.vout_avg, WAKEUP_OFFSET_VOLTAGE)) {
sleep_time = 10;
operState = Bootstrap;
nextState = ConvMPP;
@ -783,7 +819,7 @@ int main(void)
}
if((timebase_ms % 1000) == 490) {
report_status(vin_avg, vout_avg, current_avg, pwm, operState);
report_status(&power_state, pwm, operState);
}
// cpu load = timer1 value after main loop operations