Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Thanks!

Looking around I actually found a local PCB made to order manufacturer that produces locally. So that's plan B for now.

I think it might be easy to hack in auto on/off. Basically I think one just watches VGA PIN 14 for regular VSYNC using pulseIn or something. No VSYNC = No picture = turn off. Otherwise turn on.

I will try to hack that in and maybe I can provide a patch when I get it working, but I can really only start end of next week. I will report how it goes. :)

That's a really good idea! I made two of the IO lines available on a header on the J20 board so maybe I'll try and play with the idea as well.

Please let me know how it goes, because that would be a really nice addition to the circuit.
Here's the initIvadBoard method for my iMac g3

I have this model iMac M8582LL/A* - PowerMac4,1 - M5521https://everymac.com/systems/apple/imac/specs/imac_500_indigo.html

This is giving me good looking color and brightness. On the MacBook I connected to it I set the resolution to Scaled 1280x960.

Code:
void initIvadBoard() {
    writeToIvad( 0x46,0x13,0x00);
    writeToIvad(0x46,0x13,0x00);
    readFromIvad(0x46,1);
    writeToIvad(0x46,0x09,0x00);
    writeToIvad(0x53,0x33);
    readFromIvad(0x53,1);
    writeToIvad(0x46,0x13,0x0b);
    writeToIvad(0x46,0x00,0x00);
    writeToIvad(0x46,0x08,0xe4);
    writeToIvad(0x46,0x12,0xc9);
    writeToIvad(0x53,0x00);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x0a);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x14);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x1e);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x28);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x32);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x3c);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x46);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x50);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x5a);
    readFromIvad(0x53,10);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x07,0xad);
    writeToIvad(0x46,0x08,0xe4);
    writeToIvad(0x46,0x09,0x3d);
    writeToIvad(0x46,0x0a,0x9e);
    writeToIvad(0x46,0x0b,0xb4);
    writeToIvad(0x46,0x0c,0xc4);
    writeToIvad(0x46,0x0d,0x27);
    writeToIvad(0x46,0x0e,0xbf);
    writeToIvad(0x46,0x0f,0xc0);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x11,0x0a);
    writeToIvad(0x46,0x12,0x5b);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x53,0x00);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x10);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x20);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x30);
    readFromIvad(0x53,10);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x00,0x00);
    writeToIvad(0x46,0x07,0xb1);
    writeToIvad(0x46,0x0d,0x10);
    writeToIvad(0x46,0x0c,0xc7);
    writeToIvad(0x46,0x09,0x4a);
    writeToIvad(0x46,0x08,0xea);
    writeToIvad(0x46,0x0f,0xc0);
    writeToIvad(0x46,0x0b,0xae);
    writeToIvad(0x46,0x12,0x5b);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
}


That looks nice, I'll add it to the code as another init sequence. I think once we have enough of them we could come up with a generic
sequence with bullet proof register addresses and offsets. What I think happens is that each CRT was calibrated at the factory
and/or by the user. These settings are saved in the NVRAM and applied when the CRT is initialized. If not present then the default values are sent.
[automerge]1586380321[/automerge]
Something else I'm thinking about is that in this initIvadBoard function were ignoring all the data coming back from the monitor board. I wonder if it's sending information about itself back. Maybe some kind of identifier that Mac OS knows about. If so it might be possible to update the Arduino sketch to do a case statement on the monitor data and run init code for this particular monitor. That way we could have the code for all iMac g3's in one sketch.

I agree, these data need to be deciphered. I'm sure there are nice bits of useful information in there.
Have you tried looking at any of it?

One init sequence to rule them all!........ hopefully
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Here's the initIvadBoard method for my iMac g3

I have this model iMac M8582LL/A* - PowerMac4,1 - M5521https://everymac.com/systems/apple/imac/specs/imac_500_indigo.html

This is giving me good looking color and brightness. On the MacBook I connected to it I set the resolution to Scaled 1280x960.

Code:
void initIvadBoard() {
    writeToIvad( 0x46,0x13,0x00);
    writeToIvad(0x46,0x13,0x00);
    readFromIvad(0x46,1);
    writeToIvad(0x46,0x09,0x00);
    writeToIvad(0x53,0x33);
    readFromIvad(0x53,1);
    writeToIvad(0x46,0x13,0x0b);
    writeToIvad(0x46,0x00,0x00);
    writeToIvad(0x46,0x08,0xe4);
    writeToIvad(0x46,0x12,0xc9);
    writeToIvad(0x53,0x00);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x0a);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x14);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x1e);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x28);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x32);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x3c);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x46);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x50);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x5a);
    readFromIvad(0x53,10);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x07,0xad);
    writeToIvad(0x46,0x08,0xe4);
    writeToIvad(0x46,0x09,0x3d);
    writeToIvad(0x46,0x0a,0x9e);
    writeToIvad(0x46,0x0b,0xb4);
    writeToIvad(0x46,0x0c,0xc4);
    writeToIvad(0x46,0x0d,0x27);
    writeToIvad(0x46,0x0e,0xbf);
    writeToIvad(0x46,0x0f,0xc0);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x11,0x0a);
    writeToIvad(0x46,0x12,0x5b);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x53,0x00);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x10);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x20);
    readFromIvad(0x53,10);
    writeToIvad(0x53,0x30);
    readFromIvad(0x53,10);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x00,0x00);
    writeToIvad(0x46,0x07,0xb1);
    writeToIvad(0x46,0x0d,0x10);
    writeToIvad(0x46,0x0c,0xc7);
    writeToIvad(0x46,0x09,0x4a);
    writeToIvad(0x46,0x08,0xea);
    writeToIvad(0x46,0x0f,0xc0);
    writeToIvad(0x46,0x0b,0xae);
    writeToIvad(0x46,0x12,0x5b);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
    writeToIvad(0x46,0x10,0x40);
    writeToIvad(0x46,0x06,0xa0);
    writeToIvad(0x46,0x05,0xa0);
    writeToIvad(0x46,0x04,0xa0);
    writeToIvad(0x46,0x03,0x82);
    writeToIvad(0x46,0x02,0x82);
    writeToIvad(0x46,0x01,0x82);
    writeToIvad(0x46,0x11,0x05);
    writeToIvad(0x46,0x00,0xff);
}

I just tested your init sequence with my CRT and it was pretty good. The upper edges were just a little off of the screen but
other than that the brightness and contrast were good.

Also, I added your sequence to the repo in commented form.

What;s your plan for the screen geometry, brightness and contrast?

I think you'll find an address for each property with min and max values for each.

Thanks!
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
What;s your plan for the screen geometry, brightness and contrast?

I think you'll find an address for each property with min and max values for each.

Thanks!

That's what I'm hoping. The current plan is to rebuild my i2c sniffer setup tonight and watch the addresses and values as I make adjustments and learn how it works. Then I want to setup some buttons on a breadboard connected to my Arduino so I can make the adjustments without the iMac. I'll post that whole sketch when I have it.
I've got to rebuild the sniffer setup cause I've only got enough parts to have either the sniffer or the vga input port at once.

I'm thinking of an API like this

Code:
function horizontalSizeIncrease() { writeToIvad(...); }
function horizontalSizeDecrease() {}
function VerticalSizeIncrease() {}
function VerticalSizeDecrease() {}

function horizontalPositionMoveLeft() {}
function horizontalPositionMoveRight() {}
function VerticalPositionMoveLeft() {}
function VerticalPositionMoveRight() {}

function brightnessIncrease() {}
function brightnessDecrease() {}

function pincusionConcave() {}
function pincusionConvext() {}

... and so on...

That's assuming each adjustment is a transform and not a direct setting. Like I'm assuming right now that when I click the "horizontal position right" button in the monitor's control panel, the signal sent says to move right by N units. But it could be that the iMac either already knows or queries the monitor's current value for that setting. Makes the adjustment to the value. Then overwrites the existing setting with the new value.

So it may end up being something more like this

Code:
function setHorizontalSize(int size) {}
function getHorizontalSize() {}

int size = getHorizontalSize();
int new_size = (size + SIZE_INCREMENT);

setHorizontalSize(new_size);

We'll see.



Either way I'll have some kinda button interface on a breadboard I can use to call the functions. Then I don't have to worry about a perfect init since I can adjust afterward.


I wonder if there's a way to query the monitor board for all of it's current settings. If so you could adjust until it's perfect, then save the settings to be automatically reloaded next time.
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Something else I'm thinking about is that in this initIvadBoard function were ignoring all the data coming back from the monitor board. I wonder if it's sending information about itself back. Maybe some kind of identifier that Mac OS knows about. If so it might be possible to update the Arduino sketch to do a case statement on the monitor data and run init code for this particular monitor. That way we could have the code for all iMac g3's in one sketch.

W
That's what I'm hoping. The current plan is to rebuild my i2c sniffer setup tonight and watch the addresses and values as I make adjustments and learn how it works. Then I want to setup some buttons on a breadboard connected to my Arduino so I can make the adjustments without the iMac. I'll post that whole sketch when I have it.
I've got to rebuild the sniffer setup cause I've only got enough parts to have either the sniffer or the vga input port at once.

I'm thinking of an API like this

Code:
function horizontalSizeIncrease() { writeToIvad(...); }
function horizontalSizeDecrease() {}
function VerticalSizeIncrease() {}
function VerticalSizeDecrease() {}

function horizontalPositionMoveLeft() {}
function horizontalPositionMoveRight() {}
function VerticalPositionMoveLeft() {}
function VerticalPositionMoveRight() {}

function brightnessIncrease() {}
function brightnessDecrease() {}

function pincusionConcave() {}
function pincusionConvext() {}

... and so on...

That's assuming each adjustment is a transform and not a direct setting. Like I'm assuming right now that when I click the "horizontal position right" button in the monitor's control panel, the signal sent says to move right by N units. But it could be that the iMac either already knows or queries the monitor's current value for that setting. Makes the adjustment to the value. Then overwrites the existing setting with the new value.

So it may end up being something more like this

Code:
function setHorizontalSize(int size) {}
function getHorizontalSize() {}

int size = getHorizontalSize();
int new_size = (size + SIZE_INCREMENT);

setHorizontalSize(new_size);

We'll see.



Either way I'll have some kinda button interface on a breadboard I can use to call the functions. Then I don't have to worry about a perfect init since I can adjust afterward.


I wonder if there's a way to query the monitor board for all of it's current settings. If so you could adjust until it's perfect, then save the settings to be automatically reloaded next time.



The replies we're ignoring in the init sequence very well might be the IVAD board spitting back the value sent to it so that the logic board
can capture it and to use as a starting point when making adjustments in the control panel.

Adding and subtracting offsets to an initial value is probably the case then.
"
Code:
function setHorizontalSize(int size) {}

int new_size = (size + SIZE_INCREMENT);
setHorizontalSize(new_size);
"

Even if it isn't, we know what is sent when sending the init sequence, so the known value can always be the starting point adjusting from then on.

Once one has settled on a setting, the final adjustment values can be saved at runtime in the atmega328p itself!
When the CRT is turned on again, the saved values can be loaded.

We could simulate the NVRAM in that respect.

I've never saved data to an arduino's EEPROM before, but I know it's possible.

Saving persistent data on an arduino
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
I've had a handful of people ask for the board schematics and I always ended up asking myself "They're right there".
I've finally come to the realization that one reason people might be asking is because they have no
idea the need to install KiCAD and if they do, EDA programs generally aren't intuitive especially not
KiCAD.

If anyone is interested I've included pdf versions of the schematics in the repo in a folder named "pdfs"
Here are the links.

J20 board
J22 board
down converter breakout board
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
Here's the raw data for all of the monitor adjustments.

This was done using an I2c sniffer listening to the traffic between the iMac and monitor. I'm running Mac OS 9.2.2 using the "Monitors" control panel. For each setting I went from one extreme to the other.


@rockyhill You were right. It's sending the values, not commands to increment or decrement. I'll make a pull request to your GitHub project with the functions to set the different monitor setting values.

Code:
Parallelogram left to right


<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xfe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xfd ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xfb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xf8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xf4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xef ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xe9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xe2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xda ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xd1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xc8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xbf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xad ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xa4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0x9b ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0x92 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0x89 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0x80 ACK>
<STOP>

Code:
Keystone: thin at the top to thin at the bottom

<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x81 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x83 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x86 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x8a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x8f ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x95 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0x9c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xa4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xad ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xbf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xc8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xd1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xda ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xe3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xec ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xf5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xfe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xff ACK>
<STOP>

Code:
Rotation: counter clockwise to clockwise

<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x7e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x7c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x79 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x75 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x70 ACK>
<STOP>
<START 0x08 READ NACK>
<READ 0x02 ACK>
<READ 0x8d ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x63 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x5b ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x52 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x49 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x40 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x37 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x2e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x25 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x1c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x13 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x0a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x00 ACK>
<STOP>

Code:
Pincusion: concave to convex

<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x81 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x83 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x86 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x8a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x8f ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x95 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0x9c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xa4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xad ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xbf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xc8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xd1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xda ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xe3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xec ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xf5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xfe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xff ACK>
<STOP>

Code:
Vertical position: lowest to highest

<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x7e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x7c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x79 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x75 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x70 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x6a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x63 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x5b ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x52 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x49 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x40 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x37 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x2e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x25 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x1c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x13 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x0a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x00 ACK>
<STOP>

Code:
Horizontal position: far left to far right

<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xfe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xfc ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xf9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xf5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xf0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xea ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xe3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xdb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xd2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xc9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xc0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xb7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xae ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xa5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0x9c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0x93 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0x8a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0x81 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0x80 ACK>
<STOP>

Code:
Height shortest to tallest

<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x81 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x83 ACK>
<STOP>
<START 0x08 READ NACK>
<READ 0x01 ACK>
<READ 0x10 NACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x8a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x8f ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x95 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0x9c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xa4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xad ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xbf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xc8 ACK>
<STOP>
<START 0x08 READ NACK>
<READ 0x01 ACK>
<READ 0x1a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xda ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xe3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xec ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xf5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xfe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xff ACK>
<STOP>

Code:
Width thinnest to thickest

<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x7e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x7c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x79 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x75 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x70 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x6a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x63 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x5b ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x52 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x49 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x40 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x37 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x2e ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x25 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x1c ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x13 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x0a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x00 ACK>
<STOP>

Code:
Contrast from lowest to highest

<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xb9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xba ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xbb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xbc ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xbd ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xbe ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xbf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xc9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xca ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xcb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xcc ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xcd ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xce ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xcf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xd9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xda ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xdb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xdc ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xdd ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xde ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xdf ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xe9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xea ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xeb ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xec ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xed ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xee ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xef ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf1 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf2 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf3 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf5 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf6 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf7 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf8 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xf9 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xfa ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xfc ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xfd ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xff ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x00 ACK>
<WRITE 0xff ACK>
<STOP>

Code:
Brightness range starting from lowest, going to highest

<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x00 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x01 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x02 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x02 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x03 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x03 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x04 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x04 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x05 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x05 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x06 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x06 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x07 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x07 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x08 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x08 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x09 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x09 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x0a ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x11 ACK>
<WRITE 0x0a ACK>
<STOP>
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
I made a simple serial interface to control it.

You can type several characters at a time into the serial monitor. And it'll execute them one by one. I didn't add characters for controlling all settings because I don't care about rotation, parallelogram, or pincushion.

With this I'm able to get perfect settings for my iMac's display with all of my VGA devices.

Next up is saving the settings and autoloading them next time the Arduino starts. I don't think I'll get to that until the weekend.


Code:
#include <Wire.h>
#include <SoftwareWire.h>

byte data = -1;
int counter = 0 ;

int monitorAddress = 0x46;

int settingParallelogram = 0x0f;
int settingKeystone = 0x0b;
int settingRotation = 0x12;
int settingPincushion = 0x0c;


int verticalPositionSetting = 0x09;
const int verticalPositionValues[19] = {
  // Low to High
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int verticalPositionValueIndex = 9;

int contrastSetting = 0x00;
const int contrastValues[73] = {
  // Low to High
  0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 
  0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 
  0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};
int contrastValueIndex = 72;

int horizontalPositionSetting = 0x07;
const int horizontalPositionValues[19] = {
  0xfe, 0xfc, 0xf9, 0xf5, 0xf0, 0xea, 0xe3, 0xdb, 0xd2, 0xc9, 0xc0, 0xb7, 0xae, 0xa5, 0x9c, 0x93, 0x8a, 0x81, 0x80
};
int horizontalPositionValueIndex = 11;

int heightSetting = 0x08;
const int heightValues[19] = {
  // short to tall
  0x81, 0x83, 0x10, 0x8a, 0x8f, 0x95, 0x9c, 0xa4, 0xad, 0xb6, 0xbf, 0xc8, 0x1a, 0xda, 0xe3, 0xec, 0xf5, 0xfe, 0xff
};
int heightValueIndex = 14;

int widthSetting = 0x0d;
const int widthValues[19] = {
  // thinnest to thickest 
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int widthValueIndex = 12;

int brightnessSetting = 0x11;
int brightnessValues[10] = {
  // Dim to Bright
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a
};
int brightnessValueIndex = 9;

const byte edid[128] =
{

  0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x06, 0x10, 0x74, 0x61,
  0xed, 0x5f, 0x84, 0x00, 0x06, 0x1e, 0x01, 0x03, 0x6d, 0x1a, 0x14, 0x78,
  0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 0x20, 0x50, 0x54, 0x00,
  0x00, 0x00, 0x61, 0x4f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x1e, 0x00, 0x30, 0x41, 0x00,
  0x34, 0x30, 0x14, 0x60, 0xd3, 0x00, 0x0a, 0xc8, 0x10, 0x00, 0x00, 0x1e,
  0x5f, 0x18, 0x20, 0xf0, 0x30, 0x58, 0x2c, 0x20, 0x15, 0x50, 0x93, 0x00,
  0xd0, 0x9c, 0x00, 0x00, 0x00, 0x18, 0x7d, 0x13, 0x80, 0xc0, 0x20, 0xe0,
  0x22, 0x10, 0x11, 0x40, 0x13, 0x00, 0xa6, 0x7d, 0x00, 0x00, 0x00, 0x18,
  0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x3a, 0x3d, 0x08, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19
};


//The init sequence is sent on a software i2c bus.
// sda is on 4 and scl is on 5
SoftwareWire softWire(4, 5);

void setup() {
  Serial.begin(9600);
  
  initIvadBoard();

  Wire.begin(0x50);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveData);
}

void requestEvent() {
  Wire.write(edid, 128);
}

void receiveData(int byteCount) {
  while (Wire.available()) {
    data = Wire.read();
  }
}

void loop() {
  handleSerial();
}

void handleSerial() {
  /*
   * a = move left
   * s = move right
   * w = move up
   * z = move down
   * 
   * d = skinnier
   * f = fatter
   * r = taller
   * c = shorter
   * 
   * g = contrast down
   * h = contrast up
   * 
   * j = brightness down
   * k = brightness up
   * 
   * 
   */

  
  while(Serial.available() > 0) {
    char incoming = Serial.read();

    switch(incoming) {
      case 'a':
        moveLeft();
        break;
      case 's':
        moveRight();
        break;
      case 'w':
        moveUp();
        break;
      case 'z':
        moveDown();
        break;

      case 'd':
        decreaseWidth();
        break;
      case 'f':
        increaseWidth();
        break;
      case 'r':
        increaseHeight();
        break;
      case 'c':
        decreaseHeight();
        break;

      case 'g':
        decreaseContrast();
        break;
      case 'h':
        increaseContrast();
        break;

      case 'j':
         decreaseBrightness();
         break;
      case 'k':
         increaseBrightness();
         break;
    }
  }
}

void writeToIvad(int address, int message) {
  softWire.beginTransmission(address);
  softWire.write(message);
  softWire.endTransmission();
}

void writeToIvad(int address, int message1, int message2) {
  softWire.beginTransmission(address);
  softWire.write(message1);
  softWire.write(message2);
  softWire.endTransmission();
}

void  readFromIvad(int address, int bytes) {
  char buf[bytes + 1];
  int bytesRead = 0;
  softWire.requestFrom(address, bytes);
  while (softWire.available())
  {
    char c = softWire.read();
    buf[bytesRead++] = c;
  }
  buf[bytesRead] = '\0';
}


void initIvadBoard() {
    // removed for brevity
}


void moveLeft() {
  horizontalPositionValueIndex--;
  setHorizontalPosition();
}
void moveRight() {
  horizontalPositionValueIndex++;
  setHorizontalPosition();
}

void moveUp() {
  verticalPositionValueIndex++;
  setVerticalPosition();
}
void moveDown() {
  verticalPositionValueIndex--;
  setVerticalPosition();
}

void increaseWidth() {
  widthValueIndex++;
  setWidth();
}
void decreaseWidth() {
  widthValueIndex--;
  setWidth();
}

void increaseHeight() {
  heightValueIndex++;
  
  setHeight();
}
void decreaseHeight() {
  heightValueIndex--;
  
  setHeight();
}

void decreaseContrast() {
  contrastValueIndex--;
  setContrast();
}
void increaseContrast() {
  contrastValueIndex++;

  setContrast();
}

void decreaseBrightness() {
  brightnessValueIndex--;
  
  setBrightness();
}
void increaseBrightness() {
  brightnessValueIndex++;
  
  setBrightness();
}


void setVerticalPosition(int value) {
  writeToIvad(monitorAddress, verticalPositionSetting, value);
}
void setVerticalPosition() {
  limitIndex(verticalPositionValueIndex, sizeof(verticalPositionValues));
  int value = verticalPositionValues[verticalPositionValueIndex];

  setVerticalPosition(value);
}

void setHorizontalPosition(int value) {
  writeToIvad(monitorAddress, horizontalPositionSetting, value);
}
void setHorizontalPosition() {
  limitIndex(horizontalPositionValueIndex, sizeof(horizontalPositionValues));
  int value = horizontalPositionValues[horizontalPositionValueIndex];

  setHorizontalPosition(value);
}

void setHeight(int value) {
  writeToIvad(monitorAddress, heightSetting, value);
}
void setHeight() {
  limitIndex(heightValueIndex, sizeof(heightValues));
  int value = heightValues[heightValueIndex];

  setHeight(value);
}

void setWidth(int value) {
  writeToIvad(monitorAddress, widthSetting, value);
}
void setWidth() {
  limitIndex(widthValueIndex, sizeof(widthValues));
  int value = widthValues[widthValueIndex];
  
  setWidth(value);
}

void setContrast(int value) {
  writeToIvad(monitorAddress, contrastSetting, value);
}
void setContrast() {
  limitIndex(contrastValueIndex, sizeof(contrastValues));
  int value = contrastValues[contrastValueIndex];
  
  setContrast(value);
}

void setBrightness(int value) {
  writeToIvad(monitorAddress, brightnessSetting, value);
  writeToIvad(monitorAddress, brightnessSetting, value);
}
void setBrightness() {
  limitIndex(brightnessValueIndex, sizeof(brightnessValues));
  int value = brightnessValues[brightnessValueIndex];
  
  setBrightness(value);
}

void limitIndex(int &index, int array_size) {
  int maximum = ((array_size / sizeof(int)) - 1);
  
  if(index < 0) index = 0;
  if(index > maximum) index = maximum;
}


void setParallelogram(int value) {
  /*
   * Left
   * 0xfe
   * 0xfd
   * 0xfb
   * 0xf8
   * 0xf4
   * 0xef
   * 0xe9
   * 0xe2
   * 0xda
   * 0xd1
   * 0xc8
   * 0xbf
   * 0xb6
   * 0xad
   * 0xa4
   * 0x9b
   * 0x92
   * 0x89
   * 0x80
   * Right
   */
  writeToIvad(monitorAddress, settingParallelogram, value);
}
void setKeystone(int value) {
  /*
   * Thin top
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Thin Bottom
   */
  writeToIvad(monitorAddress, settingKeystone, value);
}
void setRotation(int value) {
  /*
   * Left
   * 0x7e
   * 0x7c
   * 0x79
   * 0x75
   * 0x70
   * 0x8d
   * 0x63
   * 0x5b
   * 0x52
   * 0x49
   * 0x40
   * 0x37
   * 0x2e
   * 0x25
   * 0x1c
   * 0x13
   * 0x0a
   * 0x01
   * 0x00
   * Right
   */
  writeToIvad(monitorAddress, settingRotation, value);
}
void setPincushion(int value) {
  /*
   * Concave
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Convex
   */
  writeToIvad(monitorAddress, settingPincushion, value);
}
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
I made a simple serial interface to control it.

You can type several characters at a time into the serial monitor. And it'll execute them one by one. I didn't add characters for controlling all settings because I don't care about rotation, parallelogram, or pincushion.

With this I'm able to get perfect settings for my iMac's display with all of my VGA devices.

Next up is saving the settings and autoloading them next time the Arduino starts. I don't think I'll get to that until the weekend.


Code:
#include <Wire.h>
#include <SoftwareWire.h>

byte data = -1;
int counter = 0 ;

int monitorAddress = 0x46;

int settingParallelogram = 0x0f;
int settingKeystone = 0x0b;
int settingRotation = 0x12;
int settingPincushion = 0x0c;


int verticalPositionSetting = 0x09;
const int verticalPositionValues[19] = {
  // Low to High
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int verticalPositionValueIndex = 9;

int contrastSetting = 0x00;
const int contrastValues[73] = {
  // Low to High
  0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
  0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
  0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};
int contrastValueIndex = 72;

int horizontalPositionSetting = 0x07;
const int horizontalPositionValues[19] = {
  0xfe, 0xfc, 0xf9, 0xf5, 0xf0, 0xea, 0xe3, 0xdb, 0xd2, 0xc9, 0xc0, 0xb7, 0xae, 0xa5, 0x9c, 0x93, 0x8a, 0x81, 0x80
};
int horizontalPositionValueIndex = 11;

int heightSetting = 0x08;
const int heightValues[19] = {
  // short to tall
  0x81, 0x83, 0x10, 0x8a, 0x8f, 0x95, 0x9c, 0xa4, 0xad, 0xb6, 0xbf, 0xc8, 0x1a, 0xda, 0xe3, 0xec, 0xf5, 0xfe, 0xff
};
int heightValueIndex = 14;

int widthSetting = 0x0d;
const int widthValues[19] = {
  // thinnest to thickest
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int widthValueIndex = 12;

int brightnessSetting = 0x11;
int brightnessValues[10] = {
  // Dim to Bright
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a
};
int brightnessValueIndex = 9;

const byte edid[128] =
{

  0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x06, 0x10, 0x74, 0x61,
  0xed, 0x5f, 0x84, 0x00, 0x06, 0x1e, 0x01, 0x03, 0x6d, 0x1a, 0x14, 0x78,
  0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 0x20, 0x50, 0x54, 0x00,
  0x00, 0x00, 0x61, 0x4f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x1e, 0x00, 0x30, 0x41, 0x00,
  0x34, 0x30, 0x14, 0x60, 0xd3, 0x00, 0x0a, 0xc8, 0x10, 0x00, 0x00, 0x1e,
  0x5f, 0x18, 0x20, 0xf0, 0x30, 0x58, 0x2c, 0x20, 0x15, 0x50, 0x93, 0x00,
  0xd0, 0x9c, 0x00, 0x00, 0x00, 0x18, 0x7d, 0x13, 0x80, 0xc0, 0x20, 0xe0,
  0x22, 0x10, 0x11, 0x40, 0x13, 0x00, 0xa6, 0x7d, 0x00, 0x00, 0x00, 0x18,
  0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x3a, 0x3d, 0x08, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19
};


//The init sequence is sent on a software i2c bus.
// sda is on 4 and scl is on 5
SoftwareWire softWire(4, 5);

void setup() {
  Serial.begin(9600);
 
  initIvadBoard();

  Wire.begin(0x50);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveData);
}

void requestEvent() {
  Wire.write(edid, 128);
}

void receiveData(int byteCount) {
  while (Wire.available()) {
    data = Wire.read();
  }
}

void loop() {
  handleSerial();
}

void handleSerial() {
  /*
   * a = move left
   * s = move right
   * w = move up
   * z = move down
   *
   * d = skinnier
   * f = fatter
   * r = taller
   * c = shorter
   *
   * g = contrast down
   * h = contrast up
   *
   * j = brightness down
   * k = brightness up
   *
   *
   */

 
  while(Serial.available() > 0) {
    char incoming = Serial.read();

    switch(incoming) {
      case 'a':
        moveLeft();
        break;
      case 's':
        moveRight();
        break;
      case 'w':
        moveUp();
        break;
      case 'z':
        moveDown();
        break;

      case 'd':
        decreaseWidth();
        break;
      case 'f':
        increaseWidth();
        break;
      case 'r':
        increaseHeight();
        break;
      case 'c':
        decreaseHeight();
        break;

      case 'g':
        decreaseContrast();
        break;
      case 'h':
        increaseContrast();
        break;

      case 'j':
         decreaseBrightness();
         break;
      case 'k':
         increaseBrightness();
         break;
    }
  }
}

void writeToIvad(int address, int message) {
  softWire.beginTransmission(address);
  softWire.write(message);
  softWire.endTransmission();
}

void writeToIvad(int address, int message1, int message2) {
  softWire.beginTransmission(address);
  softWire.write(message1);
  softWire.write(message2);
  softWire.endTransmission();
}

void  readFromIvad(int address, int bytes) {
  char buf[bytes + 1];
  int bytesRead = 0;
  softWire.requestFrom(address, bytes);
  while (softWire.available())
  {
    char c = softWire.read();
    buf[bytesRead++] = c;
  }
  buf[bytesRead] = '\0';
}


void initIvadBoard() {
    // removed for brevity
}


void moveLeft() {
  horizontalPositionValueIndex--;
  setHorizontalPosition();
}
void moveRight() {
  horizontalPositionValueIndex++;
  setHorizontalPosition();
}

void moveUp() {
  verticalPositionValueIndex++;
  setVerticalPosition();
}
void moveDown() {
  verticalPositionValueIndex--;
  setVerticalPosition();
}

void increaseWidth() {
  widthValueIndex++;
  setWidth();
}
void decreaseWidth() {
  widthValueIndex--;
  setWidth();
}

void increaseHeight() {
  heightValueIndex++;
 
  setHeight();
}
void decreaseHeight() {
  heightValueIndex--;
 
  setHeight();
}

void decreaseContrast() {
  contrastValueIndex--;
  setContrast();
}
void increaseContrast() {
  contrastValueIndex++;

  setContrast();
}

void decreaseBrightness() {
  brightnessValueIndex--;
 
  setBrightness();
}
void increaseBrightness() {
  brightnessValueIndex++;
 
  setBrightness();
}


void setVerticalPosition(int value) {
  writeToIvad(monitorAddress, verticalPositionSetting, value);
}
void setVerticalPosition() {
  limitIndex(verticalPositionValueIndex, sizeof(verticalPositionValues));
  int value = verticalPositionValues[verticalPositionValueIndex];

  setVerticalPosition(value);
}

void setHorizontalPosition(int value) {
  writeToIvad(monitorAddress, horizontalPositionSetting, value);
}
void setHorizontalPosition() {
  limitIndex(horizontalPositionValueIndex, sizeof(horizontalPositionValues));
  int value = horizontalPositionValues[horizontalPositionValueIndex];

  setHorizontalPosition(value);
}

void setHeight(int value) {
  writeToIvad(monitorAddress, heightSetting, value);
}
void setHeight() {
  limitIndex(heightValueIndex, sizeof(heightValues));
  int value = heightValues[heightValueIndex];

  setHeight(value);
}

void setWidth(int value) {
  writeToIvad(monitorAddress, widthSetting, value);
}
void setWidth() {
  limitIndex(widthValueIndex, sizeof(widthValues));
  int value = widthValues[widthValueIndex];
 
  setWidth(value);
}

void setContrast(int value) {
  writeToIvad(monitorAddress, contrastSetting, value);
}
void setContrast() {
  limitIndex(contrastValueIndex, sizeof(contrastValues));
  int value = contrastValues[contrastValueIndex];
 
  setContrast(value);
}

void setBrightness(int value) {
  writeToIvad(monitorAddress, brightnessSetting, value);
  writeToIvad(monitorAddress, brightnessSetting, value);
}
void setBrightness() {
  limitIndex(brightnessValueIndex, sizeof(brightnessValues));
  int value = brightnessValues[brightnessValueIndex];
 
  setBrightness(value);
}

void limitIndex(int &index, int array_size) {
  int maximum = ((array_size / sizeof(int)) - 1);
 
  if(index < 0) index = 0;
  if(index > maximum) index = maximum;
}


void setParallelogram(int value) {
  /*
   * Left
   * 0xfe
   * 0xfd
   * 0xfb
   * 0xf8
   * 0xf4
   * 0xef
   * 0xe9
   * 0xe2
   * 0xda
   * 0xd1
   * 0xc8
   * 0xbf
   * 0xb6
   * 0xad
   * 0xa4
   * 0x9b
   * 0x92
   * 0x89
   * 0x80
   * Right
   */
  writeToIvad(monitorAddress, settingParallelogram, value);
}
void setKeystone(int value) {
  /*
   * Thin top
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Thin Bottom
   */
  writeToIvad(monitorAddress, settingKeystone, value);
}
void setRotation(int value) {
  /*
   * Left
   * 0x7e
   * 0x7c
   * 0x79
   * 0x75
   * 0x70
   * 0x8d
   * 0x63
   * 0x5b
   * 0x52
   * 0x49
   * 0x40
   * 0x37
   * 0x2e
   * 0x25
   * 0x1c
   * 0x13
   * 0x0a
   * 0x01
   * 0x00
   * Right
   */
  writeToIvad(monitorAddress, settingRotation, value);
}
void setPincushion(int value) {
  /*
   * Concave
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Convex
   */
  writeToIvad(monitorAddress, settingPincushion, value);
}

Excellent! this will makes things a lot easier for everyone! your serial interface is perfect for the J20 circuit board
because it has a header with serial lines on it (TTL though).

I'm planning on writing a java(or c++) based gui to control the screen
via a serial port that can interface to it.

Nice work!
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
I was talking about collaborator access. But it looks like having a fork and making pull requests will work just fine.
 

oshimai

macrumors member
Apr 7, 2020
53
31
Europe
I got my iMac apart now and took out the logic board, down converter, and drive cage. I've been staring at https://raw.githubusercontent.com/q...ter/images/imac_g3_slot_loading_pav_board.png and probing things using a multi meter while I wait for some parts to arrive to try the VSYNC detection stuff.

Should I be worried if the J22 pins 24-26 do not give me AC 24V on the meter when power is plugged in?

I do get 5V on J20 pin 6.

The CRT and analog boards are known-good since this iMac was working before I took out the logic board. I also didn't touch the CRT portion since it seems like the top cover won't come off without considerable damage to the plastic tabs that keep it in place.

Sorry if this is a stupid question or if I missed something obvious. :/
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
I got my iMac apart now and took out the logic board, down converter, and drive cage. I've been staring at https://raw.githubusercontent.com/q...ter/images/imac_g3_slot_loading_pav_board.png and probing things using a multi meter while I wait for some parts to arrive to try the VSYNC detection stuff.

Should I be worried if the J22 pins 24-26 do not give me AC 24V on the meter when power is plugged in?

I do get 5V on J20 pin 6.

The CRT and analog boards are known-good since this iMac was working before I took out the logic board. I also didn't touch the CRT portion since it seems like the top cover won't come off without considerable damage to the plastic tabs that keep it in place.

Sorry if this is a stupid question or if I missed something obvious. :/

Not a stupid question at all!

When I first attempted to measure the voltage I couldn't see anything either but I soon discovered that the analog
board must be on for there to be voltage on those pins.

I also discovered through trial and error that my klein multi-meter was not capable of measuring the voltage and I think that's probably because the frequency of the voltage is out of its range so I borrowed a higher quality multi-meter and sure enough, the voltages are there.

They are out of phase by 180 degrees so you'll measure 48VAC when measured relative to each other and 24VAC when measured relative to ground.

Pin 6 on the J20 connector is the trickle voltage and it's always there, this is what powers the atmega328p when the CRT is off.
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
I made a simple serial interface to control it.

You can type several characters at a time into the serial monitor. And it'll execute them one by one. I didn't add characters for controlling all settings because I don't care about rotation, parallelogram, or pincushion.

With this I'm able to get perfect settings for my iMac's display with all of my VGA devices.

Next up is saving the settings and autoloading them next time the Arduino starts. I don't think I'll get to that until the weekend.


Code:
#include <Wire.h>
#include <SoftwareWire.h>

byte data = -1;
int counter = 0 ;

int monitorAddress = 0x46;

int settingParallelogram = 0x0f;
int settingKeystone = 0x0b;
int settingRotation = 0x12;
int settingPincushion = 0x0c;


int verticalPositionSetting = 0x09;
const int verticalPositionValues[19] = {
  // Low to High
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int verticalPositionValueIndex = 9;

int contrastSetting = 0x00;
const int contrastValues[73] = {
  // Low to High
  0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
  0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
  0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};
int contrastValueIndex = 72;

int horizontalPositionSetting = 0x07;
const int horizontalPositionValues[19] = {
  0xfe, 0xfc, 0xf9, 0xf5, 0xf0, 0xea, 0xe3, 0xdb, 0xd2, 0xc9, 0xc0, 0xb7, 0xae, 0xa5, 0x9c, 0x93, 0x8a, 0x81, 0x80
};
int horizontalPositionValueIndex = 11;

int heightSetting = 0x08;
const int heightValues[19] = {
  // short to tall
  0x81, 0x83, 0x10, 0x8a, 0x8f, 0x95, 0x9c, 0xa4, 0xad, 0xb6, 0xbf, 0xc8, 0x1a, 0xda, 0xe3, 0xec, 0xf5, 0xfe, 0xff
};
int heightValueIndex = 14;

int widthSetting = 0x0d;
const int widthValues[19] = {
  // thinnest to thickest
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int widthValueIndex = 12;

int brightnessSetting = 0x11;
int brightnessValues[10] = {
  // Dim to Bright
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a
};
int brightnessValueIndex = 9;

const byte edid[128] =
{

  0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x06, 0x10, 0x74, 0x61,
  0xed, 0x5f, 0x84, 0x00, 0x06, 0x1e, 0x01, 0x03, 0x6d, 0x1a, 0x14, 0x78,
  0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 0x20, 0x50, 0x54, 0x00,
  0x00, 0x00, 0x61, 0x4f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x1e, 0x00, 0x30, 0x41, 0x00,
  0x34, 0x30, 0x14, 0x60, 0xd3, 0x00, 0x0a, 0xc8, 0x10, 0x00, 0x00, 0x1e,
  0x5f, 0x18, 0x20, 0xf0, 0x30, 0x58, 0x2c, 0x20, 0x15, 0x50, 0x93, 0x00,
  0xd0, 0x9c, 0x00, 0x00, 0x00, 0x18, 0x7d, 0x13, 0x80, 0xc0, 0x20, 0xe0,
  0x22, 0x10, 0x11, 0x40, 0x13, 0x00, 0xa6, 0x7d, 0x00, 0x00, 0x00, 0x18,
  0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x3a, 0x3d, 0x08, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19
};


//The init sequence is sent on a software i2c bus.
// sda is on 4 and scl is on 5
SoftwareWire softWire(4, 5);

void setup() {
  Serial.begin(9600);
 
  initIvadBoard();

  Wire.begin(0x50);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveData);
}

void requestEvent() {
  Wire.write(edid, 128);
}

void receiveData(int byteCount) {
  while (Wire.available()) {
    data = Wire.read();
  }
}

void loop() {
  handleSerial();
}

void handleSerial() {
  /*
   * a = move left
   * s = move right
   * w = move up
   * z = move down
   *
   * d = skinnier
   * f = fatter
   * r = taller
   * c = shorter
   *
   * g = contrast down
   * h = contrast up
   *
   * j = brightness down
   * k = brightness up
   *
   *
   */

 
  while(Serial.available() > 0) {
    char incoming = Serial.read();

    switch(incoming) {
      case 'a':
        moveLeft();
        break;
      case 's':
        moveRight();
        break;
      case 'w':
        moveUp();
        break;
      case 'z':
        moveDown();
        break;

      case 'd':
        decreaseWidth();
        break;
      case 'f':
        increaseWidth();
        break;
      case 'r':
        increaseHeight();
        break;
      case 'c':
        decreaseHeight();
        break;

      case 'g':
        decreaseContrast();
        break;
      case 'h':
        increaseContrast();
        break;

      case 'j':
         decreaseBrightness();
         break;
      case 'k':
         increaseBrightness();
         break;
    }
  }
}

void writeToIvad(int address, int message) {
  softWire.beginTransmission(address);
  softWire.write(message);
  softWire.endTransmission();
}

void writeToIvad(int address, int message1, int message2) {
  softWire.beginTransmission(address);
  softWire.write(message1);
  softWire.write(message2);
  softWire.endTransmission();
}

void  readFromIvad(int address, int bytes) {
  char buf[bytes + 1];
  int bytesRead = 0;
  softWire.requestFrom(address, bytes);
  while (softWire.available())
  {
    char c = softWire.read();
    buf[bytesRead++] = c;
  }
  buf[bytesRead] = '\0';
}


void initIvadBoard() {
    // removed for brevity
}


void moveLeft() {
  horizontalPositionValueIndex--;
  setHorizontalPosition();
}
void moveRight() {
  horizontalPositionValueIndex++;
  setHorizontalPosition();
}

void moveUp() {
  verticalPositionValueIndex++;
  setVerticalPosition();
}
void moveDown() {
  verticalPositionValueIndex--;
  setVerticalPosition();
}

void increaseWidth() {
  widthValueIndex++;
  setWidth();
}
void decreaseWidth() {
  widthValueIndex--;
  setWidth();
}

void increaseHeight() {
  heightValueIndex++;
 
  setHeight();
}
void decreaseHeight() {
  heightValueIndex--;
 
  setHeight();
}

void decreaseContrast() {
  contrastValueIndex--;
  setContrast();
}
void increaseContrast() {
  contrastValueIndex++;

  setContrast();
}

void decreaseBrightness() {
  brightnessValueIndex--;
 
  setBrightness();
}
void increaseBrightness() {
  brightnessValueIndex++;
 
  setBrightness();
}


void setVerticalPosition(int value) {
  writeToIvad(monitorAddress, verticalPositionSetting, value);
}
void setVerticalPosition() {
  limitIndex(verticalPositionValueIndex, sizeof(verticalPositionValues));
  int value = verticalPositionValues[verticalPositionValueIndex];

  setVerticalPosition(value);
}

void setHorizontalPosition(int value) {
  writeToIvad(monitorAddress, horizontalPositionSetting, value);
}
void setHorizontalPosition() {
  limitIndex(horizontalPositionValueIndex, sizeof(horizontalPositionValues));
  int value = horizontalPositionValues[horizontalPositionValueIndex];

  setHorizontalPosition(value);
}

void setHeight(int value) {
  writeToIvad(monitorAddress, heightSetting, value);
}
void setHeight() {
  limitIndex(heightValueIndex, sizeof(heightValues));
  int value = heightValues[heightValueIndex];

  setHeight(value);
}

void setWidth(int value) {
  writeToIvad(monitorAddress, widthSetting, value);
}
void setWidth() {
  limitIndex(widthValueIndex, sizeof(widthValues));
  int value = widthValues[widthValueIndex];
 
  setWidth(value);
}

void setContrast(int value) {
  writeToIvad(monitorAddress, contrastSetting, value);
}
void setContrast() {
  limitIndex(contrastValueIndex, sizeof(contrastValues));
  int value = contrastValues[contrastValueIndex];
 
  setContrast(value);
}

void setBrightness(int value) {
  writeToIvad(monitorAddress, brightnessSetting, value);
  writeToIvad(monitorAddress, brightnessSetting, value);
}
void setBrightness() {
  limitIndex(brightnessValueIndex, sizeof(brightnessValues));
  int value = brightnessValues[brightnessValueIndex];
 
  setBrightness(value);
}

void limitIndex(int &index, int array_size) {
  int maximum = ((array_size / sizeof(int)) - 1);
 
  if(index < 0) index = 0;
  if(index > maximum) index = maximum;
}


void setParallelogram(int value) {
  /*
   * Left
   * 0xfe
   * 0xfd
   * 0xfb
   * 0xf8
   * 0xf4
   * 0xef
   * 0xe9
   * 0xe2
   * 0xda
   * 0xd1
   * 0xc8
   * 0xbf
   * 0xb6
   * 0xad
   * 0xa4
   * 0x9b
   * 0x92
   * 0x89
   * 0x80
   * Right
   */
  writeToIvad(monitorAddress, settingParallelogram, value);
}
void setKeystone(int value) {
  /*
   * Thin top
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Thin Bottom
   */
  writeToIvad(monitorAddress, settingKeystone, value);
}
void setRotation(int value) {
  /*
   * Left
   * 0x7e
   * 0x7c
   * 0x79
   * 0x75
   * 0x70
   * 0x8d
   * 0x63
   * 0x5b
   * 0x52
   * 0x49
   * 0x40
   * 0x37
   * 0x2e
   * 0x25
   * 0x1c
   * 0x13
   * 0x0a
   * 0x01
   * 0x00
   * Right
   */
  writeToIvad(monitorAddress, settingRotation, value);
}
void setPincushion(int value) {
  /*
   * Concave
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Convex
   */
  writeToIvad(monitorAddress, settingPincushion, value);
}


I just finshed testing your code and it works great!

Here's a video of the adjustments being made from my raspberry pi .

I used an arduino as a usb to TTL serial converter because I can't find mine.
The J20 board has the Arduino uno pins 0 and 1(rx and tx) coming out to a header so it's super easy to interface to.

 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
I made a simple serial interface to control it.

You can type several characters at a time into the serial monitor. And it'll execute them one by one. I didn't add characters for controlling all settings because I don't care about rotation, parallelogram, or pincushion.

With this I'm able to get perfect settings for my iMac's display with all of my VGA devices.

Next up is saving the settings and autoloading them next time the Arduino starts. I don't think I'll get to that until the weekend.


Code:
#include <Wire.h>
#include <SoftwareWire.h>

byte data = -1;
int counter = 0 ;

int monitorAddress = 0x46;

int settingParallelogram = 0x0f;
int settingKeystone = 0x0b;
int settingRotation = 0x12;
int settingPincushion = 0x0c;


int verticalPositionSetting = 0x09;
const int verticalPositionValues[19] = {
  // Low to High
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int verticalPositionValueIndex = 9;

int contrastSetting = 0x00;
const int contrastValues[73] = {
  // Low to High
  0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
  0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
  0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};
int contrastValueIndex = 72;

int horizontalPositionSetting = 0x07;
const int horizontalPositionValues[19] = {
  0xfe, 0xfc, 0xf9, 0xf5, 0xf0, 0xea, 0xe3, 0xdb, 0xd2, 0xc9, 0xc0, 0xb7, 0xae, 0xa5, 0x9c, 0x93, 0x8a, 0x81, 0x80
};
int horizontalPositionValueIndex = 11;

int heightSetting = 0x08;
const int heightValues[19] = {
  // short to tall
  0x81, 0x83, 0x10, 0x8a, 0x8f, 0x95, 0x9c, 0xa4, 0xad, 0xb6, 0xbf, 0xc8, 0x1a, 0xda, 0xe3, 0xec, 0xf5, 0xfe, 0xff
};
int heightValueIndex = 14;

int widthSetting = 0x0d;
const int widthValues[19] = {
  // thinnest to thickest
  0x7e, 0x7c, 0x79, 0x75, 0x70, 0x6a, 0x63, 0x5b, 0x52, 0x49, 0x40, 0x37, 0x2e, 0x25, 0x1c, 0x13, 0x0a, 0x01, 0x00
};
int widthValueIndex = 12;

int brightnessSetting = 0x11;
int brightnessValues[10] = {
  // Dim to Bright
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a
};
int brightnessValueIndex = 9;

const byte edid[128] =
{

  0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x06, 0x10, 0x74, 0x61,
  0xed, 0x5f, 0x84, 0x00, 0x06, 0x1e, 0x01, 0x03, 0x6d, 0x1a, 0x14, 0x78,
  0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 0x20, 0x50, 0x54, 0x00,
  0x00, 0x00, 0x61, 0x4f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x1e, 0x00, 0x30, 0x41, 0x00,
  0x34, 0x30, 0x14, 0x60, 0xd3, 0x00, 0x0a, 0xc8, 0x10, 0x00, 0x00, 0x1e,
  0x5f, 0x18, 0x20, 0xf0, 0x30, 0x58, 0x2c, 0x20, 0x15, 0x50, 0x93, 0x00,
  0xd0, 0x9c, 0x00, 0x00, 0x00, 0x18, 0x7d, 0x13, 0x80, 0xc0, 0x20, 0xe0,
  0x22, 0x10, 0x11, 0x40, 0x13, 0x00, 0xa6, 0x7d, 0x00, 0x00, 0x00, 0x18,
  0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x3a, 0x3d, 0x08, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19
};


//The init sequence is sent on a software i2c bus.
// sda is on 4 and scl is on 5
SoftwareWire softWire(4, 5);

void setup() {
  Serial.begin(9600);

  initIvadBoard();

  Wire.begin(0x50);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveData);
}

void requestEvent() {
  Wire.write(edid, 128);
}

void receiveData(int byteCount) {
  while (Wire.available()) {
    data = Wire.read();
  }
}

void loop() {
  handleSerial();
}

void handleSerial() {
  /*
   * a = move left
   * s = move right
   * w = move up
   * z = move down
   *
   * d = skinnier
   * f = fatter
   * r = taller
   * c = shorter
   *
   * g = contrast down
   * h = contrast up
   *
   * j = brightness down
   * k = brightness up
   *
   *
   */


  while(Serial.available() > 0) {
    char incoming = Serial.read();

    switch(incoming) {
      case 'a':
        moveLeft();
        break;
      case 's':
        moveRight();
        break;
      case 'w':
        moveUp();
        break;
      case 'z':
        moveDown();
        break;

      case 'd':
        decreaseWidth();
        break;
      case 'f':
        increaseWidth();
        break;
      case 'r':
        increaseHeight();
        break;
      case 'c':
        decreaseHeight();
        break;

      case 'g':
        decreaseContrast();
        break;
      case 'h':
        increaseContrast();
        break;

      case 'j':
         decreaseBrightness();
         break;
      case 'k':
         increaseBrightness();
         break;
    }
  }
}

void writeToIvad(int address, int message) {
  softWire.beginTransmission(address);
  softWire.write(message);
  softWire.endTransmission();
}

void writeToIvad(int address, int message1, int message2) {
  softWire.beginTransmission(address);
  softWire.write(message1);
  softWire.write(message2);
  softWire.endTransmission();
}

void  readFromIvad(int address, int bytes) {
  char buf[bytes + 1];
  int bytesRead = 0;
  softWire.requestFrom(address, bytes);
  while (softWire.available())
  {
    char c = softWire.read();
    buf[bytesRead++] = c;
  }
  buf[bytesRead] = '\0';
}


void initIvadBoard() {
    // removed for brevity
}


void moveLeft() {
  horizontalPositionValueIndex--;
  setHorizontalPosition();
}
void moveRight() {
  horizontalPositionValueIndex++;
  setHorizontalPosition();
}

void moveUp() {
  verticalPositionValueIndex++;
  setVerticalPosition();
}
void moveDown() {
  verticalPositionValueIndex--;
  setVerticalPosition();
}

void increaseWidth() {
  widthValueIndex++;
  setWidth();
}
void decreaseWidth() {
  widthValueIndex--;
  setWidth();
}

void increaseHeight() {
  heightValueIndex++;

  setHeight();
}
void decreaseHeight() {
  heightValueIndex--;

  setHeight();
}

void decreaseContrast() {
  contrastValueIndex--;
  setContrast();
}
void increaseContrast() {
  contrastValueIndex++;

  setContrast();
}

void decreaseBrightness() {
  brightnessValueIndex--;

  setBrightness();
}
void increaseBrightness() {
  brightnessValueIndex++;

  setBrightness();
}


void setVerticalPosition(int value) {
  writeToIvad(monitorAddress, verticalPositionSetting, value);
}
void setVerticalPosition() {
  limitIndex(verticalPositionValueIndex, sizeof(verticalPositionValues));
  int value = verticalPositionValues[verticalPositionValueIndex];

  setVerticalPosition(value);
}

void setHorizontalPosition(int value) {
  writeToIvad(monitorAddress, horizontalPositionSetting, value);
}
void setHorizontalPosition() {
  limitIndex(horizontalPositionValueIndex, sizeof(horizontalPositionValues));
  int value = horizontalPositionValues[horizontalPositionValueIndex];

  setHorizontalPosition(value);
}

void setHeight(int value) {
  writeToIvad(monitorAddress, heightSetting, value);
}
void setHeight() {
  limitIndex(heightValueIndex, sizeof(heightValues));
  int value = heightValues[heightValueIndex];

  setHeight(value);
}

void setWidth(int value) {
  writeToIvad(monitorAddress, widthSetting, value);
}
void setWidth() {
  limitIndex(widthValueIndex, sizeof(widthValues));
  int value = widthValues[widthValueIndex];

  setWidth(value);
}

void setContrast(int value) {
  writeToIvad(monitorAddress, contrastSetting, value);
}
void setContrast() {
  limitIndex(contrastValueIndex, sizeof(contrastValues));
  int value = contrastValues[contrastValueIndex];

  setContrast(value);
}

void setBrightness(int value) {
  writeToIvad(monitorAddress, brightnessSetting, value);
  writeToIvad(monitorAddress, brightnessSetting, value);
}
void setBrightness() {
  limitIndex(brightnessValueIndex, sizeof(brightnessValues));
  int value = brightnessValues[brightnessValueIndex];

  setBrightness(value);
}

void limitIndex(int &index, int array_size) {
  int maximum = ((array_size / sizeof(int)) - 1);

  if(index < 0) index = 0;
  if(index > maximum) index = maximum;
}


void setParallelogram(int value) {
  /*
   * Left
   * 0xfe
   * 0xfd
   * 0xfb
   * 0xf8
   * 0xf4
   * 0xef
   * 0xe9
   * 0xe2
   * 0xda
   * 0xd1
   * 0xc8
   * 0xbf
   * 0xb6
   * 0xad
   * 0xa4
   * 0x9b
   * 0x92
   * 0x89
   * 0x80
   * Right
   */
  writeToIvad(monitorAddress, settingParallelogram, value);
}
void setKeystone(int value) {
  /*
   * Thin top
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Thin Bottom
   */
  writeToIvad(monitorAddress, settingKeystone, value);
}
void setRotation(int value) {
  /*
   * Left
   * 0x7e
   * 0x7c
   * 0x79
   * 0x75
   * 0x70
   * 0x8d
   * 0x63
   * 0x5b
   * 0x52
   * 0x49
   * 0x40
   * 0x37
   * 0x2e
   * 0x25
   * 0x1c
   * 0x13
   * 0x0a
   * 0x01
   * 0x00
   * Right
   */
  writeToIvad(monitorAddress, settingRotation, value);
}
void setPincushion(int value) {
  /*
   * Concave
   * 0x81
   * 0x83
   * 0x86
   * 0x8a
   * 0x8f
   * 0x95
   * 0x9c
   * 0xa4
   * 0xad
   * 0xb6
   * 0xbf
   * 0xc8
   * 0xd1
   * 0xda
   * 0xe3
   * 0xec
   * 0xf5
   * 0xfe
   * 0xff
   * Convex
   */
  writeToIvad(monitorAddress, settingPincushion, value);
}


We have enough constants now that I pulled them out into a header file "ivad.h" and I reworked the code to get it ready for saving
and loading values from the chips flash memory.

This also allowed me to replace hard coded values in my init sequence with values from the the arrays you provided.
I think we have enough information to start building a generic init sequence that can serve as the reference point.
I think what we need now is saving and loading the property indices the user saves.

It looks way different but the logic is the same.

I also implemented the remaining picture properties. pincusion, parallelogram ect...
 
  • Like
Reactions: anotherelise

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
@anotherelise

I thought I was posting here...

My iMac has osx 10.2 and I think you said your running 9 so the display interface might be different,
in any event I just made some screen caps and I noticed that there is a factory defaults button.
Do you have this? If so, could you sniff the values coming from that? I think those values
might be the ones we could use to make a default init sequence.
 

oshimai

macrumors member
Apr 7, 2020
53
31
Europe
Since I still can't contribute my VSYNC test due to the parts I need to get started not having arrived (I don't have a ready to go electronics lab in my basement :( but if DHL doesn't let me down by Wednesday I have the basics), I figured I could make myself useful in another way. I popped the logic board back in, booted up MacOS 9 and opened various monitor related programs on my MacOS 9 install using Apple's ResEdit.

I found these three edid resources in the System folder (it was in something called "Apple Monitor Plug-In" IIRC)

Maybe one of them is for the iMac Display?

Resource IDs as found

20005
Code:
00FFFFFFFFFFFF0006105203010101010106010128261D86E801C9A05749982712484C3FFF8001013159455981596159A94F01010101100E80C020E01D10383813007C221100001EF91520F830581F20204013007C221100001E000000FD0030781E5EFF000A202020202020000000FC0041706C566973696F6E3835300A00A4

20004
Code:
00FFFFFFFFFFFF000610EE02010101010101010128201886E801C9A05749982712484C356B8001010101010101010101010101010101100E80C020E01D10383813007C221100001E100E80C020E01D10383813007C221100001E000000FD0030781E53FF000A202020202020000000FC0041706C566973696F6E3735302000A9

20002
Code:
00FFFFFFFFFFFF0006101092010101010008010168271D86E80DC9A05747982712484C35EF8031594559615981808199A940A94FA959010101010101010101010101010101010103010101010101010101010101010101010103000000FD0030781E6B17000A202020202020000000FC0053747564696F4473706C793231000F

Screenshot attached.

I also made screenshots of all the relevant icon and picture resources I found in case someone wants to clone the monitor setting panel exactly as it appears. If anyone wants those, let me know. Though it will take patience to cut out the individual graphics as I could not figure out how to actually extract the individual elements even though ResEdit would gladly let me edit them in its built-in pixel editor.

I will be cloning the MacOS 9 display control panel in C# for Windows at some point in the future and dump the code somewhere for others to use, though that stuff will be very specific for my needs. Sadly until I get the parts I ordered in the mail all I can really offer is poking around in stuff in MacOS 9 or extracting files from it. :/

What I really wanted to find, but didn't yet, was some sort of default settings resource that gets read when you restore everything to default.

There is a monitor settings document in the system preferences folder but I have no idea how to view its raw content. Last time I used classic MacOS was 20 years ago in school to play Oregon Trail. When I use it now I just think "Thanks, I hate it" because its so clunky.

That settings file may be an easier(?) alternative to sniffing data bytes. Restore defaults, close monitor settings, then extract data from the settings file.

Bild 2223.PNG
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Since I still can't contribute my VSYNC test due to the parts I need to get started not having arrived (I don't have a ready to go electronics lab in my basement :( but if DHL doesn't let me down by Wednesday I have the basics), I figured I could make myself useful in another way. I popped the logic board back in, booted up MacOS 9 and opened various monitor related programs on my MacOS 9 install using Apple's ResEdit.

I found these three edid resources in the System folder (it was in something called "Apple Monitor Plug-In" IIRC)

Maybe one of them is for the iMac Display?

Resource IDs as found

20005
Code:
00FFFFFFFFFFFF0006105203010101010106010128261D86E801C9A05749982712484C3FFF8001013159455981596159A94F01010101100E80C020E01D10383813007C221100001EF91520F830581F20204013007C221100001E000000FD0030781E5EFF000A202020202020000000FC0041706C566973696F6E3835300A00A4

20004
Code:
00FFFFFFFFFFFF000610EE02010101010101010128201886E801C9A05749982712484C356B8001010101010101010101010101010101100E80C020E01D10383813007C221100001E100E80C020E01D10383813007C221100001E000000FD0030781E53FF000A202020202020000000FC0041706C566973696F6E3735302000A9

20002
Code:
00FFFFFFFFFFFF0006101092010101010008010168271D86E80DC9A05747982712484C35EF8031594559615981808199A940A94FA959010101010101010101010101010101010103010101010101010101010101010101010103000000FD0030781E6B17000A202020202020000000FC0053747564696F4473706C793231000F

Screenshot attached.

I also made screenshots of all the relevant icon and picture resources I found in case someone wants to clone the monitor setting panel exactly as it appears. If anyone wants those, let me know. Though it will take patience to cut out the individual graphics as I could not figure out how to actually extract the individual elements even though ResEdit would gladly let me edit them in its built-in pixel editor.

I will be cloning the MacOS 9 display control panel in C# for Windows at some point in the future and dump the code somewhere for others to use, though that stuff will be very specific for my needs. Sadly until I get the parts I ordered in the mail all I can really offer is poking around in stuff in MacOS 9 or extracting files from it. :/

What I really wanted to find, but didn't yet, was some sort of default settings resource that gets read when you restore everything to default.

There is a monitor settings document in the system preferences folder but I have no idea how to view its raw content. Last time I used classic MacOS was 20 years ago in school to play Oregon Trail. When I use it now I just think "Thanks, I hate it" because its so clunky.

That settings file may be an easier(?) alternative to sniffing data bytes. Restore defaults, close monitor settings, then extract data from the settings file.

View attachment 905226


I just looked at the first string of hex bytes and there are 128 bytes being represented which is some evidence that it might be
EDID information because older EDID's are 128 bytes long. That would be a really nice find indeed.

I'm in the middle of make a front end for the monitor controls in java only because I would like to only write it once and run it everywhere.
I know these days C# is platform independent but how does the serial library work across the different platforms?

I was able to draw my own images to mimic the panel but I'm trying to mimic 10.2 not OS9.
Not because I prefer 10.2 but because it's what I have.

That said, if you have something already or are planning on something soon, I would love to use your front end as long as it can
work on linux, specifically on the raspberry pi.

I'll look at this stuff tonight
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Since I still can't contribute my VSYNC test due to the parts I need to get started not having arrived (I don't have a ready to go electronics lab in my basement :( but if DHL doesn't let me down by Wednesday I have the basics), I figured I could make myself useful in another way. I popped the logic board back in, booted up MacOS 9 and opened various monitor related programs on my MacOS 9 install using Apple's ResEdit.

I found these three edid resources in the System folder (it was in something called "Apple Monitor Plug-In" IIRC)

Maybe one of them is for the iMac Display?

Resource IDs as found

20005
Code:
00FFFFFFFFFFFF0006105203010101010106010128261D86E801C9A05749982712484C3FFF8001013159455981596159A94F01010101100E80C020E01D10383813007C221100001EF91520F830581F20204013007C221100001E000000FD0030781E5EFF000A202020202020000000FC0041706C566973696F6E3835300A00A4

20004
Code:
00FFFFFFFFFFFF000610EE02010101010101010128201886E801C9A05749982712484C356B8001010101010101010101010101010101100E80C020E01D10383813007C221100001E100E80C020E01D10383813007C221100001E000000FD0030781E53FF000A202020202020000000FC0041706C566973696F6E3735302000A9

20002
Code:
00FFFFFFFFFFFF0006101092010101010008010168271D86E80DC9A05747982712484C35EF8031594559615981808199A940A94FA959010101010101010101010101010101010103010101010101010101010101010101010103000000FD0030781E6B17000A202020202020000000FC0053747564696F4473706C793231000F

Screenshot attached.

I also made screenshots of all the relevant icon and picture resources I found in case someone wants to clone the monitor setting panel exactly as it appears. If anyone wants those, let me know. Though it will take patience to cut out the individual graphics as I could not figure out how to actually extract the individual elements even though ResEdit would gladly let me edit them in its built-in pixel editor.

I will be cloning the MacOS 9 display control panel in C# for Windows at some point in the future and dump the code somewhere for others to use, though that stuff will be very specific for my needs. Sadly until I get the parts I ordered in the mail all I can really offer is poking around in stuff in MacOS 9 or extracting files from it. :/

What I really wanted to find, but didn't yet, was some sort of default settings resource that gets read when you restore everything to default.

There is a monitor settings document in the system preferences folder but I have no idea how to view its raw content. Last time I used classic MacOS was 20 years ago in school to play Oregon Trail. When I use it now I just think "Thanks, I hate it" because its so clunky.

That settings file may be an easier(?) alternative to sniffing data bytes. Restore defaults, close monitor settings, then extract data from the settings file.

View attachment 905226


So it looks like the the first one is and EDID ill have to check the others. it's odd though
it see it as an apple product

Extracted contents:
header: 00 ff ff ff ff ff ff 00
serial number: 06 10 52 03 01 01 01 01 01 06
version: 01 01
basic params: 28 26 1d 86 e8
chroma info: 01 c9 a0 57 49 98 27 12 48 4c
established: 3f ff 80
standard: 01 01 31 59 45 59 81 59 61 59 a9 4f 01 01 01 01
descriptor 1: 10 0e 80 c0 20 e0 1d 10 38 38 13 00 7c 22 11 00 00 1e
descriptor 2: f9 15 20 f8 30 58 1f 20 20 40 13 00 7c 22 11 00 00 1e
descriptor 3: 00 00 00 fd 00 30 78 1e 5e ff 00 0a 20 20 20 20 20 20
descriptor 4: 00 00 00 fc 00 41 70 6c 56 69 73 69 6f 6e 38 35 30 0a
extensions: 00
checksum: a4

Manufacturer: APP Model 352 Serial Number 16843009
EDID version: 1.1
Analog display, Input voltage level: 0.714/0.286 V
Sync: Separate
Maximum image size: 38 cm x 29 cm
Gamma: 2.34
DPMS levels: Standby Suspend Off
RGB color display
Established timings supported:
640x480@60Hz
640x480@67Hz
640x480@72Hz
640x480@75Hz
800x600@56Hz
800x600@60Hz
800x600@72Hz
800x600@75Hz
832x624@75Hz
1280x768@87Hz
1024x768@60Hz
1024x768@70Hz
1024x768@75Hz
1280x1024@75Hz
1152x870@75Hz
Standard timings supported:
640x480@85Hz
800x600@85Hz
1280x960@85Hz
1024x768@85Hz
1600x1200@75Hz
Detailed mode: Clock 36.000 MHz, 380 mm x 290 mm
640 696 752 832 hborder 0
480 481 484 509 vborder 0
+hsync +vsync
Detailed mode: Clock 56.250 MHz, 380 mm x 290 mm
800 832 896 1048 hborder 0
600 601 604 631 vborder 0
+hsync +vsync
Monitor ranges (GTF): 48-120Hz V, 30-94kHz H, max dotclock 2550MHz
Monitor name: AplVision850
Checksum: 0xa4 (valid)
EDID block does NOT conform to EDID 1.0!
Has descriptor blocks other than detailed timings
EDID block does not conform at all!
Bad year of manufacture
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Since I still can't contribute my VSYNC test due to the parts I need to get started not having arrived (I don't have a ready to go electronics lab in my basement :( but if DHL doesn't let me down by Wednesday I have the basics), I figured I could make myself useful in another way. I popped the logic board back in, booted up MacOS 9 and opened various monitor related programs on my MacOS 9 install using Apple's ResEdit.

I found these three edid resources in the System folder (it was in something called "Apple Monitor Plug-In" IIRC)

Maybe one of them is for the iMac Display?

Resource IDs as found

20005
Code:
00FFFFFFFFFFFF0006105203010101010106010128261D86E801C9A05749982712484C3FFF8001013159455981596159A94F01010101100E80C020E01D10383813007C221100001EF91520F830581F20204013007C221100001E000000FD0030781E5EFF000A202020202020000000FC0041706C566973696F6E3835300A00A4

20004
Code:
00FFFFFFFFFFFF000610EE02010101010101010128201886E801C9A05749982712484C356B8001010101010101010101010101010101100E80C020E01D10383813007C221100001E100E80C020E01D10383813007C221100001E000000FD0030781E53FF000A202020202020000000FC0041706C566973696F6E3735302000A9

20002
Code:
00FFFFFFFFFFFF0006101092010101010008010168271D86E80DC9A05747982712484C35EF8031594559615981808199A940A94FA959010101010101010101010101010101010103010101010101010101010101010101010103000000FD0030781E6B17000A202020202020000000FC0053747564696F4473706C793231000F

Screenshot attached.

I also made screenshots of all the relevant icon and picture resources I found in case someone wants to clone the monitor setting panel exactly as it appears. If anyone wants those, let me know. Though it will take patience to cut out the individual graphics as I could not figure out how to actually extract the individual elements even though ResEdit would gladly let me edit them in its built-in pixel editor.

I will be cloning the MacOS 9 display control panel in C# for Windows at some point in the future and dump the code somewhere for others to use, though that stuff will be very specific for my needs. Sadly until I get the parts I ordered in the mail all I can really offer is poking around in stuff in MacOS 9 or extracting files from it. :/

What I really wanted to find, but didn't yet, was some sort of default settings resource that gets read when you restore everything to default.

There is a monitor settings document in the system preferences folder but I have no idea how to view its raw content. Last time I used classic MacOS was 20 years ago in school to play Oregon Trail. When I use it now I just think "Thanks, I hate it" because its so clunky.

That settings file may be an easier(?) alternative to sniffing data bytes. Restore defaults, close monitor settings, then extract data from the settings file.

View attachment 905226


I've converted the ascii hex to binary files and run them through edid-decode and they are indeed valid edid files.
The checksums are good and the manufacturer is apple. However, the three files are different and although they
have in the list of established timings the correct rez and freqs for the iMac G3, there are other listed that do not work with it.

I'll have to burn it and test it.

I'll let you know.
 

rockyhill

macrumors regular
Dec 24, 2016
214
67
Miami Fl, United States
Since I still can't contribute my VSYNC test due to the parts I need to get started not having arrived (I don't have a ready to go electronics lab in my basement :( but if DHL doesn't let me down by Wednesday I have the basics), I figured I could make myself useful in another way. I popped the logic board back in, booted up MacOS 9 and opened various monitor related programs on my MacOS 9 install using Apple's ResEdit.

I found these three edid resources in the System folder (it was in something called "Apple Monitor Plug-In" IIRC)

Maybe one of them is for the iMac Display?

Resource IDs as found

20005
Code:
00FFFFFFFFFFFF0006105203010101010106010128261D86E801C9A05749982712484C3FFF8001013159455981596159A94F01010101100E80C020E01D10383813007C221100001EF91520F830581F20204013007C221100001E000000FD0030781E5EFF000A202020202020000000FC0041706C566973696F6E3835300A00A4

20004
Code:
00FFFFFFFFFFFF000610EE02010101010101010128201886E801C9A05749982712484C356B8001010101010101010101010101010101100E80C020E01D10383813007C221100001E100E80C020E01D10383813007C221100001E000000FD0030781E53FF000A202020202020000000FC0041706C566973696F6E3735302000A9

20002
Code:
00FFFFFFFFFFFF0006101092010101010008010168271D86E80DC9A05747982712484C35EF8031594559615981808199A940A94FA959010101010101010101010101010101010103010101010101010101010101010101010103000000FD0030781E6B17000A202020202020000000FC0053747564696F4473706C793231000F

Screenshot attached.

I also made screenshots of all the relevant icon and picture resources I found in case someone wants to clone the monitor setting panel exactly as it appears. If anyone wants those, let me know. Though it will take patience to cut out the individual graphics as I could not figure out how to actually extract the individual elements even though ResEdit would gladly let me edit them in its built-in pixel editor.

I will be cloning the MacOS 9 display control panel in C# for Windows at some point in the future and dump the code somewhere for others to use, though that stuff will be very specific for my needs. Sadly until I get the parts I ordered in the mail all I can really offer is poking around in stuff in MacOS 9 or extracting files from it. :/

What I really wanted to find, but didn't yet, was some sort of default settings resource that gets read when you restore everything to default.

There is a monitor settings document in the system preferences folder but I have no idea how to view its raw content. Last time I used classic MacOS was 20 years ago in school to play Oregon Trail. When I use it now I just think "Thanks, I hate it" because its so clunky.

That settings file may be an easier(?) alternative to sniffing data bytes. Restore defaults, close monitor settings, then extract data from the settings file.

View attachment 905226


I converted the three EDID's into byte arrays, included them in the edid sketch and tested them.
When I used it as a second monitor for my iMac, it mirrored the screen perfectly at 1024x768 using all three EDID's.

I then proceeded to test it with my laptop and I had mixed results.

Two of the EDID's worked perfectly at 1024x768 and one only partially filled the screen at 1024x768.
None of them worked at 800x600 and all of them reported resolutions that are out of range
so as a result did not display anything when I tried to use them.

I've included them in the sketch in the "ivad.h" file and they are on the repo. I kept them there
as comments to experiment with in the future. Who knows, maybe they will work with someone elses
project.


I left the EDID I generated as the default because at least that one only reports working resolutions.

That said, maybe I'll try and extract the numbers for the 1024x768 from one of the EDID's you provided and graft
it into the EDID I made because the screen filled better when I used the apple one.

Thank you for those, I'm sure we'll use them one way or another! Maybe when you get your rig up and running
you can fiddle with them and get them to work.
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
@anotherelise

I thought I was posting here...

My iMac has osx 10.2 and I think you said your running 9 so the display interface might be different,
in any event I just made some screen caps and I noticed that there is a factory defaults button.
Do you have this? If so, could you sniff the values coming from that? I think those values
might be the ones we could use to make a default init sequence.

Mac OS9 has the factory defaults button as well. I'll re-assemble the sniffer in the next few days and capture that traffic and post here. I'll also spend some time on getting the Arduino to save the settings in the next few days. Getting tired of having to re-adjust every time I use it.

Also I'm noticing the settings give a good picture for various os's/machines are different. Like the settings that are just right in os9 are off in OS X 10.15 on my personal laptop. And the settings that are good for my personal laptop are wrong for my work laptop running 10.14. And any of the OS X settings are way off in os9.

Does anyone know how to disable resolution scaling on external monitors? I've only gotten it to work at 1280x1024 scaled resolution. If I pick 1024x768 scaled the CRT goes black and doesn't display an image. But 1024x768 works great in OS9, which doesn't have scaling.
 

anotherelise

macrumors newbie
Mar 22, 2020
17
0
Here's my "Factory Settings" i2c traffic. This is from the Mac OS 9 monitors control panel. Geometry section. In the bottom right corner of that screen there is a "Factory Settings" button.

Code:
<START 0x46 WRITE ACK>
<WRITE 0x07 ACK>
<WRITE 0xad ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0d ACK>
<WRITE 0x27 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0c ACK>
<WRITE 0xc4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x09 ACK>
<WRITE 0x3d ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x08 ACK>
<WRITE 0xe4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0f ACK>
<WRITE 0xc0 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x0b ACK>
<WRITE 0xb4 ACK>
<STOP>
<START 0x46 WRITE ACK>
<WRITE 0x12 ACK>
<WRITE 0x46 ACK>
<STOP>

A lot less data than I expected. Another interesting thing is that the values given for things like width/height are not values that I found when going through the full range of width/height settings during my earlier capture. So clearly the values I captured then aren't the only valid values.

And that reminds me of the video @rockyhill posted using the serial interface for the monitor settings. There were a few values that caused your monitor to black out. But all of those values work fine on mine. I dunno if it's that there are different CRT's in some of these iMac models and not all settings are valid for all models. Or maybe it's certain combinations of settings that are invalid and I haven't gotten the right combination of conflicting values yet.

I think I'm gonna get another IDC-20 breakout and more jumper wires so I can keep my sniffer and vga input setup intact. Like right now I want to try that new EDID data posted by @oshimai , but I'd have to rewire everything again first :(

I'm going to do another capture of the full range of values for each setting again. To see if I get different values this time.
[automerge]1586675052[/automerge]
@oshimai While you're looking through OS 9 monitor files over there: it'd be nice to get the ColorSync profile. I'd like to get that into a format that'll work in OS X and other OS's.
[automerge]1586675424[/automerge]
The number of values capture after restoring to factory defaults are way different than they are during my first capture. For example in that first capture I got 20 different width settings. This time I got over a hundred. I'm going to go through this again for all the settings. Restoring to factory defaults before each. And capture all these new values and update the Arduino sketch.
[automerge]1586675590[/automerge]
I think I see what's going on. The delta of the change for each setting is affected by the way I click the buttons in the control panel. clicking rapidly between the thinnest and thickest width settings gave me 20 values again. A quick click followed by a pause to let the change happen resulted in over 100 values.
 
Last edited:

anotherelise

macrumors newbie
Mar 22, 2020
17
0
Here are the new captures with much more granularity. After testing them I'll open a PR to add the new values to Arduino code.
 

Attachments

  • brightness low to high.txt
    705 bytes · Views: 205
  • rotation counter clockwise to clockwise.txt
    7.9 KB · Views: 147
  • pincushion_concave_to_convex.txt
    7.9 KB · Views: 146
  • parallelogram left to right.txt
    7.7 KB · Views: 158
  • keystone top to bottom .txt
    7.9 KB · Views: 149
  • horizontal_position_left_to_right.txt
    8 KB · Views: 168
  • height_lowest_to_highest.txt
    7.8 KB · Views: 173
  • vertical_position_bottom_to_top.txt
    7.9 KB · Views: 153
  • width_thinnest_to_thickest.txt
    7.9 KB · Views: 163
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.