-
-
Save thehilde/d362c1c76a3aa486802a6b5d3f447951 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Program to generate (EDID) accessed for I2C device 0x50 | |
# | |
# This is a quick demo not a supported program, so don't expect | |
# correctness from it. | |
# | |
# Edid format from: | |
# https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#EDID_1.4_data_format | |
# | |
# Martin Hildebrandt | |
import math | |
def WriteBuffer(aArray, filename): | |
with open(filename, 'w', encoding='utf-8') as outfile: | |
for line in range(0,16): | |
outfile.write(("".join(hex(aArray[16*line+i])[2:].zfill(2).upper()+' ' for i in range(0,16)))+'\n') | |
def WriteBufferCcode(aArray, filename): | |
with open(filename, 'w', encoding='utf-8') as outfile: | |
for line in range(0,16): | |
outfile.write("\t\t0x"+("0x".join(hex(aArray[16*line+i])[2:].zfill(2)+', ' for i in range(0,16)))+'\n') | |
def createHeader(aArray): | |
aArray[0] = 0 | |
for i in range(1,7): aArray[i] = 0xFF | |
aArray[7] = 0 | |
def createChecksum(aArray): | |
aArray[127] = 256 - sum(aArray[0:127]) % 256 | |
def createManufacturer(aArray,Manufacturer): | |
word89 = (ord(Manufacturer[2])-64)&0x001f | (((ord(Manufacturer[1])-64)&0x001f) << 5) | (((ord(Manufacturer[0])-64)&0x001f) << 10) | |
aArray[9] = word89 & 0xFF | |
aArray[8] = (word89 >> 8) & 0xFF | |
def createProductcode(aArray,Code): | |
aArray[10] = Code & 0xFF | |
aArray[11] = (Code >> 8) & 0xFF | |
def createSerialnumber(aArray,Number): | |
aArray[12] = Number & 0xFF | |
aArray[13] = (Number >> 8) & 0xFF | |
aArray[14] = (Number >> 16) & 0xFF | |
aArray[15] = (Number >> 24) & 0xFF | |
def createWeekYearnumber(aArray,Week,Year): | |
# 16: Week of manufacture; or FF model year flag. Week numbering is not consistent between manufacturers. | |
# 17: Year of manufacture, or year of model, if model year flag is set. Year = datavalue + 1990. | |
if Week > 0: | |
aArray[16] = Week & 0xFF | |
else: | |
aArray[16] = 0xFF | |
aArray[17] = (Year - 1990) & 0xFF | |
def createEdidversion(aArray,aVersion): | |
aArray[18] = int(math.trunc(aVersion)) | |
aArray[19] = int(10*(aVersion-math.trunc(aVersion))) | |
def createDigitalInput(aArray, Depth, Interface): | |
a = ['undef', '6', '8', '10', '12', '14', '16', 'res'].index(Depth) | |
b = ['undef', 'HDMIa', 'HDMIb', '?', 'MDDI', 'DisplayPort', '?', '?'].index(Interface) | |
aArray[20] = 128 | ( a<<4) | (b) | |
def createAnalogInput(aArray, Levels = '+0.7/-.03', Blank = False, Separate = False, CompositeSync = False, GreenSync = False, VSyncMustDerrated = False): | |
a = ['+0.7/-.03', '+.714/-.386', '+1.0/-.04', '+.7/0]'].index(Levels) | |
b = (10 if Blank else 0) | (8 if Separate else 0) | (4 if CompositeSync else 0) | (2 if GreenSync else 0) | (1 if VSyncMustDerrated else 0) | |
aArray[20] = ( a<<5) | (b) | |
def createScreensizeCm(aArray,hsize,vsize): | |
aArray[21] = hsize | |
aArray[22] = vsize | |
def createGamma(aArray,gamma = 1.0): | |
aArray[23] = int((gamma - 1) * 100) | |
def createDPMSinfo(aArray,DisplayType, Standby = False, Suspend = False, ActiveOff = False, StandardsRGB = True, PreferredTimingInBlock1 = False, Continuoustimings = False ): | |
#aArray[20] was created by createDigitalInput or createAnalogInput | |
Digital = (aArray[20] & 128) != 0 | |
aArray[24] = (128 if Standby else 0) | (64 if Suspend else 0) | (32 if ActiveOff else 0) | |
if Digital: | |
a = ['RGB 4:4:4', 'RGB 4:4:4 + YCrCb 4:4:4', 'RGB 4:4:4 + YCrCb 4:2:2', 'RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2'].index(DisplayType) | |
else: | |
a = ['Monochrome or grayscale', 'RGB color', 'non-RGB color', 'undef'].index(DisplayType) | |
aArray[24] |= (a & 3) << 3 | |
aArray[24] |= (4 if StandardsRGB else 0) | (2 if PreferredTimingInBlock1 else 0) | (1 if Continuoustimings else 0) | |
def createChromaticitycoordinates(aArray, rx,ry,gx,gy,bx,by,wx,wy): | |
value = int(math.trunc(1024 * rx)) | |
aArray[27] = (value >> 2) & 0xff | |
aArray[25] = (value << 6) & 0xC0 | |
value = int(math.trunc(1024 * ry)) | |
aArray[28] = (value >> 2) & 0xff | |
aArray[25] |= (value << 4) & 0x30 | |
value = int(math.trunc(1024 * gx)) | |
aArray[29] = (value >> 2) & 0xff | |
aArray[25] |= (value << 2) & 0x0C | |
value = int(math.trunc(1024 * gy)) | |
aArray[30] = (value >> 2) & 0xff | |
aArray[25] |= (value << 0) & 0x03 | |
value = int(math.trunc(1024 * bx)) | |
aArray[31] = (value >> 2) & 0xff | |
aArray[26] = (value << 6) & 0xC0 | |
value = int(math.trunc(1024 * by)) | |
aArray[32] = (value >> 2) & 0xff | |
aArray[26] |= (value << 4) & 0x30 | |
value = int(math.trunc(1024 * wx)) | |
aArray[33] = (value >> 2) & 0xff | |
aArray[26] |= (value << 2) & 0x0C | |
value = int(math.trunc(1024 * wy)) | |
aArray[34] = (value >> 2) & 0xff | |
aArray[26] |= (value << 0) & 0x03 | |
timings = [ | |
'720x400 @ 70 Hz (VGA)', | |
'720x400 @ 88 Hz (XGA)', | |
'640x480 @ 60 Hz (VGA)', | |
'640x480 @ 67 Hz (Apple Macintosh II)', | |
'640x480 @ 72 Hz', | |
'640x480 @ 75 Hz', | |
'800x600 @ 56 Hz', | |
'800x600 @ 60 Hz', | |
'800x600 @ 72 Hz', | |
'800x600 @ 75 Hz', | |
'832x624 @ 75 Hz (Apple Macintosh II)', | |
'1024x768 @ 87 Hz, interlaced (1024x768i)', | |
'1024x768 @ 60 Hz', | |
'1024x768 @ 72 Hz', | |
'1024x768 @ 75 Hz', | |
'1280x1024 @ 75 Hz', | |
'1152x870 @ 75 Hz (Apple Macintosh II)', | |
'manufacturer-specific 6', | |
'manufacturer-specific 5', | |
'manufacturer-specific 4', | |
'manufacturer-specific 3', | |
'manufacturer-specific 2', | |
'manufacturer-specific 1', | |
'manufacturer-specific 0', | |
] | |
def createTimings(aArray, aList = ['640x480 @ 60 Hz (VGA)', '1024x768 @ 60 Hz']): | |
timingWord = 0 | |
for Timing in aList: | |
if Timing in timings: | |
timingWord |= 1 << (23-timings.index(Timing)) | |
aArray[37] = timingWord & 0xff | |
aArray[36] = (timingWord >> 8) & 0xff | |
aArray[35] = (timingWord >> 16)& 0xff | |
aspect = [ | |
'16:10', | |
'4:3', | |
'5:4', | |
'16:9' | |
] | |
def createStandardtiming(aArray, dictList = [{'Xres':1152, 'aspect' : '4:3', 'vfreq' : 60}]): | |
for i in range(38, 54, 2): | |
index = int((i - 38) / 2) | |
if len(dictList) > (index): | |
aArray[i] = int((dictList[(index)]['Xres'] / 8) - 31) | |
aArray[i+1] = aspect.index(dictList[(index)]['aspect']) << 6 | |
aArray[i+1] |= int(dictList[(index)]['vfreq'] - 60) | |
else: # Unused | |
aArray[i] = 1 | |
aArray[i+1] = 1 | |
stereomodes = [ | |
'none', | |
'none', | |
'field sequential right', | |
'2-way interleaved, right image', | |
'field sequential left', | |
'2-way interleaved, left image', | |
'4-way interleaved', | |
'side-by-side interleaved' | |
] | |
def createNameDescriptor(aArray, Index, Descriptor): | |
aArray[Index+0] = 0 | |
aArray[Index+1] = 0 | |
aArray[Index+2] = 0 | |
aArray[Index+3] = 0xFC | |
aArray[Index+4] = 0 | |
for i in range(5,18): | |
aArray[Index+i] = 0x0A | |
for i,char in enumerate(Descriptor['Display name'][:13]): | |
aArray[Index+5+i] = ord(char) | |
def createSerialDescriptor(aArray, Index, Descriptor): | |
aArray[Index+0] = 0 | |
aArray[Index+1] = 0 | |
aArray[Index+2] = 0 | |
aArray[Index+3] = 0xFF | |
aArray[Index+4] = 0 | |
for i in range(5,18): | |
aArray[Index+i] = 0x0A | |
for i,char in enumerate(Descriptor['Serial number'][:13]): | |
aArray[Index+5+i] = ord(char) | |
def createTimingDescriptor(aArray, Index, Descriptor): | |
value = int(Descriptor['Pixel clock kHz'] / 10) | |
aArray[Index+0] = value & 0xff | |
aArray[Index+1] = (value >> 8) & 0xff | |
value = int(Descriptor['Horizontal active pixels']) | |
aArray[Index+2] = value & 0xff | |
aArray[Index+4] = value >> 4 & 0xf0 | |
value = int(Descriptor['Horizontal blanking pixels']) | |
aArray[Index+3] = value & 0xff | |
aArray[Index+4] |= value >> 8 & 0x0f | |
value = int(Descriptor['Vertical active lines']) | |
aArray[Index+5] = value & 0xff | |
aArray[Index+7] = value >> 4 & 0xf0 | |
value = int(Descriptor['Vertical blanking lines']) | |
aArray[Index+6] = value & 0xff | |
aArray[Index+7] |= value >> 8 & 0x0f | |
value = int(Descriptor['Horizontal front porch pixels']) | |
aArray[Index+8] = value & 0xff | |
aArray[Index+11] = value >> 2 & 0xC0 | |
value = int(Descriptor['Horizontal sync pulse pixels']) | |
aArray[Index+9] = value & 0xff | |
aArray[Index+11] |= value >> 4 & 0x30 | |
value = int(Descriptor['Vertical front porch lines']) | |
aArray[Index+10] = (value << 4) & 0xf0 | |
aArray[Index+11] = value >> 2 & 0x0C | |
value = int(Descriptor['Vertical sync pulse lines']) | |
aArray[Index+10] |= value & 0x0f | |
aArray[Index+11] |= value >> 4 & 0x03 | |
value = int(Descriptor['Horizontal image size']) | |
aArray[Index+12] = value & 0xff | |
aArray[Index+14] = value >> 4 & 0xf0 | |
value = int(Descriptor['Vertical image size']) | |
aArray[Index+13] = value & 0xff | |
aArray[Index+14] |= value >> 8 & 0x0f | |
aArray[Index+15] = int(Descriptor['Horizontal border pixels']) & 0xff | |
aArray[Index+16] = int(Descriptor['Vertical border lines']) & 0xff | |
value = stereomodes.index(Descriptor['Stereomode']) | |
aArray[Index+17] = (value & 0x01) | ((value << 4) & 0x60) | |
if Descriptor['Interlaced']: | |
aArray[Index+17] |= 0x80 | |
if 'Analog composite sync type' in Descriptor: | |
if Descriptor['Analog composite sync type'] == 'bipolar': | |
aArray[Index+17] |= 0x08 | |
if Descriptor['Serration']: | |
aArray[Index+17] |= 0x04 | |
if Descriptor['Sync on red and blue lines additionally to green']: | |
aArray[Index+17] |= 0x02 | |
else: | |
if 'Serration' in Descriptor: | |
aArray[Index+17] |= 0x10 | |
if Descriptor['Serration']: | |
aArray[Index+17] |= 0x04 | |
if Descriptor['Horizontal sync polarity'] == 'positive': | |
aArray[Index+17] |= 0x02 | |
else: | |
aArray[Index+17] |= 0x18 | |
if Descriptor['Vertical sync polarity'] == 'positive': | |
aArray[Index+17] |= 0x04 | |
if Descriptor['Horizontal sync polarity'] == 'positive': | |
aArray[Index+17] |= 0x02 | |
def createDisplayRangeLimitsDescriptor(aArray, Index, Descriptor): | |
aArray[Index+0] = 0 | |
aArray[Index+1] = 0 | |
aArray[Index+2] = 0 | |
aArray[Index+3] = 0xFD | |
aArray[Index+4] = 0 | |
value = Descriptor['Minimum vertical field rate Hz'] | |
if value > 256: | |
aArray[Index+5] = (value - 256) & 0xff | |
aArray[Index+4] |= 1 | |
else: | |
aArray[Index+5] = value & 0xff | |
value = Descriptor['Maximum vertical field rate Hz'] | |
if value > 256: | |
aArray[Index+6] = (value - 256) & 0xff | |
aArray[Index+4] |= 2 | |
else: | |
aArray[Index+6] = value & 0xff | |
value = Descriptor['Minimum horizontal field rate Hz'] | |
if value > 256: | |
aArray[Index+7] = (value - 256) & 0xff | |
aArray[Index+4] |= 4 | |
else: | |
aArray[Index+7] = value & 0xff | |
value = Descriptor['Maximum horizontal field rate Hz'] | |
if value > 256: | |
aArray[Index+8] = (value - 256) & 0xff | |
aArray[Index+4] |= 8 | |
else: | |
aArray[Index+8] = value & 0xff | |
value = Descriptor['Maximum pixel clock rate MHz'] | |
aArray[Index+9] = int(value / 10) & 0xff | |
aArray[Index+10] = 0 # default GTF | |
aArray[Index+11] = 0x0A | |
aArray[Index+12] = 0x20 | |
aArray[Index+13] = 0x20 | |
aArray[Index+14] = 0x20 | |
aArray[Index+15] = 0x20 | |
aArray[Index+16] = 0x20 | |
aArray[Index+17] = 0x20 | |
# 'Horizontal border pixels': 0, | |
# 'Vertical border lines': 0, | |
# 'Analog': 0, | |
# 'Analog composite': 0, | |
# 'Sync on green only': 0 | |
def createDescriptor(aArray, dictList = []): | |
for i in range(54, 126, 18): | |
index = int((i - 54) / 18) | |
if len(dictList) > index: | |
if 'Pixel clock kHz' in dictList[index]: | |
print(i) | |
createTimingDescriptor(aArray, i, dictList[index]) | |
if 'Display name' in dictList[index]: | |
createNameDescriptor(aArray, i, dictList[index]) | |
if 'Serial number' in dictList[index]: | |
createSerialDescriptor(aArray, i, dictList[index]) | |
if 'Minimum vertical field rate Hz' in dictList[index]: | |
createDisplayRangeLimitsDescriptor(aArray, i, dictList[index]) | |
if __name__ == "__main__": | |
buffer = bytearray((0 for i in range(256))) | |
createHeader(buffer) | |
createManufacturer(buffer,'LAB') | |
createProductcode(buffer,815) | |
createSerialnumber(buffer,1) | |
createWeekYearnumber(buffer,45,2021) | |
createEdidversion(buffer,1.3) # Version 1.3 | |
createDigitalInput(buffer, '8', 'HDMIa' ) # HDMIa | |
createScreensizeCm(buffer,47,26) | |
createGamma(buffer, 2.6) # WS2812 | |
createDPMSinfo(buffer,'RGB 4:4:4' ) | |
createChromaticitycoordinates(buffer, 0.64,0.34,0.28,0.6,0.14,0.07,0.3,0.3) | |
createTimings(buffer, aList = ['640x480 @ 60 Hz (VGA)', '800x600 @ 60 Hz', '1024x768 @ 60 Hz']) | |
createStandardtiming(buffer) | |
DescriptorList = [{'Pixel clock kHz': 85500, | |
'Horizontal active pixels' : 1024, | |
'Horizontal blanking pixels' : 320, | |
'Vertical active lines': 768, | |
'Vertical blanking lines': 25, | |
'Horizontal front porch pixels': 44, | |
'Horizontal sync pulse pixels': 88, | |
'Vertical front porch lines': 3, | |
'Vertical sync pulse lines': 6, | |
'Horizontal image size': 476, | |
'Vertical image size': 268, | |
'Horizontal border pixels': 0, | |
'Vertical border lines': 0, | |
'Stereomode' : 'none', | |
'Interlaced' : False, | |
#'Analog composite sync type' : 'bipolar', | |
#'Sync on red and blue lines additionally to green' : True, | |
#'Serration' : False, | |
'Horizontal sync polarity' : 'positive', | |
'Vertical sync polarity' : 'positive', | |
}] | |
DescriptorList.append({'Minimum vertical field rate Hz': 60, | |
'Maximum vertical field rate Hz': 60, | |
'Minimum horizontal field rate Hz': 30, | |
'Maximum horizontal field rate Hz': 83, | |
'Maximum pixel clock rate MHz' : 94.5 # Spartan6 mit Speedgrade 2 | |
}) | |
DescriptorList.append({'Serial number': '0815'}) | |
DescriptorList.append({'Display name': 'My Screen'}) | |
createDescriptor(buffer, DescriptorList) | |
#print(bytes(buffer)) | |
createChecksum(buffer) | |
WriteBuffer(buffer, 'Output.txt') | |
WriteBufferCcode(buffer, 'Output.c') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment