From 07b631b2541d891e9185faf89c3854f0aaa217a6 Mon Sep 17 00:00:00 2001 From: hulk Date: Fri, 2 Oct 2026 23:07:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(i2c):=20=E5=AF=BC=E5=85=A5=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=20IIC=20=E9=A9=B1=E5=8A=A8=E6=94=B9=E8=BF=9B=EF=BC=88?= =?UTF-8?q?=E6=80=BB=E7=BA=BF=E6=81=A2=E5=A4=8D=E9=87=8D=E8=AF=95/?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E8=BE=85=E5=8A=A9=E5=87=BD=E6=95=B0/read=5Fr?= =?UTF-8?q?aw/scan=20AERR=20=E5=A4=84=E7=90=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Inc/i2c.h | 61 +----- Src/i2c.c | 607 +++++++++++++++++++++++++++++++++------------------ Src/tmp112.c | 3 +- 3 files changed, 403 insertions(+), 268 deletions(-) diff --git a/Inc/i2c.h b/Inc/i2c.h index eaa7097..79a6d90 100644 --- a/Inc/i2c.h +++ b/Inc/i2c.h @@ -89,9 +89,9 @@ i2c_result_t i2c_bus_reset(void); void i2c_scan(void); /*! - \brief write 16-bit data to I2C device + \brief write 16-bit data to I2C device (8-bit register address + 2 data bytes) \param[in] slave_addr: 7-bit slave address - \param[in] reg_addr: register address + \param[in] reg_addr: register address (8-bit) \param[in] data: pointer to 2-byte data array \param[out] none \retval i2c_result_t @@ -99,9 +99,9 @@ void i2c_scan(void); i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]); /*! - \brief read 16-bit data from I2C device + \brief read 16-bit data from I2C device (8-bit register address + 2 data bytes) \param[in] slave_addr: 7-bit slave address - \param[in] reg_addr: register address + \param[in] reg_addr: register address (8-bit) \param[out] data: pointer to 2-byte data buffer \retval i2c_result_t */ @@ -111,70 +111,33 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data /*! \brief write data to I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[in] data: pointer to data buffer \param[in] length: number of bytes to write (1-255) \param[out] none \retval i2c_result_t: operation result */ -i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length); +i2c_result_t i2c_write(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length); /*! \brief read data from I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[out] data: pointer to data buffer \param[in] length: number of bytes to read (1-255) \retval i2c_result_t: operation result */ -i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length); +i2c_result_t i2c_read(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length); -/* Convenience functions for common operations */ +/* Pure read response without sub-address phase (for commands that already sent read request parameters) */ /*! - \brief write single byte to I2C device + \brief read data from I2C device without register/sub-address phase \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address - \param[in] data: data byte to write - \retval i2c_result_t: operation result -*/ -i2c_result_t i2c_write_8bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data); - -/*! - \brief read single byte from I2C device - \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address - \param[out] data: pointer to data byte - \retval i2c_result_t: operation result -*/ -i2c_result_t i2c_read_8bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); - -/*! - \brief write 32-bit data to I2C device - \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address - \param[in] data: pointer to 4-byte data array - \retval i2c_result_t: operation result -*/ -i2c_result_t i2c_write_32bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[4]); - -/*! - \brief read 32-bit data from I2C device - \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address - \param[out] data: pointer to 4-byte data buffer - \retval i2c_result_t: operation result -*/ -i2c_result_t i2c_read_32bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); - -/*! - \brief read display panel parameters (multi-byte) - \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address \param[out] data: pointer to data buffer - \param[in] length: number of bytes to read (1-13) + \param[in] length: number of bytes to read (1-255) \retval i2c_result_t: operation result */ -i2c_result_t i2c_read_display_params(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length); +i2c_result_t i2c_read_raw(uint8_t slave_addr, uint8_t *data, uint8_t length); #ifdef DEBUG_VERBOSE /*! diff --git a/Src/i2c.c b/Src/i2c.c index 44ba655..814535b 100644 --- a/Src/i2c.c +++ b/Src/i2c.c @@ -34,6 +34,7 @@ void i2c_gpio_config(void) { i2c_result_t i2c_config(void) { /* configure I2C GPIO */ i2c_gpio_config(); + /* enable I2C clock */ rcu_periph_clock_enable(RCU_I2C); /* configure I2C clock */ @@ -149,7 +150,7 @@ i2c_result_t i2c_bus_reset(void) { } #ifdef DEBUG_VERBOSE - printf("I2C bus reset: bus recovered\r\n"); + printf("I2C bus reset: bus recovered\r\n"); #endif /* 8. Reconfigure & enable peripheral */ i2c_config(); @@ -197,10 +198,12 @@ void i2c_scan(void) { i2c_master_addressing(I2C0, (address << 1), I2C_TRANSMITTER); timeout = 0; - // 等待地址发送完成 - while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) && (timeout < I2C_TIME_OUT)) + // 等待地址发送完成或错误 + while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) && !i2c_flag_get(I2C0, I2C_FLAG_AERR) && (timeout < I2C_TIME_OUT)) timeout++; - if (timeout < I2C_TIME_OUT) { + + if (timeout < I2C_TIME_OUT && i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) { + // 设备响应 - 清除地址标志 i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); // printf("Found device at 0x%02X\r\n", address); const char* msg2_prefix = "Found device at 0x"; @@ -221,6 +224,9 @@ void i2c_scan(void) { } while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} found_devices++; + } else if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { + // 设备无响应 - 清除错误标志 + i2c_flag_clear(I2C0, I2C_FLAG_AERR); } // 生成停止条件 @@ -263,6 +269,53 @@ void i2c_scan(void) { } } +/** + * @brief 内部辅助函数:等待指定的I2C标志位被设置,带有超时。 + * @param[in] flag: 要等待的I2C事件标志。 + * @retval i2c_result_t: I2C_RESULT_SUCCESS 或 I2C_RESULT_TIMEOUT。 + */ +static i2c_result_t _i2c_wait_flag_timeout(uint32_t flag) +{ + uint16_t timeout = 0; + while(!i2c_flag_get(I2C0, flag)){ + if(timeout++ > I2C_TIME_OUT){ +#ifdef DEBUG_VERBOSE + const char* fname = "UNKNOWN"; + switch (flag) { + case I2C_FLAG_SBSEND: fname = "SBSEND"; break; + case I2C_FLAG_ADDSEND: fname = "ADDSEND"; break; + case I2C_FLAG_TBE: fname = "TBE"; break; + case I2C_FLAG_RBNE: fname = "RBNE"; break; + case I2C_FLAG_BTC: fname = "BTC"; break; + case I2C_FLAG_AERR: fname = "AERR"; break; + case I2C_FLAG_BERR: fname = "BERR"; break; + case I2C_FLAG_LOSTARB: fname = "LOSTARB"; break; + case I2C_FLAG_I2CBSY: fname = "I2CBSY"; break; + } + printf("I2C wait flag timeout: flag=0x%08X (%s)\r\n", (unsigned int)flag, fname); +#endif + return I2C_RESULT_TIMEOUT; + } + } + return I2C_RESULT_SUCCESS; +} + +/** + * @brief 内部辅助函数:等待指定的I2C标志位被清除,带有超时。 + * @param[in] flag: 要等待的I2C事件标志。 + * @retval i2c_result_t: I2C_RESULT_SUCCESS 或 I2C_RESULT_TIMEOUT。 + */ +static i2c_result_t _i2c_wait_flag_clear_timeout(uint32_t flag) +{ + uint16_t timeout = 0; + while(i2c_flag_get(I2C0, flag)){ + if(timeout++ > I2C_TIME_OUT){ + return I2C_RESULT_TIMEOUT; + } + } + return I2C_RESULT_SUCCESS; +} + i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) { i2c_state_t state = I2C_STATE_START; uint16_t timeout = 0; @@ -325,7 +378,7 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data } else { i2c_flag_clear(I2C0, I2C_FLAG_AERR); timeout =0; -#ifdef DEBUG_VERBOES +#ifdef DEBUG_VERBOSE printf("IIC write failed for Error Slave Address. \n"); #endif return I2C_RESULT_NACK; @@ -333,39 +386,28 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data case I2C_STATE_TRANSMIT_REG: /* wait until the transmit data buffer is empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } /* send register address */ i2c_data_transmit(I2C0, reg_addr); - timeout = 0; state = I2C_STATE_TRANSMIT_DATA; break; case I2C_STATE_TRANSMIT_DATA: /* wait until the transmit data buffer is empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } /* send register MSB value */ i2c_data_transmit(I2C0, data[0]); - timeout = 0; - + /* wait until the transmit data buffer is empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -381,13 +423,9 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data /* send register LSB value */ i2c_data_transmit(I2C0, data[1]); - timeout = 0; - + /* wait until BTC bit is set */ - while (!i2c_flag_get(I2C0, I2C_FLAG_BTC) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -413,25 +451,13 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data return I2C_RESULT_SUCCESS; case I2C_STATE_ERROR: - /* send a stop condition to I2C bus */ - i2c_stop_on_bus(I2C0); - - timeout = 0; - while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { - return I2C_RESULT_ERROR; - } - - i2c_flag_clear(I2C0, I2C_FLAG_AERR); - i2c_flag_clear(I2C0, I2C_FLAG_BERR); - i2c_flag_clear(I2C0, I2C_FLAG_LOSTARB); + /* I2C bus error, try to reset the bus and retry */ + i2c_bus_reset(); retry_count ++; if (retry_count >= I2C_MAX_RETRY) { -#ifdef DEBUG_VERBOES +#ifdef DEBUG_VERBOSE printf("IIC write failed after %d retries\n", I2C_MAX_RETRY); #endif return I2C_RESULT_ERROR; @@ -444,7 +470,7 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data /* small delay before retry */ delay_10us(10); break; - + default: state = I2C_STATE_START; break; @@ -472,11 +498,8 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data case I2C_STATE_START: timeout = 0; - // wait for bus to be idle - while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + /* wait for bus to be idle */ + if(_i2c_wait_flag_clear_timeout(I2C_FLAG_I2CBSY) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -484,15 +507,11 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data // send start condition i2c_start_on_bus(I2C0); state = I2C_STATE_SEND_ADDRESS; - timeout = 0; break; case I2C_STATE_SEND_ADDRESS: /* wait for start condition to be sent */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_SBSEND) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -540,10 +559,7 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data case I2C_STATE_TRANSMIT_DATA: /* wait for transmit buffer to be empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -551,28 +567,20 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data /* send register address */ i2c_data_transmit(I2C0, reg_addr); state = I2C_STATE_RESTART; - timeout = 0; break; case I2C_STATE_RESTART: /* wait for byte transfer complete BTC: Bit Transfer Complete */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } /* generate repeated start condition */ i2c_start_on_bus(I2C0); - + /* wait for repeated start condition to be sent */ - timeout = 0; - while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_SBSEND) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -588,10 +596,7 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data case I2C_STATE_RECEIVE_DATA: /* Wait for BTC (both bytes received) */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -621,13 +626,13 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data return I2C_RESULT_SUCCESS; case I2C_STATE_ERROR: - /* send stop condition to release bus */ - i2c_stop_on_bus(I2C0); + /* I2C bus error, try to reset the bus and retry */ + i2c_bus_reset(); retry_count++; if (retry_count >= I2C_MAX_RETRY) { -#ifdef DEBUG_VERBOES - printf("IIC read failed after %d retries\n", I2C_RETRY_MAX); +#ifdef DEBUG_VERBOSE + printf("IIC read failed after %d retries\n", I2C_MAX_RETRY); #endif return I2C_RESULT_ERROR; } @@ -652,13 +657,13 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data /*! \brief write data to I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[in] data: pointer to data buffer \param[in] length: number of bytes to write (1-255) \param[out] none \retval i2c_result_t: operation result */ -i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length) { +i2c_result_t i2c_write(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length) { i2c_state_t state = I2C_STATE_START; uint16_t timeout = 0; uint8_t retry_count = 0; @@ -666,6 +671,9 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint /* parameter validation */ if (data == NULL || slave_addr > 0x7F || length == 0) { +#ifdef DEBUG_VERBOSE + printf("I2C write invalid param: slave=0x%02X, len=%u, data=%p\r\n", slave_addr, length, data); +#endif return I2C_RESULT_INVALID_PARAM; } @@ -719,67 +727,71 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint state = I2C_STATE_TRANSMIT_REG; break; } else { + /* NACK received - address not acknowledged */ i2c_flag_clear(I2C0, I2C_FLAG_AERR); + i2c_stop_on_bus(I2C0); timeout = 0; #ifdef DEBUG_VERBOSE - printf("IIC write failed for Error Slave Address. \n"); + printf("I2C write failed for Error Slave Address. \n"); #endif return I2C_RESULT_NACK; } case I2C_STATE_TRANSMIT_REG: /* wait until the transmit data buffer is empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } - /* send register address */ - i2c_data_transmit(I2C0, reg_addr); - timeout = 0; + /* send register address, high byte first */ + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr >> 8)); + + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr & 0xFF)); state = I2C_STATE_TRANSMIT_DATA; break; case I2C_STATE_TRANSMIT_DATA: /* wait until the transmit data buffer is empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { - state = I2C_STATE_ERROR; - break; - } + while (data_index < length) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } - /* send data byte */ - i2c_data_transmit(I2C0, data[data_index]); - data_index++; + /* send data byte */ + i2c_data_transmit(I2C0, data[data_index]); + data_index++; - /* check for errors */ - if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { - i2c_stop_on_bus(I2C0); - return I2C_RESULT_NACK; - } else if (i2c_flag_get(I2C0, I2C_FLAG_BERR) || i2c_flag_get(I2C0, I2C_FLAG_LOSTARB)) { - i2c_stop_on_bus(I2C0); - return I2C_RESULT_ERROR; + /* check for errors */ + if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { + i2c_stop_on_bus(I2C0); + return I2C_RESULT_NACK; + } else if (i2c_flag_get(I2C0, I2C_FLAG_BERR) || i2c_flag_get(I2C0, I2C_FLAG_LOSTARB)) { + i2c_stop_on_bus(I2C0); + return I2C_RESULT_ERROR; + } } + if(state == I2C_STATE_ERROR) break; + /* check if all data has been sent */ if (data_index >= length) { /* wait until BTC bit is set for last byte */ - timeout = 0; - while (!i2c_flag_get(I2C0, I2C_FLAG_BTC) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } state = I2C_STATE_STOP; } - timeout = 0; break; case I2C_STATE_STOP: @@ -799,17 +811,8 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint return I2C_RESULT_SUCCESS; case I2C_STATE_ERROR: - /* send a stop condition to I2C bus */ - i2c_stop_on_bus(I2C0); - - timeout = 0; - while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - - i2c_flag_clear(I2C0, I2C_FLAG_AERR); - i2c_flag_clear(I2C0, I2C_FLAG_BERR); - i2c_flag_clear(I2C0, I2C_FLAG_LOSTARB); + /* I2C bus error, try to reset the bus and retry */ + i2c_bus_reset(); retry_count++; if (retry_count >= I2C_MAX_RETRY) { @@ -826,7 +829,7 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint /* small delay before retry */ delay_10us(10); break; - + default: state = I2C_STATE_START; break; @@ -838,12 +841,12 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint /*! \brief read data from I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[out] data: pointer to data buffer \param[in] length: number of bytes to read (1-255) \retval i2c_result_t: operation result */ -i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length) { +i2c_result_t i2c_read(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length) { i2c_state_t state = I2C_STATE_START; uint16_t timeout = 0; uint8_t retry_count = 0; @@ -858,6 +861,10 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 /* enable acknowledge */ i2c_ack_config(I2C0, I2C_ACK_ENABLE); +#ifdef DEBUG_VERBOSE + printf("I2C read start: slave=0x%02X reg=0x%02X len=%u\r\n", slave_addr, reg_addr, length); +#endif + while (retry_count < (uint8_t)I2C_MAX_RETRY) { switch (state) { case I2C_STATE_START: @@ -865,10 +872,7 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 data_index = 0; /* wait for bus to be idle */ - while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_clear_timeout(I2C_FLAG_I2CBSY) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -876,15 +880,11 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 /* send start condition */ i2c_start_on_bus(I2C0); state = I2C_STATE_SEND_ADDRESS; - timeout = 0; break; case I2C_STATE_SEND_ADDRESS: /* wait for start condition to be sent */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_SBSEND) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -912,67 +912,92 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 break; } + /* debug: show address ack and error flags */ +#ifdef DEBUG_VERBOSE + printf("I2C CLEAR_ADDRESS: write_phase=%d ADDSEND=%d AERR=%d\r\n", + (int)write_phase, + (int)i2c_flag_get(I2C0, I2C_FLAG_ADDSEND), + (int)i2c_flag_get(I2C0, I2C_FLAG_AERR)); +#endif + if (write_phase) { /* clear address flag (write phase) */ i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); state = I2C_STATE_TRANSMIT_DATA; } else { - /* read phase setup based on length */ + /* READ phase: handle ACK/ACKPOS based on requested length + - length == 1: disable ACK before clearing ADDR + - length == 2: set ACKPOS_NEXT and disable ACK before clearing ADDR + - length > 2: keep ACK enabled and clear ADDR; we'll handle N-2/BTF sequence later */ if (length == 1) { - /* single byte read: disable ACK before clearing ADDR */ i2c_ack_config(I2C0, I2C_ACK_DISABLE); - i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); - /* send STOP immediately after clearing ADDR for single byte */ - i2c_stop_on_bus(I2C0); +#ifdef DEBUG_VERBOSE + printf("I2C READ phase: length=1, disabling ACK before clearing ADDSEND\r\n"); +#endif } else if (length == 2) { - /* two bytes read: set POS=NEXT and disable ACK before clearing ADDR */ i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT); i2c_ack_config(I2C0, I2C_ACK_DISABLE); - i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); +#ifdef DEBUG_VERBOSE + printf("I2C READ phase: length=2, set ACKPOS_NEXT and disabling ACK before clearing ADDSEND\r\n"); +#endif } else { - /* multi-byte read: clear ADDR with ACK enabled */ - i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + /* length > 2: keep ACK enabled so slave will clock out data */ + i2c_ack_config(I2C0, I2C_ACK_ENABLE); +#ifdef DEBUG_VERBOSE + printf("I2C READ phase: length=%u (>2), keeping ACK enabled\r\n", length); +#endif } + + /* now clear address flag to release SCL and enter data phase */ + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + state = I2C_STATE_RECEIVE_DATA; } + timeout = 0; break; case I2C_STATE_TRANSMIT_DATA: /* wait for transmit buffer to be empty */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } - /* send register address */ - i2c_data_transmit(I2C0, reg_addr); + /* send register address, high byte first */ + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr >> 8)); + + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr & 0xFF)); state = I2C_STATE_RESTART; - timeout = 0; break; case I2C_STATE_RESTART: /* wait for byte transfer complete */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } +#ifdef DEBUG_VERBOSE + printf("I2C RESTART: after BTC, delay 5ms and issuing repeated START\r\n"); +#endif + /* small delay to allow slave device to prepare multi-byte data before repeated start */ + /* increased to 5ms to improve reliability for DLPC multi-byte reads */ + delay_ms(5); + /* generate repeated start condition */ i2c_start_on_bus(I2C0); - + /* wait for repeated start condition to be sent */ - timeout = 0; - while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_SBSEND) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -984,15 +1009,18 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 write_phase = false; state = I2C_STATE_CLEAR_ADDRESS; timeout = 0; +#ifdef DEBUG_VERBOSE + printf("I2C addressing sent for read (slave=0x%02X)\r\n", slave_addr); +#endif break; case I2C_STATE_RECEIVE_DATA: +#ifdef DEBUG_VERBOSE + printf("I2C RECEIVE_DATA: expecting %u bytes\r\n", length); +#endif if (length == 1) { /* single byte read */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -1000,10 +1028,7 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 state = I2C_STATE_STOP; } else if (length == 2) { /* two bytes read */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } @@ -1016,40 +1041,39 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 } else { /* multi-byte read (length > 2) */ while (data_index < length) { - /* wait for RBNE (receive buffer not empty) */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { - state = I2C_STATE_ERROR; - break; - } - - /* special handling for last 3 bytes */ - if (data_index == length - 3) { - /* wait for BTF (byte transfer finished) before reading N-2 */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout >= I2C_TIME_OUT) { + if (data_index < length - 2) { + /* normal bytes: wait for RBNE and read with ACK */ + if(_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { state = I2C_STATE_ERROR; break; } - /* disable ACK for last 2 bytes */ + data[data_index] = i2c_data_receive(I2C0); + data_index++; + } else if (data_index == length - 2) { + /* second to last byte: wait for BTF, then disable ACK and read */ + if(_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + /* disable ACK before reading N-1 byte */ i2c_ack_config(I2C0, I2C_ACK_DISABLE); - } - - /* read data byte */ - data[data_index] = i2c_data_receive(I2C0); - data_index++; - - /* send STOP after reading N-1 byte */ - if (data_index == length - 1) { + /* send STOP before reading N-1 byte */ i2c_stop_on_bus(I2C0); + /* read N-1 byte */ + data[data_index] = i2c_data_receive(I2C0); + data_index++; + } else { + /* last byte: wait for RBNE and read */ + if(_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + /* read last byte */ + data[data_index] = i2c_data_receive(I2C0); + data_index++; } - - timeout = 0; } + if(state == I2C_STATE_ERROR) break; state = I2C_STATE_STOP; } break; @@ -1072,8 +1096,11 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 return I2C_RESULT_SUCCESS; case I2C_STATE_ERROR: - /* send stop condition to release bus */ - i2c_stop_on_bus(I2C0); + /* I2C bus error, try to reset the bus and retry */ +#ifdef DEBUG_VERBOSE + printf("I2C_STATE_ERROR: resetting bus and retrying (retry_count=%u)\r\n", retry_count); +#endif + i2c_bus_reset(); /* reset ACK configuration */ i2c_ack_config(I2C0, I2C_ACK_ENABLE); @@ -1082,7 +1109,13 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 retry_count++; if (retry_count >= I2C_MAX_RETRY) { #ifdef DEBUG_VERBOSE - printf("I2C read failed after %d retries\n", I2C_MAX_RETRY); + /* Print diagnostic flags to help root-cause analysis */ + printf("I2C read final failure after %d retries, last_state=%d\r\n", I2C_MAX_RETRY, state); + printf(" FLAGS: AERR=%d BERR=%d LOSTARB=%d I2CBSY=%d\r\n", + (int)i2c_flag_get(I2C0, I2C_FLAG_AERR), + (int)i2c_flag_get(I2C0, I2C_FLAG_BERR), + (int)i2c_flag_get(I2C0, I2C_FLAG_LOSTARB), + (int)i2c_flag_get(I2C0, I2C_FLAG_I2CBSY)); #endif return I2C_RESULT_ERROR; } @@ -1101,39 +1134,179 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 break; } } + /* timeout path: provide debug info */ +#ifdef DEBUG_VERBOSE + printf("I2C read timeout (end state). slave=0x%02X, len=%u\r\n", slave_addr, length); + printf(" FLAGS: AERR=%d BERR=%d LOSTARB=%d I2CBSY=%d\r\n", + (int)i2c_flag_get(I2C0, I2C_FLAG_AERR), + (int)i2c_flag_get(I2C0, I2C_FLAG_BERR), + (int)i2c_flag_get(I2C0, I2C_FLAG_LOSTARB), + (int)i2c_flag_get(I2C0, I2C_FLAG_I2CBSY)); +#endif return I2C_RESULT_TIMEOUT; } -/* convenience functions for common operations */ -i2c_result_t i2c_write_8bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data) { - return i2c_write(slave_addr, reg_addr, &data, 1); -} +/*! + \brief read data from I2C device without register/sub-address phase + \param[in] slave_addr: slave device address (7-bit) + \param[out] data: pointer to data buffer + \param[in] length: number of bytes to read (1-255) + \retval i2c_result_t: operation result +*/ +i2c_result_t i2c_read_raw(uint8_t slave_addr, uint8_t *data, uint8_t length) +{ + i2c_state_t state = I2C_STATE_START; + uint16_t timeout = 0; + uint8_t retry_count = 0; + uint8_t data_index = 0; -i2c_result_t i2c_read_8bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) { - return i2c_read(slave_addr, reg_addr, data, 1); -} - -i2c_result_t i2c_write_32bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[4]) { - return i2c_write(slave_addr, reg_addr, data, 4); -} - -i2c_result_t i2c_read_32bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) { - return i2c_read(slave_addr, reg_addr, data, 4); -} - -/* 显示面板专用函数 - 支持读取显示界面参数 */ -i2c_result_t i2c_read_display_params(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length) { - /* 针对显示面板的多字节读取,支持13字节的完整参数读取 */ - if (length > 13) { + if (data == NULL || slave_addr > 0x7F || length == 0) { return I2C_RESULT_INVALID_PARAM; } - return i2c_read(slave_addr, reg_addr, data, length); + + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + + while (retry_count < (uint8_t)I2C_MAX_RETRY) { + switch (state) { + case I2C_STATE_START: + timeout = 0; + data_index = 0; + + if (_i2c_wait_flag_clear_timeout(I2C_FLAG_I2CBSY) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + + i2c_start_on_bus(I2C0); + state = I2C_STATE_SEND_ADDRESS; + break; + + case I2C_STATE_SEND_ADDRESS: + if (_i2c_wait_flag_timeout(I2C_FLAG_SBSEND) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + + i2c_master_addressing(I2C0, (slave_addr << 1), I2C_RECEIVER); + state = I2C_STATE_CLEAR_ADDRESS; + timeout = 0; + break; + + case I2C_STATE_CLEAR_ADDRESS: + while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (!i2c_flag_get(I2C0, I2C_FLAG_AERR)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { + i2c_flag_clear(I2C0, I2C_FLAG_AERR); + i2c_stop_on_bus(I2C0); + return I2C_RESULT_NACK; + } + + if (length == 1) { + i2c_ack_config(I2C0, I2C_ACK_DISABLE); + } else if (length == 2) { + i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT); + i2c_ack_config(I2C0, I2C_ACK_DISABLE); + } else { + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + } + + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + state = I2C_STATE_RECEIVE_DATA; + timeout = 0; + break; + + case I2C_STATE_RECEIVE_DATA: + if (length == 1) { + if (_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + data[0] = i2c_data_receive(I2C0); + state = I2C_STATE_STOP; + } else if (length == 2) { + if (_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_stop_on_bus(I2C0); + data[0] = i2c_data_receive(I2C0); + data[1] = i2c_data_receive(I2C0); + state = I2C_STATE_STOP; + } else { + while (data_index < length) { + if (data_index < length - 2) { + if (_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + data[data_index++] = i2c_data_receive(I2C0); + } else if (data_index == length - 2) { + if (_i2c_wait_flag_timeout(I2C_FLAG_BTC) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_ack_config(I2C0, I2C_ACK_DISABLE); + i2c_stop_on_bus(I2C0); + data[data_index++] = i2c_data_receive(I2C0); + } else { + if (_i2c_wait_flag_timeout(I2C_FLAG_RBNE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + data[data_index++] = i2c_data_receive(I2C0); + } + } + if (state == I2C_STATE_ERROR) break; + state = I2C_STATE_STOP; + } + break; + + case I2C_STATE_STOP: + while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT); + return I2C_RESULT_SUCCESS; + + case I2C_STATE_ERROR: + i2c_bus_reset(); + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT); + + retry_count++; + if (retry_count >= I2C_MAX_RETRY) { + return I2C_RESULT_ERROR; + } + + state = I2C_STATE_START; + timeout = 0; + delay_10us(10); + break; + + default: + state = I2C_STATE_START; + break; + } + } + + return I2C_RESULT_TIMEOUT; } #ifdef DEBUG_VERBOSE /*! \brief get status string for debugging - \param[in] status: i2c_status_t value + \param[in] status: i2c_result_t value \param[out] none \retval const char* status string */ diff --git a/Src/tmp112.c b/Src/tmp112.c index 7f5c71f..63e2f01 100644 --- a/Src/tmp112.c +++ b/Src/tmp112.c @@ -86,8 +86,7 @@ tmp112a_status_t tmp112a_read_temperature(tmp112a_result_t *result) { } void tmp112a_get_raw_temperature_value(uint8_t *value) { - // i2c_read_16bits(TMP112A_ADDR, TMP112A_TEMP_REG, value); - i2c_read(TMP112A_ADDR, TMP112A_TEMP_REG, value, 2); + i2c_read_16bits(TMP112A_ADDR, TMP112A_TEMP_REG, value); return; }