diff --git a/fxplib b/fxplib index 035e7ec..3bd8680 160000 --- a/fxplib +++ b/fxplib @@ -1 +1 @@ -Subproject commit 035e7ecda497b408a5791ba20a0906b913dfef45 +Subproject commit 3bd86806d72a6caed2b604a0ba4c6bf344f72b1a diff --git a/src/main.c b/src/main.c index 0b96eef..58f3bc9 100644 --- a/src/main.c +++ b/src/main.c @@ -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