Skip to content

Instantly share code, notes, and snippets.

@thehilde
Created December 7, 2021 05:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thehilde/d362c1c76a3aa486802a6b5d3f447951 to your computer and use it in GitHub Desktop.
Save thehilde/d362c1c76a3aa486802a6b5d3f447951 to your computer and use it in GitHub Desktop.
# 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