diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index d663b884..2e6078f2 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -322,7 +322,6 @@ void cli_command_free_blocks(Cli* cli, string_t args, void* context) { void cli_command_i2c(Cli* cli, string_t args, void* context) { furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); - uint8_t test = 0; printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n" "Clock: 100khz, 7bit address\r\n" "\r\n"); @@ -331,8 +330,8 @@ void cli_command_i2c(Cli* cli, string_t args, void* context) { for(uint8_t row = 0; row < 0x8; row++) { printf("%x | ", row); for(uint8_t column = 0; column <= 0xF; column++) { - bool ret = furi_hal_i2c_rx( - &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, &test, 1, 2); + bool ret = furi_hal_i2c_is_device_ready( + &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2); printf("%c ", ret ? '#' : '-'); } printf("\r\n"); 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