| /** \file vp880_tracker_calibration.c |
| * vp880_tracker_calibration.c |
| * |
| * This file contains the tracker device calibration functions for |
| * the Vp880 device API. |
| * |
| * Copyright (c) 2011, Microsemi |
| * |
| * $Revision: 1.1.2.1.8.3 $ |
| * $LastChangedDate: 2010-04-01 17:44:54 -0500 (Thu, 01 Apr 2010) $ |
| */ |
| |
| #include "../includes/vp_api_cfg.h" |
| |
| #if defined (VP_CC_880_SERIES) && defined (VP880_TRACKER_SUPPORT) |
| |
| /* INCLUDES */ |
| #include "../../arch/uvb/vp_api_types.h" |
| #include "../includes/vp_api.h" |
| #include "../includes/vp_api_int.h" |
| #include "../includes/vp880_api.h" |
| #include "../vp880_api/vp880_api_int.h" |
| #include "../../arch/uvb/vp_hal.h" |
| #include "../../arch/uvb/sys_service.h" |
| |
| #ifdef VP_CSLAC_RUNTIME_CAL_ENABLED |
| |
| /* Functions that are called only inside this file. */ |
| static void Vp880AbvInit(VpDevCtxType *pDevCtx); |
| static void Vp880AbvSetAdc(VpDevCtxType *pDevCtx); |
| static void Vp880AbvStateChange(VpDevCtxType *pDevCtx); |
| static void Vp880AbvMeasure(VpDevCtxType *pDevCtx); |
| |
| #endif /* VP_CSLAC_RUNTIME_CAL_ENABLED */ |
| |
| #ifdef VP_CSLAC_RUNTIME_CAL_ENABLED |
| |
| /** |
| * Vp880CalAbv() -- Tracker Only Function |
| * This function initiates a calibration operation for Absolute Switcher |
| * circuits associated with all the lines of a device. See VP-API reference |
| * guide for more information. SWYV SWZV are global for every Channels |
| * Line must be in Disconnect state before to start the Calibration |
| * |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| * This function generates an event upon completing the requested action. |
| */ |
| VpStatusType |
| Vp880CalAbv( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| VpStatusType status = VP_STATUS_SUCCESS; |
| uint8 ecVal; |
| uint8 fxsChannels; |
| bool calCleanup = FALSE; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880CalAbv+")); |
| |
| if ((pDevObj->stateInt & VP880_IS_SINGLE_CHANNEL) || (pDevObj->stateInt & VP880_LINE1_IS_FXO)) { |
| ecVal = VP880_EC_CH1; |
| fxsChannels = 1; |
| } else { |
| ecVal = VP880_EC_CH1 | VP880_EC_CH2; |
| fxsChannels = 2; |
| } |
| |
| if (pDevObj->calData.calDeviceState == VP880_CAL_INIT |
| || pDevObj->calData.calDeviceState == VP880_CAL_EXIT) { |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv: - Setting to Vp880CalInit at time %d", pDevObj->timeStamp)); |
| |
| pDevObj->calData.calDeviceState = VP880_CAL_INIT; |
| Vp880CalInit(pDevCtx); |
| } |
| |
| switch(pDevObj->calData.calDeviceState) { |
| case VP880_CAL_INIT: |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv: - Running Vp880AbvInit at time %d", |
| pDevObj->timeStamp)); |
| Vp880AbvInit(pDevCtx); |
| break; |
| |
| case VP880_CAL_ADC: |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv: - Running Vp880AbvSetAdc at time %d", |
| pDevObj->timeStamp)); |
| Vp880AbvSetAdc(pDevCtx); |
| break; |
| |
| case VP880_CAL_STATE_CHANGE: |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv: - Running Vp880AbvStateChange at time %d", |
| pDevObj->timeStamp)); |
| Vp880AbvStateChange(pDevCtx); |
| break; |
| |
| case VP880_CAL_MEASURE: |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv - Running Vp880AbvMeasure at time %d", |
| pDevObj->timeStamp)); |
| Vp880AbvMeasure(pDevCtx); |
| break; |
| |
| case VP880_CAL_DONE: |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("ABV Cal Done at time %d", pDevObj->timeStamp)); |
| calCleanup = TRUE; |
| break; |
| |
| case VP880_CAL_ERROR: |
| /* Fall through intentional */ |
| default: |
| VP_ERROR(VpDevCtxType, pDevCtx, |
| ("Vp880CalAbv: ERROR - Cal Done at time %d", pDevObj->timeStamp)); |
| calCleanup = TRUE; |
| status = VP_STATUS_FAILURE; |
| pDevObj->responseData = VP_CAL_FAILURE; |
| break; |
| } /* end of switch(pDevObj->calData.calDeviceState) */ |
| |
| if (calCleanup == TRUE) { |
| uint8 channelId; |
| uint8 convCfg[VP880_CONV_CFG_LEN]; |
| uint8 mpiBuffer[9 + VP880_SYS_STATE_LEN + VP880_ICR1_LEN |
| + VP880_ICR2_LEN + VP880_ICR3_LEN + VP880_ICR4_LEN |
| + VP880_DISN_LEN + VP880_VP_GAIN_LEN |
| + VP880_OP_FUNC_LEN + VP880_CONV_CFG_LEN]; |
| uint8 mpiIndex = 0; |
| |
| convCfg[0] = (VP880_METALLIC_AC_V | pDevObj->txBufferDataRate); |
| |
| pDevObj->calData.calDeviceState = VP880_CAL_EXIT; |
| if (pDevObj->state & VP_DEV_INIT_IN_PROGRESS) { |
| pDevObj->deviceEvents.response |= VP_DEV_EVID_DEV_INIT_CMP; |
| pDevObj->state |= VP_DEV_INIT_CMP; |
| } else { |
| pDevObj->deviceEvents.response |= VP_EVID_CAL_CMP; |
| } |
| pDevObj->state &= ~(VP_DEV_INIT_IN_PROGRESS | VP_DEV_IN_CAL); |
| pDevObj->state &= ~VP_DEV_ABV_CAL; |
| |
| if (pDevObj->responseData == VP_CAL_FAILURE) { |
| pDevObj->stateInt &= ~VP880_DEVICE_CAL_COMPLETE; |
| } else { |
| pDevObj->stateInt |= VP880_DEVICE_CAL_COMPLETE; |
| } |
| |
| /* Restore device mode to test buffer if exists */ |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| pDevObj->devMode[0] |= VP880_DEV_MODE_TEST_DATA; |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| for (channelId = 0; channelId < pDevObj->staticInfo.maxChannels; channelId++) { |
| VpLineCtxType *pLineCtx = pDevCtx->pLineCtx[channelId]; |
| |
| mpiIndex = 0; |
| ecVal = (channelId == 0) ? VP880_EC_CH1 : VP880_EC_CH2; |
| |
| /* Restore slic state -- could be FXO also */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Setting to State 0x%02X at time %d", |
| channelId, pDevObj->calData.abvData.sysState[channelId][0], pDevObj->timeStamp)); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, |
| VP880_SYS_STATE_WRT, VP880_SYS_STATE_LEN, |
| &pDevObj->calData.abvData.sysState[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing ICR1 to 0x%02X 0x%02X 0x%02X 0x%02X", |
| channelId, |
| pDevObj->calData.abvData.icr1[channelId][0], |
| pDevObj->calData.abvData.icr1[channelId][1], |
| pDevObj->calData.abvData.icr1[channelId][2], |
| pDevObj->calData.abvData.icr1[channelId][3])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR1_WRT, |
| VP880_ICR1_LEN, &pDevObj->calData.abvData.icr1[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing ICR2 to 0x%02X 0x%02X 0x%02X 0x%02X", |
| channelId, |
| pDevObj->calData.abvData.icr2[channelId][0], |
| pDevObj->calData.abvData.icr2[channelId][1], |
| pDevObj->calData.abvData.icr2[channelId][2], |
| pDevObj->calData.abvData.icr2[channelId][3])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, &pDevObj->calData.abvData.icr2[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing ICR3 to 0x%02X 0x%02X 0x%02X 0x%02X", |
| channelId, |
| pDevObj->calData.abvData.icr3[channelId][0], |
| pDevObj->calData.abvData.icr3[channelId][1], |
| pDevObj->calData.abvData.icr3[channelId][2], |
| pDevObj->calData.abvData.icr3[channelId][3])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR3_WRT, |
| VP880_ICR3_LEN, &pDevObj->calData.abvData.icr3[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing ICR4 to 0x%02X 0x%02X 0x%02X 0x%02X", |
| channelId, |
| pDevObj->calData.abvData.icr4[channelId][0], |
| pDevObj->calData.abvData.icr4[channelId][1], |
| pDevObj->calData.abvData.icr4[channelId][2], |
| pDevObj->calData.abvData.icr4[channelId][3])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, |
| VP880_ICR4_LEN, &pDevObj->calData.abvData.icr4[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing DISN to 0x%02X", |
| channelId, pDevObj->calData.abvData.disnVal[channelId][0])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DISN_WRT, |
| VP880_DISN_LEN, &pDevObj->calData.abvData.disnVal[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing VP Gain to 0x%02X", |
| channelId, pDevObj->calData.abvData.vpGain[channelId][0])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_VP_GAIN_WRT, |
| VP880_VP_GAIN_LEN, &pDevObj->calData.abvData.vpGain[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing OP FUNC to 0x%02X", |
| channelId, pDevObj->calData.abvData.opFunc[channelId][0])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_OP_FUNC_WRT, |
| VP880_OP_FUNC_LEN, &pDevObj->calData.abvData.opFunc[channelId][0]); |
| |
| /* Restore Converter Configuration */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("CH %d: Calibration Cleanup -- Writing CONVERTER to 0x%02X", |
| channelId, convCfg[0])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_CONV_CFG_WRT, |
| VP880_CONV_CFG_LEN, convCfg); |
| |
| /* send down the mpi commands */ |
| VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); |
|
|
| /*
|
| * The Disconnect Pending flag will be set if the line started a disconnect sequence
|
| * and before the timer completed then started this calibration. In fact, this is
|
| * normal when coming out of VpInitDevice(). So once all other registers are restored
|
| * we have to complete the disconnect sequence.
|
| */ |
| if (pDevObj->state & VP_DEV_DISC_PENDING) { |
| if (pLineCtx != VP_NULL) { |
| Vp880ServiceDiscExitTimer(pLineCtx); |
| } |
| } |
| } /* end of loop "for (channelId = 0; ...)" */ |
| pDevObj->state &= ~VP_DEV_DISC_PENDING; |
|
|
| } /* eid of "if (calCleanup == TRUE)" */ |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880CalAbv-")); |
| return status; |
| } |
| |
| /** |
| * Vp880AbvInit() -- Tracker (ABV Cal) only Function |
| * This function initiates a calibration operation for ABV |
| * associated with all the lines of a device. See VP-API reference guide for |
| * more information. |
| |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| * This function generates an event upon completing the requested action. |
| */ |
| void |
| Vp880AbvInit( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| uint8 disnVal[VP880_DISN_LEN] = {0x00}; |
| uint8 vpGain[VP880_VP_GAIN_LEN] = {0x00}; |
| uint8 channelId; |
| |
| uint8 isrpMods[VP880_INT_SWREG_PARAM_LEN] = { |
| 0x00, 0x40, 0x00, 0x40, 0x00, 0x40 |
| }; |
| |
| uint8 icr1[VP880_ICR1_LEN] = {0x00, 0x00, 0x00, 0x00}; |
| uint8 icr2[VP880_ICR2_LEN] = {0x00, 0xEC, 0x2C, 0x2C}; |
| uint8 icr3[VP880_ICR3_LEN] = {0x30, 0x20, 0x00, 0x00}; |
| uint8 icr4[VP880_ICR4_LEN] = {0x01, 0x01, 0x00, 0x00}; |
| |
| uint8 data, ecVal; |
| uint8 swCal[VP880_BAT_CALIBRATION_LEN]; |
| |
| uint8 mpiBuffer[9 + VP880_ICR2_LEN + VP880_SYS_STATE_LEN + VP880_ICR3_LEN + |
| VP880_ICR4_LEN + VP880_OP_FUNC_LEN + VP880_OP_COND_LEN + |
| VP880_DISN_LEN + VP880_VP_GAIN_LEN + VP880_ICR1_LEN]; |
| uint8 mpiIndex = 0; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvInit+")); |
| |
| /* |
| * Initialize and use to measure each channels offset and voltage using |
| * same functions. |
| */ |
| pDevObj->calData.abvData.passCnt = 0; |
| |
| /* Channel specific registers to restore at end of calibration */ |
| for (channelId = 0; channelId < pDevObj->staticInfo.maxChannels; channelId++) { |
| ecVal = (channelId == 0) ? VP880_EC_CH1 : VP880_EC_CH2; |
| |
| /* Save off current slic state */ |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_RD, VP880_SYS_STATE_LEN, |
| &pDevObj->calData.abvData.sysState[channelId][0]); |
| |
| /* |
| * Disable switcher by setting duty cycle = 0. Global, so only need |
| * to do once. |
| */ |
| if (channelId == 0) { |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_INT_SWREG_PARAM_WRT, |
| VP880_INT_SWREG_PARAM_LEN, isrpMods); |
| } |
| |
| /* Clear existing correction factors */ |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_BAT_CALIBRATION_RD, |
| VP880_BAT_CALIBRATION_LEN, swCal); |
| swCal[0] &= ~(VP880_BAT_CAL_SWCAL_MASK); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_BAT_CALIBRATION_WRT, |
| VP880_BAT_CALIBRATION_LEN, swCal); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR1_RD, VP880_ICR1_LEN, |
| &pDevObj->calData.abvData.icr1[channelId][0]); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Saving ICR1 0x%02X 0x%02X 0x%02X 0x%02X Ch %d", |
| pDevObj->calData.abvData.icr1[channelId][0], |
| pDevObj->calData.abvData.icr1[channelId][1], |
| pDevObj->calData.abvData.icr1[channelId][2], |
| pDevObj->calData.abvData.icr1[channelId][3], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR2_RD, VP880_ICR2_LEN, |
| &pDevObj->calData.abvData.icr2[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Saving ICR2 0x%02X 0x%02X 0x%02X 0x%02X Ch %d", |
| pDevObj->calData.abvData.icr2[channelId][0], |
| pDevObj->calData.abvData.icr2[channelId][1], |
| pDevObj->calData.abvData.icr2[channelId][2], |
| pDevObj->calData.abvData.icr2[channelId][3], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR3_RD, VP880_ICR3_LEN, |
| &pDevObj->calData.abvData.icr3[channelId][0]); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Saving ICR3 0x%02X 0x%02X 0x%02X 0x%02X Ch %d", |
| pDevObj->calData.abvData.icr3[channelId][0], |
| pDevObj->calData.abvData.icr3[channelId][1], |
| pDevObj->calData.abvData.icr3[channelId][2], |
| pDevObj->calData.abvData.icr3[channelId][3], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR4_RD, VP880_ICR4_LEN, |
| &pDevObj->calData.abvData.icr4[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Saving ICR4 0x%02X 0x%02X 0x%02X 0x%02X Ch %d", |
| pDevObj->calData.abvData.icr4[channelId][0], |
| pDevObj->calData.abvData.icr4[channelId][1], |
| pDevObj->calData.abvData.icr4[channelId][2], |
| pDevObj->calData.abvData.icr4[channelId][3], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_DISN_RD, VP880_DISN_LEN, |
| &pDevObj->calData.abvData.disnVal[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx,("Saving DISN 0x%02X Ch %d", |
| pDevObj->calData.abvData.disnVal[channelId][0], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_VP_GAIN_RD, VP880_VP_GAIN_LEN, |
| &pDevObj->calData.abvData.vpGain[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx,("Saving VP Gain 0x%02X Ch %d", |
| pDevObj->calData.abvData.vpGain[channelId][0], |
| channelId)); |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_FUNC_RD, VP880_OP_FUNC_LEN, |
| &pDevObj->calData.abvData.opFunc[channelId][0]); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx,("Saving OP FUNC 0x%02X Ch %d", |
| pDevObj->calData.abvData.opFunc[channelId][0], |
| channelId)); |
| } |
| |
| if ((pDevObj->stateInt & VP880_IS_SINGLE_CHANNEL) |
| || (pDevObj->stateInt & VP880_LINE1_IS_FXO)) { |
| ecVal = VP880_EC_CH1; |
| } else { |
| ecVal = VP880_EC_CH1 | VP880_EC_CH2; |
| } |
| |
| /* Disable the switchers */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Writing ICR2 0x%02X 0x%02X 0x%02X 0x%02X BOTH channels", |
| icr2[0], icr2[1], icr2[2], icr2[3])); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, icr2); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Writing ICR1 0x%02X 0x%02X 0x%02X 0x%02X BOTH channels", |
| icr1[0], icr1[1], icr1[2], icr1[3])); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR1_WRT, |
| VP880_ICR1_LEN, icr1); |
| |
| /* Force sink supply current to reduce voltage */ |
| data = VP880_SS_ACTIVE; |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Setting Ch %d to State 0x%02X at time %d BOTH channels", |
| channelId, data, pDevObj->timeStamp)); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &data); |
| |
| /* Enable line control to access VBAT sense */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("1. Calibration: Write ICR3 0x%02X 0x%02X 0x%02X 0x%02X BOTH channels", |
| icr3[0], icr3[1], icr3[2], icr3[3])); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR3_WRT, |
| VP880_ICR3_LEN, icr3); |
| |
| /* Enable ADC */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Write ICR4 0x%02X 0x%02X 0x%02X 0x%02X BOTH channels", |
| icr4[0], icr4[1], icr4[2], icr4[3])); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, |
| VP880_ICR4_LEN, icr4); |
| |
| /* Set compression to Linear Mode and default AC Coefficients */ |
| data = VP880_LINEAR_CODEC; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_OP_FUNC_WRT, |
| VP880_OP_FUNC_LEN, &data); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Force OP Func to 0x%02X BOTH channels", data)); |
| |
| /* Cut TX/RX PCM and disable HPF */ |
| data = (VP880_CUT_TXPATH | VP880_CUT_RXPATH | VP880_HIGH_PASS_DIS); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, |
| VP880_OP_COND_WRT, VP880_OP_COND_LEN, &data); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Force OP Cond to 0x%02X BOTH channels", data)); |
| |
| /* Set DISN = 0 and Voice Path Gain to 0dB. */ |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DISN_WRT, |
| VP880_DISN_LEN, disnVal); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: DISN to 0x%02X BOTH channels", disnVal[0])); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_VP_GAIN_WRT, |
| VP880_VP_GAIN_LEN, vpGain); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: VP Gain to 0x%02X BOTH channels", vpGain[0])); |
| |
| /* send down the mpi commands */ |
| VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); |
| |
| /* Wait at least 100ms before collecting data */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_LONG, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| /* Advance state to measure ADC offset */ |
| pDevObj->calData.calDeviceState = VP880_CAL_STATE_CHANGE; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvInit-")); |
| } /* end Vp880AbvInit */ |
| |
| /** |
| * Vp880AbvStateChange () -- Tracker (ABV Cal) only function |
| * This function changes the line state and sets the converter configuration |
| * in order to give time for the converter to stabilize before taking the first |
| * set of data. |
| * |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| */ |
| void |
| Vp880AbvStateChange( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| uint8 data, ecVal; |
| uint8 converterCfg[VP880_CONV_CFG_LEN] = {VP880_SWITCHER_Y}; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvStateChange+")); |
| |
| if ((pDevObj->stateInt & VP880_IS_SINGLE_CHANNEL) |
| || (pDevObj->stateInt & VP880_LINE1_IS_FXO)) { |
| ecVal = VP880_EC_CH1; |
| } else { |
| ecVal = VP880_EC_CH1 | VP880_EC_CH2; |
| } |
| |
| /* Help discharge the battery */ |
| data = (VP880_SS_ACTIVE | VP880_SS_ACTIVATE_MASK); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Setting ALL_LINES to State 0x%02X at time %d", |
| data, pDevObj->timeStamp)); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_WRT, VP880_SYS_STATE_LEN, |
| &data); |
| |
| /* Don't care about the data, just force the converter configuration */ |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH1, VP880_CONV_CFG_WRT, |
| VP880_CONV_CFG_LEN, converterCfg); |
| /* |
| * Force bad initial sample to force decay check to fail. This should be a |
| * reasonably high value (far above any reasonable offset) to force the |
| * first test to fail. |
| */ |
| pDevObj->vp880SysCalData.swyOffset[0] = 13000; |
| |
| /* Reset the iteration value used later to avoid infinite retries */ |
| pDevObj->calData.iteration = 0; |
| |
| /* |
| * Allow the converter to stabilize and most line conditions to fully |
| * discharge the battery. Note that the very first check of the data will |
| * fail because the preset value will be much different than the new value. |
| * The decay algorithm takes that into account and sets the first delay |
| * based on iteration. |
| */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_LONG, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| pDevObj->calData.calDeviceState = VP880_CAL_ADC; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvStateChange-")); |
| } /* end Vp880AbvStateChange */ |
| |
| /** |
| * Vp880AbvSetAdc() -- Tracker (ABV Cal) only function |
| * This function set the converter to read the right pcm set the right state |
| * machine |
| * |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| */ |
| void |
| Vp880AbvSetAdc( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| uint8 ecVal, channelId; |
| uint8 swYZ[VP880_REGULATOR_PARAM_LEN]; |
| bool abvSetAdcDone = FALSE; |
| uint8 converterCfg[VP880_CONV_CFG_LEN] = {VP880_SWITCHER_Z}; |
| bool validData; |
| int16 newValue; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvSetAdc+")); |
| |
| if ((pDevObj->stateInt & VP880_IS_SINGLE_CHANNEL) |
| || (pDevObj->stateInt & VP880_LINE1_IS_FXO)) { |
| ecVal = VP880_EC_CH1; |
| } else { |
| ecVal = VP880_EC_CH1 | VP880_EC_CH2; |
| } |
| |
| pDevObj->calData.iteration++; |
| |
| /* Now we'll switch to channel specific measurements */ |
| /* Read SWY from first channel, SWZ from second channel */ |
| if (pDevObj->calData.abvData.passCnt == 0) { |
| newValue = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH1, VP880_SWITCHER_Y, &validData) |
| + VP880_TRACKER_BAT_OFFSET; |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function) and repeat measurement. |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } else { |
| /* Repeated as much as we can. Set 0V offset and move on. */ |
| newValue = 0; |
| } |
| } else if ((ABS(pDevObj->vp880SysCalData.swyOffset[0] - newValue) > VP880_V_1V_PCM) |
| && (ABS(newValue) > VP880_V_1V_PCM)) { |
| /* We're in a voltage decay. Need to wait longer to settle. */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Voltage Decay Detected: Old Value %d New Value %d", |
| pDevObj->vp880SysCalData.swyOffset[0], newValue)); |
| |
| pDevObj->vp880SysCalData.swyOffset[0] = newValue; |
| |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DECAY_STEP, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } else { |
| newValue = 0; |
| } |
| } |
| |
| /* |
| * Data is valid or we're going with what we've got. Reset iteration |
| * count for next data set. |
| */ |
| pDevObj->calData.iteration = 0; |
| pDevObj->vp880SysCalData.swyOffset[0] = newValue; |
| |
| /* This is done to be compatible with VVA P1.3.0 */ |
| pDevObj->calData.abvData.swyOffset[0] = |
| pDevObj->vp880SysCalData.swyOffset[0]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, ("Chan 0 Offset (10mV): SWY %d", |
| ((int16)(pDevObj->vp880SysCalData.swyOffset[0] * VP880_V_PCM_LSB / VP880_V_SCALE)))); |
| |
| if (ecVal == VP880_EC_CH1) { |
| abvSetAdcDone = TRUE; |
| } else { |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH2, VP880_CONV_CFG_WRT, |
| VP880_CONV_CFG_LEN, converterCfg); |
| |
| /* Wait for converter data to settle */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| pDevObj->calData.abvData.passCnt++; |
| } |
| } else if (pDevObj->calData.abvData.passCnt == 1) { |
| pDevObj->vp880SysCalData.swzOffset[1] = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH2, VP880_SWITCHER_Z, &validData) |
| + VP880_TRACKER_BAT_OFFSET; |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function) and repeat measurement |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } else { |
| /* Repeated as much as we can. Set 0V offset and move on. */ |
| pDevObj->vp880SysCalData.swzOffset[1] = 0; |
| } |
| } |
| /* |
| * Data is valid or we're going with what we've got. Reset iteration |
| * count for next data set. |
| */ |
| pDevObj->calData.iteration = 0; |
| |
| /* This is done to be compatible with VVA P1.3.0 */ |
| pDevObj->calData.abvData.swzOffset[1] = |
| pDevObj->vp880SysCalData.swzOffset[1]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, ("Chan 1 Offset (10mV): SWZ %d", |
| ((int16)(pDevObj->vp880SysCalData.swzOffset[1] * VP880_V_PCM_LSB / VP880_V_SCALE)))); |
| |
| converterCfg[0] = VP880_SWITCHER_Y; |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH2, VP880_CONV_CFG_WRT, |
| VP880_CONV_CFG_LEN, converterCfg); |
| |
| converterCfg[0] = VP880_SWITCHER_Z; |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH1, VP880_CONV_CFG_WRT, |
| VP880_CONV_CFG_LEN, converterCfg); |
| |
| /* Wait for converter data to settle */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| pDevObj->calData.abvData.passCnt++; |
| } else { |
| pDevObj->vp880SysCalData.swzOffset[0] = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH1, VP880_SWITCHER_Z, &validData) |
| + VP880_TRACKER_BAT_OFFSET; |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function and repeat measurement |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } else { |
| /* Repeated as much as we can. Set 0V offset and move on. */ |
| pDevObj->vp880SysCalData.swzOffset[0] = 0; |
| |
| /* |
| * Data is valid or we're going with what we've got. Reset |
| * iteration count for next data set. |
| */ |
| pDevObj->calData.iteration = 0; |
| } |
| } |
| |
| /* This is done to be compatible with VVA P1.3.0 */ |
| pDevObj->calData.abvData.swzOffset[0] = |
| pDevObj->vp880SysCalData.swzOffset[0]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, ("Chan 0 Offset: SWZ %d", |
| ((pDevObj->vp880SysCalData.swzOffset[0] * VP880_V_PCM_LSB) / 10000))); |
| |
| pDevObj->vp880SysCalData.swyOffset[1] = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH2, VP880_SWITCHER_Y, &validData) |
| + VP880_TRACKER_BAT_OFFSET; |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function and repeat measurement |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } else { |
| pDevObj->vp880SysCalData.swyOffset[1] = 0; |
| } |
| } |
| /* |
| * Data is valid or we're going with what we've got. Reset iteration |
| * count for next data set. |
| */ |
| pDevObj->calData.iteration = 0; |
| |
| /* This is done to be compatible with VVA P1.3.0 */ |
| pDevObj->calData.abvData.swyOffset[1] = |
| pDevObj->vp880SysCalData.swyOffset[1]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, ("Chan 1 Offset: SWY %d", |
| ((pDevObj->vp880SysCalData.swyOffset[1] * VP880_V_PCM_LSB) / 10000))); |
| |
| abvSetAdcDone = TRUE; |
| } |
| |
| if (abvSetAdcDone == TRUE) { |
| uint8 lineState = (VP880_SS_DISCONNECT | VP880_SS_ACTIVATE_MASK); |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Calibration: Returning ALL_LINES to State 0x%02X at time %d", |
| lineState, pDevObj->timeStamp)); |
| VpMpiCmdWrapper(deviceId, (VP880_EC_CH1 | VP880_EC_CH2), |
| VP880_SYS_STATE_WRT, VP880_SYS_STATE_LEN, &lineState); |
| |
| /* |
| * Copy the Ringing Voltage to the Floor Voltage, everything else |
| * directly from the device profile |
| */ |
| swYZ[0] = pDevObj->swParams[0]; |
| |
| swYZ[VP880_SWY_LOCATION] = |
| (pDevObj->swParams[VP880_SWZ_LOCATION] & VP880_VOLTAGE_MASK); |
| swYZ[VP880_SWY_LOCATION] |= |
| (pDevObj->swParams[VP880_SWY_LOCATION] & ~VP880_VOLTAGE_MASK); |
| |
| swYZ[2] = pDevObj->swParams[2]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880AbvInit: swYZ: 0x%02X 0x%02X 0x%02X", |
| swYZ[0], swYZ[1], swYZ[2])); |
| |
| /* Program switcher floor voltage to target ringing voltage */ |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH1, VP880_REGULATOR_PARAM_WRT, |
| VP880_REGULATOR_PARAM_LEN, swYZ); |
| VpMemCpy(pDevObj->swParamsCache, swYZ, VP880_REGULATOR_PARAM_LEN); |
| |
| /* |
| * Restore internal switcher parameters to take voltage measurement. This |
| * is a global register so it doesn't matter which EC value is used. |
| */ |
| VpMpiCmdWrapper(deviceId, VP880_EC_CH1, VP880_INT_SWREG_PARAM_WRT, |
| VP880_INT_SWREG_PARAM_LEN, pDevObj->intSwParams); |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Writing to Internal Switching Regulator: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", |
| pDevObj->intSwParams[0], pDevObj->intSwParams[1], pDevObj->intSwParams[2], |
| pDevObj->intSwParams[3], pDevObj->intSwParams[4], pDevObj->intSwParams[5])); |
| |
| pDevObj->calData.calSet = pDevObj->swParams[VP880_SWREG_RING_V_BYTE]; |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Vp880AbvInit: ABV Set Value Target %d", |
| ((pDevObj->calData.calSet * 5) + 5))); |
| |
| /* |
| * If the line is or was a Low Power Termination type the ICR values |
| * could be in a condition where the switcher is not enabled. So force |
| * to known/working values - Note: Actual device values are restored at |
| * the end of calibration |
| */ |
| for (channelId = 0; |
| channelId < pDevObj->staticInfo.maxChannels; channelId++) { |
| uint8 icr1[VP880_ICR1_LEN] = {0x0F, 0x08, 0xC0, 0x00}; |
| uint8 icr2[VP880_ICR2_LEN] = {0x00, 0x00, 0x2C, 0x7C}; |
| uint8 icr3[VP880_ICR3_LEN] = {0x30, 0x21, 0x00, 0x00}; |
| uint8 icr4[VP880_ICR4_LEN] = {0x01, 0x01, 0x00, 0x00}; |
| |
| ecVal = (channelId == 0) ? VP880_EC_CH1 : VP880_EC_CH2; |
| |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR1_WRT, VP880_ICR1_LEN, icr1); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR2_WRT, VP880_ICR2_LEN, icr2); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR3_WRT, VP880_ICR3_LEN, icr3); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR4_WRT, VP880_ICR4_LEN, icr4); |
| } |
| |
| /* Things will take time to settle after programming switcher. */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = MS_TO_TICKRATE(VP880_CAL_ABV_LONG, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| pDevObj->calData.calDeviceState = VP880_CAL_MEASURE; |
| |
| /* Reset iteration and pass count for next data set. */ |
| pDevObj->calData.iteration = 0; |
| pDevObj->calData.abvData.passCnt = 0; |
| } |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvSetAdc-")); |
| } /* end Vp880AbvSetAdc */ |
| |
| /** |
| * Vp880AbvMeasure() -- Tracker (ABV Cal) only function |
| * This function read switcher value and compare with the value read from the |
| * pcm data if the value is bigger than 1.25v this function will make a |
| * correction voltage. |
| * |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| * Battery calibration registers are adjusted |
| . Channel specific registers are |
| * restored. |
| */ |
| void |
| Vp880AbvMeasure( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| uint8 ecVal; |
| int32 abvError, abvTarget; |
| bool validData; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvMeasure+")); |
| |
| pDevObj->calData.iteration++; |
| |
| if ((pDevObj->stateInt & VP880_IS_SINGLE_CHANNEL) |
| || (pDevObj->stateInt & VP880_LINE1_IS_FXO)) { |
| ecVal = VP880_EC_CH1; |
| } else { |
| ecVal = VP880_EC_CH1 | VP880_EC_CH2; |
| } |
| |
| if (pDevObj->calData.abvData.passCnt == 0) { |
| /* |
| * Exiting this state always requires resetting the timer to keep the |
| * calibration moving along. |
| */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| /* Now we'll switch to channel specific measurements */ |
| /* Read SWY from first channel */ |
| pDevObj->calData.abvData.swyVolt[0] = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH1, VP880_SWITCHER_Y, &validData); |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function and repeat measurement |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| return; |
| } |
| } |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Chan 0 Voltage: SWY %d at time %d", |
| ((pDevObj->calData.abvData.swyVolt[0] * VP880_V_PCM_LSB) / 10000), |
| pDevObj->timeStamp)); |
| |
| /* Now have all data necessary to compute error and adjust channel 0 */ |
| abvTarget = (pDevObj->calData.calSet * 5) + 5; /* Gets it to V scale */ |
| abvTarget *= 1000; |
| abvTarget *= 1000; |
| abvTarget /= VP880_V_PCM_LSB; /* Now we're scaled to the PCM data */ |
| |
| if (pDevObj->calData.iteration >= 10) { |
| pDevObj->calData.abvData.swyVolt[0] = abvTarget; |
| } |
| abvError = abvTarget - |
| (pDevObj->calData.abvData.swyVolt[0] |
| - pDevObj->vp880SysCalData.swyOffset[0]); |
| |
| pDevObj->vp880SysCalData.abvError[0] = (((int16)abvError * VP880_V_PCM_LSB) / 10000); |
| /* |
| * Check if the error is "excessive" - meaning after a max 3.75V |
| * adjustment, it is still more than 30% out of spec. |
| */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Chan 0 Pct Error %li", (ABS((abvError * 100) / abvTarget)))); |
| if ((ABS((abvError * 100) / abvTarget)) > 30) { |
| pDevObj->responseData = VP_CAL_FAILURE; |
| } |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("1. Chan 0 Voltage Error: SWY %d Target Converted %d", |
| pDevObj->vp880SysCalData.abvError[0], |
| (((int16)abvTarget * VP880_V_PCM_LSB) / 10000))); |
| |
| /* Write the correction value to CH1 register. Steps in 1.25V increment */ |
| Vp880BatteryCalAdjust(pDevObj, VP880_EC_CH1); |
| |
| pDevObj->calData.abvData.passCnt = 1; |
| pDevObj->calData.iteration = 0; |
| return; |
| } else if (pDevObj->calData.abvData.passCnt == 1) { |
| if (ecVal == (VP880_EC_CH1 | VP880_EC_CH2)) { |
| /* Read SWZ voltages from second channel */ |
| pDevObj->calData.abvData.swzVolt[1] = |
| Vp880AdcSettling(pDevObj, VP880_EC_CH2, VP880_SWITCHER_Z, &validData); |
| |
| if (validData == FALSE) { |
| /* |
| * Data is bad. Need to reset converter (done automatically in |
| * ADC Settling function) and repeat measurement. |
| */ |
| if (pDevObj->calData.iteration < 10) { |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = |
| MS_TO_TICKRATE(VP880_CAL_ABV_DELAY, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| return; |
| } |
| } |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, ("Chan 1 Voltage: SWZ %d", |
| ((pDevObj->calData.abvData.swzVolt[1] * VP880_V_PCM_LSB) / 10000))); |
| |
| /* Now have all data necessary to compute error and adjust channel 0 */ |
| abvTarget = (pDevObj->calData.calSet * 5) + 5; /* Gets it to V scale */ |
| abvTarget *= 1000; |
| abvTarget *= 1000; |
| abvTarget /= VP880_V_PCM_LSB; /* Now we're scaled to the PCM data */ |
| |
| if (pDevObj->calData.iteration >= 10) { |
| pDevObj->calData.abvData.swzVolt[1] = abvTarget; |
| } |
| |
| abvError = abvTarget - |
| (pDevObj->calData.abvData.swzVolt[1] |
| - pDevObj->vp880SysCalData.swzOffset[1]); |
| |
| pDevObj->vp880SysCalData.abvError[1] = |
| (((int16)abvError * VP880_V_PCM_LSB) / 10000); |
| |
| /* |
| * Check if the error is "excessive" - meaning after a max 3.75V |
| * adjustment, it is still more than 30% out of spec. |
| */ |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Chan 1 Pct Error %li", (ABS((abvError * 100) / abvTarget)))); |
| if ((ABS((abvError * 100) / abvTarget)) > 30) { |
| pDevObj->responseData = VP_CAL_FAILURE; |
| } |
| |
| VP_CALIBRATION(VpDevCtxType, pDevCtx, |
| ("Chan 1 (SWZ): Offset %d Voltage %d Error Raw %d (PCM) Error Converted %d (10mV)", |
| pDevObj->vp880SysCalData.swzOffset[1], |
| pDevObj->calData.abvData.swzVolt[1], |
| (int16)abvError, |
| pDevObj->vp880SysCalData.abvError[1])); |
| |
| /* Write the correction value to CH2 register. Steps in 1.25V increment */ |
| Vp880BatteryCalAdjust(pDevObj, VP880_EC_CH2); |
| } |
| } |
| |
| /* Restore Switching Regulator Parameters */ |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_REGULATOR_PARAM_WRT, |
| VP880_REGULATOR_PARAM_LEN, pDevObj->swParams); |
| VpMemCpy(pDevObj->swParamsCache, pDevObj->swParams, VP880_REGULATOR_PARAM_LEN); |
| |
| pDevObj->calData.calDeviceState = VP880_CAL_DONE; |
| |
| /* Things will take time to settle. */ |
| pDevObj->devTimer[VP_DEV_TIMER_ABV_CAL] = MS_TO_TICKRATE(VP880_CAL_ABV_LONG, |
| pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880AbvMeasure-")); |
| return; |
| } /*end Vp880AbvMeasure */ |
| |
| /** |
| * Vp880CalInit() -- Tracker Only function, called by Vp880CalAbv() |
| * This function initiates a calibration operation for VOC associated with all |
| * the lines of a device. See VP-API reference guide for more information. |
| |
| * Preconditions: |
| * The device and line context must be created and initialized before calling |
| * this function. |
| * |
| * Postconditions: |
| * This function generates an event upon completing the requested action. |
| */ |
| void |
| Vp880CalInit( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| uint8 ecVal = (VP880_EC_CH1 | VP880_EC_CH2); |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880CalInit+")); |
| |
| /* |
| * Make sure device mode is where we need it. All channel specific registers |
| * read (for restore purposes) in functions called by the tick. |
| */ |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| pDevObj->devMode[0] &= ~(VP880_DEV_MODE_TEST_DATA); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_DEV_MODE_WRT, VP880_DEV_MODE_LEN, |
| pDevObj->devMode); |
| } |
| |
| VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("Vp880CalInit-")); |
| } |
| |
| #endif /* VP_CSLAC_RUNTIME_CAL_ENABLED */ |
| #endif /* VP_CC_880_SERIES && VP880_TRACKER_SUPPORT */ |