FreeCalypso > hg > fc-tourmaline
view src/cs/drivers/drv_app/fchg/fchg_process.c @ 252:27f17fbf0657
FCHG: add dependency on ETM for BSIM
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 12 May 2021 03:18:50 +0000 |
parents | 305d3bb691a5 |
children | 769cf6273fe4 |
line wrap: on
line source
/* * In this module we are going to implement the main process functions * for FCHG. */ #include "fchg/fchg_env.h" #include "fchg/fchg_func_i.h" #include "rv/rv_general.h" #include "rvf/rvf_api.h" #include "rvm/rvm_use_id_list.h" #include "abb/abb.h" #include "fc-target.h" #include <string.h> #include <stdio.h> extern UINT16 madc_vbat_2_physical(UINT16 adc_val); extern UINT16 madc_vbat_inverse(UINT16 mv); #if defined(CONFIG_TARGET_C155) || defined(CONFIG_TARGET_J100) #define LEDC 0x20 #else #define LEDC 0 #endif /* * The following global variable captures the initial battery voltage * at the time of system boot - it is intended to be used for various * system start-up decisions. */ UINT16 fchg_first_vbat; void pwr_init_discharge(void) { pwr_ctrl->curr_disch_thresh = 0; } static void handle_discharge(void) { UINT16 i; char trace[64]; /* first we need to find the current threshold we are at */ i = pwr_ctrl->curr_disch_thresh; /* are we at the bottom? */ if ((i + 1) == pwr_ctrl->nb_percent_thresh) return; /* are we crossing our current threshold? */ if (pwr_ctrl->batt_mv >= pwr_ctrl->batt.percent_thresh[i].bat_voltage) return; /* yes, we crossed it - see if we fell even further down */ while (i < pwr_ctrl->nb_percent_thresh-1 && pwr_ctrl->batt_mv < pwr_ctrl->batt.percent_thresh[i].bat_voltage) i++; /* the last one was it */ pwr_ctrl->curr_disch_thresh = i; sprintf(trace, "Battery fell through %u%% mark", pwr_ctrl->batt.percent_thresh[i-1].remain_capa); rvf_send_trace(trace, strlen(trace), NULL_PARAM, RV_TRACE_LEVEL_WARNING, FCHG_USE_ID); if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_DISCHARGE); } static void capture_first_vbat(void) { char trace[64]; fchg_first_vbat = pwr_ctrl->batt_mv; sprintf(trace, "System boot: Vbat=%u", pwr_ctrl->batt_mv); rvf_send_trace(trace, strlen(trace), NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); } static void start_i2v_cal(void) { UINT16 bciconf; rvf_send_trace("Calibrating i2v offset", 22, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_I2V_CAL_2; bciconf = ABB_Read_Register_on_page(PAGE1, BCICONF); bciconf &= 0x3E0; bciconf |= pwr_ctrl->config.bciconf; ABB_Write_Register_on_page(PAGE1, BCICONF, bciconf); /* * Set the CHDISPA bit and start the zero calibration routine * of the I to V converter */ ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0010); ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0019 | LEDC); } static void start_ci_charging(void) { rvf_send_trace("Start CI charging", 17, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_CI_CHARGING; /* Select constant current charging. The charger is disabled */ ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0002); /* Program the DAC with the constant current value */ ABB_Write_Register_on_page(PAGE0, CHGREG, pwr_ctrl->config.ci_current + pwr_ctrl->i2v_offset); /* Enable the charger */ ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0003 | LEDC); /* The total charging time starts now */ pwr_ctrl->start_time = rvf_get_tick_count(); if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGING_START); } static void start_cv_charging(void) { UINT16 code; rvf_send_trace("Start CV charging", 17, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_CV_CHARGING; /* Select constant voltage charging. The charger is disabled */ ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); /* figure out the DAC code */ code = madc_vbat_inverse(pwr_ctrl->config.cv_init_set); rvf_send_trace("Voltage (DAC code) ", 19, code, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); /* Program the DAC with the constant voltage value */ ABB_Write_Register_on_page(PAGE0, CHGREG, code); /* Enable the charger */ ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0001 | LEDC); /* CV control loop state init */ pwr_ctrl->cv_dac_init = code; pwr_ctrl->cv_dac_curr = code; pwr_ctrl->cv_high_vbat_count = 0; pwr_ctrl->cv_low_vbat_count = 0; /* Ichg averaging state init */ pwr_ctrl->ichg_fill_level = 0; pwr_ctrl->ichg_ring_ptr = 0; pwr_ctrl->ichg_low_count = 0; } static void start_charge_condition_met(void) { rvf_send_trace("Charge start condition met", 26, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); if (pwr_ctrl->config.bciconf) start_i2v_cal(); else { pwr_ctrl->i2v_offset = 0; start_ci_charging(); } } static void ci_progress_trace(UINT16 ichg) { char trace[64]; pwr_ctrl->ci_ichg = ichg; sprintf(trace, "CI charging: Vbat=%u Ichg=%u i2v=%u", pwr_ctrl->batt_mv, ichg, pwr_ctrl->i2v_offset); rvf_send_trace(trace, strlen(trace), NULL_PARAM, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); } static int cv_ichg_process(UINT16 ichg_new) { UINT16 ichg_clip, ichg_entry; UINT32 ichg_accum; UINT16 i; char trace[64]; if (pwr_ctrl->ichg_fill_level < ICHG_AVG_WINDOW) pwr_ctrl->ichg_avg_buf[pwr_ctrl->ichg_fill_level++] = ichg_new; else { ichg_clip = pwr_ctrl->ichg_average + pwr_ctrl->config.ichg_max_spike; if (ichg_new > ichg_clip) ichg_entry = ichg_clip; else ichg_entry = ichg_new; pwr_ctrl->ichg_avg_buf[pwr_ctrl->ichg_ring_ptr++] = ichg_entry; if (pwr_ctrl->ichg_ring_ptr >= ICHG_AVG_WINDOW) pwr_ctrl->ichg_ring_ptr = 0; } ichg_accum = 0; for (i = 0; i < pwr_ctrl->ichg_fill_level; i++) ichg_accum += pwr_ctrl->ichg_avg_buf[i]; pwr_ctrl->ichg_average = ichg_accum / pwr_ctrl->ichg_fill_level; sprintf(trace, "CV charging: Vbat=%u Ichg=%u Ichg_avg=%u i2v=%u", pwr_ctrl->batt_mv, ichg_new, pwr_ctrl->ichg_average, pwr_ctrl->i2v_offset); rvf_send_trace(trace, strlen(trace), NULL_PARAM, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); if (pwr_ctrl->ichg_average > (pwr_ctrl->config.end_current + pwr_ctrl->i2v_offset)) { pwr_ctrl->ichg_low_count = 0; return 0; } pwr_ctrl->ichg_low_count++; if (pwr_ctrl->ichg_low_count < pwr_ctrl->config.ichg_samples_needed) return 0; rvf_send_trace("Stopping charge by low current condition", 40, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); pwr_init_discharge(); pwr_ctrl->state = FCHG_STATE_READY_TO_RECHARGE; if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGING_COMPLETE); return 1; } static int overvoltage_end_charge_check(void) { if (pwr_ctrl->batt_mv < pwr_ctrl->config.overvoltage) return 0; if (pwr_ctrl->cv_dac_curr != (pwr_ctrl->cv_dac_init - pwr_ctrl->config.cv_dac_max_decr)) return 0; rvf_send_trace("Stopping charge by overvoltage condition", 40, NULL_PARAM, RV_TRACE_LEVEL_WARNING, FCHG_USE_ID); ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); pwr_init_discharge(); pwr_ctrl->state = FCHG_STATE_READY_TO_RECHARGE; if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGING_COMPLETE); return 1; } static void cv_ctrl_loop_high_check(void) { if (pwr_ctrl->batt_mv < pwr_ctrl->config.cv_ctrl_loop_high) { pwr_ctrl->cv_high_vbat_count = 0; return; } pwr_ctrl->cv_high_vbat_count++; if (pwr_ctrl->cv_high_vbat_count < pwr_ctrl->config.cv_samples_needed) return; if (pwr_ctrl->cv_dac_curr == (pwr_ctrl->cv_dac_init - pwr_ctrl->config.cv_dac_max_decr)) return; pwr_ctrl->cv_dac_curr--; ABB_Write_Register_on_page(PAGE0, CHGREG, pwr_ctrl->cv_dac_curr); rvf_send_trace("Sub CV DAC", 10, pwr_ctrl->cv_dac_curr, RV_TRACE_LEVEL_DEBUG_MEDIUM, FCHG_USE_ID); pwr_ctrl->cv_high_vbat_count = 0; } static void cv_ctrl_loop_low_check(void) { if (pwr_ctrl->batt_mv >= pwr_ctrl->config.cv_ctrl_loop_low) { pwr_ctrl->cv_low_vbat_count = 0; return; } pwr_ctrl->cv_low_vbat_count++; if (pwr_ctrl->cv_low_vbat_count < pwr_ctrl->config.cv_samples_needed) return; if (pwr_ctrl->cv_dac_curr == (pwr_ctrl->cv_dac_init + pwr_ctrl->config.cv_dac_max_incr)) return; pwr_ctrl->cv_dac_curr++; ABB_Write_Register_on_page(PAGE0, CHGREG, pwr_ctrl->cv_dac_curr); rvf_send_trace("Add CV DAC", 10, pwr_ctrl->cv_dac_curr, RV_TRACE_LEVEL_DEBUG_MEDIUM, FCHG_USE_ID); pwr_ctrl->cv_low_vbat_count = 0; } static int charging_time_limit_check(void) { if ((rvf_get_tick_count() - pwr_ctrl->start_time) < RVF_SECS_TO_TICKS(pwr_ctrl->config.charge_time_limit)) return 0; rvf_send_trace("Stopping charge by time exceeded condition", 42, NULL_PARAM, RV_TRACE_LEVEL_WARNING, FCHG_USE_ID); ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); pwr_init_discharge(); pwr_ctrl->state = FCHG_STATE_RECHARGE_TIMER; pwr_ctrl->start_time = rvf_get_tick_count(); if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGING_TIMEOUT); return 1; } void pwr_process_adc(struct pwr_adc_ind_s *msg) { pwr_ctrl->batt_mv = madc_vbat_2_physical(msg->data[0]); if (!fchg_first_vbat) capture_first_vbat(); switch (pwr_ctrl->state) { case FCHG_STATE_NO_EXT_PWR: case FCHG_STATE_PWR_PLUG_TIMER: case FCHG_STATE_NO_CHARGING: handle_discharge(); break; case FCHG_STATE_READY_TO_CHARGE: handle_discharge(); if (!(msg->data[9] & CHGPRES)) { pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; break; } if (pwr_ctrl->batt_mv < pwr_ctrl->config.start_thresh) start_charge_condition_met(); break; case FCHG_STATE_READY_TO_RECHARGE: handle_discharge(); if (!(msg->data[9] & CHGPRES)) { pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; break; } if (pwr_ctrl->batt_mv < pwr_ctrl->config.restart_thresh) start_charge_condition_met(); break; case FCHG_STATE_I2V_CAL_1: if (!(msg->data[9] & CHGPRES)) { pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; break; } if (pwr_ctrl->config.bciconf) start_i2v_cal(); else { pwr_ctrl->i2v_offset = 0; start_ci_charging(); } break; case FCHG_STATE_I2V_CAL_2: pwr_ctrl->i2v_offset = msg->data[2]; ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); rvf_send_trace("i2v offset (MADC code) ", 23, pwr_ctrl->i2v_offset, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); if (!(msg->data[9] & CHGPRES)) { pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; pwr_init_discharge(); break; } start_ci_charging(); break; case FCHG_STATE_CI_CHARGING: ci_progress_trace(msg->data[2]); if (!(msg->data[9] & CHGPRES)) { ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; pwr_init_discharge(); break; } if (charging_time_limit_check()) break; if (pwr_ctrl->batt_mv >= pwr_ctrl->config.ci2cv_thresh) start_cv_charging(); break; case FCHG_STATE_CV_CHARGING: if (!(msg->data[9] & CHGPRES)) { ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; pwr_init_discharge(); break; } if (cv_ichg_process(msg->data[2])) break; if (overvoltage_end_charge_check()) break; if (charging_time_limit_check()) break; cv_ctrl_loop_high_check(); cv_ctrl_loop_low_check(); break; case FCHG_STATE_RECHARGE_TIMER: handle_discharge(); if ((rvf_get_tick_count() - pwr_ctrl->start_time) < RVF_SECS_TO_TICKS(pwr_ctrl->config.recharge_delay)) break; rvf_send_trace("Restart time met, allowing new charging", 39, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_READY_TO_RECHARGE; break; default: rvf_send_trace("Invalid state in pwr_process_adc()", 32, pwr_ctrl->state, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); } } void pwr_handle_timer(void) { if (pwr_ctrl->state != FCHG_STATE_PWR_PLUG_TIMER) return; rvf_send_trace("Timer expired, ready to charge", 30, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_READY_TO_CHARGE; } void pwr_charger_plug(void) { if (pwr_ctrl->state != FCHG_STATE_NO_EXT_PWR) { rvf_send_trace("Charger plug event in unexpected state", 38, pwr_ctrl->state, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); return; } if (!pwr_ctrl->config_present) { rvf_send_trace( "Charger plugged in, but no config: won't charge", 47, NULL_PARAM, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_NO_CHARGING; return; } if (pwr_ctrl->config.start_delay) { rvf_send_trace("Charger plug, starting timer", 28, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); rvf_start_timer(FCHG_TIMER, RVF_MS_TO_TICKS(pwr_ctrl->config.start_delay), FALSE); pwr_ctrl->state = FCHG_STATE_PWR_PLUG_TIMER; } else { rvf_send_trace("Charger plug, ready to charge", 29, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_READY_TO_CHARGE; } if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGER_PLUG); } void pwr_charger_unplug(void) { switch (pwr_ctrl->state) { case FCHG_STATE_NO_EXT_PWR: rvf_send_trace("Charger unplug, already handled", 31, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); /* nothing to do */ break; case FCHG_STATE_PWR_PLUG_TIMER: case FCHG_STATE_READY_TO_CHARGE: case FCHG_STATE_READY_TO_RECHARGE: case FCHG_STATE_I2V_CAL_1: case FCHG_STATE_RECHARGE_TIMER: case FCHG_STATE_NO_CHARGING: rvf_send_trace("Charger unplug", 14, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_LOW, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; break; case FCHG_STATE_I2V_CAL_2: case FCHG_STATE_CI_CHARGING: case FCHG_STATE_CV_CHARGING: ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); rvf_send_trace("Charger unplug, charging stopped", 32, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_NO_EXT_PWR; pwr_init_discharge(); break; default: rvf_send_trace("Invalid state in pwr_charger_unplug()", 35, pwr_ctrl->state, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); } if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGER_UNPLUG); } void pwr_charge_start_req(void) { switch (pwr_ctrl->state) { case FCHG_STATE_NO_EXT_PWR: rvf_send_trace("Cannot charge without a power source", 36, NULL_PARAM, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); return; case FCHG_STATE_NO_CHARGING: if (!pwr_ctrl->config_present) { rvf_send_trace("No config set, cannot charge", 28, NULL_PARAM, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); return; } /* FALL THRU */ case FCHG_STATE_PWR_PLUG_TIMER: case FCHG_STATE_READY_TO_CHARGE: case FCHG_STATE_READY_TO_RECHARGE: case FCHG_STATE_RECHARGE_TIMER: rvf_send_trace("Starting charge on user request", 31, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_I2V_CAL_1; return; case FCHG_STATE_I2V_CAL_1: case FCHG_STATE_I2V_CAL_2: case FCHG_STATE_CI_CHARGING: case FCHG_STATE_CV_CHARGING: rvf_send_trace( "Charging already in progress, start request ignored", 51, NULL_PARAM, RV_TRACE_LEVEL_WARNING, FCHG_USE_ID); return; default: rvf_send_trace("Invalid state in pwr_charge_start_req()", 37, pwr_ctrl->state, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); } } void pwr_charge_stop_req(void) { switch (pwr_ctrl->state) { case FCHG_STATE_NO_EXT_PWR: case FCHG_STATE_NO_CHARGING: /* nothing to do */ return; case FCHG_STATE_PWR_PLUG_TIMER: case FCHG_STATE_READY_TO_CHARGE: case FCHG_STATE_READY_TO_RECHARGE: case FCHG_STATE_I2V_CAL_1: case FCHG_STATE_RECHARGE_TIMER: rvf_send_trace("Charging disabled by user request", 33, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_NO_CHARGING; return; case FCHG_STATE_I2V_CAL_2: case FCHG_STATE_CI_CHARGING: case FCHG_STATE_CV_CHARGING: ABB_Write_Register_on_page(PAGE0, BCICTL2, 0); rvf_send_trace("Charging stopped by user request", 32, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, FCHG_USE_ID); pwr_ctrl->state = FCHG_STATE_NO_CHARGING; pwr_init_discharge(); if (pwr_ctrl->event_handler) pwr_ctrl->event_handler(FCHG_EVENT_CHARGING_STOPPED); return; default: rvf_send_trace("Invalid state in pwr_charge_stop_req()", 36, pwr_ctrl->state, RV_TRACE_LEVEL_ERROR, FCHG_USE_ID); } }