Buck-boost converter with digital control on STM32F334 in CC / CV mode

The most popular dc / dc topologies buck and boost have a significant limitation: the buck topology can only reduce the input voltage, while the boost topology only increases it. However, there are tasks when the input voltage range requires simultaneous work both to increase and decrease, for example, we have an input of 3 ... 15V, and at the output it is necessary to obtain a stabilized 12V. Familiar situation?



There are 2 possible solutions:



  • Using the boost converter, increase the input voltage from 3 ... 15V to stable 15V at the output, and then, using the buck topology, lower the voltage to the required 12V;
  • Apply buck-boost topology, which allows you to optimally solve this problem.


An obvious disadvantage of the first method is the need to use 2 chokes, an increased number of capacitors and not the most optimal operating mode, which means a lower efficiency. Buck-boost topology lacks these shortcomings, so today the story will be about it. To make it interesting, I decided not to take some ready-made controller and implemented a digitally controlled dc / dc converter based on STM32F334C8T6.



Photo Converter



The result of work for those who do not want to read the wall of text


Within the framework of this article, I will briefly talk about the hardware implementation of the converter and how to implement a control system for various operating modes. Interesting? Then let's go ...



1. Briefly about how buck-boost topology works



: 2 2 , .. , 2 (4 ). , . :



Buck-boost structure



, : 2 ( 2 , ), 4 , . 1 2 (OCP) . 2 ? , , .



, , , STM32F334, STM32G474, XMC4108, TMS320F28027 , .. : HRPWM, , , . , , , . , , , (OCP) , , .



… buck-boost :



Equivalent buck-boost circuit



buck () boost (), — 3 buck-, L1C3 LC-. 3 boost-, . , buck-, boost-. .



buck:



Vout=VinD



boost:



Vout=Vin(1D)



, buck- boost :



Voutboost=VinbuckDbuck1Dboost



buck-boost 3- : ( PWM-BUCK PWM-BOOST). , .



, . , 8, Dboost 70%, Dbuck 50%. :



Voutboost=VinbuckDbuck1Dboost=8V0,510,7=13,33V



, 8 . , , , 2 - :



Oscilloscope chart



. (duty) 2- . duty, .



2. .



2 , : . , , CC/CV . :



Control block diagram



CC CV , : REF , , ; , , , , .



-, , " " . "" (REF), , , 10. , buck-boost , (duty). - . , .



:

error=VreferenceVout



What does this give us? And now we know how much the real value of the output voltage deviated from the set value ( REF ). This allows us to understand whether it is necessary to “give a command” to increase or decrease voltage. For example, in the buck topology, to increase the voltage, you need to increase the duty factor for the upper transistor, and to reduce the voltage, reduce the duty cycle accordingly. For the boost topology, on the contrary, and for the buck-boost there are 2 signals, and here it is already more difficult - you need to balance, but I think on average the idea is clear, for clarity I will give a pseudo-code for controlling the buck :

//      -
uint16_t dutyPWM = 0;

//  ,     
const float referenceOutputVoltage = 10.0f;

//    , ,  1 
void sTim3::handler (void) {
    float outputVoltage = GetOutputVoltage();
    if (outputVoltage > referenceOutputVoltage) {
        dutyPWM--;
    } else {
        dutyPWM++;
    }
}


, . - ( ), , -, -:



//      -
uint16_t dutyPWM = 0;

//  ,     
const float referenceOutputVoltage = 10.0f;

//    
const float Kp = 1.0f;

//    , ,  1 
void sTim3::handler (void) {
    float outputVoltage = GetOutputVoltage();
    float error = referenceOutputVoltage - outputVoltage;
    dutyPWM += Kp * error;
}


… , , (dutyPWM) buck , , . , (reference), dutyPWM .



, 1 , . error -. , dutyPWM .



buck dc/dc , 20. dutyPWM 0, 1000, buck Vout = Vin x dutyPWM = 20V x 0 = 0V, 0. ( №1) error = 10 — 0 = 10 dutyPWM = 10, Vout = Vin x dutyPWM = 20V x (10/1000) = 0.2V. 1 ( №2) error = 10 — 0.2V = 9.8V, dutyPWM = 19.8, (reference). , reference 10 ( ).



Kp, , . 1, . , 0.2 . () , , . Kp 10 : dutyPWM = Kp x error = 10 x (10 — 0) = 100, 0.2, Vout = Vin x dutyPWM = 20V x (100/1000) = 2V, " " 10 . Kp . , , , , .



… ? , .



3. CV mode



CV mode, . ( ), , , : - ++. , . , .



STM32F334C8T6, , HRPWM . , , . , (2-3-4) , , .



CV :



/***********************************************************
*       200 ,
*    HRPWM    - 30 000.
*     : 3...15
*    : 20 
*
*      : 12
***********************************************************/

void sTim3::handler (void) {
    TIM3->SR &= ~TIM_SR_UIF;

    float inputVoltage = Feedback::GetInputVoltage();

    //   boost,  Vin < 90% * Vref
    if (inputVoltage <= (Application::referenceOutputVoltage * 0.9)) {
        Hrpwm::SetDuty(Hrpwm::Channel::buck, 29000);
        float outputVoltage = Feedback::GetOutputVoltage();

        pidVoltageMode
            .SetReference(Application::referenceOutputVoltage)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputVoltage, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBoost += pidVoltageMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::boost, Application::dutyBoost);
    }

    //   buck,  Vin > Vref * 110%
    if (inputVoltage >= (Application::referenceOutputVoltage * 1.1)) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 1000);
        float outputVoltage = Feedback::GetOutputVoltage();

        pidVoltageMode
            .SetReference(Application::referenceOutputVoltage)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputVoltage, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBuck += pidVoltageMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }

    //   buck-boost,  (90% * Vref) < Vin < (110% * Vref)
    if ((inputVoltage > (Application::referenceOutputVoltage * 0.9)) && (inputVoltage < (Application::referenceOutputVoltage * 1.1))) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 6000);
        float outputVoltage = Feedback::GetOutputVoltage();

        pidVoltageMode
            .SetReference(Application::referenceOutputVoltage)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputVoltage, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBuck += pidVoltageMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }    
}


buck-boost, , 2- buck boost . 2- : . , , , .



/***********************************************************
*       200 ,
*    HRPWM    - 30 000.
*     : 3...15
*    : 20 
*
*      : 12
***********************************************************/

void sTim3::handler (void) {
    TIM3->SR &= ~TIM_SR_UIF;

    //          boost
    float inputVoltage = Feedback::GetInputVoltage();
    if (inputVoltage < 6.0f) { Application::dutyBoost = 25000; }
    if ((inputVoltage >= 6.0f) && (inputVoltage < 12.0f)) { Application::dutyBoost = 18000; }
    if (inputVoltage >= 12.0f) { Application::dutyBoost = 6000; }
    Hrpwm::SetDuty(Hrpwm::Channel::boost, Application::dutyBoost);

    //         buck 
    float outputVoltage = Feedback::GetOutputVoltage();

    pidVoltageMode
        .SetReference(Application::referenceOutputVoltage)
        .SetSaturation(-29800, 29800)
        .SetFeedback(outputVoltage, 0.001)
        .SetCoefficient(10,0,0,0,0)
        .Compute();

    Application::dutyBuck += pidVoltageMode.Get();
    Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);    
}


3 : 3...6, 6...12 12...15 boost , buck. , — , . , ( ), .



3 , , , , . dutyBoost : , buck boost-, 90% ( ). , 3...15 . dutyBoost — 3 15, , .. . dutyBuck 90% 3 "" boost- 3 x 0,9 = 2,7, boost 15 2.7! dutyBoost 1 — (Vout / Vin) = 1 — 2,7 / 15 = 82%, , - 30000, 30 000 x 82% = 24 600, 25000.



3, 5, buck 3. , dutyBoost, buck 90% 12. , ~3,2% . ? , , "" .



6...12 60% 18000, 12...15 20% 6000. - ...



3- . , , buck 100% boost-, boost dc/dc . , , — boost- 0% buckDuty, buck dc/dc . — , buck, boost buck-boost...



boost , "" 90% , Vref x 90% = 12 x 0,9 = 10,8. dutyBoost = 1 — (Vref x 90%) / (Vref x 110%) = 1 — 0,9 / 1,1 = 19% = 5700, 6000 . buck- buckDuty. "" buck-boost , . , :





4. CC mode



, . , , , LED, - , . , dc/dc , .. (duty), ?



— : U = I x R. , , , 10 . dc/dc 10 , I = U / R = 1, , .. . Li-ion , , 15, - 5, . , , I = U / R = const.



, , , . , , , I = U / R = const.



, 1. 1 1: I = 1 = const = U / R = 1 / 1 . 5 , 5: I = 1 = const = U / R = 5 / 5 . 5, 1 0.2 .



, , (error) , (dutyPWM). :




/***********************************************************
*       200 ,
*    HRPWM    - 30 000.
*     : 3...15
*    : 20 
*
*      : 1
***********************************************************/

void sTim3::handler (void) {
    TIM3->SR &= ~TIM_SR_UIF;

    float inputVoltage = Feedback::GetInputVoltage();

    //   boost,  Vin < 90% * Vref
    if (inputVoltage <= (Application::referenceOutputVoltage * 0.9)) {
        Hrpwm::SetDuty(Hrpwm::Channel::buck, 29000);
        float outputCurrent = Feedback::GetOutputCurrent();

        pidCurrentMode
            .SetReference(Application::referenceOutputCurrent)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputCurrent, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBoost += pidCurrentMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::boost, Application::dutyBoost);
    }

    //   buck,  Vin > Vref * 110%
    if (inputVoltage >= (Application::referenceOutputVoltage * 1.1)) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 1000);
        float outputCurrent = Feedback::GetOutputCurrent();

        pidCurrentMode
            .SetReference(Application::referenceOutputCurrent)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputCurrent, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBuck += pidCurrentMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }

    //   buck-boost,  (90% * Vref) < Vin < (110% * Vref)
    if ((inputVoltage > (Application::referenceOutputVoltage * 0.9)) && (inputVoltage < (Application::referenceOutputVoltage * 1.1))) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 6000);
        float outputCurrent = Feedback::GetOutputCurrent();

         pidCurrentMode
            .SetReference(Application::referenceOutputCurrent)
            .SetSaturation(-29800, 29800)
            .SetFeedback(outputCurrent, 0.001)
            .SetCoefficient(10,0,0,0,0)
            .Compute();

        Application::dutyBuck += pidCurrentMode.Get()
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }    
}


, buck-boost . , (outputCurrent) - (referenceOutputCurrent) , , . :



error=IreferenceIout



, CV mode. - , 2 : , , , .



LED 10 1, :





5. CC/CV mode



, … :



  • 10 Li-ion , , . , , ( ) , .




  • 1, Li-ion , . … ? , , , , , — .


- , ? CC/CV, . : 1 Li-ion 4.2 , CC/CV 3...15 1. , 1 , CC , . , 15, CV, 15 ( ).



2 , .. , 15 , , 1 — , . 3, , , CV , . , :




/***********************************************************
*       200 ,
*    HRPWM    - 30 000.
*     : 3...15
*    : 20 
*
*      : 10 1
***********************************************************/

void sTim3::handler (void) {
    TIM3->SR &= ~TIM_SR_UIF;

    float resultPID = 0.0f;

    float inputVoltage = Feedback::GetInputVoltage();

    //   boost,  Vin < 90% * Vref
    if (inputVoltage <= (Application::referenceOutputVoltage * 0.9)) {
        Hrpwm::SetDuty(Hrpwm::Channel::buck, 29000);

        float outputVoltage = Feedback::GetOutputVoltage();
        float outputCurrent = Feedback::GetOutputCurrent();

        //   CC mode,  Vout < Vref
        if (outputVoltage < (Application::referenceOutputVoltage - 0.2f)) {
            pidCurrentMode
                .SetReference(Application::referenceOutputCurrent)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputCurrent, 0.001)
                .SetCoefficient(20,0,0,0,0)
                .Compute();
            resultPID = pidCurrentMode.Get();
        }

        //   CV mode,  (Iout -> 0)  (Vout => Vref)
        if ((outputCurrent < 0.05f) || (outputVoltage >= (Application::referenceOutputVoltage - 0.2f))) {
            pidVoltageMode
                .SetReference(Application::referenceOutputVoltage)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputVoltage, 0.001)
                .SetCoefficient(50,0,0,0,0)
                .Compute();
            resultPID = pidVoltageMode.Get();
        }

        Application::dutyBoost += resultPID;
        Hrpwm::SetDuty(Hrpwm::Channel::boost, Application::dutyBoost);
    }

    //   buck,  Vin > Vref * 110%
    if (inputVoltage >= (Application::referenceOutputVoltage * 1.1)) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 1000);

        float outputVoltage = Feedback::GetOutputVoltage();
        float outputCurrent = Feedback::GetOutputCurrent();

        //   CC mode,  Vout < Vref
        if (outputVoltage < (Application::referenceOutputVoltage - 0.2f)) {
            pidCurrentMode
                .SetReference(Application::referenceOutputCurrent)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputCurrent, 0.001)
                .SetCoefficient(20,0,0,0,0)
                .Compute();
            resultPID = pidCurrentMode.Get();
        }

        //   CV mode,  (Iout -> 0)  (Vout => Vref)
        if ((outputCurrent < 0.05f) || (outputVoltage >= (Application::referenceOutputVoltage - 0.2f))) {
            pidVoltageMode
                .SetReference(Application::referenceOutputVoltage)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputVoltage, 0.001)
                .SetCoefficient(50,0,0,0,0)
                .Compute();
            resultPID = pidVoltageMode.Get();
        }

        Application::dutyBuck += resultPID;
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }

    //   buck-boost,  (90% * Vref) < Vin < (110% * Vref)
    if ((inputVoltage > (Application::referenceOutputVoltage * 0.9)) && (inputVoltage < (Application::referenceOutputVoltage * 1.1))) {
        Hrpwm::SetDuty(Hrpwm::Channel::boost, 6000);

        float outputVoltage = Feedback::GetOutputVoltage();
        float outputCurrent = Feedback::GetOutputCurrent();

        //   CC mode,  Vout < Vref
        if (outputVoltage < (Application::referenceOutputVoltage - 0.2f)) {
            pidCurrentMode
                .SetReference(Application::referenceOutputCurrent)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputCurrent, 0.001)
                .SetCoefficient(20,0,0,0,0)
                .Compute();
            resultPID = pidCurrentMode.Get();
        }

        //   CV mode,  (Iout -> 0)  (Vout => Vref)
        if ((outputCurrent < 0.05f) || (outputVoltage >= (Application::referenceOutputVoltage - 0.2f))) {
            pidVoltageMode
                .SetReference(Application::referenceOutputVoltage)
                .SetSaturation(-29800, 29800)
                .SetFeedback(outputVoltage, 0.001)
                .SetCoefficient(50,0,0,0,0)
                .Compute();
            resultPID = pidVoltageMode.Get();
        }

        Application::dutyBuck += resultPID;
        Hrpwm::SetDuty(Hrpwm::Channel::buck, Application::dutyBuck);
    }    
}


, , if, . .



, 2 -, , — -. CC mode CV mode. : (Application::referenceOutputVoltage) , CV mode, 15. , CC mode 1.



, / , . , LED 2 . , , 10 STM32F334 .



buck-boost dc/dc CC/CV:





...



lamerok veydlin, (, , ), !



, : https://t.me/proHardware. , , , , , .



, . buck-boost-, , , - , .




All Articles