1. 读取HDMI接收端EDID基础块
任务说明
本案例需要编写一个Linux用户空间程序,通过I2C总线读取显示器DDC通道中的EDID数据(128字节),并解析出厂商识别码、产品ID、基本显示参数(最大行/列分辨率、屏幕尺寸、信号类型等)。程序将直接操作I2C设备节点,不依赖额外库,适用于嵌入式Linux或标准Linux系统。
EDID读取原理
显示器通过I2C地址0x50(DDC2B接口)暴露EDID数据。读取时需先向地址0x00写入偏移量,然后连续读取128字节。解析规则遵循EDID 1.3/1.4规范:厂商识别码位于字节8-9(压缩为3个ASCII字符),产品ID位于字节10-11(小端序),基本参数位于字节21-25。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#define EDID_SIZE 128
#define DDC_ADDR 0x50
/* 解析厂商ID:字节8-9,每5位对应一个字母 */
void parse_manufacturer(const unsigned char *edid, char *out) {
int val = (edid[8] << 8) | edid[9];
out[0] = 'A' + ((val >> 10) & 0x1F) - 1;
out[1] = 'A' + ((val >> 5) & 0x1F) - 1;
out[2] = 'A' + (val & 0x1F) - 1;
out[3] = '\0';
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "用法: %s /dev/i2c-N\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("打开I2C设备失败");
return 1;
}
if (ioctl(fd, I2C_SLAVE, DDC_ADDR) < 0) {
perror("设置I2C从机地址失败");
close(fd);
return 1;
}
unsigned char edid[EDID_SIZE];
unsigned char offset = 0;
/* 写入EDID起始偏移 */
if (write(fd, &offset, 1) != 1) {
perror("写入偏移失败");
close(fd);
return 1;
}
/* 读取128字节EDID */
if (read(fd, edid, EDID_SIZE) != EDID_SIZE) {
perror("读取EDID失败");
close(fd);
return 1;
}
close(fd);
/* 校验头:00 FF FF FF FF FF FF 00 */
if (memcmp(edid, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8) != 0) {
fprintf(stderr, "EDID头校验失败,数据无效\n");
return 1;
}
/* 解析厂商ID */
char mfr[4];
parse_manufacturer(edid, mfr);
/* 产品ID(小端序) */
int product_id = edid[10] | (edid[11] << 8);
/* 基本参数 */
int edid_version = edid[18];
int edid_revision = edid[19];
int max_h = edid[21]; /* 最大水平尺寸(厘米) */
int max_v = edid[22]; /* 最大垂直尺寸(厘米) */
int gamma = edid[23]; /* 伽马值(需除以100) */
int video_input = edid[20];
/* 显示类型判断 */
const char *input_type;
if ((video_input & 0x80) == 0) {
input_type = "数字信号";
} else {
input_type = "模拟信号";
}
/* 输出解析结果 */
printf("========== EDID基本信息 ==========\n");
printf("厂商: %s\n", mfr);
printf("产品ID: 0x%04X (%d)\n", product_id, product_id);
printf("EDID版本: %d.%d\n", edid_version, edid_revision);
printf("信号类型: %s\n", input_type);
printf("最大水平尺寸: %d cm\n", max_h);
printf("最大垂直尺寸: %d cm\n", max_v);
printf("伽马值: %.2f\n", gamma / 100.0f);
printf("===================================\n");
return 0;
}
编译与运行
# 编译
gcc -o edid_read edid_read.c
# 运行(假设I2C总线为1)
sudo ./edid_read /dev/i2c-1
验证方法
将程序连接至真实显示器对应的I2C总线(通常为显卡DDC通道),运行后观察输出。判定标准:
- 程序能输出8字节EDID头校验通过后的解析信息;
- 厂商字段显示为3个大写字母(如DEL为戴尔、ACR为宏碁);
- 产品ID与显示器规格书或系统日志(
dmesg)中记录的ID一致; - 最大水平/垂直尺寸与显示器标称尺寸(英寸换算为厘米)相符。
若I2C总线号不确定,可通过i2cdetect -l列出所有总线,再使用i2cdetect -y <总线号>扫描地址0x50确认显示器所在总线。
判定标准
程序执行后若输出与显示器实际规格一致,且无I2C通信错误,即视为通过。若读取失败,检查:
- I2C总线号是否正确(可用
i2cdetect扫描确认); - 当前用户是否有
/dev/i2c-N的读写权限(需root或加入i2c组); - 显示器是否处于正常工作状态(非待机/休眠)。