Here is the code to the controller. Note in the charging phase there are three steps. First, have the high side transistor on (to increase the inductor current), second have the low side transistor on (at the start, you actually don't turn on the lower transistor), and lastly, dead time. The dead time is super important because at the start, you want to gradually increase the lower transistor on time so that you don't discharge the supercapacitor if it has an initial charge. Since the switching node of the controller is okay with going a bit negative, you use the body diode of the lower transistor and the dead time to decrease the inductor current until the loops converge. When the loop converges, you shed the dead time in favor of the lower transistor on time.
#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 = 212; // When charging is done
const uint8_t START_CAP_VOLTAGE = 180; // When to start charging - separate these two numbers to give 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 the low side on time
const uint8_t CHARGING_LOOP_COUNT = 8; // The number of charging loop cycles between checking the ADC reading and the comparator
// Enumerated type for the state machine
typedef enum {IDLE, CHARGING} state_var;
int main(void) { // Main routine
// 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;
// Setup the ADC, bit 6 set to 1 for internal 1.1V reference, left adjusted (so only read ADCH), and select channel 2 (PB4, ADC2)
ADMUX = (1 << 6) + (1 << 5) + (2);
// Enable ADC, start it up, auto trigger, no interrupt flag, no interrupt, divide by 128
ADCSRA = (1 << 7) + (1 << 6) + (1 << 5) + (0 << 4) + (0 << 3) + 7;
// No analog comparator mux, free running mode
ADCSRB = 0;
// Analog comparator control, enabled, bandgap turned on
ACSR = (1 << 6);
// Allow the ADC to settle, and allow for system programming
_delay_ms(2000);
// Output pins, PB0 for high side FET, PB1 for current sense (comparator), PB2 for low side FET, PB3 for CLKI, PB4 for capacitor voltage monitor
DDRB = (1 << 0) + (1 << 2);
// Set the output pins to low
PORTB = 0;
while (1) { // Main loop
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
// Charging loop
for (uint8_t ii = 0; ii < CHARGING_LOOP_COUNT; ii++) {
// Turn on the high side FET
PORTB = 1;
PORTB = 1;
PORTB = 1;
PORTB = 1;
PORTB = 1;
PORTB = 1;
// Turn off the high side FET
PORTB = 0;
PORTB = 0;
PORTB = 0;
// Turn on the low side FET
for (uint8_t jj = 0; jj < count_fet; jj++) {
PORTB = 4;
}
// Turn off the low side FET
PORTB = 0;
for (uint8_t jj = 0; jj < count_low; jj++) {
PORTB = 0;
}
}
if (ADCH > MAX_CAP_VOLTAGE) {
// Done charging, set to IDLE
the_state = IDLE;
}
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;
}
}
}
}
}
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.