From a3aaf50ecdb2827774ec84a89fea5e7e00925cfb Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Wed, 12 Jan 2022 18:29:28 +0300 Subject: [FL-2084] I2C memory read/write (#951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * furi_hal_i2c: read/write registers * remove i2c_acqure/release * i2c_is_device_ready without reading data Co-authored-by: あく --- firmware/targets/f6/furi_hal/furi_hal_i2c.c | 185 +++++++++++++++++++++++ firmware/targets/f7/furi_hal/furi_hal_i2c.c | 185 +++++++++++++++++++++++ firmware/targets/furi_hal_include/furi_hal_i2c.h | 116 ++++++++++++++ 3 files changed, 486 insertions(+) (limited to 'firmware') diff --git a/firmware/targets/f6/furi_hal/furi_hal_i2c.c b/firmware/targets/f6/furi_hal/furi_hal_i2c.c index a957e8b5..770fcdeb 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f6/furi_hal/furi_hal_i2c.c @@ -157,3 +157,188 @@ bool furi_hal_i2c_trx( return false; } } + +bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { + furi_check(handle); + + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + bool ret = true; + uint32_t timeout_tick = HAL_GetTick() + timeout; + + do { + while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + handle->bus->i2c->CR2 = + ((((uint32_t)(i2c_addr) & (I2C_CR2_SADD)) | (I2C_CR2_START) | (I2C_CR2_AUTOEND)) & + (~I2C_CR2_RD_WRN)); + + while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && + (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + LL_I2C_ClearFlag_NACK(handle->bus->i2c); + + // Clear STOP Flag generated by autoend + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + + // Generate actual STOP + LL_I2C_GenerateStopCondition(handle->bus->i2c); + + ret = false; + } + + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + } while(0); + + return ret; +} + +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); +} + +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout) { + furi_check(handle); + + uint8_t reg_data[2]; + bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); + *data = (reg_data[0] << 8) | (reg_data[1]); + + return ret; +} + +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); +} + +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout) { + furi_check(handle); + + uint8_t tx_data[2]; + tx_data[0] = reg_addr; + tx_data[1] = data; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); +} + +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout) { + furi_check(handle); + + uint8_t tx_data[3]; + tx_data[0] = reg_addr; + tx_data[1] = (data >> 8) & 0xFF; + tx_data[2] = data & 0xFF; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); +} + +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + furi_check(handle); + + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + bool ret = true; + uint8_t size = len + 1; + uint32_t timeout_tick = HAL_GetTick() + timeout; + + do { + while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + ret = false; + break; + } + } + + if(!ret) { + break; + } + + LL_I2C_HandleTransfer( + handle->bus->i2c, + i2c_addr, + LL_I2C_ADDRSLAVE_7BIT, + size, + LL_I2C_MODE_AUTOEND, + LL_I2C_GENERATE_START_WRITE); + + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { + if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { + if(size == len + 1) { + LL_I2C_TransmitData8(handle->bus->i2c, mem_addr); + } else { + LL_I2C_TransmitData8(handle->bus->i2c, (*data)); + data++; + } + size--; + } + + if(HAL_GetTick() >= timeout_tick) { + ret = false; + break; + } + } + + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + } while(0); + + return ret; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c index a957e8b5..770fcdeb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c.c @@ -157,3 +157,188 @@ bool furi_hal_i2c_trx( return false; } } + +bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { + furi_check(handle); + + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + bool ret = true; + uint32_t timeout_tick = HAL_GetTick() + timeout; + + do { + while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + handle->bus->i2c->CR2 = + ((((uint32_t)(i2c_addr) & (I2C_CR2_SADD)) | (I2C_CR2_START) | (I2C_CR2_AUTOEND)) & + (~I2C_CR2_RD_WRN)); + + while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && + (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + LL_I2C_ClearFlag_NACK(handle->bus->i2c); + + // Clear STOP Flag generated by autoend + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + + // Generate actual STOP + LL_I2C_GenerateStopCondition(handle->bus->i2c); + + ret = false; + } + + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + return false; + } + } + + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + } while(0); + + return ret; +} + +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); +} + +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout) { + furi_check(handle); + + uint8_t reg_data[2]; + bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); + *data = (reg_data[0] << 8) | (reg_data[1]); + + return ret; +} + +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); +} + +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout) { + furi_check(handle); + + uint8_t tx_data[2]; + tx_data[0] = reg_addr; + tx_data[1] = data; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); +} + +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout) { + furi_check(handle); + + uint8_t tx_data[3]; + tx_data[0] = reg_addr; + tx_data[1] = (data >> 8) & 0xFF; + tx_data[2] = data & 0xFF; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); +} + +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + furi_check(handle); + + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + bool ret = true; + uint8_t size = len + 1; + uint32_t timeout_tick = HAL_GetTick() + timeout; + + do { + while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { + if(HAL_GetTick() >= timeout_tick) { + ret = false; + break; + } + } + + if(!ret) { + break; + } + + LL_I2C_HandleTransfer( + handle->bus->i2c, + i2c_addr, + LL_I2C_ADDRSLAVE_7BIT, + size, + LL_I2C_MODE_AUTOEND, + LL_I2C_GENERATE_START_WRITE); + + while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { + if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { + if(size == len + 1) { + LL_I2C_TransmitData8(handle->bus->i2c, mem_addr); + } else { + LL_I2C_TransmitData8(handle->bus->i2c, (*data)); + data++; + } + size--; + } + + if(HAL_GetTick() >= timeout_tick) { + ret = false; + break; + } + } + + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + } while(0); + + return ret; +} diff --git a/firmware/targets/furi_hal_include/furi_hal_i2c.h b/firmware/targets/furi_hal_include/furi_hal_i2c.h index 35269ec5..5550ab08 100644 --- a/firmware/targets/furi_hal_include/furi_hal_i2c.h +++ b/firmware/targets/furi_hal_include/furi_hal_i2c.h @@ -84,6 +84,122 @@ bool furi_hal_i2c_trx( const uint8_t rx_size, uint32_t timeout); +/** Check if I2C device presents on bus + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param timeout timeout in ticks + * + * @return true if device present and is ready, false otherwise + */ +bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout); + +/** Perform I2C device register read (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout); + +/** Perform I2C device register read (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout); + +/** Perform I2C device memory read + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + +/** Perform I2C device register write (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout); + +/** Perform I2C device register write (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout); + +/** Perform I2C device memory + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + #ifdef __cplusplus } #endif -- cgit v1.2.3