Wiki source code of Front I/O

Version 8.7 by Kevin Wiki on 2024/07/07 22:21

Show last authors
1 (% class="row" %)
2 (((
3 (% class="col-xs-12 col-sm-8" %)
4 (((
5 = Pinout =
6
7 There is a single cable that controls four parts, each half side of I/O LED and center column of blue LEDs.
8
9 [[image:xserve io main cable and PCB pinout.drawio.png||alt="xserve io main cable pinout.drawio.png"]]
10
11 (% class="wikigeneratedid" %)
12 [[attach:xserve io main cable and PCB pinout.drawio.svg||target="_blank"]]
13
14 (% class="wikigeneratedid" %)
15 This cable allows us to communicate with 4 chips using two data lines, audio & service switch, case switch and (yet to be documented) compute LEDs
16
17 = How we got here =
18
19 Measure out the ground and power signals by comparing with other chips on board. E.g. we find [SAA1064T] datasheet, locate the GND (Vee) and 5V (Vcc) and measure connectivity (0 ohm resistance) between chip pins and cable pins. This gives us pins GND 2 & 8 and PWR 16 & 22.
20
21 We keep doing this for SCL & SDA pins on SAA1064T chip and find pins 14 & 15 and 20 & 21 are I2C clock and data pins for each chip.
22
23 == Finding i2c chip address ==
24
25 === SAA1064T ===
26
27 Reading the datasheet for SAA1064T chip we find that: "//This results in the corresponding valid addresses HEX 70, 72, 74 and 76 for writing and 71, 73, 75 and 77 for reading. All other addresses cannot be acknowledged by the circuit".// Giving us a clue what we are looking for, i2c addresses 0x70, 0x72 or 0x74.
28
29 === PCA9554 ===
30
31 asdf
32
33 === Code example finding i2c addresses ===
34
35 We can test the following addresses manually or use the following code snippet:
36
37 {{code language="C++"}}
38 /*I2C_scanner
39 This sketch tests standard 7-bit addresses.
40 Devices with higher bit address might not be seen properly.*/
41
42 #include <Wire.h>
43
44 void setup() {
45 Wire.begin();
46
47 Serial.begin(9600);
48 while (!Serial);
49 Serial.println("\nI2C Scanner");
50 }
51
52 void loop() {
53 byte error, address;
54 int nDevices;
55
56 Serial.println("Scanning...");
57
58 nDevices = 0;
59 for (address = 1; address < 127; address++ ) {
60 Wire.beginTransmission(address);
61 error = Wire.endTransmission();
62
63 if (error == 0) {
64 Serial.print("I2C device found at address 0x");
65 if (address < 16)
66 Serial.print("0");
67 Serial.print(address, HEX);
68 Serial.println(" !");
69
70 nDevices++;
71 }
72 else if (error == 4) {
73 Serial.print("Unknown error at address 0x");
74 if (address < 16)
75 Serial.print("0");
76 Serial.println(address, HEX);
77 }
78 }
79 if (nDevices == 0)
80 Serial.println("No I2C devices found\n");
81 else
82 Serial.println("done\n");
83
84 delay(5000);
85 }
86 {{/code}}
87
88 == i2c multiplexing with TCA9548 ==
89
90 We have two sets of chips, one for left and one for right where we have two different i2c chips on each side for controlling lights. Since the chips controlling their respective parts have the same address for each side, we can't distinguish them from each other. To handle this we use a i2c multiplexer to selectively communicate with one half at a time, switching TCA9548 between two different output ports.
91
92 === Code example finding i2c ports ===
93
94 To verify wiring, connection, output ports and device addresses run the following script:
95
96 {{code language="c++"}}
97 /**
98 * TCA9548 I2CScanner.ino -- I2C bus scanner for Arduino
99 *
100 * Based on https://playground.arduino.cc/Main/I2cScanner/
101 *
102 */
103
104 #include "Wire.h"
105
106 #define TCAADDR 0x70
107
108 void tcaselect(uint8_t i) {
109 if (i > 7) return;
110
111 Wire.beginTransmission(TCAADDR);
112 Wire.write(1 << i);
113 Wire.endTransmission();
114 }
115
116
117 // standard Arduino setup()
118 void setup()
119 {
120 while (!Serial);
121 delay(1000);
122
123 Wire.begin();
124
125 Serial.begin(9600);
126 Serial.println("\nTCAScanner ready!");
127
128 for (uint8_t t=0; t<8; t++) {
129 tcaselect(t);
130 Serial.print("TCA Port #"); Serial.println(t);
131
132 for (uint8_t addr = 0; addr<=127; addr++) {
133 if (addr == TCAADDR) continue;
134
135 Wire.beginTransmission(addr);
136 if (!Wire.endTransmission()) {
137 Serial.print("Found I2C 0x"); Serial.println(addr,HEX);
138 }
139 }
140 }
141 Serial.println("\ndone");
142 }
143
144 void loop()
145 {
146 }
147 {{/code}}
148
149 == SAA1064T data for driving center IO LED stack ==
150
151 Center IO stack is a stack of 24 LED's, 23 blue and 1 green for ethernet activity. These are duplicated next to each other and driven by each their SAA1064T chips. Earlier we found the i2c address and just by playing around figured out that 4 segments of 1 byte binary values are used to set ship register.
152
153 {{code language="C++"}}
154 void fillColumns() {
155 Serial.println("filling columns");
156 Wire.beginTransmission(saa1064);
157 Wire.write(1);
158 Wire.write(0x7F); // 127 - 1111111
159 Wire.write(0x7F); // 127 - 1111111
160 Wire.write(0x7F); // 127 - 1111111
161 Wire.write(0x1F); // 31 - 11111
162 Wire.endTransmission();
163
164 colsFilled = 1;
165 }
166 {{/code}}
167
168 (% class="wikigeneratedid" %)
169 ~-~- Here the last byte we send only is 5 bits since we only have 5 LEDs instead of 6 to address (total of 24). Also note that we start the transmission with a single bit. ~-~-
170
171 == Pinouts voltages from MLB ==
172
173 Powered off:
174
175 * PWR fail LED - 0.00 V
176 * UID LED - 4.5V
177 * OH/Fan fail LED - 4.72 V
178 * NIC1 LED - 0.8 - 2.6 V
179 * NIC2 LED - 2.95 V
180 * UID SW - 2.8V
181 * HDD LED - 0.00 V
182 * Power LED P3V3 - 0.00V
183 * Power LED - 0.00 V after unplug grows
184
185 Powered on:
186
187 * PWR tail LED - 3.47 V
188 * UID LED - 4.85V
189 * OH/Fan failed LED - 5 V
190 * NIC 1 LED - 1.2 - 2.9 V
191 * NIC 2 LED - 3.2 V
192 * UID SW - 3V
193 * HDD LED - 3 V
194 * Power LED P3V3 - 3.30V
195 * Power LED - 0.87 V
196
197 = Controlling top I/O LED =
198
199 On the top row we have the following input/output devices in order from left to right;
200
201 Left side:
202
203 * physical lock
204 * lock LED
205 * warning/service button
206 * warning/service LED
207 * locate button
208 * power LED (red & green)
209 * fan LED (red & green)
210 * temperature LED (red & green)
211 * compute LED (unknown)
212
213 Right side:
214
215 * power LED (red & green)
216 * fan LED (red & green)
217 * temperature LED (red & green)
218 * compute LED (unknown)
219 * lock switch
220
221 Each sides bank of LEDs are driven by each their PCA9554 shift register. The registers represent the following LEDs: (Note that Lock LED is only present for the LEFT side)
222
223 (% border="1" %)
224 |=(% scope="row" %)Register|1|2|3|4|5|6|7
225 |=Device|Power LED Green|Power LED Red|Fan LED Green|Fan LED Red|Temperature LED Green|Temperature LED Red|Lock LED
226
227 To control each LED we shift either a 0 to turn off or 1 to turn on. Since each device shares a single red/green LED (power LED green & power LED red) setting both to 1 at the same time will always leave it red. That is when power LED green and power LED red are both enabled, red always takes precedence.
228
229 Use following script to power LEDs one at a time:
230
231 {{code language="c++"}}
232 #include <PCA9554.h> // Load the PCA9554 Library
233
234 PCA9554 ioCon1(0x24); // Create an object at this address
235
236 uint8_t mapIO = 0b10000000;
237
238 void shiftL() {
239 mapIO = (mapIO << 1) | ((mapIO & 0x80) >> 7);
240 }
241
242 void write() {
243 Serial.println("writing to PCA9554 device");
244
245 for (int i = 0; i < 8; ++i) {
246 ioCon1.digitalWrite(i, (mapIO & (1 << i)) ? 0 : 1);
247 }
248 }
249
250 void setup()
251 {
252 Serial.begin(9600);
253 Serial.println("Setup");
254
255 ioCon1.portMode(ALLOUTPUT);
256 }
257
258 void loop()
259 {
260 write();
261 shiftL();
262
263 delay(500);
264 }
265 {{/code}}
266
267
268 Controlling middle IO strip
269
270 0 = 0000
271 1 (green) = 0001
272 2 = 0010
273 1 + 2 = 0011
274 3 = 0100
275
276
277 There are 4 words, each containing 7 data bits. They do not
278
279
280 = Controlling center LED columns =
281
282 There are a total of 4 banks of addressable LED's 12 each of the total 48.
283
284
285 |**Address Range (Binary)**|**Address Range (hex)**|**Size**|**Description**
286 |0-7|00-07|1 byte|Ethernet indicator and LEDs bank-1
287 |8-15|08-0F|1 byte|LEDs bank-2
288 |16-23|10-17|1 byte|LEDs bank-3
289 |24-31|18-1F|1 byte|LEDs bank-4
290
291 [[attach:Address Ranges-Table 1.csv||target="_blank"]]
292
293 |=**Address Banks**|=**LEDs Controlled**|=**Count**|=**Address Range (hex)**|=**Description**
294 |=**Bank-1**|1 2 4 6 8 10 12|7|00-07|LED 1 ethernet indicator, even bottom half
295 |=**Bank-2**|3 5 7 9 11 13|6|08-0F|Odd LED top half
296 |=**Bank-3**|14 16 18 20 22 23 24|7|10-17|Even LED bottom half
297 |=**Bank-4**|15 17 19 21|4|18-1F|Odd LED top half
298
299 [[attach:LEDs per bank-Table 1.csv||target="_blank"]]
300
301
302 |=Register Banks|=LEDs|=Count
303 |=Bank 1|1 2 4 6 8 10 12|7
304 |=Bank 2|3 5 7 9 11 13|6
305 |=Bank 3|14 16 18 20 22 23 24|7
306 |=Bank 4|15 17 19 21|4
307
308 Script for writing all permutations to display:
309
310 {{code language="c++"}}
311 #include "Wire.h" // enable I2C bus
312
313 byte saa1064 = 0x3B; // define the I2C bus address for our SAA1064 (pin 1 to GND) ****
314
315 void setup()
316 {
317 Wire.begin(); // start up I2C bus
318 }
319
320 void write(int value) {
321 Wire.beginTransmission(saa1064);
322 Wire.write(1);
323
324 Wire.write(value);
325 Wire.write(value);
326 Wire.write(value);
327 Wire.write(value);
328
329 Wire.endTransmission();
330 }
331
332 void loop() {
333 for (int value = 0; value < 127; value++) {
334 write(value);
335 delay(300);
336 }
337 }
338 {{/code}}
339
340 Since LED positions don't map sequentially with LED number we can't address them in 10-base form, but we can define each LED in binary and use OR operator to display LEDs we want.
341
342 {{code language="c++"}}
343 #include "Wire.h" // enable I2C bus
344
345 #define TCAADDR 0x70
346 byte saa1064 = 0x3B; // define the I2C bus address for our SAA1064
347
348 byte bank1;
349 byte bank2;
350 byte bank3;
351 byte bank4;
352
353 byte activityLED = 0b00000001;
354 byte leds[23][4] = {
355 {0b00000010, 0b00000000, 0b00000000, 0b00000000}, // 1
356 {0b00000000, 0b00000010, 0b00000000, 0b00000000}, // 2
357 {0b00000100, 0b00000000, 0b00000000, 0b00000000}, // 3
358 {0b00000000, 0b00000100, 0b00000000, 0b00000000}, // 4
359 {0b00001000, 0b00000000, 0b00000000, 0b00000000}, // 5
360 {0b00000000, 0b00001000, 0b00000000, 0b00000000}, // 6
361 {0b00010000, 0b00000000, 0b00000000, 0b00000000}, // 7
362 {0b00000000, 0b00010000, 0b00000000, 0b00000000}, // 8
363 {0b00100000, 0b00000000, 0b00000000, 0b00000000}, // 9
364 {0b00000000, 0b00100000, 0b00000000, 0b00000000}, // 10
365 {0b01000000, 0b00000000, 0b00000000, 0b00000000}, // 11
366 {0b00000000, 0b01000000, 0b00000000, 0b00000000}, // 12
367 {0b00000000, 0b00000000, 0b00000001, 0b00000000}, // 13
368 {0b00000000, 0b00000000, 0b00000000, 0b00000001}, // 14
369 {0b00000000, 0b00000000, 0b00000010, 0b00000000}, // 15
370 {0b00000000, 0b00000000, 0b00000000, 0b00000010}, // 16
371 {0b00000000, 0b00000000, 0b00000100, 0b00000000}, // 17
372 {0b00000000, 0b00000000, 0b00000000, 0b00000100}, // 18
373 {0b00000000, 0b00000000, 0b00001000, 0b00000000}, // 19
374 {0b00000000, 0b00000000, 0b00000000, 0b00001000}, // 20
375 {0b00000000, 0b00000000, 0b00010000, 0b00000000}, // 21
376 {0b00000000, 0b00000000, 0b00100000, 0b00000000}, // 22
377 {0b00000000, 0b00000000, 0b01000000, 0b00000000} // 23
378 };
379
380 void setup()
381 {
382 Serial.begin(9600);
383 Wire.begin(); // start up I2C bus
384
385 Serial.println("setting up ports");
386 }
387
388 void tcaselect(uint8_t i) {
389 if (i > 7) return;
390
391 Wire.beginTransmission(TCAADDR);
392 Wire.write(1 << i);
393 Wire.endTransmission();
394 }
395
396 void selectLeft() { tcaselect(2); }
397 void selectRight() { tcaselect(1); }
398
399 void write() {
400 Wire.beginTransmission(saa1064);
401 Wire.write(1);
402
403 Wire.write(bank1);
404 Wire.write(bank2);
405 Wire.write(bank3);
406 Wire.write(bank4);
407
408 Wire.endTransmission();
409 }
410
411 void resetBanks() {
412 bank1 = 0;
413 bank2 = 0;
414 bank3 = 0;
415 bank4 = 0;
416 }
417
418 void displayNumber(int number) {
419 bank1 = leds[number - 1][0];
420 bank2 = leds[number - 1][1];
421 bank3 = leds[number - 1][2];
422 bank4 = leds[number - 1][3];
423 }
424
425 void displayUpToNumber(int number) {
426 for (int i = 0; i < number; i++) {
427 bank1 = bank1 | leds[i][0];
428 bank2 = bank2 | leds[i][1];
429 bank3 = bank3 | leds[i][2];
430 bank4 = bank4 | leds[i][3];
431 }
432 }
433
434 void computeEthernetActivity() {
435 bank1 = bank1 | activityLED;
436 }
437
438 void loop() {
439 resetBanks();
440 delay(10);
441
442 displayUpToNumber(15);
443 computeEthernetActivity();
444
445 selectLeft();
446 write();
447 delay(2);
448
449 selectRight();
450 write();
451 delay(1000);
452 }
453 {{/code}}
454
455
456 = Missing pieces, TODO =
457
458 * how to control compute LED in top IO row
459 * control warning button LED
460
461
462 )))
463
464
465 (% class="col-xs-12 col-sm-4" %)
466 (((
467 {{box title="**Contents**"}}
468 {{toc/}}
469 {{/box}}
470
471 [[image:[email protected]]]
472 //Figure 1: [[Sea>>https://commons.wikimedia.org/wiki/File:Isle_of_Icacos_II.jpg]]//
473
474 [[image:[email protected]]]
475 //Figure 2: [[Waves>>https://commons.wikimedia.org/wiki/File:Culebra_-_Playa_de_Flamenco.jpg]]//
476
477
478
479
480
481
482
483 )))
484 )))