DDC/CI协议主要用于控制显示设备的设置,比如亮度(brightness),对比度(Contrast)等。它是基于I 2C 协议的一个半双工的通信协议,一般是PC发起控制请求,显示设备被动应答的模式。本文仅仅是提供一种简单的C语言实现方式,提供大家参考。
有三个文件,它们依次是MCCS.h,DDCCI.H和DDCCI.c。MCCS是Minitor Control Command Set的缩写,定义了spec1.0对应的命令. DDCCI主要定义了DDCCI的发送和接收的接口函数,它覆盖了DDC/CI1.0spec的要求。此外,I 2C 的读写函数在这个简单的实现中没有被实现,因为在不同的平台上,不同的硬件设备上,它们差别较大,可以日后根据情况而分别进行填写。
MCCS.h源代码
#ifndef _MCCS_H
#define _MCCS_H
#if defined( __cplusplus ) || defined( c_plusplus )
extern "C" {
#endif
//==============================================================================//
// Continuous Control Code Values //
//==============================================================================//
#define DDCCI_MCCS_BRIGHTNESS 0x10
#define DDCCI_MCCS_CONTRAST 0x12
// The level of maximum luminance of RGB pixels
#define DDCCI_MCCS_RED_VIDEO_GAIN 0x16
#define DDCCI_MCCS_GREEN_VIDEO_GAIN 0x18
#define DDCCI_MCCS_BLUE_VIDEO_GAIN 0x 1A
// The level of minimum luminance of RGB pixels
#define DDCCI_MCCS_RED_VIDEO_BLACK_LEVEL 0x 6C
#define DDCCI_MCCS_GREEN_VIDEO_BLACK_LEVEL 0x6E
#define DDCCI_MCCS_BLUE_VIDEO_BLACK_LEVEL 0x70
// Adjusts the apparent spot size
#define DDCCI_MCCS_FOCUS 0x 1C
#define DDCCI_MCCS_HORIZONTAL_POSITION 0x20
#define DDCCI_MCCS_HORIZONTAL_SIZE 0x22
// Cause the right and left sides of the image to become more or less convex
#define DDCCI_MCCS_HORIZONTAL_PINCUSHION 0x24
// Moves the center section of the image toward the right or left side of display
#define DDCCI_MCCS_HORIZONTAL_BALANCE 0x26
// Increasing(decreasing) this value will shift the red pixels to the right(left)
// accros the image and the blude pixels lef(right) across the image with respect
// to green pixels.
#define DDCCI_MCCS_HORIZONTAL_MISCONVERGENCE 0x28
// Shifts the density of pixels from the left and right ends to the center of the image
#define DDCCI_MCCS_HORIZONTAL_LINEARITY 0x 2A
// Increasing(decreasing) this value shifts the density of pixels from the left(right)
// side to the right(left) side fo the image
#define DDCCI_MCCS_HORIZONTAL_LINEARITY_BALANCE 0x 2C
#define DDCCI_MCCS_VERTICAL_POSITION 0x30
#define DDCCI_MCCS_VERTICAL_SIZE 0x32
// Increasing(decreasing) this value causes the top and bottom sides of the image to
// become more(less) convex
#define DDCCI_MCCS_VERTICAL_PINCUSHION 0x34
// Increasing(decreasing) this value moves the center section of the image toward the
// top(bottom) of the display
#define DDCCI_MCCS_VERTICAL_PINCUSHION_BALANCE 0x36
// Increase(decreasing) this value shifts the red pixels up(down) across the image
// and the blue pixels down(up) across the image with respect to the green pixels
#define DDCCI_MCCS_VERTICAL_MISCONVERGENCE 0x38
// Increasing(decreasing) this value shifts the density of scan lines from the ends
// (center) to the center(ends) of the image
#define DDCCI_MCCS_VERTICAL_LINEARITY 0x 3A
// Increasing(decreasing) this value shifts the density of scan lines from the top
// (bottom) end to the bottom(top) end of the image
#define DDCCI_MCCS_VERTICAL_LINEARITY_BALANCE 0x 3C
// Increasing(decreasing) this value shifts the top section of the image to the
// right(left) with respect to the bottom section of image
#define DDCCI_MCCS_PARALLELOGRAM_DISTORTION 0x40
// The ratio between the horizontal size at the top of the image relative to the
// horizontal size at the bottom of the image
#define DDCCI_MCCS_TRAPEZOIDAL_DISTORTION 0x42
// Increasing(decreasing) this value rotatels the image(counter) clockwise about
// the center point of the image
#define DDCCI_MCCS_TILT 0x44
#define DDCCI_MCCS_TOP_CORNER_DISTORTION_CONTROL 0x46
#define DDCCI_MCCS_TOP_CORNER_DISTORTION_BALANCE 0x48
#define DDCCI_MCCS_BOTTOM_CORNER_DISTORTION_CONTROL 0x 4A
#define DDCCI_MCCS_BOTTOM_CORNER_DISTORTION_BALANCE 0x 4C
// Adjusting this value controls the horizontal/vertical piciture moire cancellation
#define DDCCI_MCCS_HORIZONTAL_MORIE 0x56
#define DDCCI_MCCS_VERTICAL_MORIE 0x58
// Addressable horinzontal/vertical screen
#define DDCCI_MCCS_HORADD 0x72
#define DDCCI_MCCS_VERADD 0x74
// Addressable buffer
#define DDCCI_MCCS_BUFFERADD 0X76
// update rate of the screen in 0.01HZ
#define DDCCI_MCCS_UPDATE 0x78
// Changing this value will change the focal plane of the optics
#define DDCCI_MCCS_ADJUST_FOCAL_PLANE 0x 7A
// Changing this value will change the zoom of the optics
#define DDCCI_MCCS_ADJUST_ZOOM 0x 7C
// Changing this value will change the trapezoid adjustment of the optics
#define DDCCI_MCCS_TRAPEZOID 0x7E
// Changing this value will change the keystone adjustment of the optics
#define DDCCI_MCCS_KEYSTONE 0x80
// Changing this value will cause the picutre to flip horizontally by means of
// the optics of the device
#define DDCCI_MCCS_HORFLIP 0x82
// Changing this value will cause the picutre to flip vertically by means of
// the optics of the device
#define DDCCI_MCCS_VERFLIP 0x84
// Changing this value will affect the scaling(input versus output) function of display
#define DDCCI_MCCS_DISPLAY_SCALING 0x86
// Increasing(decreasing) this value will increase(decrease) the velocity modulation of
// the horizontal scan as a function of the luminance level
#define DDCCI_MCCS_VELOCITY_SCAN_MODULATION 0x88
// Increasing the control increases the amplitude of the color difference components of
// the video signal. The result is an increase in the amount of pure color relative to
// white in the video. This control does not effect the RGB input, only the TV video
// input.
#define DDCCI_MCCS_TV_COLOR_SATURATION 0x 8A
// Increasing this control increases the amplitude of the hight frequency components of
// the video signal. The result is accentuation of fine details. This control deos not
// effect the RGB input, only the TV video inputs.
#define DDCCI_MCCS_TV_SHARPNESS 0x 8C
// Increasing this control increases the ratio between whites and blacks in the video.
// The control does not effect RGB input, only the TV video inputs.
#define DDCCI_MCCS_TV_CONTRAST 0x8E
// (a.k.a. Tint)Increase this control increase the wavelength of the color component
// of the video signal. The result is a shift towards red in the hue of all colors.
// This control does not effect the RGB input, only the TV video inputs.
#define DDCCI_MCCS_TV_HUE 0x90
// Increasing this control increases the black level of the video. The result is an
// increase of the lumen level of the video. A value of zero represents the darkest
// level possible. This control deos not effect the RGB input, only the TV video inputs.
#define DDCCI_MCCS_TV_BRIGHTNESS 0x92
//==============================================================================//
// Non-Continuous Control Code Values //
//==============================================================================//
//==============================================================================//
// Non-Continuous Control Code Values //
// ( Read and Write controls ) //
//==============================================================================//
// Changing this value chooses a different video input voltage for the display on input
// source select1. Format is reference white above blank, level of sync, below blank.
// 0 none selected
// 1 0.700, 0.300 (1.00 Vpp)
// 2 0.714, 0.286 (1.00 Vpp)
// 3 1.000, 0.400 (1.40 Vpp)
// 4 0.700, 0.000 (0.700 Vpp)
#define DDCCI_MCCS_INPUT_LEVEL_SELECT1 0x5E
// 0 None selected
// 1 OSD is disabled to appear
// 2 OSD is enabled to appear
#define DDCCI_MCCS_ON_SCREEN_DISPLAY 0xCA
// Changing this value chooses a different video input voltage for the display on input
// source select2. Format is reference white above blank, level of sync, below blank.
// 0 none selected
// 1 0.700, 0.300 (1.00 vpp)
// 2 0.714, 0.286 (1.00 vpp)
// 3 1.000, 0.400 (1.40 Vpp)
// 4 0.700, 0.000 (0.800 Vpp)
#define DDCCI_MCCS_INPUT_LEVEL_SELECT2 0xCC
// Changing this value selects a different video input source.
// 0 None selected
// 1 DB-15HD/VGA1
// 2 DB-15HD/VGA2
// 3 DB-15HD/VGA3
// 4 BNC/RGB1
// 5 BNC/RGB2
// 6 BNC/RGB3
// 7 EVC1
// 8 EVC2
// 9 EVC3
// 10 MAC1
// 11 MAC2
// 12 MAC3
// 13 RCA/ Composite Video1
// 14 RCA/ Composite Video2
// 15 RCA/ Composite Video3
// 16 S-Video1
// 17 S-Video2
// 18 S-Video3
// 19 SCART-Composite1
// 20 SCART-Composite2
// 21 SCART-RGB
// 22 SCART-S-video
// 23 Tuner1
// 24 Tuner2
// 25 Tuner3
// 26 YUV1
// 27 YUV2
// 28 YUV3
#define DDCCI_MCCS_INPUT_SOURCE_SELECT1 0x60
// Changing this value selects a different video input source.
// Coding of the different sources to select are the same as
// Input Source Select 1
#define DDCCI_MCCS_INPUT_SOURCE_SELECT2 0xCE
// Changing this value selects a different video input source.
// Coding of the different sources to select are the same as
// Input Source Select 1
#define DDCCI_MCCS_OUTPUT_SOURCE_SELECT1 0xD0
// Changing this value selects a different video input source.
// Coding of the different sources to select are the same as
// Input Source Select 1
#define DDCCI_MCCS_OUTPUT_SOURCE_SELECT2 0xD2
// Changing this value selects the video mode with respect to 2D or 3D.
// 0 None selected
// 1 Mono Mode
// 2 Enable Field-Sequential Right Eye First
// 3 Enable Field-Sequential Left Eye First
// 4 Enable 2-Way Interleaved Right Eye First
// 5 Enable 2-Way Interleaved Left Eye First
// 6 Enable 4-Way Interleaved, Display Stereo Buffer 0 (even scan lines)
// 7 Enable 4-Way Interleaved, Display Stereo Buffer 1 (odd scan lines)
// 8 Enable Side-by-Side Interleaved
#define DDCCI_MCCS_STEREO_MODE 0xD4
// DPMS Power Status
// 0 none selected
// 1 On
// 2 Standby
// 3 Suspend
// 4 Off
#define DDCCI_MCCS_DISPLAY_POWER_MODE 0xD6
#define DDCCI_MCCS_PRESET_COLOT_TEMP 0xD8
// 0: none selected
// 1: underscan
// 2: overscan
// 3: 16 x 9 letterbox
#define DDCCI_MCCS_SCAN_FORMAT 0xDA
// 0: None selected
// 1: Productivity
// 2: Games
// 3: Movies
#define DDCCI_MCCS_DISPLAY_MODE 0xDC
// 0: None selected
// 1: Stand alone
// 2: Slave (full PC control)
#define DDCCI_MCCS_OPERATION_MODE 0xDE
//==============================================================================//
// Non-Continuous Control Code Values //
// ( Read Only controls ) //
//==============================================================================//
// 0. None selected
// 1. Disabled
// 2. Enabled
#define DDCCI_MCCS_AUTO_SIZE_CENTER 0xA2
// 3. None selected
// 4. Negative
// 5. Positive
#define DDCCI_MCCS_POLARITY_HORIZONTAL_SYNCHRONIZATION 0xA4
// 6. None selected
// 7. Negative
// 8. Positive
#define DDCCI_MCCS_POLARITY_VERTICAL_SYNCHRONIZATION 0xA6
// 9. None selected
// 10. Separate
// 11. Digital Composite
// 12. Composite on Green
#define DDCCI_MCCS_SYNCHRONIZATION_TYPE 0xA8
// 13. none selected
// 14. Landscape
// 15. Portrait
#define DDCCI_MCCS_SCREEN_ORIENTATION 0xAA
// Horizontal frequency in Hz.
// The value to indicate the displays information is: FFh FFh FFh
#define DDCCI_MCCS_HORFREQUENCY 0xAC
// Vertical frequency in 0.01Hz
// The value to indicate the displays information is: FFh FFh
#define DDCCI_MCCS_VERTFREQUENCY 0xAE
//==============================================================================//
// Non-Continuous Control Code Values //
// ( Write Only controls ) //
//==============================================================================//
// This request causes the display to Degauss and does not alter any of its control
// or status values. By any number not equal to zero will make the monitor to degauss.
// The value zero will nullify the degauss request.
#define DDCCI_MCCS_DEGAUSS 0x01
// 0. None selected
// 1. Store Current settings in the monitor
// 2. Restore Factory default belonging to the particular video mode the monitor is in.
// 3. Restore the user saved values belonging to the particular video mode the monitor
// is in
#define DDCCI_MCCS_SETTINGS 0xB0
#if defined( __cplusplus ) || defined( c_plusplus )
}
#endif
#endif
DDCCI.h源代码:
#ifndef _DDCCI_H
#define _DDCCI_H
#include <assert.h>
#include <sys/time.h>
#include "MCCS.h"
#if defined( __cplusplus ) || defined( c_plusplus )
extern "C" {
#endif
#define DDCCI_MSG_SZ (32)
#define DDCCI_MSG_PAYLOAD_SZ (DDCCI_MSG_SZ - 4)
// The address of normal working mode for DDC/CI
#define DDCCI_MASTER_I 2C _ADDR 0x50
#define DDCCI_SLAVE_I 2C _ADDR 0x6E
// The external display dependent device
#define DDCCI_SLAVE_PRINTER_I 2C _ADDR 0xF0 // Touch Screen, Light pen or Remote Control Track Ball
#define DDCCI_SLAVE_AUDIO_DEV_I 2C _ADDR 0xF2 // Speaker / Microphone
#define DDCCI_SLAVE_SERIAL_COMMUNICATION_I 2C _ADDR 0xF4 // Home Network IF (power line modem)
#define DDCCI_SLAVE_CALIBRATION_DEV_I 2C _ADDR 0xF6 // Luminance Probe or Colorimeter
#define DDCCI_SLAVE_INPUT_DEV_I 2C _ADDR 0xF8 // IR keyboard and remote control pad (shared IR channel)
#define DDCCI_SLAVE_RESERVED1_I 2C _ADDR 0xFA // Reserved for future use
#define DDCCI_SLAVE_RESERVED2_I 2C _ADDR 0xFC // Reserved for future use
#define DDCCI_SLAVE_RESERVED3_I 2C _ADDR 0xFE // Reserved for future use
// Fixed address I 2C devices
#define DDCCI_SLAVE_SMART_BATTERY_CHARGER_I 2C _ADDR 0x12
#define DDCCI_SLAVE_SMART_BATTERY_SELECTOR_I 2C _ADDR 0x14
#define DDCCI_SLAVE_SMART_BATTERY_I 2C _ADDR 0x16
#define DDCCI_SLAVE_AUDIO_PROCESSOR_I 2C _ADDR 0x80
#define DDCCI_SLAVE_PAL_NTSC_DECODER_I 2C _ADDR 0x40
#define DDCCI_SLAVE_DDC2B_MONITOR_I 2C _ADDR 0xA0
#define DDCCI_VCP_REQUEST 0x01
#define DDCCI_VCP_REPLY 0x02
#define DDCCI_VCP_SET 0x03
#define DDCCI_VCP_RESET 0x09
#define DDCCI_IDENTIFICATION_REQUEST 0xF1
#define DDCCI_IDENTIFICATION_REPLY 0xE1
#define DDCCI_CAPABILITIES_REQUEST 0xF3
#define DDCCI_CAPABILITIES_REPLY 0xE3
#define DDCCI_DISPLAY_SELF_TEST_REQEUST 0xB1
#define DDCCI_TIMING_REQUEST 0x07
#define DDCCI_TIMING_REPLY 0x06
#define DDCCI_TABLE_READ_REQEUST 0xE2
#define DDCCI_TABLE_READ_REPLY 0xE4
#define DDCCI_TABLE_WRITE 0xE7
#define DDCCI_ENABLE_APPLICATION_REPORT 0xF5
#define DDCCI_SAVE_CURRENT_SETTINGS 0x 0C
#define DDCCI_TX( pDDCCI, data, len ) do { /
DDCCI_Delay( (pDDCCI) ); /
DDCCI_Send( (pDDCCI), (data), (len) ); /
} while ( 0 )
#define DDCCI_RX( pDDCCI, data, len ) do { /
DDCCI_Delay( (pDDCCI) ); /
DDCCI_Receive( (pDDCCI), (data), (len) ); /
} while ( 0 )
#define DDCCI_SendNullMsg( pDDCCI ) do { /
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_SLAVE ); /
if ( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_SLAVE ) /
DDCCI_TX( (pDDCCI), NULL, 0 ); /
} while ( 0 )
#define DDCCI_SaveCurrentSettings( pDDCCI ) do { /
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ); /
if ( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ) /
{ /
unsigned char _buf[ 1 ] = { DDCCI_SAVE_CURRENT_SETTINGS }; /
DDCCI_TX( (pDDCCI), _buf, sizeof( _buf ) ); /
} /
} while ( 0 )
#define DDCCI_GetVCPFeature( pDDCCI, mccs, valU32 ) do { /
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ); /
(valU32) = -1; /
if ( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ) /
{ /
unsigned char _buf[ DDCCI_MSG_SZ ]; /
_buf[ 0 ] = DDCCI_VCP_REQUEST; /
_buf[ 1 ] = (mccs); /
DDCCI_TX( (pDDCCI), _buf, 2 ); /
DDCCI_Delay( (pDDCCI) ); /
if ( DDCCI_Receive( (pDDCCI), _buf, sizeof( _buf ) ) > 0 ) /
{ /
if ( _buf[ 0 ] == DDCCI_VCP_REPLY && /
_buf[ 1 ] == 0x00 && /
_buf[ 2 ] == (mccs) && /
_buf[ 3 ] == 0x01 ) /
{ /
(valU32) = ((_buf[ 4 ] << 24) | /
(_buf[ 5 ] << 16 ) | /
(_buf[ 6 ] << 8) | /
(_buf[ 7 ])); /
} /
} /
} /
} while ( 0 )
#define DDCCI_SetVCPFeature( pDDCCI, mccs, valU16 ) do { /
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ); /
if ( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ) /
{ /
unsigned char _buf[ 4 ]; /
_buf[ 0 ] = DDCCI_VCP_SET; /
_buf[ 1 ] = (mccs); /
_buf[ 2 ] = ((valU16) >> 8) & 0xFF; /
_buf[ 3 ] = (valU16) & 0xFF; /
DDCCI_TX( (pDDCCI), _buf, sizeof( _buf ) ); /
} /
} while ( 0 )
#define DDCCI_GetCapabilities( pDDCCI, offsetU16, buf32 ) do { /
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ); /
if ( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER ) /
{ /
int _len; /
unsigned char _buf[ DDCCI_MSG_SZ + 4 ]; /
_buf[ 0 ] = DDCCI_CAPABILITIES_REQUEST; /
_buf[ 1 ] = ((offsetU16) >> 8) & 0xFF; /
_buf[ 2 ] = (offsetU16) & 0xFF; /
DDCCI_TX( (pDDCCI), _buf, 3 ); /
DDCCI_Delay( (pDDCCI) ); /
_len = DDCCI_Receive( (pDDCCI), _buf, sizeof( _buf ) ); /
if ( _len > 0 ) /
{ /
if ( _buf[ 0 ] == DDCCI_CAPABILITIES_REPLY && /
((_buf[ 1 ] << 8) | _buf[ 2 ]) == (offsetU16) ) /
{ /
memcpy( (buf32), &_buf[ 3 ], _len ); /
} /
} /
} /
} while ( 0 )
typedef enum _EDDCCIDispDev
{
EDDCCI_DISP_DEV_INDEP, // Independent display device
EDDCCI_DISP_DEV_EXT_DEP, // External display dependent device
EDDCCI_DISP_DEV_INT_DEP, // Internal display dependent device
} EDDCCIDispDev;
typedef enum _EDDCCII2CMode
{
EDDCCI_I 2C _MODE_MASTER,
EDDCCI_I 2C _MODE_SLAVE
} EDDCCII2CMode;
typedef struct _SDDCCI
{
int flags;
unsigned char masterI2CAddr; // 0x6E in normal case
unsigned char slaveI2CAddr; // 0x 50 in normal case
unsigned char rDstI2CAddr;
unsigned char rSrcI2CAddr;
unsigned char wDstI2CAddr;
unsigned char wSrcI2CAddr;
EDDCCIDispDev dispDev;
EDDCCII2CMode i2cMode;
struct timeval DDCCITimestamp; // The timestamp of last DDC/CI OpCode
int DDCCIOpCode; // Last DDC/CI OpCode
} SDDCCI;
typedef struct _SDDCCITimingReport
{
unsigned char timingStatus;
unsigned char pad;
unsigned short horFreq; // Horizontal frequency
unsigned short verFreq; // Vertical frequency
} SDDCCITimingReport;
int DDCCI_Create( SDDCCI *pDDCCI,
unsigned char masterI2CAddr, unsigned char slaveI2CAddr,
EDDCCIDispDev dispDev, EDDCCII2CMode i2cMode );
int DDCCI_Destroy( SDDCCI *pDDCCI );
int DDCCI_Send( SDDCCI *pDDCCI, unsigned char *data, int len );
int DDCCI_Receive( SDDCCI *pDDCCI, unsigned char *data, int len );
int DDCCI_Delay( SDDCCI *pDDCCI );
int DDCCI_GetTimingReport( SDDCCI *pDDCCI, SDDCCITimingReport *pTimingReport );
#if defined( __cplusplus ) || defined( c_plusplus )
}
#endif
#endif
DDCCI.c源代码:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include "DDCCI.h"
#define DDCCI_FLG_INITED 0x00000001
#define DDCCI_FLG_READ 0x01000000
#define DDCCI_FLG_WRITE 0x02000000
#define DDCCI_FLG_RW (DDCCI_FLG_READ | DDCCI_FLG_WRITE)
#define DDCCI_LEN_MAGIC (0x80)
#define DDCCI_GEN_LEN( len ) (DDCCI_LEN_MAGIC | ((len) & (DDCCI_MSG_SZ - 1)))
#define DDCCI_GET_LEN( len ) (((len) & ~DDCCI_LEN_MAGIC) & (DDCCI_MSG_SZ - 1))
#define DDCCI_INV_OPCODE -1
#define DDCCI_MIN_READ_DELAY 35
static void _DDCCI_DumpHex( unsigned char *buf, int len );
static int _DDCCI_ReadI 2C ( unsigned char i2cAddr, unsigned char *buf, int len );
static int _DDCCI_WriteI 2C ( unsigned char i2cAddr, unsigned char *buf, int len );
static int _DDCCI_GetDelay( int DDCCIOpCode );
int DDCCI_Create( SDDCCI *pDDCCI,
unsigned char masterI2CAddr, unsigned char slaveI2CAddr,
EDDCCIDispDev dispDev, EDDCCII2CMode i2cMode )
{
assert( pDDCCI != NULL );
assert( (pDDCCI->flags & DDCCI_FLG_INITED) == 0 );
pDDCCI->flags = 0x01;
pDDCCI->masterI2CAddr = masterI2CAddr;
pDDCCI->slaveI2CAddr = slaveI2CAddr;
pDDCCI->dispDev = dispDev;
pDDCCI->i2cMode = i2cMode;
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
{
pDDCCI->flags |= DDCCI_FLG_RW;
}
else
{
pDDCCI->flags |= DDCCI_FLG_WRITE;
}
switch ( pDDCCI->dispDev )
{
case EDDCCI_DISP_DEV_INDEP:
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
{
pDDCCI->rDstI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
pDDCCI->rSrcI2CAddr = pDDCCI->slaveI2CAddr;
pDDCCI->wDstI2CAddr = pDDCCI->slaveI2CAddr;
// In order to tell the display that the received message is of DDC/CI type,
// the Source Address Byte bit 0 is set to 1.
pDDCCI->wSrcI2CAddr = pDDCCI->masterI2CAddr | 0x01;
}
else
{
// Slave mode can not read
pDDCCI->rDstI2CAddr = -1;
pDDCCI->rSrcI2CAddr = -1;
pDDCCI->wDstI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
pDDCCI->wSrcI2CAddr = pDDCCI->slaveI2CAddr;
}
break;
case EDDCCI_DISP_DEV_EXT_DEP:
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
{
pDDCCI->rDstI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
pDDCCI->rSrcI2CAddr = pDDCCI->slaveI2CAddr;
pDDCCI->wDstI2CAddr = pDDCCI->slaveI2CAddr;
// In order to tell the display that the received message is of DDC/CI type,
// the Source Address Byte bit 0 is set to 1.
pDDCCI->wSrcI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
}
else
{
// Slave mode can not read
pDDCCI->rDstI2CAddr = -1;
pDDCCI->rSrcI2CAddr = -1;
pDDCCI->wDstI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
pDDCCI->wSrcI2CAddr = pDDCCI->slaveI2CAddr;
}
break;
case EDDCCI_DISP_DEV_INT_DEP:
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
{
pDDCCI->rDstI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
pDDCCI->rSrcI2CAddr = pDDCCI->slaveI2CAddr;
pDDCCI->wDstI2CAddr = 0x6E;
// In order to tell the display that the received message is of DDC/CI type,
// the Source Address Byte bit 0 is set to 1.
pDDCCI->wSrcI2CAddr = pDDCCI->slaveI2CAddr | 0x01;
}
else
{
// Slave mode can not read
pDDCCI->rDstI2CAddr = -1;
pDDCCI->rSrcI2CAddr = -1;
pDDCCI->wDstI2CAddr = 0x 6F ;
pDDCCI->wSrcI2CAddr = pDDCCI->slaveI2CAddr;
}
break;
default:
assert( 0 );
printf( "[DDC/CI]: unknown display device/n" );
return -1;
}
timerclear( (&pDDCCI->DDCCITimestamp) );
pDDCCI->DDCCIOpCode = DDCCI_INV_OPCODE;
return 0;
}
int DDCCI_Destroy( SDDCCI *pDDCCI )
{
assert( pDDCCI != NULL );
assert( (pDDCCI->flags & DDCCI_FLG_INITED) == DDCCI_FLG_INITED );
memset( pDDCCI, 0, sizeof( SDDCCI ) );
return 0;
}
int DDCCI_Send( SDDCCI *pDDCCI, unsigned char *data, int len )
{
int idx;
int txLen;
unsigned char buf[ DDCCI_MSG_SZ ];
unsigned char checkSum;
assert( pDDCCI != NULL );
assert( (pDDCCI->flags & (DDCCI_FLG_INITED | DDCCI_FLG_WRITE) ) ==
(DDCCI_FLG_INITED | DDCCI_FLG_WRITE) );
assert( len <= DDCCI_MSG_PAYLOAD_SZ );
buf[ 0 ] = pDDCCI->wDstI2CAddr;
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
checkSum = buf[ 0 ];
else
checkSum = DDCCI_MASTER_I 2C _ADDR;
buf[ 1 ] = pDDCCI->wSrcI2CAddr;
checkSum ^= buf[ 1 ];
buf[ 2 ] = DDCCI_GEN_LEN( len );
checkSum ^= buf[ 2 ];
for ( idx = 0; idx < len; ++ idx )
{
buf[ idx + 3 ] = data[ idx ];
checkSum ^= data[ idx ];
}
buf[ len + 3 ] = checkSum;
_DDCCI_DumpHex( buf, len + 4 );
txLen = _DDCCI_WriteI 2C ( buf[ 0 ], &buf[ 1 ], len + 3 );
txLen -= 3;
assert( txLen == len );
// Trace the OpCode of DDC/CI
if ( txLen == len && data != NULL )
{
gettimeofday( &pDDCCI->DDCCITimestamp, NULL );
pDDCCI->DDCCIOpCode = data[ 0 ];
}
return txLen > 0 ? txLen : 0;
}
int DDCCI_Receive( SDDCCI *pDDCCI, unsigned char *data, int len )
{
int idx;
int rxLen;
int msgLen;
unsigned char buf[ 127 + 4 ];
unsigned char checkSum;
assert( pDDCCI != NULL );
assert( (pDDCCI->flags & (DDCCI_FLG_INITED | DDCCI_FLG_READ) ) ==
(DDCCI_FLG_INITED | DDCCI_FLG_READ) );
assert( data != NULL );
assert( len > 0 );
assert( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER );
if ( pDDCCI->i2cMode != EDDCCI_I 2C _MODE_MASTER )
{
printf( "[DDC/CI]: slave mode can not read/n" );
return -1;
}
rxLen = _DDCCI_ReadI 2C ( pDDCCI->rDstI2CAddr, buf, sizeof( buf ) );
if ( rxLen < 0 )
{
printf( "[DDC/CI]: fail to read I 2C bus/n" );
return -2;
}
_DDCCI_DumpHex( buf, rxLen );
if ( rxLen < 4 )
{
printf( "[DDC/CI]: too small size of message/n" );
return -3;
}
if ( buf[ 0 ] != pDDCCI->rDstI2CAddr )
{
printf( "[DDC/CI]: unknown Destination I 2C address(0x%X, 0x%X)/n",
buf[ 0 ], pDDCCI->rDstI2CAddr );
return -4;
}
if ( buf[ 1 ] != pDDCCI->rSrcI2CAddr )
{
printf( "[DDC/CI]: unknown source I 2C address(0x%X, 0x%X)/n",
buf[ 1 ], pDDCCI->rSrcI2CAddr );
return -5;
}
msgLen = DDCCI_GET_LEN( buf[ 2 ] );
if ( msgLen + 1 > rxLen )
{
printf( "[DDC/CI]: message is partitial(msgLen = %d, rxLen = %d)/n",
msgLen, rxLen );
return -6;
}
if ( msgLen > len )
{
printf( "[DCC/CI]: buffer is too small(len = %d, rxLen = %d, msgLen = %d)/n",
len, rxLen, msgLen );
return -7;
}
if ( pDDCCI->i2cMode == EDDCCI_I 2C _MODE_MASTER )
checkSum = buf[ 0 ];
else
checkSum = DDCCI_MASTER_I 2C _ADDR;
checkSum ^= buf[ 1 ];
checkSum ^= buf[ 2 ];
for ( idx = 0; idx < msgLen; ++ idx )
{
data[ idx ] = buf[ idx + 3 ];
checkSum ^= buf[ idx ];
}
checkSum ^= buf[ msgLen + 3 ];
if ( checkSum != 0 )
{
printf( "[DDC/CI]: checksum has error(checkSum = 0x%X)/n", checkSum );
return -8;
}
// Trace the OpCode of DDC/CI
gettimeofday( &pDDCCI->DDCCITimestamp, NULL );
pDDCCI->DDCCIOpCode = buf[ 3 ];
return msgLen;
}
int DDCCI_Delay( SDDCCI *pDDCCI )
{
int delayMs;
int deltaMs;
struct timeval tv;
assert( pDDCCI != NULL );
delayMs = _DDCCI_GetDelay( pDDCCI->DDCCIOpCode );
if ( delayMs == 0 )
return 0;
if ( gettimeofday( &tv, NULL ) != 0 )
{
usleep( deltaMs * 1000 );
}
else
{
int delta;
// Calculate the gap
if ( tv.tv_sec >= pDDCCI->DDCCITimestamp.tv_sec )
{
delta = tv.tv_sec - pDDCCI->DDCCITimestamp.tv_sec;
}
else
{
delta = ((time_t) -1) - pDDCCI->DDCCITimestamp.tv_sec + tv.tv_sec;
}
delta *= 1000;
delta += ((int) tv.tv_usec - pDDCCI->DDCCITimestamp.tv_usec) / 1000;
if ( delta < delayMs )
{
usleep( (delayMs - delta) * 1000 );
}
}
return delayMs;
}
int DDCCI_GetTimingReport( SDDCCI *pDDCCI, SDDCCITimingReport *pTimingReport )
{
int idx;
int rxLen;
unsigned char buf[ 127 + 4 ];
unsigned char checkSum;
assert( pDDCCI != NULL );
assert( pTimingReport != NULL );
assert( (pDDCCI)->i2cMode == EDDCCI_I 2C _MODE_MASTER );
if ( (pDDCCI)->i2cMode != EDDCCI_I 2C _MODE_MASTER )
{
printf( "[DDC/CI]: only master can get timing report/n" );
return -1;
}
buf[ 0 ] = DDCCI_TIMING_REQUEST;
DDCCI_TX( pDDCCI, buf, 1 );
DDCCI_Delay( pDDCCI );
rxLen = DDCCI_Receive( pDDCCI, buf, sizeof( buf ) );
if ( rxLen <= 0 )
{
printf( "[DDC/CI]: fail to receive timing report/n" );
return -2;
}
_DDCCI_DumpHex( buf, rxLen );
if ( rxLen < 9 )
{
printf( "[DDC/CI]: too small size of timing report/n" );
return -3;
}
if ( buf[ 0 ] != pDDCCI->rDstI2CAddr )
{
printf( "[DDC/CI]: unknown Destination I 2C address(0x%X, 0x%X)/n",
buf[ 0 ], pDDCCI->rDstI2CAddr );
return -4;
}
if ( buf[ 1 ] != pDDCCI->rSrcI2CAddr )
{
printf( "[DDC/CI]: unknown source I 2C address(0x%X, 0x%X)/n",
buf[ 1 ], pDDCCI->rSrcI2CAddr );
return -5;
}
if ( buf[ 2 ] != DDCCI_TIMING_REPLY )
{
printf( "[DDC/CI]: unknown replay for timing report(0x%X, 0x%X)/n",
buf[ 2 ], DDCCI_TIMING_REPLY );
return -6;
}
if ( buf[ 3 ] != 0x04 )
{
printf( "[DDC/CI]: unknown opcode of timing report(0x%X, 0x%4)/n", buf[ 3 ] );
return -7;
}
checkSum = DDCCI_MASTER_I 2C _ADDR;
for ( idx = 1; idx < 9; ++ idx )
{
checkSum ^= buf[ idx ];
}
checkSum ^= buf[ 9 ];
if ( checkSum != 0 )
{
printf( "[DDC/CI]: checksum has error(checkSum = 0x%X) for timing report./n", checkSum );
return -8;
}
pTimingReport->timingStatus = buf[ 4 ];
pTimingReport->horFreq = (buf[ 5 ] << 8) | buf[ 6 ];
pTimingReport->verFreq = (buf[ 7 ] << 8) | buf[ 8 ];
return 0;
}
static void _DDCCI_DumpHex( unsigned char *buf, int len )
{
int idx;
printf( "[DCC/CI]:" );
for ( idx = 0; idx < len; ++ idx )
{
printf( "%s0x%X", (idx ? ", " : " "), buf[ idx ] );
}
printf( "/n" );
}
static int _DDCCI_ReadI 2C ( unsigned char i2cAddr, unsigned char *buf, int len )
{
buf[ 0 ] = i2cAddr; // 0 must save i2cAdd to return
return len;
}
static int _DDCCI_WriteI 2C ( unsigned char i2cAddr, unsigned char *buf, int len )
{
return len;
}
static int _DDCCI_GetDelay( int DDCCIOpCode )
{
int delayMs;
switch ( DDCCIOpCode )
{
case DDCCI_VCP_REQUEST:
delayMs = 40;
break;
case DDCCI_VCP_REPLY:
delayMs = 0;
break;
case DDCCI_VCP_SET:
delayMs = 50;
break;
case DDCCI_VCP_RESET:
delayMs = 200;
break;
case DDCCI_IDENTIFICATION_REQUEST:
delayMs = DDCCI_MIN_READ_DELAY;
break;
case DDCCI_IDENTIFICATION_REPLY:
delayMs = 0;
break;
case DDCCI_CAPABILITIES_REQUEST:
delayMs = DDCCI_MIN_READ_DELAY;
break;
case DDCCI_CAPABILITIES_REPLY:
delayMs = 50;
break;
case DDCCI_TIMING_REQUEST:
delayMs = 40;
break;
case DDCCI_TIMING_REPLY:
delayMs = 50;
break;
case DDCCI_TABLE_READ_REQEUST:
delayMs = DDCCI_MIN_READ_DELAY;
break;
case DDCCI_TABLE_READ_REPLY:
delayMs = 50;
break;
case DDCCI_TABLE_WRITE:
delayMs = 50;
break;
case DDCCI_ENABLE_APPLICATION_REPORT:
delayMs = DDCCI_MIN_READ_DELAY;
break;
case DDCCI_SAVE_CURRENT_SETTINGS:
delayMs = 200;
break;
case DDCCI_DISPLAY_SELF_TEST_REQEUST:
delayMs = 200;
break;
case DDCCI_INV_OPCODE:
delayMs = 0;
break;
default:
delayMs = DDCCI_MIN_READ_DELAY;
break;
}
return delayMs;
}