improve DDC forward and sink presence detection
This commit is contained in:
parent
2ffbe5da77
commit
b3d2ea58e5
101
main.c
101
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;
|
||||
|
|
Loading…
Reference in New Issue