Wiki source code of Front I/O

Last modified by Kevin Wiki on 2024/07/07 22:48

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