From b8f23d9413325f92eb7e005a9cbf853e0fc9d508 Mon Sep 17 00:00:00 2001 From: roy01 Date: Tue, 30 Jun 2026 10:19:42 +0800 Subject: [PATCH] fix: remove useless variables --- .../Examples/template/LLC_48V/inc/init.h | 40 +- .../Examples/template/LLC_48V/src/copy_main.c | 2194 ++++++++--------- .../Examples/template/LLC_48V/src/init.c | 288 +-- .../Examples/template/LLC_48V/src/main.c | 1430 +++++------ 4 files changed, 1978 insertions(+), 1974 deletions(-) diff --git a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/inc/init.h b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/inc/init.h index 58cb292..8b46278 100644 --- a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/inc/init.h +++ b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/inc/init.h @@ -1,20 +1,20 @@ -/** - * Copyright (c) 2026 Wisetop. All Rights Reserved. - */ - -#ifndef INC_INIT_H_ -#define INC_INIT_H_ - -#include "main.h" - -void SYS_Config(void); -void sysclk_init(void); -void gpio_init(void); -void opa_init(void); -void adc_init(void); -void tim0_init(void); -void llc_pwm_init(void); -void pfc_pwm_init(void); -void pwm_brake(); - -#endif /* INC_INIT_H_ */ +/** + * Copyright (c) 2026 Wisetop. All Rights Reserved. + */ + +#ifndef INC_INIT_H_ +#define INC_INIT_H_ + +#include "main.h" + +void SYS_Config(void); +void sysclk_init(void); +void gpio_init(void); +void opa_init(void); +void adc_init(void); +void tim0_init(void); +void llc_pwm_init(void); +void pfc_pwm_init(void); +void pwm_brake(); + +#endif /* INC_INIT_H_ */ diff --git a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/copy_main.c b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/copy_main.c index 1c4a879..fb047e4 100644 --- a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/copy_main.c +++ b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/copy_main.c @@ -1,1097 +1,1097 @@ -#if 0 - -#include "init.h" -#include "isr.h" -#include "main.h" -//============================================================================= -// Constant Definition -//============================================================================= -#define DATA_LOG 0 -#define DATA_MONITOR 1 -#define DEBOUNCE_TIME 20 -#define BUF_SIZE 100 // > 300 記憶體不夠用(4組) > 700 記憶體不夠用(2組) - -#define CR_1P1Z_B0_Q14 6313 -#define CR_1P1Z_B1_Q14 -5113 -#define CR_1P1Z_A1_Q14 -5803 -// 前饋輸出限制(請根據您主環 Q14 格式的數值範圍調整) -// 假設最大前饋量限制為實數值 50.0 -> 50.0 * 16384 = 819200 -#define CR_FF_MAX_Q14 819200 // 50 duty << 14 -#define CR_FF_MIN_Q14 -819200 - -//============================================================================= -// Macro Definition -//============================================================================= - -//============================================================================= -// Structure Definition -//============================================================================= -typedef enum { - KEY_IDLE = 0, - KEY_DOWN, - KEY_WAIT_RELEASE -} KeyState_t; - -typedef enum { - STATE_INIT, - STATE_STANDBY, // 等 AC brown-in - STATE_PFC_SOFT_START, // 第一階段:PFC 透過最小 duty 提升 vbus,若 vbus 足夠,直接進入下一階段 - STATE_LLC_SOFT_START, // 第二階段:LLC 掃頻 - STATE_RUN_NORMAL, // 第三階段:二級都進入穩態閉環 - STATE_RUN_SKIP, // 第四階段:負載太小,暫時關閉 PFC - STATE_FAULT, // 故障保護 - STATE_PAUSE // 暫停,測試用 -} SystemState_t; - -//============================================================================= -// Global Data Definition -//============================================================================= - -//#define kp 250 -//#define ki 2 -//#define kd 0 - -//#define ff_max 8000 - -//#define v_kp 10 -#define v_ki 10 -#define v_kd 0 - -SystemState_t sys_state = STATE_INIT; -uint8_t is_zero_crossing = 0; -uint8_t burst_mode_active = 0; -uint8_t llc_running = 0; -uint8_t pfc_running = 0; -static uint8_t last_is_zero_crossing = 0; -uint8_t is_brown_in = 0; // 0: 關機/保護中, 1: 正常工作 - -static volatile int32_t print1, print2; - -static uint16_t iac = 0; -static uint16_t vac = 0; -static uint16_t vbus = 0; -static uint16_t vcr = 0; - -static int32_t vcr_x1_q14 = 0; // x[n-1] -static int32_t vcr_y1_q14 = 0; // y[n-1] -static int32_t cr_feed_forward_output_q14 = 0; // 最終輸出的 Q14 補償量 - -uint32_t count1; - -uint32_t vbus_ovp = (VBUS_OVP * 4096 / VBUS_2ADC / 3.3); -uint32_t vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); -uint32_t vbus_set_h = (VBUS_SET_H * 4096 / VBUS_2ADC / 3.3); // burst mode in -uint32_t vbus_set_l = (VBUS_SET_L * 4096 / VBUS_2ADC / 3.3); // burst mode out -//uint32_t vbus_set_h2 = (VBUS_SET_H2 * 4096 / VBUS_2ADC / 3.3); -//uint32_t digi_vcomp_max = (uint32_t)(VBUS_SET_H * 4096 / VBUS_2ADC / 3.3) << DIGI_COMP_BIT; -uint32_t digi_vcomp_max = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -int32_t vbus_err = 0; -int32_t vbus_err_prev = 0; -int32_t vbus_comp = 0; -int32_t vbus_comp2 = 0; // 過零點用 -int32_t vbus_comp_prev = 0; -int32_t vbus_int = 0, vbus_por = 0; -uint16_t pfc_duty = 0; -uint8_t pfc_ss = 0; -uint16_t pfc_ss_cnt = 0; -uint16_t llc_ss_cnt = 0; -//volatile int16_t iac_err_0, iac_err_1; -//volatile uint16_t iac_set; -//volatile int32_t iac_int, iac_por, iac_der; -//volatile int16_t iac_comp = 0; -//----------------------------------------------- -KeyState_t key1State = KEY_IDLE; -uint32_t key1Timer = 0; -uint8_t key1_action = 0; -uint8_t keyInput; -//----------------------------------------------- -uint8_t power_on_cmd = 0; -uint8_t temp_off = 0; -uint32_t msg_timer; -uint16_t vbus_volt, vac_volt; -int32_t tx_buf1[BUF_SIZE]; -int64_t tx_buf2[BUF_SIZE]; -//uint16_t tx_buf3[BUF_SIZE]; -//uint16_t tx_buf4[BUF_SIZE]; -uint16_t buf_idx = 0; -uint8_t is_full = 0; -uint16_t count_down = 60000; // for data log -uint16_t i_ff; -uint32_t vac_avg1, vac_avg2, vac_avg3; -uint32_t vbus_avg = 0; -static volatile uint16_t vac_pk1, vac_pk2; -uint8_t hi_line = 0; // 0=110V, 1=220V -uint8_t vbus_ready = 0; -uint16_t hi_cnt, lo_cnt; -uint32_t static digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -int16_t fb_enent; -uint16_t llc_period_ss = LLC_SS_MIN; -uint16_t llc_run_arr = LLC_PERIOD_MIN; -uint16_t llc_arr_cmp = 0; -uint8_t v_loop_cnt = 0; -uint16_t brownout_cnt = 0; - -uint8_t volatile tim0_trigger = 0; - -// 全局或靜態變數(需在函數外或 static 保留狀態) -static int32_t iac_err_0 = 0; // e[k] -static int32_t iac_err_1 = 0; // e[k-1] -static int32_t iac_err_2 = 0; // e[k-2] -static int32_t iac_comp = 0; // u[k] -static int32_t iac_comp_1 = 0; // u[k-1] (前一次輸出) -static int32_t iac_comp_2 = 0; // u[k-2] (前兩次輸出) - -static int32_t vbus_err_1 = 0; -static int32_t vbus_comp_1 = 0; - -// 2P2Z 係數(這些需要根據你的 PID 增益與控制頻率重新計算或調試) -//int32_t b0 = kp + ki + kd; // 對應比例 + 部分積分/微分貢獻(可調整) -//int32_t b1 = -kp - kd - kd; // e[k-1] 係數(通常為正或負) -//int32_t b2 = kd; // e[k-2] 係數 -//int32_t a1 = 128; // -A1(因為通常寫成 u[k] = ... + A1*u[k-1] + A2*u[k-2],這裡 a1 = 128 代表 A1 ≈ 1) -//int32_t a2 = 0; // -A2(PID 特例時 a2 常為 0) - -// 電壓環 2p2z 係數 -//int32_t v_b0 = v_kp + v_ki + v_kd; -//int32_t v_b1 = -v_kp - v_kd - v_kd; -//int32_t v_b2 = v_kd; -//int32_t v_a1 = 128; -//int32_t v_a2 = 0; - -uint16_t updata_cnt = 0; - -int32_t vbus_comp_prev1, vbus_comp_prev2; -int32_t vbus_err_prev1, vbus_err_prev2; -//----------------------------------------------- - -//============================================================================= -// Private Function Definition -//============================================================================= -void A_Para_Reset() -{ - vbus_int = 0; vbus_comp = 1000; - //iac_int = 0; iac_comp = 1000; - digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; - vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); - pfc_ss = 0; pfc_ss_cnt = 0; - v_loop_cnt = 0; - temp_off = 0; - - iac_err_0 = 0; - iac_err_1 = 0; - iac_err_2 = 0; - iac_comp_1 = 0; - iac_comp_2 = 0; - - vbus_err = 0; - vbus_err_prev = 0; - vbus_err_prev1 = 0; - vbus_err_prev2 = 0; - vbus_comp_prev = 0; - vbus_comp_prev1 = 0; - vbus_comp_prev2 = 0; - vbus_comp2 = 1000; - - // set default value - TIM2->ARR = PFC_PERIOD; - TIM2->CCR1 = PFC_DUTY_MIN; // min for ss - TIM2->EGR |= 0x01; - EPWM->ARR = LLC_PERIOD_SS; - EPWM->CCR1 = (LLC_PERIOD_SS >> 1); - EPWM->EGR |= 0x01; - llc_period_ss = 0; - - pfc_running = 0; - llc_running = 0; - brownout_cnt = 0; -} -//------------------------------------------------------------------------------ -void Key1_Scan() -{ - keyInput = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15); - - switch (key1State) { - case KEY_IDLE: - if (keyInput == 0) { // 剛按下 - key1Timer = sys_get_tick(); // 紀錄時間 - key1State = KEY_DOWN; - } - break; - - case KEY_DOWN: - if (keyInput == 0) { - if (sys_get_tick() - key1Timer >= DEBOUNCE_TIME) { - key1State = KEY_WAIT_RELEASE; // 完成去抖 - } - } else { - key1State = KEY_IDLE; // 回到 IDLE - } - break; - - case KEY_WAIT_RELEASE: - if (keyInput == 1) { // 剛放開 - key1Timer = sys_get_tick(); - key1State = KEY_IDLE; - key1_action = 1; // 執行動作 - Key_Action(); - } - break; - } -} -//------------------------------------------------------------------------- -void Key_Action() -{ - if (power_on_cmd == 0) - { - power_on_cmd = 1; - //A_Para_Reset(); - //Power_On(); - } - else - { - power_on_cmd = 0; - Power_Off(); - } -} -//------------------------------------------------------------------------- -void Power_On() -{ - power_on_cmd = 1; - llc_pwm_enable(); // LLC ON first - pfc_pwm_enable(); -} -//------------------------------------------------------------------------- -void Power_Off() -{ - power_on_cmd = 0; - pfc_pwm_disable(); // PFC OF first - sys_delay(2000); // discharge - llc_pwm_disable(); - sys_state = STATE_PAUSE; - A_Para_Reset(); -} -//=================================================================================== -void llc_set_period(uint16_t val) -{ - if (val > LLC_PERIOD_MAX) val = LLC_PERIOD_MAX; // 可以小,不能過大 - EPWM->ARR = val; - EPWM->CCR1 = val>>1; -} -//=================================================================================== -void pfc_set_duty(uint16_t val) -{ - if (val > PFC_DUTY_MAX) val = PFC_DUTY_MAX; - if (val < PFC_DUTY_MIN) val = PFC_DUTY_MIN; - TIM2->CCR1 = val; -} -//=================================================================================== -void llc_pwm_enable() -{ - REG_SET_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); -} -//=================================================================================== -void llc_pwm_disable() -{ - REG_CLR_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); -} -//=================================================================================== -void pfc_pwm_enable() -{ - REG_SET_BITS(TIM2->CCER, TIM_CCER_CC1E_Msk); -} -//=================================================================================== -void pfc_pwm_disable() -{ - REG_CLR_BITS(TIM2->CCER, TIM_CCER_CC1E_Msk); -} -//=================================================================================== -void Vol_Loop_PFC() -{ - // 1. 計算誤差 - vbus_err = vbus_set - vbus_avg; - - // 2. 積分項 (I):負責消除靜態誤差 - vbus_int += vbus_err; // ki gain ,總共需要 8 ~ 10 bit - if (vbus_int > 0x0FFFFFFF) vbus_int = 0x0FFFFFFF; // 數值極大,可以不設上限,不要溢位即可 - if (vbus_int < 0) vbus_int = 0; - - // 3. 比例項 (P):負責動態響應 - //if (sys_state == STATE_PFC_SOFT_BURST) vbus_por = vbus_err >> 1; // 緩啟動時要慢 - //else if (sys_state == STATE_RUN_NORMAL) vbus_por = vbus_err << 2; // RUN 要快 (5 is better) - - // P 項限幅 - if (vbus_por > 10000) vbus_por = 10000; - if (vbus_por < -10000) vbus_por = -10000; - - int32_t combined_out = (vbus_int >> 10) + vbus_por; - - // 5. 總輸出限幅 - if (combined_out > 18000) combined_out = 18000; // 提高上限以支撐大電流需求 - if (combined_out < 1000) combined_out = 1000; - - //if (is_zero_crossing == 1) { vbus_comp2 = vbus_comp; } // 只在過零點更新,THD 應該會好一點 - vbus_comp = combined_out; - vbus_comp2 = vbus_comp; - - //print1 = vbus_comp; // ok - //print2 = vbus_comp2; // ok - - // 6. 緩啟動處理 - if (sys_state == STATE_PFC_SOFT_START) - { - pfc_set_duty(vbus_comp >> 5); // 6 不夠 - } -} -//====================================================================================== -void Vol_Loop_PFC_1P1Z(void) // TEST OK -{ - // 1. 計算誤差 - int32_t vbus_err_n = (int32_t)vbus_set - (int32_t)vbus_avg; - - // --- 策略 A:動態增益調整 --- - int32_t b0 = 135; - int32_t b1 = -134; - - if (vbus_err_n > 30 || vbus_err_n < -30) - { - // 大誤差時(通常是剛啟動或大跳載),強化參數加快反應 - //b0 = 250; - //b1 = -240; - b0 = 180; // 稍微調降強增益的力道 - b1 = -175; - } - // --- 策略 B:接近目標時微調零點 (抗過衝) --- - else if (vbus_err_n < 20 && vbus_err_n > -20) - { - // 當誤差縮小時,稍微減小 b0 與 b1 的差值,降低積分衝力 - b0 = 120; - b1 = -119; // 如果不能用浮點數,請維持 120 / -119,增加阻尼感 - } - - // 2. 差分方程計算 - int64_t acc = (int64_t)b0 * vbus_err_n; - acc += (int64_t)b1 * vbus_err_1; - acc += ((int64_t)16384 * vbus_comp_1) >> 14; // 16384 = 1 >> 14 - - int32_t y_n = (int32_t)acc; - -#if 0 // 有問題 - // --- 策略 C:軟限制抗過衝 (Soft Clamping) --- - // 預設最大限制 (Duty 300 << 14) - int32_t current_max_limit = 4915200; // duty 300 - - // 如果電壓誤差已經很小(例如剩 15V 就到達),甚至已經發生過衝 (err < 0) - // 我們強制壓低 Duty 的允許上限,防止積分器帶著大 Duty 衝過頭 - if (vbus_err_n < 15) - { - // 這裡的 3276800 對應 Duty 200,您可以根據 800W 穩態時的 Duty 大約位置來設定 - // 核心目標是:不讓 Duty 在接近目標時還維持在 300 這麼高 - current_max_limit = 3276800; // duty 200 - } - - // 如果發生明顯過衝 (電壓高於目標 5V 以上) - if (vbus_err_n < -5) - { - current_max_limit = 1638400; // 強制壓低到 Duty 100 快速拉回 - } -#endif - - // 3. 輸出限幅執行 - if (y_n > 8847360) y_n = 8847360; // 540 << 14 - else if (y_n < 196608) y_n = 196608; // 12 << 14 - - // 4. 更新狀態變數 - vbus_err_1 = vbus_err_n; - vbus_comp_1 = y_n; - - // 5. 輸出 Duty >> 14 - if (sys_state == STATE_PFC_SOFT_START) // 緩啟動期間直接控制 DUTY - TIM2->CCR1 = (vbus_comp_1 >> 14); // 0~540 - - // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 - if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 - vbus_comp2 = (vbus_comp_1 >> 9); -} -//========================================================================== -// Cr 1P1Z 回授,做負載驟變補償,執行頻率 10KHZ -// 抓取 vcr 的動態變化趨勢,並在穩態時回歸平靜 -void Load_Loop_1P1Z(void) -{ - int32_t x0; - int64_t temp_y0; // 使用 64 位元暫存,完全避免 32 位元相乘時溢位 - int32_t y0; - - // 1. 讀取當前 vcr - x0 = vcr; - - // 2. 執行 1P1Z 差分方程計算: - temp_y0 = ( (int64_t)CR_1P1Z_B0_Q14 * x0 ) - + ( (int64_t)CR_1P1Z_B1_Q14 * vcr_x1_q14 ) - - ( (int64_t)CR_1P1Z_A1_Q14 * vcr_y1_q14 ); - - // 將 Q28 格式還原回 Q14 格式(右移 14 位元 - y0 = (int32_t)(temp_y0 >> 14); - - // 更新歷史狀態變數 (Q14 格式) - vcr_x1_q14 = x0; - vcr_y1_q14 = y0; - - // 定點數安全限幅 (Saturation) - if (y0 > CR_FF_MAX_Q14) y0 = CR_FF_MAX_Q14; // 868352 = 53 duty - else if (y0 > -CR_FF_MIN_Q14) y0 = -CR_FF_MIN_Q14; - - cr_feed_forward_output_q14 = y0; -} -//======================================================================== -__attribute__((always_inline)) static inline void Cur_Loop_PFC_2P2Z(void) // 優化後 3.65us -{ - if (sys_state != STATE_RUN_NORMAL) return; - - // 1. 取得回授與計算誤差 - int32_t local_vbus_comp2 = vbus_comp2; - int32_t local_vac_avg1 = vac_avg1; - - // 電流採樣處理 - int32_t local_iac = 3840 - iac; // OPA 的關係,實際值為 CURR_OFFSET - ADC - if (local_iac < 0) local_iac = 0; // 避免負值 - - // 計算電流給定值 - int32_t local_iac_set = (local_vac_avg1 * local_vbus_comp2) / 5700; - - int32_t local_iac_err_0 = local_iac_set - local_iac; // err = set - meas - //local_iac_err_0 = -local_iac_err_0; // err 反向 - print1 = local_iac_err_0; - - // 2. 差分方程 - // y[n] = b0 * err[n] + b1 * err[n-1] + b2 * err[n-2] + a1 * y[n-1] + a2 * y[n-2] - // y = 32 bit - // err = 14 bit (實際大小 12 bit) - // b and a << 14 bit - // a * y[n] 會超過 32 bit,所以中間計算暫存需要 64bit,回存時用 32bit - // 計算後的 y --> 4 給 duty - // b係數可以同時乘上一個 gain - - // 2. 2P2Z 運算 (Fixed-point Q14) -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=4, PF=0.99, THD=10.7 - int32_t local_temp = (1510 * local_iac_err_0) - + (2339 * iac_err_1) - + (859 * iac_err_2) - + (20921 * iac_comp_1) - - (4545 * iac_comp_2); -#endif - -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, PF=0.99, THD=10.7 (B1 and A1 是負的) - int32_t y_n = (377 * local_iac_err_0) - - (585 * iac_err_1) - + (215 * iac_err_2) - - (20921 * iac_comp_1) // 減 A1 - + (4545 * iac_comp_2); // 加 A2 -#endif - -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (A1為正是對的) (min duty) - int64_t acc = ((int64_t)598 * local_iac_err_0) // < 12 bit - + ((int64_t)-64 * iac_err_1) - + ((int64_t)-408 * iac_err_2); - acc <<= 6; - acc -= ((int64_t)-12644 * iac_comp_1); // y[n-1] - acc -= ((int64_t)-3615 * iac_comp_2); // y[n-2] -#endif - -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (A1為正是對的) (min duty) - int64_t acc = ((int64_t)598 * local_iac_err_0) // < 12 bit - + ((int64_t)-64 * iac_err_1) - + ((int64_t)-408 * iac_err_2) - - ((int64_t)-12644 * iac_comp_1) // y[n-1] - - ((int64_t)-3615 * iac_comp_2); // y[n-2] -#endif - -#if 1 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (AI fixed) (衝到OVP) - int64_t acc = ((int64_t)5004 * local_iac_err_0) // < 12 bit - - ((int64_t)7522 * iac_err_1) - + ((int64_t)2711 * iac_err_2) - + ((int64_t)25633 * iac_comp_1) // y[n-1] - - ((int64_t)9260 * iac_comp_2); // y[n-2] -#endif - -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=4, (4倍震盪太大) - int64_t acc = ((int64_t)1510 * local_iac_err_0) // < 12 bit - + ((int64_t)-2339 * iac_err_1) - + ((int64_t)859 * iac_err_2) - - ((int64_t)-20921 * iac_comp_1) // y[n-1] - - ((int64_t)4545 * iac_comp_2); // y[n-2] -#endif - -#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=2, (min duty) - int64_t acc = ((int64_t)755 * local_iac_err_0) // < 12 bit - - ((int64_t)1170 * iac_err_1) - + ((int64_t)430 * iac_err_2) - + ((int64_t)20921 * iac_comp_1) // y[n-1] - - ((int64_t)4545 * iac_comp_2); // y[n-2] -#endif - - // 抵消係數的 Q14,回到 32-bit 範圍 - // 此時 y_n 的量級與 err 是一致的 - //int32_t y_n = (int32_t)(acc >> 14); // 有括號,先 >> 14 再轉型成 int32 - - // 2. 更新狀態變數時,應儲存高解析度的數值 - // 假設 y_n_q14 代表帶著 Q14 的結果 - int32_t y_n = (int32_t)acc; - - // 限制極限值 - if (y_n > 8847360) y_n = 8847360; // 540 << 14 (回存原值,不是縮小後的) - else if (y_n < 196608) y_n = 196608; // 12 << 14 (最小輸出會變成 0 ??) - - print2 = y_n; - // 更新狀態變數 (儲存原始計算值以保留積分能量) - iac_err_2 = iac_err_1; - iac_err_1 = local_iac_err_0; - - // 重要:iac_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 - iac_comp_2 = iac_comp_1; - iac_comp_1 = y_n; - - // 4. 硬體 Duty 限幅與輸出 - - uint16_t local_duty = (uint16_t)(y_n >> 14); - if (local_duty > PFC_DUTY_MAX) local_duty = PFC_DUTY_MAX; // MAX = 540 - else if (local_duty < PFC_DUTY_MIN) local_duty = PFC_DUTY_MIN; // MIN = 12 - - TIM2->CCR1 = local_duty; // 有括號,先 >> 14 - - //count1++; - // ------------------------------------------- - // 5. data log -#if DATA_LOG - count_down--; - if (count_down < 1000) - { - tx_buf1[buf_idx] = local_iac_err_0; // y[n] - tx_buf2[buf_idx] = acc; // x[n] - //tx_buf3[buf_idx] = local_iac_err_0; - //tx_buf4[buf_idx] = local_duty; - buf_idx++; - } - - if (buf_idx >= (BUF_SIZE-1)) - { - is_full = 1; - pfc_pwm_disable(); - sys_state = STATE_PAUSE; - } -#endif - // ------------------------------------------- -} -//============================================================================ -/* -void Vol_Loop_LLC(void) // LLC voltage loop control: adjust frequency based on digital comparator -{ - // Read GPIO feedback signal - uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); - - // Adjust digital_vcomp based on GPIO state (increment if high, decrement if low) - if (gpio == 1) { - if (digital_vcomp < digi_vcomp_max) digital_vcomp++; - } else { - if (digital_vcomp > 1000) digital_vcomp--; - } - - // Calculate new ARR value for LLC frequency adjustment - uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); - - // Clamp to valid period range - if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; - if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; - - // Update EPWM registers only if value changed to avoid unnecessary writes - if (EPWM->ARR != new_arr) { - EPWM->ARR = new_arr; - EPWM->CCR1 = (new_arr >> 1); - } -} -*/ -void Vol_Loop_LLC(void) // 測試可工作 -{ - static uint8_t last_gpio = 0; - static uint16_t toggle_cnt = 0; - static uint16_t stable_timer = 0; - - // 1. 讀取 GPIO 回授 - uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); - - // 2. 判斷是否發生翻轉 (判斷是否在平衡點附近跳動) - if (gpio != last_gpio) { - toggle_cnt++; - last_gpio = gpio; - stable_timer = 0; // 只要有翻轉,就重置穩定計時器 - } else { - // 如果 GPIO 長時間沒翻轉,說明離目標還很遠 - if (stable_timer < 1000) stable_timer++; - } - - // 3. 根據翻轉頻率決定步長 - uint16_t current_step; - if (toggle_cnt > STABLE_THRES) { - // 頻繁翻轉:進入「死區」或「極小步長」模式 - current_step = 0; // 設為 0 表示進入死區,完全停止震盪 - - // 為了防止永久卡死,當連續相同狀態一段時間後再恢復調整 - if (stable_timer > 50) toggle_cnt = 0; - } else { - // 距離目標尚遠:根據 stable_timer 決定快慢 - current_step = (stable_timer > 100) ? STEP_FAST : STEP_SLOW; - } - - // 4. 執行調整 - if (current_step > 0) { - if (gpio == 1) { - if (digital_vcomp + current_step <= digi_vcomp_max) - digital_vcomp += current_step; - } else { - if (digital_vcomp > (1000 + current_step)) - digital_vcomp -= current_step; - } - } - - // 5. 計算並更新 ARR (原本的邏輯) - uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); - - if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; - if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; - - if (EPWM->ARR != new_arr) { - EPWM->ARR = new_arr; - EPWM->CCR1 = (new_arr >> 1); - } -} -//================================================================================= -__INTERRUPT void isr_adc_handle(void) -{ - SAVE_IRQ_CSR_CONTEXT(); - ADC_ClearITFlag(ADC0); - - //GPIO_SetBits(GPIOA, GPIO_Pin_06); - - tim0_trigger = 1; - - // get ADC value - iac = ADC0->DAT9_b.DATA; // PA7, AIN9 - vac = ADC0->DAT7_b.DATA; // PA13, AIN7 - vbus = ADC0->DAT4_b.DATA; // PA10, AIN4 - vcr = ADC0->DAT5_b.DATA; // PA11, AIN5 - - Cur_Loop_PFC_2P2Z(); // current loop - - //GPIO_ResetBits(GPIOA, GPIO_Pin_06); - - RESTORE_IRQ_CSR_CONTEXT(); - return; -} -//============================================================================ -void reset_pid_parameters(void) -{ - vbus_int = 0; vbus_comp = 1000; - - iac_err_0 = 0; - iac_err_1 = 0; - iac_err_2 = 0; - iac_comp = 0; - iac_comp_1 = 0; - iac_comp_2 = 0; - - digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -} -//============================================================================== -void Handle_UART_Monitor(void) -{ - if (sys_get_tick() - msg_timer >= 1000) // msg_out = 1, print - { - //printf("%d %d %d\r\n", vac_pk1, vac_pk2, is_brown_in); - printf("%d %d %d %d %d", power_on_cmd, sys_state, vbus_comp2, print1, print2); - //printf("%d %d", print1, print2); - // 電壓環輸出最大 COMP,但是電流環輸出最小 DUTY - msg_timer = sys_get_tick(); - } - // iac 零點大約 3640 ~ 3680 -} -//================================================================================ -void Handle_UART_Log(void) -{ - for (int i = 0; i < BUF_SIZE; i++) - { - //printf("%d:%d:%d:%d\r\n", tx_buf1[i], tx_buf2[i], tx_buf3[i], tx_buf4[i]); - printf("%d:%d\r\n", tx_buf1[i], tx_buf2[i]); - //printf("%d\r\n", tx_buf1[i]); - sys_delay(5); - } - is_full = 0; // [重要] 列印完務必清除旗標,否則會死循環 - buf_idx = 0; // 重置索引 -} -//================================================================================ -void Vac_Peak_Detector(void) -{ - // 1. 峰值追蹤 (Peak Detector):只要不在過零區,就持續抓最大值 - if (vac >= VAC_ZERO) - { - is_zero_crossing = 0; - last_is_zero_crossing = 0; - if (vac > vac_pk1) vac_pk1 = vac; // 只有比目前紀錄大才更新,確保 vac_pk1 停在波峰 - } - else - { - // 2. 進入過零區 (vac < 400) - is_zero_crossing = 1; - // 3. 邊緣觸發:只在從「非過零」進入「過零」的瞬間交接數據 - if (last_is_zero_crossing == 0 && vac_pk1 > VAC_ZERO_RISE) - { - vac_pk2 = vac_pk1; // 這裡的 vac_pk1 必定是剛才半周抓到的最高點 - - // --- 新增 Brown-in / Brown-out 判斷邏輯 --- - if (is_brown_in == 0) - { - // 目前處於關閉狀態,檢查是否達到啟動門檻 - if (vac_pk2 >= VOLT_BROWN_IN) is_brown_in = 1; // 執行 Brown-in - } - else - { - // 目前處於啟動狀態,檢查是否低於欠壓門檻 - if (vac_pk2 < VOLT_BROWN_OUT) is_brown_in = 0; // 執行 Brown-out - } - - vac_pk1 = 0; // 交接完後立即清零,為下半周做準備 - last_is_zero_crossing = 1; // 剛過上升緣 - } - } -} -//============================================================================= -// Public Function Definition -//============================================================================= -int main(void) -{ - // 基礎硬體底層設定 (HIRC 60MHz, GPIO, ADC 採樣率 1Msps) [cite: 55, 127, 184] - SYS_Config(); - -#if 0 - pfc_set_duty(540); - pfc_pwm_enable(); - - //llc_period_ss = LLC_SS_MAX; - //llc_set_period(llc_period_ss>>2); - //llc_pwm_enable(); - //while(1); -#endif - -#if 0 // 測試 LLC 電壓環 (OK) - llc_period_ss = LLC_SS_MAX; - llc_set_period(llc_period_ss>>2); - llc_pwm_enable(); - - while(1) - { - if (tim0_trigger == 1) - { - tim0_trigger = 0; - Vol_Loop_LLC(); - } - - Handle_UART_Debug(); - } -#endif - -#if 0 // 測試 PFC 電壓環 (OK) - TIM2->CCR1 = 12; // min duty - sys_state = STATE_PFC_SOFT_START; - - Vac_Peak_Detector(); - - while(1) - { - if (tim0_trigger == 1) // 100KHZ - { - tim0_trigger = 0; - // --- 1. 高速信號濾波 (簡易位移算法減少算力消耗) --- - vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop - vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop - if (vbus > vbus_ovp) Power_Off(); // ovp - - Vac_Peak_Detector(); - - if (power_on_cmd == 1) - { - if (pfc_running == 0) { - pfc_pwm_enable(); - pfc_running = 1; - } - - if (++v_loop_cnt >= 10) // 10KHZ - { - Vol_Loop_PFC_1P1Z(); // 1P1Z 不穩壓?? - v_loop_cnt = 0; - } - } - - } - Key1_Scan(); - } -#endif - -#if 1 // 測試 PFC 電壓環 + 電流環 (電流環無法穩壓) (電壓環 + 電流環有 BUG,會多一個小 PULSE) - TIM2->CCR1 = 12; // min duty - sys_state = STATE_PFC_SOFT_START; - - Vac_Peak_Detector(); - pfc_running = 0; - - while(1) - { - if (tim0_trigger == 1) // 100KHZ - { - tim0_trigger = 0; - // --- 1. 高速信號濾波 (簡易位移算法減少算力消耗) --- - vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop - vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop - if (vbus > vbus_ovp) Power_Off(); // ovp - - //Vac_Peak_Detector(); - - if (power_on_cmd == 1) - { - if (sys_state == STATE_PFC_SOFT_START) - { - if (pfc_running == 0) { - pfc_pwm_enable(); - pfc_running = 1; - } - - if (++v_loop_cnt >= 10) // 10KHZ - { - Vol_Loop_PFC_1P1Z(); - v_loop_cnt = 0; - } - - - if (vbus_avg > vbus_set_l) - { - pfc_pwm_disable(); - pfc_running = 0; - sys_delay(10); - sys_state == STATE_PAUSE; - } - - /* - if (is_zero_crossing == 1) - { - //sys_state = STATE_RUN_NORMAL; - // --- 關鍵修正:重新初始化電壓環內部狀態 --- - // 給予一個極小的初始 COMP 值,讓它從輕載慢慢往上爬 - vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 - vbus_err_1 = 0; // 清除誤差歷史 - vbus_comp2 = (vbus_comp_1 >> 9); // 讓電流環拿到的初始值是 10 - } - */ - } - /* - else if (sys_state == STATE_RUN_NORMAL) - { - if (pfc_running == 0) { - pfc_pwm_enable(); - pfc_running = 1; - } - - if (++v_loop_cnt >= 10) // 10KHZ - { - Vol_Loop_PFC_1P1Z(); - v_loop_cnt = 0; - } - } - */ - } - - } - Key1_Scan(); - - } -#endif - - - // main loop - while(1) - { - // 核心定時觸發:100kHz (由 TIMER0 旗標觸發) [cite: 318] - if (tim0_trigger == 1) - { - tim0_trigger = 0; - - // 高速信號濾波 (簡易位移算法減少算力消耗) --- - vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop - vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop - // 系統安全監控 (軟體第二道防護) - if (vbus > vbus_ovp) { sys_state = STATE_FAULT; } // vbus ovp - if (vac_pk2 > 2600) { sys_state = STATE_FAULT; } // vac over voltage - - Vac_Peak_Detector(); // VAC 峰值偵測 -#if 1 - // --- 2. 核心狀態機邏輯 ----------------------------------------------------------------------- - switch (sys_state) - { - case STATE_INIT: // 0 - // 等待約 100ms,並按下按鍵 - if (sys_get_tick() > 100 && power_on_cmd == 1) { - sys_state = STATE_STANDBY; - } - break; - - case STATE_STANDBY: // 1 - // Brown-in 判斷 - if (is_brown_in == 1) { - sys_state = STATE_PFC_SOFT_START; // 先進 PFC SS - TIM2->CCR1 = PFC_DUTY_MIN; - } - break; - - case STATE_PFC_SOFT_START: // 2 - // 若 vbus < 320 做 pre-charge - if (vbus_avg < vbus_set_l) - { - if (pfc_running == 0) { - pfc_pwm_enable(); - pfc_running = 1; - } - if (++v_loop_cnt >= 10) { - Vol_Loop_PFC_1P1Z(); - //Vol_Loop_PFC(); - v_loop_cnt = 0; - } - } - else - { - //pfc_pwm_disable(); // 測試 OK - //pfc_running = 0; - // PFC 不關閉,在 LLC SS 期間進閉環 - // sys_state = STATE_PAUSE; // 先暫停,測試用 - sys_state = STATE_LLC_SOFT_START; - // 準備 LLC 軟啟動參數 (起點頻率 2倍諧振頻率) - llc_period_ss = LLC_SS_MIN; // 已經放大 4 倍 - //sys_state = STATE_RUN_NORMAL; // 直接接電流環 - // 重要動作 - vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 - vbus_err_1 = 0; // 清除誤差歷史 - } - break; - - case STATE_LLC_SOFT_START: // 3 - // LLC 軟啟動:頻率由高往低掃描 (掃向 1倍諧振頻率) - llc_period_ss++; - llc_ss_cnt++; - llc_set_period(llc_period_ss >> 2); // 放大 4 倍,要除 4 回來 - - if (llc_running == 0) { - llc_pwm_enable(); - llc_running = 1; - } - // LLC 掃頻結束,PFC 在過零點打開 - // if (llc_ss_cnt >= LLC_SS_COUNT && is_zero_crossing == 1) { - if (llc_ss_cnt >= LLC_SS_COUNT) { - sys_state = STATE_RUN_NORMAL; - // --- 關鍵修正:重新初始化電壓環內部狀態 --- - // 給予一個極小的初始 COMP 值,讓它從輕載慢慢往上爬 - //vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 - //vbus_err_1 = 0; // 清除誤差歷史 - //vbus_comp2 = (vbus_comp_1 >> 9); // 讓電流環拿到的初始值是 10 - // 單測試 PFC 可以,但是加上 LLC 就不行? 原因是 E-LOAD ? - } - // PFC 直接閉環 - if (++v_loop_cnt >= 10) { - Vol_Loop_PFC_1P1Z(); - //Vol_Loop_PFC(); - Vol_Loop_LLC(); - v_loop_cnt = 0; - } - - break; - - case STATE_RUN_NORMAL: // 4 - //if (pfc_running == 0) { - // pfc_pwm_enable(); - // pfc_running = 1; - //} - - if (++v_loop_cnt >= 10) { - // 先算前饋,再算主環,最後疊加 - //Load_Loop_1P1Z(); // Cr 前饋 - Vol_Loop_PFC_1P1Z(); // PFC 電壓環 - //Vol_Loop_PFC(); - Vol_Loop_LLC(); // LLC 電壓環 - v_loop_cnt = 0; - } - - break; - - case STATE_RUN_SKIP: // 5 - // 電壓外環計算 (10kHz) - if (++v_loop_cnt >= 10) { - Vol_Loop_PFC(); // KP / KI 要快 - //Vol_Loop_LLC(); - v_loop_cnt = 0; - } - /* 有問題 - // Brown-out 判斷 - if (vac_pk2 < VAC_BROWN_OUT) { - brownout_cnt++; - if (brownout_cnt > BROWNOUT_DELAY) { - Power_Off(); - sys_state = STATE_STANDBY; - } - } - else { - brownout_cnt = 0; - } - */ - /* 先不做 - // --- BURST / SKIP 邏輯 (帶滯後區間) --- - // 只在過零點判斷 - if (is_zero_crossing == 1) { - if (vbus_avg > vbus_set_h) { // 上限觸發停機 - pfc_pwm_disable(); - burst_mode_active = 1; - } - else if (vbus_avg < vbus_set_l && burst_mode_active == 1) { // 下限觸發回復 - reset_pid_parameters(); - pfc_pwm_enable(); - burst_mode_active = 0; - } - } - */ - break; - - case STATE_FAULT: // 5 - // 立即封鎖所有輸出 - pfc_pwm_disable(); - llc_pwm_disable(); - // 記錄故障代碼,等待手動重置 - break; - - case STATE_PAUSE: // 6 - break; - - } // switch case -#endif - - - } // if tim0_trigger - - // --- 3. 非即時任務 (UI、通訊、Data Log) --- - Key1_Scan(); - -#if DATA_LOG - if (is_full == 1) Handle_UART_Log(); // 利用 PEC930 UART 輸出數據 [cite: 105, 336] -#endif - -#if DATA_MONITOR - Handle_UART_Monitor(); -#endif - - } // while -------------------------------------------------------------------- - - return 0; -} -#endif +#if 0 + +#include "init.h" +#include "isr.h" +#include "main.h" +//============================================================================= +// Constant Definition +//============================================================================= +#define DATA_LOG 0 +#define DATA_MONITOR 1 +#define DEBOUNCE_TIME 20 +#define BUF_SIZE 100 // > 300 記憶體不夠用(4組) > 700 記憶體不夠用(2組) + +#define CR_1P1Z_B0_Q14 6313 +#define CR_1P1Z_B1_Q14 -5113 +#define CR_1P1Z_A1_Q14 -5803 +// 前饋輸出限制(請根據您主環 Q14 格式的數值範圍調整) +// 假設最大前饋量限制為實數值 50.0 -> 50.0 * 16384 = 819200 +#define CR_FF_MAX_Q14 819200 // 50 duty << 14 +#define CR_FF_MIN_Q14 -819200 + +//============================================================================= +// Macro Definition +//============================================================================= + +//============================================================================= +// Structure Definition +//============================================================================= +typedef enum { + KEY_IDLE = 0, + KEY_DOWN, + KEY_WAIT_RELEASE +} KeyState_t; + +typedef enum { + STATE_INIT, + STATE_STANDBY, // 等 AC brown-in + STATE_PFC_SOFT_START, // 第一階段:PFC 透過最小 duty 提升 vbus,若 vbus 足夠,直接進入下一階段 + STATE_LLC_SOFT_START, // 第二階段:LLC 掃頻 + STATE_RUN_NORMAL, // 第三階段:二級都進入穩態閉環 + STATE_RUN_SKIP, // 第四階段:負載太小,暫時關閉 PFC + STATE_FAULT, // 故障保護 + STATE_PAUSE // 暫停,測試用 +} SystemState_t; + +//============================================================================= +// Global Data Definition +//============================================================================= + +//#define kp 250 +//#define ki 2 +//#define kd 0 + +//#define ff_max 8000 + +//#define v_kp 10 +#define v_ki 10 +#define v_kd 0 + +SystemState_t sys_state = STATE_INIT; +uint8_t is_zero_crossing = 0; +uint8_t burst_mode_active = 0; +uint8_t llc_running = 0; +uint8_t pfc_running = 0; +static uint8_t last_is_zero_crossing = 0; +uint8_t is_brown_in = 0; // 0: 關機/保護中, 1: 正常工作 + +static volatile int32_t print1, print2; + +static uint16_t iac = 0; +static uint16_t vac = 0; +static uint16_t vbus = 0; +static uint16_t vcr = 0; + +static int32_t vcr_x1_q14 = 0; // x[n-1] +static int32_t vcr_y1_q14 = 0; // y[n-1] +static int32_t cr_feed_forward_output_q14 = 0; // 最終輸出的 Q14 補償量 + +uint32_t count1; + +uint32_t vbus_ovp = (VBUS_OVP * 4096 / VBUS_2ADC / 3.3); +uint32_t vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); +uint32_t vbus_set_h = (VBUS_SET_H * 4096 / VBUS_2ADC / 3.3); // burst mode in +uint32_t vbus_set_l = (VBUS_SET_L * 4096 / VBUS_2ADC / 3.3); // burst mode out +//uint32_t vbus_set_h2 = (VBUS_SET_H2 * 4096 / VBUS_2ADC / 3.3); +//uint32_t digi_vcomp_max = (uint32_t)(VBUS_SET_H * 4096 / VBUS_2ADC / 3.3) << DIGI_COMP_BIT; +uint32_t digi_vcomp_max = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +int32_t vbus_err = 0; +int32_t vbus_err_prev = 0; +int32_t vbus_comp = 0; +int32_t vbus_comp2 = 0; // 過零點用 +int32_t vbus_comp_prev = 0; +int32_t vbus_int = 0, vbus_por = 0; +uint16_t pfc_duty = 0; +uint8_t pfc_ss = 0; +uint16_t pfc_ss_cnt = 0; +uint16_t llc_ss_cnt = 0; +//volatile int16_t iac_err_0, iac_err_1; +//volatile uint16_t iac_set; +//volatile int32_t iac_int, iac_por, iac_der; +//volatile int16_t iac_comp = 0; +//----------------------------------------------- +KeyState_t key1State = KEY_IDLE; +uint32_t key1Timer = 0; +uint8_t key1_action = 0; +uint8_t keyInput; +//----------------------------------------------- +uint8_t power_on_cmd = 0; +uint8_t temp_off = 0; +uint32_t msg_timer; +uint16_t vbus_volt, vac_volt; +int32_t tx_buf1[BUF_SIZE]; +int64_t tx_buf2[BUF_SIZE]; +//uint16_t tx_buf3[BUF_SIZE]; +//uint16_t tx_buf4[BUF_SIZE]; +uint16_t buf_idx = 0; +uint8_t is_full = 0; +uint16_t count_down = 60000; // for data log +uint16_t i_ff; +uint32_t vac_avg1, vac_avg2, vac_avg3; +uint32_t vbus_avg = 0; +static volatile uint16_t vac_pk1, vac_pk2; +uint8_t hi_line = 0; // 0=110V, 1=220V +uint8_t vbus_ready = 0; +uint16_t hi_cnt, lo_cnt; +uint32_t static digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +int16_t fb_enent; +uint16_t llc_period_ss = LLC_SS_MIN; +uint16_t llc_run_arr = LLC_PERIOD_MIN; +uint16_t llc_arr_cmp = 0; +uint8_t v_loop_cnt = 0; +uint16_t brownout_cnt = 0; + +uint8_t volatile tim0_trigger = 0; + +// 全局或靜態變數(需在函數外或 static 保留狀態) +static int32_t iac_err_0 = 0; // e[k] +static int32_t iac_err_1 = 0; // e[k-1] +static int32_t iac_err_2 = 0; // e[k-2] +static int32_t iac_comp = 0; // u[k] +static int32_t iac_comp_1 = 0; // u[k-1] (前一次輸出) +static int32_t iac_comp_2 = 0; // u[k-2] (前兩次輸出) + +static int32_t vbus_err_1 = 0; +static int32_t vbus_comp_1 = 0; + +// 2P2Z 係數(這些需要根據你的 PID 增益與控制頻率重新計算或調試) +//int32_t b0 = kp + ki + kd; // 對應比例 + 部分積分/微分貢獻(可調整) +//int32_t b1 = -kp - kd - kd; // e[k-1] 係數(通常為正或負) +//int32_t b2 = kd; // e[k-2] 係數 +//int32_t a1 = 128; // -A1(因為通常寫成 u[k] = ... + A1*u[k-1] + A2*u[k-2],這裡 a1 = 128 代表 A1 ≈ 1) +//int32_t a2 = 0; // -A2(PID 特例時 a2 常為 0) + +// 電壓環 2p2z 係數 +//int32_t v_b0 = v_kp + v_ki + v_kd; +//int32_t v_b1 = -v_kp - v_kd - v_kd; +//int32_t v_b2 = v_kd; +//int32_t v_a1 = 128; +//int32_t v_a2 = 0; + +uint16_t updata_cnt = 0; + +int32_t vbus_comp_prev1, vbus_comp_prev2; +int32_t vbus_err_prev1, vbus_err_prev2; +//----------------------------------------------- + +//============================================================================= +// Private Function Definition +//============================================================================= +void A_Para_Reset() +{ + vbus_int = 0; vbus_comp = 1000; + //iac_int = 0; iac_comp = 1000; + digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; + vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); + pfc_ss = 0; pfc_ss_cnt = 0; + v_loop_cnt = 0; + temp_off = 0; + + iac_err_0 = 0; + iac_err_1 = 0; + iac_err_2 = 0; + iac_comp_1 = 0; + iac_comp_2 = 0; + + vbus_err = 0; + vbus_err_prev = 0; + vbus_err_prev1 = 0; + vbus_err_prev2 = 0; + vbus_comp_prev = 0; + vbus_comp_prev1 = 0; + vbus_comp_prev2 = 0; + vbus_comp2 = 1000; + + // set default value + TIM2->ARR = PFC_PERIOD; + TIM2->CCR1 = PFC_DUTY_MIN; // min for ss + TIM2->EGR |= 0x01; + EPWM->ARR = LLC_PERIOD_SS; + EPWM->CCR1 = (LLC_PERIOD_SS >> 1); + EPWM->EGR |= 0x01; + llc_period_ss = 0; + + pfc_running = 0; + llc_running = 0; + brownout_cnt = 0; +} +//------------------------------------------------------------------------------ +void Key1_Scan() +{ + keyInput = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15); + + switch (key1State) { + case KEY_IDLE: + if (keyInput == 0) { // 剛按下 + key1Timer = sys_get_tick(); // 紀錄時間 + key1State = KEY_DOWN; + } + break; + + case KEY_DOWN: + if (keyInput == 0) { + if (sys_get_tick() - key1Timer >= DEBOUNCE_TIME) { + key1State = KEY_WAIT_RELEASE; // 完成去抖 + } + } else { + key1State = KEY_IDLE; // 回到 IDLE + } + break; + + case KEY_WAIT_RELEASE: + if (keyInput == 1) { // 剛放開 + key1Timer = sys_get_tick(); + key1State = KEY_IDLE; + key1_action = 1; // 執行動作 + Key_Action(); + } + break; + } +} +//------------------------------------------------------------------------- +void Key_Action() +{ + if (power_on_cmd == 0) + { + power_on_cmd = 1; + //A_Para_Reset(); + //Power_On(); + } + else + { + power_on_cmd = 0; + Power_Off(); + } +} +//------------------------------------------------------------------------- +void Power_On() +{ + power_on_cmd = 1; + llc_pwm_enable(); // LLC ON first + pfc_pwm_enable(); +} +//------------------------------------------------------------------------- +void Power_Off() +{ + power_on_cmd = 0; + pfc_pwm_disable(); // PFC OF first + sys_delay(2000); // discharge + llc_pwm_disable(); + sys_state = STATE_PAUSE; + A_Para_Reset(); +} +//=================================================================================== +void llc_set_period(uint16_t val) +{ + if (val > LLC_PERIOD_MAX) val = LLC_PERIOD_MAX; // 可以小,不能過大 + EPWM->ARR = val; + EPWM->CCR1 = val>>1; +} +//=================================================================================== +void pfc_set_duty(uint16_t val) +{ + if (val > PFC_DUTY_MAX) val = PFC_DUTY_MAX; + if (val < PFC_DUTY_MIN) val = PFC_DUTY_MIN; + TIM2->CCR1 = val; +} +//=================================================================================== +void llc_pwm_enable() +{ + REG_SET_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); +} +//=================================================================================== +void llc_pwm_disable() +{ + REG_CLR_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); +} +//=================================================================================== +void pfc_pwm_enable() +{ + REG_SET_BITS(TIM2->CCER, TIM_CCER_CC1E_Msk); +} +//=================================================================================== +void pfc_pwm_disable() +{ + REG_CLR_BITS(TIM2->CCER, TIM_CCER_CC1E_Msk); +} +//=================================================================================== +void Vol_Loop_PFC() +{ + // 1. 計算誤差 + vbus_err = vbus_set - vbus_avg; + + // 2. 積分項 (I):負責消除靜態誤差 + vbus_int += vbus_err; // ki gain ,總共需要 8 ~ 10 bit + if (vbus_int > 0x0FFFFFFF) vbus_int = 0x0FFFFFFF; // 數值極大,可以不設上限,不要溢位即可 + if (vbus_int < 0) vbus_int = 0; + + // 3. 比例項 (P):負責動態響應 + //if (sys_state == STATE_PFC_SOFT_BURST) vbus_por = vbus_err >> 1; // 緩啟動時要慢 + //else if (sys_state == STATE_RUN_NORMAL) vbus_por = vbus_err << 2; // RUN 要快 (5 is better) + + // P 項限幅 + if (vbus_por > 10000) vbus_por = 10000; + if (vbus_por < -10000) vbus_por = -10000; + + int32_t combined_out = (vbus_int >> 10) + vbus_por; + + // 5. 總輸出限幅 + if (combined_out > 18000) combined_out = 18000; // 提高上限以支撐大電流需求 + if (combined_out < 1000) combined_out = 1000; + + //if (is_zero_crossing == 1) { vbus_comp2 = vbus_comp; } // 只在過零點更新,THD 應該會好一點 + vbus_comp = combined_out; + vbus_comp2 = vbus_comp; + + //print1 = vbus_comp; // ok + //print2 = vbus_comp2; // ok + + // 6. 緩啟動處理 + if (sys_state == STATE_PFC_SOFT_START) + { + pfc_set_duty(vbus_comp >> 5); // 6 不夠 + } +} +//====================================================================================== +void Vol_Loop_PFC_1P1Z(void) // TEST OK +{ + // 1. 計算誤差 + int32_t vbus_err_n = (int32_t)vbus_set - (int32_t)vbus_avg; + + // --- 策略 A:動態增益調整 --- + int32_t b0 = 135; + int32_t b1 = -134; + + if (vbus_err_n > 30 || vbus_err_n < -30) + { + // 大誤差時(通常是剛啟動或大跳載),強化參數加快反應 + //b0 = 250; + //b1 = -240; + b0 = 180; // 稍微調降強增益的力道 + b1 = -175; + } + // --- 策略 B:接近目標時微調零點 (抗過衝) --- + else if (vbus_err_n < 20 && vbus_err_n > -20) + { + // 當誤差縮小時,稍微減小 b0 與 b1 的差值,降低積分衝力 + b0 = 120; + b1 = -119; // 如果不能用浮點數,請維持 120 / -119,增加阻尼感 + } + + // 2. 差分方程計算 + int64_t acc = (int64_t)b0 * vbus_err_n; + acc += (int64_t)b1 * vbus_err_1; + acc += ((int64_t)16384 * vbus_comp_1) >> 14; // 16384 = 1 >> 14 + + int32_t y_n = (int32_t)acc; + +#if 0 // 有問題 + // --- 策略 C:軟限制抗過衝 (Soft Clamping) --- + // 預設最大限制 (Duty 300 << 14) + int32_t current_max_limit = 4915200; // duty 300 + + // 如果電壓誤差已經很小(例如剩 15V 就到達),甚至已經發生過衝 (err < 0) + // 我們強制壓低 Duty 的允許上限,防止積分器帶著大 Duty 衝過頭 + if (vbus_err_n < 15) + { + // 這裡的 3276800 對應 Duty 200,您可以根據 800W 穩態時的 Duty 大約位置來設定 + // 核心目標是:不讓 Duty 在接近目標時還維持在 300 這麼高 + current_max_limit = 3276800; // duty 200 + } + + // 如果發生明顯過衝 (電壓高於目標 5V 以上) + if (vbus_err_n < -5) + { + current_max_limit = 1638400; // 強制壓低到 Duty 100 快速拉回 + } +#endif + + // 3. 輸出限幅執行 + if (y_n > 8847360) y_n = 8847360; // 540 << 14 + else if (y_n < 196608) y_n = 196608; // 12 << 14 + + // 4. 更新狀態變數 + vbus_err_1 = vbus_err_n; + vbus_comp_1 = y_n; + + // 5. 輸出 Duty >> 14 + if (sys_state == STATE_PFC_SOFT_START) // 緩啟動期間直接控制 DUTY + TIM2->CCR1 = (vbus_comp_1 >> 14); // 0~540 + + // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 + if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 + vbus_comp2 = (vbus_comp_1 >> 9); +} +//========================================================================== +// Cr 1P1Z 回授,做負載驟變補償,執行頻率 10KHZ +// 抓取 vcr 的動態變化趨勢,並在穩態時回歸平靜 +void Load_Loop_1P1Z(void) +{ + int32_t x0; + int64_t temp_y0; // 使用 64 位元暫存,完全避免 32 位元相乘時溢位 + int32_t y0; + + // 1. 讀取當前 vcr + x0 = vcr; + + // 2. 執行 1P1Z 差分方程計算: + temp_y0 = ( (int64_t)CR_1P1Z_B0_Q14 * x0 ) + + ( (int64_t)CR_1P1Z_B1_Q14 * vcr_x1_q14 ) + - ( (int64_t)CR_1P1Z_A1_Q14 * vcr_y1_q14 ); + + // 將 Q28 格式還原回 Q14 格式(右移 14 位元 + y0 = (int32_t)(temp_y0 >> 14); + + // 更新歷史狀態變數 (Q14 格式) + vcr_x1_q14 = x0; + vcr_y1_q14 = y0; + + // 定點數安全限幅 (Saturation) + if (y0 > CR_FF_MAX_Q14) y0 = CR_FF_MAX_Q14; // 868352 = 53 duty + else if (y0 > -CR_FF_MIN_Q14) y0 = -CR_FF_MIN_Q14; + + cr_feed_forward_output_q14 = y0; +} +//======================================================================== +__attribute__((always_inline)) static inline void Cur_Loop_PFC_2P2Z(void) // 優化後 3.65us +{ + if (sys_state != STATE_RUN_NORMAL) return; + + // 1. 取得回授與計算誤差 + int32_t local_vbus_comp2 = vbus_comp2; + int32_t local_vac_avg1 = vac_avg1; + + // 電流採樣處理 + int32_t local_iac = 3840 - iac; // OPA 的關係,實際值為 CURR_OFFSET - ADC + if (local_iac < 0) local_iac = 0; // 避免負值 + + // 計算電流給定值 + int32_t local_iac_set = (local_vac_avg1 * local_vbus_comp2) / 5700; + + int32_t local_iac_err_0 = local_iac_set - local_iac; // err = set - meas + //local_iac_err_0 = -local_iac_err_0; // err 反向 + print1 = local_iac_err_0; + + // 2. 差分方程 + // y[n] = b0 * err[n] + b1 * err[n-1] + b2 * err[n-2] + a1 * y[n-1] + a2 * y[n-2] + // y = 32 bit + // err = 14 bit (實際大小 12 bit) + // b and a << 14 bit + // a * y[n] 會超過 32 bit,所以中間計算暫存需要 64bit,回存時用 32bit + // 計算後的 y --> 4 給 duty + // b係數可以同時乘上一個 gain + + // 2. 2P2Z 運算 (Fixed-point Q14) +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=4, PF=0.99, THD=10.7 + int32_t local_temp = (1510 * local_iac_err_0) + + (2339 * iac_err_1) + + (859 * iac_err_2) + + (20921 * iac_comp_1) + - (4545 * iac_comp_2); +#endif + +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, PF=0.99, THD=10.7 (B1 and A1 是負的) + int32_t y_n = (377 * local_iac_err_0) + - (585 * iac_err_1) + + (215 * iac_err_2) + - (20921 * iac_comp_1) // 減 A1 + + (4545 * iac_comp_2); // 加 A2 +#endif + +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (A1為正是對的) (min duty) + int64_t acc = ((int64_t)598 * local_iac_err_0) // < 12 bit + + ((int64_t)-64 * iac_err_1) + + ((int64_t)-408 * iac_err_2); + acc <<= 6; + acc -= ((int64_t)-12644 * iac_comp_1); // y[n-1] + acc -= ((int64_t)-3615 * iac_comp_2); // y[n-2] +#endif + +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (A1為正是對的) (min duty) + int64_t acc = ((int64_t)598 * local_iac_err_0) // < 12 bit + + ((int64_t)-64 * iac_err_1) + + ((int64_t)-408 * iac_err_2) + - ((int64_t)-12644 * iac_comp_1) // y[n-1] + - ((int64_t)-3615 * iac_comp_2); // y[n-2] +#endif + +#if 1 // Z1=800, Z2=8000, P1=10, P2=18000, G=1, (AI fixed) (衝到OVP) + int64_t acc = ((int64_t)5004 * local_iac_err_0) // < 12 bit + - ((int64_t)7522 * iac_err_1) + + ((int64_t)2711 * iac_err_2) + + ((int64_t)25633 * iac_comp_1) // y[n-1] + - ((int64_t)9260 * iac_comp_2); // y[n-2] +#endif + +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=4, (4倍震盪太大) + int64_t acc = ((int64_t)1510 * local_iac_err_0) // < 12 bit + + ((int64_t)-2339 * iac_err_1) + + ((int64_t)859 * iac_err_2) + - ((int64_t)-20921 * iac_comp_1) // y[n-1] + - ((int64_t)4545 * iac_comp_2); // y[n-2] +#endif + +#if 0 // Z1=800, Z2=8000, P1=10, P2=18000, G=2, (min duty) + int64_t acc = ((int64_t)755 * local_iac_err_0) // < 12 bit + - ((int64_t)1170 * iac_err_1) + + ((int64_t)430 * iac_err_2) + + ((int64_t)20921 * iac_comp_1) // y[n-1] + - ((int64_t)4545 * iac_comp_2); // y[n-2] +#endif + + // 抵消係數的 Q14,回到 32-bit 範圍 + // 此時 y_n 的量級與 err 是一致的 + //int32_t y_n = (int32_t)(acc >> 14); // 有括號,先 >> 14 再轉型成 int32 + + // 2. 更新狀態變數時,應儲存高解析度的數值 + // 假設 y_n_q14 代表帶著 Q14 的結果 + int32_t y_n = (int32_t)acc; + + // 限制極限值 + if (y_n > 8847360) y_n = 8847360; // 540 << 14 (回存原值,不是縮小後的) + else if (y_n < 196608) y_n = 196608; // 12 << 14 (最小輸出會變成 0 ??) + + print2 = y_n; + // 更新狀態變數 (儲存原始計算值以保留積分能量) + iac_err_2 = iac_err_1; + iac_err_1 = local_iac_err_0; + + // 重要:iac_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 + iac_comp_2 = iac_comp_1; + iac_comp_1 = y_n; + + // 4. 硬體 Duty 限幅與輸出 + + uint16_t local_duty = (uint16_t)(y_n >> 14); + if (local_duty > PFC_DUTY_MAX) local_duty = PFC_DUTY_MAX; // MAX = 540 + else if (local_duty < PFC_DUTY_MIN) local_duty = PFC_DUTY_MIN; // MIN = 12 + + TIM2->CCR1 = local_duty; // 有括號,先 >> 14 + + //count1++; + // ------------------------------------------- + // 5. data log +#if DATA_LOG + count_down--; + if (count_down < 1000) + { + tx_buf1[buf_idx] = local_iac_err_0; // y[n] + tx_buf2[buf_idx] = acc; // x[n] + //tx_buf3[buf_idx] = local_iac_err_0; + //tx_buf4[buf_idx] = local_duty; + buf_idx++; + } + + if (buf_idx >= (BUF_SIZE-1)) + { + is_full = 1; + pfc_pwm_disable(); + sys_state = STATE_PAUSE; + } +#endif + // ------------------------------------------- +} +//============================================================================ +/* +void Vol_Loop_LLC(void) // LLC voltage loop control: adjust frequency based on digital comparator +{ + // Read GPIO feedback signal + uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); + + // Adjust digital_vcomp based on GPIO state (increment if high, decrement if low) + if (gpio == 1) { + if (digital_vcomp < digi_vcomp_max) digital_vcomp++; + } else { + if (digital_vcomp > 1000) digital_vcomp--; + } + + // Calculate new ARR value for LLC frequency adjustment + uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); + + // Clamp to valid period range + if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; + if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; + + // Update EPWM registers only if value changed to avoid unnecessary writes + if (EPWM->ARR != new_arr) { + EPWM->ARR = new_arr; + EPWM->CCR1 = (new_arr >> 1); + } +} +*/ +void Vol_Loop_LLC(void) // 測試可工作 +{ + static uint8_t last_gpio = 0; + static uint16_t toggle_cnt = 0; + static uint16_t stable_timer = 0; + + // 1. 讀取 GPIO 回授 + uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); + + // 2. 判斷是否發生翻轉 (判斷是否在平衡點附近跳動) + if (gpio != last_gpio) { + toggle_cnt++; + last_gpio = gpio; + stable_timer = 0; // 只要有翻轉,就重置穩定計時器 + } else { + // 如果 GPIO 長時間沒翻轉,說明離目標還很遠 + if (stable_timer < 1000) stable_timer++; + } + + // 3. 根據翻轉頻率決定步長 + uint16_t current_step; + if (toggle_cnt > STABLE_THRES) { + // 頻繁翻轉:進入「死區」或「極小步長」模式 + current_step = 0; // 設為 0 表示進入死區,完全停止震盪 + + // 為了防止永久卡死,當連續相同狀態一段時間後再恢復調整 + if (stable_timer > 50) toggle_cnt = 0; + } else { + // 距離目標尚遠:根據 stable_timer 決定快慢 + current_step = (stable_timer > 100) ? STEP_FAST : STEP_SLOW; + } + + // 4. 執行調整 + if (current_step > 0) { + if (gpio == 1) { + if (digital_vcomp + current_step <= digi_vcomp_max) + digital_vcomp += current_step; + } else { + if (digital_vcomp > (1000 + current_step)) + digital_vcomp -= current_step; + } + } + + // 5. 計算並更新 ARR (原本的邏輯) + uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); + + if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; + if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; + + if (EPWM->ARR != new_arr) { + EPWM->ARR = new_arr; + EPWM->CCR1 = (new_arr >> 1); + } +} +//================================================================================= +__INTERRUPT void isr_adc_handle(void) +{ + SAVE_IRQ_CSR_CONTEXT(); + ADC_ClearITFlag(ADC0); + + //GPIO_SetBits(GPIOA, GPIO_Pin_06); + + tim0_trigger = 1; + + // get ADC value + iac = ADC0->DAT9_b.DATA; // PA7, AIN9 + vac = ADC0->DAT7_b.DATA; // PA13, AIN7 + vbus = ADC0->DAT4_b.DATA; // PA10, AIN4 + vcr = ADC0->DAT5_b.DATA; // PA11, AIN5 + + Cur_Loop_PFC_2P2Z(); // current loop + + //GPIO_ResetBits(GPIOA, GPIO_Pin_06); + + RESTORE_IRQ_CSR_CONTEXT(); + return; +} +//============================================================================ +void reset_pid_parameters(void) +{ + vbus_int = 0; vbus_comp = 1000; + + iac_err_0 = 0; + iac_err_1 = 0; + iac_err_2 = 0; + iac_comp = 0; + iac_comp_1 = 0; + iac_comp_2 = 0; + + digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +} +//============================================================================== +void Handle_UART_Monitor(void) +{ + if (sys_get_tick() - msg_timer >= 1000) // msg_out = 1, print + { + //printf("%d %d %d\r\n", vac_pk1, vac_pk2, is_brown_in); + printf("%d %d %d %d %d", power_on_cmd, sys_state, vbus_comp2, print1, print2); + //printf("%d %d", print1, print2); + // 電壓環輸出最大 COMP,但是電流環輸出最小 DUTY + msg_timer = sys_get_tick(); + } + // iac 零點大約 3640 ~ 3680 +} +//================================================================================ +void Handle_UART_Log(void) +{ + for (int i = 0; i < BUF_SIZE; i++) + { + //printf("%d:%d:%d:%d\r\n", tx_buf1[i], tx_buf2[i], tx_buf3[i], tx_buf4[i]); + printf("%d:%d\r\n", tx_buf1[i], tx_buf2[i]); + //printf("%d\r\n", tx_buf1[i]); + sys_delay(5); + } + is_full = 0; // [重要] 列印完務必清除旗標,否則會死循環 + buf_idx = 0; // 重置索引 +} +//================================================================================ +void Vac_Peak_Detector(void) +{ + // 1. 峰值追蹤 (Peak Detector):只要不在過零區,就持續抓最大值 + if (vac >= VAC_ZERO) + { + is_zero_crossing = 0; + last_is_zero_crossing = 0; + if (vac > vac_pk1) vac_pk1 = vac; // 只有比目前紀錄大才更新,確保 vac_pk1 停在波峰 + } + else + { + // 2. 進入過零區 (vac < 400) + is_zero_crossing = 1; + // 3. 邊緣觸發:只在從「非過零」進入「過零」的瞬間交接數據 + if (last_is_zero_crossing == 0 && vac_pk1 > VAC_ZERO_RISE) + { + vac_pk2 = vac_pk1; // 這裡的 vac_pk1 必定是剛才半周抓到的最高點 + + // --- 新增 Brown-in / Brown-out 判斷邏輯 --- + if (is_brown_in == 0) + { + // 目前處於關閉狀態,檢查是否達到啟動門檻 + if (vac_pk2 >= VOLT_BROWN_IN) is_brown_in = 1; // 執行 Brown-in + } + else + { + // 目前處於啟動狀態,檢查是否低於欠壓門檻 + if (vac_pk2 < VOLT_BROWN_OUT) is_brown_in = 0; // 執行 Brown-out + } + + vac_pk1 = 0; // 交接完後立即清零,為下半周做準備 + last_is_zero_crossing = 1; // 剛過上升緣 + } + } +} +//============================================================================= +// Public Function Definition +//============================================================================= +int main(void) +{ + // 基礎硬體底層設定 (HIRC 60MHz, GPIO, ADC 採樣率 1Msps) [cite: 55, 127, 184] + SYS_Config(); + +#if 0 + pfc_set_duty(540); + pfc_pwm_enable(); + + //llc_period_ss = LLC_SS_MAX; + //llc_set_period(llc_period_ss>>2); + //llc_pwm_enable(); + //while(1); +#endif + +#if 0 // 測試 LLC 電壓環 (OK) + llc_period_ss = LLC_SS_MAX; + llc_set_period(llc_period_ss>>2); + llc_pwm_enable(); + + while(1) + { + if (tim0_trigger == 1) + { + tim0_trigger = 0; + Vol_Loop_LLC(); + } + + Handle_UART_Debug(); + } +#endif + +#if 0 // 測試 PFC 電壓環 (OK) + TIM2->CCR1 = 12; // min duty + sys_state = STATE_PFC_SOFT_START; + + Vac_Peak_Detector(); + + while(1) + { + if (tim0_trigger == 1) // 100KHZ + { + tim0_trigger = 0; + // --- 1. 高速信號濾波 (簡易位移算法減少算力消耗) --- + vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop + vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop + if (vbus > vbus_ovp) Power_Off(); // ovp + + Vac_Peak_Detector(); + + if (power_on_cmd == 1) + { + if (pfc_running == 0) { + pfc_pwm_enable(); + pfc_running = 1; + } + + if (++v_loop_cnt >= 10) // 10KHZ + { + Vol_Loop_PFC_1P1Z(); // 1P1Z 不穩壓?? + v_loop_cnt = 0; + } + } + + } + Key1_Scan(); + } +#endif + +#if 1 // 測試 PFC 電壓環 + 電流環 (電流環無法穩壓) (電壓環 + 電流環有 BUG,會多一個小 PULSE) + TIM2->CCR1 = 12; // min duty + sys_state = STATE_PFC_SOFT_START; + + Vac_Peak_Detector(); + pfc_running = 0; + + while(1) + { + if (tim0_trigger == 1) // 100KHZ + { + tim0_trigger = 0; + // --- 1. 高速信號濾波 (簡易位移算法減少算力消耗) --- + vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop + vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop + if (vbus > vbus_ovp) Power_Off(); // ovp + + //Vac_Peak_Detector(); + + if (power_on_cmd == 1) + { + if (sys_state == STATE_PFC_SOFT_START) + { + if (pfc_running == 0) { + pfc_pwm_enable(); + pfc_running = 1; + } + + if (++v_loop_cnt >= 10) // 10KHZ + { + Vol_Loop_PFC_1P1Z(); + v_loop_cnt = 0; + } + + + if (vbus_avg > vbus_set_l) + { + pfc_pwm_disable(); + pfc_running = 0; + sys_delay(10); + sys_state == STATE_PAUSE; + } + + /* + if (is_zero_crossing == 1) + { + //sys_state = STATE_RUN_NORMAL; + // --- 關鍵修正:重新初始化電壓環內部狀態 --- + // 給予一個極小的初始 COMP 值,讓它從輕載慢慢往上爬 + vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 + vbus_err_1 = 0; // 清除誤差歷史 + vbus_comp2 = (vbus_comp_1 >> 9); // 讓電流環拿到的初始值是 10 + } + */ + } + /* + else if (sys_state == STATE_RUN_NORMAL) + { + if (pfc_running == 0) { + pfc_pwm_enable(); + pfc_running = 1; + } + + if (++v_loop_cnt >= 10) // 10KHZ + { + Vol_Loop_PFC_1P1Z(); + v_loop_cnt = 0; + } + } + */ + } + + } + Key1_Scan(); + + } +#endif + + + // main loop + while(1) + { + // 核心定時觸發:100kHz (由 TIMER0 旗標觸發) [cite: 318] + if (tim0_trigger == 1) + { + tim0_trigger = 0; + + // 高速信號濾波 (簡易位移算法減少算力消耗) --- + vac_avg1 = (vac_avg1 - (vac_avg1 >> 3)) + (vac >> 3); // for current loop + vbus_avg = (vbus_avg - (vbus_avg >> 4)) + (vbus >> 4); // for voltage loop + // 系統安全監控 (軟體第二道防護) + if (vbus > vbus_ovp) { sys_state = STATE_FAULT; } // vbus ovp + if (vac_pk2 > 2600) { sys_state = STATE_FAULT; } // vac over voltage + + Vac_Peak_Detector(); // VAC 峰值偵測 +#if 1 + // --- 2. 核心狀態機邏輯 ----------------------------------------------------------------------- + switch (sys_state) + { + case STATE_INIT: // 0 + // 等待約 100ms,並按下按鍵 + if (sys_get_tick() > 100 && power_on_cmd == 1) { + sys_state = STATE_STANDBY; + } + break; + + case STATE_STANDBY: // 1 + // Brown-in 判斷 + if (is_brown_in == 1) { + sys_state = STATE_PFC_SOFT_START; // 先進 PFC SS + TIM2->CCR1 = PFC_DUTY_MIN; + } + break; + + case STATE_PFC_SOFT_START: // 2 + // 若 vbus < 320 做 pre-charge + if (vbus_avg < vbus_set_l) + { + if (pfc_running == 0) { + pfc_pwm_enable(); + pfc_running = 1; + } + if (++v_loop_cnt >= 10) { + Vol_Loop_PFC_1P1Z(); + //Vol_Loop_PFC(); + v_loop_cnt = 0; + } + } + else + { + //pfc_pwm_disable(); // 測試 OK + //pfc_running = 0; + // PFC 不關閉,在 LLC SS 期間進閉環 + // sys_state = STATE_PAUSE; // 先暫停,測試用 + sys_state = STATE_LLC_SOFT_START; + // 準備 LLC 軟啟動參數 (起點頻率 2倍諧振頻率) + llc_period_ss = LLC_SS_MIN; // 已經放大 4 倍 + //sys_state = STATE_RUN_NORMAL; // 直接接電流環 + // 重要動作 + vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 + vbus_err_1 = 0; // 清除誤差歷史 + } + break; + + case STATE_LLC_SOFT_START: // 3 + // LLC 軟啟動:頻率由高往低掃描 (掃向 1倍諧振頻率) + llc_period_ss++; + llc_ss_cnt++; + llc_set_period(llc_period_ss >> 2); // 放大 4 倍,要除 4 回來 + + if (llc_running == 0) { + llc_pwm_enable(); + llc_running = 1; + } + // LLC 掃頻結束,PFC 在過零點打開 + // if (llc_ss_cnt >= LLC_SS_COUNT && is_zero_crossing == 1) { + if (llc_ss_cnt >= LLC_SS_COUNT) { + sys_state = STATE_RUN_NORMAL; + // --- 關鍵修正:重新初始化電壓環內部狀態 --- + // 給予一個極小的初始 COMP 值,讓它從輕載慢慢往上爬 + //vbus_comp_1 = (10 << 14); // 假設從一個極小的係數開始 + //vbus_err_1 = 0; // 清除誤差歷史 + //vbus_comp2 = (vbus_comp_1 >> 9); // 讓電流環拿到的初始值是 10 + // 單測試 PFC 可以,但是加上 LLC 就不行? 原因是 E-LOAD ? + } + // PFC 直接閉環 + if (++v_loop_cnt >= 10) { + Vol_Loop_PFC_1P1Z(); + //Vol_Loop_PFC(); + Vol_Loop_LLC(); + v_loop_cnt = 0; + } + + break; + + case STATE_RUN_NORMAL: // 4 + //if (pfc_running == 0) { + // pfc_pwm_enable(); + // pfc_running = 1; + //} + + if (++v_loop_cnt >= 10) { + // 先算前饋,再算主環,最後疊加 + //Load_Loop_1P1Z(); // Cr 前饋 + Vol_Loop_PFC_1P1Z(); // PFC 電壓環 + //Vol_Loop_PFC(); + Vol_Loop_LLC(); // LLC 電壓環 + v_loop_cnt = 0; + } + + break; + + case STATE_RUN_SKIP: // 5 + // 電壓外環計算 (10kHz) + if (++v_loop_cnt >= 10) { + Vol_Loop_PFC(); // KP / KI 要快 + //Vol_Loop_LLC(); + v_loop_cnt = 0; + } + /* 有問題 + // Brown-out 判斷 + if (vac_pk2 < VAC_BROWN_OUT) { + brownout_cnt++; + if (brownout_cnt > BROWNOUT_DELAY) { + Power_Off(); + sys_state = STATE_STANDBY; + } + } + else { + brownout_cnt = 0; + } + */ + /* 先不做 + // --- BURST / SKIP 邏輯 (帶滯後區間) --- + // 只在過零點判斷 + if (is_zero_crossing == 1) { + if (vbus_avg > vbus_set_h) { // 上限觸發停機 + pfc_pwm_disable(); + burst_mode_active = 1; + } + else if (vbus_avg < vbus_set_l && burst_mode_active == 1) { // 下限觸發回復 + reset_pid_parameters(); + pfc_pwm_enable(); + burst_mode_active = 0; + } + } + */ + break; + + case STATE_FAULT: // 5 + // 立即封鎖所有輸出 + pfc_pwm_disable(); + llc_pwm_disable(); + // 記錄故障代碼,等待手動重置 + break; + + case STATE_PAUSE: // 6 + break; + + } // switch case +#endif + + + } // if tim0_trigger + + // --- 3. 非即時任務 (UI、通訊、Data Log) --- + Key1_Scan(); + +#if DATA_LOG + if (is_full == 1) Handle_UART_Log(); // 利用 PEC930 UART 輸出數據 [cite: 105, 336] +#endif + +#if DATA_MONITOR + Handle_UART_Monitor(); +#endif + + } // while -------------------------------------------------------------------- + + return 0; +} +#endif diff --git a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/init.c b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/init.c index 32592a8..ee8cddb 100644 --- a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/init.c +++ b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/init.c @@ -1,144 +1,144 @@ -/** - * Copyright (c) 2026 Wisetop. All Rights Reserved. - */ - -#include "init.h" -#include "app.h" -#include "hal_device.h" - -//============================================================================= -// Constant Definition -//============================================================================= - -//============================================================================= -// Public Function Definition -//============================================================================= -void gpio_init(void) -{ - GPIO_InitTypeDef gpio_init = { 0 }; - - // PA12, PWOK - gpio_init.GPIO_Pin = GPIO_Pin_12; - gpio_init.GPIO_Mode = GPIO_Mode_OUT; - gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(GPIOA, &gpio_init); -} -//=================================================================================== -void adc_init(void) -{ - // gpio - GPIO_InitTypeDef gpio_init; - gpio_init.GPIO_Pin = GPIO_Pin_08; // PA8 AIN2 VBUS - gpio_init.GPIO_Pin |= GPIO_Pin_09; // PA9 AIN3 IP_CS - gpio_init.GPIO_Pin |= GPIO_Pin_10; // PA10 AIN4 VCOMP - gpio_init.GPIO_Mode = GPIO_Mode_ANAL; - GPIO_Init(GPIOA, &gpio_init); - - // adc - ADC_InitTypeDef adc_init = { 0 }; - ADC_StructInit(&adc_init); - adc_init.SelChannels = ADC_Channel_02; - adc_init.SelChannels |= ADC_Channel_03; - adc_init.SelChannels |= ADC_Channel_04; - adc_init.ClkPrescaler = ADC_ClkDiv_4; // 60M/4 = 15MHz, ADC max working clock is 16MHz - adc_init.DataAlign = ADC_DataAlign_Right; - adc_init.Mode = ADC_Mode_Scan; // Scan mode - ADC_Init(ADC0, &adc_init); - ADC_ExtTrigConfig(ADC0, ADC_ExtTrigSource_EPWM_CH1R, ADC_ExtTrigMode_Enable); - - // ADC interrupt config - sys_irq_attr_t irq_attr = { - .disable_vector = false, - .trig_mode = SYS_IRQ_TRIGGER_LEVEL, - .level = SYS_IRQ_LEVEL_H, - .priority = SYS_IRQ_PRIORITY_MIDDEN, - }; - sys_register_IRQ(ADC0_IRQn, isr_adc_handle, &irq_attr); - - ADC0->CON0_b.INT_EN = 1; // ADC interrupt enable -} -//=================================================================================== -void llc_pwm_init(void) -{ - // gpio - // PA0(EPWM0P), PA1(EPWM0N) - GPIO_InitTypeDef gpio_init = { - .GPIO_Pin = GPIO_Pin_00 | GPIO_Pin_01, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_AF_Mode = GPIO_AF_6, - }; - GPIO_Init(GPIOA, &gpio_init); - - // tim - TIM_TimeBaseInitTypeDef tim_init = { - .TIM_Prescaler = 0, - .TIM_CounterMode = TIM_CounterMode_Up, - .TIM_Period = LLC_PERIOD_SS, - .TIM_ClockDivision = TIM_CKD_Div1, - .TIM_RepetitionCounter = 0, - }; - TIM_DeInit(EPWM); - TIM_TimeBaseInit(EPWM, &tim_init); - - TIM_ARRPreloadConfig(EPWM, ENABLE); - - // pwm - TIM_OCInitTypeDef pwm_init = { - .TIM_OCMode = TIM_OCMode_PWM1, - .TIM_OutputState = TIM_OutputState_Disable, - .TIM_OCPolarity = TIM_OCPolarity_High, - .TIM_OCIdleState = TIM_OCIdleState_Reset, - .TIM_OutputNState = TIM_OutputNState_Disable, - .TIM_OCNPolarity = TIM_OCPolarity_High, - .TIM_Pulse = 1, - }; - TIM_OC1Init(EPWM, &pwm_init); - TIM_OC2Init(EPWM, &pwm_init); - - { /* Automatic Output enable, Break, dead time and lock configuration */ - TIM_BDTRInitTypeDef bdtr_init = { 0 }; - bdtr_init.TIM_LOCKLevel = TIM_LockLevel_OFF; - bdtr_init.TIM_DeadTime = DEAD_TIME_VALUE; - TIM_BDTRConfig(EPWM, &bdtr_init); - } - - TIM_Cmd(EPWM, ENABLE); - TIM_CtrlPWMOutputs(EPWM, ENABLE); - EPWM->CCMR1_OUTPUT_b.OC1PE = 1; // Output Compare 1 preload enable -} -//=================================================================================== -void tim0_init(void) -{ - LPTIM_InitTypeDef tim0_init = { - .LPTIM_Prescaler = 0, - .LPTIM_ClockSource = LPTIM_CLK_Src_SysClk, - .LPTIM_MatchMode = LPTIM_MatchMode_IRQ | LPTIM_MatchMode_Reset, - .LPTIM_MatchValue = TIM0_PERIOD, - }; - LPTIM_Init(TIM0, &tim0_init); - - LPTIM_Enable(TIM0); -} -//=================================================================================== -void sysclk_init(void) -{ - SYSCFG_ClkInitTypeDef SysClkInit = { - .ClkSource = SYSCFG_ClkSrc_HSI, - }; - - SYSCFG_SysClkConfig(&SysClkInit); - sys_config_systick(SYS_TICK_1_MS); -} -//=================================================================================== -void SYS_Config(void) -{ - sysclk_init(); - - gpio_init(); - - adc_init(); - - llc_pwm_init(); - - uart_init(); -} +/** + * Copyright (c) 2026 Wisetop. All Rights Reserved. + */ + +#include "init.h" +#include "app.h" +#include "hal_device.h" + +//============================================================================= +// Constant Definition +//============================================================================= + +//============================================================================= +// Public Function Definition +//============================================================================= +void gpio_init(void) +{ + GPIO_InitTypeDef gpio_init = { 0 }; + + // PA12, PWOK + gpio_init.GPIO_Pin = GPIO_Pin_12; + gpio_init.GPIO_Mode = GPIO_Mode_OUT; + gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOA, &gpio_init); +} +//=================================================================================== +void adc_init(void) +{ + // gpio + GPIO_InitTypeDef gpio_init; + gpio_init.GPIO_Pin = GPIO_Pin_08; // PA8 AIN2 VBUS + gpio_init.GPIO_Pin |= GPIO_Pin_09; // PA9 AIN3 IP_CS + gpio_init.GPIO_Pin |= GPIO_Pin_10; // PA10 AIN4 VCOMP + gpio_init.GPIO_Mode = GPIO_Mode_ANAL; + GPIO_Init(GPIOA, &gpio_init); + + // adc + ADC_InitTypeDef adc_init = { 0 }; + ADC_StructInit(&adc_init); + adc_init.SelChannels = ADC_Channel_02; + adc_init.SelChannels |= ADC_Channel_03; + adc_init.SelChannels |= ADC_Channel_04; + adc_init.ClkPrescaler = ADC_ClkDiv_4; // 60M/4 = 15MHz, ADC max working clock is 16MHz + adc_init.DataAlign = ADC_DataAlign_Right; + adc_init.Mode = ADC_Mode_Scan; // Scan mode + ADC_Init(ADC0, &adc_init); + ADC_ExtTrigConfig(ADC0, ADC_ExtTrigSource_EPWM_CH1R, ADC_ExtTrigMode_Enable); + + // ADC interrupt config + sys_irq_attr_t irq_attr = { + .disable_vector = false, + .trig_mode = SYS_IRQ_TRIGGER_LEVEL, + .level = SYS_IRQ_LEVEL_H, + .priority = SYS_IRQ_PRIORITY_MIDDEN, + }; + sys_register_IRQ(ADC0_IRQn, isr_adc_handle, &irq_attr); + + ADC0->CON0_b.INT_EN = 1; // ADC interrupt enable +} +//=================================================================================== +void llc_pwm_init(void) +{ + // gpio + // PA0(EPWM0P), PA1(EPWM0N) + GPIO_InitTypeDef gpio_init = { + .GPIO_Pin = GPIO_Pin_00 | GPIO_Pin_01, + .GPIO_Mode = GPIO_Mode_AF, + .GPIO_AF_Mode = GPIO_AF_6, + }; + GPIO_Init(GPIOA, &gpio_init); + + // tim + TIM_TimeBaseInitTypeDef tim_init = { + .TIM_Prescaler = 0, + .TIM_CounterMode = TIM_CounterMode_Up, + .TIM_Period = LLC_PERIOD_SS, + .TIM_ClockDivision = TIM_CKD_Div1, + .TIM_RepetitionCounter = 0, + }; + TIM_DeInit(EPWM); + TIM_TimeBaseInit(EPWM, &tim_init); + + TIM_ARRPreloadConfig(EPWM, ENABLE); + + // pwm + TIM_OCInitTypeDef pwm_init = { + .TIM_OCMode = TIM_OCMode_PWM1, + .TIM_OutputState = TIM_OutputState_Disable, + .TIM_OCPolarity = TIM_OCPolarity_High, + .TIM_OCIdleState = TIM_OCIdleState_Reset, + .TIM_OutputNState = TIM_OutputNState_Disable, + .TIM_OCNPolarity = TIM_OCPolarity_High, + .TIM_Pulse = 1, + }; + TIM_OC1Init(EPWM, &pwm_init); + TIM_OC2Init(EPWM, &pwm_init); + + { /* Automatic Output enable, Break, dead time and lock configuration */ + TIM_BDTRInitTypeDef bdtr_init = { 0 }; + bdtr_init.TIM_LOCKLevel = TIM_LockLevel_OFF; + bdtr_init.TIM_DeadTime = DEAD_TIME_VALUE; + TIM_BDTRConfig(EPWM, &bdtr_init); + } + + TIM_Cmd(EPWM, ENABLE); + TIM_CtrlPWMOutputs(EPWM, ENABLE); + EPWM->CCMR1_OUTPUT_b.OC1PE = 1; // Output Compare 1 preload enable +} +//=================================================================================== +void tim0_init(void) +{ + LPTIM_InitTypeDef tim0_init = { + .LPTIM_Prescaler = 0, + .LPTIM_ClockSource = LPTIM_CLK_Src_SysClk, + .LPTIM_MatchMode = LPTIM_MatchMode_IRQ | LPTIM_MatchMode_Reset, + .LPTIM_MatchValue = TIM0_PERIOD, + }; + LPTIM_Init(TIM0, &tim0_init); + + LPTIM_Enable(TIM0); +} +//=================================================================================== +void sysclk_init(void) +{ + SYSCFG_ClkInitTypeDef SysClkInit = { + .ClkSource = SYSCFG_ClkSrc_HSI, + }; + + SYSCFG_SysClkConfig(&SysClkInit); + sys_config_systick(SYS_TICK_1_MS); +} +//=================================================================================== +void SYS_Config(void) +{ + sysclk_init(); + + gpio_init(); + + adc_init(); + + llc_pwm_init(); + + uart_init(); +} diff --git a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/main.c b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/main.c index 8b5f540..9257217 100644 --- a/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/main.c +++ b/pec930_sdk-v1.0.1/Examples/template/LLC_48V/src/main.c @@ -1,713 +1,717 @@ - -#include "main.h" -#include "app.h" -#include "init.h" -//============================================================================= -// Constant Definition -//============================================================================= -#define CAPTURE_BUF_SIZE 160 - -#define DEBOUNCE_TIME 20 - -#define CR_1P1Z_B0_Q14 6313 -#define CR_1P1Z_B1_Q14 -5113 -#define CR_1P1Z_A1_Q14 -5803 -// 前饋輸出限制(請根據您主環 Q14 格式的數值範圍調整) -// 假設最大前饋量限制為實數值 50.0 -> 50.0 * 16384 = 819200 -#define CR_FF_MAX_Q14 1638400 // 819200 // 50 duty << 14 -#define CR_FF_MIN_Q14 -1638400 //-819200 - -//============================================================================= -// Macro Definition -//============================================================================= - -//============================================================================= -// Structure Definition -//============================================================================= - -typedef enum -{ - STATE_STOP, - STATE_SS, - STATE_RUN -} SystemState_t; - -typedef struct -{ - // uint32_t timestamp; - uint16_t iac; - // uint16_t vac; - // uint16_t vbus; - // uint16_t vcr; -} CaptureSample_t; - -//============================================================================= -// Global Data Definition -//============================================================================= - -/* PID 轉換 2P2Z係數*/ -// #define kp 250 -// #define ki 2 -// #define kd 0 - -// 2P2Z 係數(這些需要根據你的 PID 增益與控制頻率重新計算或調試) -// int32_t b0 = kp + ki + kd; // 對應比例 + 部分積分/微分貢獻(可調整) -// int32_t b1 = -kp - kd - kd; // e[k-1] 係數(通常為正或負) -// int32_t b2 = kd; // e[k-2] 係數 -// int32_t a1 = 128; // -A1(因為通常寫成 u[k] = ... + A1*u[k-1] + A2*u[k-2],這裡 a1 = 128 代表 A1 ≈ 1) -// int32_t a2 = 0; // -A2(PID 特例時 a2 常為 0) - -SystemState_t sys_state = STATE_STOP; -uint8_t is_zero_crossing = 0; - -uint8_t llc_running = 0; -uint8_t pfc_running = 0; -uint8_t is_brown_in = 0; // 0: 關機/保護中, 1: 正常工作 -static uint8_t cur_loop_fun_en = 0; - -static uint16_t vbus = 0; -static uint16_t ipcs = 0; -static uint16_t vcomp = 0; - -static int32_t vcr_x1_q14 = 0; // x[n-1] -static int32_t vcr_y1_q14 = 0; // y[n-1] -static int32_t cr_feed_forward_output_q14 = 0; // 最終輸出的 Q14 補償量 - -uint32_t count1; - -uint32_t vbus_ovp = (VBUS_OVP * 4096 / VBUS_2ADC / 3.3); -uint32_t vbus_uvp = (VBUS_UVP * 4096 / VBUS_2ADC / 3.3); - -uint32_t digi_vcomp_max = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -int32_t vbus_comp2 = 0; // 過零點用 -uint16_t llc_ss_cnt = 0; -//----------------------------------------------- - -uint8_t power_on_cmd = 0; -uint32_t vac_avg1; -uint32_t vbus_avg = 0; -uint32_t vcomp_avg; - -uint32_t static digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -uint16_t llc_period_ss = LLC_SS_MIN; -uint16_t llc_run_arr = LLC_PERIOD_MIN; -uint8_t v_loop_cnt = 0; -uint8_t v_loop_cnt2 = 0; - -uint8_t volatile adc_trigger = 0; - -// 全局或靜態變數(需在函數外或 static 保留狀態) -static int32_t iac_err_1 = 0; // e[k-1] -static int32_t iac_err_2 = 0; // e[k-2] -static int32_t iac_comp_1 = 0; // u[k-1] (前一次輸出) -static int32_t iac_comp_2 = 0; // u[k-2] (前兩次輸出) - -static int32_t vbus_err_1 = 0; -static int32_t vbus_comp_1 = 0; - -uint16_t updata_cnt = 0; - -int32_t vbus_comp_prev1, vbus_comp_prev2; -int32_t vbus_err_prev1, vbus_err_prev2; - -static volatile uint8_t capture_active = 0; -static volatile uint8_t capture_ready = 0; -static volatile uint16_t capture_idx = 0; -static CaptureSample_t capture_buf[CAPTURE_BUF_SIZE]; -uint8_t capture_continuous_mode = 0; -int16_t capture_package_cnt = 5; -uint16_t capture_packet_gap_ms = 1000; - -//============================================================================= -// Private Function Definition -//============================================================================= -void A_Para_Reset() -{ - digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; - // vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); - v_loop_cnt = 0; - - iac_err_1 = 0; - iac_err_2 = 0; - iac_comp_1 = 0; - iac_comp_2 = 0; - - // set default value - TIM2->ARR = PFC_PERIOD; - TIM2->CCR1 = PFC_DUTY_MIN; // min for ss - TIM2->EGR |= 0x01; - EPWM->ARR = LLC_PERIOD_SS; - EPWM->CCR1 = (LLC_PERIOD_SS >> 1); - EPWM->EGR |= 0x01; - llc_period_ss = 0; - - pfc_running = 0; - llc_running = 0; -} -//------------------------------------------------------------------------- -void capture_start(void) -{ - capture_idx = 0; - capture_ready = 0; - capture_active = 1; - msg("capture start,size=%d\r\n", CAPTURE_BUF_SIZE); -} -//------------------------------------------------------------------------- -static inline void Capture_Sample_ISR(void) -{ - if (!capture_active) - return; - - uint16_t idx = capture_idx; - if (idx >= CAPTURE_BUF_SIZE) - return; - - // capture_buf[idx].timestamp = sys_get_tick(); - // capture_buf[idx].iac = iac; - // capture_buf[idx].vac = vac; - // capture_buf[idx].vbus = vbus; - // capture_buf[idx].vcr = vcr; - - idx++; - capture_idx = idx; - - if (idx >= CAPTURE_BUF_SIZE) - { - capture_active = 0; - capture_ready = 1; - } -} -//------------------------------------------------------------------------- -static void Capture_Dump_IfReady(void) -{ - if (!capture_ready) - return; - - for (uint16_t i = 0; i < CAPTURE_BUF_SIZE; i++) - { - msg("%d\r\n", capture_buf[i].iac); - - // msg("%d,%d,%d,%d,%d\r\n", - // capture_buf[i].timestamp, - // capture_buf[i].iac, - // capture_buf[i].vac, - // capture_buf[i].vbus, - // capture_buf[i].vcr); - } - - capture_ready = 0; - msg("capture done,count=%d\r\n", CAPTURE_BUF_SIZE); -} -//------------------------------------------------------------------------- -void llc_set_period(uint16_t val) -{ - if (val > LLC_PERIOD_MAX) val = LLC_PERIOD_MAX; // 可以小,不能過大 - EPWM->ARR = val; - EPWM->CCR1 = val >> 1; -} -//=================================================================================== -void llc_pwm_enable() -{ - REG_SET_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); -} -//=================================================================================== -void llc_pwm_disable() -{ - REG_CLR_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); -} -//=================================================================================== -// Cr 1P1Z 回授,做負載驟變補償,執行頻率 10KHZ -// 抓取 vcr 的動態變化趨勢,並在穩態時回歸平靜 -void Load_Loop_1P1Z(void) -{ - int32_t x0; - int64_t temp_y0; // 使用 64 位元暫存,完全避免 32 位元相乘時溢位 - int32_t y0; - - // 1. 讀取當前 vcr - // x0 = vcr_avg; - - // 2. 執行 1P1Z 差分方程計算: - temp_y0 = ((int64_t)CR_1P1Z_B0_Q14 * x0) + ((int64_t)CR_1P1Z_B1_Q14 * vcr_x1_q14) - ((int64_t)CR_1P1Z_A1_Q14 * vcr_y1_q14); - - // 將 Q28 格式還原回 Q14 格式(右移 14 位元 - y0 = (int32_t)(temp_y0 >> 14); - - // 更新歷史狀態變數 (Q14 格式) - vcr_x1_q14 = x0; - vcr_y1_q14 = y0; - - // 定點數安全限幅 (Saturation) - if (y0 > CR_FF_MAX_Q14) - y0 = CR_FF_MAX_Q14; - else if (y0 > -CR_FF_MIN_Q14) - y0 = -CR_FF_MIN_Q14; - - cr_feed_forward_output_q14 = y0; -} -//========================================================================== -void Vol_Loop_PFC_1P1Z(void) -{ - // 1. 計算誤差 - int32_t vbus_err_n; // = (int32_t)vbus_set - (int32_t)vbus_avg; - - // --- 策略 A:動態增益調整 --- - int32_t b0 = 139; - int32_t b1 = -129; - - // vloop_p->b0 = 139; - // vloop_p->b1 = -129; - // vloop_p->a1 = -16383; - /* - if (vbus_err_n > 40 || vbus_err_n < -40) // ~ 20V - { - // 大誤差時(通常是剛啟動或大跳載),強化參數加快反應 - // b0 = 250; - // b1 = -249; - // b0 = 180; // 稍微調降強增益的力道 - // b1 = -178; - b0 = 500; - b1 = -499; - } - // --- 策略 B:接近目標時微調零點 (抗過衝) --- - else if (vbus_err_n < 20 && vbus_err_n > -20) // ~ 5V - { - // 當誤差縮小時,稍微減小 b0 與 b1 的差值,降低積分衝力 - b0 = 120; - b1 = -119; // 如果不能用浮點數,請維持 120 / -119,增加阻尼感 - } - */ - - // 2. 差分方程計算 - int64_t acc = (int64_t)b0 * vbus_err_n; - acc += (int64_t)b1 * vbus_err_1; - acc += ((int64_t)16383 * vbus_comp_1) >> 14; // 16384 = 1 >> 14 - - int32_t y_n = (int32_t)acc; - -#if 0 // 有問題 - // --- 策略 C:軟限制抗過衝 (Soft Clamping) --- - // 預設最大限制 (Duty 300 << 14) - int32_t current_max_limit = 4915200; // duty 300 - - // 如果電壓誤差已經很小(例如剩 15V 就到達),甚至已經發生過衝 (err < 0) - // 我們強制壓低 Duty 的允許上限,防止積分器帶著大 Duty 衝過頭 - if (vbus_err_n < 15) - { - // 這裡的 3276800 對應 Duty 200,您可以根據 800W 穩態時的 Duty 大約位置來設定 - // 核心目標是:不讓 Duty 在接近目標時還維持在 300 這麼高 - current_max_limit = 3276800; // duty 200 - } - - // 如果發生明顯過衝 (電壓高於目標 5V 以上) - if (vbus_err_n < -5) - { - current_max_limit = 1638400; // 強制壓低到 Duty 100 快速拉回 - } -#endif - - // 3. 輸出限幅執行 - if (y_n > YN_MAX) - y_n = YN_MAX; - else if (y_n < YN_MIN) - y_n = YN_MIN; - - // 4. 更新狀態變數 - vbus_err_1 = vbus_err_n; - vbus_comp_1 = y_n; - - // 5. 輸出 Duty >> 14 - // if (sys_state == STATE_PFC_SOFT_START || sys_state == STATE_RUN_SKIP) // 緩啟動期間直接控制 DUTY - TIM2->CCR1 = (vbus_comp_1 >> 14); // 0 ~ 540 - - // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 - // if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 - vbus_comp2 = (vbus_comp_1 >> 9) + (cr_feed_forward_output_q14 << 1); // 太大會直接 OVP - // vbus_comp2 = (vbus_comp_1 >> 9); -} -//============================================================= -static void Vol_Loop_PFC_1P1Z_UI(void) // ZERO: 120HZ, POLE:0.1HZ, G:10 -{ - // 1. 計算誤差 - int32_t vbus_err_0; // = (int32_t)vbus_set - (int32_t)vbus_avg; - - // 2. 2P2Z 運算 (Fixed-point Q14) - struct bode_plot_param_t *vloop_p = &pfc_vloop_bode_plot_param; - int64_t acc1 = (int64_t)vloop_p->b0 * vbus_err_0 + (int64_t)vloop_p->b1 * vbus_err_1; - - int64_t acc2 = (-((int64_t)vloop_p->a1 * vbus_comp_1)) >> 14; - - acc1 += acc2; - int32_t y_n = acc1; - - // 限制極限值 - if (y_n > YN_MAX) - y_n = YN_MAX; // 540 << 14 - else if (y_n < YN_MIN) - y_n = YN_MIN; // 12 << 14 - - // 更新狀態變數 (儲存原始計算值以保留積分能量) - vbus_err_1 = vbus_err_0; - - // 重要:vbus_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 - vbus_comp_1 = y_n; - - // 5. 輸出 Duty >> 14 - // if (sys_state == STATE_PFC_SOFT_START || sys_state == STATE_RUN_SKIP) // 緩啟動期間直接控制 DUTY - TIM2->CCR1 = (vbus_comp_1 >> 14); // 0 ~ 540 - - // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 - // if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 - vbus_comp2 = (vbus_comp_1 >> 9); -} - -//======================================================================== -__attribute__((always_inline)) static inline void Cur_Loop_PFC_2P2Z(void) // 優化後 3.65us -{ - // if (sys_state != STATE_RUN_NORMAL) - return; - - // if (is_zero_crossing == 1) return; // 凍結積分,改善過零後上升緣的突波 - - // 1. 取得回授與計算誤差 - int32_t local_vbus_comp2 = vbus_comp2; - int32_t local_vac_avg1 = vac_avg1; - - // 電流採樣處理 - int32_t local_iac; // = 3840 - iac; // OPA 電路的關係,實際值為 CURR_OFFSET - ADC - if (local_iac < 0) - local_iac = 0; // 避免負值 - - // 計算電流給定值 - int32_t local_iac_set = (local_vac_avg1 * local_vbus_comp2) >> 12; // >> 12 = / 4096 - int32_t local_iac_err_0 = local_iac_set - local_iac; // err = set - meas - - // 2. 2P2Z 運算 (Fixed-point Q14) -#if 1 - struct bode_plot_param_t *cloop_p = &pfc_cloop_bode_plot_param; - - int64_t acc1 = ((int64_t)cloop_p->b0 * local_iac_err_0) + ((int64_t)cloop_p->b1 * iac_err_1) + ((int64_t)cloop_p->b2 * iac_err_2); - - int64_t acc2 = -((int64_t)cloop_p->a1 * iac_comp_1); - acc2 -= ((int64_t)cloop_p->a2 * iac_comp_2); -#endif - - acc1 += acc2; - int32_t y_n = acc1 >> 14; - - // 限制極限值 - if (y_n > YN_MAX) - y_n = YN_MAX; // 570 << 14 - else if (y_n < YN_MIN) - y_n = YN_MIN; // 12 << 14 - - // 更新狀態變數 (儲存原始計算值以保留積分能量) - iac_err_2 = iac_err_1; - iac_err_1 = local_iac_err_0; - - // 重要:iac_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 - iac_comp_2 = iac_comp_1; - iac_comp_1 = y_n; - - uint16_t local_duty = 0; - - int32_t ff_term; - ff_term = (2800 - vac_avg1) * PFC_DUTY_MAX / 2800 - 200; - - if (ff_term < 0) ff_term = 0; - if (vbus_comp2 >= 1000) - local_duty = (uint16_t)((y_n >> 14) + ff_term); - else - local_duty = (uint16_t)(y_n >> 14); - - // local_duty = (uint16_t)(y_n >> 14); - // 4. 硬體 Duty 限幅與輸出 - if (local_duty > PFC_DUTY_MAX) - local_duty = PFC_DUTY_MAX; // MAX = 540 - else if (local_duty < PFC_DUTY_MIN) - local_duty = PFC_DUTY_MIN; // MIN = 12 - TIM2->CCR1 = local_duty; -} -//============================================================================ - -void Vol_Loop_LLC(void) // 測試可工作 -{ - static uint8_t last_gpio = 0; - static uint16_t toggle_cnt = 0; - static uint16_t stable_timer = 0; - - // 1. 讀取 GPIO 回授 - uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); - - // 2. 判斷是否發生翻轉 (判斷是否在平衡點附近跳動) - if (gpio != last_gpio) - { - toggle_cnt++; - last_gpio = gpio; - stable_timer = 0; // 只要有翻轉,就重置穩定計時器 - } - else - { - // 如果 GPIO 長時間沒翻轉,說明離目標還很遠 - if (stable_timer < 1000) stable_timer++; - } - - // 3. 根據翻轉頻率決定步長 - uint16_t current_step; - if (toggle_cnt > STABLE_THRES) - { - // 頻繁翻轉:進入「死區」或「極小步長」模式 - current_step = 0; // 設為 0 表示進入死區,完全停止震盪 - - // 為了防止永久卡死,當連續相同狀態一段時間後再恢復調整 - if (stable_timer > 50) toggle_cnt = 0; - } - else - { - // 距離目標尚遠:根據 stable_timer 決定快慢 - current_step = (stable_timer > 100) ? STEP_FAST : STEP_SLOW; - } - - // 4. 執行調整 - if (current_step > 0) - { - if (gpio == 1) - { - if (digital_vcomp + current_step <= digi_vcomp_max) - digital_vcomp += current_step; - } - else - { - if (digital_vcomp > (1000 + current_step)) - digital_vcomp -= current_step; - } - } - - // 5. 計算並更新 ARR (原本的邏輯) - uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); - - if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; - if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; - - if (EPWM->ARR != new_arr) - { - EPWM->ARR = new_arr; - EPWM->CCR1 = (new_arr >> 1); - } -} -//================================================================================= -__INTERRUPT void isr_adc_handle(void) -{ - SAVE_IRQ_CSR_CONTEXT(); - ADC_ClearITFlag(ADC0); - - adc_trigger = 1; - - // get ADC value - vbus = ADC0->DAT4_b.DATA; // PA10, AIN4 - ipcs = ADC0->DAT9_b.DATA; // PA7, AIN9 - vcomp = ADC0->DAT7_b.DATA; // PA13, AIN7 - - // if (cur_loop_fun_en) Cur_Loop_PFC_2P2Z(); // current loop - /* - static uint8_t cnt = 0; - if (cnt++ >= 10) - { - Capture_Sample_ISR(); - cnt = 0; - } - */ - - RESTORE_IRQ_CSR_CONTEXT(); -} -//============================================================================ -void reset_pid_parameters(void) -{ - iac_err_1 = 0; - iac_err_2 = 0; - iac_comp_1 = 0; - iac_comp_2 = 0; - - digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; -} -//================================================================================ - -static void uart_rx_task(void) -{ - // RX timeout - if (g_rx_idx > 0 && !g_rx_ready) - { - if (sys_get_tick() - g_rx_last_tick >= UART_RX_TIMEOUT_MS) - { - UART_ResetRxFIFO(UART0); - g_rx_idx = 0; - g_rx_len = 0; - g_rx_ready = 0; - } - } - - if (!is_rx_frame_ready()) - return; - - char cmd_buf[UART_RX_BUF_SIZE]; - - uart_rx_getline(cmd_buf, sizeof(cmd_buf)); - msg("RX: %s\r\n", cmd_buf); - uart_cmd_parse(cmd_buf, sizeof(cmd_buf)); -} - -static void capture_continuous_task(void) -{ - static uint32_t msg_timer = 0; - - if (capture_continuous_mode == 1 && capture_package_cnt > 0) - { - if (sys_get_tick() - msg_timer >= capture_packet_gap_ms) - { - capture_start(); - msg_timer = sys_get_tick(); - if (capture_package_cnt-- <= 0) - { - capture_continuous_mode = 0; - capture_package_cnt = 0; - } - } - } -} -//============================================================================== -void Handle_UART_Monitor(void) -{ - static uint32_t msg_timer2 = 0; - if (sys_get_tick() - msg_timer2 >= 1000) - { - printf("%d %d %d", power_on_cmd, sys_state, vbus_comp2); - // msg_timer2 = sys_get_tick(); - } -} -//============================================================================= -// Public Function Definition -//============================================================================= -int main(void) -{ - struct bode_plot_param_t *cloop_p = &pfc_cloop_bode_plot_param; - struct bode_plot_param_t *vloop_p = &pfc_vloop_bode_plot_param; - - // system initial configuration - SYS_Config(); - sys_delay(10); // delay 10ms - - // read plot param from flash - msg("read plot param from flash\r\n"); - read_bodeplot_params_from_flash(cloop_p, vloop_p, FLASH_PROG_ADDR_USER_CALI); - print_bodeplot_params(cloop_p, vloop_p); - - // main loop =============================================================================== - - /* PFC 電流環參數*/ - // Z1=200, Z2=100000, P1=10, P2=20000, G=8000 - cloop_p->b0 = 3353681; - cloop_p->b1 = -1577632; - cloop_p->b2 = -1712512; - cloop_p->a1 = -20114; - cloop_p->a2 = 3737; - - cur_loop_fun_en = 1; // 開啟電流環,專測電流環 - while (1) - { - uart_rx_task(); - - // 核心觸發:100kHz ~ 200KHz (由 EPWM 旗標觸發) - if (adc_trigger == 1) - { - adc_trigger = 0; - - // 高速信號濾波 (簡易位移算法減少算力消耗) --- - vbus_avg = (vbus_avg - (vbus_avg >> 3)) + (vbus >> 3); - vcomp_avg = (vcomp_avg - (vcomp_avg >> 3)) + (vcomp >> 3); - - // vbus ovp, uvp, 超出工作電壓範圍就不工作 - if (vbus_avg > vbus_ovp || vbus_avg < vbus_uvp) - { - sys_state = STATE_STOP; - } - else - { - if (sys_state == STATE_STOP) sys_state = STATE_SS; - } - -#if 1 - // --- 2. 核心狀態機邏輯 ----------------------------------------------------------------------- - switch (sys_state) - { - case STATE_STOP: // 0 - llc_pwm_disable(); // 關閉 PWM - A_Para_Reset(); // 重置參數 - break; - - case STATE_SS: // 1 - // LLC 軟啟動:頻率由高往低掃描 (掃向 1倍諧振頻率) - llc_period_ss++; - llc_ss_cnt++; - llc_set_period(llc_period_ss >> 2); // 放大 4 倍,要除 4 回來 - - if (llc_running == 0) - { - llc_pwm_enable(); - llc_running = 1; - } - - if (llc_ss_cnt >= LLC_SS_COUNT) - { - sys_state = STATE_RUN; - // sys_state = STATE_PAUSE; // 測試用 - // 重要動作 ===================================== - // vbus_comp_1 = YN_MIN; // 假設從一個極小的係數開始 - // vbus_comp2 = (vbus_comp_1 >> 9); - // vbus_err_1 = 0; // 清除誤差歷史 - // iac_err_2 = 0; - // iac_err_1 = 0; - // iac_comp_2 = YN_MIN; - // iac_comp_1 = YN_MIN; - // msg("%d %d %d %d \r\n", power_on_cmd, sys_state, vbus_comp_1, vbus_comp2); - // 1 4 196608, = 12 << 14 - // E LOAD 抽太慢,導致 OVP ?? (是的) - } - break; - - case STATE_RUN: // 2 - // 根據電壓環、電流環調整頻率 - /* - if (++v_loop_cnt >= 10) // 10KHZ - { - Vol_Loop_PFC_1P1Z(); - // Vol_Loop_PFC_1P1Z(); - v_loop_cnt = 0; - } - */ - - break; - } // switch case -#endif - - } // if tim0_trigger - - // --- 3. 非即時任務 (UI、通訊、Data Log) --- - Handle_UART_Monitor(); - // Capture_Dump_IfReady(); - - // capture_continuous_task(); - } // while -------------------------------------------------------------------- - - return 0; -} - -#if 0 -#include "app.h" - -int main(void) -{ - app(); - - return 0; -} -#endif + +#include "main.h" +#include "app.h" +#include "init.h" +//============================================================================= +// Constant Definition +//============================================================================= +#define CAPTURE_BUF_SIZE 160 + +#define DEBOUNCE_TIME 20 + +#define CR_1P1Z_B0_Q14 6313 +#define CR_1P1Z_B1_Q14 -5113 +#define CR_1P1Z_A1_Q14 -5803 +// 前饋輸出限制(請根據您主環 Q14 格式的數值範圍調整) +// 假設最大前饋量限制為實數值 50.0 -> 50.0 * 16384 = 819200 +#define CR_FF_MAX_Q14 1638400 // 819200 // 50 duty << 14 +#define CR_FF_MIN_Q14 -1638400 //-819200 + +//============================================================================= +// Macro Definition +//============================================================================= + +//============================================================================= +// Structure Definition +//============================================================================= + +typedef enum +{ + STATE_STOP, + STATE_SS, + STATE_RUN +} SystemState_t; + +typedef struct +{ + // uint32_t timestamp; + uint16_t iac; + // uint16_t vac; + // uint16_t vbus; + // uint16_t vcr; +} CaptureSample_t; + +//============================================================================= +// Global Data Definition +//============================================================================= + +/* PID 轉換 2P2Z係數*/ +// #define kp 250 +// #define ki 2 +// #define kd 0 + +// 2P2Z 係數(這些需要根據你的 PID 增益與控制頻率重新計算或調試) +// int32_t b0 = kp + ki + kd; // 對應比例 + 部分積分/微分貢獻(可調整) +// int32_t b1 = -kp - kd - kd; // e[k-1] 係數(通常為正或負) +// int32_t b2 = kd; // e[k-2] 係數 +// int32_t a1 = 128; // -A1(因為通常寫成 u[k] = ... + A1*u[k-1] + A2*u[k-2],這裡 a1 = 128 代表 A1 ≈ 1) +// int32_t a2 = 0; // -A2(PID 特例時 a2 常為 0) + +SystemState_t sys_state = STATE_STOP; +uint8_t is_zero_crossing = 0; + +uint8_t llc_running = 0; +uint8_t pfc_running = 0; +uint8_t is_brown_in = 0; // 0: 關機/保護中, 1: 正常工作 +static uint8_t cur_loop_fun_en = 0; + +static uint16_t vbus = 0; +static uint16_t ipcs = 0; +static uint16_t vcomp = 0; + +uint32_t count1; + +uint32_t vbus_ovp = (VBUS_OVP * 4096 / VBUS_2ADC / 3.3); +uint32_t vbus_uvp = (VBUS_UVP * 4096 / VBUS_2ADC / 3.3); + +uint32_t digi_vcomp_max = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +int32_t vbus_comp2 = 0; // 過零點用 +uint16_t llc_ss_cnt = 0; +//----------------------------------------------- + +uint8_t power_on_cmd = 0; +uint32_t vac_avg1; +uint32_t vbus_avg = 0; +uint32_t vcomp_avg; + +uint32_t static digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +uint16_t llc_period_ss = LLC_SS_MIN; +uint16_t llc_run_arr = LLC_PERIOD_MIN; +uint8_t v_loop_cnt = 0; +uint8_t v_loop_cnt2 = 0; + +uint8_t volatile adc_trigger = 0; + +// 全局或靜態變數(需在函數外或 static 保留狀態) +static int32_t iac_err_1 = 0; // e[k-1] +static int32_t iac_err_2 = 0; // e[k-2] +static int32_t iac_comp_1 = 0; // u[k-1] (前一次輸出) +static int32_t iac_comp_2 = 0; // u[k-2] (前兩次輸出) + +#if 0 +static int32_t vbus_err_1 = 0; +static int32_t vbus_comp_1 = 0; +#endif + +uint16_t updata_cnt = 0; + +int32_t vbus_comp_prev1, vbus_comp_prev2; +int32_t vbus_err_prev1, vbus_err_prev2; + +static volatile uint8_t capture_active = 0; +static volatile uint8_t capture_ready = 0; +static volatile uint16_t capture_idx = 0; +static CaptureSample_t capture_buf[CAPTURE_BUF_SIZE]; +uint8_t capture_continuous_mode = 0; +int16_t capture_package_cnt = 5; +uint16_t capture_packet_gap_ms = 1000; + +//============================================================================= +// Private Function Definition +//============================================================================= +void A_Para_Reset() +{ + digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; + // vbus_set = (VBUS_SET * 4096 / VBUS_2ADC / 3.3); + v_loop_cnt = 0; + + iac_err_1 = 0; + iac_err_2 = 0; + iac_comp_1 = 0; + iac_comp_2 = 0; + + // set default value + TIM2->ARR = PFC_PERIOD; + TIM2->CCR1 = PFC_DUTY_MIN; // min for ss + TIM2->EGR |= 0x01; + EPWM->ARR = LLC_PERIOD_SS; + EPWM->CCR1 = (LLC_PERIOD_SS >> 1); + EPWM->EGR |= 0x01; + llc_period_ss = 0; + + pfc_running = 0; + llc_running = 0; +} +//------------------------------------------------------------------------- +void capture_start(void) +{ + capture_idx = 0; + capture_ready = 0; + capture_active = 1; + msg("capture start,size=%d\r\n", CAPTURE_BUF_SIZE); +} +//------------------------------------------------------------------------- +static inline void Capture_Sample_ISR(void) +{ + if (!capture_active) + return; + + uint16_t idx = capture_idx; + if (idx >= CAPTURE_BUF_SIZE) + return; + + // capture_buf[idx].timestamp = sys_get_tick(); + // capture_buf[idx].iac = iac; + // capture_buf[idx].vac = vac; + // capture_buf[idx].vbus = vbus; + // capture_buf[idx].vcr = vcr; + + idx++; + capture_idx = idx; + + if (idx >= CAPTURE_BUF_SIZE) + { + capture_active = 0; + capture_ready = 1; + } +} +//------------------------------------------------------------------------- +static void Capture_Dump_IfReady(void) +{ + if (!capture_ready) + return; + + for (uint16_t i = 0; i < CAPTURE_BUF_SIZE; i++) + { + msg("%d\r\n", capture_buf[i].iac); + + // msg("%d,%d,%d,%d,%d\r\n", + // capture_buf[i].timestamp, + // capture_buf[i].iac, + // capture_buf[i].vac, + // capture_buf[i].vbus, + // capture_buf[i].vcr); + } + + capture_ready = 0; + msg("capture done,count=%d\r\n", CAPTURE_BUF_SIZE); +} +//------------------------------------------------------------------------- +void llc_set_period(uint16_t val) +{ + if (val > LLC_PERIOD_MAX) val = LLC_PERIOD_MAX; // 可以小,不能過大 + EPWM->ARR = val; + EPWM->CCR1 = val >> 1; +} +//=================================================================================== +void llc_pwm_enable() +{ + REG_SET_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); +} +//=================================================================================== +void llc_pwm_disable() +{ + REG_CLR_BITS(EPWM->CCER, TIM_CCER_CC1E_Msk | TIM_CCER_CC1NE_Msk); +} +//=================================================================================== +// Cr 1P1Z 回授,做負載驟變補償,執行頻率 10KHZ +// 抓取 vcr 的動態變化趨勢,並在穩態時回歸平靜 +#if 0 +void Load_Loop_1P1Z(void) +{ + int32_t x0; + int64_t temp_y0; // 使用 64 位元暫存,完全避免 32 位元相乘時溢位 + int32_t y0; + + // 1. 讀取當前 vcr + // x0 = vcr_avg; + + // 2. 執行 1P1Z 差分方程計算: + temp_y0 = ((int64_t)CR_1P1Z_B0_Q14 * x0) + ((int64_t)CR_1P1Z_B1_Q14 * vcr_x1_q14) - ((int64_t)CR_1P1Z_A1_Q14 * vcr_y1_q14); + + // 將 Q28 格式還原回 Q14 格式(右移 14 位元 + y0 = (int32_t)(temp_y0 >> 14); + + // 更新歷史狀態變數 (Q14 格式) + vcr_x1_q14 = x0; + vcr_y1_q14 = y0; + + // 定點數安全限幅 (Saturation) + if (y0 > CR_FF_MAX_Q14) + y0 = CR_FF_MAX_Q14; + else if (y0 > -CR_FF_MIN_Q14) + y0 = -CR_FF_MIN_Q14; + + cr_feed_forward_output_q14 = y0; +} +#endif +//========================================================================== +#if 0 +void Vol_Loop_PFC_1P1Z(void) +{ + // 1. 計算誤差 + int32_t vbus_err_n; // = (int32_t)vbus_set - (int32_t)vbus_avg; + + // --- 策略 A:動態增益調整 --- + int32_t b0 = 139; + int32_t b1 = -129; + + // vloop_p->b0 = 139; + // vloop_p->b1 = -129; + // vloop_p->a1 = -16383; + /* + if (vbus_err_n > 40 || vbus_err_n < -40) // ~ 20V + { + // 大誤差時(通常是剛啟動或大跳載),強化參數加快反應 + // b0 = 250; + // b1 = -249; + // b0 = 180; // 稍微調降強增益的力道 + // b1 = -178; + b0 = 500; + b1 = -499; + } + // --- 策略 B:接近目標時微調零點 (抗過衝) --- + else if (vbus_err_n < 20 && vbus_err_n > -20) // ~ 5V + { + // 當誤差縮小時,稍微減小 b0 與 b1 的差值,降低積分衝力 + b0 = 120; + b1 = -119; // 如果不能用浮點數,請維持 120 / -119,增加阻尼感 + } + */ + + // 2. 差分方程計算 + int64_t acc = (int64_t)b0 * vbus_err_n; + acc += (int64_t)b1 * vbus_err_1; + acc += ((int64_t)16383 * vbus_comp_1) >> 14; // 16384 = 1 >> 14 + + int32_t y_n = (int32_t)acc; + +#if 0 // 有問題 + // --- 策略 C:軟限制抗過衝 (Soft Clamping) --- + // 預設最大限制 (Duty 300 << 14) + int32_t current_max_limit = 4915200; // duty 300 + + // 如果電壓誤差已經很小(例如剩 15V 就到達),甚至已經發生過衝 (err < 0) + // 我們強制壓低 Duty 的允許上限,防止積分器帶著大 Duty 衝過頭 + if (vbus_err_n < 15) + { + // 這裡的 3276800 對應 Duty 200,您可以根據 800W 穩態時的 Duty 大約位置來設定 + // 核心目標是:不讓 Duty 在接近目標時還維持在 300 這麼高 + current_max_limit = 3276800; // duty 200 + } + + // 如果發生明顯過衝 (電壓高於目標 5V 以上) + if (vbus_err_n < -5) + { + current_max_limit = 1638400; // 強制壓低到 Duty 100 快速拉回 + } +#endif + + // 3. 輸出限幅執行 + if (y_n > YN_MAX) + y_n = YN_MAX; + else if (y_n < YN_MIN) + y_n = YN_MIN; + + // 4. 更新狀態變數 + vbus_err_1 = vbus_err_n; + vbus_comp_1 = y_n; + + // 5. 輸出 Duty >> 14 + // if (sys_state == STATE_PFC_SOFT_START || sys_state == STATE_RUN_SKIP) // 緩啟動期間直接控制 DUTY + TIM2->CCR1 = (vbus_comp_1 >> 14); // 0 ~ 540 + + // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 + // if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 + vbus_comp2 = (vbus_comp_1 >> 9) + (cr_feed_forward_output_q14 << 1); // 太大會直接 OVP + // vbus_comp2 = (vbus_comp_1 >> 9); +} +#endif +//============================================================= +#if 0 +static void Vol_Loop_PFC_1P1Z_UI(void) // ZERO: 120HZ, POLE:0.1HZ, G:10 +{ + // 1. 計算誤差 + int32_t vbus_err_0; // = (int32_t)vbus_set - (int32_t)vbus_avg; + + // 2. 2P2Z 運算 (Fixed-point Q14) + struct bode_plot_param_t *vloop_p = &pfc_vloop_bode_plot_param; + int64_t acc1 = (int64_t)vloop_p->b0 * vbus_err_0 + (int64_t)vloop_p->b1 * vbus_err_1; + + int64_t acc2 = (-((int64_t)vloop_p->a1 * vbus_comp_1)) >> 14; + + acc1 += acc2; + int32_t y_n = acc1; + + // 限制極限值 + if (y_n > YN_MAX) + y_n = YN_MAX; // 540 << 14 + else if (y_n < YN_MIN) + y_n = YN_MIN; // 12 << 14 + + // 更新狀態變數 (儲存原始計算值以保留積分能量) + vbus_err_1 = vbus_err_0; + + // 重要:vbus_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 + vbus_comp_1 = y_n; + + // 5. 輸出 Duty >> 14 + // if (sys_state == STATE_PFC_SOFT_START || sys_state == STATE_RUN_SKIP) // 緩啟動期間直接控制 DUTY + TIM2->CCR1 = (vbus_comp_1 >> 14); // 0 ~ 540 + + // 6. 輸出 comp >> 9 (變大 32 倍) (MAX ~ 18000) (540*32=17280)差不多,>> 9 應該是對的 + // if (sys_state == STATE_RUN_NORMAL) // 正常工作時間輸出 COMP 值 + vbus_comp2 = (vbus_comp_1 >> 9); +} +#endif + +//======================================================================== +__attribute__((always_inline)) static inline void Cur_Loop_PFC_2P2Z(void) // 優化後 3.65us +{ + // if (sys_state != STATE_RUN_NORMAL) + return; + + // if (is_zero_crossing == 1) return; // 凍結積分,改善過零後上升緣的突波 + + // 1. 取得回授與計算誤差 + int32_t local_vbus_comp2 = vbus_comp2; + int32_t local_vac_avg1 = vac_avg1; + + // 電流採樣處理 + int32_t local_iac; // = 3840 - iac; // OPA 電路的關係,實際值為 CURR_OFFSET - ADC + if (local_iac < 0) + local_iac = 0; // 避免負值 + + // 計算電流給定值 + int32_t local_iac_set = (local_vac_avg1 * local_vbus_comp2) >> 12; // >> 12 = / 4096 + int32_t local_iac_err_0 = local_iac_set - local_iac; // err = set - meas + + // 2. 2P2Z 運算 (Fixed-point Q14) +#if 1 + struct bode_plot_param_t *cloop_p = &pfc_cloop_bode_plot_param; + + int64_t acc1 = ((int64_t)cloop_p->b0 * local_iac_err_0) + ((int64_t)cloop_p->b1 * iac_err_1) + ((int64_t)cloop_p->b2 * iac_err_2); + + int64_t acc2 = -((int64_t)cloop_p->a1 * iac_comp_1); + acc2 -= ((int64_t)cloop_p->a2 * iac_comp_2); +#endif + + acc1 += acc2; + int32_t y_n = acc1 >> 14; + + // 限制極限值 + if (y_n > YN_MAX) + y_n = YN_MAX; // 570 << 14 + else if (y_n < YN_MIN) + y_n = YN_MIN; // 12 << 14 + + // 更新狀態變數 (儲存原始計算值以保留積分能量) + iac_err_2 = iac_err_1; + iac_err_1 = local_iac_err_0; + + // 重要:iac_comp_1 儲存 local_temp,確保下一個週期的 a1, a2 運算正確 + iac_comp_2 = iac_comp_1; + iac_comp_1 = y_n; + + uint16_t local_duty = 0; + + int32_t ff_term; + ff_term = (2800 - vac_avg1) * PFC_DUTY_MAX / 2800 - 200; + + if (ff_term < 0) ff_term = 0; + if (vbus_comp2 >= 1000) + local_duty = (uint16_t)((y_n >> 14) + ff_term); + else + local_duty = (uint16_t)(y_n >> 14); + + // local_duty = (uint16_t)(y_n >> 14); + // 4. 硬體 Duty 限幅與輸出 + if (local_duty > PFC_DUTY_MAX) + local_duty = PFC_DUTY_MAX; // MAX = 540 + else if (local_duty < PFC_DUTY_MIN) + local_duty = PFC_DUTY_MIN; // MIN = 12 + TIM2->CCR1 = local_duty; +} +//============================================================================ + +void Vol_Loop_LLC(void) // 測試可工作 +{ + static uint8_t last_gpio = 0; + static uint16_t toggle_cnt = 0; + static uint16_t stable_timer = 0; + + // 1. 讀取 GPIO 回授 + uint8_t gpio = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12); + + // 2. 判斷是否發生翻轉 (判斷是否在平衡點附近跳動) + if (gpio != last_gpio) + { + toggle_cnt++; + last_gpio = gpio; + stable_timer = 0; // 只要有翻轉,就重置穩定計時器 + } + else + { + // 如果 GPIO 長時間沒翻轉,說明離目標還很遠 + if (stable_timer < 1000) stable_timer++; + } + + // 3. 根據翻轉頻率決定步長 + uint16_t current_step; + if (toggle_cnt > STABLE_THRES) + { + // 頻繁翻轉:進入「死區」或「極小步長」模式 + current_step = 0; // 設為 0 表示進入死區,完全停止震盪 + + // 為了防止永久卡死,當連續相同狀態一段時間後再恢復調整 + if (stable_timer > 50) toggle_cnt = 0; + } + else + { + // 距離目標尚遠:根據 stable_timer 決定快慢 + current_step = (stable_timer > 100) ? STEP_FAST : STEP_SLOW; + } + + // 4. 執行調整 + if (current_step > 0) + { + if (gpio == 1) + { + if (digital_vcomp + current_step <= digi_vcomp_max) + digital_vcomp += current_step; + } + else + { + if (digital_vcomp > (1000 + current_step)) + digital_vcomp -= current_step; + } + } + + // 5. 計算並更新 ARR (原本的邏輯) + uint32_t new_arr = LLC_PERIOD_MIN + (digital_vcomp >> LLC_COMP_GAIN_BIT); + + if (new_arr > LLC_PERIOD_MAX) new_arr = LLC_PERIOD_MAX; + if (new_arr < LLC_PERIOD_MIN) new_arr = LLC_PERIOD_MIN; + + if (EPWM->ARR != new_arr) + { + EPWM->ARR = new_arr; + EPWM->CCR1 = (new_arr >> 1); + } +} +//================================================================================= +__INTERRUPT void isr_adc_handle(void) +{ + SAVE_IRQ_CSR_CONTEXT(); + ADC_ClearITFlag(ADC0); + + adc_trigger = 1; + + // get ADC value + vbus = ADC0->DAT4_b.DATA; // PA10, AIN4 + ipcs = ADC0->DAT9_b.DATA; // PA7, AIN9 + vcomp = ADC0->DAT7_b.DATA; // PA13, AIN7 + + // if (cur_loop_fun_en) Cur_Loop_PFC_2P2Z(); // current loop + /* + static uint8_t cnt = 0; + if (cnt++ >= 10) + { + Capture_Sample_ISR(); + cnt = 0; + } + */ + + RESTORE_IRQ_CSR_CONTEXT(); +} +//============================================================================ +void reset_pid_parameters(void) +{ + iac_err_1 = 0; + iac_err_2 = 0; + iac_comp_1 = 0; + iac_comp_2 = 0; + + digital_vcomp = (LLC_PERIOD_MAX - LLC_PERIOD_MIN) << LLC_COMP_GAIN_BIT; +} +//================================================================================ + +static void uart_rx_task(void) +{ + // RX timeout + if (g_rx_idx > 0 && !g_rx_ready) + { + if (sys_get_tick() - g_rx_last_tick >= UART_RX_TIMEOUT_MS) + { + UART_ResetRxFIFO(UART0); + g_rx_idx = 0; + g_rx_len = 0; + g_rx_ready = 0; + } + } + + if (!is_rx_frame_ready()) + return; + + char cmd_buf[UART_RX_BUF_SIZE]; + + uart_rx_getline(cmd_buf, sizeof(cmd_buf)); + msg("RX: %s\r\n", cmd_buf); + uart_cmd_parse(cmd_buf, sizeof(cmd_buf)); +} + +static void capture_continuous_task(void) +{ + static uint32_t msg_timer = 0; + + if (capture_continuous_mode == 1 && capture_package_cnt > 0) + { + if (sys_get_tick() - msg_timer >= capture_packet_gap_ms) + { + capture_start(); + msg_timer = sys_get_tick(); + if (capture_package_cnt-- <= 0) + { + capture_continuous_mode = 0; + capture_package_cnt = 0; + } + } + } +} +//============================================================================== +void Handle_UART_Monitor(void) +{ + static uint32_t msg_timer2 = 0; + if (sys_get_tick() - msg_timer2 >= 1000) + { + printf("%d %d %d", power_on_cmd, sys_state, vbus_comp2); + // msg_timer2 = sys_get_tick(); + } +} +//============================================================================= +// Public Function Definition +//============================================================================= +int main(void) +{ + struct bode_plot_param_t *cloop_p = &pfc_cloop_bode_plot_param; + struct bode_plot_param_t *vloop_p = &pfc_vloop_bode_plot_param; + + // system initial configuration + SYS_Config(); + sys_delay(10); // delay 10ms + + // read plot param from flash + msg("read plot param from flash\r\n"); + read_bodeplot_params_from_flash(cloop_p, vloop_p, FLASH_PROG_ADDR_USER_CALI); + print_bodeplot_params(cloop_p, vloop_p); + + // main loop =============================================================================== + + /* PFC 電流環參數*/ + // Z1=200, Z2=100000, P1=10, P2=20000, G=8000 + cloop_p->b0 = 3353681; + cloop_p->b1 = -1577632; + cloop_p->b2 = -1712512; + cloop_p->a1 = -20114; + cloop_p->a2 = 3737; + + cur_loop_fun_en = 1; // 開啟電流環,專測電流環 + while (1) + { + uart_rx_task(); + + // 核心觸發:100kHz ~ 200KHz (由 EPWM 旗標觸發) + if (adc_trigger == 1) + { + adc_trigger = 0; + + // 高速信號濾波 (簡易位移算法減少算力消耗) --- + vbus_avg = (vbus_avg - (vbus_avg >> 3)) + (vbus >> 3); + vcomp_avg = (vcomp_avg - (vcomp_avg >> 3)) + (vcomp >> 3); + + // vbus ovp, uvp, 超出工作電壓範圍就不工作 + if (vbus_avg > vbus_ovp || vbus_avg < vbus_uvp) + { + sys_state = STATE_STOP; + } + else + { + if (sys_state == STATE_STOP) sys_state = STATE_SS; + } + +#if 1 + // --- 2. 核心狀態機邏輯 ----------------------------------------------------------------------- + switch (sys_state) + { + case STATE_STOP: // 0 + llc_pwm_disable(); // 關閉 PWM + A_Para_Reset(); // 重置參數 + break; + + case STATE_SS: // 1 + // LLC 軟啟動:頻率由高往低掃描 (掃向 1倍諧振頻率) + llc_period_ss++; + llc_ss_cnt++; + llc_set_period(llc_period_ss >> 2); // 放大 4 倍,要除 4 回來 + + if (llc_running == 0) + { + llc_pwm_enable(); + llc_running = 1; + } + + if (llc_ss_cnt >= LLC_SS_COUNT) + { + sys_state = STATE_RUN; + // sys_state = STATE_PAUSE; // 測試用 + // 重要動作 ===================================== + // vbus_comp_1 = YN_MIN; // 假設從一個極小的係數開始 + // vbus_comp2 = (vbus_comp_1 >> 9); + // vbus_err_1 = 0; // 清除誤差歷史 + // iac_err_2 = 0; + // iac_err_1 = 0; + // iac_comp_2 = YN_MIN; + // iac_comp_1 = YN_MIN; + // msg("%d %d %d %d \r\n", power_on_cmd, sys_state, vbus_comp_1, vbus_comp2); + // 1 4 196608, = 12 << 14 + // E LOAD 抽太慢,導致 OVP ?? (是的) + } + break; + + case STATE_RUN: // 2 + // 根據電壓環、電流環調整頻率 + /* + if (++v_loop_cnt >= 10) // 10KHZ + { + Vol_Loop_PFC_1P1Z(); + // Vol_Loop_PFC_1P1Z(); + v_loop_cnt = 0; + } + */ + + break; + } // switch case +#endif + + } // if tim0_trigger + + // --- 3. 非即時任務 (UI、通訊、Data Log) --- + Handle_UART_Monitor(); + // Capture_Dump_IfReady(); + + // capture_continuous_task(); + } // while -------------------------------------------------------------------- + + return 0; +} + +#if 0 +#include "app.h" + +int main(void) +{ + app(); + + return 0; +} +#endif