From 77a65251680d06d6cd88ad96fb469d5f4c478429 Mon Sep 17 00:00:00 2001 From: yelvlab Date: Sat, 16 Aug 2025 04:06:46 +0800 Subject: [PATCH] rewrite read 16bits --- Src/i2c.c | 318 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 165 insertions(+), 153 deletions(-) diff --git a/Src/i2c.c b/Src/i2c.c index aee320c..15bbbff 100644 --- a/Src/i2c.c +++ b/Src/i2c.c @@ -156,7 +156,6 @@ i2c_result_t i2c_bus_reset(void) { return I2C_RECOVERY_OK; } -#ifdef DEBUG_VERBOSE /** * @brief 扫描I2C总线,查找连接的设备 * @@ -263,7 +262,6 @@ void i2c_scan(void) { while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} } } -#endif i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) { uint8_t state = I2C_STATE_START; @@ -429,198 +427,212 @@ i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data } i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) { + i2c_state_t state = I2C_STATE_START; + uint16_t timeout = 0; + uint8_t retry_count = 0; + bool write_phase = true; + uint8_t data_index = 0; + // 参数检查:防止空指针和非法地址 if (data == NULL || slave_addr > 0x7F) { return I2C_RESULT_INVALID_PARAM; } - i2c_state_t state = I2C_STATE_START; - bool read_cycle = false; - bool i2c_timeout_flag = false; - uint16_t timeout = 0; - uint8_t number_of_byte = 2; - /* enable acknowledge */ i2c_ack_config(I2C0, I2C_ACK_ENABLE); - while (!(i2c_timeout_flag)) { + while (retry_count < (uint8_t)I2C_MAX_RETRY) { switch (state) { case I2C_STATE_START: - if (RESET == read_cycle) { - /* i2c master sends start signal only when the bus is idle */ - while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout < I2C_TIME_OUT) { - /* whether to send ACK or not for the next byte */ - i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT); - } else { - // i2c_bus_reset(); - timeout = 0; - state = I2C_STATE_START; -#ifdef DEBUG_VERBOES - printf("i2c bus is busy in READ!\n"); -#endif - } - } - /* send the start signal */ - i2c_start_on_bus(I2C0); 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) { + state = I2C_STATE_ERROR; + break; + } + + // send start condition + i2c_start_on_bus(I2C0); state = I2C_STATE_SEND_ADDRESS; + timeout = 0; break; + case I2C_STATE_SEND_ADDRESS: - /* i2c master sends START signal successfully */ + /* 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 (RESET == read_cycle) { - i2c_master_addressing(I2C0, slave_addr << 1, I2C_TRANSMITTER); - state = I2C_STATE_CLEAR_ADDRESS; - } else { - i2c_master_addressing(I2C0, slave_addr << 1, I2C_RECEIVER); - i2c_ack_config(I2C0, I2C_ACK_DISABLE); - state = I2C_STATE_CLEAR_ADDRESS; - } - timeout = 0; - } else { - timeout = 0; - state = I2C_STATE_START; - read_cycle = RESET; -#ifdef DEBUG_VERBOES - printf("i2c master sends start signal timeout in READ!\n"); -#endif + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; } + + // send slave address + if (write_phase) { + /* write phase: send address with write bit */ + i2c_master_addressing(I2C0, (slave_addr << 1), I2C_TRANSMITTER); + } else { + /* read phase: send address with read bit */ + i2c_master_addressing(I2C0, (slave_addr << 1) | 0x01, I2C_RECEIVER); + } + + state = I2C_STATE_CLEAR_ADDRESS; + timeout = 0; break; + case I2C_STATE_CLEAR_ADDRESS: - /* address flag set means i2c slave sends ACK */ + /* wait for address to be acknowledged */ while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT)) { timeout++; } - if (timeout < I2C_TIME_OUT) { - /* Check for NACK before clearing address flag */ - if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { - /* NACK received - slave did not acknowledge address */ - i2c_flag_clear(I2C0, I2C_FLAG_AERR); - i2c_stop_on_bus(I2C0); -#ifdef DEBUG_VERBOES - printf("i2c NACK received for address 0x%02X in read!\n", slave_addr); -#endif - return I2C_RESULT_NACK; - } - - i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); - if ((SET == read_cycle) && (1 == number_of_byte)) { - /* send a stop condition to I2C bus */ - i2c_stop_on_bus(I2C0); - } - timeout = 0; + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* clear address flag, address flag set means i2c slave sends ACK */ + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + + if (write_phase) { state = I2C_STATE_TRANSMIT_DATA; } else { - timeout = 0; - state = I2C_STATE_START; - read_cycle = RESET; -#ifdef DEBUG_VERBOES - printf("i2c master clears address flag timeout in READ!\n"); -#endif + /* 读阶段:配置ACK位置和禁用ACK(针对2字节读取) */ + i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT); + i2c_ack_config(I2C0, I2C_ACK_DISABLE); + + state = I2C_STATE_RECEIVE_DATA; + data_index = 0; } + + timeout = 0; break; + case I2C_STATE_TRANSMIT_DATA: - if (RESET == read_cycle) { - /* 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) { - /* send the EEPROM's internal address to write to : only one byte address */ - i2c_data_transmit(I2C0, reg_addr); - timeout = 0; - } else { - timeout = 0; - state = I2C_STATE_START; - read_cycle = RESET; -#ifdef DEBUG_VERBOES - printf("i2c master wait data buffer is empty timeout in READ!\n"); -#endif - } - /* wait until BTC bit is set */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { - timeout++; - } - if (timeout < I2C_TIME_OUT) { - timeout = 0; - state = I2C_STATE_START; - read_cycle = SET; - } else { - timeout = 0; - state = I2C_STATE_START; - read_cycle = RESET; -#ifdef DEBUG_VERBOES - printf("i2c master sends register address timeout in READ!\n"); -#endif - } - } else { - while (number_of_byte) { - timeout++; - if (2 == number_of_byte) { - /* wait until BTC bit is set */ - while (!i2c_flag_get(I2C0, I2C_FLAG_BTC)); - /* send a stop condition to I2C bus */ - i2c_stop_on_bus(I2C0); - } - /* wait until RBNE bit is set */ - if (i2c_flag_get(I2C0, I2C_FLAG_RBNE)) { - /* read a byte from the EEPROM */ - *data = i2c_data_receive(I2C0); - /* point to the next location where the byte read will be saved */ - data++; - /* decrement the read bytes counter */ - number_of_byte--; - timeout = 0; - } - if (timeout > I2C_TIME_OUT) { - timeout = 0; - state = I2C_STATE_START; - read_cycle = 0; -#ifdef DEBUG_VERBOES - printf("i2c master sends data timeout in READ!\n"); -#endif - } - } - timeout = 0; - state = I2C_STATE_STOP; + /* 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) { + state = I2C_STATE_ERROR; + break; + } + + /* 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) { + 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) { + state = I2C_STATE_ERROR; + break; + } + + /* send slave address with read bit */ + i2c_master_addressing(I2C0, (slave_addr << 1) | 0x01, I2C_RECEIVER); + + /* switch to read phase */ + write_phase = false; + state = I2C_STATE_CLEAR_ADDRESS; + timeout = 0; + break; + + case I2C_STATE_RECEIVE_DATA: + /* 等待BTC标志表示第一个字节接收完成 */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* 发送STOP条件 */ + i2c_stop_on_bus(I2C0); + + /* 读取第一个字节 */ + data[0] = i2c_data_receive(I2C0); + + /* 等待第二个字节接收完成 */ + timeout = 0; + while ((!i2c_flag_get(I2C0, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* 读取第二个字节 */ + data[1] = i2c_data_receive(I2C0); + + state = I2C_STATE_STOP; + break; + case I2C_STATE_STOP: - /* i2c master sends STOP signal successfully */ + /* wait for stop condition to complete */ while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { timeout++; } - if (timeout < I2C_TIME_OUT) { - timeout = 0; - state = I2C_STATE_END; - i2c_timeout_flag = I2C_OK; - } else { - timeout = 0; - state = I2C_STATE_START; - read_cycle = 0; -#ifdef DEBUG_VERBOES - printf("i2c master sends stop signal timeout in READ!\n"); -#endif + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; } + + /* i2c master sends STOP signal successfully */ + /* success */ + return I2C_RESULT_SUCCESS; + + case I2C_STATE_ERROR: + /* send stop condition to release bus */ + i2c_stop_on_bus(I2C0); + + retry_count++; + if (retry_count >= I2C_MAX_RETRY) { +#ifdef DEBUG_VERBOES + printf("IIC read failed after %d retries\n", I2C_RETRY_MAX); +#endif + return I2C_RESULT_ERROR; + } + + /* reset state machine for retry */ + state = I2C_STATE_START; + write_phase = true; + data_index = 0; + timeout = 0; + + /* small delay before retry */ + delay_10us(10); break; + default: state = I2C_STATE_START; - read_cycle = 0; - i2c_timeout_flag = I2C_OK; - timeout = 0; -#ifdef DEBUG_VERBOES - printf("i2c master sends start signal in READ.\n"); -#endif break; } } - return I2C_RESULT_SUCCESS; + return I2C_RESULT_TIMEOUT; } #ifdef DEBUG_VERBOSE