diff options
-rw-r--r-- | docs/CREDITS | 1 | ||||
-rw-r--r-- | firmware/export/powermgmt.h | 6 | ||||
-rw-r--r-- | firmware/powermgmt.c | 117 |
3 files changed, 88 insertions, 36 deletions
diff --git a/docs/CREDITS b/docs/CREDITS index 12f48f970e..9fef2c7532 100644 --- a/docs/CREDITS +++ b/docs/CREDITS | |||
@@ -176,3 +176,4 @@ Aaron F. Gonzalez | |||
176 | Aleksey Kozyulin | 176 | Aleksey Kozyulin |
177 | Jani Kinnunen | 177 | Jani Kinnunen |
178 | Rui Marinho | 178 | Rui Marinho |
179 | Alun Thomas | ||
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index a5dbcd10bd..b9ac996fc0 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h | |||
@@ -39,10 +39,10 @@ | |||
39 | 39 | ||
40 | #define POWER_HISTORY_LEN 2*60 /* 2 hours of samples, one per minute */ | 40 | #define POWER_HISTORY_LEN 2*60 /* 2 hours of samples, one per minute */ |
41 | 41 | ||
42 | #define CHARGE_END_NEGD 6 /* stop when N minutes have passed with | 42 | #define CHARGE_END_SHORTD 6 /* stop when N minutes have passed with |
43 | * avg delta being < -0.05 V */ | 43 | * avg delta being < -0.05 V */ |
44 | #define CHARGE_END_ZEROD 50 /* stop when N minutes have passed with | 44 | #define CHARGE_END_LONGD 50 /* stop when N minutes have passed with |
45 | * avg delta being < 0.005 V */ | 45 | * avg delta being < -0.02 V */ |
46 | 46 | ||
47 | #ifndef SIMULATOR | 47 | #ifndef SIMULATOR |
48 | 48 | ||
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index ff635486c1..e4330a48c0 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c | |||
@@ -175,7 +175,8 @@ charge_state_type charge_state; /* charging mode */ | |||
175 | #ifdef HAVE_CHARGE_CTRL | 175 | #ifdef HAVE_CHARGE_CTRL |
176 | int long_delta; /* long term delta battery voltage */ | 176 | int long_delta; /* long term delta battery voltage */ |
177 | int short_delta; /* short term delta battery voltage */ | 177 | int short_delta; /* short term delta battery voltage */ |
178 | 178 | bool disk_activity_last_cycle = false; /* flag set to aid charger time | |
179 | * calculation */ | ||
179 | char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in | 180 | char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in |
180 | debug menu */ | 181 | debug menu */ |
181 | /* percentage at which charging | 182 | /* percentage at which charging |
@@ -580,7 +581,12 @@ static void power_thread_sleep(int ticks) | |||
580 | battery_status_update(); | 581 | battery_status_update(); |
581 | 582 | ||
582 | } | 583 | } |
583 | 584 | #ifdef HAVE_CHARGE_CTRL | |
585 | if (ata_disk_is_active()) { | ||
586 | /* flag hdd use for charging calculation */ | ||
587 | disk_activity_last_cycle = true; | ||
588 | } | ||
589 | #endif | ||
584 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) | 590 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) |
585 | /* | 591 | /* |
586 | * If we have a lot of pending writes or if the disk is spining, | 592 | * If we have a lot of pending writes or if the disk is spining, |
@@ -608,9 +614,15 @@ static void power_thread(void) | |||
608 | int i; | 614 | int i; |
609 | short *phps, *phpd; /* power history rotation pointers */ | 615 | short *phps, *phpd; /* power history rotation pointers */ |
610 | #ifdef HAVE_CHARGE_CTRL | 616 | #ifdef HAVE_CHARGE_CTRL |
611 | unsigned int target_voltage; /* desired topoff/trickle voltage level */ | 617 | unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle |
612 | int charge_max_time_now = 0; /* max. charging duration, calculated at | 618 | * voltage level */ |
613 | beginning of charging */ | 619 | int charge_max_time_idle = 0; /* max. charging duration, calculated at |
620 | * beginning of charging */ | ||
621 | int charge_max_time_now = 0; /* max. charging duration including | ||
622 | * hdd activity */ | ||
623 | int minutes_disk_activity = 0; /* count minutes of hdd use during | ||
624 | * charging */ | ||
625 | int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */ | ||
614 | #endif | 626 | #endif |
615 | 627 | ||
616 | /* initialize the voltages for the exponential filter */ | 628 | /* initialize the voltages for the exponential filter */ |
@@ -669,19 +681,23 @@ static void power_thread(void) | |||
669 | powermgmt_last_cycle_startstop_min = 0; | 681 | powermgmt_last_cycle_startstop_min = 0; |
670 | if(battery_percent >= START_TRICKLE_CHG) { | 682 | if(battery_percent >= START_TRICKLE_CHG) { |
671 | charge_state = TRICKLE; | 683 | charge_state = TRICKLE; |
684 | target_voltage = TRICKLE_VOLTAGE; | ||
672 | } else { | 685 | } else { |
673 | charge_state = TOPOFF; | 686 | charge_state = TOPOFF; |
687 | target_voltage = TOPOFF_VOLTAGE; | ||
674 | } | 688 | } |
675 | } else { | 689 | } else { |
676 | /* | 690 | /* |
677 | * Start the charger full strength | 691 | * Start the charger full strength |
678 | */ | 692 | */ |
679 | i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; | 693 | i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; |
680 | charge_max_time_now = | 694 | charge_max_time_idle = |
681 | i * (100 + 35 - battery_percent) / 100; | 695 | i * (100 + 35 - battery_percent) / 100; |
682 | if (charge_max_time_now > i) { | 696 | if (charge_max_time_idle > i) { |
683 | charge_max_time_now = i; | 697 | charge_max_time_idle = i; |
684 | } | 698 | } |
699 | charge_max_time_now = charge_max_time_idle; | ||
700 | |||
685 | snprintf(power_message, POWER_MESSAGE_LEN, | 701 | snprintf(power_message, POWER_MESSAGE_LEN, |
686 | "ChgAt %d%% max %dm", battery_level(), | 702 | "ChgAt %d%% max %dm", battery_level(), |
687 | charge_max_time_now); | 703 | charge_max_time_now); |
@@ -699,26 +715,44 @@ static void power_thread(void) | |||
699 | } | 715 | } |
700 | } | 716 | } |
701 | if (charge_state == CHARGING) { | 717 | if (charge_state == CHARGING) { |
702 | snprintf(power_message, POWER_MESSAGE_LEN, | 718 | /* alter charge time max length with extra disk use */ |
703 | "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, | 719 | if (disk_activity_last_cycle) { |
704 | charge_max_time_now); | 720 | minutes_disk_activity++; |
721 | charge_max_time_now = charge_max_time_idle + | ||
722 | (minutes_disk_activity * 2 / 5); | ||
723 | disk_activity_last_cycle = false; | ||
724 | last_disk_activity = 0; | ||
725 | } else { | ||
726 | last_disk_activity++; | ||
727 | } | ||
705 | /* | 728 | /* |
706 | * Check the delta voltage over the last X minutes so we can do | 729 | * Check the delta voltage over the last X minutes so we can do |
707 | * our end-of-charge logic based on the battery level change. | 730 | * our end-of-charge logic based on the battery level change. |
731 | *(no longer use minimum time as logic for charge end has 50 | ||
732 | * minutes minimum charge built in) | ||
708 | */ | 733 | */ |
709 | if (powermgmt_last_cycle_startstop_min > CHARGE_MIN_TIME) { | 734 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) { |
710 | short_delta = power_history[0] - | 735 | short_delta = power_history[0] - |
711 | power_history[CHARGE_END_NEGD - 1]; | 736 | power_history[CHARGE_END_SHORTD - 1]; |
712 | } | 737 | } |
713 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_ZEROD) { | 738 | |
739 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) { | ||
714 | /* | 740 | /* |
715 | * Scan the history: if we have a big delta in the middle of | 741 | * Scan the history: the points where measurement is taken need to |
716 | * our history, the long term delta isn't a valid end-of-charge | 742 | * be fairly static. (check prior to short delta 'area') |
717 | * indicator. | 743 | * (also only check first and last 10 cycles - delta in middle OK) |
718 | */ | 744 | */ |
719 | long_delta = power_history[0] - | 745 | long_delta = power_history[0] - |
720 | power_history[CHARGE_END_ZEROD - 1]; | 746 | power_history[CHARGE_END_LONGD - 1]; |
721 | for(i = 0; i < CHARGE_END_ZEROD; i++) { | 747 | |
748 | for(i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++) { | ||
749 | if(((power_history[i] - power_history[i+1]) > 5) || | ||
750 | ((power_history[i] - power_history[i+1]) < -5)) { | ||
751 | long_delta = 777777; | ||
752 | break; | ||
753 | } | ||
754 | } | ||
755 | for(i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++) { | ||
722 | if(((power_history[i] - power_history[i+1]) > 5) || | 756 | if(((power_history[i] - power_history[i+1]) > 5) || |
723 | ((power_history[i] - power_history[i+1]) < -5)) { | 757 | ((power_history[i] - power_history[i+1]) < -5)) { |
724 | long_delta = 888888; | 758 | long_delta = 888888; |
@@ -727,22 +761,27 @@ static void power_thread(void) | |||
727 | } | 761 | } |
728 | } | 762 | } |
729 | 763 | ||
764 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
765 | "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, | ||
766 | charge_max_time_now); | ||
730 | /* | 767 | /* |
731 | * End of charge criteria (any qualify): | 768 | * End of charge criteria (any qualify): |
732 | * 1) Charged a long time | 769 | * 1) Charged a long time |
733 | * 2) DeltaV went negative for a short time | 770 | * 2) DeltaV went negative for a short time ( & long delta static) |
734 | * 3) DeltaV was close to zero for a long time | 771 | * 3) DeltaV was negative over a longer period (no disk use only) |
735 | * Note: short_delta and long_delta are centivolts | 772 | * Note: short_delta and long_delta are centivolts |
736 | */ | 773 | */ |
737 | if ((powermgmt_last_cycle_startstop_min > charge_max_time_now) || | 774 | if ((powermgmt_last_cycle_startstop_min >= charge_max_time_now) || |
738 | (short_delta <= -5) || (long_delta < 5)) | 775 | (short_delta <= -5 && long_delta < 5 ) || (long_delta < -2 && |
739 | { | 776 | last_disk_activity > CHARGE_END_LONGD)) { |
740 | if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { | 777 | if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { |
741 | DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " | 778 | DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " |
742 | "enough!\n"); | 779 | "enough!\n"); |
743 | /* have charged too long and deltaV detection did not | 780 | /* |
744 | work! */ | 781 | *have charged too long and deltaV detection did not |
745 | snprintf(power_message, POWER_MESSAGE_LEN, | 782 | *work! |
783 | */ | ||
784 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
746 | "Chg tmout %d min", charge_max_time_now); | 785 | "Chg tmout %d min", charge_max_time_now); |
747 | /* | 786 | /* |
748 | * Switch to trickle charging. We skip the top-off | 787 | * Switch to trickle charging. We skip the top-off |
@@ -753,6 +792,16 @@ static void power_thread(void) | |||
753 | powermgmt_last_cycle_level = battery_percent; | 792 | powermgmt_last_cycle_level = battery_percent; |
754 | powermgmt_last_cycle_startstop_min = 0; | 793 | powermgmt_last_cycle_startstop_min = 0; |
755 | charge_state = TRICKLE; | 794 | charge_state = TRICKLE; |
795 | |||
796 | /* | ||
797 | * set trickle charge target to a relative voltage instead | ||
798 | * of an arbitrary value - the fully charged voltage may | ||
799 | * vary according to ambient temp, battery condition etc | ||
800 | * trickle target is -0.15v from full voltage acheived | ||
801 | * topup target is -0.05v from full voltage | ||
802 | */ | ||
803 | target_voltage = power_history[0] - 15; | ||
804 | |||
756 | } else { | 805 | } else { |
757 | if(short_delta <= -5) { | 806 | if(short_delta <= -5) { |
758 | DEBUGF("power: short-term negative" | 807 | DEBUGF("power: short-term negative" |
@@ -760,12 +809,16 @@ static void power_thread(void) | |||
760 | snprintf(power_message, POWER_MESSAGE_LEN, | 809 | snprintf(power_message, POWER_MESSAGE_LEN, |
761 | "end negd %d %dmin", short_delta, | 810 | "end negd %d %dmin", short_delta, |
762 | powermgmt_last_cycle_startstop_min); | 811 | powermgmt_last_cycle_startstop_min); |
812 | target_voltage = power_history[CHARGE_END_SHORTD - 1] | ||
813 | - 5; | ||
763 | } else { | 814 | } else { |
764 | DEBUGF("power: long-term small " | 815 | DEBUGF("power: long-term small " |
765 | "positive delta, enough!\n"); | 816 | "positive delta, enough!\n"); |
766 | snprintf(power_message, POWER_MESSAGE_LEN, | 817 | snprintf(power_message, POWER_MESSAGE_LEN, |
767 | "end lowd %d %dmin", long_delta, | 818 | "end lowd %d %dmin", long_delta, |
768 | powermgmt_last_cycle_startstop_min); | 819 | powermgmt_last_cycle_startstop_min); |
820 | target_voltage = power_history[CHARGE_END_LONGD - 1] | ||
821 | - 5; | ||
769 | } | 822 | } |
770 | /* | 823 | /* |
771 | * Switch to top-off charging. | 824 | * Switch to top-off charging. |
@@ -778,7 +831,8 @@ static void power_thread(void) | |||
778 | } | 831 | } |
779 | else if (charge_state > CHARGING) /* top off or trickle */ | 832 | else if (charge_state > CHARGING) /* top off or trickle */ |
780 | { | 833 | { |
781 | /* Time to switch from topoff to trickle? | 834 | /* |
835 | *Time to switch from topoff to trickle? | ||
782 | */ | 836 | */ |
783 | if ((charge_state == TOPOFF) && | 837 | if ((charge_state == TOPOFF) && |
784 | (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME)) | 838 | (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME)) |
@@ -786,6 +840,7 @@ static void power_thread(void) | |||
786 | powermgmt_last_cycle_level = battery_percent; | 840 | powermgmt_last_cycle_level = battery_percent; |
787 | powermgmt_last_cycle_startstop_min = 0; | 841 | powermgmt_last_cycle_startstop_min = 0; |
788 | charge_state = TRICKLE; | 842 | charge_state = TRICKLE; |
843 | target_voltage = target_voltage - 10; | ||
789 | } | 844 | } |
790 | /* | 845 | /* |
791 | * Adjust trickle charge time (proportional and integral terms). | 846 | * Adjust trickle charge time (proportional and integral terms). |
@@ -793,10 +848,6 @@ static void power_thread(void) | |||
793 | * plugged in, but it doesn't appear to be necessary and will | 848 | * plugged in, but it doesn't appear to be necessary and will |
794 | * generate more heat [gvb]. | 849 | * generate more heat [gvb]. |
795 | */ | 850 | */ |
796 | if(charge_state == TOPOFF) | ||
797 | target_voltage = TOPOFF_VOLTAGE; | ||
798 | else | ||
799 | target_voltage = TRICKLE_VOLTAGE; | ||
800 | 851 | ||
801 | pid_p = target_voltage - battery_centivolts; | 852 | pid_p = target_voltage - battery_centivolts; |
802 | if((pid_p > PID_DEADZONE) || (pid_p < -PID_DEADZONE)) | 853 | if((pid_p > PID_DEADZONE) || (pid_p < -PID_DEADZONE)) |