| #!/usr/bin/env python3 |
| |
| # Copyright 2018-2020 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import argparse |
| import os |
| |
| class Style: |
| BOLD = 1 |
| DEFAULT = 39 |
| RED = 31 |
| GREEN = 32 |
| YELLOW = 33 |
| BLUE = 34 |
| MAGENTA = 35 |
| CYAN = 36 |
| DARK_GRAY = 90 |
| |
| class Kind: |
| GND, POWER, I2C, UART, GPIO, SAI, SPI, PWM = range(8) |
| |
| KINDS = { |
| Kind.GND: (Style.DARK_GRAY, 'GND', 'Ground'), |
| Kind.POWER: (Style.RED, 'POWER', 'Power: +5V or +3.3V'), |
| Kind.I2C: (Style.GREEN, 'I2C', 'Inter-Integrated Circuit [/dev/i2c-N]'), |
| Kind.UART: (Style.MAGENTA, 'UART', 'Serial Port [/dev/ttySN]'), |
| Kind.GPIO: (Style.DEFAULT, 'GPIO', 'General Purpose Input Output [/sys/class/gpio/gpioN]'), |
| Kind.SAI: (Style.CYAN, 'SAI', 'Synchronous Audio Interface'), |
| Kind.SPI: (Style.BLUE, 'SPI', 'Serial Peripheral Interface [/dev/spidevN.N]'), |
| Kind.PWM: (Style.YELLOW, 'PWM', 'Pulse Width Modulation [/sys/class/pwm/pwmchipN/pwm0]'), |
| } |
| |
| I2C_DEVICES = { |
| 'I2C1': '1100a000.i2c', |
| 'I2C2': '1100b000.i2c', |
| } |
| |
| def lookup_i2c_bus(device_tree_name): |
| sysfs_directory = '/sys/devices/platform/soc/{}'.format(device_tree_name) |
| return [x for x in os.listdir(sysfs_directory) if x.startswith('i2c-')][0] |
| |
| PINS = { |
| 1: ('3.3.V', Kind.POWER), 2: ('5V', Kind.POWER), |
| 3: ('I2C1_SDA ({})'.format(lookup_i2c_bus(I2C_DEVICES['I2C1'])), Kind.I2C), |
| 4: ('5V', Kind.POWER), |
| 5: ('I2C1_SCL ({})'.format(lookup_i2c_bus(I2C_DEVICES['I2C1'])), Kind.I2C), |
| 6: ('GND', Kind.GND), |
| 7: ('GPIO (gpio409)', Kind.GPIO), 8: ('UART0_TX (ttyS0)', Kind.UART), |
| 9: ('GND', Kind.GND), 10: ('UART0_RX (ttyS0)', Kind.UART), |
| 11: ('GPIO (gpio396)', Kind.GPIO), 12: ('GPIO (gpio423) / I2S_BCK', Kind.SAI), |
| 13: ('GPIO (gpio397)', Kind.GPIO), 14: ('GND', Kind.GND), |
| 15: ('PWM (pwmchip0.2)', Kind.PWM), 16: ('GPIO (gpio387)', Kind.GPIO), |
| 17: ('3.3V', Kind.POWER), 18: ('GPIO (gpio399)', Kind.GPIO), |
| 19: ('SPI_MOSI (spidev0)', Kind.SPI), 20: ('GND', Kind.GND), |
| 21: ('SPI_MISO (spidev0)', Kind.SPI), 22: ('GPIO (gpio394)', Kind.GPIO), |
| 23: ('SPI_SCLK (spidev0)', Kind.SPI), 24: ('SPI_CSB (spidev0.0)', Kind.SPI), |
| 25: ('GND', Kind.GND), 26: ('GPIO (gpio395)', Kind.GPIO), |
| 27: ('I2C2_SDA ({})'.format(lookup_i2c_bus(I2C_DEVICES['I2C2'])), Kind.I2C), |
| 28: ('I2C2_SCL ({})'.format(lookup_i2c_bus(I2C_DEVICES['I2C2'])), Kind.I2C), |
| 29: ('UART1_TX (ttyS1)', Kind.UART), 30: ('GND', Kind.GND), |
| 31: ('UART1_RX (ttyS1)', Kind.UART), 32: ('PWM (pwmchip0.0)', Kind.PWM), |
| 33: ('PWM (pwmchip0.1)', Kind.PWM), 34: ('GND', Kind.GND), |
| 35: ('GPIO (gpio424) / I2S_LRCK', Kind.SAI), 36: ('GPIO (gpio400)', Kind.GPIO), |
| 37: ('GPIO (gpio432)', Kind.GPIO), 38: ('GPIO (gpio425) / I2S_DO', Kind.SAI), |
| 39: ('GND', Kind.GND), 40: ('GPIO (gpio426) / I2S_MCK', Kind.SAI) |
| } |
| |
| assert(len(PINS) % 2 == 0) |
| assert(set(PINS.keys()) == set(range(1, len(PINS) + 1))) |
| |
| def pins(): |
| for i in range(len(PINS) // 2): |
| yield (2 * i + 1, 2 * i + 2) |
| |
| def pin_desc(pin): |
| text, _ = PINS[pin] |
| return text |
| |
| def pin_kind(pin): |
| _, kind = PINS[pin] |
| return kind |
| |
| def stylize(text, style, esc='\033['): |
| return '%s%dm%s%s0m' % (esc, style, text, esc) |
| |
| def stylize_pin(text, pin): |
| style, _, _ = KINDS[pin_kind(pin)] |
| return stylize(text, style) |
| |
| def print_pinout(color): |
| max_len = max(len(pin_desc(l)) for l, _ in pins()) |
| |
| for l, r in pins(): |
| l_pin, r_pin = str(l).ljust(2), str(r).rjust(2) |
| l_txt, r_txt = pin_desc(l).rjust(max_len), pin_desc(r) |
| |
| if color: |
| l_pin = stylize(l_pin, Style.BOLD) |
| r_pin = stylize(r_pin, Style.BOLD) |
| l_txt, r_txt = stylize_pin(l_txt, l), stylize_pin(r_txt, r) |
| |
| print('%s -> %s %s <- %s' % (l_txt, l_pin, r_pin, r_txt)) |
| |
| def print_legend(color): |
| max_len = max(len(name) for _, (_, name, _) in KINDS.items()) |
| |
| for kind, (style, name, desc) in KINDS.items(): |
| txt = name.ljust(max_len) |
| if color: |
| txt = stylize(txt, style) |
| print('%s - %s' % (txt, desc)) |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--nocolor', dest='color', action='store_false', |
| default=True, help='Do not output in color.') |
| parser.add_argument('--nolegend', dest='legend', action='store_false', |
| default=True, help='Do not output legend.') |
| args = parser.parse_args() |
| |
| print_pinout(args.color) |
| if args.legend: |
| print() |
| print_legend(args.color) |
| |
| if __name__ == '__main__': |
| main() |