/*------------------------------------------------------------------------------------------------*/ /* UNICENS V2.1.0-3491 */ /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */ /* */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /* */ /* You may also obtain this software under a propriety license from Microchip. */ /* Please contact Microchip for further information. */ /*------------------------------------------------------------------------------------------------*/ /*! * \file * \brief Implementation of the I2C Module. * * \cond UCS_INTERNAL_DOC * \addtogroup G_I2C * @{ */ /*------------------------------------------------------------------------------------------------*/ /* Includes */ /*------------------------------------------------------------------------------------------------*/ #include "ucs_i2c.h" #include "ucs_misc.h" /*------------------------------------------------------------------------------------------------*/ /* Internal prototypes */ /*------------------------------------------------------------------------------------------------*/ static void I2c_PortCreateResCb(void *self, void *result_ptr); static void I2c_PortWriteResCb(void *self, void *result_ptr); static void I2c_PortReadResCb(void *self, void *result_ptr); static void I2c_TriggerEventStatusCb(void *self, void *result_ptr); static bool I2c_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self); static void I2c_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr); static void I2c_PortRead_Result(void *self, Msg_MostTel_t *msg_ptr); static void I2c_PortWrite_Result(void *self, Msg_MostTel_t *msg_ptr); static void I2c_RxError(void *self, Msg_MostTel_t *msg_ptr, I2c_ErrResultCb_t res_cb_fptr); static void I2c_NsmResultCb(void * self, Nsm_Result_t result); /*------------------------------------------------------------------------------------------------*/ /* Implementation of class I2C */ /*------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------*/ /* Initialization Methods */ /*------------------------------------------------------------------------------------------------*/ /*! \brief Constructor of the I2C class. * \param self Instance pointer * \param init_ptr init data_ptr */ void I2c_Ctor(CI2c * self, I2c_InitData_t * init_ptr) { MISC_MEM_SET(self, 0, sizeof(CI2c)); /* Set class instances */ self->inic_ptr = init_ptr->inic_ptr; self->base_ptr = self->inic_ptr->base_ptr; self->nsm_ptr = init_ptr->nsm_ptr; self->curr_user_data.i2c_interrupt_report_fptr = init_ptr->i2c_interrupt_report_fptr; /* Init GPIOTriggerEvent observer */ Obs_Ctor(&self->triggerevent_observer, self, &I2c_TriggerEventStatusCb); /* Subscribe Observers */ Inic_AddObsrvGpioTriggerEvent(self->inic_ptr, &self->triggerevent_observer); /* Set device id */ self->device_address = Inic_GetTargetAddress(self->inic_ptr); } /*------------------------------------------------------------------------------------------------*/ /* Service Functions */ /*------------------------------------------------------------------------------------------------*/ /*! \brief Creates an I2C Port with its associated parameter. * \param self Reference to CI2c instance * \param index I2C Port instance * \param speed The speed grade of the I2C Port * \param i2c_int_mask The I2C interrupt pin mask on the GPIO Port. * \param res_fptr Required result callback function pointer. * \return Possible return values are shown in the table below. * Value | Description * --------------------------- | ------------------------------------ * UCS_RET_SUCCESS | No error * UCS_RET_ERR_PARAM | At least one parameter is wrong * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available * UCS_RET_ERR_API_LOCKED | API is currently locked */ Ucs_Return_t I2c_CreatePort(CI2c * self, uint8_t index, Ucs_I2c_Speed_t speed, uint8_t i2c_int_mask, Ucs_I2c_CreatePortResCb_t res_fptr) { Ucs_Return_t result = UCS_RET_ERR_PARAM; uint8_t address = 0x00U; /* Address will be ignored */ uint8_t mode = 0x01U; /* Master Mode */ if ((NULL != self) && (NULL != res_fptr)) { result = UCS_RET_ERR_API_LOCKED; if (!Nsm_IsLocked(self->nsm_ptr)) { I2c_Script_t * tmp_script = &self->curr_script; /* Set Data */ tmp_script->cfg_data[0] = index; tmp_script->cfg_data[1] = address; tmp_script->cfg_data[2] = mode; tmp_script->cfg_data[3] = (uint8_t)speed; /* Set message id */ tmp_script->cfg_msg.FBlockId = FB_INIC; tmp_script->cfg_msg.InstId = 0U; tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_CREATE; tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT; tmp_script->cfg_msg.DataLen = 4U; tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0]; /* Set script */ tmp_script->script.send_cmd = &tmp_script->cfg_msg; tmp_script->script.pause = 0U; /* Transmit script */ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb); if(result == UCS_RET_SUCCESS) { self->curr_user_data.int_pin_mask = i2c_int_mask; self->curr_user_data.portcreate_res_cb = res_fptr; self->curr_res_cb = &I2c_PortCreateResCb; } } } return result; } /*! \brief Writes a block of bytes to an I2C device at a specified I2C address. * \param self Reference to CI2c instance * \param port_handle Port resource handle * \param mode The write transfer mode * \param block_count The number of blocks to be written to the I2C address. * \param slave_address The 7-bit I2C slave address of the peripheral to be read * \param timeout The timeout for the I2C Port write * \param data_len Number of bytes to be written to the addressed I2C peripheral * \param data_ptr Reference to the data to be written * \param res_fptr Required result callback function pointer. * \return Possible return values are shown in the table below. * Value | Description * --------------------------- | ------------------------------------ * UCS_RET_SUCCESS | No error * UCS_RET_ERR_PARAM | At least one parameter is wrong * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available * UCS_RET_ERR_API_LOCKED | API is currently locked */ Ucs_Return_t I2c_WritePort(CI2c * self, uint16_t port_handle, Ucs_I2c_TrMode_t mode, uint8_t block_count, uint8_t slave_address, uint16_t timeout, uint8_t data_len, uint8_t data_ptr[], Ucs_I2c_WritePortResCb_t res_fptr) { Ucs_Return_t result = UCS_RET_ERR_PARAM; if ((NULL != self) && (NULL != res_fptr)) { if ((0U < data_len) && (NULL != data_ptr)) { result = UCS_RET_ERR_API_LOCKED; if (!Nsm_IsLocked(self->nsm_ptr)) { bool is_ok = true; result = UCS_RET_ERR_PARAM; if ((UCS_I2C_BURST_MODE == mode) && (0U == block_count)) { is_ok = false; } if (is_ok) { uint8_t i; I2c_Script_t * tmp_script = &self->curr_script; for (i = 0U; i < data_len; i++) { tmp_script->cfg_data[8U + i] = data_ptr[i]; } /* Set Data */ tmp_script->cfg_data[0] = MISC_HB(port_handle); tmp_script->cfg_data[1] = MISC_LB(port_handle); tmp_script->cfg_data[2] = (uint8_t)mode; tmp_script->cfg_data[3] = block_count; tmp_script->cfg_data[4] = slave_address; tmp_script->cfg_data[5] = (mode == UCS_I2C_BURST_MODE) ? (data_len/block_count):data_len; tmp_script->cfg_data[6] = MISC_HB(timeout); tmp_script->cfg_data[7] = MISC_LB(timeout); /* Set message id */ tmp_script->cfg_msg.FBlockId = FB_INIC; tmp_script->cfg_msg.InstId = 0U; tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_WRITE; tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT; tmp_script->cfg_msg.DataLen = data_len + 8U; tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0]; /* Set script */ tmp_script->script.send_cmd = &tmp_script->cfg_msg; tmp_script->script.pause = 0U; /* Transmit script */ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb); if(result == UCS_RET_SUCCESS) { self->curr_user_data.portwrite_res_cb = res_fptr; self->curr_res_cb = &I2c_PortWriteResCb; } } } } } return result; } /*! \brief Reads a block of bytes from an I2C device at a specified I2C address. * \param self Reference to CI2c instance * \param port_handle Port resource handle * \param slave_address The 7-bit I2C slave address of the peripheral to be read * \param data_len Number of bytes to be read from the address * \param timeout The timeout for the I2C Port read * \param res_fptr Required result callback function pointer. * \return Possible return values are shown in the table below. * Value | Description * --------------------------- | ------------------------------------ * UCS_RET_SUCCESS | No error * UCS_RET_ERR_PARAM | At least one parameter is wrong * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available * UCS_RET_ERR_API_LOCKED | API is currently locked */ Ucs_Return_t I2c_ReadPort(CI2c * self, uint16_t port_handle, uint8_t slave_address, uint8_t data_len, uint16_t timeout, Ucs_I2c_ReadPortResCb_t res_fptr) { Ucs_Return_t result = UCS_RET_ERR_PARAM; if ((NULL != self) && (NULL != res_fptr)) { result = UCS_RET_ERR_API_LOCKED; if (!Nsm_IsLocked(self->nsm_ptr)) { I2c_Script_t * tmp_script = &self->curr_script; /* Set Data */ tmp_script->cfg_data[0] = MISC_HB(port_handle); tmp_script->cfg_data[1] = MISC_LB(port_handle); tmp_script->cfg_data[2] = slave_address; tmp_script->cfg_data[3] = data_len; tmp_script->cfg_data[4] = MISC_HB(timeout); tmp_script->cfg_data[5] = MISC_LB(timeout); /* Set message id */ tmp_script->cfg_msg.FBlockId = FB_INIC; tmp_script->cfg_msg.InstId = 0U; tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_READ; tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT; tmp_script->cfg_msg.DataLen = 6U; tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0]; /* Set script */ tmp_script->script.send_cmd = &tmp_script->cfg_msg; tmp_script->script.pause = 0U; /* Transmit script */ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb); if(result == UCS_RET_SUCCESS) { self->curr_user_data.portread_res_cb = res_fptr; self->curr_res_cb = &I2c_PortReadResCb; } } } return result; } /*------------------------------------------------------------------------------------------------*/ /* Private Methods */ /*------------------------------------------------------------------------------------------------*/ /*! \brief Handles the result of the I2CPortCreate.StartResultAck * \param self Instance pointer * \param result_ptr result pointer */ static void I2c_PortCreateResCb(void *self, void *result_ptr) { CI2c *self_ = (CI2c *)self; uint16_t i2c_port_handle; Ucs_I2c_Result_t res; Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr; /* Init result */ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t)); if (NULL != result_ptr_) { i2c_port_handle = 0U; res.code = UCS_I2C_RES_ERR_CMD; res.details.result_type = UCS_I2C_RESULT_TYPE_TGT; res.details.inic_result = result_ptr_->result; if (result_ptr_->data_info != NULL) { if(result_ptr_->result.code == UCS_RES_SUCCESS) { res.code = UCS_I2C_RES_SUCCESS; i2c_port_handle = *(uint16_t *)result_ptr_->data_info; } else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION) { res.details.result_type = UCS_I2C_RESULT_TYPE_TX; res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info); } else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION) { res.code = UCS_I2C_RES_ERR_SYNC; } } if (NULL != self_->curr_user_data.portcreate_res_cb) { self_->curr_user_data.portcreate_res_cb(self_->device_address, i2c_port_handle, res, self_->base_ptr->ucs_user_ptr); } } } /*! \brief Handles the result of the I2CPortWrite.StartResultAck * \param self Instance pointer * \param result_ptr result pointer */ static void I2c_PortWriteResCb(void *self, void *result_ptr) { CI2c *self_ = (CI2c *)self; Inic_I2cWriteResStatus_t wr_res; Ucs_I2c_Result_t res; Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr; /* Init result */ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t)); if (NULL != result_ptr_) { wr_res.data_len = 0U; wr_res.port_handle = 0U; wr_res.slave_address = 0U; res.code = UCS_I2C_RES_ERR_CMD; res.details.result_type = UCS_I2C_RESULT_TYPE_TGT; res.details.inic_result = result_ptr_->result; if (result_ptr_->data_info != NULL) { if(result_ptr_->result.code == UCS_RES_SUCCESS) { res.code = UCS_I2C_RES_SUCCESS; wr_res = *(Inic_I2cWriteResStatus_t *)result_ptr_->data_info; } else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION) { res.details.result_type = UCS_I2C_RESULT_TYPE_TX; res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info); } else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION) { res.code = UCS_I2C_RES_ERR_SYNC; } } if (NULL != self_->curr_user_data.portwrite_res_cb) { self_->curr_user_data.portwrite_res_cb(self_->device_address, wr_res.port_handle, wr_res.slave_address, wr_res.data_len, res, self_->base_ptr->ucs_user_ptr); } } } /*! \brief Handles the result of the I2CPortRead.StartResultAck * \param self Instance pointer * \param result_ptr result pointer */ static void I2c_PortReadResCb(void *self, void *result_ptr) { CI2c *self_ = (CI2c *)self; Inic_I2cReadResStatus_t read_res; Ucs_I2c_Result_t res; Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr; /* Init result */ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t)); if (NULL != result_ptr_) { read_res.data_len = 0U; read_res.data_ptr = NULL; read_res.port_handle = 0U; read_res.slave_address = 0U; res.code = UCS_I2C_RES_ERR_CMD; res.details.result_type = UCS_I2C_RESULT_TYPE_TGT; res.details.inic_result = result_ptr_->result; if (result_ptr_->data_info != NULL) { if(result_ptr_->result.code == UCS_RES_SUCCESS) { res.code = UCS_I2C_RES_SUCCESS; read_res = *(Inic_I2cReadResStatus_t *)result_ptr_->data_info; } else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION) { res.details.result_type = UCS_I2C_RESULT_TYPE_TX; res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info); } else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION) { res.code = UCS_I2C_RES_ERR_SYNC; } } if (NULL != self_->curr_user_data.portread_res_cb) { self_->curr_user_data.portread_res_cb(self_->device_address, read_res.port_handle, read_res.slave_address, read_res.data_len, read_res.data_ptr, res, self_->base_ptr->ucs_user_ptr); } } } /*! \brief Handles the result of the GPIOPortTriggerEvent.Status * \param self Instance pointer * \param result_ptr result pointer */ static void I2c_TriggerEventStatusCb(void *self, void *result_ptr) { CI2c *self_ = (CI2c *)self; Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr; if ((NULL != result_ptr_) && (NULL != self_->curr_user_data.i2c_interrupt_report_fptr)) { Inic_GpioTriggerEventStatus_t status; uint16_t int_mask = self_->curr_user_data.int_pin_mask; status = *(Inic_GpioTriggerEventStatus_t *)result_ptr_->data_info; if ((!status.is_first_report) && ((int_mask == (status.rising_edges & int_mask)) || (int_mask == (status.levels & int_mask)) || (int_mask == (status.falling_edges & int_mask)))) { self_->curr_user_data.i2c_interrupt_report_fptr(self_->device_address, self_->base_ptr->ucs_user_ptr); } } } /*! \brief Checks whether the incoming is our message and handles It if it's. * \param tel_ptr Reference to the message object. * \param self Reference to the user argument. * \return Returns \c true to discard the message and free it to the pool if it's our message. Otherwise, returns * \c false. */ static bool I2c_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self) { CI2c *self_ = (CI2c *)self; bool ret_val = true; if ((tel_ptr != NULL) && (tel_ptr->id.function_id == self_->curr_script.script.send_cmd->FunktId)) { if (tel_ptr->id.op_type == UCS_OP_RESULT) { switch(tel_ptr->id.function_id) { case INIC_FID_I2C_PORT_CREATE: I2c_PortCreate_Result(self_, tel_ptr); break; case INIC_FID_I2C_PORT_READ: I2c_PortRead_Result(self_, tel_ptr); break; case INIC_FID_I2C_PORT_WRITE: I2c_PortWrite_Result(self_, tel_ptr); break; default: ret_val = false; break; } } else if (tel_ptr->id.op_type == UCS_OP_ERROR) { I2c_ErrResultCb_t res_cb_fptr = self_->curr_res_cb; I2c_RxError(self_, tel_ptr, res_cb_fptr); } } else { ret_val = false; } return ret_val; } /*! \brief Result callback function for NSM result. Whenever this function is called the NodeScripting has finished the * script's execution. This function handles transmission and sync error. Only these two kind of errors can occur. * \param self Reference to the called user instance. * \param result Result of the scripting operation. */ static void I2c_NsmResultCb(void * self, Nsm_Result_t result) { CI2c *self_ = (CI2c *)self; if (self_ != NULL) { Inic_StdResult_t res_data; bool allow_report = false; if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TX)) { res_data.data_info = &result.details.tx_result; res_data.result.code = UCS_RES_ERR_TRANSMISSION; res_data.result.info_ptr = NULL; res_data.result.info_size = 0U; allow_report = true; } else if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TGT_SYNC)) { res_data.data_info = &result.details.inic_result; res_data.result.code = result.details.inic_result.code; res_data.result.info_ptr = result.details.inic_result.info_ptr; res_data.result.info_size = result.details.inic_result.info_size; allow_report = true; } else if ((result.code == UCS_NS_RES_ERROR) && ((result.details.tx_result == UCS_MSG_STAT_OK) || (result.details.inic_result.code == UCS_RES_SUCCESS))) { res_data.data_info = NULL; res_data.result.code = UCS_RES_ERR_TIMEOUT; res_data.result.info_ptr = NULL; res_data.result.info_size = 0U; TR_ERROR((self_->base_ptr->ucs_user_ptr, "[I2C]", "TIMEOUT ERROR occurred for currently I2C command. No response received from target device with address 0x%X.", 1U, self_->device_address)); } if ((self_->curr_res_cb != NULL) && (allow_report)) { self_->curr_res_cb(self_, &res_data); } } } /*---------------------------------- GW Functions ----------------------------------*/ /*! \brief Error Handler function for all I2C methods * \param self Reference to CI2c instance * \param msg_ptr Pointer to received message * \param res_cb_fptr Pointer to a specified error handler function */ static void I2c_RxError(void *self, Msg_MostTel_t *msg_ptr, I2c_ErrResultCb_t res_cb_fptr) { CI2c *self_ = (CI2c *)self; Inic_StdResult_t res_data; res_data.data_info = NULL; res_data.result = Inic_TranslateError(self_->inic_ptr, &msg_ptr->tel.tel_data_ptr[0], (msg_ptr->tel.tel_len)); if (res_cb_fptr != NULL) { res_cb_fptr(self_, &res_data); } } /*! \brief Handler function for I2CPortCreate.ResultAck * \details Element res_data.data_info points to the variable i2c_port_handle which holds the * I2C Port resource handle. * \param self Reference to CI2c instance * \param msg_ptr Pointer to received message */ static void I2c_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr) { CI2c *self_ = (CI2c *)self; uint16_t i2c_port_handle; Inic_StdResult_t res_data; MISC_DECODE_WORD(&i2c_port_handle, &(msg_ptr->tel.tel_data_ptr[0])); res_data.data_info = &i2c_port_handle; res_data.result.code = UCS_RES_SUCCESS; res_data.result.info_ptr = NULL; I2c_PortCreateResCb(self_, &res_data); } /*! \brief Handler function for I2CPortRead.ResultAck * \details Element res_data.data_info points to a variable of type Inic_I2cReadResStatus_t which holds the * the results of the I2CPortRead.StartResultAck command. * \param self Reference to CI2c instance * \param msg_ptr Pointer to received message */ static void I2c_PortRead_Result(void *self, Msg_MostTel_t *msg_ptr) { CI2c *self_ = (CI2c *)self; Inic_I2cReadResStatus_t i2c_read_res; Inic_StdResult_t res_data; res_data.data_info = &i2c_read_res; res_data.result.code = UCS_RES_SUCCESS; res_data.result.info_ptr = NULL; MISC_DECODE_WORD(&i2c_read_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0])); i2c_read_res.slave_address = msg_ptr->tel.tel_data_ptr[2]; i2c_read_res.data_len = msg_ptr->tel.tel_data_ptr[3]; i2c_read_res.data_ptr = &msg_ptr->tel.tel_data_ptr[4]; I2c_PortReadResCb(self_, &res_data); } /*! \brief Handler function for I2CPortWrite.ResultAck * \details Element res_data.data_info points to a variable of type Inic_I2cWriteResStatus_t which holds the * the results of the I2CPortWrite.StartResultAck command. * \param self Reference to CI2c instance * \param msg_ptr Pointer to received message */ static void I2c_PortWrite_Result(void *self, Msg_MostTel_t *msg_ptr) { CI2c *self_ = (CI2c *)self; Inic_I2cWriteResStatus_t i2c_write_res; Inic_StdResult_t res_data; res_data.data_info = &i2c_write_res; res_data.result.code = UCS_RES_SUCCESS; res_data.result.info_ptr = NULL; MISC_DECODE_WORD(&i2c_write_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0])); i2c_write_res.slave_address = msg_ptr->tel.tel_data_ptr[2]; i2c_write_res.data_len = msg_ptr->tel.tel_data_ptr[3]; I2c_PortWriteResCb(self_, &res_data); } /*! * @} * \endcond */ /*------------------------------------------------------------------------------------------------*/ /* End of file */ /*------------------------------------------------------------------------------------------------*/