This commit is contained in:
2026-03-21 10:53:02 +01:00
parent 3a6c07513c
commit ccfeb6f2c1
1997 changed files with 361936 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,714 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2019 Ben Nuttall <ben@bennuttall.com>
# Copyright (c) 2017-2018 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
# 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}{P2:{style} row8}{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}{P2:{style} row7}{P3:{style} row7}{style:white on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| {P2:{style} row6}{P3:{style} row6}{style:white on green} |{style:reset}
{style:white on green}| {style:on black}+---+{style:on green} {P2:{style} row5}{P3:{style} row5}{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} {P2:{style} row4}{P3:{style} row4}{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|S{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {P2:{style} row3}{P3:{style} row3}{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|I{style:on green} {style:bold}{model:3s}V{pcb_revision:3s}{style:normal} {P2:{style} row2}{P3:{style} row2}{style:white on green} P3 |{style:reset}
{style:white on green}| {style:on black}|0{style:on green} P2 {P2:{style} row1}{P3:{style} row1}{style:white on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:on black}C|{style:on green} {style:black on white}| Net{style:reset}
{style:white on green}| {style:on black}S|{style:on green} {style:black on white}+======{style:reset}
{style:black on white}=pwr{style:white on green} {style:black on white}|HDMI|{style:white on green} {style:on black}I|{style:on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}--{style:on black}0|{style: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}{P2:{style} row8}{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}{P2:{style} row7}{P3:{style} row7}{style:white on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| {P5:{style} col1}{style:white on green} {P2:{style} row6}{P3:{style} row6}{style:white on green} |{style:reset}
{style:white on green}| P5 {P5:{style} col2}{style:white on green} {style:on black}+---+{style:on green} {P2:{style} row5}{P3:{style} row5}{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} {P2:{style} row4}{P3:{style} row4}{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|S{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {P2:{style} row3}{P3:{style} row3}{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|I{style:on green} {style:bold}{model:3s}V{pcb_revision:3s}{style:normal} {P2:{style} row2}{P3:{style} row2}{style:white on green} P3 |{style:reset}
{style:white on green}| {style:on black}|0{style:on green} P2 {P2:{style} row1}{P3:{style} row1}{style:white on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:on black}C|{style:on green} {style:black on white}| Net{style:reset}
{style:white on green}| {P6:{style} row2}{style:white on green} {style:on black}S|{style:on green} {style:black on white}+======{style:reset}
{style:black on white}=pwr{style:white on green} P6 {P6:{style} row1}{style:white on green} {style:black on white}|HDMI|{style:white on green} {style:on black}I|{style:on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}--{style:on black}0|{style: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}{P2:{style} row8}{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}{P2:{style} row7}{P3:{style} row7}{style:white on cyan}+-+{style:on green} |{style:reset}
{style:white on green}| {P5:{style} col1}{style:white on green} {P2:{style} row6}{P3:{style} row6}{style:white on green} |{style:reset}
{style:white on green}| P5 {P5:{style} col2}{style:white on green} {style:on black}+---+{style:on green} {P2:{style} row5}{P3:{style} row5}{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} {P2:{style} row4}{P3:{style} row4}{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|S{style:on green} {style:bold}Pi Model{style:normal} {style:on black}+---+{style:on green} {P2:{style} row3}{P3:{style} row3}{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|I{style:on green} {style:bold}{model:3s}V{pcb_revision:3s}{style:normal} {P2:{style} row2}{P3:{style} row2}{style:white on green} P3 |{style:reset}
{style:white on green}| {style:on black}|0{style:on green} P2 {P2:{style} row1}{P3:{style} row1}{style:white on green} |{style:reset}
{style:white on green}| {style:on black}C|{style:on green} |{style:reset}
{style:white on green}| {P6:{style} row2}{style:white on green} {style:on black}S|{style:on green} |{style:reset}
{style:black on white}=pwr{style:white on green} P6 {P6:{style} row1}{style:white on green} {style:black on white}|HDMI|{style:white on green} {style:on black}I|{style:on green} |{style:reset}
{style:white on green}+----------------{style:black on white}| |{style:white on green}--{style:on black}0|{style:on green}------+{style:reset}"""
BPLUS_BOARD = """\
{style:white on green},--------------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 {style:black on white}+===={style:reset}
{style:white on green}| {J8:{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}| {RUN:{style} rev col1}{style:white on green} RUN{style:bold} Pi Model {model:4s}V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|D{style:on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|{style:black on white}S{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}|{style:black on white}I{style:white on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|0{style:on green} {style:on black}C|{style:on green} |{style:reset}
{style:white on green}| {style:black on white}S{style:white on black}|{style:on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:black on white}I{style:white on black}|{style:on green} {style:on black}|A|{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}0|{style:on green} {style:on black}|u|{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}|x|{style:on green}--------'{style:reset}"""
B3PLUS_BOARD = """\
{style:white on green},--------------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 PoE {style:black on white}+===={style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} {POE:{style} row1}{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:black on white} Wi {style:white on green} {POE:{style} row2}{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:black on white} Fi {style:white on green} {style:bold}Pi Model {model:4s}V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|D{style:on green} {style:black on white},---.{style:on green} {RUN:{style} col1}{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|{style:black on white}S{style:white on green} {style:black on white}|SoC|{style:white on green} RUN {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|{style:black on white}I{style:white on green} {style:black on white}`---'{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|0{style:on green} {style:on black}C|{style:on green} |{style:reset}
{style:white on green}| {style:black on white}S{style:white on black}|{style:on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:black on white}I{style:white on black}|{style:on green} {style:on black}|A|{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}0|{style:on green} {style:on black}|u|{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}|x|{style:on green}--------'{style:reset}"""
B4_BOARD = """\
{style:white on green},--------------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 {style:black on white}+======{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} J14 {style:black on white}| Net{style:reset}
{style:white on green}| {style:black on white} Wi {style:white on green} {J14:{style} row1}{style:on green} {style:black on white}+======{style:reset}
{style:white on green}| {style:black on white} Fi {style:white on green} {style:bold}Pi Model {model:4s}V{pcb_revision:3s}{style:normal} {J14:{style} row2}{style:normal white on green} |{style:reset}
{style:white on green}| {style:on black}|D{style:on green} {style:black on white},---.{style:on green} {style:white on black}+---+{style:on green} {style:blue on white}+===={style:reset}
{style:white on green}| {style:on black}|{style:black on white}S{style:white on green} {style:black on white}|SoC|{style:on green} {style:white on black}|RAM|{style:on green} {style:blue on white}|USB3{style:reset}
{style:white on green}| {style:on black}|{style:black on white}I{style:white on green} {style:black on white}`---'{style:on green} {style:white on black}+---+{style:on green} {style:blue on white}+===={style:reset}
{style:white on green}| {style:on black}|0{style:on green} {style:on black}C|{style:white on green} |{style:reset}
{style:white on green}| {J2:{style} rev col1}{style:white on green} J2 {style:black on white}S{style:white on black}|{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:black on white}I{style:white on black}|{style:on green} {style:white on black}|A|{style:white on green} {style:black on white}|USB2{style:reset}
{style:white on green}| {style:black on white}pwr{style:white on green} {style:black on white}|hd|{style:white on green} {style:black on white}|hd|{style:white on green} {style:on black}0|{style:on green} {style:on black}|u|{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}|m0|{style:white on green}---{style:black on white}|m1|{style:white on green}----{style:on black}|x|{style:on green}-------'{style:reset}"""
B5_BOARD = """\
{style:white on green},--------------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 {style:black on white}:{style:on green} {style:on white}+===={style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} {style:black on white}:{style:on green} {style:on white}|USB2{style:reset}
{style:white on green}| {style:black on white} Wi {style:white on green} {style:bold}Pi Model {model:4s}V{pcb_revision:3s}{style:normal} fan {style:black on white}+===={style:reset}
{style:white on green}| {style:black on white} Fi {style:white on green} {style:on black}+---+{style:on green} {style:on black}+---+{style:on green} |{style:reset}
{style:white on green}| {style:on black}|RAM|{style:on green} {style:on black}|RP1|{style:on green} {style:blue on white}+===={style:reset}
{style:white on green}|{style:black on yellow}|p{style:white on green} {style:on black}+---+{style:on green} {style:on black}+---+{style:on green} {style:blue on white}|USB3{style:reset}
{style:white on green}|{style:black on yellow}|{style:on white}c{style:white on green} {style:black on white}-------{style:white on green} {style:blue on white}+===={style:reset}
{style:white on green}|{style:black on yellow}|i{style:white on green} {style:black on white} SoC {style:white on green} {style:black on yellow}|c|c{style:white on green} J14 |{style:reset}
{style:bold white on green}({style:normal} {style:black on white}-------{style:white on green} J7{style:black on yellow}|{style:on white}s{style:on yellow}|{style:on white}s{style:white on green} {J14:{style} row1}{style:on green} {style:black on white}+======{style:reset}
{style:white on green}| J2 bat uart {J7:{style} row1}{style:black on yellow}|{style:on white}i{style:on yellow}|{style:on white}i{style:white on green} {J14:{style} row2}{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}..|hd|...|hd|{style:white on green}{J7:{style} row2}{style:black on yellow}|1|0{style:white on green} {style:black on white}+======{style:reset}
{style:white on green}`-{style:black on white}| |{style:white on green}-{J2:{style} col1}{style:black on white}|m0|{style:white on green}---{style:black on white}|m1|{style:white on green}--------------'{style:reset}"""
APLUS_BOARD = """\
{style:white on green},--------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 |{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| {style:bold}Pi Model {model:4s}V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|D{style:on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|S{style:on green} {style:on black}|SoC|{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|I{style:on green} {style:on black}+---+{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|0{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:on black}|A|{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}0|{style:on green} {style:on black}|u|{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}|x|{style:on green}--'{style:reset}"""
A3PLUS_BOARD = """\
{style:white on green},--------------------------.{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} J8 |{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} RUN|{style:reset}
{style:white on green}| {style:black on white} Wi {style:white on green} {RUN:{style} col1}{style:white on green}|{style:reset}
{style:white on green}| {style:black on white} Fi {style:white on green} {style:bold}Pi Model {model:4s}V{pcb_revision:3s}{style:normal} |{style:reset}
{style:white on green}| {style:on black}|D{style:on green} {style:black on white},---.{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|{style:black on white}S{style:white on green} {style:black on white}|SoC|{style:on green} {style:black on white}| USB{style:reset}
{style:white on green}| {style:on black}|{style:black on white}I{style:white on green} {style:black on white}`---'{style:on green} {style:black on white}+===={style:reset}
{style:white on green}| {style:on black}|0{style:on green} {style:on black}C|{style:on green} |{style:reset}
{style:white on green}| {style:black on white}S{style:white on black}|{style:on green} |{style:reset}
{style:white on green}| {style:black on white}I{style:white on black}|{style:on green} {style:on black}|A|{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}0|{style:on green} {style:on black}|u|{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}|x|{style:on green}--'{style:reset}"""
ZERO12_BOARD = """\
{style:white on green},--{J8:{style} col2}{style:white on green}---.{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} J8|{style:reset}
{style:black on white}---+{style:white on green} {style:bold}Pi{model:6s}{style:normal} RUN {RUN:{style} rev col1}{style:white on green} |{style:reset}
{style:black on white} sd|{style:white on green} {style:bold}V{pcb_revision:3s}{style:normal} {style:white on black}+---+{style:white on green} TV {TV:{style} col1}{style:white on green} |{style:reset}
{style:black on white}---+{style:white on green} {style:white on black}|SoC|{style:white on green} |{style:reset}
{style:white on green}| {style:black on white}hdmi{style:white on green} {style:white on black}+---+{style:white 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},--{J8:{style} col2}{style:white on green}---.{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} J8|{style:reset}
{style:black on white}---+{style:white on green} {style:bold}Pi{model:6s}{style:normal} RUN {RUN:{style} rev col1}{style:white on green} {style:black on white}c{style:white on black}|{style:reset}
{style:black on white} sd|{style:white on green} {style:bold}V{pcb_revision:3s}{style:normal} {style:white on black}+---+{style:white on green} TV {TV:{style} col1}{style:white on green} {style:black on white}s{style:white on black}|{style:reset}
{style:black on white}---+{style:white on green} {style:white on black}|SoC|{style:white on green} {style:black on white}i{style:white on black}|{style:reset}
{style:white on green}| {style:black on white}hdmi{style:white on green} {style:white on black}+---+{style:white 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}"""
ZERO2_BOARD = """\
{style:white on green},--{J8:{style} col2}{style:white on green}---.{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} J8|{style:reset}
{style:black on white}---+{style:white on green} {style:normal on black}+---+{style:on green} {style:bold}Pi{model:6s} {style:normal black on white}c{style:white on black}|{style:reset}
{style:black on white} sd|{style:white on green} {style:white on black}|SoC|{style:white on green} {style:black on white} Wi {style:bold white on green}V{pcb_revision:3s} {style:normal black on white}s{style:white on black}|{style:reset}
{style:black on white}---+{style:white on green} {style:white on black}+---+{style:white on green} {style:black on white} Fi {style:on green} {style:on white}i{style:white on black}|{style:reset}
{style:white on green}| {style:black on white}hdmi{style:white 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}
"""
CM_BOARD = """\
{style:white on green}+---------------------------------------+{style:reset}
{style:white on green}| {style:yellow on black}O{style:bold white on green} Raspberry Pi {model:4s} {style:normal yellow on black}O{style:white on green} |{style:reset}
{style:white on green}) Version {pcb_revision:3s} {style:on black}+---+{style:on green} ({style:reset}
{style:white on green}| {style:on black}|SoC|{style:on green} |{style:reset}
{style:white on green}) {style:on black}+---+{style:on green} ({style:reset}
{style:white on green}| {style:on black}O{style:on green} _ {style:on black}O{style:on green} |{style:reset}
{style:white on green}||||||{style:reset} {style:white on green}||||||||||||||||||||||||||||||||||{style:reset}"""
CM3PLUS_BOARD = """\
{style:white on green}+---------------------------------------+{style:reset}
{style:white on green}| {style:yellow on black}O{style:bold white on green} Raspberry Pi {model:4s} {style:normal yellow on black}O{style:white on green} |{style:reset}
{style:white on green}) Version {pcb_revision:3s} {style:black on white},---.{style:white on green} ({style:reset}
{style:white on green}| {style:black on white}|SoC|{style:white on green} |{style:reset}
{style:white on green}) {style:black on white}`---'{style:white on green} ({style:reset}
{style:white on green}| {style:on black}O{style:on green} _ {style:on black}O{style:on green} |{style:reset}
{style:white on green}||||||{style:reset} {style:white on green}||||||||||||||||||||||||||||||||||{style:reset}"""
CM4_BOARD = """\
{style:white on green},--{style:black on white}csi1{style:white on green}---{style:black on white}dsi0{style:white on green}---{style:black on white}dsi1{style:white on green}-----------{style:bold},-------------.{style:normal}-----------.{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} J2 {J2:{style} col2}{style:bold white on green}| |{style:normal}{J3:{style} rev col1}{style:white on green} |{style:reset}
{style:white on green}{style:black on white}c|{style:white on green} {style:bold}Pi {model:4s} Rev {pcb_revision:3s}{style:normal} {J2:{style} col1}{style:bold white on green}| {style:normal black on white} Wi {style:white on green} {style:bold}|{style:normal}J3 |{style:reset}
{style:white on green}{style:black on white}s|{style:white on green} {style:bold}IO Board{style:normal} {style:bold}| {style:normal black on white} Fi {style:white on green} {style:bold}|{style:normal} |{style:reset}
{style:white on green}{style:black on white}i|{style:white on green} J6 {J6:{style} col2}{style:bold white on green} | {style:normal white on black}+--+{style:on green}{style:bold}| {style:normal white on black}|P|{style:on green} |{style:reset}
{style:white on green}| J8 {J6:{style} col1}{style:bold white on green} | {style:normal black on white},----.{style:on green} {style:white on black}|eM|{style:bold on green}| {style:normal white on black}}}-{{{style:on green} |{style:reset}
{style:white on green}| {J8:{style} col2}{style:white on green} {style:bold}| {style:normal black on white}|SoC |{style:on green} {style:white on black}|MC|{style:bold on green}| {style:normal white on black}|C|{style:on green} |{style:reset}
{style:white on green}| {J8:{style} col1}{style:white on green} J9 {style:bold}| {style:normal black on white}| |{style:on green} {style:white on black}+--+{style:bold on green}| {style:normal white on black}|I|{style:on green} |{style:reset}
{style:white on green}| {style:black on white},---.{style:white on green} {J9:{style} row1}{style:bold white on green} | {style:normal black on white}`----'{style:white on green} {style:bold}| {style:normal white on black}|e|{style:on green} |{style:reset}
{style:white on green}|{style:black on white}( ={style:on green}O{style:on white} |{style:white on green} {J9:{style} row2}{style:bold white on green} | {style:normal white on black}+----+{style:on green} {style:bold}|{style:normal} |{style:reset}
{style:white on green}| {style:black on white}) + |{style:white on green} {style:bold}| {style:normal white on black}|RAM |{style:bold white on green} |{style:normal} |{style:reset}
{style:white on green}|{style:black on white}( ={style:on green}O{style:on white} |{style:white on green} {style:bold}`--{style:normal white on black}+----+{style:bold on green}-----'{style:normal} |{style:reset}
{style:white on green}| {style:black on white}`---'{style:white on green} |{style:reset}
{style:white on green}| {J1:{style} rev col1}{style:white on green} J1 |{style:reset}
{style:white on green}| |{style:reset}
{style:white on green}| {style:black on white}|Net |{style:on green} {style:black on white}|USB|{style:on green} {style:black on white}|uSD|{style:white on green} {style:on black}|p|{style:on green}|{style:reset}
{style:white on green}| {style:black on white}|HDMI|{style:on green} {style:black on white}|HDMI|{style:white on green} {style:black on white}| |{style:on green} {style:black on white}| 2 |{style:on green} {style:black on white}usb{style:white on green} {style:black on white}| |{style:white on green} {style:on black}|w|{style:on green}|{style:reset}
{style:white on green}`----{style:black on white}| 0 |{style:white on green}---{style:black on white}| 1 |{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:white on black}|r|{style:on green}'{style:reset}"""
P400_BOARD = """\
{style:white on red},------+----+----+----+----+---+--+--+--+--------------------+---.{style:reset}
{style:white on red},' |{style:white on black}Net {style:white on red}|{style:white on black}USB {style:white on red}|{style:cyan on black}USB {style:white on red}|{style:cyan on black}USB {style:white on red}|{style:white on black}pwr{style:white on red}|{style:white on black}hd{style:white on red}|{style:white on black}hd{style:white on red}|{style:white on black}sd{style:white on red}|{J8:{style} col2}{style:white on red}| `.{style:reset}
{style:white on red}/ {style:black}=={style:white} |{style:white on black} {style:white on red}|{style:white on black} 2 {style:white on red}|{style:cyan on black} 3 {style:white on red}|{style:cyan on black} 3 {style:white on red}|{style:white on black} {style:white on red}|{style:white on black}m1{style:white on red}|{style:white on black}m0{style:white on red}|{style:white on black} {style:white on red}|{J8:{style} col1}{style:white on red}| \\{style:reset}
{style:black on white},------------------------------------------------------------------------.{style:reset}
{style:black on white}| ___ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ {style:bold on white}o o {style:green}o{style:normal black}____ |{style:reset}
{style:black on white}| |Esc|F1{style:red}11{style:black}|F2{style:red}12{style:black}|F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10{style:red}o{style:black}|NumL|Pt{style:red}Sq{style:black}|Dl{style:red}In{style:black}| |{style:reset}
{style:black on white}| ___ ___ ____ ____ ____ ____ ____ ___ ____ ____ ____ ___ ____ _______ |{style:reset}
{style:black on white}| |¬ |! |" |£ |$ |% |^ |& {style:red}7{style:black}|* {style:red}8{style:black}|( {style:red}9{style:black}|) {style:red}*{style:black}|_ |+ |BkSpc | |{style:reset}
{style:black on white}| |` ||1 |2 |3 |4 |5 |6 |7 |8 |9 |0 |- |= |<-- | |{style:reset}
{style:black on white}| _____ ___ ____ ____ ____ ____ ____ ___ ____ ____ ____ ____ __ ______ |{style:reset}
{style:black on white}| |Tab |Q |W |E |R |T |Y |U {style:red}4{style:black}|I {style:red}5{style:black}|O {style:red}6{style:black}|P {style:red}-{style:black}|{{ |}} |Enter | |{style:reset}
{style:black on white}| |->| | | | | | | | | | | |[ |] |<-' | |{style:reset}
{style:black on white}| ______ ____ ____ ____ ____ ____ ____ ___ ____ ____ ____ ____ __ | |{style:reset}
{style:black on white}| |Caps |A |S |D |F |G |H |J {style:red}1{style:black}|K {style:red}2{style:black}|L {style:red}3{style:black}|: {style:red}+{style:black}|@ |~ | | |{style:reset}
{style:black on white}| |Lock | | | | | | | | | |; |' |# | | |{style:reset}
{style:black on white}| _____ ___ ___ ____ ____ ____ ____ ____ ___ ____ ____ ____ __________ |{style:reset}
{style:black on white}| |Shift|| |Z |X |C |V |B |N |M {style:red}0{style:black}|< |> {style:red}.{style:black}|? {style:red}/{style:black}|Shift | |{style:reset}
{style:black on white}| |^ |\\ | | | | | | | |, |. |/ |^ | |{style:reset}
{style:black on white}| ____ ___ ____ ____ _______________________ ____ ____ _____ |{style:reset}
{style:black on white}| |Ctrl|{style:red}Fn{style:black} | {style:red}**{style:black} |Alt | |Alt |Ctrl|____|^{style:red}PgUp{style:black}|____ |{style:reset}
{style:black on white}| | | | {style:red}{{}}{style:black} | | | | |<{style:red}Hom{style:black}|v{style:red}PgDn{style:black}|>{style:red}End{style:black}| |{style:reset}
{style:black on white}`------------------------------------------------------------------------'{style:reset}
Raspberry Pi {style:bold red}{model}{style:reset} Rev {pcb_revision}"""
# Pin maps for various board revisions and headers. Much of the information
# below is derived from the BCM2835 ARM Peripherals datasheet, but Gadgetoid's
# superb https://pinout.xyz site was also a great deal of help in filling in
# the gaps!
import re
def gpiof(*names):
return {
'gpio' if re.match(r'GPIO\d+$', name) else
'i2c' if re.match(r'I2C\d (SDA|SCL)$', name) else
'spi' if re.match(r'SPI\d (SCLK|MOSI|MISO|CE\d)$', name) else
'uart' if re.match(r'UART\d (RXD|TXD|RTS|CTS)$', name) else
'smi' if re.match(r'SMI (name[AD]\d+|SOE / SE|SWE / SRW)$', name) else
'dpi' if re.match(r'DPI (D\d+|PCLK|DE|[HV]SYNC)$', name) else
'pwm' if re.match(r'PWM\d+ \d+$', name) else
'pcm' if re.match(r'PCM (CLK|FS|DIN|DOUT)$', name) else
'sdio' if re.match(r'SD\d+ (CLK|CMD|DAT\d+)$', name) else
'jtag' if re.match(r'JTAG (TDI|TDO|TCK|TMS|RTCK|TRST)$', name) else
'mii' if re.match(r'(RG)?MII ', name) else
'': name
for name in names
if name
}
V1_8 = {'': '1V8'}
V3_3 = {'': '3V3'}
V5 = {'': '5V'}
GND = {'': 'GND'}
NC = {'': 'NC'} # not connected
# gpio alt0 alt1 alt2 alt3 alt4 alt5
PI1_GPIO0 = gpiof('GPIO0', 'I2C0 SDA', 'SMI SA5', 'DPI PCLK')
PI1_GPIO1 = gpiof('GPIO1', 'I2C0 SCL', 'SMI SA4', 'DPI DE')
PI1_GPIO2 = gpiof('GPIO2', 'I2C1 SDA', 'SMI SA3', 'DPI VSYNC')
PI1_GPIO3 = gpiof('GPIO3', 'I2C1 SCL', 'SMI SA2', 'DPI HSYNC')
PI1_GPIO4 = gpiof('GPIO4', 'GPCLK0', 'SMI SA1', 'DPI D0', '', '', 'JTAG TDI')
PI1_GPIO5 = gpiof('GPIO5', 'GPCLK1', 'SMI SA0', 'DPI D1', '', '', 'JTAG TDO')
PI1_GPIO6 = gpiof('GPIO6', 'GPCLK2', 'SMI SOE / SE', 'DPI D2', '', '', 'JTAG RTCK')
PI1_GPIO7 = gpiof('GPIO7', 'SPI0 CE1', 'SMI SWE / SRW', 'DPI D3')
PI1_GPIO8 = gpiof('GPIO8', 'SPI0 CE0', 'SMI SD0', 'DPI D4')
PI1_GPIO9 = gpiof('GPIO9', 'SPI0 MISO', 'SMI SD1', 'DPI D5')
PI1_GPIO10 = gpiof('GPIO10', 'SPI0 MOSI', 'SMI SD2', 'DPI D6')
PI1_GPIO11 = gpiof('GPIO11', 'SPI0 SCLK', 'SMI SD3', 'DPI D7')
PI1_GPIO12 = gpiof('GPIO12', 'PWM0 0', 'SMI SD4', 'DPI D8', '', '', 'JTAG TMS' )
PI1_GPIO13 = gpiof('GPIO13', 'PWM0 1', 'SMI SD5', 'DPI D9', '', '', 'JTAG TCK' )
PI1_GPIO14 = gpiof('GPIO14', 'UART0 TXD', 'SMI SD6', 'DPI D10' '', '', 'UART1 TXD')
PI1_GPIO15 = gpiof('GPIO15', 'UART0 RXD', 'SMI SD7', 'DPI D11' '', '', 'UART1 RXD')
PI1_GPIO16 = gpiof('GPIO16', '', 'SMI SD8', 'DPI D11', 'UART0 CTS', 'SPI1 CE2', 'UART1 CTS')
PI1_GPIO17 = gpiof('GPIO17', '', 'SMI SD9', 'DPI D13', 'UART0 RTS', 'SPI1 CE1', 'UART1 RTS')
PI1_GPIO18 = gpiof('GPIO18', 'PCM CLK', 'SMI SD10', 'DPI D14', 'BSC SDA / MOSI', 'SPI1 CE0', 'PWM0 0')
PI1_GPIO19 = gpiof('GPIO19', 'PCM FS', 'SMI SD11', 'DPI D15', 'BSC SCL / SCLK', 'SPI1 MISO', 'PWM0 1')
PI1_GPIO20 = gpiof('GPIO20', 'PCM DIN', 'SMI SD12', 'DPI D16', 'BSC MISO', 'SPI1 MOSI', 'GPCLK0')
PI1_GPIO21 = gpiof('GPIO21', 'PCM DOUT', 'SMI SD13', 'DPI D17', 'BSC CE', 'SPI1 SCLK', 'GPCLK1')
PI1_GPIO22 = gpiof('GPIO22', 'SD0 CLK', 'SMI SD14', 'DPI D18', 'SD1 CLK', 'JTAG TRST')
PI1_GPIO23 = gpiof('GPIO23', 'SD0 CMD', 'SMI SD15', 'DPI D19', 'SD1 CMD', 'JTAG RTCK')
PI1_GPIO24 = gpiof('GPIO24', 'SD0 DAT0', 'SMI SD16', 'DPI D20', 'SD1 DAT0', 'JTAG TDO')
PI1_GPIO25 = gpiof('GPIO25', 'SD0 DAT1', 'SMI SD17', 'DPI D21', 'SD1 DAT1', 'JTAG TCK')
PI1_GPIO26 = gpiof('GPIO26', 'SD0 DAT2', '', 'DPI D22', 'SD1 DAT2', 'JTAG TDI')
PI1_GPIO27 = gpiof('GPIO27', 'SD0 DAT3', '', 'DPI D23', 'SD1 DAT3', 'JTAG TMS')
PI1_GPIO28 = gpiof('GPIO28', 'I2C0 SDA', 'SMI SA5', 'PCM CLK')
PI1_GPIO29 = gpiof('GPIO29', 'I2C0 SCL', 'SMI SA4', 'PCM FS')
PI1_GPIO30 = gpiof('GPIO30', '', 'SMI SA3', 'PCM DIN', 'UART0 CTS', '', 'UART1 CTS')
PI1_GPIO31 = gpiof('GPIO31', '', 'SMI SA2', 'PCM DOUT', 'UART0 RTS', '', 'UART1 RTS')
PI1_GPIO32 = gpiof('GPIO32', 'GPCLK0', 'SMI SA1', '', 'UART0 TXD', '', 'UART1 TXD')
PI1_GPIO33 = gpiof('GPIO33', '', 'SMI SA0', '', 'UART0 RXD', '', 'UART1 RXD')
PI1_GPIO34 = gpiof('GPIO34', 'GPCLK0', 'SMI SOE / SE')
PI1_GPIO35 = gpiof('GPIO35', 'SPI0 CE1', 'SMI SWE / SRW')
PI1_GPIO36 = gpiof('GPIO36', 'SPI0 CE0', 'SMI SD0', 'UART0 TXD')
PI1_GPIO37 = gpiof('GPIO37', 'SPI0 MISO', 'SMI SD1', 'UART0 RXD')
PI1_GPIO38 = gpiof('GPIO38', 'SPI0 MOSI', 'SMI SD2', 'UART0 RTS')
PI1_GPIO39 = gpiof('GPIO39', 'SPI0 SCLK', 'SMI SD3', 'UART0 CTS')
PI1_GPIO40 = gpiof('GPIO40', 'PWM0 0', 'SMI SD4', '', '', 'SPI2 MISO', 'UART1 TXD')
PI1_GPIO41 = gpiof('GPIO41', 'PWM0 1', 'SMI SD5', '', '', 'SPI2 MOSI', 'UART1 RXD')
PI1_GPIO42 = gpiof('GPIO42', 'GPCLK1', 'SMI SD6', '', '', 'SPI2 SCLK', 'UART1 RTS')
PI1_GPIO43 = gpiof('GPIO43', 'GPCLK2', 'SMI SD7', '', '', 'SPI2 CE0', 'UART1 CTS')
PI1_GPIO44 = gpiof('GPIO44', 'GPCLK1', 'I2C0 SDA', 'I2C1 SDA', '', 'SPI2 CE1')
PI1_GPIO45 = gpiof('GPIO45', 'PWM0 1', 'I2C0 SCL', 'I2C1 SCL', '', 'SPI2 CE2')
PI1_GPIO46 = gpiof('GPIO46')
PI1_GPIO47 = gpiof('GPIO47')
PI1_GPIO48 = gpiof('GPIO48')
PI1_GPIO49 = gpiof('GPIO49')
PI1_GPIO50 = gpiof('GPIO50')
PI1_GPIO51 = gpiof('GPIO51')
PI1_GPIO52 = gpiof('GPIO52')
PI1_GPIO53 = gpiof('GPIO53')
# gpio alt0 alt1 alt2 alt3 alt4 alt5
PI4_GPIO0 = gpiof('GPIO0', 'I2C0 SDA', 'SMI SA5', 'DPI PCLK', 'SPI3 CE0', 'UART2 TXD', 'I2C6 SDA')
PI4_GPIO1 = gpiof('GPIO1', 'I2C0 SCL', 'SMI SA4', 'DPI DE', 'SPI3 MISO', 'UART2 RXD', 'I2C6 SCL')
PI4_GPIO2 = gpiof('GPIO2', 'I2C1 SDA', 'SMI SA3', 'DPI VSYNC', 'SPI3 MOSI', 'UART2 CTS', 'I2C3 SDA')
PI4_GPIO3 = gpiof('GPIO3', 'I2C1 SCL', 'SMI SA2', 'DPI HSYNC', 'SPI3 SCLK', 'UART2 RTS', 'I2C3 SCL')
PI4_GPIO4 = gpiof('GPIO4', 'GPCLK0', 'SMI SA1', 'DPI D0', 'SPI4 CE0', 'UART3 TXD', 'I2C3 SDA')
PI4_GPIO5 = gpiof('GPIO5', 'GPCLK1', 'SMI SA0', 'DPI D1', 'SPI4 MISO', 'UART3 RXD', 'I2C3 SCL')
PI4_GPIO6 = gpiof('GPIO6', 'GPCLK2', 'SMI SOE / SE', 'DPI D2', 'SPI4 MOSI', 'UART3 CTS', 'I2C4 SDA')
PI4_GPIO7 = gpiof('GPIO7', 'SPI0 CE1', 'SMI SWE / SRW', 'DPI D3', 'SPI4 SCLK', 'UART3 RTS', 'I2C4 SCL')
PI4_GPIO8 = gpiof('GPIO8', 'SPI0 CE0', 'SMI SD0', 'DPI D4', 'BSC CE', 'UART4 TXD', 'I2C4 SDA')
PI4_GPIO9 = gpiof('GPIO9', 'SPI0 MISO', 'SMI SD1', 'DPI D5', 'BSC MISO', 'UART4 RXD', 'I2C4 SCL')
PI4_GPIO10 = gpiof('GPIO10', 'SPI0 MOSI', 'SMI SD2', 'DPI D6', 'BSC SDA / MOSI', 'UART4 CTS', 'I2C5 SDA')
PI4_GPIO11 = gpiof('GPIO11', 'SPI0 SCLK', 'SMI SD3', 'DPI D7', 'BSC SCL / SCLK', 'UART4 RTS', 'I2C5 SCL')
PI4_GPIO12 = gpiof('GPIO12', 'PWM0 0', 'SMI SD4', 'DPI D8', 'SPI5 CE0', 'UART5 TXD', 'I2C5 SDA')
PI4_GPIO13 = gpiof('GPIO13', 'PWM0 1', 'SMI SD5', 'DPI D9', 'SPI5 MISO', 'UART5 RXD', 'I2C5 SCL')
PI4_GPIO14 = gpiof('GPIO14', 'UART0 TXD', 'SMI SD6', 'DPI D10', 'SPI5 MOSI', 'UART5 CTS', 'UART1 TXD')
PI4_GPIO15 = gpiof('GPIO15', 'UART0 RXD', 'SMI SD7', 'DPI D11', 'SPI5 SCLK', 'UART5 RTS', 'UART1 RXD')
PI4_GPIO16 = gpiof('GPIO16', '', 'SMI SD8', 'DPI D12', 'UART0 CTS', 'SPI1 CE2', 'UART1 CTS')
PI4_GPIO17 = gpiof('GPIO17', '', 'SMI SD9', 'DPI D13', 'UART0 RTS', 'SPI1 CE1', 'UART1 RTS')
PI4_GPIO18 = gpiof('GPIO18', 'PCM CLK', 'SMI SD10', 'DPI D14', 'SPI6 CE0', 'SPI1 CE0', 'PWM0 0')
PI4_GPIO19 = gpiof('GPIO19', 'PCM FS', 'SMI SD11', 'DPI D15', 'SPI6 MISO', 'SPI1 MISO', 'PWM0 1')
PI4_GPIO20 = gpiof('GPIO20', 'PCM DIN', 'SMI SD12', 'DPI D16', 'SPI6 MOSI', 'SPI1 MOSI', 'GPCLK0')
PI4_GPIO21 = gpiof('GPIO21', 'PCM DOUT', 'SMI SD13', 'DPI D17', 'SPI6 SCLK', 'SPI1 SCLK', 'GPCLK1')
PI4_GPIO22 = gpiof('GPIO22', 'SD0 CLK', 'SMI SD14', 'DPI D18', 'SD1 CLK', 'JTAG TRST', 'I2C6 SDA')
PI4_GPIO23 = gpiof('GPIO23', 'SD0 CMD', 'SMI SD15', 'DPI D19', 'SD1 CMD', 'JTAG RTCK', 'I2C6 SCL')
PI4_GPIO24 = gpiof('GPIO24', 'SD0 DAT0', 'SMI SD16', 'DPI D20', 'SD1 DAT0', 'JTAG TDO', 'SPI3 CE1')
PI4_GPIO25 = gpiof('GPIO25', 'SD0 DAT1', 'SMI SD17', 'DPI D21', 'SD1 DAT1', 'JTAG TCK', 'SPI4 CE1')
PI4_GPIO26 = gpiof('GPIO26', 'SDA DAT2', '', 'DPI D22', 'SD1 DAT2', 'JTAG TDI', 'SPI5 CE1')
PI4_GPIO27 = gpiof('GPIO27', 'SDA DAT3', '', 'DPI D23', 'SD1 DAT3', 'JTAG TMS', 'SPI6 CE1')
PI4_GPIO28 = gpiof('GPIO28', 'I2C0 SDA', 'SMI SA5', 'PCM CLK', '', 'MII RX ERR', 'RGMII MDIO')
PI4_GPIO29 = gpiof('GPIO29', 'I2C0 SCL', 'SMI SA4', 'PCM FS', '', 'MII TX ERR', 'RGMII MDC')
PI4_GPIO30 = gpiof('GPIO30', '', 'SMI SA3', 'PCM DIN', 'UART0 CTS', 'MII CRS', 'UART1 CTS')
PI4_GPIO31 = gpiof('GPIO31', '', 'SMI SA2', 'PCM DOUT', 'UART0 RTS', 'MII COL', 'UART1 RTS')
PI4_GPIO32 = gpiof('GPIO32', 'GPCLK0', 'SMI SA1', '', 'UART0 TXD', 'SD CARD PRES', 'UART1 TXD')
PI4_GPIO33 = gpiof('GPIO33', '', 'SMI SA0', '', 'UART0 RXD', 'SD CARD WRPROT', 'UART1 RXD')
PI4_GPIO34 = gpiof('GPIO34', 'GPCLK0', 'SMI SOE / SE', '', 'SD1 CLK', 'SD CARD LED', 'RGMII IRQ')
PI4_GPIO35 = gpiof('GPIO35', 'SPI0 CE1', 'SMI SWE / SRW', '', 'SD1 CMD', 'RGMII START STOP')
PI4_GPIO36 = gpiof('GPIO36', 'SPI0 CE0', 'SMI SD0', 'UART0 TXD', 'SD1 DAT0', 'RGMII RX OK', 'MII RX ERR')
PI4_GPIO37 = gpiof('GPIO37', 'SPI0 MISO', 'SMI SD1', 'UART0 RXD', 'SD1 DAT1', 'RGMII MDIO', 'MII TX ERR')
PI4_GPIO38 = gpiof('GPIO38', 'SPI0 MOSI', 'SMI SD2', 'UART0 RTS', 'SD1 DAT2', 'RGMII MDC', 'MII CRS')
PI4_GPIO39 = gpiof('GPIO39', 'SPI0 SCLK', 'SMI SD3', 'UART0 CTS', 'SD1 DAT3', 'RGMII IRQ', 'MII COL')
PI4_GPIO40 = gpiof('GPIO40', 'PWM1 0', 'SMI SD4', '', 'SD1 DAT4', 'SPI0 MISO', 'UART1 TXD')
PI4_GPIO41 = gpiof('GPIO41', 'PWM1 1', 'SMI SD5', '', 'SD1 DAT5', 'SPI0 MOSI', 'UART1 RXD')
PI4_GPIO42 = gpiof('GPIO42', 'GPCLK1', 'SMI SD6', '', 'SD1 DAT6', 'SPI0 SCLK', 'UART1 RTS')
PI4_GPIO43 = gpiof('GPIO43', 'GPCLK2', 'SMI SD7', '', 'SD1 DAT7', 'SPI0 CE0', 'UART1 CTS')
PI4_GPIO44 = gpiof('GPIO44', 'GPCLK1', 'I2C0 SDA', 'I2C1 SDA', '', 'SPI0 CE1', 'SD CARD VOLT')
PI4_GPIO45 = gpiof('GPIO45', 'PWM0 1', 'I2C0 SCL', 'I2C1 SCL', '', 'SPI0 CE2', 'SD CARD PWR0')
PI4_GPIO46 = gpiof('GPIO46')
PI4_GPIO47 = gpiof('GPIO47')
PI4_GPIO48 = gpiof('GPIO48')
PI4_GPIO49 = gpiof('GPIO49')
PI4_GPIO50 = gpiof('GPIO50')
PI4_GPIO51 = gpiof('GPIO51')
PI4_GPIO52 = gpiof('GPIO52')
PI4_GPIO53 = gpiof('GPIO53')
PI4_GPIO54 = gpiof('GPIO54')
PI4_GPIO55 = gpiof('GPIO55')
PI4_GPIO56 = gpiof('GPIO56')
PI4_GPIO57 = gpiof('GPIO57')
del gpiof
del re
REV1_P1 = (13, 2, {
1: V3_3, 2: V5,
3: PI1_GPIO0, 4: V5,
5: PI1_GPIO1, 6: GND,
7: PI1_GPIO4, 8: PI1_GPIO14,
9: GND, 10: PI1_GPIO15,
11: PI1_GPIO17, 12: PI1_GPIO18,
13: PI1_GPIO21, 14: GND,
15: PI1_GPIO22, 16: PI1_GPIO23,
17: V3_3, 18: PI1_GPIO24,
19: PI1_GPIO10, 20: GND,
21: PI1_GPIO9, 22: PI1_GPIO25,
23: PI1_GPIO11, 24: PI1_GPIO8,
25: GND, 26: PI1_GPIO7,
})
REV2_P1 = (13, 2, {
1: V3_3, 2: V5,
3: PI1_GPIO2, 4: V5,
5: PI1_GPIO3, 6: GND,
7: PI1_GPIO4, 8: PI1_GPIO14,
9: GND, 10: PI1_GPIO15,
11: PI1_GPIO17, 12: PI1_GPIO18,
13: PI1_GPIO27, 14: GND,
15: PI1_GPIO22, 16: PI1_GPIO23,
17: V3_3, 18: PI1_GPIO24,
19: PI1_GPIO10, 20: GND,
21: PI1_GPIO9, 22: PI1_GPIO25,
23: PI1_GPIO11, 24: PI1_GPIO8,
25: GND, 26: PI1_GPIO7,
})
REV2_P5 = (4, 2, {
1: V5, 2: V3_3,
3: PI1_GPIO28, 4: PI1_GPIO29,
5: PI1_GPIO30, 6: PI1_GPIO31,
7: GND, 8: GND,
})
PI1_P2 = (8, 1, {
1: {'': 'GPU JTAG'},
2: {'': 'GPU JTAG'},
3: {'': 'GPU JTAG'},
4: {'': 'GPU JTAG'},
5: {'': 'GPU JTAG'},
6: {'': 'GPU JTAG'},
7: {'': 'GPU JTAG'},
8: {'': 'GPU JTAG'},
})
PI1_P3 = (7, 1, {
1: {'': 'LAN JTAG'},
2: {'': 'LAN JTAG'},
3: {'': 'LAN JTAG'},
4: {'': 'LAN JTAG'},
5: {'': 'LAN JTAG'},
6: {'': 'LAN JTAG'},
7: {'': 'LAN JTAG'},
})
REV2_P6 = (2, 1, {
1: {'': 'RUN'},
2: GND,
})
PLUS_J8 = (20, 2, {
1: V3_3, 2: V5,
3: PI1_GPIO2, 4: V5,
5: PI1_GPIO3, 6: GND,
7: PI1_GPIO4, 8: PI1_GPIO14,
9: GND, 10: PI1_GPIO15,
11: PI1_GPIO17, 12: PI1_GPIO18,
13: PI1_GPIO27, 14: GND,
15: PI1_GPIO22, 16: PI1_GPIO23,
17: V3_3, 18: PI1_GPIO24,
19: PI1_GPIO10, 20: GND,
21: PI1_GPIO9, 22: PI1_GPIO25,
23: PI1_GPIO11, 24: PI1_GPIO8,
25: GND, 26: PI1_GPIO7,
27: PI1_GPIO0, 28: PI1_GPIO1,
29: PI1_GPIO5, 30: GND,
31: PI1_GPIO6, 32: PI1_GPIO12,
33: PI1_GPIO13, 34: GND,
35: PI1_GPIO19, 36: PI1_GPIO16,
37: PI1_GPIO26, 38: PI1_GPIO20,
39: GND, 40: PI1_GPIO21,
})
PLUS_POE = (2, 2, {
1: {'': 'TR01 TAP'}, 2: {'': 'TR00 TAP'},
3: {'': 'TR03 TAP'}, 4: {'': 'TR02 TAP'},
})
PLUS_RUN = (2, 1, {
1: {'': 'POWER ENABLE'},
2: {'': 'RUN'},
})
ZERO_RUN = REV2_P6
ZERO_TV = (2, 1, {
1: {'': 'COMPOSITE'},
2: GND,
})
PI4_J8 = (20, 2, {
1: V3_3, 2: V5,
3: PI4_GPIO2, 4: V5,
5: PI4_GPIO3, 6: GND,
7: PI4_GPIO4, 8: PI4_GPIO14,
9: GND, 10: PI4_GPIO15,
11: PI4_GPIO17, 12: PI4_GPIO18,
13: PI4_GPIO27, 14: GND,
15: PI4_GPIO22, 16: PI4_GPIO23,
17: V3_3, 18: PI4_GPIO24,
19: PI4_GPIO10, 20: GND,
21: PI4_GPIO9, 22: PI4_GPIO25,
23: PI4_GPIO11, 24: PI4_GPIO8,
25: GND, 26: PI4_GPIO7,
27: PI4_GPIO0, 28: PI4_GPIO1,
29: PI4_GPIO5, 30: GND,
31: PI4_GPIO6, 32: PI4_GPIO12,
33: PI4_GPIO13, 34: GND,
35: PI4_GPIO19, 36: PI4_GPIO16,
37: PI4_GPIO26, 38: PI4_GPIO20,
39: GND, 40: PI4_GPIO21,
})
PI4_J2 = (3, 1, {
1: {'': 'GLOBAL ENABLE'},
2: GND,
3: {'': 'RUN'},
})
PI4_J14 = PLUS_POE
PI5_J2 = (2, 1, {
1: {'': 'RUN'},
2: GND,
})
PI5_J7 = (2, 1, {
1: {'': 'COMPOSITE'},
2: GND,
})
CM_SODIMM = (100, 2, {
1: GND, 2: {'': 'EMMC DISABLE N'},
3: PI1_GPIO0, 4: NC,
5: PI1_GPIO1, 6: NC,
7: GND, 8: NC,
9: PI1_GPIO2, 10: NC,
11: PI1_GPIO3, 12: NC,
13: GND, 14: NC,
15: PI1_GPIO4, 16: NC,
17: PI1_GPIO5, 18: NC,
19: GND, 20: NC,
21: PI1_GPIO6, 22: NC,
23: PI1_GPIO7, 24: NC,
25: GND, 26: GND,
27: PI1_GPIO8, 28: PI1_GPIO28,
29: PI1_GPIO9, 30: PI1_GPIO29,
31: GND, 32: GND,
33: PI1_GPIO10, 34: PI1_GPIO30,
35: PI1_GPIO11, 36: PI1_GPIO31,
37: GND, 38: GND,
39: {'': 'GPIO0-27 VREF'}, 40: {'': 'GPIO0-27 VREF'},
# Gap in SODIMM pins
41: {'': 'GPIO28-45 VREF'}, 42: {'': 'GPIO28-45 VREF'},
43: GND, 44: GND,
45: PI1_GPIO12, 46: PI1_GPIO32,
47: PI1_GPIO13, 48: PI1_GPIO33,
49: GND, 50: GND,
51: PI1_GPIO14, 52: PI1_GPIO34,
53: PI1_GPIO15, 54: PI1_GPIO35,
55: GND, 56: GND,
57: PI1_GPIO16, 58: PI1_GPIO36,
59: PI1_GPIO17, 60: PI1_GPIO37,
61: GND, 62: GND,
63: PI1_GPIO18, 64: PI1_GPIO38,
65: PI1_GPIO19, 66: PI1_GPIO39,
67: GND, 68: GND,
69: PI1_GPIO20, 70: PI1_GPIO40,
71: PI1_GPIO21, 72: PI1_GPIO41,
73: GND, 74: GND,
75: PI1_GPIO22, 76: PI1_GPIO42,
77: PI1_GPIO23, 78: PI1_GPIO43,
79: GND, 80: GND,
81: PI1_GPIO24, 82: PI1_GPIO44,
83: PI1_GPIO25, 84: PI1_GPIO45,
85: GND, 86: GND,
87: PI1_GPIO26, 88: {'': 'GPIO46 1V8'},
89: PI1_GPIO27, 90: {'': 'GPIO47 1V8'},
91: GND, 92: GND,
93: {'': 'DSI0 DN1'}, 94: {'': 'DSI1 DP0'},
95: {'': 'DSI0 DP1'}, 96: {'': 'DSI1 DN0'},
97: GND, 98: GND,
99: {'': 'DSI0 DN0'}, 100: {'': 'DSI1 CP'},
101: {'': 'DSI0 DP0'}, 102: {'': 'DSI1 CN'},
103: GND, 104: GND,
105: {'': 'DSI0 CN'}, 106: {'': 'DSI1 DP3'},
107: {'': 'DSI0 CP'}, 108: {'': 'DSI1 DN3'},
109: GND, 110: GND,
111: {'': 'HDMI CK N'}, 112: {'': 'DSI1 DP2'},
113: {'': 'HDMI CK P'}, 114: {'': 'DSI1 DN2'},
115: GND, 116: GND,
117: {'': 'HDMI D0 N'}, 118: {'': 'DSI1 DP1'},
119: {'': 'HDMI D0 P'}, 120: {'': 'DSI1 DN1'},
121: GND, 122: GND,
123: {'': 'HDMI D1 N'}, 124: NC,
125: {'': 'HDMI D1 P'}, 126: NC,
127: GND, 128: NC,
129: {'': 'HDMI D2 N'}, 130: NC,
131: {'': 'HDMI D2 P'}, 132: NC,
133: GND, 134: GND,
135: {'': 'CAM1 DP3'}, 136: {'': 'CAM0 DP0'},
137: {'': 'CAM1 DN3'}, 138: {'': 'CAM0 DN0'},
139: GND, 140: GND,
141: {'': 'CAM1 DP2'}, 142: {'': 'CAM0 CP'},
143: {'': 'CAM1 DN2'}, 144: {'': 'CAM0 CN'},
145: GND, 146: GND,
147: {'': 'CAM1 CP'}, 148: {'': 'CAM0 DP1'},
149: {'': 'CAM1 CN'}, 150: {'': 'CAM0 DN1'},
151: GND, 152: GND,
153: {'': 'CAM1 DP1'}, 154: NC,
155: {'': 'CAM1 DN1'}, 156: NC,
157: GND, 158: NC,
159: {'': 'CAM1 DP0'}, 160: NC,
161: {'': 'CAM1 DN0'}, 162: NC,
163: GND, 164: GND,
165: {'': 'USB DP'}, 166: {'': 'TVDAC'},
167: {'': 'USB DM'}, 168: {'': 'USB OTGID'},
169: GND, 170: GND,
171: {'': 'HDMI CEC'}, 172: {'': 'VC TRST N'},
173: {'': 'HDMI SDA'}, 174: {'': 'VC TDI'},
175: {'': 'HDMI SCL'}, 176: {'': 'VC TMS'},
177: {'': 'RUN'}, 178: {'': 'VC TDO'},
179: {'': 'VDD CORE'}, 180: {'': 'VC TCK'},
181: GND, 182: GND,
183: V1_8, 184: V1_8,
185: V1_8, 186: V1_8,
187: GND, 188: GND,
189: {'': 'VDAC'}, 190: {'': 'VDAC'},
191: V3_3, 192: V3_3,
193: V3_3, 194: V3_3,
195: GND, 196: GND,
197: {'': 'VBAT'}, 198: {'': 'VBAT'},
199: {'': 'VBAT'}, 200: {'': 'VBAT'},
})
CM3_SODIMM = (CM_SODIMM[0], CM_SODIMM[1], CM_SODIMM[2].copy())
CM3_SODIMM[-1].update({
4: {'': 'NC / SDX VREF'},
6: {'': 'NC / SDX VREF'},
8: GND,
10: {'': 'NC / SDX CLK'},
12: {'': 'NC / SDX CMD'},
14: GND,
16: {'': 'NC / SDX D0'},
18: {'': 'NC / SDX D1'},
20: GND,
22: {'': 'NC / SDX D2'},
24: {'': 'NC / SDX D3'},
88: {'': 'HDMI HPD N 1V8'},
90: {'': 'EMMC EN N 1V8'},
})
CM4_J6 = (2, 2, {
1: {'': '1-2 CAM0+DISP0'}, 2: {'': '1-2 CAM0+DISP0'},
3: {'': '3-4 CAM0+DISP0'}, 4: {'': '3-4 CAM0+DISP0'},
})
CM4_J2 = (7, 2, {
1: {'': '1-2 DISABLE eMMC BOOT'}, 2: {'': '1-2 DISABLE eMMC BOOT'},
3: {'': '3-4 WRITE-PROT EEPROM'}, 4: {'': '3-4 WRITE-PROT EEPROM'},
5: {'': 'AIN0 MXL7704'}, 6: {'': 'AIN1 MXL7704'},
7: GND, 8: {'': 'SYNC_IN'},
9: {'': 'SYNC OUT'}, 10: GND,
11: {'': 'TV OUT'}, 12: GND,
13: {'': '13-14 WAKE'}, 14: {'': '13-14 WAKE'},
})
CM4_J1 = PI4_J2
CM4_J9 = PLUS_POE
CM4_J3 = (3, 1, {
1: {'': 'WL DISABLE'},
2: GND,
3: {'': 'BT DISABLE'},
})
# The following data is sourced from a combination of the following locations:
#
# http://elinux.org/RPi_HardwareHistory
# http://elinux.org/RPi_Low-level_peripherals
# https://git.drogon.net/?p=wiringPi;a=blob;f=wiringPi/wiringPi.c#l807
# https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
PI_REVISIONS = {
# 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, 'P2': PI1_P2, 'P3': PI1_P3}, REV1_BOARD, ),
0x3: ('B', '1.0', '2012Q3', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1, 'P2': PI1_P2, 'P3': PI1_P3}, REV1_BOARD, ),
0x4: ('B', '2.0', '2012Q3', 'BCM2835', 'Sony', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0x5: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0x6: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0x7: ('A', '2.0', '2013Q1', 'BCM2835', 'Egoman', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, A_BOARD, ),
0x8: ('A', '2.0', '2013Q1', 'BCM2835', 'Sony', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, A_BOARD, ),
0x9: ('A', '2.0', '2013Q1', 'BCM2835', 'Qisda', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, A_BOARD, ),
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P2': PI1_P2, 'P3': PI1_P3, 'P5': REV2_P5, 'P6': REV2_P6}, REV2_BOARD, ),
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'J8': PLUS_J8}, 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, {'J8': PLUS_J8}, APLUS_BOARD, ),
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'J8': PLUS_J8}, 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, {'J8': PLUS_J8}, APLUS_BOARD, ),
}
SPI_HARDWARE_PINS = {
0: {
'clock': 'GPIO11',
'mosi': 'GPIO10',
'miso': 'GPIO9',
'select': ('GPIO8', 'GPIO7'),
},
}

View File

@@ -0,0 +1,365 @@
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2021-2023 Dave Jones <dave@waveform.org.uk>
#
# SPDX-License-Identifier: BSD-3-Clause
import os
import lgpio
from . import SPI
from .pi import spi_port_device
from .local import LocalPiFactory, LocalPiPin
from ..mixins import SharedMixin
from ..exc import (
PinInvalidFunction,
PinSetInput,
PinFixedPull,
PinInvalidPull,
PinInvalidBounce,
PinInvalidState,
SPIBadArgs,
SPIInvalidClockMode,
PinPWMFixedValue,
DeviceClosed
)
try:
# Patch several constants which changed incompatibly between lg 0.1.6.0
# and 0.2.0.0
lgpio.SET_PULL_NONE
except AttributeError:
lgpio.SET_PULL_NONE = lgpio.SET_BIAS_DISABLE
lgpio.SET_PULL_UP = lgpio.SET_BIAS_PULL_UP
lgpio.SET_PULL_DOWN = lgpio.SET_BIAS_PULL_DOWN
class LGPIOFactory(LocalPiFactory):
"""
Extends :class:`~gpiozero.pins.local.LocalPiFactory`. Uses the `lgpio`_
library to interface to the local computer's GPIO pins. The lgpio library
simply talks to Linux gpiochip devices; it is not specific to the Raspberry
Pi although this class is currently constructed under the assumption that
it is running on a Raspberry Pi.
You can construct lgpio pins manually like so::
from gpiozero.pins.lgpio import LGPIOFactory
from gpiozero import LED
factory = LGPIOFactory(chip=0)
led = LED(12, pin_factory=factory)
The *chip* parameter to the factory constructor specifies which gpiochip
device to attempt to open. It defaults to 0 and thus doesn't normally need
to be specified (the example above only includes it for completeness).
The lgpio library relies on access to the :file:`/dev/gpiochip*` devices.
If you run into issues, please check that your user has read/write access
to the specific gpiochip device you are attempting to open (0 by default).
.. _lgpio: http://abyz.me.uk/lg/py_lgpio.html
"""
def __init__(self, chip=None):
super().__init__()
chip = 4 if (self._get_revision() & 0xff0) >> 4 == 0x17 else 0
self._handle = lgpio.gpiochip_open(chip)
self._chip = chip
self.pin_class = LGPIOPin
def close(self):
super().close()
if self._handle is not None:
lgpio.gpiochip_close(self._handle)
self._handle = None
@property
def chip(self):
return self._chip
def _get_spi_class(self, shared, hardware):
# support via lgpio instead of spidev
if hardware:
return [LGPIOHardwareSPI, LGPIOHardwareSPIShared][shared]
return super()._get_spi_class(shared, hardware=False)
class LGPIOPin(LocalPiPin):
"""
Extends :class:`~gpiozero.pins.local.LocalPiPin`. Pin implementation for
the `lgpio`_ library. See :class:`LGPIOFactory` for more information.
.. _lgpio: http://abyz.me.uk/lg/py_lgpio.html
"""
GPIO_IS_KERNEL = 1 << 0
GPIO_IS_OUT = 1 << 1
GPIO_IS_ACTIVE_LOW = 1 << 2
GPIO_IS_OPEN_DRAIN = 1 << 3
GPIO_IS_OPEN_SOURCE = 1 << 4
GPIO_IS_BIAS_PULL_UP = 1 << 5
GPIO_IS_BIAS_PULL_DOWN = 1 << 6
GPIO_IS_BIAS_DISABLE = 1 << 7
GPIO_IS_LG_INPUT = 1 << 8
GPIO_IS_LG_OUTPUT = 1 << 9
GPIO_IS_LG_ALERT = 1 << 10
GPIO_IS_LG_GROUP = 1 << 11
GPIO_LINE_FLAGS_MASK = (
GPIO_IS_ACTIVE_LOW | GPIO_IS_OPEN_DRAIN | GPIO_IS_OPEN_SOURCE |
GPIO_IS_BIAS_PULL_UP | GPIO_IS_BIAS_PULL_DOWN | GPIO_IS_BIAS_DISABLE)
GPIO_EDGES = {
'both': lgpio.BOTH_EDGES,
'rising': lgpio.RISING_EDGE,
'falling': lgpio.FALLING_EDGE,
}
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
def __init__(self, factory, info):
super().__init__(factory, info)
self._pwm = None
self._bounce = None
self._callback = None
self._edges = lgpio.BOTH_EDGES
lgpio.gpio_claim_input(
self.factory._handle, self._number, lgpio.SET_PULL_NONE)
def close(self):
if self.factory._handle is not None:
# Closing is really just "resetting" the function of the pin;
# we let the factory close deal with actually freeing stuff
lgpio.gpio_claim_input(
self.factory._handle, self._number, lgpio.SET_PULL_NONE)
def _get_function(self):
mode = lgpio.gpio_get_mode(self.factory._handle, self._number)
return ['input', 'output'][bool(mode & self.GPIO_IS_OUT)]
def _set_function(self, value):
if self._callback is not None:
self._callback.cancel()
self._callback = None
try:
{
'input': lgpio.gpio_claim_input,
'output': lgpio.gpio_claim_output,
}[value](self.factory._handle, self._number)
except KeyError:
raise PinInvalidFunction(
f'invalid function "{value}" for pin {self!r}')
def _get_state(self):
if self._pwm:
return self._pwm[1] / 100
else:
return bool(lgpio.gpio_read(self.factory._handle, self._number))
def _set_state(self, value):
if self._pwm:
freq, duty = self._pwm
self._pwm = (freq, int(value * 100))
try:
lgpio.tx_pwm(self.factory._handle, self._number, *self._pwm)
except lgpio.error:
raise PinInvalidState(
f'invalid state "{value}" for pin {self!r}')
elif self.function == 'input':
raise PinSetInput(f'cannot set state of pin {self!r}')
else:
lgpio.gpio_write(self.factory._handle, self._number, bool(value))
def _get_pull(self):
mode = lgpio.gpio_get_mode(self.factory._handle, self._number)
if mode & self.GPIO_IS_BIAS_PULL_UP:
return 'up'
elif mode & self.GPIO_IS_BIAS_PULL_DOWN:
return 'down'
else:
return 'floating'
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull not in (value, ''):
raise PinFixedPull(
f'{self!r} has a physical pull-{self.info.pull} resistor')
try:
flags = {
'up': lgpio.SET_PULL_UP,
'down': lgpio.SET_PULL_DOWN,
'floating': lgpio.SET_PULL_NONE,
}[value]
except KeyError:
raise PinInvalidPull(f'invalid pull "{value}" for pin {self!r}')
else:
# Simply calling gpio_claim_input is insufficient to change the
# line flags on a pin; it needs to be freed and re-claimed
lgpio.gpio_free(self.factory._handle, self._number)
lgpio.gpio_claim_input(self.factory._handle, self._number, flags)
def _get_frequency(self):
if self._pwm:
freq, duty = self._pwm
return freq
else:
return None
def _set_frequency(self, value):
if not self._pwm and value is not None and value > 0:
if self.function != 'output':
raise PinPWMFixedValue(f'cannot start PWM on pin {self!r}')
lgpio.tx_pwm(self.factory._handle, self._number, value, 0)
self._pwm = (value, 0)
elif self._pwm and value is not None and value > 0:
freq, duty = self._pwm
lgpio.tx_pwm(self.factory._handle, self._number, value, duty)
self._pwm = (value, duty)
elif self._pwm and (value is None or value == 0):
lgpio.tx_pwm(self.factory._handle, self._number, 0, 0)
self._pwm = None
def _get_bounce(self):
return None if not self._bounce else self._bounce / 1000000
def _set_bounce(self, value):
if value is None:
value = 0
elif value < 0:
raise PinInvalidBounce('bounce must be 0 or greater')
value = int(value * 1000000)
lgpio.gpio_set_debounce_micros(
self.factory._handle, self._number, value)
self._bounce = value
def _get_edges(self):
return self.GPIO_EDGES_NAMES[self._edges]
def _set_edges(self, value):
f = self.when_changed
self.when_changed = None
try:
self._edges = self.GPIO_EDGES[value]
finally:
self.when_changed = f
def _call_when_changed(self, chip, gpio, level, ticks):
super()._call_when_changed(ticks / 1000000000, level)
def _enable_event_detect(self):
lgpio.gpio_claim_alert(
self.factory._handle, self._number, self._edges,
lgpio.gpio_get_mode(self.factory._handle, self._number) &
self.GPIO_LINE_FLAGS_MASK)
self._callback = lgpio.callback(
self.factory._handle, self._number, self._edges,
self._call_when_changed)
def _disable_event_detect(self):
if self._callback is not None:
self._callback.cancel()
self._callback = None
lgpio.gpio_claim_input(
self.factory._handle, self._number,
lgpio.gpio_get_mode(self.factory._handle, self._number) &
self.GPIO_LINE_FLAGS_MASK)
class LGPIOHardwareSPI(SPI):
"""
Hardware SPI implementation for the `lgpio`_ library. Uses the ``spi_*``
functions from the lgpio API.
.. _lgpio: http://abyz.me.uk/lg/py_lgpio.html
"""
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
port, device = spi_port_device(
clock_pin, mosi_pin, miso_pin, select_pin)
self._port = port
self._device = device
self._baud = 500000
self._spi_flags = 0
self._handle = None
super().__init__(pin_factory=pin_factory)
to_reserve = {clock_pin, select_pin}
if mosi_pin is not None:
to_reserve.add(mosi_pin)
if miso_pin is not None:
to_reserve.add(miso_pin)
self.pin_factory.reserve_pins(self, *to_reserve)
self._handle = lgpio.spi_open(port, device, self._baud, self._spi_flags)
def _conflicts_with(self, other):
return not (
isinstance(other, LGPIOHardwareSPI) and
(self._port, self._device) != (other._port, other._device)
)
def close(self):
if not self.closed:
lgpio.spi_close(self._handle)
self._handle = None
self.pin_factory.release_all(self)
super().close()
@property
def closed(self):
return self._handle is None
def __repr__(self):
try:
self._check_open()
return f'SPI(port={self._port:d}, device={self._device:d})'
except DeviceClosed:
return 'SPI(closed)'
def _get_clock_mode(self):
return self._spi_flags
def _set_clock_mode(self, value):
self._check_open()
if not 0 <= value < 4:
raise SPIInvalidClockMode(f"{value} is not a valid SPI clock mode")
lgpio.spi_close(self._handle)
self._spi_flags = value
self._handle = lgpio.spi_open(
self._port, self._device, self._baud, self._spi_flags)
def _get_rate(self):
return self._baud
def _set_rate(self, value):
self._check_open()
value = int(value)
lgpio.spi_close(self._handle)
self._baud = value
self._handle = lgpio.spi_open(
self._port, self._device, self._baud, self._spi_flags)
def read(self, n):
self._check_open()
count, data = lgpio.spi_read(self._handle, n)
if count < 0:
raise IOError(f'SPI transfer error {count}')
return [int(b) for b in data]
def write(self, data):
self._check_open()
count = lgpio.spi_write(self._handle, data)
if count < 0:
raise IOError(f'SPI transfer error {count}')
return len(data)
def transfer(self, data):
self._check_open()
count, data = lgpio.spi_xfer(self._handle, data)
if count < 0:
raise IOError(f'SPI transfer error {count}')
return [int(b) for b in data]
class LGPIOHardwareSPIShared(SharedMixin, LGPIOHardwareSPI):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
return (clock_pin, select_pin)

View File

@@ -0,0 +1,214 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2020 Fangchen Li <fangchen.li@outlook.com>
# Copyright (c) 2016 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
import io
import errno
import struct
from collections import defaultdict
from threading import Lock
from time import monotonic
try:
from spidev import SpiDev
except ImportError:
SpiDev = None
from . import SPI
from .pi import PiFactory, PiPin, SPI_HARDWARE_PINS, spi_port_device
from .spi import SPISoftware
from ..devices import Device
from ..mixins import SharedMixin
from ..output_devices import OutputDevice
from ..exc import DeviceClosed, PinUnknownPi, SPIInvalidClockMode
def get_pi_revision():
revision = None
try:
with io.open('/proc/device-tree/system/linux,revision', 'rb') as f:
revision = hex(struct.unpack('>L', f.read(4))[0])[2:]
except IOError as e:
if e.errno != errno.ENOENT:
raise e
with io.open('/proc/cpuinfo', 'r') as f:
for line in f:
if line.startswith('Revision'):
revision = line.split(':')[1].strip().lower()
if revision is not None:
overvolted = revision.startswith('100')
if overvolted:
revision = revision[-4:]
return int(revision, base=16)
raise PinUnknownPi(
'unable to locate Pi revision in /proc/device-tree or /proc/cpuinfo')
class LocalPiFactory(PiFactory):
"""
Extends :class:`~gpiozero.pins.pi.PiFactory`. Abstract base class
representing pins attached locally to a Pi. This forms the base class for
local-only pin interfaces (:class:`~gpiozero.pins.rpigpio.RPiGPIOPin`,
:class:`~gpiozero.pins.lgpio.LGPIOPin`, and
:class:`~gpiozero.pins.native.NativePin`).
"""
pins = {}
_reservations = defaultdict(list)
_res_lock = Lock()
def __init__(self):
super().__init__()
# Override the reservations and pins dict to be this class' attributes.
# This is a bit of a dirty hack, but ensures that anyone evil enough to
# mix pin implementations doesn't try and control the same pin with
# different backends
self.pins = LocalPiFactory.pins
self._reservations = LocalPiFactory._reservations
self._res_lock = LocalPiFactory._res_lock
def _get_revision(self):
return get_pi_revision()
def _get_spi_class(self, shared, hardware):
return {
(False, True): LocalPiHardwareSPI,
(True, True): LocalPiHardwareSPIShared,
(False, False): LocalPiSoftwareSPI,
(True, False): LocalPiSoftwareSPIShared,
}[shared, hardware]
@staticmethod
def ticks():
return monotonic()
@staticmethod
def ticks_diff(later, earlier):
return later - earlier
class LocalPiPin(PiPin):
"""
Extends :class:`~gpiozero.pins.pi.PiPin`. Abstract base class representing
a multi-function GPIO pin attached to the local Raspberry Pi.
"""
def _call_when_changed(self, ticks=None, state=None):
"""
Overridden to provide default ticks from the local Pi factory.
.. warning::
The local pin factory uses a seconds-based monotonic value for
its ticks but you *must not* rely upon this behaviour. Ticks are
an opaque value that should only be compared with the associated
:meth:`Factory.ticks_diff` method.
"""
super()._call_when_changed(
self._factory.ticks() if ticks is None else ticks,
self.state if state is None else state)
class LocalPiHardwareSPI(SPI):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
self._port, self._device = spi_port_device(
clock_pin, mosi_pin, miso_pin, select_pin)
self._bus = None
if SpiDev is None:
raise ImportError('failed to import spidev')
super().__init__(pin_factory=pin_factory)
to_reserve = {clock_pin, select_pin}
if mosi_pin is not None:
to_reserve.add(mosi_pin)
if miso_pin is not None:
to_reserve.add(miso_pin)
self.pin_factory.reserve_pins(self, *to_reserve)
self._bus = SpiDev()
self._bus.open(self._port, self._device)
self._bus.max_speed_hz = 500000
def _conflicts_with(self, other):
if isinstance(other, LocalPiHardwareSPI):
return (
(self._port == other._port) and
(self._device == other._device))
else:
return True
def close(self):
if self._bus is not None:
self._bus.close()
self._bus = None
self.pin_factory.release_all(self)
super().close()
@property
def closed(self):
return self._bus is None
def __repr__(self):
try:
self._check_open()
return 'SPI(port=%d, device=%d)' % (self._port, self._device)
except DeviceClosed:
return 'SPI(closed)'
def transfer(self, data):
"""
Writes data (a list of integer words where each word is assumed to have
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
equivalent number of words, returning them as a list of integers.
"""
return self._bus.xfer2(data)
def _get_clock_mode(self):
return self._bus.mode
def _set_clock_mode(self, value):
self._bus.mode = value
def _get_lsb_first(self):
return self._bus.lsbfirst
def _set_lsb_first(self, value):
self._bus.lsbfirst = bool(value)
def _get_select_high(self):
return self._bus.cshigh
def _set_select_high(self, value):
self._bus.cshigh = bool(value)
def _get_bits_per_word(self):
return self._bus.bits_per_word
def _set_bits_per_word(self, value):
self._bus.bits_per_word = value
def _get_rate(self):
return self._bus.max_speed_hz
def _set_rate(self, value):
self._bus.max_speed_hz = int(value)
class LocalPiSoftwareSPI(SPISoftware):
pass
class LocalPiHardwareSPIShared(SharedMixin, LocalPiHardwareSPI):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin,
pin_factory):
return (clock_pin, select_pin)
class LocalPiSoftwareSPIShared(SharedMixin, LocalPiSoftwareSPI):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin,
pin_factory):
return (clock_pin, select_pin)

View File

@@ -0,0 +1,535 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2024 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2020 Fangchen Li <fangchen.li@outlook.com>
# Copyright (c) 2016 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
import os
from collections import namedtuple
from time import time, sleep, monotonic
from threading import Thread, Event
from math import isclose
# NOTE: Remove try when compatibility moves beyond Python 3.10
try:
from importlib_metadata import entry_points
except ImportError:
from importlib.metadata import entry_points
from ..exc import (
PinPWMUnsupported,
PinSetInput,
PinFixedPull,
PinInvalidPin,
PinInvalidFunction,
PinInvalidPull,
PinInvalidBounce,
)
from ..devices import Device
from ..mixins import SharedMixin
from . import SPI
from .pi import PiPin, PiFactory
from .spi import SPISoftware
PinState = namedtuple('PinState', ('timestamp', 'state'))
class MockPin(PiPin):
"""
A mock pin used primarily for testing. This class does *not* support PWM.
"""
def __init__(self, factory, info):
super().__init__(factory, info)
self._function = 'input'
self._pull = info.pull or 'floating'
self._state = self._pull == 'up'
self._bounce = None
self._edges = 'both'
self._when_changed = None
self.clear_states()
def close(self):
self.when_changed = None
self.function = 'input'
def _get_function(self):
return self._function
def _set_function(self, value):
if value not in ('input', 'output'):
raise PinInvalidFunction('function must be input or output')
self._function = value
if value == 'input':
# Drive the input to the pull
self._set_pull(self._get_pull())
def _get_state(self):
return self._state
def _set_state(self, value):
if self._function == 'input':
raise PinSetInput(f'cannot set state of pin {self!r}')
assert self._function == 'output'
assert 0 <= value <= 1
self._change_state(bool(value))
def _change_state(self, value):
if self._state != value:
t = monotonic()
self._state = value
self.states.append(PinState(t - self._last_change, value))
self._last_change = t
return True
return False
def _get_frequency(self):
return None
def _set_frequency(self, value):
if value is not None:
raise PinPWMUnsupported()
def _get_pull(self):
return self._pull
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull and value != self.info.pull:
raise PinFixedPull(f'{self!r} has a fixed pull resistor')
if value not in ('floating', 'up', 'down'):
raise PinInvalidPull('pull must be floating, up, or down')
self._pull = value
if value == 'up':
self.drive_high()
elif value == 'down':
self.drive_low()
def _get_bounce(self):
return self._bounce
def _set_bounce(self, value):
# XXX Need to implement this
if value is not None:
try:
value = float(value)
except ValueError:
raise PinInvalidBounce('bounce must be None or a float')
self._bounce = value
def _get_edges(self):
return self._edges
def _set_edges(self, value):
assert value in ('none', 'falling', 'rising', 'both')
self._edges = value
def _disable_event_detect(self):
pass
def _enable_event_detect(self):
pass
def _call_when_changed(self):
super()._call_when_changed(self._last_change, self._state)
def drive_high(self):
assert self._function == 'input'
if self._change_state(True):
if self._edges in ('both', 'rising') and self._when_changed is not None:
self._call_when_changed()
def drive_low(self):
assert self._function == 'input'
if self._change_state(False):
if self._edges in ('both', 'falling') and self._when_changed is not None:
self._call_when_changed()
def clear_states(self):
self._last_change = monotonic()
self.states = [PinState(0.0, self._state)]
def assert_states(self, expected_states):
# Tests that the pin went through the expected states (a list of values)
for actual, expected in zip(self.states, expected_states):
assert actual.state == expected
def assert_states_and_times(self, expected_states):
# Tests that the pin went through the expected states at the expected
# times (times are compared with a tolerance of tens-of-milliseconds as
# that's about all we can reasonably expect in a non-realtime
# environment on a Pi 1)
for actual, expected in zip(self.states, expected_states):
assert isclose(actual.timestamp, expected[0], rel_tol=0.05, abs_tol=0.05)
assert isclose(actual.state, expected[1])
class MockConnectedPin(MockPin):
"""
This derivative of :class:`MockPin` emulates a pin connected to another
mock pin. This is used in the "real pins" portion of the test suite to
check that one pin can influence another.
"""
def __init__(self, factory, info, input_pin=None):
super().__init__(factory, info)
self.input_pin = input_pin
def _change_state(self, value):
if self.input_pin:
if value:
self.input_pin.drive_high()
else:
self.input_pin.drive_low()
return super()._change_state(value)
class MockChargingPin(MockPin):
"""
This derivative of :class:`MockPin` emulates a pin which, when set to
input, waits a predetermined length of time and then drives itself high
(as if attached to, e.g. a typical circuit using an LDR and a capacitor
to time the charging rate).
"""
def __init__(self, factory, info, charge_time=0.01):
super().__init__(factory, info)
self.charge_time = charge_time # dark charging time
self._charge_stop = Event()
self._charge_thread = None
def _set_function(self, value):
super()._set_function(value)
if value == 'input':
if self._charge_thread:
self._charge_stop.set()
self._charge_thread.join()
self._charge_stop.clear()
self._charge_thread = Thread(target=self._charge)
self._charge_thread.start()
elif value == 'output':
if self._charge_thread:
self._charge_stop.set()
self._charge_thread.join()
else:
assert False
def _charge(self):
if not self._charge_stop.wait(self.charge_time):
try:
self.drive_high()
except AssertionError: # pragma: no cover
# Charging pins are typically flipped between input and output
# repeatedly; if another thread has already flipped us to
# output ignore the assertion-error resulting from attempting
# to drive the pin high
pass
class MockTriggerPin(MockPin):
"""
This derivative of :class:`MockPin` is intended to be used with another
:class:`MockPin` to emulate a distance sensor. Set *echo_pin* to the
corresponding pin instance. When this pin is driven high it will trigger
the echo pin to drive high for the echo time.
"""
def __init__(self, factory, info, echo_pin=None, echo_time=0.04):
super().__init__(factory, info)
self.echo_pin = echo_pin
self.echo_time = echo_time # longest echo time
self._echo_thread = None
def _set_state(self, value):
super()._set_state(value)
if value:
if self._echo_thread:
self._echo_thread.join()
self._echo_thread = Thread(target=self._echo)
self._echo_thread.start()
def _echo(self):
sleep(0.001)
self.echo_pin.drive_high()
sleep(self.echo_time)
self.echo_pin.drive_low()
class MockPWMPin(MockPin):
"""
This derivative of :class:`MockPin` adds PWM support.
"""
def __init__(self, factory, info):
super().__init__(factory, info)
self._frequency = None
def close(self):
self.frequency = None
super().close()
def _set_state(self, value):
if self._function == 'input':
raise PinSetInput(f'cannot set state of pin {self!r}')
assert self._function == 'output'
assert 0 <= value <= 1
self._change_state(float(value))
def _get_frequency(self):
return self._frequency
def _set_frequency(self, value):
if value is not None:
assert self._function == 'output'
self._frequency = value
if value is None:
self._change_state(0.0)
class MockSPIClockPin(MockPin):
"""
This derivative of :class:`MockPin` is intended to be used as the clock pin
of a mock SPI device. It is not intended for direct construction in tests;
rather, construct a :class:`MockSPIDevice` with various pin numbers, and
this class will be used for the clock pin.
"""
def __init__(self, factory, info):
super().__init__(factory, info)
self.spi_devices = getattr(self, 'spi_devices', [])
def _set_state(self, value):
super()._set_state(value)
for dev in self.spi_devices:
dev.on_clock()
class MockSPISelectPin(MockPin):
"""
This derivative of :class:`MockPin` is intended to be used as the select
pin of a mock SPI device. It is not intended for direct construction in
tests; rather, construct a :class:`MockSPIDevice` with various pin numbers,
and this class will be used for the select pin.
"""
def __init__(self, factory, info):
super().__init__(factory, info)
self.spi_device = getattr(self, 'spi_device', None)
def _set_state(self, value):
super()._set_state(value)
if self.spi_device:
self.spi_device.on_select()
class MockSPIDevice:
"""
This class is used to test :class:`SPIDevice` implementations. It can be
used to mock up the slave side of simple SPI devices, e.g. the MCP3xxx
series of ADCs.
Descendants should override the :meth:`on_start` and/or :meth:`on_bit`
methods to respond to SPI interface events. The :meth:`rx_word` and
:meth:`tx_word` methods can be used facilitate communications within these
methods. Such descendents can then be passed as the *spi_class* parameter
of the :class:`MockFactory` constructor to have instances attached to any
SPI interface requested by an :class:`SPIDevice`.
"""
def __init__(self, clock_pin, mosi_pin=None, miso_pin=None,
select_pin=None, *, clock_polarity=False, clock_phase=False,
lsb_first=False, bits_per_word=8, select_high=False,
pin_factory=None):
if pin_factory is None:
pin_factory = Device.pin_factory
assert isinstance(pin_factory, MockFactory)
self.clock_pin = pin_factory.pin(clock_pin, pin_class=MockSPIClockPin)
self.mosi_pin = None if mosi_pin is None else pin_factory.pin(mosi_pin)
self.miso_pin = None if miso_pin is None else pin_factory.pin(miso_pin)
self.select_pin = None if select_pin is None else pin_factory.pin(select_pin, pin_class=MockSPISelectPin)
self.clock_polarity = clock_polarity
self.clock_phase = clock_phase
self.lsb_first = lsb_first
self.bits_per_word = bits_per_word
self.select_high = select_high
self.rx_bit = 0
self.rx_buf = []
self.tx_buf = []
self.clock_pin.spi_devices.append(self)
self.select_pin.spi_device = self
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_tb):
self.close()
def close(self):
if self in self.clock_pin.spi_devices:
self.clock_pin.spi_devices.remove(self)
if self.select_pin is not None:
self.select_pin.spi_device = None
def on_select(self):
if self.select_pin.state == self.select_high:
self.on_start()
def on_clock(self):
# Don't do anything if this SPI device isn't currently selected
if self.select_pin is None or self.select_pin.state == self.select_high:
# The XOR of the clock pin's values, polarity and phase indicates
# whether we're meant to be acting on this edge
if self.clock_pin.state ^ self.clock_polarity ^ self.clock_phase:
self.rx_bit += 1
if self.mosi_pin is not None:
self.rx_buf.append(self.mosi_pin.state)
if self.miso_pin is not None:
try:
tx_value = self.tx_buf.pop(0)
except IndexError:
tx_value = 0
if tx_value:
self.miso_pin.drive_high()
else:
self.miso_pin.drive_low()
self.on_bit()
def on_start(self):
"""
Override this in descendents to detect when the mock SPI device's
select line is activated.
"""
self.rx_bit = 0
self.rx_buf = []
self.tx_buf = []
def on_bit(self):
"""
Override this in descendents to react to receiving a bit.
The :attr:`rx_bit` attribute gives the index of the bit received (this
is reset to 0 by default by :meth:`on_select`). The :attr:`rx_buf`
sequence gives the sequence of 1s and 0s that have been recevied so
far. The :attr:`tx_buf` sequence gives the sequence of 1s and 0s to
transmit on the next clock pulses. All these attributes can be modified
within this method.
The :meth:`rx_word` and :meth:`tx_word` methods can also be used to
read and append to the buffers using integers instead of bool bits.
"""
pass
def rx_word(self):
result = 0
bits = reversed(self.rx_buf) if self.lsb_first else self.rx_buf
for bit in bits:
result <<= 1
result |= bit
return result
def tx_word(self, value, bits_per_word=None):
if bits_per_word is None:
bits_per_word = self.bits_per_word
bits = [0] * bits_per_word
for bit in range(bits_per_word):
bits[bit] = value & 1
value >>= 1
assert not value
if not self.lsb_first:
bits = reversed(bits)
self.tx_buf.extend(bits)
class MockFactory(PiFactory):
"""
Factory for generating mock pins.
The *revision* parameter specifies what revision of Pi the mock factory
pretends to be (this affects the result of the :attr:`Factory.board_info`
attribute as well as where pull-ups are assumed to be).
The *pin_class* attribute specifies which mock pin class will be generated
by the :meth:`pin` method by default. This can be changed after
construction by modifying the :attr:`pin_class` attribute.
.. attribute:: pin_class
This attribute stores the :class:`MockPin` class (or descendant) that
will be used when constructing pins with the :meth:`pin` method (if
no *pin_class* parameter is used to override it). It defaults on
construction to the value of the *pin_class* parameter in the
constructor, or :class:`MockPin` if that is unspecified.
"""
def __init__(self, revision=None, pin_class=None):
super().__init__()
if revision is None:
revision = os.environ.get('GPIOZERO_MOCK_REVISION', 'a02082')
if pin_class is None:
pin_class = os.environ.get('GPIOZERO_MOCK_PIN_CLASS', MockPin)
self._revision = int(revision, base=16)
if isinstance(pin_class, bytes):
pin_class = pin_class.decode('ascii')
if isinstance(pin_class, str):
group = entry_points(group='gpiozero_mock_pin_classes')
pin_class = group[pin_class.lower()].load()
if not issubclass(pin_class, MockPin):
raise ValueError(f'invalid mock pin_class: {pin_class!r}')
self.pin_class = pin_class
def _get_revision(self):
return self._revision
def reset(self):
"""
Clears the pins and reservations sets. This is primarily useful in
test suites to ensure the pin factory is back in a "clean" state before
the next set of tests are run.
"""
self.pins.clear()
self._reservations.clear()
def pin(self, name, pin_class=None, **kwargs):
"""
The pin method for :class:`MockFactory` additionally takes a
*pin_class* attribute which can be used to override the class'
:attr:`pin_class` attribute. Any additional keyword arguments will be
passed along to the pin constructor (useful with things like
:class:`MockConnectedPin` which expect to be constructed with another
pin).
"""
if pin_class is None:
pin_class = self.pin_class
for header, info in self.board_info.find_pin(name):
try:
pin = self.pins[info]
except KeyError:
pin = pin_class(self, info, **kwargs)
self.pins[info] = pin
else:
# Ensure the pin class expected supports PWM (or not)
if issubclass(pin_class, MockPWMPin) != isinstance(pin, MockPWMPin):
raise ValueError(
f'pin {info.name} is already in use as a '
f'{pin.__class__.__name__}')
return pin
raise PinInvalidPin(f'{name} is not a valid pin name')
def _get_spi_class(self, shared, hardware):
return MockSPIInterfaceShared if shared else MockSPIInterface
@staticmethod
def ticks():
return monotonic()
@staticmethod
def ticks_diff(later, earlier):
return later - earlier
class MockSPIInterface(SPISoftware):
pass
class MockSPIInterfaceShared(SharedMixin, MockSPIInterface):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin,
pin_factory):
return (clock_pin, select_pin)

View File

@@ -0,0 +1,619 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2015-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2020 Fangchen Li <fangchen.li@outlook.com>
# Copyright (c) 2016-2020 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
import io
import os
import sys
import mmap
import errno
import struct
import select
from time import sleep
from threading import Thread, Event, RLock
from queue import Queue, Empty
from pathlib import Path
from .local import LocalPiPin, LocalPiFactory
from ..exc import (
PinInvalidPull,
PinInvalidEdges,
PinInvalidFunction,
PinFixedPull,
PinSetInput,
)
def dt_resolve_alias(alias, root=Path('/proc/device-tree')):
"""
Returns the full :class:`~pathlib.Path` of a device-tree alias. For
example:
>>> dt_resolve_alias('gpio')
'/proc/device-tree/soc/gpio@7e200000'
>>> dt_resolve_alias('ethernet0', root='/proc/device-tree')
'/proc/device-tree/scb/ethernet@7d580000'
"""
if not isinstance(root, Path):
root = Path(root)
filename = root / 'aliases' / alias
with filename.open('rb') as f:
node, tail = f.read().split(b'\0', 1)
fs_encoding = sys.getfilesystemencoding()
return root / node.decode(fs_encoding).lstrip('/')
def dt_peripheral_reg(node, root=Path('/proc/device-tree')):
"""
Returns the :class:`range` covering the registers of the specified *node*
of the device-tree, mapped to the CPU's address space. For example:
>>> reg = dt_peripheral_reg(dt_resolve_alias('gpio'))
>>> f'{reg.start:#x}..{reg.stop:#x}'
'0xfe200000..0xfe2000b4'
>>> hex(dt_peripheral_reg(dt_resolve_alias('ethernet0')).start)
'0xfd580000'
"""
# Returns a tuple of (address-cells, size-cells) for *node*
def _cells(node):
with (node / '#address-cells').open('rb') as f:
address_cells = struct.unpack('>L', f.read())[0]
with (node / '#size-cells').open('rb') as f:
size_cells = struct.unpack('>L', f.read())[0]
return (address_cells, size_cells)
# Returns a generator function which, given a file-like object *source*
# iteratively decodes it, yielding a tuple of values from it. Each tuple
# contains one integer for each specified *length*, which is the number of
# 32-bit device-tree cells that make up that value.
def _reader(*lengths):
structs = [struct.Struct(f'>{cells}L') for cells in lengths]
offsets = [sum(s.size for s in structs[:i])
for i in range(len(structs))]
buf_len = sum(s.size for s in structs)
def fn(source):
while True:
buf = source.read(buf_len)
if not buf:
break
elif len(buf) < buf_len:
raise IOError(f'failed to read {buf_len} bytes')
row = ()
for offset, s in zip(offsets, structs):
cells = s.unpack_from(buf, offset)
value = 0
for cell in cells:
value = (value << 32) | cell
row += (value,)
yield row
return fn
# Returns a list of (child-range, parent-range) tuples for *node*
def _ranges(node):
child_cells, size_cells = _cells(node)
parent_cells, _ = _cells(node.parent)
ranges_reader = _reader(child_cells, parent_cells, size_cells)
with (node / 'ranges').open('rb') as f:
return [
(range(child_base, child_base + size),
range(parent_base, parent_base + size))
for child_base, parent_base, size in ranges_reader(f)
]
if not isinstance(root, Path):
root = Path(root)
node = root / node
child_cells, size_cells = _cells(node.parent)
reg_reader = _reader(child_cells, size_cells)
with (node / 'reg').open('rb') as f:
base, size = list(reg_reader(f))[0]
while node.parent != root:
# Iterate up the hierarchy, resolving the base address as we go
if (node.parent / 'ranges').exists():
for child_range, parent_range in _ranges(node.parent):
if base in child_range:
base += parent_range.start - child_range.start
break
node = node.parent
return range(base, base + size)
class GPIOMemory:
GPIO_BASE_OFFSET = 0x200000
PERI_BASE_OFFSET = {
'BCM2835': 0x20000000,
'BCM2836': 0x3f000000,
'BCM2837': 0x3f000000,
'BCM2711': 0xfe000000,
}
# From BCM2835 data-sheet, p.91
GPFSEL_OFFSET = 0x00 >> 2
GPSET_OFFSET = 0x1c >> 2
GPCLR_OFFSET = 0x28 >> 2
GPLEV_OFFSET = 0x34 >> 2
GPEDS_OFFSET = 0x40 >> 2
GPREN_OFFSET = 0x4c >> 2
GPFEN_OFFSET = 0x58 >> 2
GPHEN_OFFSET = 0x64 >> 2
GPLEN_OFFSET = 0x70 >> 2
GPAREN_OFFSET = 0x7c >> 2
GPAFEN_OFFSET = 0x88 >> 2
GPPUD_OFFSET = 0x94 >> 2
GPPUDCLK_OFFSET = 0x98 >> 2
# pull-control registers for BCM2711
GPPUPPDN_OFFSET = 0xe4 >> 2
def __init__(self, soc):
try:
self.fd = os.open('/dev/gpiomem', os.O_RDWR | os.O_SYNC)
except OSError:
try:
self.fd = os.open('/dev/mem', os.O_RDWR | os.O_SYNC)
except OSError:
raise IOError(
'unable to open /dev/gpiomem or /dev/mem; '
'upgrade your kernel or run as root')
else:
offset = self.gpio_base(soc)
else:
offset = 0
self.mem = mmap.mmap(self.fd, 4096, offset=offset)
# Register reads and writes must be in native format (otherwise
# struct resorts to individual byte reads/writes and you can't hit
# half a register :). For arm64 compat we have to figure out what the
# native unsigned 32-bit type is...
try:
self.reg_fmt = {
struct.calcsize(fmt): fmt
for fmt in ('@I', '@L')
}[4]
except KeyError:
raise RuntimeError('unable to find native unsigned 32-bit type')
def close(self):
self.mem.close()
os.close(self.fd)
def gpio_base(self, soc):
try:
return dt_peripheral_reg(dt_resolve_alias('gpio')).start
except IOError:
try:
return self.PERI_BASE_OFFSET[soc] + self.GPIO_BASE_OFFSET
except KeyError:
pass
raise IOError('unable to determine gpio base')
def __getitem__(self, index):
return struct.unpack_from(self.reg_fmt, self.mem, index * 4)[0]
def __setitem__(self, index, value):
struct.pack_into(self.reg_fmt, self.mem, index * 4, value)
class GPIOFS:
GPIO_PATH = '/sys/class/gpio'
def __init__(self, factory, queue):
self._lock = RLock()
self._exports = {}
self._thread = NativeWatchThread(factory, queue)
def close(self):
# We *could* track the stuff we've exported and unexport it here, but
# exports are a system global resource. We can't guarantee that some
# other process isn't relying on something we've exported. In other
# words, once exported it's *never* safe to unexport something. The
# unexport method below is largely provided for debugging and testing.
if self._thread is not None:
self._thread.close()
self._thread = None
def path(self, name):
return os.path.join(self.GPIO_PATH, name)
def path_value(self, pin):
return self.path(f'gpio{pin:d}/value')
def path_dir(self, pin):
return self.path(f'gpio{pin:d}/direction')
def path_edge(self, pin):
return self.path(f'gpio{pin:d}/edge')
def exported(self, pin):
return pin in self._exports
def export(self, pin):
with self._lock:
try:
result = self._exports[pin]
except KeyError:
result = None
# Dirty hack to wait for udev to set permissions on
# gpioN/value; there's no other way around this as there's no
# synchronous mechanism for setting permissions on sysfs
for i in range(10):
try:
# Must be O_NONBLOCK for use with epoll in edge
# triggered mode
result = os.open(self.path_value(pin),
os.O_RDONLY | os.O_NONBLOCK)
except IOError as e:
if e.errno == errno.ENOENT:
with io.open(self.path('export'), 'wb') as f:
f.write(str(pin).encode('ascii'))
elif e.errno == errno.EACCES:
sleep(i / 100)
else:
raise
else:
self._exports[pin] = result
break
# Same for gpioN/edge. It must exist by this point but the
# chmod -R may not have reached it yet...
for i in range(10):
try:
with io.open(self.path_edge(pin), 'w+b'):
pass
except IOError as e:
if e.errno == errno.EACCES:
sleep(i / 100)
else:
raise
if result is None:
raise RuntimeError(f'failed to export pin {pin:d}')
return result
def unexport(self, pin):
with self._lock:
try:
os.close(self._exports.pop(pin))
except KeyError:
# unexport should be idempotent
pass
else:
try:
with io.open(self.path('unexport'), 'wb') as f:
f.write(str(pin).encode('ascii'))
except IOError as e:
if e.errno == errno.EINVAL:
# Someone already unexported it; ignore the error
pass
def watch(self, pin):
with self._lock:
self._thread.watch(self.export(pin), pin)
def unwatch(self, pin):
with self._lock:
try:
self._thread.unwatch(self._exports[pin])
except KeyError:
pass
class NativeWatchThread(Thread):
def __init__(self, factory, queue):
super().__init__(
target=self._run, args=(factory, queue))
self.daemon = True
self._stop_evt = Event()
# XXX Make this compatible with BSDs with poll() option?
self._epoll = select.epoll()
self._watches = {}
self.start()
def close(self):
self._stop_evt.set()
self.join()
self._epoll.close()
def watch(self, fd, pin):
self._watches[fd] = pin
flags = select.EPOLLIN | select.EPOLLPRI | select.EPOLLET
self._epoll.register(fd, flags)
def unwatch(self, fd):
self._epoll.unregister(fd)
fd = self._watches.pop(fd, None)
def _run(self, factory, queue):
ticks = factory.ticks
while not self._stop_evt.wait(0):
for fd, event in self._epoll.poll(0.01):
when = ticks()
state = os.read(fd, 1) == b'1'
os.lseek(fd, 0, 0)
try:
queue.put((self._watches[fd], when, state))
except KeyError:
pass
class NativeDispatchThread(Thread):
def __init__(self, factory, queue):
super().__init__(
target=self._run, args=(factory, queue))
self.daemon = True
self._stop_evt = Event()
self.start()
def close(self):
self._stop_evt.set()
self.join()
def _run(self, factory, queue):
pins = factory.pins
while not self._stop_evt.wait(0):
try:
num, ticks, state = queue.get(timeout=0.1)
except Empty:
continue
try:
pin = pins[num]
except KeyError:
pass
else:
if (
pin._bounce is None or pin._last_call is None or
factory.ticks_diff(ticks, pin._last_call) > pin._bounce
):
pin._call_when_changed(ticks, state)
pin._last_call = ticks
class NativeFactory(LocalPiFactory):
"""
Extends :class:`~gpiozero.pins.local.LocalPiFactory`. Uses a built-in pure
Python implementation to interface to the Pi's GPIO pins. This is the
default pin implementation if no third-party libraries are discovered.
.. warning::
This implementation does *not* currently support PWM. Attempting to
use any class which requests PWM will raise an exception.
You can construct native pin instances manually like so::
from gpiozero.pins.native import NativeFactory
from gpiozero import LED
factory = NativeFactory()
led = LED(12, pin_factory=factory)
"""
def __init__(self):
super().__init__()
queue = Queue()
self.mem = GPIOMemory(self.board_info.soc)
self.fs = GPIOFS(self, queue)
self.dispatch = NativeDispatchThread(self, queue)
if self.board_info.soc == 'BCM2711':
self.pin_class = Native2711Pin
else:
self.pin_class = Native2835Pin
def close(self):
if self.dispatch is not None:
self.dispatch.close()
self.dispatch = None
super().close()
if self.fs is not None:
self.fs.close()
self.fs = None
if self.mem is not None:
self.mem.close()
self.mem = None
class NativePin(LocalPiPin):
"""
Extends :class:`~gpiozero.pins.local.LocalPiPin`. Native pin
implementation. See :class:`NativeFactory` for more information.
"""
GPIO_FUNCTIONS = {
'input': 0b000,
'output': 0b001,
'alt0': 0b100,
'alt1': 0b101,
'alt2': 0b110,
'alt3': 0b111,
'alt4': 0b011,
'alt5': 0b010,
}
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
def __init__(self, factory, info):
super().__init__(factory, info)
self._reg_init(factory, self._number)
self._last_call = None
self._when_changed = None
self._change_thread = None
self._change_event = Event()
self.function = 'input'
self.pull = info.pull or 'floating'
self.bounce = None
self.edges = 'none'
def _reg_init(self, factory, number):
self._func_offset = self.factory.mem.GPFSEL_OFFSET + (number // 10)
self._func_shift = (number % 10) * 3
self._set_offset = self.factory.mem.GPSET_OFFSET + (number // 32)
self._set_shift = number % 32
self._clear_offset = self.factory.mem.GPCLR_OFFSET + (number // 32)
self._clear_shift = number % 32
self._level_offset = self.factory.mem.GPLEV_OFFSET + (number // 32)
self._level_shift = number % 32
self._edge_offset = self.factory.mem.GPEDS_OFFSET + (number // 32)
self._edge_shift = number % 32
self._rising_offset = self.factory.mem.GPREN_OFFSET + (number // 32)
self._rising_shift = number % 32
self._falling_offset = self.factory.mem.GPFEN_OFFSET + (number // 32)
self._falling_shift = number % 32
def close(self):
self.edges = 'none'
self.frequency = None
self.when_changed = None
self.function = 'input'
self.pull = self.info.pull or 'floating'
def _get_function(self):
return self.GPIO_FUNCTION_NAMES[(self.factory.mem[self._func_offset] >> self._func_shift) & 7]
def _set_function(self, value):
try:
value = self.GPIO_FUNCTIONS[value]
except KeyError:
raise PinInvalidFunction(
f'invalid function "{value}" for pin {self!r}')
self.factory.mem[self._func_offset] = (
self.factory.mem[self._func_offset]
& ~(7 << self._func_shift)
| (value << self._func_shift)
)
def _get_state(self):
return bool(self.factory.mem[self._level_offset] & (1 << self._level_shift))
def _set_state(self, value):
if self.function == 'input':
raise PinSetInput(f'cannot set state of pin {self!r}')
if value:
self.factory.mem[self._set_offset] = 1 << self._set_shift
else:
self.factory.mem[self._clear_offset] = 1 << self._clear_shift
def _get_pull(self):
raise NotImplementedError
def _set_pull(self, value):
raise NotImplementedError
def _get_bounce(self):
return self._bounce
def _set_bounce(self, value):
self._bounce = None if value is None else float(value)
def _get_edges(self):
try:
with io.open(self.factory.fs.path_edge(self._number), 'r') as f:
return f.read().strip()
except IOError as e:
if e.errno == errno.ENOENT:
return 'none'
else:
raise
def _set_edges(self, value):
if value != 'none':
self.factory.fs.export(self._number)
try:
with io.open(self.factory.fs.path_edge(self._number), 'w') as f:
f.write(value)
except IOError as e:
if e.errno == errno.ENOENT and value == 'none':
pass
elif e.errno == errno.EINVAL:
raise PinInvalidEdges(
f'invalid edge specification "{value}" for pin {self!r}')
else:
raise
def _enable_event_detect(self):
self.factory.fs.watch(self._number)
self._last_call = None
def _disable_event_detect(self):
self.factory.fs.unwatch(self._number)
class Native2835Pin(NativePin):
"""
Extends :class:`NativePin` for Pi hardware prior to the Pi 4 (Pi 0, 1, 2,
3, and 3+).
"""
GPIO_PULL_UPS = {
'up': 0b10,
'down': 0b01,
'floating': 0b00,
}
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
def _reg_init(self, factory, number):
super()._reg_init(factory, number)
self._pull_offset = self.factory.mem.GPPUDCLK_OFFSET + (number // 32)
self._pull_shift = number % 32
self._pull = 'floating'
def _get_pull(self):
return self.GPIO_PULL_UP_NAMES[self._pull]
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull not in (value, ''):
raise PinFixedPull(
f'{self!r} has a physical pull-{self.info.pull} resistor')
try:
value = self.GPIO_PULL_UPS[value]
except KeyError:
raise PinInvalidPull(
f'invalid pull direction "{value}" for pin {self!r}')
self.factory.mem[self.factory.mem.GPPUD_OFFSET] = value
sleep(0.000000214)
self.factory.mem[self._pull_offset] = 1 << self._pull_shift
sleep(0.000000214)
self.factory.mem[self.factory.mem.GPPUD_OFFSET] = 0
self.factory.mem[self._pull_offset] = 0
self._pull = value
class Native2711Pin(NativePin):
"""
Extends :class:`NativePin` for Pi 4 hardware (Pi 4, CM4, Pi 400 at the time
of writing).
"""
GPIO_PULL_UPS = {
'up': 0b01,
'down': 0b10,
'floating': 0b00,
}
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
def _reg_init(self, factory, number):
super()._reg_init(factory, number)
self._pull_offset = self.factory.mem.GPPUPPDN_OFFSET + (number // 16)
self._pull_shift = (number % 16) * 2
def _get_pull(self):
pull = (self.factory.mem[self._pull_offset] >> self._pull_shift) & 3
return self.GPIO_PULL_UP_NAMES[pull]
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull not in (value, ''):
raise PinFixedPull(
f'{self!r} has a physical pull-{self.info.pull} resistor')
try:
value = self.GPIO_PULL_UPS[value]
except KeyError:
raise PinInvalidPull(
f'invalid pull direction "{value}" for pin {self!r}')
self.factory.mem[self._pull_offset] = (
self.factory.mem[self._pull_offset]
& ~(3 << self._pull_shift)
| (value << self._pull_shift)
)

View File

@@ -0,0 +1,693 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2020 Fangchen Li <fangchen.li@outlook.com>
#
# SPDX-License-Identifier: BSD-3-Clause
import re
from threading import RLock
from types import MethodType
from weakref import ref, WeakMethod
import warnings
try:
from spidev import SpiDev
except ImportError:
SpiDev = None
from . import Factory, Pin, BoardInfo, HeaderInfo, PinInfo, data
from .data import SPI_HARDWARE_PINS
from ..compat import frozendict
from ..devices import Device
from ..exc import (
GPIOPinInUse,
PinInvalidPin,
PinNoPins,
PinNonPhysical,
PinUnknownPi,
SPIBadArgs,
SPISoftwareFallback,
)
def spi_port_device(clock_pin, mosi_pin, miso_pin, select_pin):
"""
Convert a mapping of pin definitions, which must contain 'clock_pin', and
'select_pin' at a minimum, to a hardware SPI port, device tuple. Raises
:exc:`~gpiozero.SPIBadArgs` if the pins do not represent a valid hardware
SPI device.
"""
for port, pins in SPI_HARDWARE_PINS.items():
if all((
clock_pin == pins['clock'],
mosi_pin in (None, pins['mosi']),
miso_pin in (None, pins['miso']),
select_pin in pins['select'],
)):
device = pins['select'].index(select_pin)
return (port, device)
raise SPIBadArgs('invalid pin selection for hardware SPI')
class PiBoardInfo(BoardInfo):
__slots__ = () # workaround python issue #24931
@classmethod
def from_revision(cls, revision):
"""
Construct a :class:`PiBoardInfo` instance from the specified Raspberry
Pi board *revision* which must be specified as an :class:`int`
(typically in hexi-decimal format).
For example, from an old-style revision code for the model B+::
>>> from gpiozero.pins.pi import PiBoardInfo
>>> PiBoardInfo.from_revision(0x0010)
PiBoardInfo(revision='0010', model='B+', pcb_revision='1.2',
released='2014Q3', soc='BCM2835', manufacturer='Sony', memory=512,
storage='MicroSD', usb=4, usb3=0, ethernet=1, eth_speed=100,
wifi=False, bluetooth=False, csi=1, dsi=1, headers=..., board=...)
Or from a new-style revision code for the Pi Zero 2W::
>>> PiBoardInfo.from_revision(0x902120)
PiBoardInfo(revision='902120', model='Zero2W', pcb_revision='1.0',
released='2021Q4', soc='BCM2837', manufacturer='Sony', memory=512,
storage='MicroSD', usb=1, usb3=0, ethernet=0, eth_speed=0,
wifi=True, bluetooth=True, csi=1, dsi=0, headers=..., board=...)
"""
if revision & 0x800000:
# New-style revision, parse information from bit-pattern:
#
# MSB -----------------------> LSB
# NOQuuuWuFMMMCCCCPPPPTTTTTTTTRRRR
#
# N - Overvoltage (0=allowed, 1=disallowed)
# O - OTP programming (0=allowed, 1=disallowed)
# Q - OTP read (0=allowed, 1=disallowed)
# u - Unused
# W - Warranty bit (0=intact, 1=voided by overclocking)
# F - New flag (1=valid new-style revision, 0=old-style)
# MMM - Memory size (see memory dict below)
# CCCC - Manufacturer (see manufacturer dict below)
# PPPP - Processor (see soc dict below)
# TTTTTTTT - Type (see model dict below)
# RRRR - Revision (0, 1, 2, etc.)
revcode_memory = (revision & 0x700000) >> 20
revcode_manufacturer = (revision & 0xf0000) >> 16
revcode_processor = (revision & 0xf000) >> 12
revcode_type = (revision & 0xff0) >> 4
revcode_revision = (revision & 0x0f)
model = {
0x0: 'A',
0x1: 'B',
0x2: 'A+',
0x3: 'B+',
0x4: '2B',
0x6: 'CM',
0x8: '3B',
0x9: 'Zero',
0xa: 'CM3',
0xc: 'Zero W',
0xd: '3B+',
0xe: '3A+',
0x10: 'CM3+',
0x11: '4B',
0x12: 'Zero2W',
0x13: '400',
0x14: 'CM4',
0x17: '5B',
}.get(revcode_type, '???')
if model in ('A', 'B'):
pcb_revision = {
0: '1.0', # is this right?
1: '1.0',
2: '2.0',
}.get(revcode_revision, 'Unknown')
else:
pcb_revision = f'1.{revcode_revision}'
soc = {
0: 'BCM2835',
1: 'BCM2836',
2: 'BCM2837',
3: 'BCM2711',
4: 'BCM2712',
}.get(revcode_processor, 'Unknown')
manufacturer = {
0: 'Sony',
1: 'Egoman',
2: 'Embest',
3: 'Sony Japan',
4: 'Embest',
5: 'Stadium',
}.get(revcode_manufacturer, 'Unknown')
memory = {
0: 256,
1: 512,
2: 1024,
3: 2048,
4: 4096,
5: 8192,
6: 16384,
}.get(revcode_memory, None)
released = {
'A': '2013Q1',
'B': '2012Q1' if pcb_revision == '1.0' else '2012Q4',
'A+': '2014Q4' if memory == 512 else '2016Q3',
'B+': '2014Q3',
'2B': '2015Q1' if pcb_revision in ('1.0', '1.1') else '2016Q3',
'CM': '2014Q2',
'3B': '2016Q1' if manufacturer in ('Sony', 'Embest') else '2016Q4',
'Zero': '2015Q4' if pcb_revision == '1.2' else '2016Q2',
'CM3': '2017Q1',
'Zero W': '2017Q1',
'3B+': '2018Q1',
'3A+': '2018Q4',
'CM3+': '2019Q1',
'4B': '2020Q2' if memory == 8192 else '2019Q2',
'CM4': '2020Q4',
'400': '2020Q4',
'Zero2W': '2021Q4',
'5B': '2023Q4',
}.get(model, 'Unknown')
storage = {
'A': 'SD',
'B': 'SD',
'CM': 'eMMC',
'CM3': 'eMMC / off-board',
'CM3+': 'eMMC / off-board',
'CM4': 'eMMC / off-board',
}.get(model, 'MicroSD')
usb = {
'A': 1,
'A+': 1,
'Zero': 1,
'Zero W': 1,
'Zero2W': 1,
'B': 2,
'CM': 1,
'CM3': 1,
'3A+': 1,
'CM3+': 1,
'CM4': 2,
'400': 3,
}.get(model, 4)
usb3 = {
'4B': 2,
'400': 2,
'5B': 2,
}.get(model, 0)
ethernet = {
'A': 0,
'A+': 0,
'Zero': 0,
'Zero W': 0,
'Zero2W': 0,
'CM': 0,
'CM3': 0,
'3A+': 0,
'CM3+': 0,
}.get(model, 1)
eth_speed = {
'B': 100,
'B+': 100,
'2B': 100,
'3B': 100,
'3B+': 300,
'4B': 1000,
'400': 1000,
'CM4': 1000,
'5B': 1000,
}.get(model, 0)
bluetooth = wifi = {
'3B': True,
'Zero W': True,
'Zero2W': True,
'3B+': True,
'3A+': True,
'4B': True,
'400': True,
'CM4': True,
'5B': True,
}.get(model, False)
csi = {
'Zero': 0 if pcb_revision == '1.2' else 1,
'CM': 2,
'CM3': 2,
'CM3+': 2,
'400': 0,
'CM4': 2,
'5B': 2,
}.get(model, 1)
dsi = {
'Zero': 0,
'Zero W': 0,
'Zero2W': 0,
'5B': 2,
}.get(model, csi)
headers = {
'A': {'P1': data.REV2_P1, 'P5': data.REV2_P5, 'P6': data.REV2_P6, 'P2': data.PI1_P2, 'P3': data.PI1_P3},
'B': {'P1': data.REV1_P1, 'P2': data.PI1_P2, 'P3': data.PI1_P3} if pcb_revision == '1.0' else
{'P1': data.REV2_P1, 'P5': data.REV2_P5, 'P6': data.REV2_P6, 'P2': data.PI1_P2, 'P3': data.PI1_P3},
'B+': {'J8': data.PLUS_J8, 'RUN': data.ZERO_RUN},
'CM': {'SODIMM': data.CM_SODIMM},
'CM3': {'SODIMM': data.CM3_SODIMM},
'CM3+': {'SODIMM': data.CM3_SODIMM},
'Zero': {'J8': data.PLUS_J8, 'RUN': data.ZERO_RUN, 'TV': data.ZERO_TV},
'Zero W': {'J8': data.PLUS_J8, 'RUN': data.ZERO_RUN, 'TV': data.ZERO_TV},
'2B': {'J8': data.PLUS_J8, 'RUN': data.ZERO_RUN},
'3B': {'J8': data.PLUS_J8, 'RUN': data.ZERO_RUN},
'3A+': {'J8': data.PLUS_J8, 'RUN': data.PLUS_RUN},
'3B+': {'J8': data.PLUS_J8, 'RUN': data.PLUS_RUN, 'POE': data.PLUS_POE},
'4B': {'J8': data.PI4_J8, 'J2': data.PI4_J2, 'J14': data.PI4_J14},
'400': {'J8': data.PI4_J8},
'CM4': {'J8': data.PI4_J8, 'J1': data.CM4_J1, 'J2': data.CM4_J2, 'J3': data.CM4_J3, 'J6': data.CM4_J6, 'J9': data.CM4_J9},
'5B': {'J8': data.PI4_J8, 'J2': data.PI5_J2, 'J7': data.PI5_J7, 'J14': data.PI4_J14},
}.get(model, {'J8': data.PLUS_J8})
board = {
'A': data.A_BOARD,
'B': data.REV1_BOARD if pcb_revision == '1.0' else data.REV2_BOARD,
'A+': data.APLUS_BOARD,
'CM': data.CM_BOARD,
'CM3': data.CM_BOARD,
'CM3+': data.CM3PLUS_BOARD,
'Zero': data.ZERO12_BOARD if pcb_revision == '1.2' else data.ZERO13_BOARD,
'Zero W': data.ZERO13_BOARD,
'Zero2W': data.ZERO2_BOARD,
'3A+': data.A3PLUS_BOARD,
'3B+': data.B3PLUS_BOARD,
'4B': data.B4_BOARD,
'CM4': data.CM4_BOARD,
'400': data.P400_BOARD,
'5B': data.B5_BOARD,
}.get(model, data.BPLUS_BOARD)
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,
) = data.PI_REVISIONS[revision]
usb3 = 0
eth_speed = ethernet * 100
except KeyError:
raise PinUnknownPi(f'unknown old-style revision "{revision:x}"')
headers = frozendict({
header: HeaderInfo(
name=header, rows=rows, columns=columns,
pins=frozendict({
number: cls._make_pin(
header, number, row + 1, col + 1, functions)
for number, functions in header_data.items()
for row, col in (divmod(number - 1, 2),)
})
)
for header, (rows, columns, header_data) in headers.items()
})
return cls(
f'{revision:04x}',
model,
pcb_revision,
released,
soc,
manufacturer,
memory,
storage,
usb,
usb3,
ethernet,
eth_speed,
wifi,
bluetooth,
csi,
dsi,
headers,
board,
)
@staticmethod
def _make_pin(header, number, row, col, interfaces):
pull = 'up' if number in (3, 5) and header in ('P1', 'J8') else ''
phys_name = f'{header}:{number}'
names = {phys_name}
if header in ('P1', 'J8', 'SODIMM'):
names.add(f'BOARD{number}')
try:
name = interfaces['gpio']
gpio = int(name[4:])
names.add(name)
names.add(gpio)
names.add(str(gpio))
names.add(f'BCM{gpio}')
try:
wpi_map = {
'J8:3': 8, 'J8:5': 9, 'J8:7': 7, 'J8:8': 15,
'J8:10': 16, 'J8:11': 0, 'J8:12': 1, 'J8:13': 2,
'J8:15': 3, 'J8:16': 4, 'J8:18': 5, 'J8:19': 12,
'J8:21': 13, 'J8:22': 6, 'J8:23': 14, 'J8:24': 10,
'J8:26': 11, 'J8:27': 30, 'J8:28': 31, 'J8:29': 21,
'J8:31': 22, 'J8:32': 26, 'J8:33': 23, 'J8:35': 24,
'J8:36': 27, 'J8:37': 25, 'J8:38': 28, 'J8:40': 29,
'P1:3': 8, 'P1:5': 9, 'P1:7': 7, 'P1:8': 15,
'P1:10': 16, 'P1:11': 0, 'P1:12': 1, 'P1:13': 2,
'P1:15': 3, 'P1:16': 4, 'P1:18': 5, 'P1:19': 12,
'P1:21': 13, 'P1:22': 6, 'P1:23': 14, 'P1:24': 10,
'P1:26': 11, 'P1:27': 30, 'P1:28': 31, 'P1:29': 21,
'P1:31': 22, 'P1:32': 26, 'P1:33': 23, 'P1:35': 24,
'P1:36': 27, 'P1:37': 25, 'P1:38': 28, 'P1:40': 29,
'P5:3': 17, 'P5:4': 18, 'P5:5': 19, 'P5:6': 20,
}
names.add(f'WPI{wpi_map[phys_name]}')
except KeyError:
pass
except KeyError:
name = interfaces['']
names.add(name)
return PinInfo(
number=number, name=name, names=frozenset(names), pull=pull,
row=row, col=col, interfaces=frozenset(interfaces))
@property
def description(self):
return f"Raspberry Pi {self.model} rev {self.pcb_revision}"
class PiFactory(Factory):
"""
Extends :class:`~gpiozero.Factory`. Abstract base class representing
hardware attached to a Raspberry Pi. This forms the base of
:class:`~gpiozero.pins.local.LocalPiFactory`.
"""
def __init__(self):
super().__init__()
self._info = None
self.pins = {}
self.pin_class = None
def close(self):
for pin in self.pins.values():
pin.close()
self.pins.clear()
def pin(self, name):
for header, info in self.board_info.find_pin(name):
try:
pin = self.pins[info]
except KeyError:
pin = self.pin_class(self, info)
self.pins[info] = pin
return pin
raise PinInvalidPin(f'{name} is not a valid pin name')
def _get_revision(self):
"""
This method must be overridden by descendents to return the Pi's
revision code as an :class:`int`. The default is unimplemented.
"""
raise NotImplementedError
def _get_board_info(self):
if self._info is None:
self._info = PiBoardInfo.from_revision(self._get_revision())
return self._info
def spi(self, **spi_args):
"""
Returns an SPI interface, for the specified SPI *port* and *device*, or
for the specified pins (*clock_pin*, *mosi_pin*, *miso_pin*, and
*select_pin*). Only one of the schemes can be used; attempting to mix
*port* and *device* with pin numbers will raise
:exc:`~gpiozero.SPIBadArgs`.
If the pins specified match the hardware SPI pins (clock on GPIO11,
MOSI on GPIO10, MISO on GPIO9, and chip select on GPIO8 or GPIO7), and
the spidev module can be imported, a hardware based interface (using
spidev) will be returned. Otherwise, a software based interface will be
returned which will use simple bit-banging to communicate.
Both interfaces have the same API, support clock polarity and phase
attributes, and can handle half and full duplex communications, but the
hardware interface is significantly faster (though for many simpler
devices this doesn't matter).
"""
spi_args, kwargs = self._extract_spi_args(**spi_args)
shared = bool(kwargs.pop('shared', False))
if kwargs:
raise SPIBadArgs(
f'unrecognized keyword argument {kwargs.popitem()[0]}')
try:
port, device = spi_port_device(**spi_args)
except SPIBadArgs:
# Assume request is for a software SPI implementation
pass
else:
try:
return self._get_spi_class(shared, hardware=True)(
pin_factory=self, **spi_args)
except GPIOPinInUse:
# If a pin is already reserved, don't fallback to software SPI
# as it'll just be reserved there too
raise
except Exception as e:
warnings.warn(
SPISoftwareFallback(
f'failed to initialize hardware SPI, falling back to '
f'software (error was: {e!s})'))
return self._get_spi_class(shared, hardware=False)(
pin_factory=self, **spi_args)
def _extract_spi_args(self, **kwargs):
"""
Given a set of keyword arguments, splits it into those relevant to SPI
implementations and all the rest. SPI arguments are augmented with
defaults and converted into the pin format (from the port/device
format) if necessary.
Returns a tuple of ``(spi_args, other_args)``.
"""
dev_defaults = {
'port': 0,
'device': 0,
}
default_hw = SPI_HARDWARE_PINS[dev_defaults['port']]
pin_defaults = {
'clock_pin': default_hw['clock'],
'mosi_pin': default_hw['mosi'],
'miso_pin': default_hw['miso'],
'select_pin': default_hw['select'][dev_defaults['device']],
}
spi_args = {
key: value for (key, value) in kwargs.items()
if key in pin_defaults or key in dev_defaults
}
kwargs = {
key: value for (key, value) in kwargs.items()
if key not in spi_args
}
if not spi_args:
spi_args = pin_defaults
elif set(spi_args) <= set(pin_defaults):
spi_args = {
key: None if spi_args.get(key, default) is None else
self.board_info.to_gpio(spi_args.get(key, default))
for key, default in pin_defaults.items()
}
elif set(spi_args) <= set(dev_defaults):
spi_args = {
key: spi_args.get(key, default)
for key, default in dev_defaults.items()
}
try:
selected_hw = SPI_HARDWARE_PINS[spi_args['port']]
except KeyError:
raise SPIBadArgs(
f"port {spi_args['port']} is not a valid SPI port")
try:
selected_hw['select'][spi_args['device']]
except IndexError:
raise SPIBadArgs(
f"device must be in the range 0.."
f"{len(selected_hw['select'])}")
# XXX: This is incorrect; assumes port == dev_defaults['port']
spi_args = {
key: value if key != 'select_pin' else selected_hw['select'][spi_args['device']]
for key, value in pin_defaults.items()
}
else:
raise SPIBadArgs(
'you must either specify port and device, or clock_pin, '
'mosi_pin, miso_pin, and select_pin; combinations of the two '
'schemes (e.g. port and clock_pin) are not permitted')
return spi_args, kwargs
def _get_spi_class(self, shared, hardware):
"""
Returns a sub-class of the :class:`SPI` which can be constructed with
*clock_pin*, *mosi_pin*, *miso_pin*, and *select_pin* arguments. The
*shared* argument dictates whether the returned class uses the
:class:`SharedMixin` to permit sharing instances between components,
while *hardware* indicates whether the returned class uses the kernel's
SPI device(s) rather than a bit-banged software implementation.
"""
raise NotImplementedError
class PiPin(Pin):
"""
Extends :class:`~gpiozero.Pin`. Abstract base class representing a
multi-function GPIO pin attached to a Raspberry Pi. Descendents *must*
override the following methods:
* :meth:`_get_function`
* :meth:`_set_function`
* :meth:`_get_state`
* :meth:`_call_when_changed`
* :meth:`_enable_event_detect`
* :meth:`_disable_event_detect`
Descendents *may* additionally override the following methods, if
applicable:
* :meth:`close`
* :meth:`output_with_state`
* :meth:`input_with_pull`
* :meth:`_set_state`
* :meth:`_get_frequency`
* :meth:`_set_frequency`
* :meth:`_get_pull`
* :meth:`_set_pull`
* :meth:`_get_bounce`
* :meth:`_set_bounce`
* :meth:`_get_edges`
* :meth:`_set_edges`
"""
def __init__(self, factory, info):
super().__init__()
if 'gpio' not in info.interfaces:
raise PinInvalidPin(f'{info} is not a GPIO pin')
self._factory = factory
self._info = info
self._number = int(info.name[4:])
self._when_changed_lock = RLock()
self._when_changed = None
@property
def info(self):
return self._info
@property
def number(self):
warnings.warn(
DeprecationWarning(
"PiPin.number is deprecated; please use Pin.info.name instead"))
return self._number
def __repr__(self):
return self._info.name
@property
def factory(self):
return self._factory
def _call_when_changed(self, ticks, state):
"""
Called to fire the :attr:`when_changed` event handler; override this
in descendents if additional (currently redundant) parameters need
to be passed.
"""
method = self._when_changed()
if method is None:
self.when_changed = None
else:
method(ticks, state)
def _get_when_changed(self):
return None if self._when_changed is None else self._when_changed()
def _set_when_changed(self, value):
with self._when_changed_lock:
if value is None:
if self._when_changed is not None:
self._disable_event_detect()
self._when_changed = None
else:
enabled = self._when_changed is not None
# Have to take care, if value is either a closure or a bound
# method, not to keep a strong reference to the containing
# object
if isinstance(value, MethodType):
self._when_changed = WeakMethod(value)
else:
self._when_changed = ref(value)
if not enabled:
self._enable_event_detect()
def _enable_event_detect(self):
"""
Enables event detection. This is called to activate event detection on
pin :attr:`number`, watching for the specified :attr:`edges`. In
response, :meth:`_call_when_changed` should be executed.
"""
raise NotImplementedError
def _disable_event_detect(self):
"""
Disables event detection. This is called to deactivate event detection
on pin :attr:`number`.
"""
raise NotImplementedError
def pi_info(revision=None):
"""
Deprecated function for retrieving information about a *revision* of the
Raspberry Pi. If you wish to retrieve information about the board that your
script is running on, please query the :attr:`Factory.board_info` property
like so::
>>> from gpiozero import Device
>>> Device.ensure_pin_factory()
>>> Device.pin_factory.board_info
PiBoardInfo(revision='a02082', model='3B', pcb_revision='1.2',
released='2016Q1', soc='BCM2837', manufacturer='Sony', memory=1024,
storage='MicroSD', usb=4, usb3=0, ethernet=1, eth_speed=100, wifi=True,
bluetooth=True, csi=1, dsi=1, headers=..., board=...)
To obtain information for a specific Raspberry Pi board revision, use the
:meth:`PiBoardInfo.from_revision` constructor.
:param str revision:
The revision of the Pi to return information about. If this is omitted
or :data:`None` (the default), then the library will attempt to
determine the model of Pi it is running on and return information about
that.
"""
if revision is None:
if Device.pin_factory is None:
Device.pin_factory = Device._default_pin_factory()
return Device.pin_factory.board_info
else:
if isinstance(revision, bytes):
revision = revision.decode('ascii')
if isinstance(revision, str):
revision = int(revision, base=16)
else:
# be nice to people passing an int (or something numeric anyway)
revision = int(revision)
return PiBoardInfo.from_revision(revision)

View File

@@ -0,0 +1,600 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2021 Kyle Morgan <kyle@knmorgan.net>
# Copyright (c) 2020 Ben Nuttall <ben@bennuttall.com>
# Copyright (c) 2019 Maksim Levental <maksim.levental@gmail.com>
# Copyright (c) 2019 Aaron Rogers <aaron.kyle.rogers@gmail.com>
# Copyright (c) 2016 BuildTools <david.glaude@gmail.com>
# Copyright (c) 2016 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
import os
import pigpio
from . import SPI
from .pi import PiPin, PiFactory, spi_port_device
from ..mixins import SharedMixin
from ..exc import (
PinInvalidFunction,
PinSetInput,
PinFixedPull,
PinInvalidPull,
PinInvalidBounce,
PinInvalidState,
SPIBadArgs,
SPIInvalidClockMode,
PinPWMFixedValue,
DeviceClosed
)
class PiGPIOFactory(PiFactory):
"""
Extends :class:`~gpiozero.pins.pi.PiFactory`. Uses the `pigpio`_ library to
interface to the Pi's GPIO pins. The pigpio library relies on a daemon
(:command:`pigpiod`) to be running as root to provide access to the GPIO
pins, and communicates with this daemon over a network socket.
While this does mean only the daemon itself should control the pins, the
architecture does have several advantages:
* Pins can be remote controlled from another machine (the other
machine doesn't even have to be a Raspberry Pi; it simply needs the
`pigpio`_ client library installed on it)
* The daemon supports hardware PWM via the DMA controller
* Your script itself doesn't require root privileges; it just needs to
be able to communicate with the daemon
You can construct pigpio pins manually like so::
from gpiozero.pins.pigpio import PiGPIOFactory
from gpiozero import LED
factory = PiGPIOFactory()
led = LED(12, pin_factory=factory)
This is particularly useful for controlling pins on a remote machine. To
accomplish this simply specify the host (and optionally port) when
constructing the pin::
from gpiozero.pins.pigpio import PiGPIOFactory
from gpiozero import LED
factory = PiGPIOFactory(host='192.168.0.2')
led = LED(12, pin_factory=factory)
.. note::
In some circumstances, especially when playing with PWM, it does appear
to be possible to get the daemon into "unusual" states. We would be
most interested to hear any bug reports relating to this (it may be a
bug in our pin implementation). A workaround for now is simply to
restart the :command:`pigpiod` daemon.
.. _pigpio: http://abyz.me.uk/rpi/pigpio/
"""
def __init__(self, host=None, port=None):
super().__init__()
if host is None:
host = os.environ.get('PIGPIO_ADDR', 'localhost')
if port is None:
# XXX Use getservbyname
port = int(os.environ.get('PIGPIO_PORT', 8888))
self.pin_class = PiGPIOPin
self._connection = pigpio.pi(host, port)
# Annoyingly, pigpio doesn't raise an exception when it fails to make a
# connection; it returns a valid (but disconnected) pi object
if self.connection is None:
raise IOError(f'failed to connect to {host}:{port}')
self._host = host
self._port = port
self._spis = []
def close(self):
super().close()
# We *have* to keep track of SPI interfaces constructed with pigpio;
# if we fail to close them they prevent future interfaces from using
# the same pins
if self.connection:
while self._spis:
self._spis[0].close()
self.connection.stop()
self._connection = None
@property
def connection(self):
# If we're shutting down, the connection may have disconnected itself
# already. Unfortunately, the connection's "connected" property is
# rather buggy - disconnecting doesn't set it to False! So we're
# naughty and check an internal variable instead...
try:
if self._connection.sl.s is not None:
return self._connection
except AttributeError:
pass
@property
def host(self):
return self._host
@property
def port(self):
return self._port
def _get_revision(self):
return self.connection.get_hardware_revision()
def _get_spi_class(self, shared, hardware):
return {
(False, True): PiGPIOHardwareSPI,
(True, True): PiGPIOHardwareSPIShared,
(False, False): PiGPIOSoftwareSPI,
(True, False): PiGPIOSoftwareSPIShared,
}[shared, hardware]
def spi(self, **spi_args):
intf = super().spi(**spi_args)
self._spis.append(intf)
return intf
def ticks(self):
return self._connection.get_current_tick()
@staticmethod
def ticks_diff(later, earlier):
# NOTE: pigpio ticks are unsigned 32-bit quantities that wrap every
# 71.6 minutes. The modulo below (oh the joys of having an *actual*
# modulo operator, unlike C's remainder) ensures the result is valid
# even when later < earlier due to wrap-around (assuming the duration
# measured is not longer than the period)
return ((later - earlier) % 0x100000000) / 1000000
class PiGPIOPin(PiPin):
"""
Extends :class:`~gpiozero.pins.pi.PiPin`. Pin implementation for the
`pigpio`_ library. See :class:`PiGPIOFactory` for more information.
.. _pigpio: http://abyz.me.uk/rpi/pigpio/
"""
GPIO_FUNCTIONS = {
'input': pigpio.INPUT,
'output': pigpio.OUTPUT,
'alt0': pigpio.ALT0,
'alt1': pigpio.ALT1,
'alt2': pigpio.ALT2,
'alt3': pigpio.ALT3,
'alt4': pigpio.ALT4,
'alt5': pigpio.ALT5,
}
GPIO_PULL_UPS = {
'up': pigpio.PUD_UP,
'down': pigpio.PUD_DOWN,
'floating': pigpio.PUD_OFF,
}
GPIO_EDGES = {
'both': pigpio.EITHER_EDGE,
'rising': pigpio.RISING_EDGE,
'falling': pigpio.FALLING_EDGE,
}
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
def __init__(self, factory, info):
super().__init__(factory, info)
self._pull = info.pull or 'floating'
self._pwm = False
self._bounce = None
self._callback = None
self._edges = pigpio.EITHER_EDGE
try:
self.factory.connection.set_mode(self._number, pigpio.INPUT)
except pigpio.error as e:
raise ValueError(e)
self.factory.connection.set_pull_up_down(
self._number, self.GPIO_PULL_UPS[self._pull])
self.factory.connection.set_glitch_filter(self._number, 0)
def close(self):
if self.factory.connection:
self.frequency = None
self.when_changed = None
self.function = 'input'
self.pull = self.info.pull or 'floating'
def _get_function(self):
return self.GPIO_FUNCTION_NAMES[
self.factory.connection.get_mode(self._number)]
def _set_function(self, value):
if value != 'input':
self._pull = 'floating'
try:
self.factory.connection.set_mode(
self._number, self.GPIO_FUNCTIONS[value])
except KeyError:
raise PinInvalidFunction(
f'invalid function "{value}" for pin {self!r}')
def _get_state(self):
if self._pwm:
return (
self.factory.connection.get_PWM_dutycycle(self._number) /
self.factory.connection.get_PWM_range(self._number)
)
else:
return bool(self.factory.connection.read(self._number))
def _set_state(self, value):
if self._pwm:
try:
value = int(value * self.factory.connection.get_PWM_range(self._number))
if value != self.factory.connection.get_PWM_dutycycle(self._number):
self.factory.connection.set_PWM_dutycycle(self._number, value)
except pigpio.error:
raise PinInvalidState(
f'invalid state "{value}" for pin {self!r}')
elif self.function == 'input':
raise PinSetInput(f'cannot set state of pin {self!r}')
else:
# write forces pin to OUTPUT, hence the check above
self.factory.connection.write(self._number, bool(value))
def _get_pull(self):
return self._pull
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull and value != self.info.pull:
raise PinFixedPull(f'{self!r} has a fixed pull resistor')
try:
self.factory.connection.set_pull_up_down(
self._number, self.GPIO_PULL_UPS[value])
self._pull = value
except KeyError:
raise PinInvalidPull(f'invalid pull "{value}" for pin {self!r}')
def _get_frequency(self):
if self._pwm:
return self.factory.connection.get_PWM_frequency(self._number)
return None
def _set_frequency(self, value):
if not self._pwm and value is not None:
if self.function != 'output':
raise PinPWMFixedValue(f'cannot start PWM on pin {self!r}')
# NOTE: the pin's state *must* be set to zero; if it's currently
# high, starting PWM and setting a 0 duty-cycle *doesn't* bring
# the pin low; it stays high!
self.factory.connection.write(self._number, 0)
self.factory.connection.set_PWM_frequency(self._number, int(value))
self.factory.connection.set_PWM_range(self._number, 10000)
self.factory.connection.set_PWM_dutycycle(self._number, 0)
self._pwm = True
elif self._pwm and value is not None:
if value != self.factory.connection.get_PWM_frequency(self._number):
self.factory.connection.set_PWM_frequency(self._number, int(value))
self.factory.connection.set_PWM_range(self._number, 10000)
elif self._pwm and value is None:
self.factory.connection.write(self._number, 0)
self._pwm = False
def _get_bounce(self):
return None if not self._bounce else self._bounce / 1000000
def _set_bounce(self, value):
if value is None:
value = 0
elif not 0 <= value <= 0.3:
raise PinInvalidBounce('bounce must be between 0 and 0.3')
self.factory.connection.set_glitch_filter(
self._number, int(value * 1000000))
def _get_edges(self):
return self.GPIO_EDGES_NAMES[self._edges]
def _set_edges(self, value):
f = self.when_changed
self.when_changed = None
try:
self._edges = self.GPIO_EDGES[value]
finally:
self.when_changed = f
def _call_when_changed(self, gpio, level, ticks):
super()._call_when_changed(ticks, level)
def _enable_event_detect(self):
self._callback = self.factory.connection.callback(
self._number, self._edges, self._call_when_changed)
def _disable_event_detect(self):
if self._callback is not None:
self._callback.cancel()
self._callback = None
class PiGPIOHardwareSPI(SPI):
"""
Hardware SPI implementation for the `pigpio`_ library. Uses the ``spi_*``
functions from the pigpio API.
.. _pigpio: http://abyz.me.uk/rpi/pigpio/
"""
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
port, device = spi_port_device(
clock_pin, mosi_pin, miso_pin, select_pin)
self._port = port
self._device = device
self._handle = None
super().__init__(pin_factory=pin_factory)
to_reserve = {clock_pin, select_pin}
if mosi_pin is not None:
to_reserve.add(mosi_pin)
if miso_pin is not None:
to_reserve.add(miso_pin)
self.pin_factory.reserve_pins(self, *to_reserve)
self._spi_flags = (8 << 16) | (port << 8)
self._baud = 500000
self._handle = self.pin_factory.connection.spi_open(
device, self._baud, self._spi_flags)
def _conflicts_with(self, other):
return not (
isinstance(other, PiGPIOHardwareSPI) and
(self.pin_factory.host, self._port, self._device) !=
(other.pin_factory.host, other._port, other._device)
)
def close(self):
try:
self.pin_factory._spis.remove(self)
except (ReferenceError, ValueError):
# If the factory has died already or we're not present in its
# internal list, ignore the error
pass
if not self.closed:
self.pin_factory.connection.spi_close(self._handle)
self._handle = None
self.pin_factory.release_all(self)
super().close()
@property
def closed(self):
return self._handle is None or self.pin_factory.connection is None
def __repr__(self):
try:
self._check_open()
return f'SPI(port={self._port:d}, device={self._device:d})'
except DeviceClosed:
return 'SPI(closed)'
def _get_clock_mode(self):
return self._spi_flags & 0x3
def _set_clock_mode(self, value):
self._check_open()
if not 0 <= value < 4:
raise SPIInvalidClockMode(f"{value} is not a valid SPI clock mode")
self.pin_factory.connection.spi_close(self._handle)
self._spi_flags = (self._spi_flags & ~0x3) | value
self._handle = self.pin_factory.connection.spi_open(
self._device, self._baud, self._spi_flags)
def _get_select_high(self):
return bool((self._spi_flags >> (2 + self._device)) & 0x1)
def _set_select_high(self, value):
self._check_open()
self.pin_factory.connection.spi_close(self._handle)
self._spi_flags = (self._spi_flags & ~0x1c) | (bool(value) << (2 + self._device))
self._handle = self.pin_factory.connection.spi_open(
self._device, self._baud, self._spi_flags)
def _get_bits_per_word(self):
return (self._spi_flags >> 16) & 0x3f
def _set_bits_per_word(self, value):
self._check_open()
self.pin_factory.connection.spi_close(self._handle)
self._spi_flags = (self._spi_flags & ~0x3f0000) | ((value & 0x3f) << 16)
self._handle = self.pin_factory.connection.spi_open(
self._device, self._baud, self._spi_flags)
def _get_rate(self):
return self._baud
def _set_rate(self, value):
self._check_open()
value = int(value)
self.pin_factory.connection.spi_close(self._handle)
self._baud = value
self._handle = self.pin_factory.connection.spi_open(
self._device, self._baud, self._spi_flags)
def _get_lsb_first(self):
return bool((self._spi_flags >> 14) & 0x1) if self._port else False
def _set_lsb_first(self, value):
if self._port:
self._check_open()
self.pin_factory.connection.spi_close(self._handle)
self._spi_flags = (
(self._spi_flags & ~0xc000)
| (bool(value) << 14)
| (bool(value) << 15)
)
self._handle = self.pin_factory.connection.spi_open(
self._device, self._baud, self._spi_flags)
else:
super()._set_lsb_first(value)
def transfer(self, data):
self._check_open()
count, data = self.pin_factory.connection.spi_xfer(self._handle, data)
if count < 0:
raise IOError(f'SPI transfer error {count}')
# Convert returned bytearray to list of ints.
# XXX Not sure how non-byte sized words (aux intf only) are returned
# ... padded to 16/32-bits?
return [int(b) for b in data]
class PiGPIOSoftwareSPI(SPI):
"""
Software SPI implementation for the `pigpio`_ library. Uses the ``bb_spi_*``
functions from the pigpio API.
.. _pigpio: http://abyz.me.uk/rpi/pigpio/
"""
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
self._closed = True
self._select_pin = select_pin
self._clock_pin = clock_pin
self._mosi_pin = mosi_pin
self._miso_pin = miso_pin
super().__init__(pin_factory=pin_factory)
# Can't "unreserve" MOSI/MISO on this implementation
self.pin_factory.reserve_pins(
self,
clock_pin,
mosi_pin,
miso_pin,
select_pin,
)
self._spi_flags = 0
self._baud = 100000
try:
self.pin_factory.connection.bb_spi_open(
select_pin, miso_pin, mosi_pin, clock_pin,
self._baud, self._spi_flags)
# Only set after opening bb_spi; if that fails then close() will
# also fail if bb_spi_close is attempted on an un-open interface
self._closed = False
except:
self.close()
raise
def _conflicts_with(self, other):
return not (
isinstance(other, PiGPIOSoftwareSPI) and
(self._select_pin) != (other._select_pin)
)
def close(self):
try:
self.pin_factory._spis.remove(self)
except (ReferenceError, ValueError):
# If the factory has died already or we're not present in its
# internal list, ignore the error
pass
if not self._closed and self.pin_factory.connection:
self._closed = True
self.pin_factory.connection.bb_spi_close(self._select_pin)
self.pin_factory.release_all(self)
super().close()
@property
def closed(self):
return self._closed
def __repr__(self):
try:
self._check_open()
return (
f'SPI(clock_pin={self._clock_pin}, mosi_pin={self._mosi_pin}, '
f'miso_pin={self._miso_pin}, select_pin={self._select_pin})')
except DeviceClosed:
return 'SPI(closed)'
def _spi_flags(self):
return (
self._mode << 0 |
self._select_high << 2 |
self._lsb_first << 14 |
self._lsb_first << 15
)
def _get_clock_mode(self):
return self._spi_flags & 0x3
def _set_clock_mode(self, value):
self._check_open()
if not 0 <= value < 4:
raise SPIInvalidClockMode(f"{value} is not a valid SPI clock mode")
self.pin_factory.connection.bb_spi_close(self._select_pin)
self._spi_flags = (self._spi_flags & ~0x3) | value
self.pin_factory.connection.bb_spi_open(
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
self._baud, self._spi_flags)
def _get_select_high(self):
return bool(self._spi_flags & 0x4)
def _set_select_high(self, value):
self._check_open()
self.pin_factory.connection.bb_spi_close(self._select_pin)
self._spi_flags = (self._spi_flags & ~0x4) | (bool(value) << 2)
self.pin_factory.connection.bb_spi_open(
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
self._baud, self._spi_flags)
def _get_lsb_first(self):
return bool(self._spi_flags & 0xc000)
def _set_lsb_first(self, value):
self._check_open()
self.pin_factory.connection.bb_spi_close(self._select_pin)
self._spi_flags = (
(self._spi_flags & ~0xc000)
| (bool(value) << 14)
| (bool(value) << 15)
)
self.pin_factory.connection.bb_spi_open(
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
self._baud, self._spi_flags)
def _get_rate(self):
return self._baud
def _set_rate(self, value):
self._check_open()
value = int(value)
self.pin_factory.connection.bb_spi_close(self._select_pin)
self._baud = value
self.pin_factory.connection.bb_spi_open(
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
self._baud, self._spi_flags)
def transfer(self, data):
self._check_open()
count, data = self.pin_factory.connection.bb_spi_xfer(
self._select_pin, data)
if count < 0:
raise IOError(f'SPI transfer error {count}')
# Convert returned bytearray to list of ints. bb_spi only supports
# byte-sized words so no issues here
return [int(b) for b in data]
class PiGPIOHardwareSPIShared(SharedMixin, PiGPIOHardwareSPI):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
return (pin_factory.host, clock_pin, select_pin)
class PiGPIOSoftwareSPIShared(SharedMixin, PiGPIOSoftwareSPI):
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory):
return (pin_factory.host, clock_pin, select_pin)

View File

@@ -0,0 +1,226 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2015-2023 Dave Jones <dave@waveform.org.uk>
# Copyright (c) 2016 Andrew Scheller <github@loowis.durge.org>
#
# SPDX-License-Identifier: BSD-3-Clause
from RPi import GPIO
from .local import LocalPiFactory, LocalPiPin
from ..exc import (
PinInvalidFunction,
PinSetInput,
PinFixedPull,
PinInvalidPull,
PinInvalidState,
PinInvalidBounce,
PinPWMFixedValue,
)
class RPiGPIOFactory(LocalPiFactory):
"""
Extends :class:`~gpiozero.pins.local.LocalPiFactory`. Uses the `RPi.GPIO`_
library to interface to the Pi's GPIO pins. This is the default pin
implementation if the RPi.GPIO library is installed. Supports all features
including PWM (via software).
Because this is the default pin implementation you can use it simply by
specifying an integer number for the pin in most operations, e.g.::
from gpiozero import LED
led = LED(12)
However, you can also construct RPi.GPIO pins manually if you wish::
from gpiozero.pins.rpigpio import RPiGPIOFactory
from gpiozero import LED
factory = RPiGPIOFactory()
led = LED(12, pin_factory=factory)
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
"""
def __init__(self):
super().__init__()
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
self.pin_class = RPiGPIOPin
def close(self):
super().close()
GPIO.cleanup()
class RPiGPIOPin(LocalPiPin):
"""
Extends :class:`~gpiozero.pins.local.LocalPiPin`. Pin implementation for
the `RPi.GPIO`_ library. See :class:`RPiGPIOFactory` for more information.
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
"""
GPIO_FUNCTIONS = {
'input': GPIO.IN,
'output': GPIO.OUT,
'i2c': GPIO.I2C,
'spi': GPIO.SPI,
'pwm': GPIO.HARD_PWM,
'serial': GPIO.SERIAL,
'unknown': GPIO.UNKNOWN,
}
GPIO_PULL_UPS = {
'up': GPIO.PUD_UP,
'down': GPIO.PUD_DOWN,
'floating': GPIO.PUD_OFF,
}
GPIO_EDGES = {
'both': GPIO.BOTH,
'rising': GPIO.RISING,
'falling': GPIO.FALLING,
}
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
def __init__(self, factory, info):
super().__init__(factory, info)
self._pull = info.pull or 'floating'
self._pwm = None
self._frequency = None
self._duty_cycle = None
self._bounce = -666
self._edges = GPIO.BOTH
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[self._pull])
def close(self):
self.frequency = None
self.when_changed = None
GPIO.cleanup(self._number)
def output_with_state(self, state):
self._pull = 'floating'
GPIO.setup(self._number, GPIO.OUT, initial=state)
def input_with_pull(self, pull):
if self.info.pull and pull != self.info.pull:
raise PinFixedPull(f'{self!r} has a fixed pull resistor')
try:
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[pull])
self._pull = pull
except KeyError:
raise PinInvalidPull(f'invalid pull "{pull}" for pin {self!r}')
def _get_function(self):
return self.GPIO_FUNCTION_NAMES[GPIO.gpio_function(self._number)]
def _set_function(self, value):
if value != 'input':
self._pull = 'floating'
if value in ('input', 'output') and value in self.GPIO_FUNCTIONS:
GPIO.setup(self._number, self.GPIO_FUNCTIONS[value],
self.GPIO_PULL_UPS[self._pull])
else:
raise PinInvalidFunction(
f'invalid function "{value}" for pin {self!r}')
def _get_state(self):
if self._pwm:
return self._duty_cycle
else:
return GPIO.input(self._number)
def _set_state(self, value):
if self._pwm:
try:
self._pwm.ChangeDutyCycle(value * 100)
except ValueError:
raise PinInvalidState(
f'invalid state "{value}" for pin {self!r}')
self._duty_cycle = value
else:
try:
GPIO.output(self._number, value)
except ValueError:
raise PinInvalidState(
f'invalid state "{value}" for pin {self!r}')
except RuntimeError:
raise PinSetInput(f'cannot set state of pin {self!r}')
def _get_pull(self):
return self._pull
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull(f'cannot set pull on non-input pin {self!r}')
if self.info.pull and value != self.info.pull:
raise PinFixedPull(f'{self!r} has a fixed pull resistor')
try:
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[value])
self._pull = value
except KeyError:
raise PinInvalidPull(f'invalid pull "{value}" for pin {self!r}')
def _get_frequency(self):
return self._frequency
def _set_frequency(self, value):
if self._frequency is None and value is not None:
try:
self._pwm = GPIO.PWM(self._number, value)
except RuntimeError:
raise PinPWMFixedValue(f'cannot start PWM on pin {self!r}')
self._pwm.start(0)
self._duty_cycle = 0
self._frequency = value
elif self._frequency is not None and value is not None:
self._pwm.ChangeFrequency(value)
self._frequency = value
elif self._frequency is not None and value is None:
self._pwm.stop()
self._pwm = None
self._duty_cycle = None
self._frequency = None
def _get_bounce(self):
return None if self._bounce == -666 else (self._bounce / 1000)
def _set_bounce(self, value):
if value is not None and value < 0:
raise PinInvalidBounce('bounce must be 0 or greater')
f = self.when_changed
self.when_changed = None
try:
self._bounce = -666 if value is None else int(value * 1000)
finally:
self.when_changed = f
def _get_edges(self):
return self.GPIO_EDGES_NAMES[self._edges]
def _set_edges(self, value):
f = self.when_changed
self.when_changed = None
try:
self._edges = self.GPIO_EDGES[value]
finally:
self.when_changed = f
def _call_when_changed(self, channel):
super()._call_when_changed()
def _enable_event_detect(self):
GPIO.add_event_detect(
self._number, self._edges,
callback=self._call_when_changed,
bouncetime=self._bounce)
def _disable_event_detect(self):
GPIO.remove_event_detect(self._number)

View File

@@ -0,0 +1,219 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2016-2023 Dave Jones <dave@waveform.org.uk>
#
# SPDX-License-Identifier: BSD-3-Clause
import operator
from threading import RLock
from . import SPI
from ..devices import Device, SharedMixin
from ..input_devices import InputDevice
from ..output_devices import OutputDevice
from ..exc import DeviceClosed, SPIInvalidClockMode
class SPISoftware(SPI):
"""
A software bit-banged implementation of the :class:`gpiozero.pins.SPI`
interface.
This is a reasonable basis for a *local* SPI software implementation, but
be aware that it's unlikely to be usable for remote operation (a dedicated
daemon that locally handles SPI transactions should be used for such
operations). Instances will happily share their clock, mosi, and miso pins
with other instances provided each has a distinct select pin.
See :class:`~gpiozero.pins.spi.SPISoftwareBus` for the actual SPI
transfer logic.
"""
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, *,
pin_factory):
self._bus = None
self._select = None
super().__init__(pin_factory=pin_factory)
try:
# XXX We *should* be storing clock_mode locally, not clock_phase;
# after all different users of the bus can disagree about the
# clock's polarity and even select pin polarity
self._clock_phase = False
self._lsb_first = False
self._bits_per_word = 8
self._bus = SPISoftwareBus(
clock_pin, mosi_pin, miso_pin, pin_factory=pin_factory)
self._select = OutputDevice(
select_pin, active_high=False, pin_factory=pin_factory)
except:
self.close()
raise
def _conflicts_with(self, other):
if isinstance(other, SoftwareSPI):
return self._select.pin.info.name == other._select.pin.info.name
else:
return True
def close(self):
if self._select:
self._select.close()
self._select = None
if self._bus is not None:
self._bus.close()
self._bus = None
super().close()
@property
def closed(self):
return self._bus is None
def __repr__(self):
try:
self._check_open()
return (
f'SPI(clock_pin={self._bus.clock.pin.info.name!r}, '
f'mosi_pin={self._bus.mosi.pin.info.name!r}, '
f'miso_pin={self._bus.miso.pin.info.name!r}, '
f'select_pin={self._select.pin.info.name!r})')
except DeviceClosed:
return 'SPI(closed)'
def transfer(self, data):
with self._bus.lock:
self._select.on()
try:
return self._bus.transfer(
data, self._clock_phase, self._lsb_first,
self._bits_per_word)
finally:
self._select.off()
def _get_clock_mode(self):
with self._bus.lock:
return (not self._bus.clock.active_high) << 1 | self._clock_phase
def _set_clock_mode(self, value):
if not (0 <= value < 4):
raise SPIInvalidClockMode(f"{value} is not a valid clock mode")
with self._bus.lock:
self._bus.clock.active_high = not (value & 2)
self._clock_phase = bool(value & 1)
def _get_lsb_first(self):
return self._lsb_first
def _set_lsb_first(self, value):
self._lsb_first = bool(value)
def _get_bits_per_word(self):
return self._bits_per_word
def _set_bits_per_word(self, value):
if value < 1:
raise ValueError('bits_per_word must be positive')
self._bits_per_word = int(value)
def _get_select_high(self):
return self._select.active_high
def _set_select_high(self, value):
with self._bus.lock:
self._select.active_high = value
self._select.off()
class SPISoftwareBus(SharedMixin, Device):
"""
A software bit-banged SPI bus implementation, used by
:class:`~gpiozero.pins.spi.SPISoftware` to implement shared SPI interfaces.
.. warning::
This implementation has no rate control; it simply clocks out data as
fast as it can as Python isn't terribly quick on a Pi anyway, and the
extra logic required for rate control is liable to reduce the maximum
achievable data rate quite substantially.
"""
def __init__(self, clock_pin, mosi_pin, miso_pin, *, pin_factory):
self.lock = None
self.clock = None
self.mosi = None
self.miso = None
super().__init__()
# XXX Should probably just use CompositeDevice for this; would make
# close() a bit cleaner - any implications with the RLock?
self.lock = RLock()
try:
self.clock = OutputDevice(
clock_pin, active_high=True, pin_factory=pin_factory)
if mosi_pin is not None:
self.mosi = OutputDevice(mosi_pin, pin_factory=pin_factory)
if miso_pin is not None:
self.miso = InputDevice(miso_pin, pin_factory=pin_factory)
except:
self.close()
raise
def close(self):
super().close()
if getattr(self, 'lock', None):
with self.lock:
if self.miso is not None:
self.miso.close()
self.miso = None
if self.mosi is not None:
self.mosi.close()
self.mosi = None
if self.clock is not None:
self.clock.close()
self.clock = None
self.lock = None
@property
def closed(self):
return self.lock is None
@classmethod
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, *, pin_factory=None):
return (clock_pin, mosi_pin, miso_pin)
def transfer(self, data, clock_phase=False, lsb_first=False, bits_per_word=8):
"""
Writes data (a list of integer words where each word is assumed to have
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
equivalent number of words, returning them as a list of integers.
"""
result = []
with self.lock:
# See https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
# (specifically the section "Example of bit-banging the master
# protocol") for a simpler C implementation of this which ignores
# clock polarity, phase, variable word-size, and multiple input
# words
if lsb_first:
shift = operator.lshift
init_mask = 1
else:
shift = operator.rshift
init_mask = 1 << (bits_per_word - 1)
for write_word in data:
mask = init_mask
read_word = 0
for _ in range(bits_per_word):
if self.mosi is not None:
self.mosi.value = bool(write_word & mask)
# read bit on clock activation
self.clock.on()
if not clock_phase:
if self.miso is not None and self.miso.value:
read_word |= mask
# read bit on clock deactivation
self.clock.off()
if clock_phase:
if self.miso is not None and self.miso.value:
read_word |= mask
mask = shift(mask, 1)
result.append(read_word)
return result

View File

@@ -0,0 +1,92 @@
# vim: set fileencoding=utf-8:
#
# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins
#
# Copyright (c) 2021-2023 Dave Jones <dave@waveform.org.uk>
#
# SPDX-License-Identifier: BSD-3-Clause
import os
import sys
# ANSI color codes, for the pretty printers (nothing comprehensive, just enough
# for our purposes)
class Style:
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 not content:
content = 'full'
else:
content = ' '.join(content)
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(f'invalid format specification "{spec}"')
if self.color:
codes = ';'.join(str(code) for code in codes)
return f'\x1b[{codes}m'
else:
return ''
def __format__(self, format_spec):
if format_spec == '':
return 'color' if self.color else 'mono'
else:
return self(format_spec)