对于EDID的信息有分析及编程

goodman 2017-11-18 16:34:44 5205

最近项目上要用到对显示器的分辨率的获取,自然想到了读取显示器的EDID信息,对于EDID的信息有标准的文档E-EDIDTM Standard.pdf(网上可以下到)本编文章是为了软件开发人员快速了解EDID并解析EDID数据获取显示器支持分辨率,在最后会提供一个类来解析EDID数据。
首先需要说明的是EDID的版本,EDID标准从1994年一路走来到2006年(我下的文档标注的日期),从最初的1.0发展了到了现在的1.4,但是市面上大部分的显示器的使用EDID版本是1.3,包括我测试的3台显示器都是的。
现在的EDID基本的信息表是128个字节(以下的偏移地址以128为总地址也就是00~7F),但是可以扩展N张表,我们这里只介绍基本的信息表的一些基本信息,整个EDID的信息白表:


EDID头(00~07)

EDID头是由固定的8个字节组成的,可以那拿它来验证是否是EDID数据

00 FF FF FF FF FF FF 00

厂商、产品号、序列号生产日期占10个字节(08~11)

生产厂家名称(08~09)

生产厂家由2个字节组成,2个字节一共16位最高为去除剩下15位,每5位代表一个字母(标准里面称为压缩的ASCII码)总共3个字母而且全是大写。
压缩的ASCII表示:00001-A 00010-B 00011-C.......11010-Z
由这3个字母组成的序列(例如APP)称作PNPID,这货由微软管理可以到去http://www.microsoft.com/whdc/system/pnppwr/pnp/pnpid.mspx
下载一个xsl表格里面罗列了所有的注册显示器厂商PNPID,刚才举例使用的APP就是苹果显示器。

ID产品码2个字节(0A~0B)
ID序列号4个字节(0C~0F)

序列号没有什么好说的

制造日期2个字节(10~11)

这个日期有2个模式一个制造年/周还有个叫模式年周(week model year),这个看10是怎么定义的了
地址10里面的值为(01~36),那代表制造周范围(1~54十进制),(37~FE)是保留值,FF代表地址11里面村的是model year.
地址11里面的值代表年具体含义由10决定如果10位FF那么11就代表model year,否则代表制造日期,公式如下:
Stored Value = (Year of Manufacture {or Model Year} - 1990)

EDID版本号和修正版本号(12~13)

地址12里面的值可称为主版本号,目前来讲可定是01, 00,02~FF是保留值地址13里面的值可称位修正版本号,目前来讲是03为主,新显示器可能会是04,05~FF是保留值

既定时序1既定时序2和标准时序(23,24, 26~36)

介绍完上面一些基本的信息和必要的信息后我们再来看看那些地址里面的值是可以决定分辨率的,在开始将分辨之前先简单的说下他支持的时序(这里的时序代表分辨率、刷新率)总共由3重时序,
前2种时序称为既定时序1和既定时序2,最后一种称为标准时序,既定时序1和既定时序2是的发展比较早在EDID形成之前就已经由工业联盟定义好了所以使用了2个字节存放既定时序.
既定时序1和2我直接引用1张图来说明,他们分别使用地址23和地址24,每个代表了8种时序如果支持相应的时序则相应的位是"1".

标准时序(26~36)

标准时序有16个字节,每2个字节为一组代表一种时序,最大支持8组,目前来讲按我测试的显示器没有用完8组,没有的使用0x1填充本身解析也比较简单看下图

图例使用的是地址26-27,其他(28-29等都市使用相同的解析方法)
水平分辨率:
水平分辨率=([26] + 31)8
垂直分辨:
垂直分辨率要看[27]的6&7位我们可以将[27]>>6即可得到值:
0(0 0)->16:10 ---> 垂直分辨 = 水平分辨率
10/16
1(0 1)->4:3 ---> 垂直分辨 = 水平分辨率3/4
2(1 0)->5:4 ---> 垂直分辨 = 水平分辨率
4/5
3(1 1)->16:9 ---> 垂直分辨 = 水平分辨率*9/16
刷新率:
Hz = [27] & 0x3f +60
这样我们就能获取显示器的支持的标注分辨率了,至于EDID信息的其他地址里面包含的内容可以自己去深究协议文档,这里不做过多讨论
最后给出一个C++的解析类EDIDParser

//edidparser.h

#ifndef EDIDPARSER _H
#defineEDIDPARSER _H
#include <cstdint>
#include <cstdlib>
#include <vector>
using namespace std;
struct ResolutionInfo
{
    ResolutionInfo(int width=0, int height=0, int fps=0)
    : width(width)
    , height(height)
    , fps(fps)
    {}
    int width; ///< 宽度
    int height; ///< 高度
    int fps; ///< 帧率
};
 /// @brief 定义分辨率列表
typedef std::vector<ResolutionInfo> ResolutionList;
class EDIDParser
{
public:
    EDIDParser();
    /// @brief 构造
    EDIDParser(const u_int8_t* data, int size);
    /// @brief 析构
    ~EDIDParser(void);
public:
    /// @brief 解析 EDID 数据
    /// @param [in] data 要解析的 EDID 数据
    /// @param [in] size 要解析的 EDID 数据大小,只有 512 以内的数据长度是有效的
    /// @return true/false
    bool parse(const u_int8_t* data, u_int32_t size);
    /// @brief 获取支持的所有分辨率
    const ResolutionList& supportedResolutions(void) const;
    /// @biref 检测是否支持指定分辨率
    /// @param [in] res 要检测的分辨率信息
    bool isSupported(const ResolutionInfo& res);
    /// @brief 获取厂商名称
    const char* vendor(void) const;
    /// @brief 获取主版本号
    int version(void) const;
    /// @brief 获取修订号
    int revision(void) const;
private:
    void parseTimingI(u_int8_t value);
    void parseTimingII(u_int8_t value);
    void parseTimingStarnd(const u_int8_t* data, u_int32_t size);
    ResolutionInfo getMaxResolution() const ;
private:
    u_int8_t _data[512]; ///< 保存的 EDID 数据
    u_int32_t _size; ///< 保存的 EDID 数据
    int _version; ///< 主版本号
    int _revision; ///< 修订号
    std::string _vendor; ///< 厂商名称
    ResolutionList _resolutions; ///< 所有支持的分辨率列表
};

//edidparser.cpp

#include <ctype.h>
#include <cstring>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "edidparser .h"
#define TIMING_I_800_600_60 0x01
#define TIMING_II_1024_768_60 0x08
EDIDParser::EDIDParser() :
    _size(0),
    _version(0),
    _revision(0)
{
}
EDIDParser::EDIDParser(const u_int8_t* data, int size)
{
    parse(data ,size);
}
EDIDParser::~EDIDParser()
{
}
int EDIDParser::version() const
{
    return (int)_data[0x12];
}
int EDIDParser::revision() const
{
    return (int)_data[0x13];
}

//只处理800x600@60fps其他的分辨对我没有意义
//可以自己处理相关的分辨率parseTimingII也是一样
void EDIDParser::parseTimingI(u_int8_t value)
{
    ResolutionInfo resolutionInfo;
    if(value & TIMING_I_800_600_60) {
        resolutionInfo.width = 800;
        resolutionInfo.height = 600;
        resolutionInfo.fps = 60;
        _resolutions.push_back(resolutionInfo);
    }
}
void EDIDParser::parseTimingII(u_int8_t value)
{
    ResolutionInfo resolutionInfo;
    if(value & TIMING_II_1024_768_60) {
        resolutionInfo.width = 1024;
        resolutionInfo.height = 768;
        resolutionInfo.fps = 60;
        _resolutions.push_back(resolutionInfo);
    }
}
//标准时序:16 Bytes, 起始地址:0x26 结束地址:0x36
// 每2个字节为一组(称为地址1,地址2),水平分辨率=([1]+31)*8,
// 垂直分辨以地址2里面的第7位和第6位决定的屏幕比计算
// 0 0 -> 16:10 垂直分辨率 = 水平分辨率*10/16
// 0 1 -> 4:3 \
// 1 0 -> 5:4 --    计算方式和16:10一样
// 1 1 -> 16:9/
// 地址2里面的第0位到第5位决定屏幕的刷新率Hz
// Hz = [2] & 0x3f + 60
// 如果地址1和地址2里面的值是0x1则表示该值是无效的值
void EDIDParser::parseTimingStarnd(const uint8_t* data, u_int32_t size)
{
    for(unsigned int i=0; i<size -2; i+=2) {
        if(data[i] == 0x1 && data[i+1] == 0x1)
            return;
        int width = (data[i]+31)*8;
        int fps = (data[i+1]&0x3f) + 60;
        int height = 0;
        u_int8_t aspectRatio = (data[i+1]>>6) & 0xff;
        switch(aspectRatio) {
            case 0:
                height = width*10/16;
                break;
            case 1:
                height = width*3/4;
                break;
            case 2:
                height = width*4/5;
                break;
            case 3:
                height = width*9/16;
                break;
            default:
                break;
        }
        _resolutions.push_back(ResolutionInfo(width, height, fps));
    }
}
ResolutionInfo EDIDParser::getMaxResolution() const
{
    ResolutionInfo maxResolution;
    ResolutionList::const_iterator iter = _resolutions.begin();
    for( ; iter != _resolutions.end(); ++iter) {
        if(iter->width * iter->height > maxResolution.width * maxResolution.height) {
            maxResolution.width = iter->width;
            maxResolution.height = iter->height;
            maxResolution.fps = iter->fps;
        }
    }
    return maxResolution;
}
bool EDIDParser::parse(const uint8_t* data, u_int32_t size)
{
    if(size < 128 || size > sizeof(_data))
        return false;
    if(data[0] !=0 || data[1]!=0xFF || data[2] != 0xFF || data[3] != 0xFF ||
       data[4] != 0xFF || data[5]!=0xFF || data[6]!= 0xFF || data[7] !=0) {
        printf("Invaild EDID information!");
        return false;
    }
    _size = size;
    std::memcpy(_data, data, size);
    parseTimingStarnd(data+0x26,16);
    if(_resolutions.empty()) {
        parseTimingI(*(data+0x23));
        parseTimingII(*(data+0x24));
    }
    ResolutionInfo maxResolution = getMaxResolution();
    if(maxResolution.width != 1920) {
        parseTimingII(*(data+0x24));
    }
    return _resolutions.empty() ? false : true;
}
const ResolutionList& EDIDParser::supportedResolutions(void) const
{
    return _resolutions;
}
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
goodman
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
goodman
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区