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
- communication with monitor failed, due to damaged cable
- 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
===========

95
main.c
View File

@ -12,7 +12,7 @@
#include "softi2c_master.h"
// enable UART debug
#define DEBUG 0
#define DEBUG 1
#define EEPROM_ADDR 0x4000 // EEPROM start address
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
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;
// 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)
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 (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
puts("(v1.4 ");
const uint16_t length = 128 + edid[126] * 128; // get length with all extensions
puts("(v1.3/1.4, ");
putn(edid[126] / 100);
putn((edid[126] / 10) % 10);
putn(edid[126] % 10);
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 (!checksum_ok(&edid[i], 128)) {
puts("extension CRC error\r\n");
return 0; // EDID is broken
if (edid[126]) { // extensions are present
for (uint16_t i = 128; i < length && i < 256; i += 128) { // verify checksum of each extension (we actually only support one extension)
if (!checksum_ok(&edid[i], 128)) {
puts("extension CRC error\r\n");
return 0; // EDID is broken
}
}
}
puts("CRC ok\r\n");
puts("CRC OK\r\n");
return length;
} else {
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
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");
}
@ -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
action = false; // clear flag
} else { // nothing down
@ -510,25 +552,24 @@ void awu(void) __interrupt(IRQ_AWU) // 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
const uint8_t sr1 = I2C_SR1;
const uint8_t sr2 = I2C_SR2;
const uint8_t sr3 = I2C_SR3; // clears ADDR after reading SR1
if (sr1 & I2C_SR1_TXE) { // transmission buffer is empty
// transmit next byte
if (eeprom_valid) {
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++);
} else {
I2C_DR = 0xff;
}
I2C_DR = *(uint8_t*)(EEPROM_ADDR + i2c_addr++); // transmit next byte (even if invalid)
}
if (sr1 & I2C_SR1_RXNE) { // receive buffer is full
const uint8_t data = I2C_DR; // read data (also clears flag)
if (I2C_CR2 & I2C_CR2_ACK) {
addr = data; // we only take the first address byte
i2c_data = I2C_DR; // read data (also clears flag)
if (i2c_input_new) { // we just received the first 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
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
i2c_transaction_new = true; // notify main loop transaction started
if (sr3 & I2C_SR3_TRA) { // start data transmission
if (eeprom_valid) {
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++); // transmit selected byte
} else {
I2C_DR = 0xff;
}
I2C_DR = *(uint8_t*)(EEPROM_ADDR + i2c_addr++); // transmit selected byte
i2c_input_new = false; // notify we send 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
}
}