diff --git a/main.c b/main.c index 5c1c08e..433d1f1 100644 --- a/main.c +++ b/main.c @@ -15,12 +15,26 @@ static bool eeprom_valid = true; // if the EDID can be read from EEPROM // LED pin (sink for on) #define LED_PORT GPIO_PA #define LED_PIN PA3 -// pull ups for device facing I²C port -#define SDA_PU_PORT GPIO_PC -#define SDA_PU_PIN PC3 -#define SCL_PU_PORT GPIO_PC -#define SCL_PU_PIN PC4 +// I²C pins (sink and source) +#define SDA_SRC_PORT GPIO_PB +#define SDA_SRC_PIN PB5 +#define SCL_SRC_PORT GPIO_PB +#define SCL_SRC_PIN PB4 +#define SDA_SRC_PU_PORT GPIO_PC +#define SDA_SRC_PU_PIN PC3 +#define SCL_SRC_PU_PORT GPIO_PC +#define SCL_SRC_PU_PIN PC4 +#define SDA_SNK_PORT GPIO_PD +#define SDA_SNK_PIN PD2 +#define SCL_SNK_PORT GPIO_PD +#define SCL_SNK_PIN PD3 +#define SDA_SNK_PU_PORT GPIO_PD +#define SDA_SNK_PU_PIN PD4 +#define SCL_SNK_PU_PORT GPIO_PA +#define SCL_SNK_PU_PIN PA1 + static bool i2c_fwd = false; // if the I²C source lines are connected to sink +static bool sink_present = false; // if he sink is present (tested using sink DDC) // external EEPROM write protect pin #define WP_PORT GPIO_PC #define WP_PIN PC5 @@ -213,30 +227,47 @@ void main(void) UART1->BRR1.reg = 0x08; // set baud rate to 115200 (at 16 MHz) UART1->CR2.fields.TEN = 1; // enable TX + // configure I²C lines (sink and source sides) + SDA_SRC_PU_PORT->ODR.reg |= SDA_SRC_PU_PIN; // pull up SDA line + SDA_SRC_PU_PORT->CR1.reg |= SDA_SRC_PU_PIN; // switch pin to push pull + SDA_SRC_PU_PORT->DDR.reg |= SDA_SRC_PU_PIN; // switch pin to output + SCL_SRC_PU_PORT->ODR.reg |= SCL_SRC_PU_PIN; // pull up SCL line + SCL_SRC_PU_PORT->CR1.reg |= SCL_SRC_PU_PIN; // switch pin to push pull + SCL_SRC_PU_PORT->DDR.reg |= SCL_SRC_PU_PIN; // switch pin to output + SCL_SRC_PORT->ODR.reg |= SCL_SRC_PIN; // release SCL line + SCL_SRC_PORT->CR1.reg &= ~SCL_SRC_PIN; // switch pin to open-drain + SCL_SRC_PORT->DDR.reg |= SCL_SRC_PIN; // switch pin to output + SDA_SRC_PORT->ODR.reg |= SDA_SRC_PIN; // release SDA line + SDA_SRC_PORT->CR1.reg &= ~SDA_SRC_PIN; // switch pin to open-drain + SDA_SRC_PORT->DDR.reg |= SDA_SRC_PIN; // switch pin to output + SCL_SNK_PORT->ODR.reg |= SCL_SNK_PIN; // release SCL line + SCL_SNK_PORT->CR1.reg &= ~SCL_SNK_PIN; // switch pin to open-drain + SCL_SNK_PORT->DDR.reg |= SCL_SNK_PIN; // switch pin to output + SDA_SNK_PORT->ODR.reg |= SDA_SNK_PIN; // release SDA line + SDA_SNK_PORT->CR1.reg &= ~SDA_SNK_PIN; // switch pin to open-drain + SDA_SNK_PORT->DDR.reg |= SDA_SNK_PIN; // switch pin to output + // test I²C forwarding + SCL_SNK_PORT->ODR.reg &= ~SCL_SNK_PIN; // assert SCL line + SDA_SNK_PORT->ODR.reg &= ~SDA_SNK_PIN; // assert SDA line + if (0 == (SCL_SRC_PORT->IDR.reg & SCL_SRC_PIN) || 0 == (SDA_SRC_PORT->IDR.reg & SDA_SRC_PIN)) { + i2c_fwd = true; // remember the I²C line(s) are forwarded + puts("I²C lines forwarded\r\n"); + } + SCL_SNK_PORT->ODR.reg |= SCL_SNK_PIN; // release SCL line + SDA_SNK_PORT->ODR.reg |= SDA_SNK_PIN; // release SDA line + if (!i2c_fwd) { + wait_10us(5); // let sink 47k pull-up take effect + if ((SCL_SNK_PORT->IDR.reg & SCL_SNK_PIN) && (SDA_SNK_PORT->IDR.reg & SDA_SNK_PIN)) { + sink_present = true; + } + } + // 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 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 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 + if (i2c_fwd) { // I²C lines are not pulled up + SDA_SRC_PU_PORT->CR1.reg &= ~SDA_SRC_PU_PIN; // disable pull up + SDA_SRC_PU_PORT->DDR.reg &= ~SDA_SRC_PU_PIN; // switch pin to input + SCL_SRC_PU_PORT->CR1.reg &= ~SCL_SRC_PU_PIN; // disable pull up + SCL_SRC_PU_PORT->DDR.reg &= ~SCL_SRC_PU_PIN; // switch pin to input } // configure I²C @@ -287,7 +318,9 @@ we will only be able to provide 1 extension block since we can only respond to o rim(); // re-enable interrupts // even if we don't pull up HPD ourself, we should be able to respond to I²C within 20 ms - puts("I²C ready\r\n"); + if (!i2c_fwd) { + puts("I²C source ready\r\n"); + } // check if EDID should be copied EDID_PORT->DDR.reg &= ~EDID_PIN; // switch pin to output @@ -297,9 +330,19 @@ we will only be able to provide 1 extension block since we can only respond to o } else if (i2c_fwd) { // we are not the only master on the I²C sink lines puts("can't read EDID: I²C lines forwarded\r\n"); LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate error + } else if (!sink_present) { + puts("can't read EDID: sink not present\r\n"); + LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate error } else { IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog puts("reading sink EDID: "); + LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate we are saving EDID + SDA_SNK_PU_PORT->ODR.reg |= SDA_SNK_PU_PIN; // pull up SDA line + SDA_SNK_PU_PORT->CR1.reg |= SDA_SNK_PU_PIN; // switch pin to push pull + SDA_SNK_PU_PORT->DDR.reg |= SDA_SNK_PU_PIN; // switch pin to output + SCL_SNK_PU_PORT->ODR.reg |= SCL_SNK_PU_PIN; // pull up SCL line + SCL_SNK_PU_PORT->CR1.reg |= SCL_SNK_PU_PIN; // switch pin to push pull + SCL_SNK_PU_PORT->DDR.reg |= SCL_SNK_PU_PIN; // switch pin to output uint8_t i2c_rc = 1; // if the complete read succeeded if (!softi2c_master_setup(1)) { // start the I²C master to talk to sink i2c_rc = 2;