diff --git a/firmware/Sources/APP/APP_CTRL/app_ctrl.c b/firmware/Sources/APP/APP_CTRL/app_ctrl.c new file mode 100644 index 0000000..7bc37c1 --- /dev/null +++ b/firmware/Sources/APP/APP_CTRL/app_ctrl.c @@ -0,0 +1,344 @@ +/********************************************************************************* +* @file : app_ctrl.c +* @brief : Implementation of APP CTRL +**********************************************************************************/ + +/**********************************************************************************/ +/* Include common and project definition header */ +/**********************************************************************************/ + +#include "app_ctrl.h" + +/**********************************************************************************/ +/* Include other headers */ +/**********************************************************************************/ + +#include "mid_reg.h" //Import MID_REG_info to check type +#include "epc_conf.h" +#include "mid_pwr.h" +#include +/**********************************************************************************/ +/* Definition of imported constant data */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local symbolic constants */ +/**********************************************************************************/ +#define steps_to_ms 10 //10kHz to 1kHz (1ms) + +/**********************************************************************************/ +/* Definition of local function like macros */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local types (typedef, enum, struct, union) */ +/**********************************************************************************/ +typedef enum +{ + limit_reached = 0x0U, + limit_not_reached = 0x01U, + limit_already_reached = 0x02U, + limit_error +} limit_status_e; + +typedef enum +{ + action_charge = 0x0U, + action_discharge = 0x01U, + action_wait = 0x02U, + action_error = 0x03U +} action_e; + + +/**********************************************************************************/ +/* Definition of local variables */ +/**********************************************************************************/ + +static limit_status_e ctrl_status = limit_already_reached; +static action_e ctrl_action = action_wait; +static uint32_t ctrl_time = 0; + +/**********************************************************************************/ +/* Definition of exported variables */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of exported constant data */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of imported constant data */ +/**********************************************************************************/ +extern const MID_REG_info_s EPC_CONF_info; + +/**********************************************************************************/ +/* Declaration of local function prototypes */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local constant data */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local functions */ +/**********************************************************************************/ + +void updateCtrlTime(uint32_t * ctrl_time){ + static uint8_t steps = 0; + steps +=1; + if (steps >= steps_to_ms){ + steps = 0; + ctrl_time +=1; + } +} + +action_e calcNewAction(const MID_REG_control_s * mode, const MID_REG_meas_property_s * meas){ + action_e res = action_error; + switch(mode->mode) + { + case MID_REG_MODE_IDLE: + res = action_wait; + case MID_REG_MODE_WAIT: + res = action_wait; + case MID_REG_MODE_CC: + res = (mode->modeRef >= 0) ? action_charge : action_discharge; + case MID_REG_MODE_CV: + res = (mode->modeRef >= meas->lsVolt) ? action_charge : action_discharge; + case MID_REG_MODE_CP: + res = (mode->modeRef >= 0) ? action_charge : action_discharge; + default: + res = action_wait; + } + return res; +} + +limit_status_e checkLimit(MID_REG_control_s * mode, const MID_REG_meas_property_s * meas, action_e ctrl_action, uint32_t ctrl_time){ + limit_status_e res = limit_not_reached; + //check mode + + // voltage limit, charge + if (mode->limitType == MID_REG_LIMIT_VOLT && ctrl_action == action_charge){ + if (meas->lsVolt >= (uint16_t)mode->limRef){ + res = limit_reached; + } + } // voltage limit, discharge + else if(mode->limitType == MID_REG_LIMIT_VOLT && ctrl_action == action_discharge){ + if (meas->lsVolt <= (uint16_t)mode->limRef){ + res = limit_reached; + } + } // current limit, charge + else if(mode->limitType == MID_REG_LIMIT_CURR && ctrl_action == action_charge){ + if (meas->lsCurr <= (int16_t)mode->limRef){ + res = limit_reached; + } + } // current limit, discharge + else if(mode->limitType == MID_REG_LIMIT_CURR && ctrl_action == action_discharge){ + if (meas->lsCurr >= (int16_t)mode->limRef){ + res = limit_reached; + } + } // power, charge + else if(mode->limitType == MID_REG_LIMIT_PWR && ctrl_action == action_charge){ + int16_t power = (int16_t)((int32_t)(meas->lsCurr * (int16_t)meas->lsVolt) / MID_PWR_TO_dW); + if (power <= (int16_t)mode->limRef){ + res = limit_reached; + } + } // power, discharge + else if(mode->limitType == MID_REG_LIMIT_PWR && ctrl_action == action_discharge){ + int16_t power = (int16_t)((int32_t)(meas->lsCurr * (int16_t)meas->lsVolt) / MID_PWR_TO_dW); + if (power >= (int16_t)mode->limRef){ + res = limit_reached; + } + } // time + else if(mode->limitType == MID_REG_LIMIT_TIME){ + if (ctrl_time >= mode->limRef){ + res = limit_reached; + } + }else if (ctrl_action==action_wait){ + res = limit_not_reached; + }else{ + res = limit_error; + } + + return res; +} + + +/**********************************************************************************/ +/* Definition of exported functions */ +/**********************************************************************************/ + +APP_CTRL_result_e APP_CtrlCheckErrors (MID_REG_error_status_s * errors, + const MID_REG_meas_property_s * meas, const MID_REG_limit_s * limits){ + + APP_CTRL_result_e res = APP_CTRL_RESULT_ERROR_INT; + MID_PWR_result_e internalRes = MID_PWR_RESULT_TIMEOUT; + + //Inverse priority check to set lastErrVal to error with the biggest priority + + // Check body temp + if ((EPC_CONF_info.hwVer%2 == 0) && // TODO: Use function to know if there is sensor present + ((meas->tempBody > limits->tempMax) || (meas->tempBody < limits->tempMin))){ + errors->tempErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = (uint16_t)meas->tempBody; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->tempErr = MID_REG_ERROR_NONE; + } + + // Check anode temp + if ((EPC_CONF_info.hwVer%2 == 0) && // TODO: Use function to know if there is sensor present + ((meas->tempAnod > limits->tempMax) || (meas->tempAnod < limits->tempMin))){ + errors->tempErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = (uint16_t)meas->tempBody; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->tempErr = MID_REG_ERROR_NONE; + } + + // Check amb temp + if ((EPC_CONF_info.hwVer%2 == 0) && // TODO: Use function to know if there is sensor present + ((meas->tempBody > limits->tempMax) || (meas->tempBody < limits->tempMin))){ + errors->tempErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = (uint16_t)meas->tempBody; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->tempErr = MID_REG_ERROR_NONE; + } + + // Check ls current + if (meas->lsCurr > limits->lsCurrMax || meas->lsCurr < limits->lsCurrMin){ + errors->lsCurrErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = (uint16_t)meas->lsCurr; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->lsCurrErr = MID_REG_ERROR_NONE; + } + + // Check ls voltage + if (meas->lsVolt > limits->lsVoltMax || meas->lsVolt < limits->lsVoltMin){ + errors->lsVoltErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = meas->lsVolt; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->lsVoltErr = MID_REG_ERROR_NONE; + } + + // Check hs voltage + if (meas->hsVolt > limits->hsVoltMax || meas->hsVolt < limits->hsVoltMin){ + errors->hsVoltErr = MID_REG_ERROR_RAISED; + errors->lastErrVal = meas->hsVolt; + res = APP_CTRL_RESULT_ERROR_RAISED; + }else{ + errors->hsVoltErr = MID_REG_ERROR_NONE; + } + + // Check internal error flag && comm flag to stop conversion if raised in other modules + if (errors->commErr == MID_REG_ERROR_RAISED || errors->intErr == MID_REG_ERROR_RAISED){ + res = APP_CTRL_RESULT_ERROR_RAISED; + } + + // Disable power conversion if any error is raised + if (res == APP_CTRL_RESULT_ERROR_RAISED){ + internalRes = MID_PwrSetOutput(MID_PWR_Disable); + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_ERROR_RAISED : APP_CTRL_RESULT_ERROR_INT; + }else{ + res = APP_CTRL_RESULT_SUCCESS; + } + + return res; +} + +APP_CTRL_result_e APP_CtrlUpdate (MID_REG_control_s * mode, const MID_REG_meas_property_s * meas, + const MID_REG_limit_s * limits){ + APP_CTRL_result_e res = APP_CTRL_RESULT_ERROR_INT; + MID_PWR_result_e internalRes = MID_PWR_RESULT_SUCCESS; + updateCtrlTime(&ctrl_time); + ctrl_status = checkLimit(mode, meas, ctrl_action, ctrl_time); + + if (ctrl_status == limit_not_reached && internalRes == MID_PWR_RESULT_SUCCESS){ + //check if limit is reached, if not apply control + switch(mode->mode) + { + case MID_REG_MODE_WAIT: + // If change output to disable change also register to disable + internalRes = MID_PwrSetOutput(MID_PWR_Disable); + mode->outStatus = MID_PWR_Disable; + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + break; + case MID_REG_MODE_CV: + internalRes = MID_PwrApplyCtrl(mode->modeRef, meas->lsVolt, meas->lsCurr, MID_PWR_MODE_CV, *limits); + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + break; + case MID_REG_MODE_CC: + internalRes = MID_PwrApplyCtrl(mode->modeRef, meas->lsVolt, meas->lsCurr, MID_PWR_MODE_CC, *limits); + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + break; + case MID_REG_MODE_CP: + internalRes = MID_PwrApplyCtrl(mode->modeRef, meas->lsVolt, meas->lsCurr, MID_PWR_MODE_CP, *limits); + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + break; + default: + // If change output to disable change also register to disable + internalRes = MID_PwrSetOutput(MID_PWR_Disable); + mode->outStatus = MID_PWR_Disable; + res = APP_CTRL_RESULT_ERROR_INT; + break; + } + }else if(ctrl_status == limit_reached && internalRes == MID_PWR_RESULT_SUCCESS){ + + MID_REG_control_s newMode = { + MID_REG_DISABLED, // outStatus + MID_REG_MODE_IDLE, // mode + MID_REG_LIMIT_TIME, // limitType + 0, // modeRef + 0 // limRef + }; + //First change output + // If change output to disable change also register to disable + internalRes = MID_PwrSetOutput(MID_PWR_Disable); + mode->outStatus = MID_PWR_Disable; + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + //Then change the mode + if (res == APP_CTRL_RESULT_SUCCESS){ + res = APP_CtrlApplyNewMode(&newMode, mode, meas); + ctrl_status = limit_already_reached; + } + }else if(ctrl_status == limit_already_reached){ + //If no operation needed is success + res = APP_CTRL_RESULT_SUCCESS; + }else{ + res = APP_CTRL_RESULT_ERROR_INT; + } + + return res; +} + +APP_CTRL_result_e APP_CtrlApplyNewMode (const MID_REG_control_s * newMode, MID_REG_control_s * mode, + const MID_REG_meas_property_s * meas){ + APP_CTRL_result_e res = APP_CTRL_RESULT_ERROR_INT; + MID_PWR_result_e internalRes = MID_PWR_RESULT_TIMEOUT; + if (newMode->outStatus == MID_REG_DISABLED){ + internalRes = MID_PwrSetOutput(MID_PWR_Disable); + } + //ctrl_action will receive if mode is charging or discharging + ctrl_action = calcNewAction(newMode, meas); + ctrl_status = limit_not_reached; + ctrl_time = 0; + memcpy(mode, newMode, sizeof(MID_REG_control_s)); + //Enable PWR output for the modes that require power transfer + if ((newMode->mode == MID_REG_MODE_CV || newMode->mode == MID_REG_MODE_CP || newMode->mode == MID_REG_MODE_CC) && + newMode->outStatus == MID_REG_ENABLED){ + //Update DO when new mode is applied + internalRes = MID_PwrCalculateD0(meas->hsVolt, meas->lsVolt); + if (internalRes == MID_PWR_RESULT_SUCCESS){ + internalRes = MID_PwrSetOutput(MID_PWR_Enable); + } + }else { + //If not in control mode, the output is always disabled + mode->outStatus == MID_REG_DISABLED; + } + res = (internalRes == MID_PWR_RESULT_SUCCESS) ? APP_CTRL_RESULT_SUCCESS : APP_CTRL_RESULT_ERROR_INT; + + return res; +} + diff --git a/firmware/Sources/APP/APP_CTRL/app_ctrl.h b/firmware/Sources/APP/APP_CTRL/app_ctrl.h new file mode 100644 index 0000000..2e1b69a --- /dev/null +++ b/firmware/Sources/APP/APP_CTRL/app_ctrl.h @@ -0,0 +1,92 @@ +/********************************************************************************* +* @file : app_ctrl.h +* @brief : APP header file for CONTROL of EPC +**********************************************************************************/ + +#ifndef APP_CTRL_H_ +#define APP_CTRL_H_ + +/**********************************************************************************/ +/* Project Includes */ +/**********************************************************************************/ + +#include "mid_reg.h" +/**********************************************************************************/ +/* Include other headers */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local symbolic constants */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local function like macros */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of exported types (typedef, enum, struct, union) */ +/**********************************************************************************/ + +/** + * @enum APP_CTRL_result_e + * @brief Structure for the result of the APP CTRL operations. + */ +typedef enum +{ + APP_CTRL_RESULT_SUCCESS = 0x0U, /**< APP_CTRL success operation result **/ + APP_CTRL_RESULT_ERROR_RAISED = 0x01U, /**< APP_CTRL error found**/ + APP_CTRL_RESULT_ERROR_INT = 0x02U /**< APP_CTRL error in calculus **/ +} APP_CTRL_result_e; + +/**********************************************************************************/ +/* Definition of exported variables */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of local constant data */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of exported variables */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Definition of exported constant data */ +/**********************************************************************************/ + +/**********************************************************************************/ +/* Declaration of exported function prototypes */ +/**********************************************************************************/ + + +/** + * @fn APP_CTRL_result_e APP_CtrlCheckErrors(MID_REG_error_status_s, + * MID_REG_meas_property_s, MID_REG_limit_s ) + * @brief Check if there is some value in measures above or below limits and updates + * the error register + * @return @ref APP_CTRL_RESULT_SUCCESS if no errors, APP_CTRL_RESULT_ERROR_RAISED if + * errors found and APP_CTRL_RESULT_ERROR_INT if any error in comparison + */ +APP_CTRL_result_e APP_CtrlCheckErrors (MID_REG_error_status_s *, const MID_REG_meas_property_s *, const MID_REG_limit_s *); + +/** + * @fn APP_CTRL_result_e APP_CtrlUpdate(MID_REG_control_s, + * MID_REG_meas_property_s, MID_REG_limit_s ) + * @brief Update internal PIs and apply control to PWM. If limit is reached, stop PWM + * @return @ref APP_CTRL_RESULT_SUCCESS if no errors, APP_CTRL_RESULT_ERROR_INT if + * errors in calculus or MID_PWR results + */ +APP_CTRL_result_e APP_CtrlUpdate (MID_REG_control_s *, const MID_REG_meas_property_s *, const MID_REG_limit_s *); + +/** + * @fn APP_CTRL_result_e APP_CtrlApplyNewMode(MID_REG_control_s, + * MID_REG_control_s) + * @brief Reset ctrl internal variables and set the module to apply the new control mode + * @return @ref APP_CTRL_RESULT_SUCCESS if no errors, APP_CTRL_RESULT_ERROR_INT if + * any error + */ +APP_CTRL_result_e APP_CtrlApplyNewMode (const MID_REG_control_s *, MID_REG_control_s *, const MID_REG_meas_property_s *); + + + +#endif /* APP_APP_CTRL_APP_CTRL_H_ */