generated from hulk/gd32e23x_template_cmake_vscode
975 lines
35 KiB
C
975 lines
35 KiB
C
// ...existing code...
|
||
|
||
/*!
|
||
\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] 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_state_t state = I2C_STATE_START;
|
||
uint16_t timeout = 0;
|
||
uint8_t retry_count = 0;
|
||
uint8_t data_index = 0;
|
||
|
||
/* parameter validation */
|
||
if (data == NULL || slave_addr > 0x7F || length == 0) {
|
||
return I2C_RESULT_INVALID_PARAM;
|
||
}
|
||
|
||
while (retry_count < I2C_MAX_RETRY) {
|
||
switch (state) {
|
||
case I2C_STATE_START:
|
||
timeout = 0;
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
i2c_start_on_bus(I2C0);
|
||
timeout = 0;
|
||
state = I2C_STATE_SEND_ADDRESS;
|
||
break;
|
||
|
||
case I2C_STATE_SEND_ADDRESS:
|
||
/* wait for start condition to be sent. SBSEND flag */
|
||
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 */
|
||
i2c_master_addressing(I2C0, slave_addr << 1, I2C_TRANSMITTER);
|
||
timeout = 0;
|
||
state = I2C_STATE_CLEAR_ADDRESS;
|
||
break;
|
||
|
||
case I2C_STATE_CLEAR_ADDRESS:
|
||
/* wait for address to be acknowledged.ADDSEND set means i2c slave sends ACK */
|
||
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;
|
||
} else if (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) {
|
||
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
|
||
timeout = 0;
|
||
state = I2C_STATE_TRANSMIT_REG;
|
||
break;
|
||
} else {
|
||
i2c_flag_clear(I2C0, I2C_FLAG_AERR);
|
||
timeout = 0;
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("IIC 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) {
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
/* 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 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
state = I2C_STATE_STOP;
|
||
}
|
||
timeout = 0;
|
||
break;
|
||
|
||
case I2C_STATE_STOP:
|
||
/* 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
/* success */
|
||
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);
|
||
|
||
retry_count++;
|
||
if (retry_count >= I2C_MAX_RETRY) {
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("IIC write failed after %d retries\n", I2C_MAX_RETRY);
|
||
#endif
|
||
return I2C_RESULT_ERROR;
|
||
}
|
||
|
||
/* reset state machine for retry */
|
||
state = I2C_STATE_START;
|
||
timeout = 0;
|
||
|
||
/* small delay before retry */
|
||
delay_10us(10);
|
||
break;
|
||
|
||
default:
|
||
state = I2C_STATE_START;
|
||
break;
|
||
}
|
||
}
|
||
return I2C_RESULT_TIMEOUT;
|
||
}
|
||
|
||
/*!
|
||
\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[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_state_t state = I2C_STATE_START;
|
||
uint16_t timeout = 0;
|
||
uint8_t retry_count = 0;
|
||
bool write_phase = true;
|
||
uint8_t data_index = 0;
|
||
|
||
/* parameter validation */
|
||
if (data == NULL || slave_addr > 0x7F || length == 0) {
|
||
return I2C_RESULT_INVALID_PARAM;
|
||
}
|
||
|
||
/* enable acknowledge */
|
||
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;
|
||
|
||
/* 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:
|
||
/* 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) {
|
||
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:
|
||
/* wait for address to be acknowledged */
|
||
while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
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 */
|
||
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);
|
||
} 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);
|
||
} else {
|
||
/* multi-byte read: clear ADDR with ACK enabled */
|
||
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) {
|
||
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 */
|
||
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), I2C_RECEIVER);
|
||
|
||
/* switch to read phase */
|
||
write_phase = false;
|
||
state = I2C_STATE_CLEAR_ADDRESS;
|
||
timeout = 0;
|
||
break;
|
||
|
||
case I2C_STATE_RECEIVE_DATA:
|
||
if (length == 1) {
|
||
/* single byte read */
|
||
while ((!i2c_flag_get(I2C0, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
data[0] = i2c_data_receive(I2C0);
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
/* send STOP before reading the last two bytes */
|
||
i2c_stop_on_bus(I2C0);
|
||
/* read the two bytes back-to-back */
|
||
data[0] = i2c_data_receive(I2C0);
|
||
data[1] = i2c_data_receive(I2C0);
|
||
state = I2C_STATE_STOP;
|
||
} 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
/* disable ACK for last 2 bytes */
|
||
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) {
|
||
i2c_stop_on_bus(I2C0);
|
||
}
|
||
|
||
timeout = 0;
|
||
}
|
||
state = I2C_STATE_STOP;
|
||
}
|
||
break;
|
||
|
||
case I2C_STATE_STOP:
|
||
/* wait for stop condition to complete (only if not already sent) */
|
||
if (length > 2) {
|
||
/* for multi-byte reads, STOP was already sent */
|
||
/* just wait for the STOP bit to clear */
|
||
while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* reset ACK configuration for next operation */
|
||
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
|
||
i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
|
||
|
||
/* success */
|
||
return I2C_RESULT_SUCCESS;
|
||
|
||
case I2C_STATE_ERROR:
|
||
/* send stop condition to release bus */
|
||
i2c_stop_on_bus(I2C0);
|
||
|
||
/* reset ACK configuration */
|
||
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
|
||
i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
|
||
|
||
retry_count++;
|
||
if (retry_count >= I2C_MAX_RETRY) {
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("I2C read failed after %d retries\n", I2C_MAX_RETRY);
|
||
#endif
|
||
return I2C_RESULT_ERROR;
|
||
}
|
||
|
||
/* reset state machine for retry */
|
||
state = I2C_STATE_START;
|
||
write_phase = true;
|
||
timeout = 0;
|
||
|
||
/* small delay before retry */
|
||
delay_10us(10);
|
||
break;
|
||
|
||
default:
|
||
state = I2C_STATE_START;
|
||
break;
|
||
}
|
||
}
|
||
return I2C_RESULT_TIMEOUT;
|
||
}
|
||
|
||
/* compatibility functions for legacy code */
|
||
i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) {
|
||
return i2c_write(slave_addr, reg_addr, data, 2);
|
||
}
|
||
|
||
i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) {
|
||
return i2c_read(slave_addr, reg_addr, data, 2);
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
// ...existing code...
|
||
|
||
|
||
|
||
// ...existing code...
|
||
|
||
/*!
|
||
\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] 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_state_t state = I2C_STATE_START;
|
||
uint16_t timeout = 0;
|
||
uint8_t retry_count = 0;
|
||
uint8_t data_index = 0;
|
||
|
||
/* parameter validation */
|
||
if (data == NULL || slave_addr > 0x7F || length == 0) {
|
||
return I2C_RESULT_INVALID_PARAM;
|
||
}
|
||
|
||
while (retry_count < I2C_MAX_RETRY) {
|
||
switch (state) {
|
||
case I2C_STATE_START:
|
||
timeout = 0;
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
i2c_start_on_bus(I2C0);
|
||
timeout = 0;
|
||
state = I2C_STATE_SEND_ADDRESS;
|
||
break;
|
||
|
||
case I2C_STATE_SEND_ADDRESS:
|
||
/* wait for start condition to be sent. SBSEND flag */
|
||
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 */
|
||
i2c_master_addressing(I2C0, slave_addr << 1, I2C_TRANSMITTER);
|
||
timeout = 0;
|
||
state = I2C_STATE_CLEAR_ADDRESS;
|
||
break;
|
||
|
||
case I2C_STATE_CLEAR_ADDRESS:
|
||
/* wait for address to be acknowledged.ADDSEND set means i2c slave sends ACK */
|
||
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;
|
||
} else if (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) {
|
||
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
|
||
timeout = 0;
|
||
state = I2C_STATE_TRANSMIT_REG;
|
||
break;
|
||
} else {
|
||
i2c_flag_clear(I2C0, I2C_FLAG_AERR);
|
||
timeout = 0;
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("IIC 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) {
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
/* 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 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
state = I2C_STATE_STOP;
|
||
}
|
||
timeout = 0;
|
||
break;
|
||
|
||
case I2C_STATE_STOP:
|
||
/* 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
/* success */
|
||
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);
|
||
|
||
retry_count++;
|
||
if (retry_count >= I2C_MAX_RETRY) {
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("IIC write failed after %d retries\n", I2C_MAX_RETRY);
|
||
#endif
|
||
return I2C_RESULT_ERROR;
|
||
}
|
||
|
||
/* reset state machine for retry */
|
||
state = I2C_STATE_START;
|
||
timeout = 0;
|
||
|
||
/* small delay before retry */
|
||
delay_10us(10);
|
||
break;
|
||
|
||
default:
|
||
state = I2C_STATE_START;
|
||
break;
|
||
}
|
||
}
|
||
return I2C_RESULT_TIMEOUT;
|
||
}
|
||
|
||
/*!
|
||
\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[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_state_t state = I2C_STATE_START;
|
||
uint16_t timeout = 0;
|
||
uint8_t retry_count = 0;
|
||
bool write_phase = true;
|
||
uint8_t data_index = 0;
|
||
|
||
/* parameter validation */
|
||
if (data == NULL || slave_addr > 0x7F || length == 0) {
|
||
return I2C_RESULT_INVALID_PARAM;
|
||
}
|
||
|
||
/* enable acknowledge */
|
||
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;
|
||
|
||
/* 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:
|
||
/* 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) {
|
||
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:
|
||
/* wait for address to be acknowledged */
|
||
while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
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 */
|
||
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);
|
||
} 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);
|
||
} else {
|
||
/* multi-byte read: clear ADDR with ACK enabled */
|
||
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) {
|
||
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 */
|
||
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), I2C_RECEIVER);
|
||
|
||
/* switch to read phase */
|
||
write_phase = false;
|
||
state = I2C_STATE_CLEAR_ADDRESS;
|
||
timeout = 0;
|
||
break;
|
||
|
||
case I2C_STATE_RECEIVE_DATA:
|
||
if (length == 1) {
|
||
/* single byte read */
|
||
while ((!i2c_flag_get(I2C0, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
data[0] = i2c_data_receive(I2C0);
|
||
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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
/* send STOP before reading the last two bytes */
|
||
i2c_stop_on_bus(I2C0);
|
||
/* read the two bytes back-to-back */
|
||
data[0] = i2c_data_receive(I2C0);
|
||
data[1] = i2c_data_receive(I2C0);
|
||
state = I2C_STATE_STOP;
|
||
} 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) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
/* disable ACK for last 2 bytes */
|
||
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) {
|
||
i2c_stop_on_bus(I2C0);
|
||
}
|
||
|
||
timeout = 0;
|
||
}
|
||
state = I2C_STATE_STOP;
|
||
}
|
||
break;
|
||
|
||
case I2C_STATE_STOP:
|
||
/* wait for stop condition to complete */
|
||
while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) {
|
||
timeout++;
|
||
}
|
||
if (timeout >= I2C_TIME_OUT) {
|
||
state = I2C_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
/* reset ACK configuration for next operation */
|
||
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
|
||
i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
|
||
|
||
/* success */
|
||
return I2C_RESULT_SUCCESS;
|
||
|
||
case I2C_STATE_ERROR:
|
||
/* send stop condition to release bus */
|
||
i2c_stop_on_bus(I2C0);
|
||
|
||
/* reset ACK configuration */
|
||
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
|
||
i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
|
||
|
||
retry_count++;
|
||
if (retry_count >= I2C_MAX_RETRY) {
|
||
#ifdef DEBUG_VERBOSE
|
||
printf("I2C read failed after %d retries\n", I2C_MAX_RETRY);
|
||
#endif
|
||
return I2C_RESULT_ERROR;
|
||
}
|
||
|
||
/* reset state machine for retry */
|
||
state = I2C_STATE_START;
|
||
write_phase = true;
|
||
timeout = 0;
|
||
|
||
/* small delay before retry */
|
||
delay_10us(10);
|
||
break;
|
||
|
||
default:
|
||
state = I2C_STATE_START;
|
||
break;
|
||
}
|
||
}
|
||
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);
|
||
}
|
||
|
||
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) {
|
||
return I2C_RESULT_INVALID_PARAM;
|
||
}
|
||
return i2c_read(slave_addr, reg_addr, data, length);
|
||
}
|
||
|
||
// ...existing code...
|