Close
0%
0%

25 A Supercapacitor Charger

Higher current, still air supercapacitor charger

Public Chat
Similar projects worth following
Parallel MOSFET implementation of a supercapacitor charger with 25A current. Also, uses a super cool planar inductor.

I wanted to extend the learning that I had done on my 6 Amp supercapacitor charger, and push it to 25A.  Here is the previous project:

https://hackaday.io/project/174806-supercapacitor-rapid-charger

The main goal of the project is to increase the charging current, yet still be able to run the charger in still air.  Side goals included using a nifty planar inductor and changing over to an ATMEGA328PB.

Here is the whole setup on the bench:

At the top is the 2 oz perfect purple PCB that carries the switching MOSFETs, inductor, 50A fuse (safety!), driver and current amplifier.

The middle board is an ATMEGA328PB evb that I designed (20 MHz external clock, and voltage dividers/filters for the comparator and analog to digital converters).

The bottom breakout board is the "chicken button".  When you press the button, the 328PB executes the charging code.  When you release the button, the 328PB stops.  I was playing with higher charging currents and different parameters, so sometimes the switching MOSFETs can get a little warm, and you want to be able to stop the charging if things start to go into thermal runaway.

The first thing to test is applying the charging current to a wire (when the supercapacitor is completely discharged, it will act like a short circuit).  Here is the oscilloscope view of passing the ~25 A through the wire.

The top two traces are the switching voltages (channel 1 for the high side MOSFETS, channel 2 for the low side MOSFETs).  Channel 3 is the voltage at the input to the inductor.  Channel 4 is the current amplifier output.  The current sense resistor is 300 uOhm, the amplifier gain is 200, and output voltage of the current sense amplifier is ~1.5 volts.  So, 25A is flowing through the wire.  The ATMEGA328PB is controlling the current by monitoring the current amplifier output using the comparator.  If the comparator output is low (indicating the current is too high), the controller will decrease the inductor current by increasing the "on-time" of the low-side FET.  If the comparator output is high, the controller will increase the inductor current by decreasing the "on-time" of the low-side FET.

Here is the lab video showing the charging of a 450 F supercapacitor.  You know the charging is done, because the capacitor hissing stops!  The ATMEGA328PB shuts down the charging when the capacitor voltage is about 2.8 V (this is a 3 V supercapacitor, but I wanted a bit of margin).

To charge the supercapacitor from 0.67 V to 2.81 volts takes approximately 40 seconds, so the average charging current is about 24 amps.  

  • 1 × PQ3218-6R0-50-T Planar inductor
  • 1 × ATMEGA328PB Microcontroller

  • Code for the ATMEGA328PB

    sciencedude199010/30/2020 at 15:35 0 comments

    Here is the code for the ATMEGA328PB

    Here is the code for the ATMEGA328PB
    
    #include <avr/io.h>
    #define F_CPU 20000000UL // Use external 20 MHz clock, set to external oscillator in the fuses
    #include <util/delay.h>
    
    const uint8_t MAX_CAP_VOLTAGE = 145; // Assume output of cap is more or less at ADC
    const uint8_t START_CAP_VOLTAGE = 130; // When to start charging - hysteresis
    
    const uint8_t COUNT_FET_DEFAULT = 0; // When starting to charge, the lower FET on time
    const uint8_t COUNT_LOW_DEFAULT = 200; // The starting count for all switches off
    
    // Enumerated type for the state machine
    typedef enum {IDLE, CHARGING, STOP} state_var;
    
    int main(void)
    {
        // Variable to hold the state
        volatile state_var the_state = IDLE;
        
        // The low side on time
        uint8_t count_fet = COUNT_FET_DEFAULT;
        
        // The idle time when everything is low
        uint8_t count_low = COUNT_LOW_DEFAULT;
        
        // PB0 for V high (high side)
        // PB1 for V low (low side)
        // PB2 for trigger
        DDRB = 7;
        PORTB = 0;
        
        //DDRD = 255 - (1 << 5);
        
        // Setup the analog comparator, set bandgap reference (instead of AIN0)
        ACSR = (1 << 6);
        
        // Setup the ADC (use AVCC, left adjust, and ADC0 as the input)
        ADMUX = (1 << 6) + (1 << 5) + 0;
        
        // Enable ADC, start it up, auto trigger, divide by 128 for prescalar
        ADCSRA = (1 << 7) + (1 << 6) + (1 << 5) + 7;
        
        // Free running mode
        ADCSRB = 0;
        
        // At start, just wait in case of issues with programming
        _delay_ms(3000);
            
        while (1) {
            
            if ((PIND & (1 << 5)) == 0) {
                the_state = STOP;
            }
                    
            if (the_state == IDLE) {  // Idle state
                
                // Wait to let the ADC readings settle
                _delay_ms(3);
                
                if (ADCH < START_CAP_VOLTAGE) { // Begin charging
                    // Initialize lower FET on time
                    count_fet = COUNT_FET_DEFAULT;
                    count_low = COUNT_LOW_DEFAULT;
                    
                    // Update state variable
                    the_state = CHARGING;
                }
            }
            else if (the_state == CHARGING) { // Charging state
                
                for (uint8_t qq = 0; qq < 128; qq++) { // Cycles of current control
                    // Charging loop
                    // Turn on the high side FET
                    PORTB = 1 + 4;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                    PORTB = 1;
                                            
                    // Turn off the high side FET, prevent shoot-through
                    PORTB = 0;
                    PORTB = 0;
                    PORTB = 0;
                    
                    // Turn on the low side FET
                    for (uint8_t jj = 0; jj < count_fet; jj++) {
                        PORTB = 2;
                    }
                    
                    // Turn off the low side FET, all low
                    PORTB = 0;                
                    for (uint8_t jj = 0; jj < count_low; jj++) {
                        PORTB = 0;
                    }
                            
                    if ((ACSR & (1 << 5)) == 0) { // Current too high                    
                        if (count_fet < 255) {
                            count_fet = count_fet + 1;
                        }                    
                    }
                    else { // Current too low
                        if (count_fet > 0) {
                            count_fet = count_fet - 1;                    
                        }
                    
                        if (count_low > 0) {
                            count_low = count_low - 1;
                        }
                    }
                }
                if (ADCH > MAX_CAP_VOLTAGE) {
                    // Done charging, set to IDLE
                    the_state = IDLE;
                }                                    
            }    
            else if (the_state == STOP) {
                PORTB = 0;
                _delay_ms(1);
                if ((PIND & (1 << 5)) == (1 << 5)) {
                    the_state = IDLE;
                }
            }                
        }
    }

View project log

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates