diff --git a/main.c b/main.c index 23737d3..f83649c 100644 --- a/main.c +++ b/main.c @@ -18,6 +18,7 @@ #define SDA_PU_PIN PC3 #define SCL_PU_PORT GPIO_PC #define SCL_PU_PIN PC4 +static bool i2c_fwd = false; // if the I²C source lines are connected to sink // external EEPROM write protect pin #define WP_PORT GPIO_PC #define WP_PIN PC5 @@ -110,12 +111,29 @@ void main(void) // configure I²C pull-ups SDA_PU_PORT->DDR.reg |= SDA_PU_PIN; // switch pin to output - SDA_PU_PORT->CR1.reg |= SDA_PU_PIN; // switch pin to push pull - SDA_PU_PORT->ODR.reg |= SDA_PU_PIN; // pull up SDA line + SDA_PU_PORT->CR1.reg &= ~SDA_PU_PIN; // switch pin to open-drain + SDA_PU_PORT->ODR.reg &= ~SDA_PU_PIN; // pull SDA line low + wait_10us(0); // wait shortly to clear tristate + SDA_PU_PORT->DDR.reg &= ~SDA_PU_PIN; // switch pin to input (already floating) + if (SDA_PU_PORT->IDR.reg & SDA_PU_PIN) { // line is pulled up + i2c_fwd = true; // remember the I²C line(s) are forwarded + } SCL_PU_PORT->DDR.reg |= SCL_PU_PIN; // switch pin to output - SCL_PU_PORT->CR1.reg |= SCL_PU_PIN; // switch pin to push pull - SCL_PU_PORT->ODR.reg |= SCL_PU_PIN; // pull up SCL line - // TODO verify if I²C lines are connected to monitor + SCL_PU_PORT->CR1.reg &= ~SCL_PU_PIN; // switch pin to open-drain + SCL_PU_PORT->ODR.reg &= ~SCL_PU_PIN; // pull SDA line low + wait_10us(0); // wait shortly to clear tristate + SCL_PU_PORT->DDR.reg &= ~SCL_PU_PIN; // switch pin to input (already floating) + if (SCL_PU_PORT->IDR.reg & SCL_PU_PIN) { // line is pulled up + i2c_fwd = true; // remember the I²C line(s) are forwarded + } + if (!i2c_fwd) { // I²C lines are not pulled up + SDA_PU_PORT->DDR.reg |= SDA_PU_PIN; // switch pin to output + SDA_PU_PORT->CR1.reg |= SDA_PU_PIN; // switch pin to push pull + SDA_PU_PORT->ODR.reg |= SDA_PU_PIN; // pull up SDA line + SCL_PU_PORT->DDR.reg |= SCL_PU_PIN; // switch pin to output + SCL_PU_PORT->CR1.reg |= SCL_PU_PIN; // switch pin to push pull + SCL_PU_PORT->ODR.reg |= SCL_PU_PIN; // pull up SCL line + } // configure external EEPROM (actually not used) WP_PORT->DDR.reg |= WP_PIN; // switch pin to output @@ -123,20 +141,22 @@ void main(void) WP_PORT->ODR.reg |= WP_PIN; // let write protect pulled up // configure I²C - GPIO_PB->CR1.reg |= (PB4 | PB5); // enable internal pull-up on SCL/SDA - GPIO_PB->DDR.reg &= ~(PB4 | PB5); // set SCL/SDA as input before it is used as alternate function by the peripheral - I2C_CR1 |= I2C_CR1_PE; // enable I²C peripheral (must be done before any other register is written) - I2C_CR2 |= I2C_CR2_STOP; // release lines - I2C_CR2 |= I2C_CR2_SWRST; // reset peripheral, in case we got stuck and the dog bit - while (0 == (GPIO_PB->IDR.reg & PB4)); // wait for SCL line to be released - while (0 == (GPIO_PB->IDR.reg & PB5)); // wait for SDA line to be released - I2C_CR2 &= ~I2C_CR2_SWRST; // release reset - I2C_CR1 |= I2C_CR1_PE; // re-enable I²C peripheral - I2C_FREQR = 16; // the peripheral frequency is 4 MHz (must match CPU frequency) - I2C_CR2 |= I2C_CR2_ACK; // enable acknowledgement if address matches - // since we are slave and not master, we don't have to set CCR - I2C_OARL = (0x50U << 1U); // set slave address for EEPROM - I2C_ITR |= (I2C_ITR_ITBUFEN | I2C_ITR_ITEVTEN); // enable buffer and event interrupts + if (!i2c_fwd) { // we will act as I²C slave EEPROM + GPIO_PB->CR1.reg |= (PB4 | PB5); // enable internal pull-up on SCL/SDA + GPIO_PB->DDR.reg &= ~(PB4 | PB5); // set SCL/SDA as input before it is used as alternate function by the peripheral + I2C_CR1 |= I2C_CR1_PE; // enable I²C peripheral (must be done before any other register is written) + I2C_CR2 |= I2C_CR2_STOP; // release lines + I2C_CR2 |= I2C_CR2_SWRST; // reset peripheral, in case we got stuck and the dog bit + while (0 == (GPIO_PB->IDR.reg & PB4)); // wait for SCL line to be released + while (0 == (GPIO_PB->IDR.reg & PB5)); // wait for SDA line to be released + I2C_CR2 &= ~I2C_CR2_SWRST; // release reset + I2C_CR1 |= I2C_CR1_PE; // re-enable I²C peripheral + I2C_FREQR = 16; // the peripheral frequency is 4 MHz (must match CPU frequency) + I2C_CR2 |= I2C_CR2_ACK; // enable acknowledgement if address matches + // since we are slave and not master, we don't have to set CCR + I2C_OARL = (0x50U << 1U); // set slave address for EEPROM + I2C_ITR |= (I2C_ITR_ITBUFEN | I2C_ITR_ITEVTEN); // enable buffer and event interrupts + } // configure hot plug detect HPD_PORT->DDR.reg |= HPD_PIN; // switch pin to output