/** ********************************************************************************* * * @file ald_i2c.c * @brief I2C module driver. * * @version V1.0 * @date 24 Feb. 2023 * @author AE Team * @note * Change Logs: * Date Author Notes * 24 Feb. 2023 Lisq The first version * * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************** * @verbatim =============================================================================== ##### How to use this driver ##### =============================================================================== [..] The I2C driver can be used as follows: (#) Declare a i2c_handle_t handle structure, for example: i2c_handle_t hperh; (#) Configure the Communication Speed, Addressing mode, Own Address1, Dual Addressing mode, Own Address2, General call and Nostretch mode in the hperh init structure. (#) Initialize the I2C registers by calling the ald_i2c_init(). (#) For I2C IO and IO MEM operations, three operation modes are available within this driver : *** Polling mode IO operation *** ================================= [..] (+) Transmit in master mode an amount of data in blocking mode using ald_i2c_master_send() (+) Receive in master mode an amount of data in blocking mode using ald_i2c_master_recv() (+) Transmit in slave mode an amount of data in blocking mode using ald_i2c_slave_send() (+) Receive in slave mode an amount of data in blocking mode using ald_i2c_slave_recv() *** Polling mode IO MEM operation *** ===================================== [..] (+) Write an amount of data in blocking mode to a specific memory address using ald_i2c_mem_write() (+) Read an amount of data in blocking mode from a specific memory address using ald_i2c_mem_read() *** Interrupt mode IO operation *** =================================== [..] (+) The I2C interrupts should have the highest priority in the application in order to make them uninterruptible. (+) Transmit in master mode an amount of data in non-blocking mode using ald_i2c_master_send_by_it() (+) At transmission end of transfer, hperh->master_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->master_tx_cplt_cbk() (+) Receive in master mode an amount of data in non-blocking mode using ald_i2c_master_recv_by_it() (+) At reception end of transfer, hperh->master_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->master_rx_cplt_cbk() (+) Transmit in slave mode an amount of data in non-blocking mode using ald_i2c_slave_send_by_it() (+) At transmission end of transfer, hperh->slave_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->slave_tx_cplt_cbk() (+) Receive in slave mode an amount of data in non-blocking mode using ald_i2c_slave_recv_by_it() (+) At reception end of transfer, hperh->slave_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->slave_rx_cplt_cbk() (+) In case of transfer Error, hperh->error_callback() function is executed and user can add his own code by customization of function pointer hperh->error_callback() *** Interrupt mode IO MEM operation *** ======================================= [..] (+) The I2C interrupts should have the highest priority in the application in order to make them uninterruptible. (+) Write an amount of data in non-blocking mode with Interrupt to a specific memory address using ald_i2c_mem_write_by_it() (+) At Memory end of write transfer, hperh->mem_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->mem_tx_cplt_cbk() (+) Read an amount of data in non-blocking mode with Interrupt from a specific memory address using ald_i2c_mem_read_by_it() (+) At Memory end of read transfer, hperh->mem_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->mem_rx_cplt_cbk() (+) In case of transfer Error, hperh->error_callback() function is executed and user can add his own code by customization of function pointer hperh->error_callback() *** DMA mode IO operation *** ============================== [..] (+) Transmit in master mode an amount of data in non-blocking mode (DMA) using ald_i2c_master_send_by_dma() (+) At transmission end of transfer, hperh->master_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->master_tx_cplt_cbk() (+) Receive in master mode an amount of data in non-blocking mode (DMA) using ald_i2c_master_recv_by_dma() (+) At reception end of transfer, hperh->master_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->master_rx_cplt_cbk() (+) Transmit in slave mode an amount of data in non-blocking mode (DMA) using ald_i2c_slave_send_by_dma() (+) At transmission end of transfer, hperh->slave_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->slave_tx_cplt_cbk() (+) Receive in slave mode an amount of data in non-blocking mode (DMA) using ald_i2c_slave_recv_by_dma() (+) At reception end of transfer, hperh->slave_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->slave_rx_cplt_cbk() (+) In case of transfer Error, hperh->error_callback() function is executed and user can add his own code by customization of function pointer hperh->error_callback() *** DMA mode IO MEM operation *** ================================= [..] (+) Write an amount of data in non-blocking mode with DMA to a specific memory address using ald_i2c_mem_write_by_dma() (+) At Memory end of write transfer, hperh->mem_tx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->mem_tx_cplt_cbk() (+) Read an amount of data in non-blocking mode with DMA from a specific memory address using ald_i2c_mem_read_by_dma() (+) At Memory end of read transfer, hperh->mem_rx_cplt_cbk() is executed and user can add his own code by customization of function pointer hperh->mem_rx_cplt_cbk() (+) In case of transfer Error, hperh->error_callback() function is executed and user can add his own code by customization of function pointer hperh->error_callback() *** I2C ald_status_t driver macros list *** ================================== [..] Below the list of most used macros in I2C ald_status_t driver. (+) __I2C_ENABLE: Enable the I2C peripheral (+) __I2C_DISABLE: Disable the I2C peripheral (+) I2C_GET_FLAG: Check whether the specified I2C flag is set or not (+) I2C_ENABLE_IT: Enable the specified I2C interrupt (+) I2C_DISABLE_IT: Disable the specified I2C interrupt (@) You can refer to the I2C ald_status_t driver header file for more useful macros *** I2C Workarounds linked to Silicon Limitation *** ==================================================== [..] Below the list of all silicon limitations implemented for library on our product. (@) See ErrataSheet to know full silicon limitation list of your product. (#) Workarounds Implemented inside I2C library (##) Wrong data read into data register (Polling and Interrupt mode) (##) Start cannot be generated after a misplaced Stop (##) Some software events must be managed before the current byte is being transferred: Workaround: Use DMA in general, except when the Master is receiving a single byte. For Interupt mode, I2C should have the highest priority in the application. (##) Mismatch on the "Setup time for a repeated Start condition" timing parameter: Workaround: Reduce the frequency down to 88 kHz or use the I2C Fast-mode if supported by the slave. (##) Data valid time (tVD;DAT) violated without the OVR flag being set: Workaround: If the slave device allows it, use the clock stretching mechanism by programming no_stretch = I2C_NOSTRETCH_DISABLE in ald_i2c_init. @endverbatim ********************************************************************************* */ #include "ald_i2c.h" /** @addtogroup ES32VF2264_ALD * @{ */ /** @defgroup I2C I2C * @brief I2C module driver * @{ */ /** @addtogroup I2C_Private_Constants I2C Private Constants * @{ */ #define I2C_TIMEOUT_FLAG (10) #define I2C_TIMEOUT_ADDR_SLAVE (10) #define I2C_TIMEOUT_BUSY_FLAG (10) #define I2C_MAX_DELAY 0xFFFFFFFF /** * @} */ /** @addtogroup I2C_Private_Functions I2C Private Functions * @{ */ static void i2c_dma_master_send_cplt(void *argv); static void i2c_dma_master_recv_cplt(void *argv); static void i2c_dma_slave_send_cplt(void *argv); static void i2c_dma_slave_recv_cplt(void *argv); static void i2c_dma_mem_send_cplt(void *argv); static void i2c_dma_mem_recv_cplt(void *argv); static ald_status_t i2c_req_mem_read(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout); static ald_status_t i2c_master_req_write(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout); static ald_status_t i2c_master_req_read(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout); static ald_status_t i2c_req_mem_write(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout); static ald_status_t i2c_wait_flag_change_to_timeout(ald_i2c_handle_t *hperh, uint32_t flag, flag_status_t status, uint32_t timeout); static ald_status_t i2c_wait_master_addr_to_timeout(ald_i2c_handle_t *hperh, uint32_t flag, uint32_t timeout); static ald_status_t i2c_wait_txe_to_timeout(ald_i2c_handle_t *hperh, uint32_t timeout); static ald_status_t i2c_wait_rxne_to_timeout(ald_i2c_handle_t *hperh, uint32_t timeout); static ald_status_t i2c_master_send_tc(ald_i2c_handle_t *hperh); static ald_status_t i2c_master_send_txe(ald_i2c_handle_t *hperh); static ald_status_t i2c_master_recv_tc(ald_i2c_handle_t *hperh); static ald_status_t i2c_master_recv_rxne(ald_i2c_handle_t *hperh); static ald_status_t i2c_slave_send_txe(ald_i2c_handle_t *hperh); static ald_status_t i2c_slave_recv_rxne(ald_i2c_handle_t *hperh); static ald_status_t i2c_slave_stopf(ald_i2c_handle_t *hperh); /** * @} */ /** @defgroup I2C_Public_Functions I2C Public functions * @{ */ /** @defgroup I2C_Public_Functions_Group1 Initialization and de-initialization functions * @brief Initialization and Configuration functions * @verbatim =============================================================================== ##### Initialization and de-initialization functions ##### =============================================================================== [..] This subsection provides a set of functions allowing to initialize and de-initialiaze the I2Cx peripheral: (+) Call the function ald_i2c_init() to configure the selected device with the selected configuration: (++) Communication Speed (++) Addressing mode (++) Own Address 1 (++) Dual Addressing mode (++) Own Address 2 (++) General call mode (++) Nostretch mode (+) Call the function ald_i2c_reset() to restore the default configuration of the selected I2Cx periperal. @endverbatim * @{ */ /** * @brief I2C Configuration Speed function. * @param hperh: Pointer to a i2c_handle_t structure that contains. * the configuration information for the I2C speed. * @param clk: I2C Peripheral bus clock * @retval Status, see @ref ald_status_t. */ ald_status_t i2c_speed_init(ald_i2c_handle_t *hperh, uint32_t clk) { int32_t t_scl, t_pre, tmp; uint8_t scll, sclh, sdadel, scldel; if (clk > 72000000) return ALD_ERROR; if (hperh->init.module == ALD_I2C_MODULE_SLAVE) { hperh->init.clk_speed = 400000UL; } if (hperh->init.clk_speed <= 100000UL) { tmp = clk / 4000000UL; tmp = tmp >= 16UL ? 16UL : tmp; clk = clk / tmp; } else if (hperh->init.clk_speed <= 500000UL) { if (clk < 8000000UL) return ALD_ERROR; tmp = clk / 8000000UL; clk = clk / tmp; } else { if (clk < 12000000UL) return ALD_ERROR; tmp = clk / 12000000UL; clk = clk / tmp; } MODIFY_REG(hperh->perh->TIMINGR, I2C_TIMINGR_PRESC_MSK, (tmp - 1) << I2C_TIMINGR_PRESC_POSS); t_scl = 1000000000UL / hperh->init.clk_speed; t_pre = 1000000000UL / clk; tmp = (t_scl << 4UL) / (t_pre << 1UL); tmp = ((tmp + 8UL) >> 4UL); tmp = tmp >= 255UL ? 255UL : tmp; scll = tmp; if (scll < 4) return ALD_ERROR; MODIFY_REG(hperh->perh->TIMINGR, I2C_TIMINGR_SCLL_MSK, (scll - 1) << I2C_TIMINGR_SCLL_POSS); scldel = ((scll << 1) / 3); scldel = scldel > 16 ? 16 : scldel; sdadel = (scll / 3) < (scll - scldel - 1) ? (scll / 3) : (scll - scldel - 1); sdadel = sdadel > 3 ? 3 : sdadel; MODIFY_REG(hperh->perh->TIMINGR, I2C_TIMINGR_SCLDEL_MSK, (scldel - 1) << I2C_TIMINGR_SCLDEL_POSS); MODIFY_REG(hperh->perh->TIMINGR, I2C_TIMINGR_SDADEL_MSK, sdadel << I2C_TIMINGR_SDADEL_POSS); if (scll > 6) sclh = scll - 3; else if (scll > 4) sclh = 3; else sclh = 2; MODIFY_REG(hperh->perh->TIMINGR, I2C_TIMINGR_SCLH_MSK, (sclh - 1UL) << I2C_TIMINGR_SCLH_POSS); return ALD_OK; } /** * @brief Initializes the I2C according to the specified parameters * in the i2c_init_t and initialize the associated handle. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_init(ald_i2c_handle_t *hperh) { uint32_t freqrange = ald_cmu_get_pclk_clock(); if (hperh == NULL) return ALD_ERROR; /* Check the parameters */ assert_param(IS_I2C_CLOCK_SPEED(hperh->init.clk_speed)); assert_param(IS_I2C_ADDRESSING_MODE(hperh->init.addr_mode)); assert_param(IS_I2C_GENERAL_CALL(hperh->init.general_call)); assert_param(IS_I2C_NO_STRETCH(hperh->init.no_stretch)); assert_param(IS_I2C_MODULE(hperh->init.module)); if (hperh->state == ALD_I2C_STATE_RESET) hperh->lock = UNLOCK; hperh->state = ALD_I2C_STATE_BUSY; ALD_I2C_DISABLE(hperh); if (ALD_OK != i2c_speed_init(hperh, freqrange)) return ALD_ERROR; MODIFY_REG(hperh->perh->CON1, I2C_CON1_NOSTRETCH_MSK, (hperh->init.no_stretch) << I2C_CON1_NOSTRETCH_POS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_GCEN_MSK, (hperh->init.general_call) << I2C_CON1_GCEN_POS); if (hperh->init.dual_addr == ALD_I2C_DUALADDR_ENABLE) { CLEAR_BIT(hperh->perh->ADDR1, I2C_ADDR1_OA1EN_MSK); MODIFY_REG(hperh->perh->ADDR1, I2C_ADDR1_OA1_MSK, (hperh->init.own_addr1 & 0x3FF) << I2C_ADDR1_OA1_POSS); SET_BIT(hperh->perh->ADDR1, I2C_ADDR1_OA1EN_MSK); CLEAR_BIT(hperh->perh->ADDR2, I2C_ADDR2_OA2EN_MSK); MODIFY_REG(hperh->perh->ADDR2, I2C_ADDR2_OA2_MSK, (hperh->init.own_addr2 & 0x7F) << I2C_ADDR2_OA2_POSS); SET_BIT(hperh->perh->ADDR2, I2C_ADDR2_OA2EN_MSK); } else { if (hperh->init.addr_mode == ALD_I2C_ADDR_10BIT) { CLEAR_BIT(hperh->perh->ADDR1, I2C_ADDR1_OA1EN_MSK); SET_BIT(hperh->perh->ADDR1, I2C_ADDR1_OA1MODE_MSK); MODIFY_REG(hperh->perh->ADDR1, I2C_ADDR1_OA1_MSK, (hperh->init.own_addr1 & 0x3FF) << I2C_ADDR1_OA1_POSS); SET_BIT(hperh->perh->ADDR1, I2C_ADDR1_OA1EN_MSK); } else { CLEAR_BIT(hperh->perh->ADDR2, I2C_ADDR2_OA2EN_MSK); MODIFY_REG(hperh->perh->ADDR2, I2C_ADDR2_OA2_MSK, (hperh->init.own_addr2 & 0x7F) << I2C_ADDR2_OA2_POSS); SET_BIT(hperh->perh->ADDR2, I2C_ADDR2_OA2EN_MSK); } } ALD_I2C_ENABLE(hperh); hperh->error_code = ALD_I2C_ERROR_NONE; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; return ALD_OK; } /** * @brief DeInitialize the I2C peripheral. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_reset(ald_i2c_handle_t *hperh) { if (hperh == NULL) return ALD_ERROR; ALD_I2C_DISABLE(hperh); hperh->error_code = ALD_I2C_ERROR_NONE; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); WRITE_REG(hperh->perh->CON1, 0); WRITE_REG(hperh->perh->CON2, 0); WRITE_REG(hperh->perh->ADDR1, 0); WRITE_REG(hperh->perh->ADDR2, 0); WRITE_REG(hperh->perh->TIMINGR, 0); WRITE_REG(hperh->perh->TIMEOUTR, 0); WRITE_REG(hperh->perh->IDR, ALD_I2C_FLAG_MASK); WRITE_REG(hperh->perh->ICR, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE(hperh); return ALD_OK; } /** * @} */ /** @defgroup I2C_Public_Functions_Group2 Input and Output operation functions * @brief Data transfers functions * @verbatim =============================================================================== ##### IO operation functions ##### =============================================================================== [..] This subsection provides a set of functions allowing to manage the I2C data transfers. (#) There are two modes of transfer: (++) Blocking mode : The communication is performed in the polling mode. The status of all data processing is returned by the same function after finishing transfer. (++) No-Blocking mode : The communication is performed using Interrupts or DMA. These functions return the status of the transfer startup. The end of the data processing will be indicated through the dedicated I2C IRQ when using Interrupt mode or the DMA IRQ when using DMA mode. (#) Blocking mode functions are : (++) ald_i2c_master_send() (++) ald_i2c_master_recv() (++) ald_i2c_slave_send() (++) ald_i2c_slave_recv() (++) ald_i2c_mem_write() (++) ald_i2c_mem_read() (#) No-Blocking mode functions with Interrupt are : (++) ald_i2c_master_send_by_it() (++) ald_i2c_master_recv_by_it() (++) ald_i2c_slave_send_by_it() (++) ald_i2c_slave_recv_by_it() (++) ald_i2c_mem_write_by_it() (++) ald_i2c_mem_read_by_it() (#) No-Blocking mode functions with DMA are : (++) ald_i2c_master_send_by_dma() (++) ald_i2c_master_recv_by_dma() (++) ald_i2c_slave_send_by_dma() (++) ald_i2c_slave_recv_by_dma() (++) ald_i2c_mem_write_by_dma() (++) ald_i2c_mem_read_by_dma() (#) A set of Transfer Complete Callbacks are provided in non Blocking mode: (++) hperh->mem_tx_cplt_cbk() (++) hperh->mem_rx_cplt_cbk() (++) hperh->master_tx_cplt_cbk() (++) hperh->master_rx_cplt_cbk() (++) hperh->slave_tx_cplt_cbk() (++) hperh->slave_rx_cplt_cbk() (++) hperh->error_callback() @endverbatim * @{ */ /** * @brief Transmits in master mode an amount of data in blocking mode. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_send(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size, uint32_t timeout) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; i2c_master_req_write(hperh, dev_addr, timeout); if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); while (size > 0) { hperh->perh->TXDATA = (*buf++); size--; hperh->xfer_count++; if (i2c_wait_txe_to_timeout(hperh, timeout) != ALD_OK) goto ERROR; if (((hperh->xfer_count % 0xFFFF) == 0) && (READ_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK))) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TCR, RESET, I2C_TIMEOUT_FLAG) == ALD_OK) { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } } else { goto ERROR; } } } if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK) == SET) goto SUCCESS; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TC, RESET, I2C_TIMEOUT_FLAG) == ALD_OK) { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); goto SUCCESS; } else { goto ERROR; } ERROR: SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Receives in master mode an amount of data in blocking mode. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t */ ald_status_t ald_i2c_master_recv(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size, uint32_t timeout) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; i2c_master_req_read(hperh, dev_addr, timeout); if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); while (size > 0) { if (i2c_wait_rxne_to_timeout(hperh, timeout) != ALD_OK) goto ERROR; (*buf++) = hperh->perh->RXDATA; size--; hperh->xfer_count++; if (((hperh->xfer_count % 0xFF) == 0) && (READ_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK))) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TCR, RESET, I2C_TIMEOUT_FLAG) == ALD_OK) { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } } else { goto ERROR; } } } if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK)) goto SUCCESS; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); goto SUCCESS; ERROR: SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Transmits in slave mode an amount of data in blocking mode. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_send(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size, uint32_t timeout) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; ALD_I2C_ENABLE(hperh); if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TXE, RESET, timeout) != ALD_OK) goto ERROR; hperh->perh->TXDATA = (*buf++); --size; hperh->xfer_count++; if (i2c_wait_master_addr_to_timeout(hperh, ALD_I2C_IT_ADDR, timeout) == ALD_ERROR) goto ERROR; while (size > 0) { if (i2c_wait_txe_to_timeout(hperh, timeout) == ALD_ERROR) goto ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TXE, RESET, timeout) != ALD_OK) goto ERROR; hperh->perh->TXDATA = (*buf++); --size; hperh->xfer_count++; } goto SUCCESS; ERROR: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Receive in slave mode an amount of data in blocking mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_recv(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size, uint32_t timeout) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_count = 0; hperh->xfer_size = size; if (i2c_wait_master_addr_to_timeout(hperh, ALD_I2C_IT_ADDR, timeout) == ALD_ERROR) goto ERROR; while (size > 0) { if (i2c_wait_rxne_to_timeout(hperh, timeout) == ALD_ERROR) goto ERROR; if (READ_BIT(hperh->perh->CON2, I2C_CON2_HOLDACK_MSK)) SET_BIT(hperh->perh->CON2, I2C_CON2_ACK_UPD_MSK); (*buf++) = hperh->perh->RXDATA; --size; hperh->xfer_count++; } goto SUCCESS; ERROR: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Transmit in master mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_send_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG); if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TXE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_NACK); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TCR); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TC | ALD_I2C_IT_TCR); return ALD_OK; } /** * @brief Receive in master mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_recv_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; i2c_master_req_read(hperh, dev_addr, I2C_TIMEOUT_FLAG) ; if (size <= 0xFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_RXNE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TCR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_TCR | ALD_I2C_IT_TC); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); return ALD_OK; } /** * @brief Transmit in slave mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_send_by_it(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TXE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_NACK); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_STOP); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_ADDR | ALD_I2C_IT_NACK | ALD_I2C_IT_STOP | ALD_I2C_IT_TXE); return ALD_OK; } /** * @brief Receive in slave mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_recv_by_it(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_RXNE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_STOP); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_ADDR | ALD_I2C_IT_STOP); return ALD_OK; } /** * @brief Sequential Transmit in master mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param option: Options of Transfer * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_seq_send_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size, uint32_t option) { uint32_t pre_state = 0; uint32_t it_flag = 0; assert_param(IS_I2C_TRANSFER_OPTIONS(option)); if (hperh->state == ALD_I2C_STATE_READY) { if ((READ_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK) == I2C_CON2_STOP_MSK) || (option == ALD_I2C_FIRST_AND_LAST_FRAME) || (option == ALD_I2C_FIRST_FRAME)) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) { hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; __UNLOCK(hperh); return ALD_BUSY; } } __LOCK(hperh); if ((READ_BIT(hperh->perh->CON1, I2C_CON1_PE_MSK) != I2C_CON1_PE_MSK)) { ALD_I2C_ENABLE(hperh); } hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; hperh->xfer_opt = option; pre_state = hperh->pre_state; if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } if ((pre_state != ALD_I2C_STATE_MASTER_BUSY_TX) || (IS_I2C_TRANSFER_OTHER_OPTIONS(option))) { i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TXE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_NACK); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TCR); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); it_flag = ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TCR; if ((option == ALD_I2C_FIRST_AND_LAST_FRAME) || (option == ALD_I2C_LAST_FRAME) || (option == ALD_I2C_OTHER_AND_LAST_FRAME)) { it_flag |= ALD_I2C_IT_TC; if (size <= 0xFFFF) { CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); it_flag &= ~ALD_I2C_IT_TCR; } } ALD_I2C_ENABLE_IT(hperh, it_flag); __UNLOCK(hperh); } else { return ALD_BUSY; } return ALD_OK; } /** * @brief Sequential Receive in master mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param option: Options of Transfer * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_seq_recv_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint32_t size, uint32_t option) { uint32_t pre_state = 0; uint32_t it_flag = 0; assert_param(IS_I2C_TRANSFER_OPTIONS(option)); if (hperh->state == ALD_I2C_STATE_READY) { if ((READ_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK) == I2C_CON2_STOP_MSK) || (option == ALD_I2C_FIRST_AND_LAST_FRAME) || (option == ALD_I2C_FIRST_FRAME)) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) { hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; __UNLOCK(hperh); return ALD_BUSY; } } __LOCK(hperh); if ((READ_BIT(hperh->perh->CON1, I2C_CON1_PE_MSK) != I2C_CON1_PE_MSK)) ALD_I2C_ENABLE(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; pre_state = hperh->pre_state; if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } if ((pre_state != ALD_I2C_STATE_MASTER_BUSY_RX) || (IS_I2C_TRANSFER_OTHER_OPTIONS(option))) { i2c_master_req_read(hperh, dev_addr, I2C_TIMEOUT_FLAG); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_RXNE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TCR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); it_flag = ALD_I2C_IT_RXNE | ALD_I2C_IT_TCR; if ((option == ALD_I2C_FIRST_AND_LAST_FRAME) || (option == ALD_I2C_LAST_FRAME) || (option == ALD_I2C_OTHER_AND_LAST_FRAME)) it_flag |= ALD_I2C_IT_TC; ALD_I2C_ENABLE_IT(hperh, it_flag); __UNLOCK(hperh); } else { return ALD_BUSY; } return ALD_OK; } /** * @brief Sequential Transmit in slave mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param option: Options of Transfer * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_seq_send_by_it(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size, uint32_t option) { assert_param(IS_I2C_TRANSFER_OPTIONS(option)); if (((hperh->state & ALD_I2C_STATE_LISTEN) == ALD_I2C_STATE_LISTEN) || (hperh->state == ALD_I2C_STATE_READY)) { if ((buf == NULL) || (size == 0)) return ALD_ERROR; __LOCK(hperh); if ((READ_BIT(hperh->perh->CON1, I2C_CON1_PE_MSK) != I2C_CON1_PE_MSK)) ALD_I2C_ENABLE(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX_LISTEN; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; hperh->xfer_opt = option; __UNLOCK(hperh); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TXE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_NACK); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_STOP); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_ADDR | ALD_I2C_IT_NACK | ALD_I2C_IT_STOP | ALD_I2C_IT_TXE); } else { return ALD_BUSY; } return ALD_OK; } /** * @brief Sequential Receive in slave mode an amount of data in non-blocking mode with Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param option: Options of Transfer * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_seq_recv_by_it(ald_i2c_handle_t *hperh, uint8_t *buf, uint32_t size, uint32_t option) { assert_param(IS_I2C_TRANSFER_OPTIONS(option)); if (((hperh->state & ALD_I2C_STATE_LISTEN) == ALD_I2C_STATE_LISTEN) || (hperh->state == ALD_I2C_STATE_READY)) { if ((buf == NULL) || (size == 0)) return ALD_ERROR; __LOCK(hperh); if ((READ_BIT(hperh->perh->CON1, I2C_CON1_PE_MSK) != I2C_CON1_PE_MSK)) ALD_I2C_ENABLE(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX_LISTEN; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; hperh->xfer_opt = option; __UNLOCK(hperh); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_RXNE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_STOP); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_ADDR | ALD_I2C_IT_STOP); } else { return ALD_BUSY; } return ALD_OK; } /** * @brief Enable the Address listen mode whit Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param addr: Devaddress Target device address * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_abort_it(ald_i2c_handle_t *hperh, uint16_t addr) { UNUSED(addr); if ((READ_BIT(hperh->perh->STAT, I2C_STAT_BUSY_MSK) != RESET) && (hperh->mode == ALD_I2C_MODE_MASTER)) { __LOCK(hperh); hperh->pre_state = ALD_I2C_STATE_NONE; hperh->state = ALD_I2C_STATE_ABORT; SET_BIT(hperh->perh->CON1, I2C_CON2_STOP_MSK); hperh->xfer_size = 0; hperh->xfer_count = 0; ALD_I2C_CLEAR_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); __UNLOCK(hperh); return ALD_OK; } else { return ALD_ERROR; } } /** * @brief Enable the Address listen mode whit Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_enablelisten_it(ald_i2c_handle_t *hperh) { if (hperh->state == ALD_I2C_STATE_READY) { hperh->state = ALD_I2C_STATE_LISTEN; if ((READ_BIT(hperh->perh->CON1, I2C_CON1_PE_MSK) != I2C_CON1_PE_MSK)) ALD_I2C_ENABLE(hperh); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_ADDR); return ALD_OK; } else { return ALD_BUSY; } } /** * @brief Disable the Address listen mode whit Interrupt * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_disablelisten_it(ald_i2c_handle_t *hperh) { if (hperh->state == ALD_I2C_STATE_LISTEN) { hperh->pre_state = ((hperh->state) & ALD_I2C_STATE_MSK) | (hperh->mode); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_ADDR); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); return ALD_OK; } else { return ALD_BUSY; } } /** * @brief Transmit in master mode an amount of data in non-blocking mode with DMA * @note The maximum amount of data to be sent is 0xFF. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent (maximum is 0xFF) * @param channel: DMA channel as I2C transmit * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_send_by_dma(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint16_t size, uint8_t channel) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); if (size >= 0xFFFF) size = 0xFFFF; hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; if (hperh->hdmatx.perh == NULL) hperh->hdmatx.perh = DMA; hperh->hdmatx.cplt_tc_cbk = i2c_dma_master_send_cplt; hperh->hdmatx.cplt_tc_arg = hperh; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmatx.config); hperh->hdmatx.config.size = size; hperh->hdmatx.config.src = (void *)hperh->p_buff; hperh->hdmatx.config.dst = (void *)&hperh->perh->TXDATA; hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.src_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmatx.config.dst_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmatx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmatx.config.msigsel = ALD_DMA_MSIGSEL_I2C_TXEMPTY; hperh->hdmatx.config.channel = channel; ald_dma_config_basic(&hperh->hdmatx); i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); __UNLOCK(hperh); return ALD_OK; } /** * @brief Receive in master mode an amount of data in non-blocking mode with DMA * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param channel: DMA channel as I2C receive * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_master_recv_by_dma(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint16_t size, uint8_t channel) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); if (size >= 0xFFFF) size = 0xFFFF; hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; if (hperh->hdmarx.perh == NULL) hperh->hdmarx.perh = DMA; hperh->hdmarx.cplt_tc_cbk = i2c_dma_master_recv_cplt; hperh->hdmarx.cplt_tc_arg = (void *)hperh; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmarx.config); hperh->hdmarx.config.size = size; hperh->hdmarx.config.src = (void *)&hperh->perh->RXDATA; hperh->hdmarx.config.dst = (void *)buf; hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmarx.config.src_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmarx.config.dst_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmarx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmarx.config.msigsel = ALD_DMA_MSIGSEL_I2C_RNR; hperh->hdmarx.config.channel = channel; ald_dma_config_basic(&hperh->hdmarx); i2c_master_req_read(hperh, dev_addr, I2C_TIMEOUT_FLAG); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); return ALD_OK; } /** * @brief Transmit in slave mode an amount of data in non-blocking mode with DMA * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param channel: DMA channel as I2C Transmit * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_send_by_dma(ald_i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; if (size >= 0xFFFF) size = 0xFFFF; if (hperh->hdmatx.perh == NULL) hperh->hdmatx.perh = DMA; hperh->hdmatx.cplt_tc_cbk = i2c_dma_slave_send_cplt; hperh->hdmatx.cplt_tc_arg = hperh; SET_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmatx.config); hperh->hdmatx.config.src = (void *)buf; hperh->hdmatx.config.dst = (void *)&hperh->perh->TXDATA; hperh->hdmatx.config.size = size; hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.src_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmatx.config.dst_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmatx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmatx.config.msigsel = ALD_DMA_MSIGSEL_I2C_TXEMPTY; hperh->hdmatx.config.channel = channel; ald_dma_config_basic(&hperh->hdmatx); if (i2c_wait_master_addr_to_timeout(hperh, ALD_I2C_IT_ADDR, I2C_TIMEOUT_ADDR_SLAVE) == ALD_ERROR) { hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; } return ALD_OK; } /** * @brief Receive in slave mode an amount of data in non-blocking mode with DMA * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param channel: DMA channel as I2C receive * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_slave_recv_by_dma(ald_i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel) { if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_SLAVE; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; if (size >= 0xFF) size = 0xFF; if (hperh->hdmarx.perh == NULL) hperh->hdmarx.perh = DMA; hperh->hdmarx.cplt_tc_cbk = i2c_dma_slave_recv_cplt; hperh->hdmarx.cplt_tc_arg = (void *)hperh; SET_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmarx.config); hperh->hdmarx.config.src = (void *)&hperh->perh->RXDATA; hperh->hdmarx.config.dst = (void *)buf; hperh->hdmarx.config.size = size; hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmarx.config.src_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmarx.config.dst_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmarx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmarx.config.msigsel = ALD_DMA_MSIGSEL_I2C_RNR; hperh->hdmarx.config.channel = channel; ald_dma_config_basic(&hperh->hdmarx); if (i2c_wait_master_addr_to_timeout(hperh, ALD_I2C_IT_ADDR, I2C_TIMEOUT_ADDR_SLAVE) == ALD_ERROR) { hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; } return ALD_OK; } /** * @brief Write an amount of data in blocking mode to a specific memory address * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_write(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint32_t size, uint32_t timeout) { uint32_t nbyte = 0; assert_param(IS_I2C_MEMADD_size(add_size)); if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MEM; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->xfer_size = size; hperh->xfer_count = 0; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, timeout) != ALD_OK) goto ERROR; if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } while (size > 0) { hperh->perh->TXDATA = (*buf++); --size; hperh->xfer_count++; if (((hperh->xfer_count % 0xFF) == 0) && (READ_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK))) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TCR, RESET, I2C_TIMEOUT_FLAG) == ALD_OK) { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } } else { goto ERROR; } } if (i2c_wait_txe_to_timeout(hperh, timeout) != ALD_OK) goto ERROR; } if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK)) goto SUCCESS; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TC, RESET, timeout)) goto ERROR; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); goto SUCCESS; ERROR: SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Read an amount of data in blocking mode from a specific memory address * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_read(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint32_t size, uint32_t timeout) { uint32_t nbyte = 0; assert_param(IS_I2C_MEMADD_size(add_size)); if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MEM; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->xfer_count = 0; hperh->xfer_size = size; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, timeout) != ALD_OK) return ALD_ERROR; if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } SET_BIT(hperh->perh->CON2, I2C_CON2_RD_WRN_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); while (size > 0) { if (i2c_wait_rxne_to_timeout(hperh, timeout) != ALD_OK) goto ERROR; (*buf++) = hperh->perh->RXDATA; size--; hperh->xfer_count++; if (((hperh->xfer_count % 0xFF) == 0) && (READ_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK))) { if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TCR, RESET, timeout) == ALD_OK) { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } } else { goto ERROR; } } } if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK)) goto SUCCESS; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); goto SUCCESS; ERROR: SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; SUCCESS: hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_OK; } /** * @brief Write an amount of data in non-blocking mode with Interrupt to a specific memory address * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_write_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint32_t size) { uint32_t nbyte = 0; assert_param(IS_I2C_MEMADD_size(add_size)); if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MEM; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != ALD_OK) { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; } if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TXE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_NACK); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TCR); hperh->perh->TXDATA = (*hperh->p_buff++); hperh->xfer_count++; ALD_I2C_DISABLE_IT(hperh, ALD_I2C_FLAG_MASK); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TC | ALD_I2C_IT_TCR); return ALD_OK; } /** * @brief Read an amount of data in non-blocking mode with Interrupt from a specific memory address * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_read_by_it(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint32_t size) { uint32_t nbyte = 0; assert_param(IS_I2C_MEMADD_size(add_size)); if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_TYPE(hperh->perh)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MEM; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != ALD_OK) { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; } if (size <= 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_RXNE); ALD_I2C_CLEAR_IT(hperh, ALD_I2C_IT_TC); ALD_I2C_CLEAR_IT(hperh , ALD_I2C_IT_TCR); ALD_I2C_ENABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_TC | ALD_I2C_IT_TCR); SET_BIT(hperh->perh->CON2, I2C_CON2_RD_WRN_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); return ALD_OK; } /** * @brief Write an amount of data in non-blocking mode with DMA to a specific memory address * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address(Maxsiz 0xFF) * @param buf: Pointer to data buffer * @param size: Amount of data to be sent * @param channel: DMA channel * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_write_by_dma(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint16_t size, uint8_t channel) { uint32_t nbyte = 0; if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_MEMADD_size(add_size)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_TX; hperh->mode = ALD_I2C_MODE_MASTER; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = 0; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != ALD_OK) { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); return ALD_ERROR; } if (hperh->hdmatx.perh == NULL) hperh->hdmatx.perh = DMA; hperh->hdmatx.cplt_tc_cbk = i2c_dma_mem_send_cplt; hperh->hdmatx.cplt_tc_arg = hperh; SET_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmatx.config); hperh->hdmatx.config.src = (void *)hperh->p_buff; hperh->hdmatx.config.dst = (void *)&hperh->perh->TXDATA; hperh->hdmatx.config.size = size; hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmatx.config.src_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmatx.config.dst_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmatx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmatx.config.msigsel = ALD_DMA_MSIGSEL_I2C_TXEMPTY; hperh->hdmatx.config.channel = channel; ald_dma_config_basic(&hperh->hdmatx); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); return ALD_OK; } /** * @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param buf: Pointer to data buffer * @param size: Amount of data to be read * @param channel: DMA channel * @retval Status, see @ref ald_status_t. */ ald_status_t ald_i2c_mem_read_by_dma(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, ald_i2c_addr_size_t add_size, uint8_t *buf, uint16_t size, uint8_t channel) { uint32_t nbyte = 0; if (hperh->state != ALD_I2C_STATE_READY) return ALD_BUSY; if ((buf == NULL) || (size == 0)) return ALD_ERROR; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != ALD_OK) return ALD_BUSY; assert_param(IS_I2C_MEMADD_size(add_size)); __LOCK(hperh); hperh->state = ALD_I2C_STATE_BUSY_RX; hperh->mode = ALD_I2C_MODE_MEM; hperh->error_code = ALD_I2C_ERROR_NONE; hperh->p_buff = buf; hperh->xfer_size = size; hperh->xfer_count = size; nbyte = (add_size == ALD_I2C_MEMADD_SIZE_8BIT) ? 1 : 2; MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, nbyte << I2C_CON2_NBYTES_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); if (i2c_req_mem_read(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != ALD_OK) { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); } if (hperh->hdmarx.perh == NULL) hperh->hdmarx.perh = DMA; hperh->hdmarx.cplt_tc_cbk = i2c_dma_mem_recv_cplt; hperh->hdmarx.cplt_tc_arg = (void *)hperh; SET_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE); ald_dma_config_struct(&hperh->hdmarx.config); hperh->hdmarx.config.src = (void *)&hperh->perh->RXDATA; hperh->hdmarx.config.dst = (void *)buf; hperh->hdmarx.config.src_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmarx.config.dst_data_width = ALD_DMA_DATA_SIZE_BYTE; hperh->hdmarx.config.size = size; hperh->hdmarx.config.src_inc = ALD_DMA_DATA_INC_DISABLE; hperh->hdmarx.config.dst_inc = ALD_DMA_DATA_INC_ENABLE; hperh->hdmarx.config.msel = hperh->perh == I2C0 ? ALD_DMA_MSEL_I2C0 : ALD_DMA_MSEL_I2C1; hperh->hdmarx.config.msigsel = ALD_DMA_MSIGSEL_I2C_RNR; hperh->hdmarx.config.channel = channel; ald_dma_config_basic(&hperh->hdmarx); MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_RD_WRN_MSK); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); __UNLOCK(hperh); return ALD_OK; } /** * @} */ /** @defgroup I2C_Public_Functions_Group3 Peripheral state and Errors functions * @brief Peripheral state and Errors functions * @verbatim =============================================================================== ##### Peripheral state and Errors functions ##### =============================================================================== [..] This subsection permits to get in run-time the status of the peripheral and the data flow. @endverbatim * @{ */ /** * @brief Return the I2C handle state. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval ald_status_t state */ ald_i2c_state_t ald_i2c_get_state(ald_i2c_handle_t *hperh) { return hperh->state; } /** * @brief Return the I2C error code. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval I2C Error Code */ uint32_t ald_i2c_get_error(ald_i2c_handle_t *hperh) { return hperh->error_code; } /** * @} */ /** @defgroup I2C_Public_Functions_Group4 IRQ Handler and Callbacks * @{ */ /** * @brief This function handles I2C event interrupt request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval None */ void ald_i2c_ev_irq_handler(ald_i2c_handle_t *hperh) { uint32_t size = hperh->xfer_size - hperh->xfer_count; /**< Transmit empty interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXE) == SET) { if ((hperh->mode == ALD_I2C_MODE_MASTER) || (hperh->mode == ALD_I2C_MODE_MEM)) i2c_master_send_txe(hperh); else if (hperh->mode == ALD_I2C_MODE_SLAVE) i2c_slave_send_txe(hperh); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXE); } /**< Receive not empty interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXNE) == SET) { if ((hperh->mode == ALD_I2C_MODE_MASTER) || (hperh->mode == ALD_I2C_MODE_MEM)) i2c_master_recv_rxne(hperh); else if (hperh->mode == ALD_I2C_MODE_SLAVE) i2c_slave_recv_rxne(hperh); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXNE); } /**< Transmit completed interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TC) == SET) { if ((hperh->mode == ALD_I2C_MODE_MASTER) || (hperh->mode == ALD_I2C_MODE_MEM)) { if (ALD_I2C_MASTER_GET_DIR(hperh) == RESET) i2c_master_send_tc(hperh); else i2c_master_recv_tc(hperh); } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TC); } /**< Transmit and reload completed interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TCR) == SET) { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TCR); } /**< Address matching interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ADDR) == SET) ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ADDR); /**< Stop detection interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_STOP) == SET) { i2c_slave_stopf(hperh); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_STOP); } } /** * @brief This function handles I2C error interrupt request. * @param hperh: pointer to a i2c_handle_t structure that contains * the configuration information for I2C module * @retval NONE */ void ald_i2c_er_irq_handler(ald_i2c_handle_t *hperh) { /**< Transmit overrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXOV) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXOV); hperh->error_code |= ALD_I2C_ERROR_TOV; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Transmit underrun */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXUD) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXUD); hperh->error_code |= ALD_I2C_ERROR_TUD; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Receive overrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXOV) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXOV); hperh->error_code |= ALD_I2C_ERROR_ROV; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Receive underrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXUD) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXUD); hperh->error_code |= ALD_I2C_ERROR_RUD; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< NACK interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_NACK) == SET) { if (hperh->xfer_count != hperh->xfer_size) { hperh->state |= ALD_I2C_ERROR_AF; } else { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_NACK); return; } if ((hperh->mode == ALD_I2C_MODE_MASTER) || (hperh->mode == ALD_I2C_MODE_MEM)) SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_NACK); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Bus error interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_BERR) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_BERR); hperh->state |= ALD_I2C_ERROR_BERR; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Arbitration loss interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ARLO) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ARLO); hperh->state |= ALD_I2C_ERROR_ARLO; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< PEC error interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_PECE) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_PECE); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< Timeout interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TOUT) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TOUT); hperh->state |= ALD_I2C_ERROR_TIMEOUT; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /**< SMBus Alert interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ALERT) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ALERT); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } } /** * @brief This function handles I2C event interrupt request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval None */ void ald_i2c_seq_ev_irq_handler(ald_i2c_handle_t *hperh) { uint32_t size = hperh->xfer_size - hperh->xfer_count; ald_i2c_state_t current_state = hperh->state; uint32_t current_opt = hperh->xfer_opt; uint8_t tmp = 0; /**< Address matching interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ADDR) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ADDR); if ((hperh->state & ALD_I2C_STATE_LISTEN) == ALD_I2C_STATE_LISTEN) { if (hperh->addr_cplt_cbk != NULL) hperh->addr_cplt_cbk(hperh); if (hperh->state == ALD_I2C_STATE_BUSY_TX_LISTEN) hperh->state = ALD_I2C_STATE_BUSY_TX; if (hperh->state == ALD_I2C_STATE_BUSY_RX_LISTEN) hperh->state = ALD_I2C_STATE_BUSY_RX; } } /**< Transmit empty interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXE) == SET) { if (hperh->mode == ALD_I2C_MODE_MASTER) { if ((hperh->xfer_size == 0) && (current_state == ALD_I2C_STATE_BUSY_TX)) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TC | ALD_I2C_IT_TCR); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; if ((current_opt != ALD_I2C_LAST_FRAME) && (current_opt != ALD_I2C_FIRST_AND_LAST_FRAME) && (current_opt != ALD_I2C_OTHER_AND_LAST_FRAME)) { hperh->pre_state = ALD_I2C_STATE_BUSY_TX; __UNLOCK(hperh); } else { SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->pre_state = (uint32_t)ALD_I2C_STATE_NONE; __UNLOCK(hperh); } if (hperh->master_tx_cplt_cbk != NULL) hperh->master_tx_cplt_cbk (hperh); } if (hperh->xfer_count != hperh->xfer_size) { hperh->perh->TXDATA = (*hperh->p_buff++); hperh->xfer_count++; } } else if (hperh->mode == ALD_I2C_MODE_SLAVE) { if (hperh->xfer_size > hperh->xfer_count) { while(hperh->perh->STAT & (0x1 << 1)); hperh->perh->TXDATA = (*hperh->p_buff++); hperh->xfer_count++; } } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXE); } /**< Receive not empty interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXNE) == SET) { if ((hperh->state == ALD_I2C_STATE_BUSY_RX) || (hperh->state == ALD_I2C_STATE_BUSY_RX_LISTEN)) { if (hperh->xfer_size - hperh->xfer_count > 0) { (*hperh->p_buff++) = hperh->perh->RXDATA; hperh->xfer_count++; } else { tmp = hperh->perh->RXDATA; UNUSED(tmp); } } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXNE); } /**< Transmit completed interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TC) == SET) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TC | ALD_I2C_IT_TCR); SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); hperh->pre_state = ALD_I2C_STATE_NONE; hperh->mode = ALD_I2C_MODE_NONE; hperh->state = ALD_I2C_STATE_READY; __UNLOCK(hperh); if (current_state == ALD_I2C_STATE_MASTER_BUSY_TX) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK); if (hperh->master_tx_cplt_cbk != NULL) hperh->master_tx_cplt_cbk (hperh); } else if (current_state == ALD_I2C_STATE_SLAVE_BUSY_TX) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK); if (hperh->slave_tx_cplt_cbk != NULL) hperh->slave_tx_cplt_cbk (hperh); } else if (current_state == ALD_I2C_STATE_MASTER_BUSY_RX) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_RXNE); if (hperh->master_rx_cplt_cbk != NULL) hperh->master_rx_cplt_cbk (hperh); } else if (current_state == ALD_I2C_STATE_SLAVE_BUSY_RX) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_RXNE); if (hperh->slave_rx_cplt_cbk != NULL) hperh->slave_rx_cplt_cbk (hperh); } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TC); } /**< Transmit and reload completed interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TCR) == SET) { if (size == 0) { if (current_state == ALD_I2C_STATE_BUSY_TX) { if (hperh->mode == ALD_I2C_MODE_MASTER) hperh->pre_state = ALD_I2C_STATE_MASTER_BUSY_TX; else hperh->pre_state = ALD_I2C_STATE_SLAVE_BUSY_TX; hperh->mode = ALD_I2C_MODE_NONE; hperh->state = ALD_I2C_STATE_READY; __UNLOCK(hperh); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TCR); if (hperh->master_tx_cplt_cbk != NULL) hperh->master_tx_cplt_cbk (hperh); } else if (current_state == ALD_I2C_STATE_BUSY_RX) { if (hperh->mode == ALD_I2C_MODE_MASTER) hperh->pre_state = ALD_I2C_STATE_MASTER_BUSY_RX; else hperh->pre_state = ALD_I2C_STATE_SLAVE_BUSY_RX; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_TCR); if (hperh->master_rx_cplt_cbk != NULL) hperh->master_rx_cplt_cbk(hperh); } } else { if (size > 0xFFFF) { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, 0xFF << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, 0xFF << I2C_CON1_NBYTES_POSS); } else { MODIFY_REG(hperh->perh->CON2, I2C_CON2_NBYTES_MSK, (size & 0xFF) << I2C_CON2_NBYTES_POSS); MODIFY_REG(hperh->perh->CON1, I2C_CON1_NBYTES_MSK, (size >> 8) << I2C_CON1_NBYTES_POSS); if ((current_opt == ALD_I2C_FIRST_AND_LAST_FRAME) || (current_opt == ALD_I2C_LAST_FRAME) || (current_opt == ALD_I2C_OTHER_AND_LAST_FRAME)) CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); } } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TCR); } /**< Stop detection interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_STOP) == SET) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_ADDR | ALD_I2C_IT_NACK | ALD_I2C_IT_RXNE | ALD_I2C_IT_TXE | ALD_I2C_IT_STOP); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_STOP); if ((size == 0) && (hperh->error_code == ALD_I2C_ERROR_NONE)) { if (hperh->state == ALD_I2C_STATE_BUSY_TX_LISTEN) { hperh->pre_state = ALD_I2C_STATE_BUSY_TX_LISTEN; hperh->state = ALD_I2C_STATE_LISTEN; hperh->mode = ALD_I2C_MODE_NONE; if (hperh->slave_tx_cplt_cbk != NULL) hperh->slave_tx_cplt_cbk(hperh); } if (hperh->state == ALD_I2C_STATE_BUSY_RX_LISTEN) { hperh->pre_state = ALD_I2C_STATE_SLAVE_BUSY_RX; hperh->state = ALD_I2C_STATE_LISTEN; hperh->mode = ALD_I2C_MODE_NONE; if (hperh->slave_rx_cplt_cbk != NULL) hperh->slave_rx_cplt_cbk(hperh); } if (hperh->state == ALD_I2C_STATE_LISTEN) { hperh->xfer_opt = ALD_I2C_NO_OPTION_FRAME; hperh->pre_state = ALD_I2C_STATE_NONE; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; if (hperh->listen_cplt_cbk != NULL) hperh->listen_cplt_cbk(hperh); } } } } /** * @brief This function handles I2C error interrupt request. * @param hperh: pointer to a i2c_handle_t structure that contains * the configuration information for I2C module * @retval NONE */ void ald_i2c_seq_er_irq_handler(ald_i2c_handle_t *hperh) { /**< Transmit overrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXOV) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXOV); hperh->error_code |= ALD_I2C_ERROR_TOV; } /**< Transmit underrun */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TXUD) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TXUD); hperh->error_code |= ALD_I2C_ERROR_TUD; } /**< Receive overrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXOV) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXOV); hperh->error_code |= ALD_I2C_ERROR_ROV; } /**< Receive underrun interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_RXUD) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_RXUD); hperh->error_code |= ALD_I2C_ERROR_RUD; } /**< NACK interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_NACK) == SET) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK); if ((hperh->mode == ALD_I2C_MODE_SLAVE) && (hperh->xfer_count == hperh->xfer_size) && \ ((hperh->state == ALD_I2C_STATE_BUSY_TX) || (hperh->state == ALD_I2C_STATE_BUSY_TX_LISTEN) || \ ((hperh->state == ALD_I2C_STATE_LISTEN) && (hperh->pre_state == ALD_I2C_STATE_SLAVE_BUSY_TX)))) { if (((hperh->xfer_opt == ALD_I2C_FIRST_AND_LAST_FRAME) || (hperh->xfer_opt == ALD_I2C_LAST_FRAME)) && \ (hperh->state == ALD_I2C_STATE_LISTEN)) { hperh->pre_state = ALD_I2C_STATE_NONE; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; if (hperh->listen_cplt_cbk != NULL) hperh->listen_cplt_cbk(hperh); } else if (hperh->state == ALD_I2C_STATE_BUSY_TX) { hperh->xfer_opt = ALD_I2C_NO_OPTION_FRAME; hperh->pre_state = ALD_I2C_STATE_SLAVE_BUSY_TX; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; if (hperh->slave_tx_cplt_cbk != NULL) hperh->slave_tx_cplt_cbk(hperh); } } else { hperh->error_code |= ALD_I2C_ERROR_AF; if (hperh->mode == ALD_I2C_MODE_MASTER) SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_NACK); } /**< Bus error interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_BERR) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_BERR); hperh->state |= ALD_I2C_ERROR_BERR; } /**< Arbitration loss interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ARLO) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ARLO); hperh->state |= ALD_I2C_ERROR_ARLO; } /**< PEC error interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_PECE) == SET) ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_PECE); /**< Timeout interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_TOUT) == SET) { ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TOUT); hperh->state |= ALD_I2C_ERROR_TIMEOUT; } /**< SMBus Alert interrupt */ if (ald_i2c_get_mask_flag_status(hperh, ALD_I2C_IT_ALERT) == SET) ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_ALERT); hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_callback) hperh->error_callback(hperh); } /** * @brief Enable/disable the specified I2C interrupts. * @param hperh: Pointer to a i2c_handle_t structure. * @param it: Specifies the i2c interrupt sources to be enabled or disabled. * @param state: New state of the specified I2C interrupts. * This parameter can be: * @arg ENABLE * @arg DISABLE * @retval None */ void ald_i2c_interrupt_config(ald_i2c_handle_t *hperh, ald_i2c_interrupt_t it, type_func_t state) { assert_param(IS_I2C_TYPE(hperh->perh)); assert_param(IS_I2C_IT(it)); assert_param(IS_FUNC_STATE(state)); if (state == ENABLE) hperh->perh->IER = it; else hperh->perh->IDR = it; return; } /** * @brief Get the status of I2C interrupt source. * @param hperh: Pointer to a i2c_handle_t structure. * @param it: Specifies the I2C interrupt source. * This parameter can be one of the @ref i2c_interrupt_t. * @retval Status: * - 0: RESET * - 1: SET */ it_status_t ald_i2c_get_it_status(ald_i2c_handle_t *hperh, ald_i2c_interrupt_t it) { it_status_t status = RESET; assert_param(IS_I2C_TYPE(hperh->perh)); assert_param(IS_I2C_IT(it)); if (hperh->perh->IVS & it) status = SET; return status; } /** * @brief Get the status of I2C interrupt flag. * @param hperh: Pointer to a i2c_handle_t structure. * @param flag: Specifies the I2C interrupt flag. * This parameter can be one of the @ref i2c_interrupt_t. * @retval Status: * - 0: RESET * - 1: SET */ flag_status_t ald_i2c_get_flag_status(ald_i2c_handle_t *hperh, ald_i2c_interrupt_t flag) { assert_param(IS_I2C_TYPE(hperh->perh)); assert_param(IS_I2C_IT(flag)); if (hperh->perh->RIF & flag) return SET; return RESET; } /** * @brief Get the status of interrupt flag and interupt source. * @param hperh: Pointer to a i2c_handle_t structure. * @param flag: Specifies the I2C interrupt flag. * This parameter can be one of the @ref i2c_interrupt_t. * @retval Status: * - 0: RESET * - 1: SET */ flag_status_t ald_i2c_get_mask_flag_status(ald_i2c_handle_t *hperh, ald_i2c_interrupt_t flag) { assert_param(IS_I2C_TYPE(hperh->perh)); assert_param(IS_I2C_IT(flag)); if (hperh->perh->IFM & flag) return SET; return RESET; } /** * @brief Clear the I2C interrupt flag. * @param hperh: Pointer to a i2c_handle_t structure. * @param flag: Specifies the I2C interrupt flag. * @retval None */ void ald_i2c_clear_flag_status(ald_i2c_handle_t *hperh, ald_i2c_interrupt_t flag) { assert_param(IS_I2C_TYPE(hperh->perh)); assert_param(IS_I2C_IT(flag)); hperh->perh->ICR = flag; return; } /** * @} */ /** @addtogroup I2C_Private_Functions * @{ */ /** * @brief Handle transmit complete flag for Master Transmit mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_send_tc(ald_i2c_handle_t *hperh) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_TXE | ALD_I2C_IT_NACK | ALD_I2C_IT_TC | ALD_I2C_IT_TCR); if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK) == RESET) SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); __UNLOCK(hperh); hperh->state = ALD_I2C_STATE_READY; if (hperh->mode == ALD_I2C_MODE_MEM) { if (hperh->mem_tx_cplt_cbk) hperh->mem_tx_cplt_cbk(hperh); } else { if (hperh->master_tx_cplt_cbk) hperh->master_tx_cplt_cbk(hperh); } return ALD_OK; } /** * @brief Handle transmit empty flag for Master Transmit mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_send_txe(ald_i2c_handle_t *hperh) { if (hperh->xfer_count != hperh->xfer_size) { hperh->perh->TXDATA = (*hperh->p_buff++); hperh->xfer_count++; } return ALD_OK; } /** * @brief Handle receive complete for Master Receive mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_recv_tc(ald_i2c_handle_t *hperh) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_RXNE | ALD_I2C_IT_TCR | ALD_I2C_IT_TC); if (READ_BIT(hperh->perh->CON2, I2C_CON2_AUTOEND_MSK) == RESET) SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); __UNLOCK(hperh); hperh->state = ALD_I2C_STATE_READY; if (hperh->mode == ALD_I2C_MODE_MEM) { if (hperh->mem_rx_cplt_cbk) hperh->mem_rx_cplt_cbk(hperh); } else { if (hperh->master_rx_cplt_cbk) hperh->master_rx_cplt_cbk(hperh); } return ALD_OK; } /** * @brief Handle receive not empty for Master Receive mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_recv_rxne(ald_i2c_handle_t *hperh) { if (hperh->xfer_size - hperh->xfer_count > 0) { (*hperh->p_buff++) = hperh->perh->RXDATA; hperh->xfer_count++; } else { return ALD_OK; } return ALD_OK; } /** * @brief Handle TXE flag for Slave Transmit mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_slave_send_txe(ald_i2c_handle_t *hperh) { if (hperh->xfer_size > hperh->xfer_count) { hperh->perh->TXDATA = (*hperh->p_buff++); hperh->xfer_count++; } return ALD_OK; } /** * @brief Handle RXNE flag for Slave Receive mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_slave_recv_rxne(ald_i2c_handle_t *hperh) { if (hperh->xfer_size > hperh->xfer_count) { (*hperh->p_buff++) = hperh->perh->RXDATA; hperh->xfer_count++; } else { return ALD_OK; } return ALD_OK; } /** * @brief Handle STOPF flag for Slave mode * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_slave_stopf(ald_i2c_handle_t *hperh) { ALD_I2C_DISABLE_IT(hperh, ALD_I2C_IT_ADDR | ALD_I2C_IT_NACK | ALD_I2C_IT_RXNE | ALD_I2C_IT_TXE | ALD_I2C_IT_STOP); hperh->mode = ALD_I2C_MODE_NONE; hperh->error_code = ALD_I2C_ERROR_NONE; __UNLOCK(hperh); if (hperh->state == ALD_I2C_STATE_BUSY_TX) { hperh->state = ALD_I2C_STATE_READY; if ((hperh->slave_tx_cplt_cbk) && (hperh->xfer_count != 0)) hperh->slave_tx_cplt_cbk(hperh); } else if (hperh->state == ALD_I2C_STATE_BUSY_RX) { hperh->state = ALD_I2C_STATE_READY; if ((hperh->slave_rx_cplt_cbk) && (hperh->xfer_count != 0)) hperh->slave_rx_cplt_cbk(hperh); } return ALD_OK; } /** * @brief Master sends target device address followed by internal memory address for write request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_req_write(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout) { if (hperh->init.addr_mode == ALD_I2C_ADDR_7BIT) CLEAR_BIT(hperh->perh->CON2, I2C_CON2_ADD10_MSK); else SET_BIT(hperh->perh->CON2, I2C_CON2_ADD10_MSK); MODIFY_REG(hperh->perh->CON2, I2C_CON2_SADD_MSK, dev_addr << I2C_CON2_SADD_POSS); CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RD_WRN_MSK); return ALD_OK; } /** * @brief Master sends target device address followed by internal memory address for read request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_master_req_read(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout) { if (hperh->init.addr_mode == ALD_I2C_ADDR_7BIT) CLEAR_BIT(hperh->perh->CON2, I2C_CON2_ADD10_MSK); else SET_BIT(hperh->perh->CON2, I2C_CON2_ADD10_MSK); MODIFY_REG(hperh->perh->CON2, I2C_CON2_SADD_MSK, dev_addr << I2C_CON2_SADD_POSS); SET_BIT(hperh->perh->CON2, I2C_CON2_RD_WRN_MSK); return ALD_OK; } /** * @brief Master sends target device address followed by internal memory address for write request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_req_mem_write(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout) { uint32_t tmp = hperh->perh->CON2; i2c_master_req_write(hperh, dev_addr, timeout); SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); if (add_size == ALD_I2C_MEMADD_SIZE_8BIT) { hperh->perh->TXDATA = I2C_MEM_ADD_LSB(mem_addr); } else { hperh->perh->TXDATA = I2C_MEM_ADD_MSB(mem_addr); if (i2c_wait_txe_to_timeout(hperh, timeout) != ALD_OK) return ALD_ERROR; hperh->perh->TXDATA = I2C_MEM_ADD_LSB(mem_addr); } if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TCR, RESET, timeout) != ALD_OK) { CLEAR_BIT(hperh->perh->CON2, I2C_CON2_RELOAD_MSK); ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TCR); return ALD_ERROR; } ald_i2c_clear_flag_status(hperh, ALD_I2C_IT_TCR); if ((hperh->state == ALD_I2C_STATE_BUSY_RX) && (hperh->mode == ALD_I2C_MODE_MEM)) { tmp = hperh->perh->CON2; tmp &= ~(I2C_CON2_RELOAD_MSK | I2C_CON2_NBYTES_MSK); hperh->perh->CON2 = tmp; tmp = hperh->perh->CON1; tmp &= ~I2C_CON1_NBYTES_MSK; hperh->perh->CON1 = tmp; } return ALD_OK; } /** * @brief Master sends target device address followed by internal memory address for read request. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param dev_addr: Target device address * @param mem_addr: Internal memory address * @param add_size: size of internal memory address * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_req_mem_read(ald_i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout) { uint32_t tim_count = 0; if (i2c_master_req_write(hperh, dev_addr, timeout) != ALD_OK) { __UNLOCK(hperh); return ALD_ERROR; } SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); if (add_size == ALD_I2C_MEMADD_SIZE_8BIT) { hperh->perh->TXDATA = I2C_MEM_ADD_LSB(mem_addr); } else { hperh->perh->TXDATA = I2C_MEM_ADD_MSB(mem_addr); if (i2c_wait_txe_to_timeout(hperh, timeout) != ALD_OK) { if (hperh->error_code == ALD_I2C_ERROR_AF) { SET_BIT(hperh->perh->CON2, I2C_CON2_START_MSK); return ALD_ERROR; } else { return ALD_TIMEOUT; } } hperh->perh->TXDATA = I2C_MEM_ADD_LSB(mem_addr); } while (!ALD_I2C_GET_FLAG(hperh, ALD_I2C_STAT_TXE)) { tim_count++; if (tim_count > 0xFFFF) return ALD_TIMEOUT; } return ALD_OK; } /** * @brief DMA I2C master transmit process complete callback. * @param argv: I2C handle * @retval None */ static void i2c_dma_master_send_cplt(void *argv) { ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TC, RESET, I2C_TIMEOUT_FLAG) == ALD_ERROR) hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); CLEAR_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->master_tx_cplt_cbk) hperh->master_tx_cplt_cbk(hperh); } } /** * @brief DMA I2C slave transmit process complete callback. * @param argv: I2C handle * @retval None */ static void i2c_dma_slave_send_cplt(void *argv) { ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; CLEAR_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->slave_tx_cplt_cbk) hperh->slave_tx_cplt_cbk(hperh); } } /** * @brief DMA I2C master receive process complete callback * @param argv: I2C handle * @retval None */ static void i2c_dma_master_recv_cplt(void *argv) { ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; if (i2c_wait_flag_change_to_timeout(hperh, ALD_I2C_STAT_TC, RESET, I2C_TIMEOUT_FLAG) == ALD_ERROR) hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); CLEAR_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->master_rx_cplt_cbk) hperh->master_rx_cplt_cbk(hperh); } } /** * @brief DMA I2C slave receive process complete callback. * @param argv: I2C handle * @retval None */ static void i2c_dma_slave_recv_cplt(void *argv) { ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; CLEAR_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->slave_rx_cplt_cbk) hperh->slave_rx_cplt_cbk(hperh); } } /** * @brief DMA I2C Memory Write process complete callback * @param argv: I2C handle * @retval None */ static void i2c_dma_mem_send_cplt(void *argv) { uint32_t cnt = 0xFFFFFF; ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; while (cnt--) { if ((hperh->perh->STAT & ALD_I2C_STAT_TC) != 0) break; } if (cnt == 0) hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); CLEAR_BIT(hperh->perh->CON1, I2C_CON1_TXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->mem_tx_cplt_cbk) hperh->mem_tx_cplt_cbk(hperh); } } /** * @brief DMA I2C Memory Read process complete callback * @param argv: I2C handle * @retval None */ static void i2c_dma_mem_recv_cplt(void *argv) { uint32_t cnt = 0xFFFFF; ald_i2c_handle_t* hperh = (ald_i2c_handle_t*)argv; while (cnt--) { if ((hperh->perh->STAT & ALD_I2C_STAT_TC) != 0) break; } if (cnt == 0) hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; SET_BIT(hperh->perh->CON2, I2C_CON2_STOP_MSK); CLEAR_BIT(hperh->perh->CON1, I2C_CON1_RXDMAEN_MSK); hperh->xfer_count = 0; hperh->state = ALD_I2C_STATE_READY; hperh->mode = ALD_I2C_MODE_NONE; __UNLOCK(hperh); if (hperh->error_code != ALD_I2C_ERROR_NONE) { if (hperh->error_callback) hperh->error_callback(hperh); } else { if (hperh->mem_rx_cplt_cbk) hperh->mem_rx_cplt_cbk(hperh); } } /** * @brief This function handles I2C Communication timeout. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param flag: specifies the I2C flag to check. * @param status: The checked flag status (SET or RESET). * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_wait_flag_change_to_timeout(ald_i2c_handle_t *hperh, uint32_t flag, flag_status_t status, uint32_t timeout) { uint32_t tickstart = 0; tickstart = ald_get_tick(); if (status == RESET) { while (ALD_I2C_GET_FLAG(hperh, flag) == RESET) { if ((timeout == 0) || ((ald_get_tick() - tickstart ) > timeout)) { hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; return ALD_TIMEOUT; } } } else { while (ALD_I2C_GET_FLAG(hperh, flag) != RESET) { if ((timeout == 0) || ((ald_get_tick() - tickstart ) > timeout)) { hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; return ALD_TIMEOUT; } } } return ALD_OK; } /** * @brief This function handles I2C Communication timeout for Master addressing phase. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param flag: specifies the I2C flag to check. * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_wait_master_addr_to_timeout(ald_i2c_handle_t *hperh, uint32_t flag, uint32_t timeout) { uint32_t tickstart = ald_get_tick(); while (ALD_I2C_GET_IT_FLAG(hperh, flag) == RESET) { if ((timeout == 0) || ((ald_get_tick() - tickstart ) > timeout)) { hperh->error_code = ALD_I2C_ERROR_TIMEOUT; return ALD_ERROR; } } return ALD_OK; } /** * @brief This function handles I2C Communication timeout for specific usage of TXE flag. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_wait_txe_to_timeout(ald_i2c_handle_t *hperh, uint32_t timeout) { uint32_t tickstart = ald_get_tick(); while (ALD_I2C_GET_FLAG(hperh, ALD_I2C_STAT_TXE) == RESET) { if (ALD_I2C_GET_IT_FLAG(hperh, ALD_I2C_IT_ARLO)) { hperh->error_code |= ALD_I2C_ERROR_ARLO; return ALD_ERROR; } if (ALD_I2C_GET_IT_FLAG(hperh, ALD_I2C_IT_NACK) == SET) { hperh->error_code |= ALD_I2C_ERROR_AF; return ALD_ERROR; } if ((timeout == 0) || ((ald_get_tick() - tickstart) > timeout)) { hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; return ALD_ERROR; } } return ALD_OK; } /** * @brief This function handles I2C Communication timeout for specific usage of RXNE flag. * @param hperh: Pointer to a i2c_handle_t structure that contains * the configuration information for the specified I2C. * @param timeout: Timeout duration * @retval Status, see @ref ald_status_t. */ static ald_status_t i2c_wait_rxne_to_timeout(ald_i2c_handle_t *hperh, uint32_t timeout) { uint32_t tickstart = ald_get_tick(); while (ALD_I2C_GET_FLAG(hperh, ALD_I2C_STAT_RXNE) == RESET) { if ((timeout == 0) || ((ald_get_tick() - tickstart) > timeout)) { hperh->error_code |= ALD_I2C_ERROR_TIMEOUT; return ALD_ERROR; } } return ALD_OK; } /** * @} */ /** * @} */ /** * @} */ /** * @} */