read pins file if they exist
[linux-gpio-pinout] / gpio.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5 use Data::Dump qw(dump);
6 use Getopt::Long;
7
8 my $opt_svg = 0;
9 my $opt_alt = 0;
10 my $opt_invert = 0;
11 my $opt_vertical = 0;
12 my $opt_horizontal = 0;
13 my $opt_edge = 0;
14 my $opt_middle = 0;
15 my $opt_zebra = 0;
16 my $opt_lines = 0;
17 my $opt_read = '';
18 my $opt_pins = '';
19 my $opt_color = 0;
20 GetOptions(
21         'svg!' => \$opt_svg,
22         'alt!' => \$opt_alt,
23         'invert!' => \$opt_invert,
24         'vertical-flip!' => \$opt_vertical,
25         'horizontal-flip!' => \$opt_horizontal,
26         'edge-pins!' => \$opt_edge,
27         'middle-pins!' => \$opt_middle,
28         'zebra!' => \$opt_zebra,
29         'lines!' => \$opt_lines,
30         'read=s' => \$opt_read,
31         'pins=s' => \$opt_pins,
32         'color' => \$opt_color,
33 );
34
35 # svg font hints
36 my $font_w = 1.67; # < 2.54, font is not perfect square
37 my $font_b = 2.10; # font baseline position
38
39 $opt_read .= '/' unless $opt_read =~ m/\/$/;
40
41 sub slurp {
42         open(my $fh, '<', $opt_read . shift);
43         local $/ = undef;
44         <$fh>;
45 }
46
47 my $pins;
48
49 my $model = slurp('/proc/device-tree/model');
50 $model =~ s/\x00$//; # strip kernel NULL
51 warn "# model [$model]";
52
53 OPEN_PINS_AGAIN:
54 open(DATA, '<', $opt_pins) if $opt_pins;
55
56 my @lines;
57 my $line_i = 0;
58
59 my $include = 0;
60 while(<DATA>) {
61         chomp;
62         if ( m/^#\s(.+)/ ) {
63                 warn "MODEL [$1] == [$model] ?\n";
64                 if ( $model =~ m/$1/ ) {
65                         $include = 1;
66                 } else {
67                         $include = 0;
68                 }
69         } elsif ( $include || $opt_pins ) {
70                 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
71
72                 push @lines, $_;
73
74                 $line_i++;
75         } else {
76                 warn "IGNORE: [$_]\n";
77         }
78 }
79
80 if ( ! $opt_pins && ! $pins ) {
81         my $glob = $model;
82         my $glob =~ s/^(\w+).*$/$1/;
83         my @pins = glob "pins/${glob}*";
84         warn "# possible pins: ",dump( \@pins );
85         $opt_pins = $pins[0];
86         goto OPEN_PINS_AGAIN;
87 }
88
89 die "add pin definition for # $model" unless $pins;
90
91 #warn "# lines ",dump( \@lines );
92 warn "# pins ",dump($pins);
93
94 my $serial_tty;
95 foreach (
96         glob($opt_read . '/sys/devices/platform/soc*/*.serial/tty/tty*'),       # 4.x
97         glob(            '/sys/devices/soc.*/*.uart/tty/tty*')                  # 3.10
98 ) {
99         my @v = split(/\//, $_);
100         $serial_tty->{ $v[-3] } = $v[-1];
101 }
102 warn "# serial_tty = ",dump($serial_tty);
103
104
105 my $pin_function;
106 my $device;
107 my $pin;
108 my $function;
109
110 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-maps');
111 while(<$fh>) {
112         chomp;
113         if ( m/^device (\S+)/ ) {
114                 $device = $1;
115                 if ( my $replace = $serial_tty->{$device} ) {
116                         $device = $replace; # replace serial hex with kernel name
117                 } else {
118                         $device =~ s/^[0-9a-f]*\.//; # remove hex address
119                 }
120         } elsif ( m/^group (\w+\d+)/ ) {
121                 $pin = $1;
122
123         } elsif ( m/^function (\S+)/ ) {
124                 $function = $1;
125         } elsif ( m/^$/ ) {
126                 if ( $device && $pin && $function ) {
127                         push @{ $pin_function->{$pin} }, "$device $function";
128
129                         if ( $pins->{$pin} ) {
130                                 foreach my $line ( @{$pins->{$pin}} ) {
131                                         my $t = $lines[$line];
132                                         if ( $opt_svg ) {
133                                                 $t =~ s/$pin/[$device $function]/;
134                                         } else {
135                                                 $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
136                                         }
137                                         $lines[$line] = $t;
138                                         warn "# $line: $lines[$line]\n";
139                                 }
140                         } else {
141                                 warn "IGNORED: pin $pin function $function\n";
142                         }
143
144                 } else {
145                         warn "missing one of ",dump( $device, $pin, $function );
146                 }
147
148                 $device = undef;
149                 $pin = undef;
150                 $function = undef;
151
152         }
153 }
154
155 warn "# pin_function = ",dump($pin_function);
156
157 my @max_len = ( 0,0,0,0 );
158 my @line_parts;
159
160 shift(@lines) while ( ! $lines[0] );    # remove empty at beginning
161 pop(@lines) while ( ! $lines[-1] );     # remove empty at end
162
163 foreach my $line (@lines) {
164         if ( $line =~ m/^#/ ) {
165                 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
166                 next;
167         }
168         $line =~ s/\[(\w+)\s+(\w+)\] \[\1\s+(\w+)\]/[$1 $2 $3]/g; # compress kernel annotation with same prefix
169         $line =~ s/(\[(?:uart\d*|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
170         $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
171         $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
172         $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
173
174         # shorten duplicate kernel device/function
175         $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
176         $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
177
178         $line =~ s/\[(\w+)\s+([^\]]+)\s+\1\]/[$1 $2]/g; # duplicate
179
180         my @v = split(/\s*\t+\s*/,$line,4);
181         @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
182
183         push @line_parts, [ @v ];
184         foreach my $i ( 0 .. 3 ) {
185                 next unless exists $v[$i];
186                 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
187                 my $l = length($v[$i]);
188                 $max_len[$i] = $l if $l > $max_len[$i];
189         }
190 }
191
192 warn "# max_len = ",dump( \@max_len );
193 warn "# line_parts = ",dump( \@line_parts );
194
195 #print "$_\n" foreach @lines;
196
197 my $x = 20.00; # mm
198 my $y = 20.00; # mm
199
200 if ( $opt_svg ) {
201         print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
202 <svg
203    xmlns:dc="http://purl.org/dc/elements/1.1/"
204    xmlns:cc="http://creativecommons.org/ns#"
205    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
206    xmlns:svg="http://www.w3.org/2000/svg"
207    xmlns="http://www.w3.org/2000/svg"
208    xmlns:xlink="http://www.w3.org/1999/xlink"
209    id="svg8"
210    version="1.1"
211    viewBox="0 0 210 297"
212    height="297mm"
213    width="210mm">
214
215
216 <g id="layer1">
217
218         }; # svg, insert rest of rect
219
220         print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
221 }
222
223 my @later;
224
225 my $cols = {    # foreground background
226         txt  => [ '#000000', '#ffffff' ],
227         pins => [ '#ffffff', '#ff00ff' ],
228         vcc  => [ '#ff0000', '#ffff00' ],
229         gnd  => [ '#000000', '#00ffff' ],
230         i2c  => [ '#008800', '#ffcccc' ],
231         serial=>[ '#000088', '#ccffcc' ],
232         spi  => [ '#880000', '#ccccff' ],
233 };
234
235 sub swap_cols {
236         my $swap = shift;
237         die "$swap not found in ",dump($cols) unless $cols->{$swap};
238         my ( $c1, $c2 ) = @{ $cols->{$swap} };
239         $cols->{$swap} = [ $c2, $c1 ];
240 }
241
242 swap_cols 'txt' if $opt_invert;
243         
244
245 sub svg_style {
246         my ($name,$x,$y,$col) = @_;
247
248         return '' unless $opt_color;
249
250         $y -= $font_b; # shift box overlay to right vertical position based on font baseline
251
252         sub rect {
253                 my ($x,$y,$col,$fill) = @_;
254                 print qq{<rect x="$x" y="$y" height="2.54" width="}, $max_len[$col] * $font_w, qq{" style="fill:$fill;stroke:#ffffff;stroke-width:0.10" />\n};
255
256         }
257
258         if ( $name =~ m/^(\d+)$/ ) { # pins
259                 my $pin = $1;
260                 my ( $fg, $bg ) = @{ $cols->{pins} };
261                 if ( $pin == 1 ) {
262                         my $w  = $max_len[$col]*$font_w - 0.1;
263                         my $cx = $x + $w;
264                         my $cy = $y + 2.54;
265                         #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
266                         #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
267                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
268                         my ( $fg, $bg ) = @{ $cols->{txt} };
269                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
270                 } else {
271                         rect $x,$y,$col,$fg;
272                 }
273                 return qq{ style="fill:$bg"};
274         }
275
276         if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
277                 my ($fg,$bg) = @{ $cols->{vcc} };
278                 rect $x,$y,$col,$bg;
279                 return qq{ style="fill:$fg"};
280         } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
281                 my ($fg,$bg) = @{ $cols->{gnd} };
282                 rect $x,$y,$col,$bg;
283                 return qq{ style="fill:$fg"};
284         } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
285                 my $dev = $1;
286                 my ($fg,$bg) = @{ $cols->{txt} };
287                 $dev = 'serial' if $dev =~ m/^tty/;
288                 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
289                 rect $x,$y,$col,$bg;
290                 return qq{ style="fill:$fg"};
291         } else {
292                 my ( $fg, $bg ) = @{ $cols->{txt} };
293                 rect $x,$y,$col,$bg;
294                 #return qq{ style="fill:$fg"};
295                 return '';
296         }
297 }
298
299 my $alt_col = 0;
300
301 my @cols_order = ( 0,1,2,3 );
302 my @cols_align = ( '','-','','-' ); # sprintf prefix
303
304 my @cols_shuffle = @cols_order;
305
306 if ( $opt_edge ) {
307         # pins outside on the right
308         @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
309         @cols_align = ( '-','-','','' );
310 } elsif ( $opt_middle ) {
311         # pins in middle
312         @cols_shuffle = ( 1,0,2,3 );
313         @cols_align = ( '','','-','-' );
314 }
315
316 sub cols_shuffle {
317         my ( $what, $order ) = @_;
318         my $new = [];
319         foreach my $i ( 0 .. $#$what ) {
320                 $new->[$i] = $what->[ $order->[$i] ];
321         }
322         warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
323         return @$new;
324 }
325
326 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
327 @max_len    = cols_shuffle( \@max_len,    \@cols_shuffle );
328
329 warn "# cols_order = ",dump( \@cols_order );
330 warn "# cols_align = ",dump( \@cols_align );
331
332 my $fmt = "%$cols_align[0]$max_len[0]s %$cols_align[1]$max_len[1]s %$cols_align[2]$max_len[2]s %$cols_align[3]$max_len[3]s\n";
333
334
335 # cut marks
336 my ($fg,$bg) = @{ $cols->{txt} };
337 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
338
339 my @cut_marks;
340 sub cut_mark {
341         my ($x,$y) = @_;
342         return unless $opt_svg;
343         push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b,   $x+5, $y-$font_b);
344         push @cut_marks, sprintf($line_fmt, $x,   $y-$font_b-5, $x,   $y-$font_b+5);
345 }
346 #cut_mark $x, $y;
347 my $max_x = $x;
348 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
349 #cut_mark $max_x, $y;
350
351 sub line {
352         my ($x,$y,$max_x) = @_;
353         push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
354 }
355
356
357 my $last_cut_mark = 0;
358
359 foreach my $i ( 0 .. $#line_parts ) {
360         $i = $#line_parts - $i if $opt_vertical;
361         my $line = $line_parts[$i];
362
363         if ( $opt_svg ) {
364
365                 # not a minimal two column pin description
366                 if ( ! exists $line->[1] ) {
367                         $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
368
369                         # before first empty line
370                         if ( $last_cut_mark == 0 ) {
371                                 cut_mark $x, $y;
372                                 cut_mark $max_x, $y;
373                                 $last_cut_mark = 1;
374                                 line $x, $y, $max_x if $opt_lines;
375                                 $y += 15; # make spacing between pinouts
376                         }
377                 } elsif ( $last_cut_mark ) {
378                         # first full line
379                         cut_mark $x, $y;
380                         cut_mark $max_x, $y;
381                         $last_cut_mark = 0;
382                 } else {
383                         #warn "CUTMARK no magic";
384                 }
385
386                 line $x, $y, $max_x if $opt_lines && exists $line->[1];
387
388                 my ($fg,$bg) = @{ $cols->{txt} };
389                 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
390
391                 my $x_pos = $x;
392                 foreach my $i ( 0 .. $#cols_order ) {
393                         my $order = $cols_order[$i];
394                         next unless $line->[$order];
395
396                         my $text_anchor = 'middle';
397                         my $len = $max_len[$i];
398                         my $x2 = $x_pos + ( $len * $font_w ) / 2;
399                         # is this comment?
400                         if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
401                                 # comment, center over whole width
402                                 $len = length($line->[$order]);
403                                 $x2 = $x + (($max_x-$x)/2); # middle
404                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
405                         } else {
406                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$order],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[$order]);
407                         }
408                         $x_pos += $len * $font_w;
409                 }
410
411                 $tspan .= qq{\n</tspan>\n};
412                 push @later,sprintf $tspan, @$line;
413                 $y += 2.54;
414
415                 # swap pin colors for line stripe
416                 if ( $opt_zebra ) {
417                         swap_cols $_ foreach qw( pins txt );
418                 } else {
419                         swap_cols 'pins';
420                 }
421
422         } else {
423
424                 if ( $#$line == 0 ) {
425                         print $line->[0], "\n";
426                 } else {
427                         push @$line, '' while ($#$line < 3); # fill-in single row header
428                         printf $fmt, map { $line->[$_] } @cols_order;
429                 }
430
431         }
432 }
433
434 if ( $opt_svg ) {
435         cut_mark $x,$y;
436         cut_mark $max_x,$y;
437         line $x, $y, $max_x if $opt_lines;
438
439         print qq{
440     <text
441        id="text4506"
442        y="$x"
443        x="$y"
444        style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
445        xml:space="preserve">
446
447         }; #svg
448
449         print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
450
451 }
452
453 __DATA__
454 # Cubietech Cubieboard
455 ## U14 (Next to SATA connector)
456 ###     SPI0
457 48      PI13 (SPI0-MISO/UART6-RX/EINT25)        47      PI11 (SPI0-CLK/UART5-RX/EINT23)
458 46      PI12 (SPI0-MOSI/UART6-TX/EINT24)        45      PI10 (SPI0-CS/UART5-TX/EINT22)
459 ###     LCD
460 44      3.3V (nc in 2012-08-08)                 43      VCC-5V
461 42      Ground                                  41      SPDIF
462 40      PB10 (LCD0-SCK/LCD-PIO1)                39      PB11 (LCD0-SDA/LCD-PIO2)
463 38      Ground                                  37      PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
464 36      XN_TP (TP-X2)                           35      YN_TP (TP-Y2)
465 34      XP_TP (TP-X1)                           33      YP_TP (TP-Y1)
466 32      PD25 (LCDDE)                            31      PB2 (PWM0)
467 30      PD26 (LCDHSYNC/VGA-HSYNC)               29      PD24 (LCDCLK)
468 28      PD23 (LCDD23)                           27      PD27 (LCDVSYNC/VGA-VSYNC)
469 26      PD21 (LCDD21)                           25      PD22 (LCDD22)
470 24      PD19 (LCDD19/LVDS1N3)                   23      PD20 (LCDD20)
471 22      PD17 (LCDD17/LVDS1NC)                   21      PD18 (LCDD18/LVDS1P3)
472 20      Ground                                  19      PD16 (LCDD16/LVDS1PC)
473 18      PD14 (LCDD14/LVDS1P2)                   17      PD15 (LCDD15/LVDS1N2)
474 16      PD12 (LCDD12/LVDS1P1)                   15      PD13 (LCDD13/LVDS1N1)
475 14      PD10 (LCDD10/LVDS1P0)                   13      PD11 (LCDD11/LVDS1N0)
476 12      PD8 (LCDD8/LVDS0P3)                     11      PD9 (LCDD9/LVDS0N3)
477 10      PD7 (LCDD7/LVDS0NC)                     9       Ground
478 8       PD5 (LCDD5/LVDS0N2)                     7       PD6 (LCDD6/LVDS0PC)
479 6       PD3 (LCDD3/LVDS0N1)                     5       PD4 (LCDD4/LNVS0P2)
480 4       PD1 (LCDD1/LVDS0N0)                     3       PD2 (LCDD2/LVDS0P1)
481 2       Ground                                  1       PD0 (LCDD0/LVDSP0)
482
483 ## U15 (Between Ethernet port and USB ports)
484 ### CSI1/TS
485 1       VCC-5V                                  2       PH15 (CSI1-PWR/EINT15)
486 3       CSI1-IO-2V8                             4       PH14 (CSI1-RST#/EINT14)
487 5       PG0 (CSI1-PCLK/SDC1-CMD)                6       PB18 (TWI1-SCK)
488 7       PB19 (TWI1-SDA)                         8       PG3 (CSI1-VSYNC/SDC1-D1)
489 9       PG2 (CSI1-HSYNC/SDC1-D0)                10      PG1 (CSI1-MCLK/SDC1-CLK)
490 11      PG4 (CSI1-D0/SDC1-D2)                   12      PG5 (CSI1-D1/SDC1-D3)
491 13      PG6 (CSI1-D2/UART3-TX)                  14      PG7 (CSI1-D3/UART3-RX)
492 15      PG8 (CSI1-D4/UART3-RTS)                 16      PG9 (CSI1-D5/UART3-CTS)
493 17      PG10 (CSI1-D6/UART4-TX)                 18      PG11 (CSI1-D7/UART4-RX)
494 19      Ground                                  20      Ground
495 ###     Analog SDIO3
496 21      FMINL                                   22      PI4 (SDC3-CMD)
497 23      FMINR                                   24      PI5 (SDC3-CLK)
498 25      Ground                                  26      PI6 (SDC3-D0)
499 27      VGA-R                                   28      PI7 (SDC3-D1)
500 29      VGA-G                                   30      PI8 (SDC3-D2)
501 31      VGA-B                                   32      PI9 (SDC3-D3)
502 ###     CSI0/TS
503 33      LCD1-VSYNC                              34      PE4 (CSI0-D0)
504 35      LCD1-HSYNC                              36      PE5 (CSI0-D1)
505 37      Ground                                  38      PE6 (CSI0-D2)
506 39      AVCC                                    40      PE7 (CSI0-D3)
507 41      LRADC0                                  42      PE8 (CSI0-D4)
508 43      CVBS                                    44      PE9 (CSI0-D5)
509 45      HPL                                     46      PE10 (CSI0-D6)
510 47      HPR                                     48      PE11 (CSI0-D7)
511
512 ## DEBUG serial (middle of board)
513 4       PB22 (UART0-TX)
514 3       PB23 (UART0-RX)
515 2       VCC-3V3
516 1       GND
517
518
519 # Lamobo R1
520 ## CON3 rpi DIP26-254
521 1       3.3v                    2       5v     
522 3       PB20 SDA.1              4       5V     
523 5       PB21 SCL.1              6       0v     
524 7       PI3 PWM1                8       PH0 UART3_TX
525 9       0v                      10      PH1 UART3_RX
526 11      PI19 UART2_RX           12      PH2
527 13      PI18 UART2_TX           14      0v     
528 15      PI17 UART2_CTS          16      PH21 CAN_RX 
529 17      3.3v                    18      PH20 CAN_TX 
530 19      PI12 SPI0_MOSI          20      0v     
531 21      PI13 SPI0_MISO          22      PI16 UART2_RTS   
532 23      PI11 SPI0_SCLK          24      PI10 SPI0_CS0    
533 25      0v                      26      PI14 SPI0_CS1
534
535 ## J13 DIP2-254
536 2       PB22 UART0_TX
537 1       PB23 UART0_RX
538
539 ## J12 DIP8-254
540 8       GND                     7       GND
541 6       PI20 UART7_TX           5       PH3
542 4       PI21 UART7_RX           3       PH5
543 2       3V3                     1       SATA-5V
544
545 # Raspberry Pi
546 1       3.3v                    2       5v
547 3       gpio2 (SDA.1)           4       5v
548 5       gpio3 (SCL.1)           6       0v
549 7       gpio4 (WPi 7)           8       gpio14  (TxD)
550 9       0v                      10      gpio15  (RxD)
551 11      gpio17 (WPi 0)          12      gpio18  (WPi 1)
552 13      gpio27 (WPi 2)          14      0v
553 15      gpio22 (WPi 3)          16      gpio23  (WPi 4)
554 17      3.3v                    18      gpio24  (WPi 5)
555 19      gpio10 (MOSI)           20      0v
556 21      gpio9 (MISO)            22      gpio25  (WPi 6) 
557 23      gpio11 (SCLK)           24      gpio8   (CE0)
558 25      0v                      26      gpio7   (CE1)
559 # Raspberry Pi 3 Model B Rev 1.2
560 27      gpio0 (SDA.0)           28      gpio1   (SCL.0)
561 29      gpio5 (WPi 21)          30      0v
562 31      gpio6 (WPi 22)          32      gpio12  (WPi 26)
563 33      gpio13 (WPi 23)         34      0v
564 35      gpio19 (WPi 24)         36      gpio16  (WPi 27)
565 37      gpio26 (WPi 25)         38      gpio20  (WPi 28)
566 39      0v                      40      gpio21  (WPi 29)
567