添加产测充放电拷机功能(FD272/HP4570)。
authorcaisking <caiyqm@qq.com>
Tue, 2 Jun 2026 03:05:55 +0000 (11:05 +0800)
committercaisking <caiyqm@qq.com>
Thu, 11 Jun 2026 03:07:56 +0000 (11:07 +0800)
新增 cc_cycle_service 与拷机 UI,支持插线自动循环:满电放电(Hi-Z+电池供电)至 10% 后按温控充电;拷机放电阶段修正 SOC 显示并关闭热路径调试日志。

Co-authored-by: Cursor <cursoragent@cursor.com>
24 files changed:
code/sdk/application/wearable/fd272/nativeapp/nativelauncher/include/AppViewIDs.h
code/sdk/build/config/target_config/brandy/config.py
code/sdk/zhihui/UI/producttest/CMakeLists.txt
code/sdk/zhihui/UI/producttest/include/ccCycleBurnPresenter.h [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/include/ccCycleBurnView.h [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/include/testCcCycle.h [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/include/testPresenter.h
code/sdk/zhihui/UI/producttest/include/testView.h
code/sdk/zhihui/UI/producttest/src/ccCycleBurnPresenter.cpp [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/src/ccCycleBurnView.cpp [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/src/testCcCycle.cpp [new file with mode: 0644]
code/sdk/zhihui/UI/producttest/src/testPresenter.cpp
code/sdk/zhihui/UI/producttest/src/testView.cpp
code/sdk/zhihui/services/CMakeLists.txt
code/sdk/zhihui/services/battery/hal/battery_hal.cpp
code/sdk/zhihui/services/battery/hal/battery_hal.h
code/sdk/zhihui/services/battery/service/battery_driver.cpp
code/sdk/zhihui/services/battery/service/battery_service.cpp
code/sdk/zhihui/services/battery/service/battery_service.h
code/sdk/zhihui/services/cc_cycle_service/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.c [new file with mode: 0644]
code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.h [new file with mode: 0644]
code/sdk/zhihui/utils/CMakeLists.txt
code/sdk/zhihui/utils/custom_utils.c

index 1717493..5c3d0e9 100644 (file)
@@ -143,6 +143,7 @@ typedef enum : uint16_t
   // VIEW_MAIN_PHONE,     // 主电话Slice
   VIEW_DIALING_METHOD, // 拨号方式
   VIEW_TEST,           // 测试
+  VIEW_CC_CYCLE_BURN,  // 充放电拷机亮屏
   VIEW_CALCULATOR,     // 计算器
   // VIEW_SCHEDULE,       // 日程
   VIEW_SLEEPMODE,      // sleep mode
index 1006a59..3739b6d 100644 (file)
@@ -1201,6 +1201,7 @@ target = {
                            "blemoder",
                            "transspecard",
                            "factorytest_service",
+                           "cc_cycle_service",
                         #    "hid_service",
                            "producttest",
                             "ppg_hal",
index b7c7891..c2631cc 100644 (file)
@@ -21,6 +21,9 @@ set(SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testhrsenson.cpp 
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testgsenson.cpp  
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testold.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/testCcCycle.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/ccCycleBurnView.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/ccCycleBurnPresenter.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testbt.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testSEChipTest.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/src/testModel.cpp
@@ -71,6 +74,7 @@ set(PRIVATE_HEADER
     ${ROOT_DIR}/open_source/openharmony/third_party/qrcodegen/cpp
 
     ${ROOT_DIR}/zhihui/services/factorytest_service
+    ${ROOT_DIR}/zhihui/services/cc_cycle_service
     ${ROOT_DIR}/zhihui/drivers/
     ${ROOT_DIR}/zhihui/drivers/charge/hp4570
     ${ROOT_DIR}/zhihui/4g/cat1Service/include
@@ -132,6 +136,7 @@ set(PRIVATE_HEADER
     ${ROOT_DIR}/application/wearable/fd272/nativeapp/nativeui/alarmclocktimerconfig/include
     ${ROOT_DIR}/application/wearable/fd272/nativeapp/nativeui/player/include
     ${ROOT_DIR}/zhihui/services/factorytest_service
+    ${ROOT_DIR}/zhihui/services/cc_cycle_service
 )
 elseif( BOARD_N9 IN_LIST DEFINES )
 set(PRIVATE_HEADER
diff --git a/code/sdk/zhihui/UI/producttest/include/ccCycleBurnPresenter.h b/code/sdk/zhihui/UI/producttest/include/ccCycleBurnPresenter.h
new file mode 100644 (file)
index 0000000..2992785
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef CC_CYCLE_BURN_PRESENTER_H
+#define CC_CYCLE_BURN_PRESENTER_H
+
+#include "Presenter.h"
+#include "ccCycleBurnView.h"
+namespace OHOS
+{
+    class ccCycleBurnPresenter : public Presenter<ccCycleBurnView>,
+                                 public UIView::OnDragListener
+    {
+    public:
+        ccCycleBurnPresenter();
+        ~ccCycleBurnPresenter() override;
+        static ccCycleBurnPresenter *GetInstance(void);
+
+        void OnStart() override;
+        void OnResume() override;
+        void OnPause() override;
+        void OnStop() override;
+
+    private:
+        bool OnDrag(UIView &view, const DragEvent &event) override;
+        bool OnDragEnd(UIView &view, const DragEvent &event) override;
+        static bool IsSwipeBackToExit(const DragEvent &event);
+    };
+}
+
+#endif
diff --git a/code/sdk/zhihui/UI/producttest/include/ccCycleBurnView.h b/code/sdk/zhihui/UI/producttest/include/ccCycleBurnView.h
new file mode 100644 (file)
index 0000000..8ccd02f
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef CC_CYCLE_BURN_VIEW_H
+#define CC_CYCLE_BURN_VIEW_H
+
+#include "View.h"
+#include "components/ui_view_group.h"
+#include "components/ui_label.h"
+
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+#endif
+
+namespace OHOS
+{
+    class ccCycleBurnPresenter;
+
+    class ccCycleBurnView : public View<ccCycleBurnPresenter>
+    {
+    public:
+        ccCycleBurnView();
+        ~ccCycleBurnView() override;
+        static ccCycleBurnView *GetInstance(void);
+
+        void OnStart() override;
+        void OnStop() override;
+        void RefreshStatus(void);
+        void ApplyScreenAlwaysOn(void);
+
+    private:
+        void DestroyBurnWidgets(void);
+        bool CreateBurnView(void);
+
+        UIViewGroup *burnGroup_{nullptr};
+        UILabel *phaseLabel_{nullptr};
+        UILabel *statusLabel_{nullptr};
+        bool viewInit_{false};
+        void UpdatePhaseBackground(cc_cycle_phase_e phase);
+    };
+}
+
+#endif
diff --git a/code/sdk/zhihui/UI/producttest/include/testCcCycle.h b/code/sdk/zhihui/UI/producttest/include/testCcCycle.h
new file mode 100644 (file)
index 0000000..46da2d2
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef TEST_CC_CYCLE_VIEW_H
+#define TEST_CC_CYCLE_VIEW_H
+
+#include "components/ui_scroll_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button_ext.h"
+
+namespace OHOS
+{
+    class testCcCycleView
+    {
+    public:
+        testCcCycleView();
+        virtual ~testCcCycleView();
+        static testCcCycleView *GetInstance(void);
+
+        UIScrollView *testCcCycleInitIView(uint8_t id);
+        void ClearAll(void);
+        void RefreshCcCycleButtons(void);
+        void RefreshStatusLabel(void);
+
+    private:
+        static void StatusTimerCallback(void *arg);
+        bool testCcCycleScrollViewInit(uint8_t id);
+        UIScrollView *testCcCycleScrollView_{nullptr};
+        UILabel *title_{nullptr};
+        UILabel *statusLabel_{nullptr};
+        UILabel *hintLabel_{nullptr};
+        UILabelButtonExt *labelButton_cc_sw_{nullptr};
+        UILabelButtonExt *labelButton_cc_enter_{nullptr};
+        UILabelButtonExt *labelButton_next_{nullptr};
+    };
+}
+
+#endif
index 6ec2b31..911e3a3 100644 (file)
@@ -62,6 +62,9 @@ namespace OHOS
 #endif\r
         LUMINANCE,\r
         CHARG,\r
+#ifdef ENABLE_HP4570\r
+        CCCYCLE,\r
+#endif\r
         SPEAK,\r
         MOTOR,\r
 #ifdef SUPPORT_CAT1_SERVICE\r
index c62f886..86ac488 100644 (file)
@@ -26,6 +26,9 @@
 #include "producttest/include/testhrsenson.h"\r
 #include "producttest/include/testgsenson.h"\r
 #include "producttest/include/testold.h"\r
+#ifdef ENABLE_HP4570\r
+#include "producttest/include/testCcCycle.h"\r
+#endif\r
 #include "producttest/include/testbt.h"\r
 #include "producttest/include/testSEChipTest.h"\r
 #include "producttest/include/testPing.h"\r
@@ -85,6 +88,9 @@ namespace OHOS
         bool ChangeTPTESTView(int16_t thisView);\r
         bool ChangLUMTESTView(int16_t thisView);\r
         bool ChangchargingTESTView(int16_t thisView);\r
+#ifdef ENABLE_HP4570\r
+        bool ChangeCcCycleTESTView(int16_t thisView);\r
+#endif\r
         bool ChangspeakTESTView(int16_t thisView);\r
         bool ChangmotorTESTView(int16_t thisView);\r
         bool ChangKEYTESTView(int16_t thisView);\r
@@ -144,6 +150,9 @@ namespace OHOS
         testTPView *test_TP_view{nullptr};\r
         testLUMView *test_lum_view{nullptr};\r
         testCHANINGView *test_charging_view{nullptr};\r
+#ifdef ENABLE_HP4570\r
+        testCcCycleView *test_cc_cycle_view{nullptr};\r
+#endif\r
         testSPEAKView *test_speak_view{nullptr};\r
         testMOTORView *test_motor_view{nullptr};\r
         testcat1View *test_cat1_view{nullptr};\r
diff --git a/code/sdk/zhihui/UI/producttest/src/ccCycleBurnPresenter.cpp b/code/sdk/zhihui/UI/producttest/src/ccCycleBurnPresenter.cpp
new file mode 100644 (file)
index 0000000..6830b9b
--- /dev/null
@@ -0,0 +1,168 @@
+#include "AppViewIDs.h"
+#include "NativeAbility.h"
+#include "NativeRegisterManager.h"
+#include "producttest/include/ccCycleBurnPresenter.h"
+#include "producttest/include/ccCycleBurnView.h"
+#include "producttest/include/testPresenter.h"
+#include "producttest/include/testView.h"
+#include "producttest/include/testCcCycle.h"
+#include "wearable_log.h"
+#include "common/screen.h"
+#include "cmsis_os2.h"
+#include <cstdlib>
+#include <cstring>
+
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+#endif
+
+static osTimerId_t g_ccBurnRefreshTimer = nullptr;
+static osTimerId_t g_ccBurnShowUiTimer = nullptr;
+static volatile uint8_t g_burnUiValid = 0;
+
+static void cc_burn_refresh_timer_cb(void *arg)
+{
+    (void)arg;
+    if (!g_burnUiValid) {
+        return;
+    }
+#ifdef ENABLE_HP4570
+    if (!cc_cycle_is_running()) {
+        return;
+    }
+#endif
+    OHOS::ccCycleBurnView *v = OHOS::ccCycleBurnView::GetInstance();
+    if (v != nullptr) {
+        v->RefreshStatus();
+    }
+}
+
+static void cc_burn_show_ui_timer_cb(void *arg)
+{
+    (void)arg;
+#ifdef ENABLE_HP4570
+    if (!cc_cycle_is_running()) {
+        return;
+    }
+#endif
+    OHOS::NativeAbility::GetInstance().ChangeSlice(VIEW_CC_CYCLE_BURN);
+}
+
+namespace OHOS
+{
+    REGIST_SLICE(VIEW_CC_CYCLE_BURN, ccCycleBurnView, ccCycleBurnPresenter);
+
+    static ccCycleBurnPresenter *g_burnPresenter = nullptr;
+
+    ccCycleBurnPresenter::ccCycleBurnPresenter()
+    {
+        g_burnPresenter = this;
+    }
+
+    ccCycleBurnPresenter::~ccCycleBurnPresenter()
+    {
+        if (g_ccBurnRefreshTimer != nullptr) {
+            (void)osTimerStop(g_ccBurnRefreshTimer);
+            (void)osTimerDelete(g_ccBurnRefreshTimer);
+            g_ccBurnRefreshTimer = nullptr;
+        }
+        g_burnPresenter = nullptr;
+    }
+
+    ccCycleBurnPresenter *ccCycleBurnPresenter::GetInstance(void)
+    {
+        return g_burnPresenter;
+    }
+
+    bool ccCycleBurnPresenter::IsSwipeBackToExit(const DragEvent &event)
+    {
+        if (event.GetDragDirection() != DragEvent::DIRECTION_LEFT_TO_RIGHT) {
+            return false;
+        }
+        Point startPoint = event.GetStartPoint();
+        Point endPoint = event.GetLastPoint();
+        int dragDistance = endPoint.x - startPoint.x;
+        int dragHeight = abs(endPoint.y - startPoint.y);
+        int screenWidth = Screen::GetInstance().GetWidth();
+
+        return (dragDistance > 80 && dragHeight < 80 &&
+            startPoint.x < screenWidth / 2 && dragDistance > dragHeight * 2);
+    }
+
+    bool ccCycleBurnPresenter::OnDrag(UIView &view, const DragEvent &event)
+    {
+        (void)view;
+        (void)event;
+        return true;
+    }
+
+    bool ccCycleBurnPresenter::OnDragEnd(UIView &view, const DragEvent &event)
+    {
+        (void)view;
+        if (IsSwipeBackToExit(event)) {
+            cc_cycle_hide_burn_ui();
+            return true;
+        }
+        return false;
+    }
+
+    void ccCycleBurnPresenter::OnStart()
+    {
+        Presenter<ccCycleBurnView>::OnStart();
+        g_burnUiValid = 1;
+        if (g_ccBurnRefreshTimer == nullptr) {
+            g_ccBurnRefreshTimer = osTimerNew(cc_burn_refresh_timer_cb, osTimerPeriodic, nullptr, nullptr);
+        }
+        if (g_ccBurnRefreshTimer != nullptr) {
+            (void)osTimerStart(g_ccBurnRefreshTimer, 1000);
+        }
+    }
+
+    void ccCycleBurnPresenter::OnResume(void)
+    {
+        if (view_ != nullptr) {
+            view_->ApplyScreenAlwaysOn();
+            view_->RefreshStatus();
+        }
+    }
+
+    void ccCycleBurnPresenter::OnPause(void)
+    {
+        Presenter<ccCycleBurnView>::OnPause();
+    }
+
+    void ccCycleBurnPresenter::OnStop(void)
+    {
+        g_burnUiValid = 0;
+        if (g_ccBurnRefreshTimer != nullptr) {
+            (void)osTimerStop(g_ccBurnRefreshTimer);
+        }
+        Presenter<ccCycleBurnView>::OnStop();
+    }
+}
+
+void cc_cycle_show_burn_ui(void)
+{
+    if (g_ccBurnShowUiTimer == nullptr) {
+        g_ccBurnShowUiTimer = osTimerNew(cc_burn_show_ui_timer_cb, osTimerOnce, nullptr, nullptr);
+    }
+    if (g_ccBurnShowUiTimer != nullptr) {
+        (void)osTimerStop(g_ccBurnShowUiTimer);
+        (void)osTimerStart(g_ccBurnShowUiTimer, 500);
+    }
+}
+
+void cc_cycle_hide_burn_ui(void)
+{
+    OHOS::NativeAbility::GetInstance().ChangeSlice(VIEW_TEST);
+    if (OHOS::testView::GetInstance() != nullptr) {
+#ifdef ENABLE_HP4570
+        OHOS::testView::GetInstance()->ChangeView(OHOS::TESTVIEW, OHOS::CCCYCLE);
+#else
+        OHOS::testView::GetInstance()->ChangeView(OHOS::TESTVIEW, OHOS::TESTVIEW);
+#endif
+    }
+    if (OHOS::testCcCycleView::GetInstance() != nullptr) {
+        OHOS::testCcCycleView::GetInstance()->RefreshCcCycleButtons();
+    }
+}
diff --git a/code/sdk/zhihui/UI/producttest/src/ccCycleBurnView.cpp b/code/sdk/zhihui/UI/producttest/src/ccCycleBurnView.cpp
new file mode 100644 (file)
index 0000000..8621238
--- /dev/null
@@ -0,0 +1,196 @@
+#include "producttest/include/ccCycleBurnView.h"
+#include "producttest/include/ccCycleBurnPresenter.h"
+#include "wearable_log.h"
+#include "common/screen.h"
+#include "gfx_utils/color.h"
+#include "power_display_service.h"
+#include "custom_utils.h"
+#include <cstdio>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "pm_definition.h"
+#include "pm_veto.h"
+#include "pm.h"
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+#endif
+
+namespace OHOS
+{
+    static ccCycleBurnView *g_burnView = nullptr;
+
+    ccCycleBurnView::ccCycleBurnView()
+    {
+        g_burnView = this;
+    }
+
+    ccCycleBurnView::~ccCycleBurnView()
+    {
+        DestroyBurnWidgets();
+        g_burnView = nullptr;
+    }
+
+    ccCycleBurnView *ccCycleBurnView::GetInstance(void)
+    {
+        return g_burnView;
+    }
+
+    void ccCycleBurnView::UpdatePhaseBackground(cc_cycle_phase_e phase)
+    {
+        if (burnGroup_ == nullptr) {
+            return;
+        }
+        if (phase == CC_PHASE_CHARGING) {
+            burnGroup_->SetStyle(STYLE_BACKGROUND_COLOR, 0xFFC8E6C9); /* 浅绿=充电 */
+            if (phaseLabel_ != nullptr) {
+                phaseLabel_->SetStyle(STYLE_TEXT_COLOR, 0xFF1B5E20);
+                phaseLabel_->SetText("【充电中】");
+            }
+        } else if (phase == CC_PHASE_DISCHARGE) {
+            burnGroup_->SetStyle(STYLE_BACKGROUND_COLOR, Color::White().full);
+            if (phaseLabel_ != nullptr) {
+                phaseLabel_->SetStyle(STYLE_TEXT_COLOR, Color::Black().full);
+                phaseLabel_->SetText("【放电中】");
+            }
+        } else {
+            burnGroup_->SetStyle(STYLE_BACKGROUND_COLOR, Color::Gray().full);
+            if (phaseLabel_ != nullptr) {
+                phaseLabel_->SetText("【空闲】");
+            }
+        }
+    }
+
+    void ccCycleBurnView::ApplyScreenAlwaysOn(void)
+    {
+        const power_display_svr_api_t *display_api = power_display_svr_get_api();
+        if (display_api == nullptr) {
+            return;
+        }
+        if (display_api->get_screen_state() != SCREEN_ON) {
+            display_api->turn_on_screen();
+        }
+        display_api->set_auto_timeout_function(false);
+        uapi_setScreenBrightness(255);
+    }
+
+    void ccCycleBurnView::RefreshStatus(void)
+    {
+#ifdef ENABLE_HP4570
+        cc_cycle_phase_e p = cc_cycle_get_phase();
+        UpdatePhaseBackground(p);
+
+        if (statusLabel_ != nullptr) {
+            char buf[96];
+            (void)cc_cycle_format_status_line(buf, sizeof(buf));
+            statusLabel_->SetText(buf);
+        }
+        // RootView::GetInstance()->Invalidate();
+#else
+        if (statusLabel_ != nullptr) {
+            statusLabel_->SetText("拷机");
+        }
+#endif
+        ApplyScreenAlwaysOn();
+    }
+
+    void ccCycleBurnView::DestroyBurnWidgets(void)
+    {
+        if (burnGroup_ == nullptr) {
+            return;
+        }
+        UIViewGroup *root = GetRootContainer();
+        if (root != nullptr) {
+            root->Remove(burnGroup_);
+        }
+        burnGroup_->RemoveAll();
+        delete phaseLabel_;
+        delete statusLabel_;
+        delete burnGroup_;
+        phaseLabel_ = nullptr;
+        statusLabel_ = nullptr;
+        burnGroup_ = nullptr;
+        viewInit_ = false;
+    }
+
+    bool ccCycleBurnView::CreateBurnView(void)
+    {
+        int w = Screen::GetInstance().GetWidth();
+        int h = Screen::GetInstance().GetHeight();
+
+        burnGroup_ = new UIViewGroup();
+        if (burnGroup_ == nullptr) {
+            return false;
+        }
+        burnGroup_->SetPosition(0, 0, w, h);
+        burnGroup_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+        burnGroup_->SetStyle(STYLE_BACKGROUND_COLOR, Color::White().full);
+        burnGroup_->SetTouchable(true);
+
+        phaseLabel_ = new UILabel();
+        if (phaseLabel_ == nullptr) {
+            return false;
+        }
+        phaseLabel_->SetPosition(0, h / 2 - 80, w, 64);
+        phaseLabel_->SetStyle(STYLE_TEXT_FONT, 40);
+        phaseLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        phaseLabel_->SetText("【拷机】");
+        burnGroup_->Add(phaseLabel_);
+
+        statusLabel_ = new UILabel();
+        if (statusLabel_ == nullptr) {
+            return false;
+        }
+        statusLabel_->SetPosition(20, h / 2, w - 40, 72);
+        statusLabel_->SetStyle(STYLE_TEXT_COLOR, Color::Black().full);
+        statusLabel_->SetStyle(STYLE_TEXT_FONT, 24);
+        statusLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_TOP);
+        statusLabel_->SetText("左滑返回");
+        burnGroup_->Add(statusLabel_);
+
+        burnGroup_->SetDraggable(true);
+        UIView::OnDragListener *dragListener =
+            static_cast<UIView::OnDragListener *>(ccCycleBurnPresenter::GetInstance());
+        if (dragListener != nullptr) {
+            burnGroup_->SetOnDragListener(dragListener);
+        }
+
+        AddViewToRootContainer(burnGroup_);
+        return true;
+    }
+
+    void ccCycleBurnView::OnStart()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ccCycleBurnView::OnStart");
+        if (!viewInit_) {
+            if (!CreateBurnView()) {
+                return;
+            }
+            viewInit_ = true;
+        }
+        ApplyScreenAlwaysOn();
+        uint8_t cnt = pm_veto_get_id_cnt(PM_ID_P_TEST);
+        if (cnt == 0) {
+            uapi_pm_add_sleep_veto(PM_ID_P_TEST);
+        }
+        RefreshStatus();
+    }
+
+    void ccCycleBurnView::OnStop()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ccCycleBurnView::OnStop");
+        const power_display_svr_api_t *display_api = power_display_svr_get_api();
+        if (display_api != nullptr) {
+            display_api->set_auto_timeout_function(true);
+        }
+        uint8_t cnt = pm_veto_get_id_cnt(PM_ID_P_TEST);
+        if (cnt == 1) {
+            uapi_pm_remove_sleep_veto(PM_ID_P_TEST);
+        }
+    }
+}
diff --git a/code/sdk/zhihui/UI/producttest/src/testCcCycle.cpp b/code/sdk/zhihui/UI/producttest/src/testCcCycle.cpp
new file mode 100644 (file)
index 0000000..9d4b843
--- /dev/null
@@ -0,0 +1,203 @@
+#include "wearable_log.h"
+#include "custom_utils.h"
+#include "producttest/include/testPresenter.h"
+#include "producttest/include/testCcCycle.h"
+#include "producttest/include/testView.h"
+#include "producttest/include/testabout.h"
+#include "common/screen.h"
+#include "gfx_utils/color.h"
+#include "cmsis_os2.h"
+#include <cstdio>
+
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+
+namespace OHOS
+{
+    static testCcCycleView *g_testCcCycleView = nullptr;
+    static osTimerId_t g_ccCycleStatusTimer = nullptr;
+
+    testCcCycleView::testCcCycleView()
+    {
+        g_testCcCycleView = this;
+    }
+
+    testCcCycleView::~testCcCycleView()
+    {
+        if (g_ccCycleStatusTimer != nullptr) {
+            (void)osTimerStop(g_ccCycleStatusTimer);
+            (void)osTimerDelete(g_ccCycleStatusTimer);
+            g_ccCycleStatusTimer = nullptr;
+        }
+        ClearAll();
+        g_testCcCycleView = nullptr;
+    }
+
+    void testCcCycleView::StatusTimerCallback(void *arg)
+    {
+        (void)arg;
+        testCcCycleView *v = testCcCycleView::GetInstance();
+        if (v != nullptr) {
+            v->RefreshCcCycleButtons();
+        }
+    }
+
+    void testCcCycleView::RefreshStatusLabel(void)
+    {
+        if (statusLabel_ == nullptr) {
+            return;
+        }
+        char buf[96];
+        (void)cc_cycle_format_status_line(buf, sizeof(buf));
+        statusLabel_->SetText(buf);
+    }
+
+    testCcCycleView *testCcCycleView::GetInstance(void)
+    {
+        return g_testCcCycleView;
+    }
+
+    void testCcCycleView::RefreshCcCycleButtons(void)
+    {
+        if (labelButton_cc_sw_ != nullptr) {
+            if (cc_cycle_get_enabled()) {
+                labelButton_cc_sw_->SetText("拷机开关:开");
+            } else {
+                labelButton_cc_sw_->SetText("拷机开关:关");
+            }
+        }
+        if (labelButton_cc_enter_ != nullptr) {
+            if (cc_cycle_is_running()) {
+                labelButton_cc_enter_->SetText("停止自动拷机");
+            } else {
+                labelButton_cc_enter_->SetText("开始自动拷机(需插线)");
+            }
+        }
+        RefreshStatusLabel();
+    }
+
+    void testCcCycleView::ClearAll(void)
+    {
+        if (testCcCycleScrollView_ == nullptr) {
+            return;
+        }
+        testCcCycleScrollView_->RemoveAll();
+        if (labelButton_cc_sw_ != nullptr) {
+            delete labelButton_cc_sw_;
+            labelButton_cc_sw_ = nullptr;
+        }
+        if (labelButton_cc_enter_ != nullptr) {
+            delete labelButton_cc_enter_;
+            labelButton_cc_enter_ = nullptr;
+        }
+        if (labelButton_next_ != nullptr) {
+            delete labelButton_next_;
+            labelButton_next_ = nullptr;
+        }
+        if (hintLabel_ != nullptr) {
+            delete hintLabel_;
+            hintLabel_ = nullptr;
+        }
+        if (statusLabel_ != nullptr) {
+            delete statusLabel_;
+            statusLabel_ = nullptr;
+        }
+        if (title_ != nullptr) {
+            delete title_;
+            title_ = nullptr;
+        }
+        delete testCcCycleScrollView_;
+        testCcCycleScrollView_ = nullptr;
+    }
+
+    bool testCcCycleView::testCcCycleScrollViewInit(uint8_t id)
+    {
+        int w = Screen::GetInstance().GetWidth();
+        int h = Screen::GetInstance().GetHeight();
+
+        testCcCycleScrollView_ = new UIScrollView();
+        if (testCcCycleScrollView_ == nullptr) {
+            return false;
+        }
+        testCcCycleScrollView_->SetPosition(0, 0, w, h);
+        testCcCycleScrollView_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+        testCcCycleScrollView_->SetStyle(STYLE_BACKGROUND_COLOR, Color::Black().full);
+
+        title_ = testAboutView::GetInstance()->init_title(165, 45, "充放电拷机");
+        if (title_ == nullptr) {
+            return false;
+        }
+        testCcCycleScrollView_->Add(title_);
+
+        statusLabel_ = new UILabel();
+        if (statusLabel_ == nullptr) {
+            return false;
+        }
+        statusLabel_->SetPosition(30, 90, w - 60, 56);
+        statusLabel_->SetStyle(STYLE_TEXT_COLOR, Color::Yellow().full);
+        statusLabel_->SetStyle(STYLE_TEXT_FONT, 26);
+        statusLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        statusLabel_->SetText("状态:未运行");
+        testCcCycleScrollView_->Add(statusLabel_);
+
+        hintLabel_ = new UILabel();
+        if (hintLabel_ == nullptr) {
+            return false;
+        }
+        hintLabel_->SetPosition(40, 120, w - 80, 80);
+        hintLabel_->SetStyle(STYLE_TEXT_COLOR, Color::White().full);
+        hintLabel_->SetStyle(STYLE_TEXT_FONT, 24);
+        hintLabel_->SetText("插线后开开关再开始;亮屏页左滑返回;本页左滑退出");
+        hintLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_TOP);
+        testCcCycleScrollView_->Add(hintLabel_);
+
+        (void)cc_cycle_init();
+        labelButton_cc_sw_ = testAboutView::GetInstance()->init_button_next(
+            300, 280, Test_ViewMapper_t[id].viewid_str_next);
+        UIView::OnClickListener *clickListener =
+            static_cast<UIView::OnClickListener *>(testPresenter::GetInstance());
+
+        labelButton_cc_sw_->SetViewId("cc_cycle_sw");
+        labelButton_cc_sw_->SetText("拷机开关:关");
+        labelButton_cc_sw_->SetOnClickListener(clickListener);
+        testCcCycleScrollView_->Add(labelButton_cc_sw_);
+
+        labelButton_cc_enter_ = testAboutView::GetInstance()->init_button_next(
+            300, 380, Test_ViewMapper_t[id].viewid_str_next);
+        labelButton_cc_enter_->SetViewId("cc_cycle_enter");
+        labelButton_cc_enter_->SetText("开始自动拷机(需插线)");
+        labelButton_cc_enter_->SetOnClickListener(clickListener);
+        testCcCycleScrollView_->Add(labelButton_cc_enter_);
+
+        labelButton_next_ = testAboutView::GetInstance()->init_button_next(
+            267, 480, Test_ViewMapper_t[id].viewid_str_next);
+        labelButton_next_->SetOnClickListener(clickListener);
+        testCcCycleScrollView_->Add(labelButton_next_);
+
+        RefreshCcCycleButtons();
+        if (g_ccCycleStatusTimer == nullptr) {
+            g_ccCycleStatusTimer = osTimerNew(StatusTimerCallback, osTimerPeriodic, nullptr, nullptr);
+        }
+        if (g_ccCycleStatusTimer != nullptr) {
+            (void)osTimerStart(g_ccCycleStatusTimer, 1000);
+        }
+        testCcCycleScrollView_->SetTouchable(true);
+        testCcCycleScrollView_->SetDraggable(true);
+        UIView::OnDragListener *dragListener =
+            static_cast<UIView::OnDragListener *>(testPresenter::GetInstance());
+        if (dragListener != nullptr) {
+            testCcCycleScrollView_->SetOnDragListener(dragListener);
+        }
+        return true;
+    }
+
+    UIScrollView *testCcCycleView::testCcCycleInitIView(uint8_t id)
+    {
+        if (!testCcCycleScrollViewInit(id)) {
+            return nullptr;
+        }
+        return testCcCycleScrollView_;
+    }
+}
+
+#endif /* ENABLE_HP4570 */
index d19b5ca..0daa026 100644 (file)
 #ifdef CUSTOM_ZHIHUI
     #include "factorytest_protocol.h"
 #endif
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+#include "producttest/include/testCcCycle.h"
+#endif
 
 
 
@@ -259,6 +263,30 @@ namespace OHOS
             return true;
         }
 
+#ifdef ENABLE_HP4570
+        if (strcmp(view.GetViewId(), "cc_cycle_sw") == 0) {
+            cc_cycle_set_enabled(cc_cycle_get_enabled() ? 0 : 1);
+            if (testCcCycleView::GetInstance() != nullptr) {
+                testCcCycleView::GetInstance()->RefreshCcCycleButtons();
+            }
+            return true;
+        }
+        if (strcmp(view.GetViewId(), "cc_cycle_enter") == 0) {
+            if (!cc_cycle_get_enabled()) {
+                return true;
+            }
+            if (cc_cycle_is_running()) {
+                cc_cycle_stop_automation();
+            } else if (cc_cycle_enter_test_ui() != ERRCODE_SUCC) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "cc_cycle: need charger plugged");
+            }
+            if (testCcCycleView::GetInstance() != nullptr) {
+                testCcCycleView::GetInstance()->RefreshCcCycleButtons();
+            }
+            return true;
+        }
+#endif
+
          for(int i=0;i<len;i++)
         {
             if(strcmp(view.GetViewId(),Test_ViewMapper_t[i].viewid_str) == 0)
index 3b50fb7..01d49ac 100644 (file)
@@ -49,6 +49,9 @@ namespace OHOS
 #endif
         {LUMINANCE, "luminanceButton", "亮度", "luminanceButton_next", &testView::ChangLUMTESTView, true},
         {CHARG, "chargingButton", "电池充电", "chargingButton_next", &testView::ChangchargingTESTView, true},
+#ifdef ENABLE_HP4570
+        {CCCYCLE, "ccCycleButton", "充放电拷机", "ccCycleButton_next", &testView::ChangeCcCycleTESTView, true},
+#endif
         {SPEAK, "speakButton", "喇叭", "speakButton_next", &testView::ChangspeakTESTView, true},
         {MOTOR, "motorButton", "马达", "motorButton_next", &testView::ChangmotorTESTView, true},
 #ifdef SUPPORT_CAT1_SERVICE
@@ -593,6 +596,23 @@ namespace OHOS
         return true;
     }
 
+#ifdef ENABLE_HP4570
+    bool testView::ChangeCcCycleTESTView(int16_t thisView)
+    {
+        if (SystemScrollView[thisView] == nullptr) {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SystemView::ChangeCcCycleTESTView");
+            test_cc_cycle_view = new testCcCycleView();
+            if (test_cc_cycle_view == nullptr) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SystemView:: new scrollView[%d] fail", thisView);
+                return false;
+            }
+            SystemScrollView[thisView] = test_cc_cycle_view->testCcCycleInitIView((uint8_t)thisView);
+            group->Add(SystemScrollView[thisView]);
+        }
+        return true;
+    }
+#endif
+
     bool testView::ChangspeakTESTView(int16_t thisView)
     {
         if (SystemScrollView[thisView] == nullptr)
@@ -1041,6 +1061,15 @@ namespace OHOS
                 test_G_view = nullptr;
                 SystemScrollView[hideView] = nullptr;
                 break;
+#ifdef ENABLE_HP4570
+            case CCCYCLE:
+                if (SystemScrollView[hideView] != nullptr)
+                    group->Remove(SystemScrollView[hideView]);
+                delete test_cc_cycle_view;
+                test_cc_cycle_view = nullptr;
+                SystemScrollView[hideView] = nullptr;
+                break;
+#endif
             case OLDTEST:
                 if (SystemScrollView[hideView] != nullptr)
                     group->Remove(SystemScrollView[hideView]);
index db17dee..7fb54b7 100644 (file)
@@ -12,5 +12,6 @@ else()
 endif()
 add_subdirectory_if_exist(ble_service)
 add_subdirectory_if_exist(factorytest_service)
+add_subdirectory_if_exist(cc_cycle_service)
 add_subdirectory_if_exist(hid_service)
 
index 85452d8..e4dc86c 100644 (file)
@@ -23,3 +23,8 @@ uint8_t battery_get_voltage(float *voltage)
     return batteryservice_get_voltage(voltage);
 }
 
+void battery_set_force_discharge_soc(uint8_t on)
+{
+    batteryservice_set_force_discharge_soc(on);
+}
+
index 749061a..0472090 100644 (file)
@@ -45,6 +45,9 @@ uint8_t get_charge_stat(void);
 
 void battery_percent_init(uint8_t percent);
 
+/* 拷机放电:仍插线但禁充时,按未插线曲线/缓降规则算 SOC(避免 99% 锁死) */
+void battery_set_force_discharge_soc(uint8_t on);
+
 #ifdef __cplusplus
 #if __cplusplus
 }
index 7159db3..8ae60bf 100644 (file)
@@ -72,6 +72,18 @@ typedef enum {
 #include "debug_print.h"
 // #include "cat1_utils.h"
 #include <math.h>
+
+/* 电量热路径调试:拷机每秒多次算 SOC + printf 易堵串口/耗 CPU,默认关 */
+#ifndef BATTERY_SOC_VERBOSE_LOG
+#define BATTERY_SOC_VERBOSE_LOG 0
+#endif
+#if BATTERY_SOC_VERBOSE_LOG
+#define BAT_SOC_LOG(...) printf(__VA_ARGS__)
+#define BAT_SOC_PRINT(...) PRINT(__VA_ARGS__)
+#else
+#define BAT_SOC_LOG(...) ((void)0)
+#define BAT_SOC_PRINT(...) ((void)0)
+#endif
 // #include "custom_save_runtime.h"
 
 #include "charge_ic.h"
@@ -552,7 +564,25 @@ static uint32_t g_adc_sample_stick = 0;
 static BAT_VOL_T bat_vol;
 
 static int history_percent = 0;
+static uint8_t cur_percent = 0;
+static uint8_t g_force_discharge_soc_mode = 0;
+/* 拷机放电阶段 SOC 峰值,禁止显示电量回升 */
+static int s_cc_burn_discharge_soc_peak = -1;
 
+void batteryservice_set_force_discharge_soc(uint8_t on)
+{
+    g_force_discharge_soc_mode = on ? 1 : 0;
+    if (on) {
+        if (s_cc_burn_discharge_soc_peak < 0) {
+            int seed = history_percent > 0 ? history_percent :
+                       (cur_percent > 0 ? (int)cur_percent : -1);
+            s_cc_burn_discharge_soc_peak = seed;
+        }
+    } else {
+        s_cc_burn_discharge_soc_peak = -1;
+    }
+}
+  
 // static float sum_vol = 0;
 // static int vol_num=0;
 
@@ -564,8 +594,6 @@ static uint32_t sample_time = 0;
 
 static battery_info_t current_battery_info={0};
 
-static uint8_t cur_percent = 0;
-
 static uint8_t percernt_installized = 0;
 
 static float test_temp = 0;
@@ -799,9 +827,9 @@ static bool battery_check_charge_done(float voltage, int *percent)
     if (s_chg_done_flat_cnt >= CHG_DONE_FLAT_STREAK && history_percent < 100) {
         history_percent++;
         *percent = history_percent;
-        printf("[电池计算] CHG_DONE+>=%.1fV 连续电压不升 cnt=%u(>=%u) -> 累加 %% hist=%d\n",
-            CHG_DONE_MIN_VOLTAGE_V,
-            (unsigned int)s_chg_done_flat_cnt, (unsigned int)CHG_DONE_FLAT_STREAK, history_percent);
+        BAT_SOC_LOG("[电池计算] HP4570 CHG_DONE+>=%.1fV 连续电压不升 cnt=%u(>=%u) -> 累加 %% hist=%d\n",
+            HP4570_CHG_DONE_MIN_VOLTAGE_V,
+            (unsigned int)s_hp4570_chg_done_flat_cnt, (unsigned int)HP4570_CHG_DONE_FLAT_STREAK, history_percent);
         if (history_percent >= 100) {
             s_chg_done_flat_cnt = 0;
             s_chg_done_last_v = -1.0f;
@@ -820,38 +848,40 @@ static int battery_get_percent_by_voltage(float voltage)
 {
     uint32_t current_time = 0;
     int percent = 0;
-    // ===================== 最小修改:根据充电状态选择MAP =====================
-    const float (*battery_map)[2] = get_charge_stat() ? battery_charge_percent_map : battery_percent_map;
+    /* 插线拷机禁充放电:仍检测到 plug,但按未插线 MAP/缓降算电量 */
+    uint8_t treat_as_charging = (get_charge_stat() != 0) && (g_force_discharge_soc_mode == 0);
+    const float (*battery_map)[2] = treat_as_charging ? battery_charge_percent_map : battery_percent_map;
     int map_len = sizeof(battery_charge_percent_map)/sizeof(battery_charge_percent_map[0]); // 两个map长度一样
     // ======================================================================
     float temp_value = 0.0f;
 
     // // ===================== 只打印当前使用的数组 =====================
     static bool print_flag = false;
-    if(!print_flag)
-    {
+    if (!print_flag) {
         print_flag = true;
+#if BATTERY_SOC_VERBOSE_LOG
         printf("==== 当前使用的电池电压映射表 ====\n");
-        if(get_charge_stat())
-        {
+        if (treat_as_charging) {
             printf("==== 充电模式 battery_charge_percent_map ====\n");
-        }
-        else
-        {
+        } else if (g_force_discharge_soc_mode) {
+            printf("==== 拷机放电(插线禁充) battery_percent_map ====\n");
+        } else {
             printf("==== 非充电模式 battery_percent_map ====\n");
         }
 
-        for(int i=0; i<map_len; i++)
-        {
+        for (int i = 0; i < map_len; i++) {
             printf("[%2d] 电压: %.3f V → 电量: %d %%\n",
-                   i,
-                   battery_map[i][0],
-                   (int)battery_map[i][1]);
+                i, battery_map[i][0], (int)battery_map[i][1]);
         }
         printf("=======================================\n");
+#endif
     }
     // ===============================================================
 
+    if (voltage > battery_map[0][0]) {
+        percent = (int)battery_map[0][1];
+    }
+
     for (int i =0;i<map_len -1;i++)
     {
         //电压在前后两个之间
@@ -864,17 +894,10 @@ static int battery_get_percent_by_voltage(float voltage)
 
             percent =  (int)(temp_value + 0.5);
 
-            // ===================== 打印关键计算变量 =====================
-            printf("[电池计算] 当前电压=%.3fV | 区间[%d]%.3fV ~ [%d]%.3fV | r_div=%.3f | 计算电量=%.1f%% | 取整=%d%%\n",
-                   voltage,
-                   i, battery_map[i][0],
-                   i+1, battery_map[i+1][0],
-                   r_div,
-                   temp_value,
-                   percent);
-            // ============================================================
-
-            if(get_charge_stat())
+            BAT_SOC_LOG("[电池计算] 当前电压=%.3fV | 区间[%d]%.3fV ~ [%d]%.3fV | r_div=%.3f | 计算电量=%.1f%% | 取整=%d%%\n",
+                voltage, i, battery_map[i][0], i + 1, battery_map[i + 1][0], r_div, temp_value, percent);
+
+            if (treat_as_charging)
             {
                 // if(history_percent > percent && abs(history_percent - percent) < 10)
                 if(history_percent > percent)
@@ -931,36 +954,66 @@ static int battery_get_percent_by_voltage(float voltage)
     
     }
 
-    printf("000 history_percent:%d ,percent : %d ,voltage:%.3f \n",history_percent,percent,voltage);
+    BAT_SOC_LOG("000 history_percent:%d ,percent : %d ,voltage:%.3f \n", history_percent, percent, voltage);
 
     if (voltage <= batteryservice_VOLTAGE_MIN)
         percent = 0;
 
     if (voltage >= batteryservice_VOLTAGE_MAX)
     {
-        if(get_charge_stat() && history_percent < 100 && history_percent)
+        if (g_force_discharge_soc_mode) {
+            /* 拷机放电:不逐格累加;满电区仍需给出合理 SOC,避免 percent 仍为 0 */
+            if (percent <= 0) {
+                percent = history_percent > 0 ? history_percent : (int)battery_map[0][1];
+            }
+        } else if (treat_as_charging && history_percent < 100 && history_percent)
         {
-            printf("111 history_percent:%d ,percent : %d ,voltage:%.3f \n",history_percent,percent,voltage);
+            BAT_SOC_LOG("111 history_percent:%d ,percent : %d ,voltage:%.3f \n", history_percent, percent, voltage);
             history_percent++;
             percent = history_percent;
         }
-        else if(!get_charge_stat() && history_percent)
+        else if (!treat_as_charging && history_percent)
         {
-            printf("222 history_percent:%d ,percent : %d ,voltage:%.3f \n",history_percent,percent,voltage);
+            BAT_SOC_LOG("222 history_percent:%d ,percent : %d ,voltage:%.3f \n", history_percent, percent, voltage);
             percent = history_percent;
         }
         else
         {
-            printf("333 history_percent:%d ,percent : %d ,voltage:%.3f \n",history_percent,percent,voltage);
+            BAT_SOC_LOG("333 history_percent:%d ,percent : %d ,voltage:%.3f \n", history_percent, percent, voltage);
             percent = 100;
             history_percent = 100;
         }
     }
 
-    if (battery_check_charge_done(voltage, &percent)) {
+    if (!g_force_discharge_soc_mode && battery_check_charge_done(voltage, &percent)) {
         return percent;
     }
 
+    if (g_force_discharge_soc_mode) {
+        if (s_cc_burn_discharge_soc_peak < 0) {
+            int seed = history_percent > 0 ? history_percent :
+                       (cur_percent > 0 ? (int)cur_percent : percent);
+            if (seed <= 0 && voltage >= battery_map[0][0] - 0.08f) {
+                seed = (int)battery_map[0][1];
+            }
+            s_cc_burn_discharge_soc_peak = seed;
+        }
+        if (s_cc_burn_discharge_soc_peak < 0) {
+            s_cc_burn_discharge_soc_peak = percent > 0 ? percent : 0;
+        }
+        if (percent > s_cc_burn_discharge_soc_peak) {
+            percent = s_cc_burn_discharge_soc_peak;
+        } else if (percent < s_cc_burn_discharge_soc_peak) {
+            /* 拒绝单次异常 0% 把满电峰值打穿;真放空时电压会进低压区 */
+            if (percent > 0 || voltage <= batteryservice_VOLTAGE_MIN + 0.05f) {
+                s_cc_burn_discharge_soc_peak = percent;
+            } else {
+                percent = s_cc_burn_discharge_soc_peak;
+            }
+        }
+        history_percent = percent;
+    }
+
     return percent;
 }
 
@@ -1082,7 +1135,7 @@ static void get_avg_voltage(float *vol)
         else {
             bat_vol.avg_vol = bat_vol.sum_vol / bat_vol.vol_num;
         }
-        printf("avg_vol : %.3f\n",bat_vol.avg_vol);
+        BAT_SOC_LOG("avg_vol : %.3f\n", bat_vol.avg_vol);
     }  
 }
 
@@ -1148,7 +1201,7 @@ static bool adc_get_sample_result(float *voltage, uint8_t channel)
         int DecimalPart_1 = (int)(DecimalPart / 100);
         int DecimalPart_2 = (int)((DecimalPart % 100) / 10);
         int DecimalPart_3 = (int)(DecimalPart % 10);
-        PRINT("222*voltage : %.3f \n",*voltage);
+        BAT_SOC_PRINT("222*voltage : %.3f \n", *voltage);
         // PRINT("DecimalPart_1 : %d , DecimalPart_2 : %d DecimalPart_3 : %d \n",DecimalPart_1,DecimalPart_2,DecimalPart_3);
         uint16_t value = DecimalPart_1 << 8 | DecimalPart_2 << 4 | DecimalPart_3 << 0;
 
@@ -1356,8 +1409,13 @@ uint8_t batteryservice_get_voltage(float *voltage)
     uint8_t percent = 0;
     battery_info_t *current_battery_info = get_current_battery_info();
 
-    percent = battery_get_power_percentage();
     *voltage = current_battery_info->battery_voltage;
+    if (g_force_discharge_soc_mode) {
+        percent = (uint8_t)battery_get_percent_by_voltage(current_battery_info->battery_voltage);
+        cur_percent = percent;
+    } else {
+        percent = battery_get_power_percentage();
+    }
 
     return percent;
 }
index 2781b00..63107b2 100644 (file)
@@ -41,11 +41,12 @@ extern "C" {
 
 #include "debug_print.h"
 
-// #ifdef ENABLE_HP4570
-// #include "hp4570.h"
-// #elif defined ENABLE_SGM41562
-// #include "sgm41562.h"
-// #endif
+#ifdef ENABLE_HP4570
+#include "hp4570.h"
+extern uint8_t cc_cycle_is_discharge_phase(void);
+#elif defined ENABLE_SGM41562
+#include "sgm41562.h"
+#endif
 
 #include "charge_ic.h"
 // #include "cat1_utils.h"
@@ -373,6 +374,12 @@ static void battery_check_chargement(void)
 {
     float voltage;
     float bat_temp;
+#ifdef ENABLE_HP4570
+    /* 拷机放电:Hi-Z/禁充由 cc_cycle 管;拷机充电:走正常温控与 ICHG 档位 */
+    if (cc_cycle_is_discharge_phase()) {
+        return;
+    }
+#endif
     //获取电量
     uint8_t percent = batteryservice_get_voltage(&voltage);
 
index bfc3e85..5874928 100644 (file)
@@ -65,6 +65,7 @@ void battery_get_data_t(BATTERY_DATA_T **data,uint16_t *length);
 uint8_t batteryservice_set_test_bat_temp(float temp);
 void battery_save_vol(void);
 void battery_restore_vol(void);
+void batteryservice_set_force_discharge_soc(uint8_t on);
 
 #ifdef __cplusplus
 #if __cplusplus
diff --git a/code/sdk/zhihui/services/cc_cycle_service/CMakeLists.txt b/code/sdk/zhihui/services/cc_cycle_service/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1c51194
--- /dev/null
@@ -0,0 +1,28 @@
+set(COMPONENT_NAME "cc_cycle_service")
+
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+set(SOURCES
+    ${CMAKE_CURRENT_SOURCE_DIR}/cc_cycle_service.c
+)
+
+set(PRIVATE_HEADER
+    ${ROOT_DIR}/zhihui/utils
+    ${ROOT_DIR}/zhihui/services/battery/hal
+    ${ROOT_DIR}/zhihui/services/battery/service
+    ${ROOT_DIR}/zhihui/drivers/charge/hp4570
+    ${ROOT_DIR}/zhihui/services/gnss/inc
+    ${ROOT_DIR}/application/brandy/brandy_standard
+)
+
+set(COMPONENT_CCFLAGS
+    -Wno-error
+    -Wno-unused-parameter
+)
+
+set(WHOLE_LINK true)
+set(MAIN_COMPONENT false)
+
+build_component()
diff --git a/code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.c b/code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.c
new file mode 100644 (file)
index 0000000..58d9025
--- /dev/null
@@ -0,0 +1,551 @@
+#include "cc_cycle_service.h"
+#include "custom_utils.h"
+#include "errcode.h"
+#include "battery_hal.h"
+#include "cmsis_os2.h"
+#include "securec.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifdef ENABLE_HP4570
+#include "hp4570.h"
+#include "battery_service.h"
+#endif
+
+#if defined(SUPPORT_GNSS)
+#include "gnss_ctl.h"
+#endif
+
+#define CC_CYCLE_MAGIC     0x43434643u /* "CCFC" */
+#define CC_CYCLE_VERSION   2
+#define CC_CYCLE_SOC_FULL  99
+#define CC_CYCLE_SOC_LOW   10
+
+typedef struct {
+    uint32_t magic;
+    uint16_t version;
+    uint8_t  test_enabled;
+    uint8_t  phase;
+    uint8_t  auto_running;
+    uint8_t  reserved;
+    uint32_t charge_start_ts;
+    uint32_t charge_end_ts;
+    uint16_t round_count;
+} cc_cycle_store_t;
+
+static cc_cycle_store_t g_store;
+static uint8_t g_task_running;
+static uint8_t g_lcd_on;
+static uint8_t g_gps_on;
+static uint8_t g_pending_hide_burn_ui;
+static osMutexId_t g_mutex;
+
+static void cc_lock(void)
+{
+    if (g_mutex != NULL) {
+        (void)osMutexAcquire(g_mutex, osWaitForever);
+    }
+}
+
+static void cc_unlock(void)
+{
+    if (g_mutex != NULL) {
+        (void)osMutexRelease(g_mutex);
+    }
+}
+
+static void cc_ensure_userdata_dir(void)
+{
+    static uint8_t s_dir_ready;
+
+    if (s_dir_ready) {
+        return;
+    }
+    uapi_create_folder((char *)"user");
+    uapi_create_folder((char *)"user/userdata");
+    s_dir_ready = 1;
+}
+
+static void cc_store_load(void)
+{
+    uint16_t len = sizeof(g_store);
+    memset(&g_store, 0, sizeof(g_store));
+    uapi_get_file_data((char *)CC_CYCLE_STORE_PATH, (uint8_t *)&g_store, &len);
+    if (g_store.magic != CC_CYCLE_MAGIC) {
+        memset(&g_store, 0, sizeof(g_store));
+        g_store.magic = CC_CYCLE_MAGIC;
+        g_store.version = CC_CYCLE_VERSION;
+        g_store.phase = (uint8_t)CC_PHASE_IDLE;
+    }
+}
+
+static void cc_store_save(void)
+{
+    g_store.magic = CC_CYCLE_MAGIC;
+    g_store.version = CC_CYCLE_VERSION;
+    uapi_save_file_data((char *)CC_CYCLE_STORE_PATH, (uint8_t *)&g_store,
+        (uint16_t)sizeof(g_store), "wb");
+}
+
+static void cc_sync_discharge_soc_mode(void)
+{
+#ifdef SUPPORT_BATTERY_SERVICE
+    if (g_store.auto_running && g_store.phase == (uint8_t)CC_PHASE_DISCHARGE) {
+        battery_set_force_discharge_soc(1);
+    } else {
+        battery_set_force_discharge_soc(0);
+    }
+#else
+    (void)0;
+#endif
+}
+
+static uint8_t cc_read_soc(float *voltage)
+{
+    float v = 0.0f;
+    uint8_t soc = 0;
+#ifdef SUPPORT_BATTERY_SERVICE
+    soc = battery_get_voltage(&v);
+#else
+    (void)voltage;
+    return 0;
+#endif
+    if (voltage != NULL) {
+        *voltage = v;
+    }
+    return soc;
+}
+
+static uint8_t cc_is_plugged(void)
+{
+    return get_charge_stat() ? 1 : 0;
+}
+
+/*
+ * 充电:恢复 VBUS 路径 + 允充。
+ * 放电:禁充 + VBUS Hi-Z,负载由电池承担(线仍插着用于检测/拷机循环)。
+ */
+static void cc_set_charge_enable(uint8_t on)
+{
+#ifdef ENABLE_HP4570
+    if (on) {
+        /* 恢复 VBUS 路径、允充;ICHG 由 battery_service 按温度/SOC 配置(非固定死 260mA) */
+        hp4570n_set_hiz_en(false);
+        hp4570n_set_ceb_en(true);
+        disable_02c();
+    } else {
+        hp4570n_set_ceb_en(false);
+        hp4570n_set_hiz_en(true);
+    }
+#else
+    (void)on;
+#endif
+}
+
+static void cc_set_white_screen(uint8_t on)
+{
+    /* 白屏与常亮由 VIEW_CC_CYCLE_BURN 界面负责 */
+    if (on) {
+        g_lcd_on = 1;
+    } else if (g_lcd_on) {
+        g_lcd_on = 0;
+    }
+}
+
+static void cc_set_gps(uint8_t on)
+{
+#if defined(SUPPORT_GNSS)
+    if (on && !g_gps_on) {
+        gnss_start();
+        g_gps_on = 1;
+    } else if (!on && g_gps_on) {
+        gnss_stop();
+        g_gps_on = 0;
+    }
+#else
+    (void)on;
+#endif
+}
+
+static void cc_append_charge_log(uint32_t t_start, uint32_t t_end, uint8_t soc_end)
+{
+    FILE *fp;
+    char line[96];
+    cc_ensure_userdata_dir();
+    (void)snprintf_s(line, sizeof(line), sizeof(line) - 1,
+        "start=%u end=%u soc_end=%u round=%u\n",
+        (unsigned int)t_start, (unsigned int)t_end, (unsigned int)soc_end,
+        (unsigned int)g_store.round_count);
+    fp = fopen(CC_CYCLE_CHARGE_LOG_PATH, "a");
+    if (fp == NULL) {
+        return;
+    }
+    (void)fwrite(line, 1, strlen(line), fp);
+    (void)fclose(fp);
+}
+
+static void cc_enter_discharge_phase(void)
+{
+    g_store.phase = (uint8_t)CC_PHASE_DISCHARGE;
+    g_store.charge_start_ts = 0;
+    g_store.charge_end_ts = 0;
+    cc_set_charge_enable(0);
+    cc_sync_discharge_soc_mode();
+    if (g_store.auto_running) {
+        cc_set_white_screen(1);
+        cc_set_gps(1);
+    }
+}
+
+static void cc_enter_charging_phase(void)
+{
+    uint32_t now = 0;
+    (void)uapi_get_timestampe_ss(&now);
+    g_store.phase = (uint8_t)CC_PHASE_CHARGING;
+    g_store.charge_start_ts = now;
+    g_store.charge_end_ts = 0;
+    cc_set_white_screen(0);
+    cc_set_charge_enable(1);
+    cc_sync_discharge_soc_mode();
+    if (g_store.auto_running) {
+        cc_set_gps(1);
+    }
+}
+
+static void cc_finish_charging_phase(uint8_t soc)
+{
+    uint32_t now = 0;
+    (void)uapi_get_timestampe_ss(&now);
+    g_store.charge_end_ts = now;
+    g_store.round_count++;
+    cc_append_charge_log(g_store.charge_start_ts, g_store.charge_end_ts, soc);
+    cc_enter_discharge_phase();
+}
+
+static void cc_finish_discharge_phase(uint8_t soc)
+{
+    (void)soc;
+    if (!cc_is_plugged()) {
+        return;
+    }
+    cc_enter_charging_phase();
+}
+
+static void cc_pick_initial_phase(uint8_t soc)
+{
+#ifdef ENABLE_HP4570
+    if (soc >= CC_CYCLE_SOC_FULL || hp4570n_read_chrg_stat_chrg_done()) {
+        cc_enter_discharge_phase();
+    } else if (soc <= CC_CYCLE_SOC_LOW) {
+        cc_enter_charging_phase();
+    } else if (g_store.phase == (uint8_t)CC_PHASE_CHARGING) {
+        cc_enter_charging_phase();
+    } else {
+        cc_enter_discharge_phase();
+    }
+#else
+    if (soc >= CC_CYCLE_SOC_FULL) {
+        cc_enter_discharge_phase();
+    } else {
+        cc_enter_charging_phase();
+    }
+#endif
+}
+
+static void cc_tick(void)
+{
+    uint8_t soc;
+    uint32_t now = 0;
+
+    if (!g_store.test_enabled || !g_store.auto_running) {
+        return;
+    }
+    if (!cc_is_plugged()) {
+        /* 插线拷机:掉线则暂停自动循环,恢复充电 IC 默认 */
+        cc_set_white_screen(0);
+        cc_set_gps(0);
+        cc_set_charge_enable(1);
+        g_store.phase = (uint8_t)CC_PHASE_IDLE;
+        g_store.auto_running = 0;
+        battery_set_force_discharge_soc(0);
+        g_pending_hide_burn_ui = 1;
+        return;
+    }
+
+    (void)uapi_get_timestampe_ss(&now);
+    soc = cc_read_soc(NULL);
+
+    if (g_store.phase == (uint8_t)CC_PHASE_DISCHARGE) {
+        cc_set_charge_enable(0);
+        cc_set_white_screen(1);
+        cc_set_gps(1);
+        if (soc <= CC_CYCLE_SOC_LOW) {
+            cc_finish_discharge_phase(soc);
+        }
+        return;
+    }
+
+    if (g_store.phase == (uint8_t)CC_PHASE_CHARGING) {
+        cc_set_white_screen(0);
+        cc_set_gps(1);
+        cc_set_charge_enable(1);
+        if (g_store.charge_start_ts == 0) {
+            g_store.charge_start_ts = now;
+        }
+#ifdef ENABLE_HP4570
+        if (soc >= CC_CYCLE_SOC_FULL || hp4570n_read_chrg_stat_chrg_done()) {
+            cc_finish_charging_phase(soc);
+        }
+#else
+        if (soc >= CC_CYCLE_SOC_FULL) {
+            cc_finish_charging_phase(soc);
+        }
+#endif
+    }
+}
+
+static void cc_cycle_task(void *arg)
+{
+    (void)arg;
+    g_task_running = 1;
+    while (g_task_running) {
+        uint8_t hide_ui = 0;
+        cc_lock();
+        cc_tick();
+        cc_store_save();
+        hide_ui = g_pending_hide_burn_ui;
+        g_pending_hide_burn_ui = 0;
+        cc_unlock();
+        if (hide_ui) {
+            cc_cycle_hide_burn_ui();
+        }
+        osDelay(1000);
+    }
+}
+
+static void cc_start_task(void)
+{
+    static uint8_t started;
+    app_task_definition_t taskcfg = {0};
+    if (started) {
+        return;
+    }
+    if (g_mutex == NULL) {
+        g_mutex = osMutexNew(NULL);
+    }
+    taskcfg.create_queue_handle = NULL;
+    taskcfg.attr.name = "cc_cycle_svc";
+    taskcfg.attr.priority = osPriorityNormal;
+    taskcfg.attr.stack_size = 1024 * 6;
+    taskcfg.func = cc_cycle_task;
+    taskcfg.use_mem = USE_EXTEND_MEM;
+    if (uapi_create_osThread_custom(&taskcfg) != ERRCODE_SUCC) {
+        return;
+    }
+    started = 1;
+}
+
+void cc_cycle_init(void)
+{
+    cc_ensure_userdata_dir();
+    cc_start_task();
+    cc_lock();
+    cc_store_load();
+    cc_unlock();
+}
+
+void cc_cycle_on_boot(void)
+{
+    cc_cycle_init();
+    cc_lock();
+    if (!g_store.test_enabled || !g_store.auto_running) {
+        cc_unlock();
+        return;
+    }
+    if (!cc_is_plugged()) {
+        g_store.auto_running = 0;
+        g_store.phase = (uint8_t)CC_PHASE_IDLE;
+        cc_store_save();
+        cc_unlock();
+        return;
+    }
+    cc_set_gps(1);
+    if (g_store.phase == (uint8_t)CC_PHASE_CHARGING) {
+        cc_enter_charging_phase();
+    } else if (g_store.phase == (uint8_t)CC_PHASE_DISCHARGE) {
+        cc_enter_discharge_phase();
+    } else {
+        cc_pick_initial_phase(cc_read_soc(NULL));
+    }
+    cc_store_save();
+    cc_unlock();
+    cc_cycle_show_burn_ui();
+}
+
+uint8_t cc_cycle_get_enabled(void)
+{
+    return g_store.test_enabled;
+}
+
+void cc_cycle_set_enabled(uint8_t on)
+{
+    uint8_t hide_ui = 0;
+    cc_lock();
+    if (!on && g_store.auto_running) {
+        g_store.auto_running = 0;
+        g_store.phase = (uint8_t)CC_PHASE_IDLE;
+        cc_set_white_screen(0);
+        cc_set_gps(0);
+        cc_set_charge_enable(1);
+        battery_set_force_discharge_soc(0);
+        hide_ui = 1;
+    }
+    g_store.test_enabled = on ? 1 : 0;
+    cc_store_save();
+    cc_unlock();
+    if (hide_ui) {
+        cc_cycle_hide_burn_ui();
+    }
+}
+
+cc_cycle_phase_e cc_cycle_get_phase(void)
+{
+    return (cc_cycle_phase_e)g_store.phase;
+}
+
+uint16_t cc_cycle_get_round_count(void)
+{
+    return g_store.round_count;
+}
+
+uint8_t cc_cycle_get_soc(void)
+{
+    uint8_t soc;
+    cc_lock();
+    soc = cc_read_soc(NULL);
+    cc_unlock();
+    return soc;
+}
+
+uint8_t cc_cycle_get_charge_enable_intent(void)
+{
+    uint8_t on = 0;
+    cc_lock();
+    if (g_store.auto_running && g_store.phase == (uint8_t)CC_PHASE_CHARGING) {
+        on = 1;
+    }
+    cc_unlock();
+    return on;
+}
+
+uint8_t cc_cycle_get_hw_is_charging(void)
+{
+#ifdef ENABLE_HP4570
+    if (hp4570n_read_chrg_stat_fast_chrg() || hp4570n_read_chrg_stat_pre_chrg() ||
+        hp4570n_read_chrg_stat_dead_chrg()) {
+        return 1;
+    }
+    if (hp4570n_read_chrg_stat_chrg_done()) {
+        return 0;
+    }
+#endif
+    return 0;
+}
+
+int cc_cycle_format_status_line(char *buf, unsigned int buf_size)
+{
+    cc_cycle_phase_e phase;
+    uint8_t soc;
+    uint8_t intent;
+    uint8_t hw_chg;
+    uint16_t round;
+    uint8_t running;
+
+    if (buf == NULL || buf_size == 0) {
+        return 0;
+    }
+    cc_lock();
+    phase = (cc_cycle_phase_e)g_store.phase;
+    soc = cc_read_soc(NULL);
+    round = g_store.round_count;
+    running = g_store.auto_running;
+    cc_unlock();
+
+    intent = cc_cycle_get_charge_enable_intent();
+    hw_chg = cc_cycle_get_hw_is_charging();
+
+    if (!running || phase == CC_PHASE_IDLE) {
+        return snprintf_s(buf, buf_size, buf_size - 1,
+            "状态:未运行 SOC:%u%%", (unsigned int)soc);
+    }
+    if (phase == CC_PHASE_DISCHARGE) {
+        if (hw_chg) {
+            return snprintf_s(buf, buf_size, buf_size - 1,
+                "【放电中】电池供电 SOC:%u%% 轮次:%u HiZ:开 HW:充!",
+                (unsigned int)soc, (unsigned int)round);
+        }
+        return snprintf_s(buf, buf_size, buf_size - 1,
+            "【放电中】电池供电 SOC:%u%% 轮次:%u HiZ:开",
+            (unsigned int)soc, (unsigned int)round);
+    }
+    if (hw_chg) {
+        return snprintf_s(buf, buf_size, buf_size - 1,
+            "【充电中】SOC:%u%% 轮次:%u IC:开 HW:充",
+            (unsigned int)soc, (unsigned int)round);
+    }
+    return snprintf_s(buf, buf_size, buf_size - 1,
+        "【充电中】SOC:%u%% 轮次:%u IC:%s HW:未充",
+        (unsigned int)soc, (unsigned int)round, intent ? "开" : "关");
+}
+
+uint8_t cc_cycle_is_active(void)
+{
+    return (g_store.test_enabled && g_store.auto_running &&
+        g_store.phase != (uint8_t)CC_PHASE_IDLE) ? 1 : 0;
+}
+
+uint8_t cc_cycle_is_discharge_phase(void)
+{
+    return (g_store.test_enabled && g_store.auto_running &&
+        g_store.phase == (uint8_t)CC_PHASE_DISCHARGE) ? 1 : 0;
+}
+
+uint8_t cc_cycle_is_running(void)
+{
+    return g_store.auto_running;
+}
+
+void cc_cycle_stop_automation(void)
+{
+    cc_lock();
+    g_store.auto_running = 0;
+    g_store.phase = (uint8_t)CC_PHASE_IDLE;
+    cc_set_white_screen(0);
+    cc_set_gps(0);
+    cc_set_charge_enable(1);
+    battery_set_force_discharge_soc(0);
+    cc_store_save();
+    cc_unlock();
+    cc_cycle_hide_burn_ui();
+}
+
+errcode_t cc_cycle_enter_test_ui(void)
+{
+    uint8_t soc;
+    cc_lock();
+    if (!g_store.test_enabled) {
+        cc_unlock();
+        return ERRCODE_FAIL;
+    }
+    if (!cc_is_plugged()) {
+        cc_unlock();
+        return ERRCODE_FAIL;
+    }
+    g_store.auto_running = 1;
+    soc = cc_read_soc(NULL);
+    cc_pick_initial_phase(soc);
+    cc_store_save();
+    cc_unlock();
+    cc_cycle_show_burn_ui();
+    return ERRCODE_SUCC;
+}
diff --git a/code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.h b/code/sdk/zhihui/services/cc_cycle_service/cc_cycle_service.h
new file mode 100644 (file)
index 0000000..18916c0
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef CC_CYCLE_SERVICE_H
+#define CC_CYCLE_SERVICE_H
+
+#include <stdint.h>
+#include "errcode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CC_CYCLE_STORE_PATH      "user/userdata/cc_cycle.bin"
+#define CC_CYCLE_CHARGE_LOG_PATH "user/userdata/cc_charge_log.txt"
+
+typedef enum {
+    CC_PHASE_IDLE = 0,
+    CC_PHASE_DISCHARGE = 1,  /* 插线拷机:VBUS Hi-Z+禁充,电池放电;白屏+GPS */
+    CC_PHASE_CHARGING = 2,   /* 插线拷机:允许充电,记录起止时间 */
+} cc_cycle_phase_e;
+
+void cc_cycle_init(void);
+void cc_cycle_on_boot(void);
+
+uint8_t cc_cycle_get_enabled(void);
+void cc_cycle_set_enabled(uint8_t on);
+
+cc_cycle_phase_e cc_cycle_get_phase(void);
+uint16_t cc_cycle_get_round_count(void);
+uint8_t cc_cycle_get_soc(void);
+/* 软件意图:放电阶段为0(禁充),充电阶段为1(允充) */
+uint8_t cc_cycle_get_charge_enable_intent(void);
+/* HP4570 硬件充电状态:1=预充/快充中 */
+uint8_t cc_cycle_get_hw_is_charging(void);
+uint8_t cc_cycle_is_active(void);
+/* 仅放电阶段:拷机需接管 Hi-Z/禁充;充电阶段交还 battery_service 管电流 */
+uint8_t cc_cycle_is_discharge_phase(void);
+uint8_t cc_cycle_is_running(void);
+
+/* 写入状态文案,buf 建议>=96 字节,返回 snprintf 结果 */
+int cc_cycle_format_status_line(char *buf, unsigned int buf_size);
+
+/* 需已插充电线;启动后自动循环充放电 */
+errcode_t cc_cycle_enter_test_ui(void);
+void cc_cycle_stop_automation(void);
+
+/* 全屏白底拷机界面(producttest 实现) */
+void cc_cycle_show_burn_ui(void);
+void cc_cycle_hide_burn_ui(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 723f664..0bb22fd 100644 (file)
@@ -47,6 +47,7 @@ set(PRIVATE_HEADER
     ${ROOT_DIR}/middleware/services/gui/uikit/ui/frameworks/dock/
     ${ROOT_DIR}/zhihui/UI/producttest/include
     ${ROOT_DIR}/zhihui/UI
+    ${ROOT_DIR}/zhihui/services/cc_cycle_service
     ${ROOT_DIR}/zhihui/AI/AImain
 )
 
index 933cc29..3128413 100644 (file)
@@ -89,6 +89,9 @@ struct timezone {
 
 #include "custom_utils.h"
 #include "custom_save_runtime.h"
+#ifdef ENABLE_HP4570
+#include "cc_cycle_service.h"
+#endif
 
 /* Forward declarations for functions used in this file */
 extern void save_time(void);
@@ -888,9 +891,15 @@ if (custom_get_factory_test_flag() != 0 ) {
     if(factory_test_flag == 2)
     {
         custom_aging_test_init();
-    }    
+    }
 
 #endif
+
+#ifdef ENABLE_HP4570
+    if (custom_get_factory_test_flag() != 0) {
+        cc_cycle_on_boot();
+    }
+#endif
 }
     
 void startup_post_init(void)