Merge pull request #431 from waveform80/pinout

Fix #354
This commit is contained in:
Dave Jones
2016-09-13 18:48:11 +01:00
committed by GitHub
4 changed files with 742 additions and 206 deletions

View File

@@ -134,5 +134,7 @@ functions and classes can be used to query this database:
.. autoclass:: PiBoardInfo
.. autoclass:: HeaderInfo
.. autoclass:: PinInfo

View File

@@ -11,6 +11,7 @@ from .pins import (
)
from .pins.data import (
PiBoardInfo,
HeaderInfo,
PinInfo,
pi_info,
)

View File

@@ -7,6 +7,11 @@ from __future__ import (
str = type('')
import io
import os
import sys
from textwrap import dedent
from itertools import cycle
from operator import attrgetter
from collections import namedtuple
from ..exc import PinUnknownPi, PinMultiplePins, PinNoPins
@@ -66,6 +71,110 @@ GPIO43 = 'GPIO43'
GPIO44 = 'GPIO44'
GPIO45 = 'GPIO45'
# Board layout ASCII art
REV1_BOARD = """\
{style:white on green}+------------------{style:black on white}| |{style:white on green}--{style:on cyan}| |{style:on green}------+{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 {style:black on yellow}|C|{style:white on green} {style:on cyan}|A|{style:on green} |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on yellow}+-+{style:white on green} {style:on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|SoC|{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|D|{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:bold}{model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|I|{style:on green} {style:on black}|C|{style:black on white}+======{style:reset}
{style:white on green}| {style:on black}|S|{style:black on white}| Net{style:reset}
{style:white on green}| {style:on black}|I|{style:black on white}+======{style:reset}
{style:black on white}=pwr{style:on green} {style:on white}|HDMI|{style:white on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}----------+{style:reset}"""
REV2_BOARD = """\
{style:white on green}+------------------{style:black on white}| |{style:white on green}--{style:on cyan}| |{style:on green}------+{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 {style:black on yellow}|C|{style:white on green} {style:on cyan}|A|{style:on green} |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on yellow}+-+{style:white on green} {style:on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| {P5:{style} col1}{style:white on green} |{style:reset}
{style:white on green}| P5 {P5:{style} col2}{style:white on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|SoC|{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|D|{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:bold}{model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|I|{style:on green} {style:on black}|C|{style:black on white}+======{style:reset}
{style:white on green}| {style:on black}|S|{style:black on white}| Net{style:reset}
{style:white on green}| {style:on black}|I|{style:black on white}+======{style:reset}
{style:black on white}=pwr{style:on green} {style:on white}|HDMI|{style:white on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}----------+{style:reset}"""
A_BOARD = """\
{style:white on green}+------------------{style:black on white}| |{style:white on green}--{style:on cyan}| |{style:on green}------+{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 {style:black on yellow}|C|{style:white on green} {style:on cyan}|A|{style:on green} |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on yellow}+-+{style:white on green} {style:on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| {P5:{style} col1}{style:white on green} |{style:reset}
{style:white on green}| P5 {P5:{style} col2}{style:white on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|SoC|{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|D|{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:bold}{model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|I|{style:on green} {style:on black}|C|{style:on green} |{style:reset}
{style:white on green}| {style:on black}|S|{style:on green} |{style:reset}
{style:white on green}| {style:on black}|I|{style:on green} |{style:reset}
{style:black on white}=pwr{style:on green} {style:on white}|HDMI|{style:white on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}----------+{style:reset}"""
BPLUS_BOARD = """\
{style:white on green},--------------------------------.{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 {style:black on white}+===={style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:black on white}+===={style:reset}
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|D|{style:on green} {style:on black}|SoC |{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:on black}| |{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|I|{style:on green} {style:on black}+----+{style:on green} |{style:reset}
{style:white on green}| {style:on black}|C|{style:on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:black on white}| Net{style:reset}
{style:white on green}| {style:black on white}pwr{style:white on green} {style:black on white}|HDMI|{style:white on green} {style:on black}|I||A|{style:on green} {style:black on white}+======{style:reset}
{style:white on green}`-{style:black on white}| |{style:white on green}--------{style:black on white}| |{style:white on green}----{style:on black}|V|{style:on green}-------'{style:reset}"""
APLUS_BOARD = """\
{style:white on green},--------------------------.{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|D|{style:on green} {style:on black}|SoC |{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|S|{style:on green} {style:on black}| |{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|I|{style:on green} {style:on black}+----+{style:on green} |{style:reset}
{style:white on green}| {style:on black}|C|{style:on green} |{style:reset}
{style:white on green}| {style:on black}|S|{style:on green} |{style:reset}
{style:white on green}| {style:black on white}pwr{style:white on green} {style:black on white}|HDMI|{style:white on green} {style:on black}|I||A|{style:on green} |{style:reset}
{style:white on green}`-{style:black on white}| |{style:white on green}--------{style:black on white}| |{style:white on green}----{style:on black}|V|{style:on green}-'{style:reset}"""
ZERO12_BOARD = """\
{style:white on green},-------------------------.{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} |{style:reset}
{style:black on white}---+{style:white on green} {style:on black}+----+{style:on green} {style:bold}PiZero{style:normal} |{style:reset}
{style:black on white} sd|{style:white on green} {style:on black}|SoC |{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} |{style:reset}
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+----+{style:on green} {style:black on white}usb{style:on green} {style:black on white}pwr{style:white on green} |{style:reset}
{style:white on green}`---{style:black on white}| |{style:white on green}--------{style:black on white}| |{style:white on green}-{style:black on white}| |{style:white on green}-'{style:reset}"""
ZERO13_BOARD = """\
{style:white on green}.-------------------------.{style:reset}
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on white}|c{style:reset}
{style:black on white}---+{style:white on green} {style:on black}+----+{style:on green} {style:bold}PiZero{style:normal} {style:black on white}|s{style:reset}
{style:black on white} sd|{style:white on green} {style:on black}|SoC |{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} {style:black on white}|i{style:reset}
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+----+{style:on green} {style:black on white}usb{style:on green} {style:on white}pwr{style:white on green} |{style:reset}
{style:white on green}`---{style:black on white}| |{style:white on green}--------{style:black on white}| |{style:white on green}-{style:black on white}| |{style:white on green}-'{style:reset}"""
CM_BOARD = """\
{style:white on green}+-----------------------------------------------------------------------------------------------------------------------+{style:reset}
{style:white on green}| Raspberry Pi Compute Module |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| You were expecting more detail? Sorry, the Compute Module's a bit hard to do right now! |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}||||||||||||||||||||-||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||{style:reset}"""
# Pin maps for various board revisions and headers
REV1_P1 = {
@@ -242,37 +351,113 @@ CM_SODIMM = {
# https://git.drogon.net/?p=wiringPi;a=blob;f=wiringPi/wiringPi.c#l807
PI_REVISIONS = {
# rev model pcb_rev released soc manufacturer ram storage usb eth wifi bt csi dsi headers
0x2: ('B', '1.0', '2012Q1', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
0x3: ('B', '1.0', '2012Q3', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
0x4: ('B', '2.0', '2012Q3', 'BCM2835', 'Sony', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x5: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x6: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x7: ('A', '2.0', '2013Q1', 'BCM2835', 'Egoman', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x8: ('A', '2.0', '2013Q1', 'BCM2835', 'Sony', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x9: ('A', '2.0', '2013Q1', 'BCM2835', 'Qisda', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
0x11: ('CM', '1.2', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
0x12: ('A+', '1.2', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
0x14: ('CM', '1.1', '2014Q2', 'BCM2835', 'Embest', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
0x15: ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
0xa01041: ('2B', '1.1', '2015Q1', 'BCM2836', 'Sony', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
0xa21041: ('2B', '1.1', '2015Q1', 'BCM2836', 'Embest', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
0x900092: ('Zero', '1.2', '2015Q4', 'BCM2835', 'Sony', 512, 'MicroSD', 1, 0, False, False, 0, 0, {'P1': PLUS_P1}, ),
0xa02082: ('3B', '1.2', '2016Q1', 'BCM2837', 'Sony', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
0xa22082: ('3B', '1.2', '2016Q1', 'BCM2837', 'Embest', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
0x900093: ('Zero', '1.3', '2016Q2', 'BCM2835', 'Sony', 512, 'MicroSD', 1, 0, False, False, 1, 0, {'P1': PLUS_P1}, ),
# rev model pcb_rev released soc manufacturer ram storage usb eth wifi bt csi dsi headers board
0x2: ('B', '1.0', '2012Q1', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, REV1_BOARD, ),
0x3: ('B', '1.0', '2012Q3', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, REV1_BOARD, ),
0x4: ('B', '2.0', '2012Q3', 'BCM2835', 'Sony', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0x5: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0x6: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0x7: ('A', '2.0', '2013Q1', 'BCM2835', 'Egoman', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, A_BOARD, ),
0x8: ('A', '2.0', '2013Q1', 'BCM2835', 'Sony', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, A_BOARD, ),
0x9: ('A', '2.0', '2013Q1', 'BCM2835', 'Qisda', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, A_BOARD, ),
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, BPLUS_BOARD, ),
0x11: ('CM', '1.1', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
0x12: ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, APLUS_BOARD, ),
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, BPLUS_BOARD, ),
0x14: ('CM', '1.1', '2014Q2', 'BCM2835', 'Embest', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
0x15: ('A+', '1.1', '2014Q4', 'BCM2835', 'Embest', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, APLUS_BOARD, ),
}
# ANSI color codes, for the pretty printers (nothing comprehensive, just enough
# for our purposes)
class Style(object):
def __init__(self, color=None):
self.color = self._term_supports_color() if color is None else bool(color)
self.effects = {
'reset': 0,
'bold': 1,
'normal': 22,
}
self.colors = {
'black': 0,
'red': 1,
'green': 2,
'yellow': 3,
'blue': 4,
'magenta': 5,
'cyan': 6,
'white': 7,
'default': 9,
}
@staticmethod
def _term_supports_color():
try:
stdout_fd = sys.stdout.fileno()
except IOError:
return False
else:
is_a_tty = os.isatty(stdout_fd)
is_windows = sys.platform.startswith('win')
return is_a_tty and not is_windows
@classmethod
def from_style_content(cls, format_spec):
specs = set(format_spec.split())
style = specs & {'mono', 'color'}
content = specs - style
if len(style) > 1:
raise ValueError('cannot specify both mono and color styles')
try:
style = style.pop()
except KeyError:
style = 'color' if cls._term_supports_color() else 'mono'
if len(content) > 1:
raise ValueError('cannot specify more than one content element')
content = content.pop()
return cls(style == 'color'), content
def __call__(self, format_spec):
specs = format_spec.split()
codes = []
fore = True
for spec in specs:
if spec == 'on':
fore = False
else:
try:
codes.append(self.effects[spec])
except KeyError:
try:
if fore:
codes.append(30 + self.colors[spec])
else:
codes.append(40 + self.colors[spec])
except KeyError:
raise ValueError('invalid format specification "%s"' % spec)
if self.color:
return '\x1b[%sm' % (';'.join(str(code) for code in codes))
else:
return ''
def __format__(self, format_spec):
if format_spec == '':
return 'color' if self.color else 'mono'
else:
return self(format_spec)
class PinInfo(namedtuple('PinInfo', (
'number',
'function',
'pull_up',
'row',
'col',
))):
"""
This class is a :func:`~collections.namedtuple` derivative used to
@@ -298,7 +483,162 @@ class PinInfo(namedtuple('PinInfo', (
are *usually* ``True``). This is used internally by gpiozero to raise
errors when pull-down is requested on a pin with a physical pull-up
resistor.
.. attribute:: row
An integer indicating on which row the pin is physically located in
the header (1-based)
.. attribute:: col
An integer indicating in which column the pin is physically located
in the header (1-based)
"""
__slots__ = () # workaround python issue #24931
class HeaderInfo(namedtuple('HeaderInfo', (
'name',
'rows',
'columns',
'pins',
))):
"""
This class is a :func:`~collections.namedtuple` derivative used to
represent information about a pin header on a board. The object can be used
in a format string with various custom specifications::
from gpiozero import *
print('{0:full}'.format(pi_info().headers['P1']))
print('{0:col2}'.format(pi_info().headers['P1']))
print('{0:row1}'.format(pi_info().headers['P1']))
`'color'` and `'mono'` can be prefixed to format specifications to force
the use of `ANSI color codes`_. If neither is specified, ANSI codes will
only be used if stdout is detected to be a tty::
print('{0:color row2}'.format(pi_info().headers['P1'])) # force use of ANSI codes
print('{0:mono row2}'.format(pi_info().headers['P1'])) # force plain ASCII
.. _ANSI color codes: https://en.wikipedia.org/wiki/ANSI_escape_code
The following attributes are defined:
.. automethod:: pprint
.. attribute:: name
The name of the header, typically as it appears silk-screened on the
board (e.g. "P1").
.. attribute:: rows
The number of rows on the header.
.. attribute:: columns
The number of columns on the header.
.. attribute:: pins
A dictionary mapping physical pin numbers to :class:`PinInfo` tuples.
"""
__slots__ = () # workaround python issue #24931
def _func_style(self, function, style):
if function == V5:
return style('bold red')
elif function in (V3_3, V1_8):
return style('bold cyan')
elif function in (GND, NC):
return style('bold black')
elif function.startswith('GPIO') and function[4:].isdigit():
return style('bold green')
else:
return style('yellow')
def _format_full(self, style):
Cell = namedtuple('Cell', ('content', 'align', 'style'))
pin_digits = len(str(self.rows * self.columns))
lines = []
for row in range(self.rows):
line = []
for col in range(self.columns):
pin = (row * self.columns) + col + 1
try:
pin = self.pins[pin]
cells = [
Cell(pin.function, '><'[col % 2], self._func_style(pin.function, style)),
Cell('(%d)' % pin.number, '><'[col % 2], ''),
]
if col % 2:
cells = reversed(cells)
line.extend(cells)
except KeyError:
line.append(Cell('', '<', ''))
lines.append(line)
cols = list(zip(*lines))
col_lens = [max(len(cell.content) for cell in col) for col in cols]
lines = [
' '.join(
'{cell.style}{cell.content:{cell.align}{width}s}{style:reset}'.format(
cell=cell, width=width, style=style)
for cell, width, align in zip(line, col_lens, cycle('><')))
for line in lines
]
return '\n'.join(lines)
def _format_pin(self, pin, style):
return ''.join((
style('on black'),
(
' ' if pin is None else
self._func_style(pin.function, style) +
('1' if pin.number == 1 else 'o')
),
style('reset')
))
def _format_row(self, row, style):
if row > self.rows:
raise ValueError('invalid row %d for header %s' % (row, self.name))
start_pin = (row - 1) * self.columns + 1
return ''.join(
self._format_pin(pin, style)
for n in range(start_pin, start_pin + self.columns)
for pin in (self.pins.get(n),)
)
def _format_col(self, col, style):
if col > self.columns:
raise ValueError('invalid col %d for header %s' % (col, self.name))
return ''.join(
self._format_pin(pin, style)
for n in range(col, self.rows * self.columns + 1, self.columns)
for pin in (self.pins.get(n),)
)
def __format__(self, format_spec):
style, content = Style.from_style_content(format_spec)
if content == 'full':
return self._format_full(style)
elif content.startswith('row') and content[3:].isdigit():
return self._format_row(int(content[3:]), style)
elif content.startswith('col') and content[3:].isdigit():
return self._format_col(int(content[3:]), style)
def pprint(self, color=None):
"""
Pretty-print a diagram of the header pins.
If *color* is ``None`` (the default, the diagram will include ANSI
color codes if stdout is a color-capable terminal). Otherwise *color*
can be set to ``True`` or ``False`` to force color or monochrome
output.
"""
print('{0:{style} full}'.format(self, style=Style(color)))
class PiBoardInfo(namedtuple('PiBoardInfo', (
@@ -317,17 +657,37 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
'csi',
'dsi',
'headers',
'board',
))):
"""
This class is a :func:`~collections.namedtuple` derivative used to
represent information about a particular model of Raspberry Pi. While it is
a tuple, it is strongly recommended that you use the following named
attributes to access the data contained within.
attributes to access the data contained within. The object can be used
in format strings with various custom format specifications::
from gpiozero import *
print('{0:full}'.format(pi_info()))
print('{0:board}'.format(pi_info()))
print('{0:specs}'.format(pi_info()))
print('{0:headers}'.format(pi_info()))
`'color'` and `'mono'` can be prefixed to format specifications to force
the use of `ANSI color codes`_. If neither is specified, ANSI codes will
only be used if stdout is detected to be a tty::
print('{0:color board}'.format(pi_info())) # force use of ANSI codes
print('{0:mono board}'.format(pi_info())) # force plain ASCII
.. _ANSI color codes: https://en.wikipedia.org/wiki/ANSI_escape_code
.. automethod:: physical_pin
.. automethod:: physical_pins
.. automethod:: pprint
.. automethod:: pulled_up
.. attribute:: revision
@@ -422,13 +782,185 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
.. attribute:: headers
A dictionary which maps header labels to dictionaries which map
physical pin numbers to :class:`PinInfo` tuples. For example, to obtain
information about pin 12 on header P1 you would query
``headers['P1'][12]``.
A dictionary which maps header labels to :class:`HeaderInfo` tuples.
For example, to obtain information about header P1 you would query
``headers['P1']``. To obtain information about pin 12 on header P1 you
would query ``headers['P1'].pins[12]``.
A rendered version of this data can be obtained by using the
:class:`PiBoardInfo` object in a format string::
from gpiozero import *
print('{0:headers}'.format(pi_info()))
.. attribute:: board
An ASCII art rendition of the board, primarily intended for console
pretty-print usage. A more usefully rendered version of this data can
be obtained by using the :class:`PiBoardInfo` object in a format
string. For example::
from gpiozero import *
print('{0:board}'.format(pi_info()))
.. _system on a chip: https://en.wikipedia.org/wiki/System_on_a_chip
"""
__slots__ = () # workaround python issue #24931
@classmethod
def from_revision(cls, revision):
if revision & 0x800000:
# New-style revision, parse information from bit-pattern:
#
# MSB -----------------------> LSB
# uuuuuuuuFMMMCCCCPPPPTTTTTTTTRRRR
#
# uuuuuuuu - Unused
# F - New flag (1=valid new-style revision, 0=old-style)
# MMM - Memory size (0=256, 1=512, 2=1024)
# CCCC - Manufacturer (0=Sony, 1=Egoman, 2=Embest)
# PPPP - Processor (0=2835, 1=2836, 2=2837)
# TTTTTTTT - Type (0=A, 1=B, 2=A+, 3=B+, 4=2B, 5=Alpha (??), 6=CM, 8=3B, 9=Zero)
# RRRR - Revision (0, 1, 2, etc.)
try:
model = {
0: 'A',
1: 'B',
2: 'A+',
3: 'B+',
4: '2B',
6: 'CM',
8: '3B',
9: 'Zero',
}[(revision & 0xff0) >> 4]
if model in ('A', 'B'):
pcb_revision = {
0: '1.0', # is this right?
1: '1.0',
2: '2.0',
}[revision & 0x0f]
else:
pcb_revision = '1.%d' % (revision & 0x0f)
released = {
'A': '2013Q1',
'B': '2012Q1' if pcb_revision == '1.0' else '2012Q4',
'A+': '2014Q4',
'B+': '2014Q3',
'2B': '2015Q1',
'CM': '2014Q2',
'3B': '2016Q1',
'Zero': '2015Q4' if pcb_revision == '1.2' else '2016Q2',
}[model]
soc = {
0: 'BCM2835',
1: 'BCM2836',
2: 'BCM2837',
}[(revision & 0xf000) >> 12]
manufacturer = {
0: 'Sony',
1: 'Egoman',
2: 'Embest',
}[(revision & 0xf0000) >> 16]
memory = {
0: 256,
1: 512,
2: 1024,
}[(revision & 0x700000) >> 20]
storage = {
'A': 'SD',
'B': 'SD',
'CM': 'eMMC',
}.get(model, 'MicroSD')
usb = {
'A': 1,
'A+': 1,
'Zero': 1,
'B': 2,
'CM': 1,
}.get(model, 4)
ethernet = {
'A': 0,
'A+': 0,
'Zero': 0,
'CM': 0,
}.get(model, 1)
wifi = {
'3B': True,
}.get(model, False)
bluetooth = {
'3B': True,
}.get(model, False)
csi = {
'Zero': 0 if pcb_revision == '1.2' else 1,
'CM': 2,
}.get(model, 1)
dsi = {
'Zero': 0,
}.get(model, csi)
headers = {
'A': {'P1': REV2_P1, 'P5': REV2_P5},
'B': {'P1': REV1_P1} if pcb_revision == '1.0' else {'P1': REV2_P1, 'P5': REV2_P5},
'CM': {'SODIMM': CM_SODIMM},
}.get(model, {'P1': PLUS_P1})
board = {
'A': A_BOARD,
'B': REV1_BOARD if pcb_revision == '1.0' else REV2_BOARD,
'A+': APLUS_BOARD,
'CM': CM_BOARD,
'Zero': ZERO12_BOARD if pcb_revision == '1.2' else ZERO13_BOARD,
}.get(model, BPLUS_BOARD)
except KeyError:
raise PinUnknownPi('unable to parse new-style revision "%x"' % revision)
else:
# Old-style revision, use the lookup table
try:
(
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
board,
) = PI_REVISIONS[revision]
except KeyError:
raise PinUnknownPi('unknown old-style revision "%x"' % revision)
headers = {
header: HeaderInfo(name=header, rows=max(header_data) // 2, columns=2, pins={
number: PinInfo(
number=number, function=function, pull_up=pull_up,
row=row + 1, col=col + 1)
for number, (function, pull_up) in header_data.items()
for row, col in (divmod(number, 2),)
})
for header, header_data in headers.items()
}
return cls(
'%04x' % revision,
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
board,
)
def physical_pins(self, function):
"""
@@ -445,8 +977,8 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
"""
return {
(header, pin.number)
for (header, pins) in self.headers.items()
for pin in pins.values()
for (header, info) in self.headers.items()
for pin in info.pins.values()
if pin.function == function
}
@@ -486,124 +1018,69 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
except PinNoPins:
return False
else:
return self.headers[header][number].pull_up
return self.headers[header].pins[number].pull_up
def _parse_pi_revision(revision):
# For new-style revisions the value's bit pattern is as follows:
#
# MSB -----------------------> LSB
# uuuuuuuuFMMMCCCCPPPPTTTTTTTTRRRR
#
# uuuuuuuu - Unused
# F - New flag (1=valid new-style revision, 0=old-style)
# MMM - Memory size (0=256, 1=512, 2=1024)
# CCCC - Manufacturer (0=Sony, 1=Egoman, 2=Embest)
# PPPP - Processor (0=2835, 1=2836, 2=2837)
# TTTTTTTT - Type (0=A, 1=B, 2=A+, 3=B+, 4=2B, 5=Alpha (??), 6=CM, 8=3B, 9=Zero)
# RRRR - Revision (0, 1, or 2)
if not (revision & 0x800000):
raise PinUnknownPi('cannot parse "%x"; this is not a new-style revision' % revision)
try:
model = {
0: 'A',
1: 'B',
2: 'A+',
3: 'B+',
4: '2B',
6: 'CM',
8: '3B',
9: 'Zero',
}[(revision & 0xff0) >> 4]
if model in ('A', 'B'):
pcb_revision = {
0: '1.0', # is this right?
1: '1.0',
2: '2.0',
}[revision & 0x0f]
else:
pcb_revision = '1.%d' % (revision & 0x0f)
released = {
'A': '2013Q1',
'B': '2012Q1' if pcb_revision == '1.0' else '2012Q4',
'A+': '2014Q4',
'B+': '2014Q3',
'2B': '2015Q1',
'CM': '2014Q2',
'3B': '2016Q1',
'Zero': '2015Q4' if pcb_revision == '1.0' else '2016Q2',
}[model]
soc = {
0: 'BCM2835',
1: 'BCM2836',
2: 'BCM2837',
}[(revision & 0xf000) >> 12]
manufacturer = {
0: 'Sony',
1: 'Egoman',
2: 'Embest',
}[(revision & 0xf0000) >> 16]
memory = {
0: 256,
1: 512,
2: 1024,
}[(revision & 0x700000) >> 20]
storage = {
'A': 'SD',
'B': 'SD',
'CM': 'eMMC',
}.get(model, 'MicroSD')
usb = {
'A': 1,
'A+': 1,
'Zero': 1,
'B': 2,
'CM': 0,
}.get(model, 4)
ethernet = {
'A': 0,
'A+': 0,
'Zero': 0,
'CM': 0,
}.get(model, 1)
wifi = {
'3B': True,
}.get(model, False)
bluetooth = {
'3B': True,
}.get(model, False)
csi = {
'Zero': 0 if pcb_revision == '1.0' else 1,
'CM': 2,
}.get(model, 1)
dsi = {
'Zero': 0,
}.get(model, csi)
headers = {
'A': {'P1': REV2_P1, 'P5': REV2_P5},
'B': {'P1': REV2_P1, 'P5': REV2_P5} if pcb_revision == '2.0' else {'P1': REV1_P1},
'CM': {'SODIMM': CM_SODIMM},
}.get(model, {'P1': PLUS_P1})
except KeyError:
raise PinUnknownPi('unable to parse new-style revision "%x"' % revision)
else:
return (
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
def __repr__(self):
return '{cls}({fields})'.format(
cls=self.__class__.__name__,
fields=', '.join(
(
'{name}=...' if name in ('headers', 'board') else
'{name}={value!r}').format(name=name, value=value)
for name, value in zip(self._fields, self)
)
)
def __format__(self, format_spec):
style, content = Style.from_style_content(format_spec)
if content == 'full':
return dedent("""\
{self:{style} board}
{self:{style} specs}
{self:{style} headers}"""
).format(self=self, style=style)
elif content == 'board':
kw = self._asdict()
kw.update({
name: header
for name, header in self.headers.items()
})
return self.board.format(style=style, **kw)
elif content == 'specs':
return dedent("""\
{style:bold}Revision {style:reset}: {revision}
{style:bold}SoC {style:reset}: {soc}
{style:bold}RAM {style:reset}: {memory}Mb
{style:bold}Storage {style:reset}: {storage}
{style:bold}USB ports {style:reset}: {usb} {style:yellow}(excluding power){style:reset}
{style:bold}Ethernet ports {style:reset}: {ethernet}
{style:bold}Wi-fi {style:reset}: {wifi}
{style:bold}Bluetooth {style:reset}: {bluetooth}
{style:bold}Camera ports (CSI) {style:reset}: {csi}
{style:bold}Display ports (DSI){style:reset}: {dsi}"""
).format(style=style, **self._asdict())
elif content == 'headers':
return '\n\n'.join(
dedent("""\
{style:bold}{header.name}{style:reset}:
{header:{style} full}"""
).format(header=header, style=style)
for header in sorted(self.headers.values(), key=attrgetter('name'))
)
def pprint(self, color=None):
"""
Pretty-print a representation of the board along with header diagrams.
If *color* is ``None`` (the default, the diagram will include ANSI
color codes if stdout is a color-capable terminal). Otherwise *color*
can be set to ``True`` or ``False`` to force color or monochrome
output.
"""
print('{0:{style} full}'.format(self, style=Style(color)))
def pi_info(revision=None):
"""
@@ -635,63 +1112,5 @@ def pi_info(revision=None):
else:
# be nice to people passing an int (or something numeric anyway)
revision = int(revision)
try:
(
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
) = PI_REVISIONS[revision]
except KeyError:
(
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
) = _parse_pi_revision(revision)
headers = {
header: {
number: PinInfo(number, function, pull_up)
for number, (function, pull_up) in header_data.items()
}
for header, header_data in headers.items()
}
return PiBoardInfo(
'%04x' % revision,
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
ethernet,
wifi,
bluetooth,
csi,
dsi,
headers,
)
return PiBoardInfo.from_revision(revision)

View File

@@ -7,13 +7,14 @@ from __future__ import (
str = type('')
import re
import pytest
from mock import patch, MagicMock
import gpiozero.devices
import gpiozero.pins.data
import gpiozero.pins.native
from gpiozero.pins.data import pi_info
from gpiozero.pins.data import pi_info, Style, HeaderInfo, PinInfo
from gpiozero import PinMultiplePins, PinNoPins, PinUnknownPi
@@ -88,3 +89,116 @@ def test_pulled_up():
assert not pi_info('a21041').pulled_up('GPIO4')
assert not pi_info('a21041').pulled_up('GPIO47')
def test_pprint_content():
with patch('sys.stdout') as stdout:
stdout.output = []
stdout.write = lambda buf: stdout.output.append(buf)
pi_info('900092').pprint(color=False)
s = ''.join(stdout.output)
assert ('o' * 20 + ' ') in s # first header row
assert ('1' + 'o' * 19 + ' ') in s # second header row
assert 'PiZero' in s
assert 'V1.2' in s # PCB revision
assert '900092' in s # Pi revision
assert 'BCM2835' in s # SOC name
stdout.output = []
pi_info('0002').pprint(color=False)
s = ''.join(stdout.output)
assert ('o' * 13 + ' ') in s # first header row
assert ('1' + 'o' * 12 + ' ') in s # second header row
assert 'Pi Model' in s
assert 'B V1.0' in s # PCB revision
assert '0002' in s # Pi revision
assert 'BCM2835' in s # SOC name
stdout.output = []
pi_info('0014').headers['SODIMM'].pprint(color=False)
assert len(''.join(stdout.output).splitlines()) == 100
def test_pprint_headers():
assert len(pi_info('0002').headers) == 1
assert len(pi_info('000e').headers) == 2
assert len(pi_info('900092').headers) == 1
with patch('sys.stdout') as stdout:
stdout.output = []
stdout.write = lambda buf: stdout.output.append(buf)
pi_info('0002').pprint()
s = ''.join(stdout.output)
assert 'P1:\n' in s
assert 'P5:\n' not in s
stdout.output = []
pi_info('000e').pprint()
s = ''.join(stdout.output)
assert 'P1:\n' in s
assert 'P5:\n' in s
stdout.output = []
pi_info('900092').pprint()
s = ''.join(stdout.output)
assert 'P1:\n' in s
assert 'P5:\n' not in s
def test_pprint_color():
with patch('sys.stdout') as stdout:
stdout.output = []
stdout.write = lambda buf: stdout.output.append(buf)
pi_info('900092').pprint(color=False)
s = ''.join(stdout.output)
assert '\x1b[0m' not in s # make sure ANSI reset code isn't in there
stdout.output = []
pi_info('900092').pprint(color=True)
s = ''.join(stdout.output)
assert '\x1b[0m' in s # check the ANSI reset code *is* in there (can't guarantee much else!)
stdout.output = []
stdout.fileno.side_effect = IOError('not a real file')
pi_info('900092').pprint()
s = ''.join(stdout.output)
assert '\x1b[0m' not in s # default should output mono
with patch('os.isatty') as isatty:
isatty.return_value = True
stdout.fileno.side_effect = None
stdout.output = []
pi_info('900092').pprint()
s = ''.join(stdout.output)
assert '\x1b[0m' in s # default should now output color
def test_pprint_styles():
with pytest.raises(ValueError):
Style.from_style_content('mono color full')
with pytest.raises(ValueError):
Style.from_style_content('full specs')
with patch('sys.stdout') as stdout:
s = '{0:full}'.format(pi_info('900092'))
assert '\x1b[0m' not in s # ensure default is mono when stdout is not a tty
with pytest.raises(ValueError):
'{0:foo on bar}'.format(Style())
def test_pprint_missing_pin():
header = HeaderInfo('FOO', 4, 2, {
1: PinInfo(1, '5V', False, 1, 1),
2: PinInfo(2, 'GND', False, 1, 2),
# Pin 3 is deliberately missing
4: PinInfo(4, 'GPIO1', False, 2, 2),
5: PinInfo(5, 'GPIO2', False, 3, 1),
6: PinInfo(6, 'GPIO3', False, 3, 2),
7: PinInfo(7, '3V3', False, 4, 1),
8: PinInfo(8, 'GND', False, 4, 2),
})
with patch('sys.stdout') as stdout:
stdout.output = []
stdout.write = lambda buf: stdout.output.append(buf)
s = ''.join(stdout.output)
header.pprint()
for i in range(1, 9):
if i == 3:
assert '(3)' not in s
else:
assert ('(%d)' % i)
def test_pprint_rows_cols():
assert '{0:row1}'.format(pi_info('900092').headers['P1']) == '1o'
assert '{0:row2}'.format(pi_info('900092').headers['P1']) == 'oo'
assert '{0:col1}'.format(pi_info('0002').headers['P1']) == '1oooooooooooo'
assert '{0:col2}'.format(pi_info('0002').headers['P1']) == 'ooooooooooooo'
with pytest.raises(ValueError):
'{0:row16}'.format(pi_info('0002').headers['P1'])
with pytest.raises(ValueError):
'{0:col3}'.format(pi_info('0002').headers['P1'])