📄 charger.c
字号:
c=pgm_read_byte(p++);
if (!c) break;
xmit(c);
}
}
/* This function transmits a byte in 'c' over the serial channel. It stores
the byte in the transmit FIFO and sets the transmission counters if
appropriate. Actual transmission happens in the Timer0 interrupt service
routine */
void xmit(unsigned char c)
{
// Find out what the next tail index will be, accounting for circularity
unsigned char next_tail=txtail+1;
if (next_tail==TXFIFOSIZE) next_tail=0;
// If the FIFO is full, return with no action, discarding the byte
if (next_tail==txhead) return;
// Insert byte into the transmit FIFO and update tail index
fifo[txtail]=c; txtail=next_tail;
// Start a new transmission cycle if we're not already doing so
if (!xmitbit) {
xmitbit=1; xmitbyte=fifo[txhead++];
if (txhead==TXFIFOSIZE) txhead=0;
}
}
void do_charge_control(void)
{
// Sense the current battery voltage. Since we do this at the very beggining
// of the RTC clock cycle, we know for sure that the PWM is off and we're
// sensing the battery voltage and not the charger voltage.
voltage=get_battery_voltage();
// This is a trick to have the average accumulator and counter in the same
// integer. The current accumulated average is in bits 31..8 and the average
// counter is in bits 7..0.
avg_accum+=((unsigned long)voltage<<8)|1;
// If we don't have main voltage (or, more precisely, the main voltage is
// less than 10.2V), we won't charge the battery, period, no matter what
// the charging logic says
unsigned short mains_voltage=get_mains_voltage();
if (mains_voltage<650 || quell_charging) {
set_duty_cycle(0);
disable_charger();
//disable_battery_pwm();
//DDRD |= _BV(PD7);
//PORTD |= _BV(PD7);
if (charging_state!=BatteryNotCharging) {
charging_state=BatteryNotCharging;
#ifdef BATT_DEBUG
xmit_timestamp();
xmit_pstr(" LOW MAINS (");
xmit_u8as4d(mains_voltage);
xmit_pstr("): MOVING TO 'NOT CHARGING'\r\n");
#endif
}
} else {
if (charging_state==BatteryNotCharging) {
charging_state=BatteryInitialSense;
xmit_timestamp();
xmit_pstr(" STARTING UP: MOVING TO 'INITIAL SENSE'\r\n");
}
}
// If we had 256 samples accumulated already, let's take some action
if (!(avg_accum&0xFF)) {
short voltage=avg_accum>>13; // Compute average
avg_accum=0; // Zero average accumulator
max_battery_charge_time--;
#ifdef BATT_DEBUG
xmit_timestamp();
xmit_pstr(" V=");
xmit_k1_u8as5dot2f(voltage);
xmit_crlf();
#endif
switch (charging_state) {
case BatteryNotCharging:
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" NOT CHARGING: ");
if (quell_charging)
xmit_pstr("DISABLED BY SOFTWARE");
else
xmit_pstr("NO MAIN POWER\r\n");
#endif
break;
case BatteryInitialSense:
// If we're below 2 volts, the battery is almost certainly
// disconnected, so we have nothing to charge. We also don't
// charge if the battery voltage is beyond levels only found
// during fast charge
if (voltage<1088 || voltage>4592) {
set_duty_cycle(0);
disable_charger();
//disable_battery_pwm();
//DDRD |= _BV(PD7);
//PORTD |= _BV(PD7);
charging_state=BatteryInitialSense;
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" INITIAL SENSE: "
"VOLTAGE OUT OF RANGE\r\n");
#endif
break;
}
// If we're above 8 volts, the battery is still in very good
// shape and maybe didn't even had the time to cool down from
// the last charge, so let's switch to trickle. Otherwise,
// start a soft charge to get things started
if (voltage>4256) {
charging_state=BatteryTrickleCharging;
//OCR2=245; // 4% Duty cycle
set_duty_cycle(4); // 4% duty cycle
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" INITIAL SENSE: "
"MOVING TO 'TRICKLE CHARGE'\r\n");
#endif
// Come back to this state ~5 min before each hour starts
// so we can recheck the voltage and decide what to do
max_battery_charge_time=13;
} else {
charging_state=BatterySoftCharging;
//OCR2=128; // 50% Duty cycle
set_duty_cycle(50); // 50% duty cycle
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" INITIAL SENSE: "
"MOVING TO 'SOFT CHARGE'\r\n");
#endif
// Arm a timer to stop the full charging process at
// most 63 256-second cycles (i.e., around 4h28min) later
max_battery_charge_time=63;
}
//DDRD |= _BV(PD7);
//enable_battery_pwm();
#ifdef BATT_DEBUG
xmit_timestamp();
xmit_pstr(" INITIAL SENSE: "
"DONE\r\n");
#endif
break;
case BatteryTrickleCharging:
// If enough time has passed, give the battery a
// rest and let the initial state decide what to do
if (!max_battery_charge_time) {
charging_state=BatteryInitialSense;
set_duty_cycle(0);
disable_charger();
//disable_battery_pwm();
//DDRD |= _BV(PD7);
//PORTD |= _BV(PD7);
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" TRICKLE: "
"MOVING TO 'NO CHARGING'\r\n");
#endif
}
break;
case BatterySoftCharging:
// After one cycle of soft charge, let's go to fast
// charging. The soft charge thing helps to eliminate
// false peaks that may confuse the full charge mode
// negative delta-V (that is, voltage drop) -based
// termination algorithm.
//OCR2=5; // 98% duty cycle
set_duty_cycle(98); // 98% duty cycle
charging_state=BatteryFastCharging;
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" SOFT: "
"MOVING TO 'FAST CHARGING'\r\n");
#endif
break;
case BatteryFastCharging:
// We finish the fast charge process if either we
// spent the absolute maximum time or if the voltage
// has decreased since our last measurement -- If the
// battery is in use (i.e., we have no main power), this
// is because it is being discharged, so our charging
// attempts are moot. If we do have main power, the
// voltage drop is because we've hit the maximum battery
// capacity, so we should stop.
if (!max_battery_charge_time || last_voltage>voltage) {
charging_state=BatteryInitialSense;
set_duty_cycle(0);
disable_charger();
//disable_battery_pwm();
//DDRD |= _BV(PD7);
//PORTD |= _BV(PD7);
#ifdef BATT_DEBUG
xmit_crlf();
xmit_timestamp();
xmit_pstr(" FAST: "
"MOVING TO 'NO CHARGING'\r\n");
#endif
}
last_voltage=voltage;
break;
}
}
}
/* Select ADC channel 'ch' and takes a voltage reading. Used indirectly
through the get_battery_voltage() and get_mains_voltage() macros. */
unsigned short getVoltage(unsigned char ch)
{
adc_set_channel(ch);
adc_start();
while (adc_busy());
return adc_value();
}
/* Timer0 Output Compare Interrupt Service Routine: this is called
BAUD_RATE*PHASE times per second to serially transmitting bits -- a
software UART, in fact. (Actually, a software UAT, because it's transmit
only. The PHASE thing is a relic from an earlier version that did have a
receiver. It is not really needed for the transmitter but since it doesn't
hurt, I opted for leaving it there).
Besides, the routine updates the clock each second and also controls the
timing of the battery charger enable bit -- like a slow PWM.
*/
SIGNAL(SIG_OUTPUT_COMPARE0A)
{
// --- Software UART for serial reception and transmission
phase=cnt++&PHASE_MASK;
// Serial transmission: if it's time to transmit a bit and
// we do have something to transmit, let's do it!
if (!phase) {
if (xmitbit) {
if (xmitbit==1) { // Start bit
xmit0();
xmitbit++;
} else if (xmitbit==10) { // Stop bit
xmit1();
if (txhead==txtail)
xmitbit=0;
else {
xmitbit=1;
xmitbyte=fifo[txhead++];
if (txhead==TXFIFOSIZE) txhead=0;
}
} else { // Data bits
if (xmitbyte&1) {
xmit1();
} else {
xmit0();
}
xmitbyte>>=1; xmitbit++;
}
}
if (++gcount==BAUD_RATE) {
// 1Hz ticker for updating the clock and resseting the
// charger control
gcount=0;
if (seconds==59) {
seconds=0;
if (minutes==59) {
minutes=0;
if (hours==23) {
hours=0;
} else hours++;
} else minutes++;
} else seconds++;
has_new_second=TRUE;
if (duty_cycle>0)
disable_charger();
}
// Enables the charger at some point within the 1Hz cycle
if (gcount==duty_cycle) {
enable_charger();
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -