Compare commits

...

8 Commits

2 changed files with 73 additions and 27 deletions

View File

@ -17,6 +17,11 @@ if the ERROR LED is on, the possible cause is one of the following:
- tried reading the EDID from the monitor, but it is not connected - tried reading the EDID from the monitor, but it is not connected
- communication with monitor failed, due to damaged cable - communication with monitor failed, due to damaged cable
- monitor EDID is invalid - monitor EDID is invalid
- storing EDID in EEPROM failed
the firewall only acts as an I²C EEPROM at address 0x50 toward the HDMI device to provide the EDID information.
if the EDID switch is on the BLOCK position, the EEPROM is read only.
if the EDID switch is on the ALLOW position, writing the EEPROM is possible over the HDMI connection using standard I²C write operations.
limitations limitations
=========== ===========

95
main.c
View File

@ -12,7 +12,7 @@
#include "softi2c_master.h" #include "softi2c_master.h"
// enable UART debug // enable UART debug
#define DEBUG 0 #define DEBUG 1
#define EEPROM_ADDR 0x4000 // EEPROM start address #define EEPROM_ADDR 0x4000 // EEPROM start address
static bool eeprom_valid = true; // if the EDID can be read from EEPROM static bool eeprom_valid = true; // if the EDID can be read from EEPROM
@ -48,8 +48,14 @@ static bool hpd_fwd = false; // if the I²C source line is connected to sink
// if an I²C transaction started // if an I²C transaction started
static volatile bool i2c_transaction_new = false; static volatile bool i2c_transaction_new = false;
// if an I²C transaction with new data started // if an I²C transaction with incoming data started
static volatile bool i2c_input_new = false; static volatile bool i2c_input_new = false;
// if the byte should be programmed
static volatile bool i2c_prog = false;
// the address of the byte to be read or programmed
static volatile uint8_t i2c_addr = 0;
// the data to be programmed
static volatile uint8_t i2c_data = 0;
// blocking wait (in 10 us steps, up to UINT32_MAX / 10) // blocking wait (in 10 us steps, up to UINT32_MAX / 10)
static void wait_10us(uint32_t us10) static void wait_10us(uint32_t us10)
@ -130,19 +136,21 @@ static uint16_t edid_length(const uint8_t* edid)
if (1 == edid[18]) { // EDID 1.3/1.4 128-byte structure if (1 == edid[18]) { // EDID 1.3/1.4 128-byte structure
if (checksum_ok(&edid[0], 128)) { // ensure checksum of base EDID is ok if (checksum_ok(&edid[0], 128)) { // ensure checksum of base EDID is ok
const uint16_t length = 128 + (1 + edid[126]) * 128; // get length with all extensions const uint16_t length = 128 + edid[126] * 128; // get length with all extensions
puts("(v1.4 "); puts("(v1.3/1.4, ");
putn(edid[126] / 100); putn(edid[126] / 100);
putn((edid[126] / 10) % 10); putn((edid[126] / 10) % 10);
putn(edid[126] % 10); putn(edid[126] % 10);
puts(" extension) "); puts(" extension) ");
for (uint16_t i = 128; i < length && i < 256; i += 128) { // verify checksum of each extension (we actually only support one extension) if (edid[126]) { // extensions are present
if (!checksum_ok(&edid[i], 128)) { for (uint16_t i = 128; i < length && i < 256; i += 128) { // verify checksum of each extension (we actually only support one extension)
puts("extension CRC error\r\n"); if (!checksum_ok(&edid[i], 128)) {
return 0; // EDID is broken puts("extension CRC error\r\n");
return 0; // EDID is broken
}
} }
} }
puts("CRC ok\r\n"); puts("CRC OK\r\n");
return length; return length;
} else { } else {
puts("base CRC error\r\n"); puts("base CRC error\r\n");
@ -476,7 +484,7 @@ level pulse on the Hot Plug Detect pin. This pulse shall be at least 100 msec.
// verify stored EDID validity // verify stored EDID validity
if (!edid_length((uint8_t*)EEPROM_ADDR)) { if (!edid_length((uint8_t*)EEPROM_ADDR)) {
LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate I²C read fail LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate EDID is invalid
puts("EEPROM EDID invalid\r\n"); puts("EEPROM EDID invalid\r\n");
} }
@ -494,6 +502,40 @@ level pulse on the Hot Plug Detect pin. This pulse shall be at least 100 msec.
} }
} }
*/ */
if (i2c_prog) { // received data over i2c to be programmed
if (EDID_PORT->IDR.reg & EDID_PIN) { // EDID switched off
puts("I²C prog disabled\r\n");
I2C_CR2 &= I2C_CR2_ACK; // NACK next received byte to indicate programming it disabled
} else { // EDID programming allowed
/*
puts("I²C prog ");
puth(i2c_data);
puts("@");
puth(i2c_addr);
puts("\r\n");
*/
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
// disable DATA (e.g. EEPROM) write protection
if (0 == (FLASH_IAPSR & FLASH_IAPSR_DUL)) {
FLASH_DUKR = FLASH_DUKR_KEY1;
FLASH_DUKR = FLASH_DUKR_KEY2;
}
// save need EDID
*(uint8_t*)(EEPROM_ADDR + i2c_addr) = i2c_data; // write byte
while (FLASH_CR2 & FLASH_CR2_WPRG); // wait for write to complete
// check if programming failed
// we don't check for WR_PG_DIS (while checking EOP) because EEPROM isn't (and can't be) write protected
if (!(FLASH_IAPSR & FLASH_IAPSR_EOP)) {
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate programming failed
I2C_CR2 &= I2C_CR2_ACK; // NACK next received byte to indicate programming error
puts("EEPROM byte prog failed\r\n");
}
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
}
action = true; // re-run loop
i2c_prog = false; // clear flag
}
if (action) { // something has been performed, check if other flags have been set meanwhile if (action) { // something has been performed, check if other flags have been set meanwhile
action = false; // clear flag action = false; // clear flag
} else { // nothing down } else { // nothing down
@ -510,25 +552,24 @@ void awu(void) __interrupt(IRQ_AWU) // auto wakeup
void i2c(void) __interrupt(IRQ_I2C) // auto wakeup void i2c(void) __interrupt(IRQ_I2C) // auto wakeup
{ {
static uint8_t addr = 0; // EEPROM address to read
// make copies of status registers, since some bits might be cleared meanwhile // make copies of status registers, since some bits might be cleared meanwhile
const uint8_t sr1 = I2C_SR1; const uint8_t sr1 = I2C_SR1;
const uint8_t sr2 = I2C_SR2; const uint8_t sr2 = I2C_SR2;
const uint8_t sr3 = I2C_SR3; // clears ADDR after reading SR1 const uint8_t sr3 = I2C_SR3; // clears ADDR after reading SR1
if (sr1 & I2C_SR1_TXE) { // transmission buffer is empty if (sr1 & I2C_SR1_TXE) { // transmission buffer is empty
// transmit next byte I2C_DR = *(uint8_t*)(EEPROM_ADDR + i2c_addr++); // transmit next byte (even if invalid)
if (eeprom_valid) {
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++);
} else {
I2C_DR = 0xff;
}
} }
if (sr1 & I2C_SR1_RXNE) { // receive buffer is full if (sr1 & I2C_SR1_RXNE) { // receive buffer is full
const uint8_t data = I2C_DR; // read data (also clears flag) i2c_data = I2C_DR; // read data (also clears flag)
if (I2C_CR2 & I2C_CR2_ACK) { if (i2c_input_new) { // we just received the first byte
addr = data; // we only take the first address byte i2c_addr = i2c_data; // we only take the first address byte
i2c_input_new = false; // next byte is not the first
} else { // received data byte
i2c_prog = true; // notify main loop data needs to be programmed
}
if (EDID_PORT->IDR.reg & EDID_PIN) { // EDID programming is not enabled
I2C_CR2 &= I2C_CR2_ACK; // NACK next received byte to indicate programming it disabled
} }
I2C_CR2 &= I2C_CR2_ACK; // NACK next received byte
} }
if (sr1 & I2C_SR1_STOPF) { // stop received if (sr1 & I2C_SR1_STOPF) { // stop received
I2C_CR2 |= I2C_CR2_ACK; // this is just to clear the flag I2C_CR2 |= I2C_CR2_ACK; // this is just to clear the flag
@ -536,14 +577,14 @@ void i2c(void) __interrupt(IRQ_I2C) // auto wakeup
if (sr1 & I2C_SR1_ADDR) { // our slave address has been selected if (sr1 & I2C_SR1_ADDR) { // our slave address has been selected
i2c_transaction_new = true; // notify main loop transaction started i2c_transaction_new = true; // notify main loop transaction started
if (sr3 & I2C_SR3_TRA) { // start data transmission if (sr3 & I2C_SR3_TRA) { // start data transmission
if (eeprom_valid) { I2C_DR = *(uint8_t*)(EEPROM_ADDR + i2c_addr++); // transmit selected byte
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++); // transmit selected byte
} else {
I2C_DR = 0xff;
}
i2c_input_new = false; // notify we send data i2c_input_new = false; // notify we send data
} else { // we will receive data } else { // we will receive data
I2C_CR2 |= I2C_CR2_ACK; // ACK next received byte if (EDID_PORT->IDR.reg & EDID_PIN) { // EDID switched off
I2C_CR2 &= I2C_CR2_ACK; // NACK next received byte to indicate programming it disabled
} else {
I2C_CR2 |= I2C_CR2_ACK; // ACK next received byte
}
i2c_input_new = true; // notify we get data i2c_input_new = true; // notify we get data
} }
} }