Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
projects:acb8300 [2016/01/02 16:43] – created Andreas Böhler | projects:acb8300 [2016/01/25 23:20] (current) – [Wait for button inpurt] Andreas Böhler | ||
---|---|---|---|
Line 6: | Line 6: | ||
This page describes the USB protocol as well as the work towards integrating it into Argyll CMS. | This page describes the USB protocol as well as the work towards integrating it into Argyll CMS. | ||
+ | |||
+ | ===== First steps with the calibrator ===== | ||
+ | |||
+ | At first, I poked around the installation directory of True Color Pro and - to my surprise - found a DLL called LG_ACB8300.dll. This sounds interesting. I fired up Dependency Walker to get the exported functions: | ||
+ | |||
+ | * LG_Calibrator_CloseWaitButton | ||
+ | * LG_Calibrator_DeviceCheck_Signage | ||
+ | * LG_Calibrator_DeviceClose | ||
+ | * LG_Calibrator_DeviceClose_Signage | ||
+ | * LG_Calibrator_DeviceNumberRead | ||
+ | * LG_Calibrator_DeviceNumberWrite | ||
+ | * LG_Calibrator_DeviceOpen | ||
+ | * LG_Calibrator_DeviceOpen_Signage | ||
+ | * LG_Calibrator_GetADC | ||
+ | * LG_Calibrator_GetAPIVersion | ||
+ | * LG_Calibrator_GetFWVersion | ||
+ | * LG_Calibrator_GetXYZ | ||
+ | * LG_Calibrator_GetXYZ_Signage | ||
+ | * LG_Calibrator_SetMonitorType | ||
+ | * LG_Calibrator_StartWaitButton | ||
+ | |||
+ | I don't know what " | ||
+ | |||
+ | Thanks to his work, I could code a simple Python wrapper and later a simple C wrapper in order to debug the DLL. | ||
+ | |||
+ | ===== Debugging the LG_ACB8300.dll ===== | ||
+ | |||
+ | I traced parts of the LG_ACB8300.dll with OllyDBG. I built a Python and a small C wrapper around the library in order to have access to the funtions and their parameters. The wrapper simply loads the DLL entry points and calls DeviceOpen, Get_ADC, Get_XYZ, etc. in order to reverse engineer the functionality. | ||
+ | |||
+ | Inside the Get_XYZ function, a lot of floating point magic is going on. After reversing all the floating point assembly, it turns out that the conversion from ADC to XYZ is performing as follows: | ||
+ | |||
+ | - Read ADC value from device | ||
+ | - Do an offset correction (can be read from calibration, | ||
+ | - Apply a correction matrix (can be read from calibration, | ||
+ | - Do an offset correction (can be read from calibration, | ||
+ | - Apply a monitor correction matrix (**multiply** by matrix from the DLL) | ||
+ | |||
+ | The monitor correction matrix is hardcoded in the DLL. The SetMonitorType() function simply loads a different matrix into memory. | ||
+ | |||
+ | The data section contains 7 different monitor types. Monitor Type 1 is a simple unity matrix. | ||
+ | |||
+ | For my DLL, the memory locations are: | ||
+ | |||
+ | ^ Monitor Type ^ Location ^ | ||
+ | | 0 | 0x1000c150 | | ||
+ | | 1 | 0x1000c198 | | ||
+ | | 2 | 0x1000c1e0 | | ||
+ | | 3 | 0x1000c228 | | ||
+ | | 4 | 0x1000c108 | | ||
+ | | 5 | 0x1000c030 | | ||
+ | | 6 | 0x1000c0c0 | | ||
+ | |||
+ | The next 9 double numbers at the memory locations define the correction matrices. They are in row-column order. | ||
===== Hacking the USB protocol ===== | ===== Hacking the USB protocol ===== | ||
- | The USB protocol consists of input reports and output reports, with a fixed length of 43 bytes. The host speaks first, the device responds (most of the time). | + | The USB protocol consists of input reports and output reports, with a fixed length of 43 bytes. The host speaks first, the device responds (most of the time). |
+ | |||
+ | They seem to not clear the send buffer before responding to a command. Thus, you always get the remaining bytes of the previous commands as well! | ||
==== Initialisation ==== | ==== Initialisation ==== | ||
Send: '' | Send: '' | ||
- | Response: '' | + | Response: '' |
+ | |||
+ | This seems to initialise the device. The bytes in the response are yet unknown. | ||
==== Read Calibration ==== | ==== Read Calibration ==== | ||
+ | |||
+ | Get the different calibration matrices and offset values from the device. M is a 3x3 matrix for the ADC to XYZ conversion, A is the first ADC offset, X is the second XYZ offset (both being a vector of length 3). | ||
Send: '' | Send: '' | ||
Response: '' | Response: '' | ||
+ | |||
+ | ^ Bytes ^ Format ^ Description ^ | ||
+ | | 0-7 | double | M[0][0] | | ||
+ | | 8-15 | double | M[0][1] | | ||
+ | | 16-23 | double | M[0][2] | | ||
+ | | 24-31 | double | M[1][0] | | ||
+ | | 32-39 | double | M[1][1] | | ||
+ | |||
Send: '' | Send: '' | ||
Response: '' | Response: '' | ||
+ | |||
+ | ^ Bytes ^ Format ^ Description ^ | ||
+ | | 0-7 | double | M[1][2] | | ||
+ | | 8-15 | double | M[2][0] | | ||
+ | | 16-23 | double | M[2][1] | | ||
+ | | 24-31 | double | M[2][2] | | ||
Send: '' | Send: '' | ||
Response: '' | Response: '' | ||
+ | |||
+ | ^ Bytes ^ Format ^ Description ^ | ||
+ | | 0-7 | double | A[0] | | ||
+ | | 8-15 | double | A[1] | | ||
+ | | 16-23 | double | A[2] | | ||
+ | | 24-31 | double | X[0] | | ||
+ | | 32-39 | double | X[1] | | ||
Send: '' | Send: '' | ||
- | Response: '' | + | Response: '' |
+ | |||
+ | ^ Bytes ^ Format ^ Description ^ | ||
+ | | 0-7 | double | X[2] | | ||
==== Read ADC values ==== | ==== Read ADC values ==== | ||
+ | |||
+ | Read ADC values from the device. | ||
Send: '' | Send: '' | ||
Response: '' | Response: '' | ||
+ | |||
+ | ^ Bytes ^ Format ^ Description ^ | ||
+ | | 1-2 | int | unknown | | ||
+ | | 3-4 | int | Z value | | ||
+ | | 5-6 | int | Y value | | ||
+ | | 7-8 | int | X value | | ||
==== Read Firmware version ==== | ==== Read Firmware version ==== | ||
Line 40: | Line 131: | ||
Response: '' | Response: '' | ||
- | ==== Wait for button | + | ^ Bytes ^ Format ^ Description ^ |
+ | | 1 | int | Firmare Version | | ||
+ | |||
+ | In order to derive the firmware version, divide the value by 100. My device reports '' | ||
+ | |||
+ | ==== Wait for button | ||
Send: '' | Send: '' | ||
Line 46: | Line 142: | ||
Whenever the button is pressed, the device sends '' | Whenever the button is pressed, the device sends '' | ||
+ | |||
+ | ===== ArgyllCMS support ===== | ||
+ | |||
+ | Although the device will probably be never officially supported by ArgyllCMS, I developed an initial driver for the device. You can find the patch for Argyll 1.8.3 {{: |