fix i2c reset bus function

This commit is contained in:
2025-08-14 23:59:15 +08:00
parent 88f79f7eb0
commit 90486c6609
3 changed files with 112 additions and 42 deletions

View File

@@ -39,7 +39,10 @@ typedef enum {
I2C_RESULT_NACK, /* No acknowledge received */ I2C_RESULT_NACK, /* No acknowledge received */
I2C_RESULT_BUS_BUSY, /* Bus is busy */ I2C_RESULT_BUS_BUSY, /* Bus is busy */
I2C_RESULT_ERROR, /* General error */ I2C_RESULT_ERROR, /* General error */
I2C_RESULT_INVALID_PARAM /* Invalid parameter */ I2C_RESULT_INVALID_PARAM, /* Invalid parameter */
I2C_RECOVERY_OK,
I2C_RECOVERY_SDA_STUCK_LOW,
I2C_RECOVERY_SCL_STUCK_LOW
} i2c_result_t; } i2c_result_t;
/* I2C state machine enumeration */ /* I2C state machine enumeration */
@@ -69,24 +72,21 @@ typedef enum {
/* Function declarations */ /* Function declarations */
// TODO I2C Result
/*! /*!
\brief configure the I2C interface \brief configure the I2C interface
\param[in] none \param[in] none
\param[out] none \param[out] none
\retval i2c_result_t \retval i2c_result_t
*/ */
void i2c_config(void); i2c_result_t i2c_config(void);
// TODO I2C Result
/*! /*!
\brief reset I2C bus with proper recovery \brief reset I2C bus with proper recovery
\param[in] none \param[in] none
\param[out] none \param[out] none
\retval i2c_result_t \retval i2c_result_t
*/ */
void i2c_bus_reset(void); i2c_result_t i2c_bus_reset(void);
/*! /*!
\brief scan I2C bus for devices \brief scan I2C bus for devices

120
Src/i2c.c
View File

@@ -31,7 +31,7 @@ void i2c_gpio_config(void) {
\param[out] none \param[out] none
\retval none \retval none
*/ */
void i2c_config(void) { i2c_result_t i2c_config(void) {
/* configure I2C GPIO */ /* configure I2C GPIO */
i2c_gpio_config(); i2c_gpio_config();
/* enable I2C clock */ /* enable I2C clock */
@@ -44,6 +44,26 @@ void i2c_config(void) {
i2c_enable(I2C0); i2c_enable(I2C0);
/* enable acknowledge */ /* enable acknowledge */
i2c_ack_config(I2C0, I2C_ACK_ENABLE); i2c_ack_config(I2C0, I2C_ACK_ENABLE);
return I2C_RESULT_SUCCESS;
}
static bool i2c_wait_scl_high(uint16_t max_wait_time) {
while (max_wait_time--) {
if (gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN)) {
return true;
}
delay_10us(1);
}
return false;
}
/* generate one manual SCL pulse; return true if SCL observed high (no stuck/overstretch) */
static bool i2c_generate_scl_pulse(void) {
GPIO_BC(I2C_SCL_PORT) = I2C_SCL_PIN; /* drive SCL low */
delay_10us(1);
GPIO_BOP(I2C_SCL_PORT) = I2C_SCL_PIN; /* release SCL (open-drain -> high via pull-up) */
return i2c_wait_scl_high(200); /* wait up to ~2ms for clock stretching release */
} }
/*! /*!
@@ -52,31 +72,87 @@ void i2c_config(void) {
\param[out] none \param[out] none
\retval none \retval none
*/ */
void i2c_bus_reset(void) { i2c_result_t i2c_bus_reset(void) {
/* 1. Disable & deinit peripheral so pins can be fully controlled */
i2c_disable(I2C0);
i2c_deinit(I2C0); i2c_deinit(I2C0);
/* configure SDA/SCL for GPIO */
GPIO_BC(I2C_SCL_PORT) |= I2C_SCL_PIN; #ifdef DEBUG_VERBOSE
GPIO_BC(I2C_SDA_PORT) |= I2C_SDA_PIN; printf("I2C bus reset\r\n");
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, I2C_SCL_PIN); #endif
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
__NOP(); /* 2. Configure SCL/SDA as GPIO open-drain outputs with pull-up and release them */
__NOP(); gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
__NOP(); gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
__NOP();
__NOP();
GPIO_BOP(I2C_SCL_PORT) |= I2C_SCL_PIN;
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
GPIO_BOP(I2C_SDA_PORT) |= I2C_SDA_PIN;
/* connect I2C_SCL_PIN to I2C_SCL */
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN); gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN); gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
/* configure the I2CX interface */ gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* release SCL */
gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* release SDA */
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL = %d, SDA = %d\r\n", gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN), gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN));
#endif
/* 3. Double sample to confirm bus state */
delay_10us(1);
bool scl_value1 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN);
bool sda_value1 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN);
delay_10us(1);
bool scl_value2 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN);
bool sda_value2 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN);
/* 4. If SCL low -> stuck (cannot proceed) */
if (!scl_value2) {
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL stuck low\r\n");
#endif
return I2C_RECOVERY_SCL_STUCK_LOW;
}
/* 5. Fast path: bus idle */
if (scl_value1 && sda_value1 && scl_value2 && sda_value2) {
i2c_config();
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: bus idle\r\n");
#endif
return I2C_RECOVERY_OK;
}
/* 6. SDA low: attempt to free by generating up to I2C_RECOVERY_CLOCKS pulses */
if (scl_value2 && !sda_value2) {
bool sda_released = false;
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL will try to free SDA\r\n");
#endif
for (uint8_t i = 0; i < I2C_RECOVERY_CLOCKS && !sda_released; i++) {
if (!i2c_generate_scl_pulse()) {
return I2C_RECOVERY_SCL_STUCK_LOW; /* SCL failed to go high */
}
if (gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN)) {
sda_released = true;
}
}
if (!sda_released) {
return I2C_RECOVERY_SDA_STUCK_LOW;
}
/* 7. Generate a STOP condition to leave bus in idle state */
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: generating STOP condition\r\n");
#endif
gpio_bit_reset(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA low */
delay_10us(1);
gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* ensure SCL high */
delay_10us(1);
gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA rising while SCL high -> STOP */
delay_10us(1);
}
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: bus recovered\r\n");
#endif
/* 8. Reconfigure & enable peripheral */
i2c_config(); i2c_config();
return I2C_RECOVERY_OK;
} }
/** /**

View File

@@ -60,7 +60,7 @@ int main(void)
led_init(); led_init();
#ifdef DEBUG_VERBOSE #ifdef DEBUG_VERBOSE
char hello_world[] = {"Hello World!"}; char hello_world[] = {"Hello World!\r\n"};
for (uint8_t i = 0; i < sizeof(hello_world); i++) for (uint8_t i = 0; i < sizeof(hello_world); i++)
{ {
@@ -69,29 +69,23 @@ int main(void)
} }
while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {}
#endif
#endif
#ifdef DEBUG_VERBOSE
i2c_config(); i2c_config();
i2c_scan(); i2c_scan();
// i2c_bus_reset(); i2c_bus_reset();
// uint8_t sensor_data[2] = {0}; i2c_scan();
#endif
// i2c_read_16bits(0x2B, 0x7E, sensor_data);
// printf("Sensor Data: 0x%02X 0x%02X\r\n", sensor_data[0], sensor_data[1]);
// i2c_bus_reset();
ldc1612_iic_get_sensor_infomation(); ldc1612_iic_get_sensor_infomation();
#ifdef DEBUG_VERBOSE
i2c_scan();
#endif
while(1){ while(1){
command_process(); command_process();