├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── test.yml ├── .gohci.yml ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── analog ├── analog.go ├── analog_test.go └── func.go ├── codecov.yml ├── conn.go ├── conn_test.go ├── conntest ├── conntest.go └── conntest_test.go ├── display ├── display.go ├── displaytest │ ├── displaytest.go │ ├── doc.go │ └── text.go ├── example_test.go └── text_display.go ├── doc.go ├── driver ├── driver.go └── driverreg │ ├── driverreg.go │ ├── driverreg_parallel.go │ ├── driverreg_serial.go │ ├── driverreg_test.go │ └── example_test.go ├── example_test.go ├── go.mod ├── go.sum ├── gpio ├── example_test.go ├── func.go ├── gpio.go ├── gpio_test.go ├── gpioreg │ ├── example_test.go │ ├── gpioreg.go │ ├── gpioreg_test.go │ ├── natsort.go │ └── natsort_test.go ├── gpiostream │ ├── example_test.go │ ├── gpiostream.go │ ├── gpiostream_test.go │ └── gpiostreamtest │ │ ├── gpiostreamtest.go │ │ └── gpiostreamtest_test.go ├── gpiotest │ ├── gpiotest.go │ └── gpiotest_test.go ├── gpioutil │ ├── debounce.go │ ├── debounce_test.go │ ├── doc.go │ ├── example_test.go │ ├── polledge.go │ ├── polledge_test.go │ ├── pulsein.go │ └── pulsein_test.go └── group.go ├── i2c ├── example_test.go ├── func.go ├── i2c.go ├── i2c_test.go ├── i2creg │ ├── example_test.go │ ├── i2creg.go │ └── i2creg_test.go └── i2ctest │ ├── i2ctest.go │ └── i2ctest_test.go ├── i2s ├── func.go └── i2s.go ├── ir └── ir.go ├── jtag ├── func.go └── jtag.go ├── mmr ├── example_test.go ├── mmr.go └── mmr_test.go ├── onewire ├── crc.go ├── crc_test.go ├── example_test.go ├── func.go ├── onewire.go ├── onewire_test.go ├── onewirereg │ ├── example_test.go │ ├── onewirereg.go │ └── onewirereg_test.go ├── onewiretest │ ├── onewiretest.go │ └── onewiretest_test.go ├── search.go └── search_test.go ├── physic ├── doc.go ├── example_test.go ├── physic.go ├── units.go └── units_test.go ├── pin ├── example_test.go ├── func.go ├── func_test.go ├── pin.go ├── pin_test.go └── pinreg │ ├── doc.go │ ├── pinreg.go │ └── pinreg_test.go ├── spi ├── example_test.go ├── func.go ├── spi.go ├── spi_test.go ├── spireg │ ├── example_test.go │ ├── spireg.go │ └── spireg_test.go └── spitest │ ├── spitest.go │ └── spitest_test.go └── uart ├── example_test.go ├── func.go ├── uart.go └── uartreg ├── example_test.go ├── uartreg.go └── uartreg_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Signal a problem with this package. 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 1. Run program 12 | ```go 13 | package main 14 | 15 | import ( 16 | "periph.io/x/host/v3" 17 | ) 18 | 19 | func main() { 20 | host.Init() 21 | } 22 | ``` 23 | 2. Run it. 24 | 3. See error 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | 29 | **Platform (please complete the following information):** 30 | - OS: [e.g. Raspbian Buster Lite] 31 | - Board [e.g. Raspberry Pi 4] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: What would you like to see supported? 4 | --- 5 | 6 | **What kind of new feature are you looking for?** 7 | (Keep the one that applies, please describe) 8 | - Hardware: [new interface] 9 | - Software: [new fakes, etc] 10 | 11 | **Do you plan to:** 12 | - Contribute an initial package: [Yes/No] 13 | - Write unit tests: [Yes/No] 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 1. Please prefix the issue title with the primary package affected. For example, 2 | if this PR fixes periph.io/x/conn/v3/i2c, prefix the PR title with "i2c:". 3 | 4 | 2. Mention the issue number it fixes or add the details of the changes if it 5 | doesn't have a specific issue. Examples: 6 | - Fixes #12345 7 | - Helps with #12345 but doesn't not completely fix it. 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of The Periph Authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | Cássio Botaro 7 | Fractal Industries, Inc 8 | Google Inc. 9 | Josh Gardiner 10 | Marc-Antoine Ruel 11 | Matt Aimonetti 12 | Max Ekman 13 | Rifiniti, Inc 14 | Stephan Sperber 15 | Thorsten von Eicken 16 | 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for contributing to the project! Please look at [the periph contribution 4 | guidelines](https://periph.io/project/contributing/) first. 5 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the periph repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # Names should be added to this file only after verifying that 8 | # the individual or the individual's organization has agreed to 9 | # the appropriate Contributor License Agreement, found here: 10 | # 11 | # https://cla.developers.google.com/ 12 | # 13 | # When adding J Random Contributor's name to this file, 14 | # either J's name or J's organization's name should be 15 | # added to the AUTHORS file, depending on whether the 16 | # individual or corporate CLA was used. 17 | 18 | # Names should be added to this file like so: 19 | # Individual's name 20 | # Individual's name 21 | # 22 | # An entry with multiple email addresses specifies that the 23 | # first address should be used in the submit logs and 24 | # that the other addresses should be recognized as the 25 | # same person when interacting with Gerrit. 26 | 27 | # Please keep the list sorted. 28 | 29 | Cássio Botaro 30 | Eugene Dzhurynsky 31 | Hidetoshi Shimokawa 32 | John Maguire 33 | Josh Gardiner 34 | Marc-Antoine Ruel 35 | Matt Aimonetti 36 | Max Ekman 37 | Matias Insaurralde 38 | Seán C McCord 39 | Stephan Sperber 40 | Thorsten von Eicken 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # periph - Peripherals I/O in Go 2 | 3 | Documentation is at https://periph.io 4 | 5 | Join us for a chat on 6 | [gophers.slack.com/messages/periph](https://gophers.slack.com/messages/periph), 7 | get an [invite here](https://invite.slack.golangbridge.org/). 8 | 9 | [![mascot](https://raw.githubusercontent.com/periph/website/master/site/static/img/periph-mascot-280.png)](https://periph.io/) 10 | 11 | [![PkgGoDev](https://pkg.go.dev/badge/periph.io/x/conn/v3)](https://pkg.go.dev/periph.io/x/conn/v3) 12 | [![codecov](https://codecov.io/gh/periph/conn/branch/main/graph/badge.svg?token=1WIDCAJIK8)](https://codecov.io/gh/periph/conn) 13 | 14 | 15 | ## Example 16 | 17 | Blink a LED: 18 | 19 | ~~~go 20 | package main 21 | 22 | import ( 23 | "time" 24 | "periph.io/x/conn/v3/gpio" 25 | "periph.io/x/host/v3" 26 | "periph.io/x/host/v3/rpi" 27 | ) 28 | 29 | func main() { 30 | host.Init() 31 | t := time.NewTicker(500 * time.Millisecond) 32 | for l := gpio.Low; ; l = !l { 33 | rpi.P1_33.Out(l) 34 | <-t.C 35 | } 36 | } 37 | ~~~ 38 | 39 | Curious? Look at [supported devices](https://periph.io/device/) for more 40 | examples! 41 | 42 | 43 | ## Authors 44 | 45 | `periph` was initiated with ❤️️ and passion by [Marc-Antoine 46 | Ruel](https://github.com/maruel). The full list of contributors is in 47 | [AUTHORS](https://github.com/periph/conn/blob/main/AUTHORS) and 48 | [CONTRIBUTORS](https://github.com/periph/conn/blob/main/CONTRIBUTORS). 49 | -------------------------------------------------------------------------------- /analog/analog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package analog defines analog pins, both digital to analog converter (DAC) 6 | // and analog to digital converter (ADC). 7 | package analog 8 | 9 | import ( 10 | "errors" 11 | 12 | "periph.io/x/conn/v3/physic" 13 | "periph.io/x/conn/v3/pin" 14 | ) 15 | 16 | // Sample is one analog sample. 17 | // 18 | // Raw must be set, but V may or may not be set, depending if the device knows 19 | // the electrical tension this measurement represents. 20 | type Sample struct { 21 | // V is the interpreted electrical tension. 22 | V physic.ElectricPotential 23 | // Raw is the raw measurement. 24 | Raw int32 25 | } 26 | 27 | // PinADC is an analog-to-digital-conversion input. 28 | type PinADC interface { 29 | pin.Pin 30 | // Range returns the maximum supported range [min, max] of the values. 31 | // 32 | // It is possible for a DAC that the Sample.V value is not set. 33 | Range() (Sample, Sample) 34 | // Read returns the current pin level. 35 | Read() (Sample, error) 36 | } 37 | 38 | // PinDAC is an digital-to-analog-conversion output. 39 | type PinDAC interface { 40 | pin.Pin 41 | // Range returns the maximum supported range [min, max] of the values. 42 | // 43 | // It is possible for a DAC that the Sample.V value is not set. 44 | Range() (Sample, Sample) 45 | // Out sets an analog output value. 46 | Out(v int32) error 47 | } 48 | 49 | // INVALID implements both PinADC and PinDAC and fails on all access. 50 | var INVALID invalidPin 51 | 52 | // 53 | 54 | // errInvalidPin is returned when trying to use INVALID. 55 | var errInvalidPin = errors.New("invalid pin") 56 | 57 | // invalidPin implements PinIO for compatibility but fails on all access. 58 | type invalidPin struct { 59 | } 60 | 61 | func (invalidPin) Number() int { 62 | return -1 63 | } 64 | 65 | func (invalidPin) Name() string { 66 | return "INVALID" 67 | } 68 | 69 | func (invalidPin) String() string { 70 | return "INVALID" 71 | } 72 | 73 | func (invalidPin) Function() string { 74 | return "" 75 | } 76 | 77 | func (invalidPin) Func() pin.Func { 78 | return pin.FuncNone 79 | } 80 | 81 | func (invalidPin) SupportedFuncs() []pin.Func { 82 | return nil 83 | } 84 | 85 | func (invalidPin) SetFunc(f pin.Func) error { 86 | return errInvalidPin 87 | } 88 | 89 | func (invalidPin) Halt() error { 90 | return errInvalidPin 91 | } 92 | 93 | func (invalidPin) Range() (Sample, Sample) { 94 | return Sample{}, Sample{} 95 | } 96 | 97 | func (invalidPin) Read() (Sample, error) { 98 | return Sample{}, errInvalidPin 99 | } 100 | 101 | func (invalidPin) Out(v int32) error { 102 | return errInvalidPin 103 | } 104 | 105 | var _ PinADC = &INVALID 106 | var _ PinDAC = &INVALID 107 | var _ pin.PinFunc = &INVALID 108 | -------------------------------------------------------------------------------- /analog/analog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package analog 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/pin" 11 | ) 12 | 13 | func TestINVALID(t *testing.T) { 14 | if INVALID.Number() != -1 { 15 | t.Fatal("Number") 16 | } 17 | if INVALID.Name() != "INVALID" { 18 | t.Fatal("Name") 19 | } 20 | if INVALID.String() != "INVALID" { 21 | t.Fatal("String") 22 | } 23 | if INVALID.Function() != "" { 24 | t.Fatal("Function") 25 | } 26 | if INVALID.Func() != pin.FuncNone { 27 | t.Fatal("Func") 28 | } 29 | if v := INVALID.SupportedFuncs(); len(v) != 0 { 30 | t.Fatal("SupportedFuncs") 31 | } 32 | if err := INVALID.SetFunc(pin.FuncNone); err == nil { 33 | t.Fatal("SetFunc") 34 | } 35 | if INVALID.Halt() == nil { 36 | t.Fatal("Halt") 37 | } 38 | sample := Sample{} 39 | if s1, s2 := INVALID.Range(); s1 != sample || s2 != sample { 40 | t.Fatal("Range") 41 | } 42 | if _, err := INVALID.Read(); err == nil { 43 | t.Fatal("Read") 44 | } 45 | if INVALID.Out(0) == nil { 46 | t.Fatal("Out") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /analog/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package analog 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | // ADC means Analog-to-Digital Converter. 12 | ADC pin.Func = "ADC" 13 | // DAC means Digital-to-Analog Converter. 14 | DAC pin.Func = "DAC" 15 | ) 16 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Periph Authors. All rights reserved. 2 | # Use of this source code is governed under the Apache License, Version 2.0 3 | # that can be found in the LICENSE file. 4 | 5 | # https://docs.codecov.io/docs/codecovyml-reference 6 | # and 7 | # https://docs.codecov.io/docs/coverage-configuration 8 | coverage: 9 | precision: 1 10 | range: "40...80" 11 | round: nearest 12 | status: 13 | patch: 14 | default: 15 | target: 60% 16 | threshold: 10% 17 | project: 18 | default: 19 | target: 60% 20 | threshold: 10% 21 | -------------------------------------------------------------------------------- /conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package conn 6 | 7 | import "strconv" 8 | 9 | // Resource is a basic resource (like a gpio pin) or a device. 10 | type Resource interface { 11 | // String returns a human readable identifier representing this resource in a 12 | // descriptive way for the user. It is the same signature as fmt.Stringer. 13 | String() string 14 | // Halt stops the resource. 15 | // 16 | // Unlike a Conn, a Resource may not be closable, On the other hand, a 17 | // resource can be halted. What halting entails depends on the resource 18 | // device but it should stop motion, sensing loop, light emission or PWM 19 | // output and go back into an inert state. 20 | Halt() error 21 | } 22 | 23 | // Duplex declares whether communication can happen simultaneously both ways. 24 | // 25 | // Some protocol can be either depending on configuration settings, like UART. 26 | type Duplex int 27 | 28 | const ( 29 | // DuplexUnknown is used when the duplex of a connection is yet to be known. 30 | // 31 | // Some protocol can be configured either as half-duplex or full-duplex and 32 | // the connection is not yet is a determinate state. 33 | DuplexUnknown Duplex = 0 34 | // Half means that communication can only occurs one way at a time. 35 | // 36 | // Examples include 1-wire and I²C. 37 | Half Duplex = 1 38 | // Full means that communication occurs simultaneously both ways in a 39 | // synchronized manner. 40 | // 41 | // Examples include SPI (except 3-wire variant). 42 | Full Duplex = 2 43 | ) 44 | 45 | const duplexName = "DuplexUnknownHalfFull" 46 | 47 | var duplexIndex = [...]uint8{0, 13, 17, 21} 48 | 49 | func (i Duplex) String() string { 50 | if i < 0 || i >= Duplex(len(duplexIndex)-1) { 51 | return "Duplex(" + strconv.Itoa(int(i)) + ")" 52 | } 53 | return duplexName[duplexIndex[i]:duplexIndex[i+1]] 54 | } 55 | 56 | // Conn defines the interface for a connection on a point-to-point 57 | // communication channel. 58 | // 59 | // The connection can either be unidirectional (read-only, write-only) or 60 | // bidirectional (read-write). It can either be half-duplex or full duplex. 61 | // 62 | // This is the lowest common denominator for all point-to-point communication 63 | // channels. 64 | // 65 | // Implementation are expected but not required to also implement the following 66 | // interfaces: 67 | // 68 | // - fmt.Stringer which returns something meaningful to the user like "SPI0.1", 69 | // "I2C1.76", "COM6", etc. 70 | // 71 | // - io.Reader and io.Writer as a way to use io.Copy() for half duplex 72 | // operation. 73 | // 74 | // - io.Closer for the owner of the communication channel. 75 | type Conn interface { 76 | String() string 77 | // Tx does a single transaction. 78 | // 79 | // For full duplex protocols (generally SPI, UART), the two buffers must have 80 | // the same length as both reading and writing happen simultaneously. 81 | // 82 | // For half duplex protocols (I²C), there is no restriction as reading 83 | // happens after writing, and r can be nil. 84 | // 85 | // Query Limits.MaxTxSize() to know if there is a limit on the buffer size 86 | // per Tx() call. 87 | Tx(w, r []byte) error 88 | // Duplex returns the current duplex setting for this point-to-point 89 | // connection. 90 | // 91 | // It is expected to be either Half or Full unless the connection itself is 92 | // in an unknown state. 93 | Duplex() Duplex 94 | } 95 | 96 | // Limits returns information about the connection's limits. 97 | type Limits interface { 98 | // MaxTxSize returns the maximum allowed data size to be sent as a single 99 | // I/O. 100 | // 101 | // Returns 0 if undefined. 102 | MaxTxSize() int 103 | } 104 | -------------------------------------------------------------------------------- /conn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package conn 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestDuplex(t *testing.T) { 12 | if Half.String() != "Half" || Duplex(10).String() != "Duplex(10)" { 13 | t.Fatal() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /conntest/conntest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package conntest implements fakes for package conn. 6 | package conntest 7 | 8 | import ( 9 | "bytes" 10 | "fmt" 11 | "io" 12 | "sync" 13 | 14 | "periph.io/x/conn/v3" 15 | ) 16 | 17 | // IsErr returns true if the error is from a conntest failure. 18 | func IsErr(err error) bool { 19 | _, ok := err.(testErr) 20 | return ok 21 | } 22 | 23 | // Errorf returns a new error that returns true with IsErr(). 24 | func Errorf(format string, a ...interface{}) error { 25 | return testErr{fmt.Errorf(format, a...)} 26 | } 27 | 28 | // RecordRaw implements conn.Conn. It sends everything written to it to W. 29 | type RecordRaw struct { 30 | sync.Mutex 31 | W io.Writer 32 | } 33 | 34 | func (r *RecordRaw) String() string { 35 | return "recordraw" 36 | } 37 | 38 | // Tx implements conn.Conn. 39 | func (r *RecordRaw) Tx(w, read []byte) error { 40 | if len(read) != 0 { 41 | return Errorf("conntest: not implemented") 42 | } 43 | _, err := r.W.Write(w) 44 | return err 45 | } 46 | 47 | // Duplex implements conn.Conn. 48 | func (r *RecordRaw) Duplex() conn.Duplex { 49 | return conn.Half 50 | } 51 | 52 | // IO registers the I/O that happened on either a real or fake connection. 53 | type IO struct { 54 | W []byte 55 | R []byte 56 | } 57 | 58 | // Record implements conn.Conn that records everything written to it. 59 | // 60 | // This can then be used to feed to Playback to do "replay" based unit tests. 61 | type Record struct { 62 | sync.Mutex 63 | Conn conn.Conn // Conn can be nil if only writes are being recorded. 64 | Ops []IO 65 | } 66 | 67 | func (r *Record) String() string { 68 | return "record" 69 | } 70 | 71 | // Tx implements conn.Conn. 72 | func (r *Record) Tx(w, read []byte) error { 73 | io := IO{} 74 | if len(w) != 0 { 75 | io.W = make([]byte, len(w)) 76 | copy(io.W, w) 77 | } 78 | r.Lock() 79 | defer r.Unlock() 80 | if r.Conn == nil { 81 | if len(read) != 0 { 82 | return Errorf("conntest: read unsupported when no bus is connected") 83 | } 84 | } else { 85 | if err := r.Conn.Tx(w, read); err != nil { 86 | return err 87 | } 88 | } 89 | if len(read) != 0 { 90 | io.R = make([]byte, len(read)) 91 | copy(io.R, read) 92 | } 93 | r.Ops = append(r.Ops, io) 94 | return nil 95 | } 96 | 97 | // Duplex implements conn.Conn. 98 | func (r *Record) Duplex() conn.Duplex { 99 | if r.Conn != nil { 100 | return r.Conn.Duplex() 101 | } 102 | return conn.DuplexUnknown 103 | } 104 | 105 | // Playback implements conn.Conn and plays back a recorded I/O flow. 106 | // 107 | // While "replay" type of unit tests are of limited value, they still present 108 | // an easy way to do basic code coverage. 109 | // 110 | // Set DontPanic to true to return an error instead of panicking, which is the 111 | // default. 112 | type Playback struct { 113 | sync.Mutex 114 | Ops []IO 115 | D conn.Duplex 116 | Count int 117 | DontPanic bool 118 | } 119 | 120 | func (p *Playback) String() string { 121 | return "playback" 122 | } 123 | 124 | // Close verifies that all the expected Ops have been consumed. 125 | func (p *Playback) Close() error { 126 | p.Lock() 127 | defer p.Unlock() 128 | if len(p.Ops) != p.Count { 129 | return errorf(p.DontPanic, "conntest: expected playback to be empty: I/O count %d; expected %d", p.Count, len(p.Ops)) 130 | } 131 | return nil 132 | } 133 | 134 | // Tx implements conn.Conn. 135 | func (p *Playback) Tx(w, r []byte) error { 136 | p.Lock() 137 | defer p.Unlock() 138 | if len(p.Ops) <= p.Count { 139 | return errorf(p.DontPanic, "conntest: unexpected Tx() (count #%d) expecting []conntest.IO{W:%#v, R:%#v}", p.Count, w, r) 140 | } 141 | if !bytes.Equal(p.Ops[p.Count].W, w) { 142 | return errorf(p.DontPanic, "conntest: unexpected write (count #%d) %#v != %#v", p.Count, w, p.Ops[p.Count].W) 143 | } 144 | if len(p.Ops[p.Count].R) != len(r) { 145 | return errorf(p.DontPanic, "conntest: unexpected read buffer length (count #%d) %d != %d", p.Count, len(r), len(p.Ops[p.Count].R)) 146 | } 147 | copy(r, p.Ops[p.Count].R) 148 | p.Count++ 149 | return nil 150 | } 151 | 152 | // Duplex implements conn.Conn. 153 | func (p *Playback) Duplex() conn.Duplex { 154 | p.Lock() 155 | defer p.Unlock() 156 | return p.D 157 | } 158 | 159 | // Discard implements conn.Conn and discards all writes and reads zeros. It 160 | // never fails. 161 | type Discard struct { 162 | D conn.Duplex 163 | } 164 | 165 | func (d *Discard) String() string { 166 | return "discard" 167 | } 168 | 169 | // Tx implements conn.Conn. 170 | func (d *Discard) Tx(w, r []byte) error { 171 | for i := range r { 172 | r[i] = 0 173 | } 174 | return nil 175 | } 176 | 177 | // Duplex implements conn.Conn. 178 | func (d *Discard) Duplex() conn.Duplex { 179 | return d.D 180 | } 181 | 182 | // 183 | 184 | // errorf is the internal implementation that optionally panic. 185 | // 186 | // If dontPanic is false, it panics instead. 187 | func errorf(dontPanic bool, format string, a ...interface{}) error { 188 | err := Errorf(format, a...) 189 | if !dontPanic { 190 | panic(err) 191 | } 192 | return err 193 | } 194 | 195 | type testErr struct { 196 | error 197 | } 198 | 199 | var _ conn.Conn = &RecordRaw{} 200 | var _ conn.Conn = &Record{} 201 | var _ conn.Conn = &Playback{} 202 | -------------------------------------------------------------------------------- /conntest/conntest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package conntest 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "periph.io/x/conn/v3" 12 | ) 13 | 14 | func TestRecordRaw(t *testing.T) { 15 | b := bytes.Buffer{} 16 | r := RecordRaw{W: &b} 17 | if d := r.Duplex(); d != conn.Half { 18 | t.Fatal(d) 19 | } 20 | if s := r.String(); s != "recordraw" { 21 | t.Fatal(s) 22 | } 23 | if r.Tx(nil, []byte{0}) == nil { 24 | t.Fatal("cannot accept read buffer") 25 | } 26 | if err := r.Tx([]byte{'a'}, nil); err != nil { 27 | t.Fatal(err) 28 | } 29 | if s := b.String(); s != "a" { 30 | t.Fatal(s) 31 | } 32 | } 33 | 34 | func TestRecord_empty(t *testing.T) { 35 | r := Record{} 36 | if s := r.String(); s != "record" { 37 | t.Fatal(s) 38 | } 39 | if r.Tx(nil, []byte{'a'}) == nil { 40 | t.Fatal("Bus is nil") 41 | } 42 | if d := r.Duplex(); d != conn.DuplexUnknown { 43 | t.Fatal(d) 44 | } 45 | } 46 | 47 | func TestRecord_Tx_empty(t *testing.T) { 48 | r := Record{} 49 | if err := r.Tx(nil, nil); err != nil { 50 | t.Fatal(err) 51 | } 52 | if len(r.Ops) != 1 { 53 | t.Fatal(r.Ops) 54 | } 55 | if err := r.Tx([]byte{'a', 'b'}, nil); err != nil { 56 | t.Fatal(err) 57 | } 58 | if len(r.Ops) != 2 { 59 | t.Fatal(r.Ops) 60 | } 61 | if r.Tx([]byte{'a', 'b'}, []byte{'d'}) == nil { 62 | t.Fatal("Bus is nil") 63 | } 64 | if len(r.Ops) != 2 { 65 | t.Fatal(r.Ops) 66 | } 67 | } 68 | 69 | func TestPlayback(t *testing.T) { 70 | p := Playback{} 71 | if s := p.String(); s != "playback" { 72 | t.Fatal(s) 73 | } 74 | if err := p.Close(); err != nil { 75 | t.Fatal(err) 76 | } 77 | } 78 | 79 | func TestPlayback_Close_panic(t *testing.T) { 80 | p := Playback{Ops: []IO{{W: []byte{10}}}} 81 | defer func() { 82 | v := recover() 83 | err, ok := v.(error) 84 | if !ok { 85 | t.Fatal("expected error") 86 | } 87 | if !IsErr(err) { 88 | t.Fatalf("unexpected error: %v", err) 89 | } 90 | }() 91 | _ = p.Close() 92 | t.Fatal("shouldn't run") 93 | } 94 | 95 | func TestPlayback_Tx(t *testing.T) { 96 | p := Playback{ 97 | Ops: []IO{ 98 | { 99 | W: []byte{10}, 100 | R: []byte{12}, 101 | }, 102 | }, 103 | DontPanic: true, 104 | } 105 | if p.Tx(nil, nil) == nil { 106 | t.Fatal("missing read and write") 107 | } 108 | if p.Close() == nil { 109 | t.Fatal("Ops is not empty") 110 | } 111 | v := [1]byte{} 112 | if p.Tx([]byte{10}, make([]byte, 2)) == nil { 113 | t.Fatal("invalid read size") 114 | } 115 | if err := p.Tx([]byte{10}, v[:]); err != nil { 116 | t.Fatal(err) 117 | } 118 | if v[0] != 12 { 119 | t.Fatalf("expected 12, got %v", v) 120 | } 121 | if err := p.Tx([]byte{10}, v[:]); err == nil { 122 | t.Fatal("Playback.Ops is empty") 123 | } 124 | if err := p.Close(); err != nil { 125 | t.Fatal(err) 126 | } 127 | } 128 | 129 | func TestPlayback_Tx_panic_count(t *testing.T) { 130 | p := Playback{} 131 | defer func() { 132 | v := recover() 133 | err, ok := v.(error) 134 | if !ok { 135 | t.Fatal("expected error") 136 | } 137 | if !IsErr(err) { 138 | t.Fatalf("unexpected error: %v", err) 139 | } 140 | }() 141 | _ = p.Tx([]byte{0}, nil) 142 | t.Fatal("shouldn't run") 143 | } 144 | 145 | func TestPlayback_Tx_panic_write(t *testing.T) { 146 | p := Playback{Ops: []IO{{W: []byte{1}}}} 147 | defer func() { 148 | v := recover() 149 | err, ok := v.(error) 150 | if !ok { 151 | t.Fatal("expected error") 152 | } 153 | if !IsErr(err) { 154 | t.Fatalf("unexpected error: %v", err) 155 | } 156 | }() 157 | _ = p.Tx([]byte{0}, nil) 158 | t.Fatal("shouldn't run") 159 | } 160 | 161 | func TestPlayback_Tx_panic_read(t *testing.T) { 162 | p := Playback{Ops: []IO{{R: []byte{1}}}} 163 | defer func() { 164 | v := recover() 165 | err, ok := v.(error) 166 | if !ok { 167 | t.Fatal("expected error") 168 | } 169 | if !IsErr(err) { 170 | t.Fatalf("unexpected error: %v", err) 171 | } 172 | }() 173 | _ = p.Tx(nil, []byte{0, 1}) 174 | t.Fatal("shouldn't run") 175 | } 176 | 177 | func TestRecord_Playback(t *testing.T) { 178 | r := Record{ 179 | Conn: &Playback{ 180 | Ops: []IO{ 181 | { 182 | W: []byte{10}, 183 | R: []byte{12}, 184 | }, 185 | }, 186 | D: conn.Full, 187 | DontPanic: true, 188 | }, 189 | } 190 | if d := r.Duplex(); d != conn.Full { 191 | t.Fatal(d) 192 | } 193 | 194 | v := [1]byte{} 195 | if err := r.Tx([]byte{10}, v[:]); err != nil { 196 | t.Fatal(err) 197 | } 198 | if v[0] != 12 { 199 | t.Fatalf("expected 12, got %v", v) 200 | } 201 | if r.Tx([]byte{10}, v[:]) == nil { 202 | t.Fatal("Playback.Ops is empty") 203 | } 204 | } 205 | 206 | func TestDiscard(t *testing.T) { 207 | d := Discard{D: conn.Half} 208 | if s := d.String(); s != "discard" { 209 | t.Fatal(s) 210 | } 211 | if v := d.Duplex(); v != conn.Half { 212 | t.Fatal(v) 213 | } 214 | if err := d.Tx(nil, nil); err != nil { 215 | t.Fatal(err) 216 | } 217 | if err := d.Tx([]byte{0}, []byte{0}); err != nil { 218 | t.Fatal(err) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /display/display.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package display implements interfaces for visual output devices. These can 6 | // be pixel or text based. 7 | package display 8 | 9 | import ( 10 | "image" 11 | "image/color" 12 | 13 | "periph.io/x/conn/v3" 14 | ) 15 | 16 | // Drawer represents a context to display pixels on an output device. It is a 17 | // write-only interface. 18 | // 19 | // What Drawer represents can be as varied as a 1 bit OLED display or a strip 20 | // of LED lights. The implementation keeps a single frame buffer, so that 21 | // partial updates can be done. 22 | type Drawer interface { 23 | conn.Resource 24 | 25 | // ColorModel returns the device native color model. 26 | ColorModel() color.Model 27 | // Bounds returns the size of the output device. 28 | // 29 | // Generally displays should have Min at {0, 0} but this is not guaranteed in 30 | // multiple displays setup or when an instance of this interface represents a 31 | // section of a larger logical display. 32 | Bounds() image.Rectangle 33 | // Draw updates the display with this image. 34 | // 35 | // Only the pixels within the display boundary are updated. Partial update is 36 | // supported. 37 | // 38 | // Coordinates are top-left 0,0. 39 | // 40 | // dstRect aligns the the drawing operation in the display, enabling partial 41 | // update. 42 | // 43 | // srcPts aligns the image at this offset, enabling using a subset of the 44 | // source image. use image.Point{} to take the image at its origin. 45 | Draw(dstRect image.Rectangle, src image.Image, srcPts image.Point) error 46 | } 47 | -------------------------------------------------------------------------------- /display/displaytest/displaytest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package displaytest 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "image/draw" 11 | 12 | "periph.io/x/conn/v3/display" 13 | ) 14 | 15 | // Drawer is a fake display.Drawer. 16 | type Drawer struct { 17 | Img *image.NRGBA 18 | } 19 | 20 | func (d *Drawer) String() string { 21 | return "Drawer" 22 | } 23 | 24 | // Halt implements conn.Resource. It is a noop. 25 | func (d *Drawer) Halt() error { 26 | return nil 27 | } 28 | 29 | // ColorModel implements image.Image. 30 | func (d *Drawer) ColorModel() color.Model { 31 | return d.Img.ColorModel() 32 | } 33 | 34 | // Bounds implements image.Image. 35 | func (d *Drawer) Bounds() image.Rectangle { 36 | return d.Img.Bounds() 37 | } 38 | 39 | // Draw implements draw.Image. 40 | func (d *Drawer) Draw(dstRect image.Rectangle, src image.Image, sp image.Point) error { 41 | draw.Draw(d.Img, dstRect, src, sp, draw.Src) 42 | return nil 43 | } 44 | 45 | var _ display.Drawer = &Drawer{} 46 | -------------------------------------------------------------------------------- /display/displaytest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package displaytest contains non-hardware devices implementations for 6 | // testing or emulation purpose. 7 | package displaytest 8 | -------------------------------------------------------------------------------- /display/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package display_test 6 | 7 | import ( 8 | "image" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/display" 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | ) 14 | 15 | func ExampleDrawer() { 16 | // Make sure periph is initialized. 17 | // TODO: Use host.Init(). It is not used in this example to prevent circular 18 | // go package import. 19 | if _, err := driverreg.Init(); err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | // Get a display output device, like an apa102 or ssd1306. For example: 24 | // s, _ := spireg.Open("") 25 | // d, _ := apa102.New(s, &apa102.DefaultOpts) 26 | var d display.Drawer 27 | 28 | // Get an image. You could load a PNG. Resize it to the device display size. 29 | img := image.NewNRGBA(d.Bounds()) 30 | 31 | // Render the image. The normal use case is: 32 | // - Use d.Bounds() as the dstRect, to cover the whole screen. 33 | // - Use image.Point{} as 'srcPts' unless you want to offset inside 34 | // the image. 35 | if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { 36 | log.Fatal(err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /display/text_display.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package display 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | type CursorDirection int 12 | 13 | const ( 14 | // Constants for moving the cursor relative to it's current position. 15 | // 16 | // Move the cursor one unit back. 17 | Backward CursorDirection = iota 18 | // Move the cursor one unit forward. 19 | Forward 20 | Up 21 | Down 22 | ) 23 | 24 | type CursorMode int 25 | 26 | const ( 27 | // Turn the cursor Off 28 | CursorOff CursorMode = iota 29 | // Enable Underline Cursor 30 | CursorUnderline 31 | // Enable Block Cursor 32 | CursorBlock 33 | // Blinking 34 | CursorBlink 35 | ) 36 | 37 | // TextDisplay represents an interface to a basic character device. It provides 38 | // standard methods implemented by a majority of character LCD devices. Pixel 39 | // type displays can implement this interface if desired. 40 | type TextDisplay interface { 41 | // Enable/Disable auto scroll 42 | AutoScroll(enabled bool) (err error) 43 | // Return the number of columns the display supports 44 | Cols() int 45 | // Clear the display and move the cursor home. 46 | Clear() (err error) 47 | // Set the cursor mode. You can pass multiple arguments. 48 | // Cursor(CursorOff, CursorUnderline) 49 | // 50 | // Implementations should return an error if the value of mode is not 51 | // mode>= CursorOff && mode <= CursorBlink 52 | Cursor(mode ...CursorMode) (err error) 53 | // Move the cursor home (MinRow(),MinCol()) 54 | Home() (err error) 55 | // Return the min column position. 56 | MinCol() int 57 | // Return the min row position. 58 | MinRow() int 59 | // Move the cursor forward or backward. 60 | Move(dir CursorDirection) (err error) 61 | // Move the cursor to arbitrary position. Implementations should return an 62 | // error if row < MinRow() || row > (Rows()-MinRow()), or col < MinCol() 63 | // || col > (Cols()-MinCol()) 64 | MoveTo(row, col int) (err error) 65 | // Return the number of rows the display supports. 66 | Rows() int 67 | // Turn the display on / off 68 | Display(on bool) (err error) 69 | // return info about the display. 70 | String() string 71 | // Write a set of bytes to the display. 72 | Write(p []byte) (n int, err error) 73 | // Write a string output to the display. 74 | WriteString(text string) (n int, err error) 75 | } 76 | 77 | type Intensity int 78 | 79 | // Interface for displays that support a monochrome backlight. Displays that 80 | // support RGB Backlights should implement this as well for maximum 81 | // compatibility. 82 | // 83 | // Many units that support this command write the value to EEPROM, which has a 84 | // finite number of writes. To turn the unit on/off, use TextDisplay.Display() 85 | type DisplayBacklight interface { 86 | Backlight(intensity Intensity) error 87 | } 88 | 89 | // Interface for displays that support a RGB Backlight. E.G. the Sparkfun SerLCD 90 | type DisplayRGBBacklight interface { 91 | RGBBacklight(red, green, blue Intensity) error 92 | } 93 | 94 | type Contrast int 95 | 96 | // Interface for displays that support a programmable contrast adjustment. 97 | // As with SetBacklight(), many devices serialize the value to EEPROM, 98 | // which support only a finite number of writes, so this should be used 99 | // sparingly. 100 | type DisplayContrast interface { 101 | Contrast(contrast Contrast) error 102 | } 103 | 104 | var ErrNotImplemented = errors.New("not implemented") 105 | var ErrInvalidCommand = errors.New("invalid command") 106 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package conn defines core interfaces for protocols and connections. 6 | // 7 | // This package and its subpackages describe the base interfaces to connect the 8 | // software with the real world. It doesn't contain any implementation but 9 | // includes registries to enable the application to discover the available 10 | // hardware. 11 | // 12 | // # Concepts 13 | // 14 | // periph uses 3 layered concepts for interfacing: 15 | // 16 | // Bus → Port → Conn 17 | // 18 | // Not every subpackage expose all 3 concepts. In fact, most packages don't. 19 | // For example, SPI doesn't expose Bus as the OSes generally only expose the 20 | // Port, that is, a Chip Select (CS) line must be selected right upfront to get 21 | // an handle. For I²C, there's no Port to configure, so selecting a "slave" 22 | // address is sufficient to jump directly from a Bus to a Conn. 23 | // 24 | // periph doesn't have yet a concept of star-like communication network, like 25 | // an IP network. 26 | // 27 | // # Bus 28 | // 29 | // A Bus is a multi-point communication channel where one "master" and multiple 30 | // "slaves" communicate together. In the case of periph, the Bus handle is 31 | // assumed to be the "master". The "master" generally initiates communications 32 | // and selects the "slave" to talk to. 33 | // 34 | // As the "master" selects a "slave" over a bus, a virtual Port is 35 | // automatically created. 36 | // 37 | // Examples include SPI, I²C and 1-wire. In each case, selecting a 38 | // communication line (Chip Select (CS) line for SPI, address for I²C or 39 | // 1-wire) converts the Bus into a Port. 40 | // 41 | // # Port 42 | // 43 | // A port is a point-to-point communication channel that is yet to be 44 | // initialized. It cannot be used for communication until it is connected and 45 | // transformed into a Conn. Configuring a Port converts it into a Conn. Not all 46 | // Port need configuration. 47 | // 48 | // # Conn 49 | // 50 | // A Conn is a fully configured half or full duplex communication channel that 51 | // is point-to-point, only between two devices. It is ready to use like any 52 | // readable and/or writable pipe. 53 | // 54 | // # Subpackages 55 | // 56 | // Most connection-type specific subpackages include subpackages: 57 | // 58 | // → XXXreg: registry as that is populated by the host drivers and that can be 59 | // leveraged by applications. 60 | // 61 | // → XXXtest: fake implementation that can be leveraged when writing device 62 | // driver unit test. 63 | package conn 64 | -------------------------------------------------------------------------------- /driver/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package driver devices a host peripheral driver to register when 6 | // initializing. 7 | // 8 | // Drivers that can be automatically discovered should be registered in 9 | // driverreg so discovery is done automatically. 10 | package driver 11 | 12 | // Impl is a host peripheral driver implementation. 13 | type Impl interface { 14 | // String returns the name of the driver, as to be presented to the user. 15 | // 16 | // It must be unique in the list of registered drivers. 17 | String() string 18 | // Prerequisites returns a list of drivers that must be successfully loaded 19 | // first before attempting to load this driver. 20 | // 21 | // A driver listing a prerequisite not registered is a fatal failure at 22 | // initialization time. 23 | Prerequisites() []string 24 | // After returns a list of drivers that must be loaded first before 25 | // attempting to load this driver. 26 | // 27 | // Unlike Prerequisites(), this driver will still be attempted even if the 28 | // listed driver is missing or failed to load. 29 | // 30 | // This permits serialization without hard requirement. 31 | After() []string 32 | // Init initializes the driver. 33 | // 34 | // A driver may enter one of the three following state: loaded successfully, 35 | // was skipped as irrelevant on this host, failed to load. 36 | // 37 | // On success, it must return true, nil. 38 | // 39 | // When irrelevant (skipped), it must return false, errors.New(). 40 | // 41 | // On failure, it must return true, errors.New(). The failure must 42 | // state why it failed, for example an expected OS provided driver couldn't 43 | // be opened, e.g. /dev/gpiomem on Raspbian. 44 | Init() (bool, error) 45 | } 46 | -------------------------------------------------------------------------------- /driver/driverreg/driverreg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package driverreg is a registry for all host driver implementation that can 6 | // be automatically discovered. 7 | package driverreg 8 | 9 | import ( 10 | "errors" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | 15 | "periph.io/x/conn/v3/driver" 16 | ) 17 | 18 | // DriverFailure is a driver that wasn't loaded, either because it was skipped 19 | // or because it failed to load. 20 | type DriverFailure struct { 21 | D driver.Impl 22 | Err error 23 | } 24 | 25 | func (d DriverFailure) String() string { 26 | out := d.D.String() + ": " 27 | if d.Err != nil { 28 | out += d.Err.Error() 29 | } else { 30 | out += "" 31 | } 32 | return out 33 | } 34 | 35 | // State is the state of loaded device drivers. 36 | // 37 | // Each list is sorted by the driver name. 38 | type State struct { 39 | Loaded []driver.Impl 40 | Skipped []DriverFailure 41 | Failed []DriverFailure 42 | } 43 | 44 | // Init initialises all the relevant drivers. 45 | // 46 | // Drivers are started concurrently. 47 | // 48 | // It is safe to call this function multiple times, the previous state is 49 | // returned on later calls. 50 | // 51 | // Users will want to use host.Init(), which guarantees a baseline of included 52 | // host drivers. 53 | func Init() (*State, error) { 54 | mu.Lock() 55 | defer mu.Unlock() 56 | if state != nil { 57 | return state, nil 58 | } 59 | return initImpl() 60 | } 61 | 62 | // Register registers a driver to be initialized automatically on Init(). 63 | // 64 | // The d.String() value must be unique across all registered drivers. 65 | // 66 | // It is an error to call Register() after Init() was called. 67 | func Register(d driver.Impl) error { 68 | mu.Lock() 69 | defer mu.Unlock() 70 | if state != nil { 71 | return errors.New("periph: can't call Register() after Init()") 72 | } 73 | 74 | n := d.String() 75 | if _, ok := byName[n]; ok { 76 | return errors.New("periph: driver with same name " + strconv.Quote(n) + " was already registered") 77 | } 78 | byName[n] = d 79 | return nil 80 | } 81 | 82 | // MustRegister calls Register() and panics if registration fails. 83 | // 84 | // This is the function to call in a driver's package init() function. 85 | func MustRegister(d driver.Impl) { 86 | if err := Register(d); err != nil { 87 | panic(err) 88 | } 89 | } 90 | 91 | // 92 | 93 | var ( 94 | // mu guards byName and state. 95 | // - byName is only mutated by Register(). 96 | // - state is only mutated by Init(). 97 | // 98 | // Once Init() is called, Register() refuses registering more drivers, thus 99 | // byName is immutable once Init() started. 100 | mu sync.Mutex 101 | byName = map[string]driver.Impl{} 102 | state *State 103 | ) 104 | 105 | // stage is a set of drivers that can be loaded in parallel. 106 | type stage struct { 107 | // Subset of byName drivers, for the ones in this stage. 108 | drvs map[string]driver.Impl 109 | } 110 | 111 | // explodeStages creates one or multiple stages by processing byName. 112 | // 113 | // It searches if there's any driver than has dependency on another driver and 114 | // create stages from this DAG. 115 | // 116 | // It also verifies that there is not cycle in the DAG. 117 | // 118 | // When this function starts, allDriver and byName are guaranteed to be 119 | // immutable. state must not be touched by this function. 120 | func explodeStages() ([]*stage, error) { 121 | // First, create the DAG. 122 | dag := map[string]map[string]struct{}{} 123 | for name, d := range byName { 124 | m := map[string]struct{}{} 125 | for _, p := range d.Prerequisites() { 126 | if _, ok := byName[p]; !ok { 127 | return nil, errors.New("periph: unsatisfied dependency " + strconv.Quote(name) + "->" + strconv.Quote(p) + "; it is missing; skipping") 128 | } 129 | m[p] = struct{}{} 130 | } 131 | for _, p := range d.After() { 132 | // Skip undefined drivers silently, unlike Prerequisites(). 133 | if _, ok := byName[p]; ok { 134 | m[p] = struct{}{} 135 | } 136 | } 137 | dag[name] = m 138 | } 139 | 140 | // Create stages. 141 | var stages []*stage 142 | for len(dag) != 0 { 143 | s := &stage{drvs: map[string]driver.Impl{}} 144 | for name, deps := range dag { 145 | // This driver has no dependency, add it to the current stage. 146 | if len(deps) == 0 { 147 | s.drvs[name] = byName[name] 148 | delete(dag, name) 149 | } 150 | } 151 | if len(s.drvs) == 0 { 152 | // Print out the remaining DAG so users can diagnose. 153 | // It'd probably be nicer if it were done in Register()? 154 | s := make([]string, 0, len(dag)) 155 | for name, deps := range dag { 156 | x := make([]string, 0, len(deps)) 157 | for d := range deps { 158 | x = insertString(x, d) 159 | } 160 | s = insertString(s, name+": "+strings.Join(x, ", ")) 161 | } 162 | return nil, errors.New("periph: found cycle(s) in drivers dependencies:\n" + strings.Join(s, "\n")) 163 | } 164 | stages = append(stages, s) 165 | 166 | // Trim the dependencies for the items remaining in the dag. 167 | for passed := range s.drvs { 168 | for name := range dag { 169 | delete(dag[name], passed) 170 | } 171 | } 172 | } 173 | return stages, nil 174 | } 175 | 176 | func insertDriver(l []driver.Impl, d driver.Impl) []driver.Impl { 177 | n := d.String() 178 | i := search(len(l), func(i int) bool { return l[i].String() > n }) 179 | l = append(l, nil) 180 | copy(l[i+1:], l[i:]) 181 | l[i] = d 182 | return l 183 | } 184 | 185 | func insertDriverFailure(l []DriverFailure, f DriverFailure) []DriverFailure { 186 | n := f.String() 187 | i := search(len(l), func(i int) bool { return l[i].String() > n }) 188 | l = append(l, DriverFailure{}) 189 | copy(l[i+1:], l[i:]) 190 | l[i] = f 191 | return l 192 | } 193 | 194 | func insertString(l []string, s string) []string { 195 | i := search(len(l), func(i int) bool { return l[i] > s }) 196 | l = append(l, "") 197 | copy(l[i+1:], l[i:]) 198 | l[i] = s 199 | return l 200 | } 201 | 202 | // search implements the same algorithm as sort.Search(). 203 | // 204 | // It was extracted to to not depend on sort, which depends on reflect. 205 | func search(n int, f func(int) bool) int { 206 | lo := 0 207 | for hi := n; lo < hi; { 208 | if i := int(uint(lo+hi) >> 1); !f(i) { 209 | lo = i + 1 210 | } else { 211 | hi = i 212 | } 213 | } 214 | return lo 215 | } 216 | -------------------------------------------------------------------------------- /driver/driverreg/driverreg_parallel.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // This file contains the parallelized driver loading logic. It is meant to be 6 | // load the drivers as fast as possible by parallelising work. 7 | 8 | //go:build !tinygo 9 | // +build !tinygo 10 | 11 | package driverreg 12 | 13 | import ( 14 | "errors" 15 | "strconv" 16 | "sync" 17 | 18 | "periph.io/x/conn/v3/driver" 19 | ) 20 | 21 | func initImpl() (*State, error) { 22 | state = &State{} 23 | // At this point, byName is guaranteed to be immutable. 24 | cD := make(chan driver.Impl) 25 | cS := make(chan DriverFailure) 26 | cE := make(chan DriverFailure) 27 | var wg sync.WaitGroup 28 | wg.Add(1) 29 | go func() { 30 | defer wg.Done() 31 | for d := range cD { 32 | state.Loaded = insertDriver(state.Loaded, d) 33 | } 34 | }() 35 | wg.Add(1) 36 | go func() { 37 | defer wg.Done() 38 | for f := range cS { 39 | state.Skipped = insertDriverFailure(state.Skipped, f) 40 | } 41 | }() 42 | wg.Add(1) 43 | go func() { 44 | defer wg.Done() 45 | for f := range cE { 46 | state.Failed = insertDriverFailure(state.Failed, f) 47 | } 48 | }() 49 | 50 | stages, err := explodeStages() 51 | if err != nil { 52 | return state, err 53 | } 54 | loaded := sync.Map{} 55 | for _, s := range stages { 56 | // It's very important that each of the stage is fully completed before the 57 | // next one is attempted. 58 | s.loadParallel(&loaded, cD, cS, cE) 59 | } 60 | close(cD) 61 | close(cS) 62 | close(cE) 63 | wg.Wait() 64 | return state, nil 65 | } 66 | 67 | // loadParallel loads all the drivers for this stage in parallel and returns 68 | // once they are all loaded. 69 | func (s *stage) loadParallel(loaded *sync.Map, cD chan<- driver.Impl, cS, cE chan<- DriverFailure) { 70 | success := make(chan string) 71 | go func() { 72 | defer close(success) 73 | wg := sync.WaitGroup{} 74 | loop: 75 | for name, drv := range s.drvs { 76 | // Intentionally do not look at After(), only Prerequisites(). 77 | for _, dep := range drv.Prerequisites() { 78 | if _, ok := loaded.Load(dep); !ok { 79 | cS <- DriverFailure{drv, errors.New("dependency not loaded: " + strconv.Quote(dep))} 80 | continue loop 81 | } 82 | } 83 | 84 | // Not skipped driver, attempt loading in a goroutine. 85 | wg.Add(1) 86 | go func(n string, d driver.Impl) { 87 | defer wg.Done() 88 | if ok, err := d.Init(); ok { 89 | if err == nil { 90 | cD <- d 91 | success <- n 92 | return 93 | } 94 | cE <- DriverFailure{d, err} 95 | } else { 96 | cS <- DriverFailure{d, err} 97 | } 98 | }(name, drv) 99 | } 100 | wg.Wait() 101 | }() 102 | for s := range success { 103 | loaded.Store(s, nil) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /driver/driverreg/driverreg_serial.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // This file contains the single threaded driver loading code, to be used on 6 | // low performance cores. 7 | 8 | //go:build tinygo 9 | // +build tinygo 10 | 11 | package driverreg 12 | 13 | import ( 14 | "errors" 15 | "strconv" 16 | ) 17 | 18 | func initImpl() (*State, error) { 19 | state = &State{} 20 | // At this point, byName is guaranteed to be immutable. 21 | stages, err := explodeStages() 22 | if err != nil { 23 | return state, err 24 | } 25 | loaded := make(map[string]struct{}, len(byName)) 26 | for _, s := range stages { 27 | s.loadSerial(state, loaded) 28 | } 29 | return state, nil 30 | } 31 | 32 | // loadSerial loads all the drivers for this stage, one after the other. 33 | func (s *stage) loadSerial(state *State, loaded map[string]struct{}) { 34 | for name, drv := range s.drvs { 35 | // Intentionally do not look at After(), only Prerequisites(). 36 | for _, dep := range drv.Prerequisites() { 37 | if _, ok := loaded[dep]; !ok { 38 | state.Skipped = insertDriverFailure(state.Skipped, DriverFailure{drv, errors.New("dependency not loaded: " + strconv.Quote(dep))}) 39 | goto loop 40 | } 41 | } 42 | 43 | // Not skipped driver, attempt loading in a goroutine. 44 | if ok, err := drv.Init(); ok { 45 | if err == nil { 46 | state.Loaded = insertDriver(state.Loaded, drv) 47 | loaded[name] = struct{}{} 48 | } else { 49 | state.Failed = insertDriverFailure(state.Failed, DriverFailure{drv, err}) 50 | } 51 | } else { 52 | state.Skipped = insertDriverFailure(state.Skipped, DriverFailure{drv, err}) 53 | } 54 | loop: 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /driver/driverreg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package driverreg_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | ) 13 | 14 | func Example() { 15 | // Make sure periph is initialized. 16 | // TODO: Use host.Init(). It is not used in this example to prevent circular 17 | // go package import. 18 | state, err := driverreg.Init() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | // Prints the loaded driver. 24 | fmt.Printf("Using drivers:\n") 25 | for _, driver := range state.Loaded { 26 | fmt.Printf("- %s\n", driver) 27 | } 28 | 29 | // Prints the driver that were skipped as irrelevant on the platform. 30 | fmt.Printf("Drivers skipped:\n") 31 | for _, failure := range state.Skipped { 32 | fmt.Printf("- %s: %s\n", failure.D, failure.Err) 33 | } 34 | 35 | // Having drivers failing to load may not require process termination. It 36 | // is possible to continue to run in partial failure mode. 37 | fmt.Printf("Drivers failed to load:\n") 38 | for _, failure := range state.Failed { 39 | fmt.Printf("- %s: %v\n", failure.D, failure.Err) 40 | } 41 | 42 | // Use pins, buses, devices, etc. 43 | } 44 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package conn_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/physic" 13 | "periph.io/x/conn/v3/spi" 14 | "periph.io/x/conn/v3/spi/spireg" 15 | ) 16 | 17 | func Example() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // Using SPI as an example. See package ./spi/spireg for more details. 26 | p, err := spireg.Open("") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer p.Close() 31 | c, err := p.Connect(physic.MegaHertz, spi.Mode3, 8) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | // Write 0x10 to the device, and read a byte right after. 37 | write := []byte{0x10, 0x00} 38 | read := make([]byte, len(write)) 39 | if err := c.Tx(write, read); err != nil { 40 | log.Fatal(err) 41 | } 42 | // Use read. 43 | fmt.Printf("%v\n", read[1:]) 44 | } 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | module periph.io/x/conn/v3 6 | 7 | go 1.22.6 8 | 9 | // Warning: do not add any external dependencies here unless absolutely necessary. 10 | // This package should primarily depend on the standard library. 11 | 12 | require github.com/jonboulle/clockwork v0.4.0 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= 2 | github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= 3 | -------------------------------------------------------------------------------- /gpio/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpio_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/conn/v3/physic" 15 | ) 16 | 17 | func Example() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // Use gpioreg GPIO pin registry to find a GPIO pin by name. 26 | p := gpioreg.ByName("GPIO6") 27 | if p == nil { 28 | log.Fatal("Failed to find GPIO6") 29 | } 30 | 31 | // A pin can be read, independent of its state; it doesn't matter if it is 32 | // set as input or output. 33 | fmt.Printf("%s is %s\n", p, p.Read()) 34 | } 35 | 36 | func ExampleParseDuty() { 37 | d, err := gpio.ParseDuty("33%") 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | fmt.Println(d) 42 | // Output: 43 | // 33% 44 | } 45 | 46 | func ExamplePinIn() { 47 | // Make sure periph is initialized. 48 | // TODO: Use host.Init(). It is not used in this example to prevent circular 49 | // go package import. 50 | if _, err := driverreg.Init(); err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | // Use gpioreg GPIO pin registry to find a GPIO pin by name. 55 | p := gpioreg.ByName("GPIO6") 56 | if p == nil { 57 | log.Fatal("Failed to find GPIO6") 58 | } 59 | 60 | // Set it as input, with a pull down (defaults to Low when unconnected) and 61 | // enable rising edge triggering. 62 | if err := p.In(gpio.PullDown, gpio.RisingEdge); err != nil { 63 | log.Fatal(err) 64 | } 65 | fmt.Printf("%s is %s\n", p, p.Read()) 66 | 67 | // Wait for rising edges (Low -> High) and print when one occur. 68 | for p.WaitForEdge(-1) { 69 | fmt.Printf("%s went %s\n", p, gpio.High) 70 | } 71 | } 72 | 73 | func ExamplePinOut() { 74 | // Make sure periph is initialized. 75 | // TODO: Use host.Init(). It is not used in this example to prevent circular 76 | // go package import. 77 | if _, err := driverreg.Init(); err != nil { 78 | log.Fatal(err) 79 | } 80 | 81 | // Use gpioreg GPIO pin registry to find a GPIO pin by name. 82 | p := gpioreg.ByName("GPIO6") 83 | if p == nil { 84 | log.Fatal("Failed to find GPIO6") 85 | } 86 | 87 | // Set the pin as output High. 88 | if err := p.Out(gpio.High); err != nil { 89 | log.Fatal(err) 90 | } 91 | } 92 | 93 | func ExamplePinOut_pWM() { 94 | // Make sure periph is initialized. 95 | // TODO: Use host.Init(). It is not used in this example to prevent circular 96 | // go package import. 97 | if _, err := driverreg.Init(); err != nil { 98 | log.Fatal(err) 99 | } 100 | 101 | // Use gpioreg GPIO pin registry to find a GPIO pin by name. 102 | p := gpioreg.ByName("GPIO6") 103 | if p == nil { 104 | log.Fatal("Failed to find GPIO6") 105 | } 106 | 107 | // Generate a 33% duty cycle 10KHz signal. 108 | if err := p.PWM(gpio.DutyMax/3, 10*physic.KiloHertz); err != nil { 109 | log.Fatal(err) 110 | } 111 | } 112 | 113 | func ExampleRealPin() { 114 | // Make sure periph is initialized. 115 | // TODO: Use host.Init(). It is not used in this example to prevent circular 116 | // go package import. 117 | if _, err := driverreg.Init(); err != nil { 118 | log.Fatal(err) 119 | } 120 | 121 | // Use gpioreg GPIO pin registry to find a GPIO pin by name. 122 | p := gpioreg.ByName("P1_3") 123 | if p == nil { 124 | log.Fatal("Failed to find P1_3") 125 | } 126 | fmt.Printf("P1_3: %s", p) 127 | 128 | // Resolve the real underlying pin. 129 | if r, ok := p.(gpio.RealPin); ok { 130 | // On Raspberry Pis, pin #3 on header P1 is an alias for GPIO2. 131 | fmt.Printf("%s is in fact %s", p, r.Real()) 132 | } else { 133 | log.Printf("%s is not an alias", p) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /gpio/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpio 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | // Inputs 12 | IN pin.Func = "IN" // Input 13 | IN_HIGH pin.Func = "In/High" // Read high 14 | IN_LOW pin.Func = "In/Low" // Read low 15 | 16 | // Outputs 17 | OUT pin.Func = "OUT" // Output, drive 18 | OUT_OC pin.Func = "OUT_OPEN" // Output, open collector/drain 19 | OUT_HIGH pin.Func = "Out/High" // Drive high 20 | OUT_LOW pin.Func = "Out/Low" // Drive low; open collector low 21 | 22 | FLOAT pin.Func = "FLOAT" // Input float or Output open collector high 23 | 24 | CLK pin.Func = "CLK" // Clock is a subset of a PWM, with a 50% duty cycle 25 | PWM pin.Func = "PWM" // Pulse Width Modulation, which is a clock with variable duty cycle 26 | ) 27 | -------------------------------------------------------------------------------- /gpio/gpio_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpio 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "time" 11 | 12 | "periph.io/x/conn/v3/physic" 13 | "periph.io/x/conn/v3/pin" 14 | ) 15 | 16 | func TestStrings(t *testing.T) { 17 | data := []struct { 18 | t fmt.Stringer 19 | s string 20 | }{ 21 | {Low, "Low"}, 22 | {High, "High"}, 23 | {PullNoChange, "PullNoChange"}, 24 | {Float, "Float"}, 25 | {PullDown, "PullDown"}, 26 | {PullUp, "PullUp"}, 27 | {Pull(100), "Pull(100)"}, 28 | {NoEdge, "NoEdge"}, 29 | {Edge(100), "Edge(100)"}, 30 | } 31 | for i, l := range data { 32 | if a := l.t.String(); a != l.s { 33 | t.Fatalf("#%d: %s != %s", i, l.s, a) 34 | } 35 | } 36 | } 37 | 38 | func TestDuty_String(t *testing.T) { 39 | data := []struct { 40 | d Duty 41 | expected string 42 | }{ 43 | {0, "0%"}, 44 | {1, "0%"}, 45 | {DutyMax / 200, "0%"}, 46 | {DutyMax/100 - 1, "1%"}, 47 | {DutyMax / 100, "1%"}, 48 | {DutyMax, "100%"}, 49 | {DutyMax - 1, "100%"}, 50 | {DutyHalf, "50%"}, 51 | {DutyHalf + 1, "50%"}, 52 | {DutyHalf - 1, "50%"}, 53 | {DutyHalf + DutyMax/100, "51%"}, 54 | {DutyHalf - DutyMax/100, "49%"}, 55 | } 56 | for i, line := range data { 57 | if actual := line.d.String(); actual != line.expected { 58 | t.Fatalf("line %d: Duty(%d).String() == %q, expected %q", i, line.d, actual, line.expected) 59 | } 60 | } 61 | } 62 | 63 | func TestDuty_Valid(t *testing.T) { 64 | if !Duty(0).Valid() { 65 | t.Fatal("0 is valid") 66 | } 67 | if !DutyHalf.Valid() { 68 | t.Fatal("half is valid") 69 | } 70 | if !DutyMax.Valid() { 71 | t.Fatal("half is valid") 72 | } 73 | if Duty(-1).Valid() { 74 | t.Fatal("-1 is not valid") 75 | } 76 | if (DutyMax + 1).Valid() { 77 | t.Fatal("-1 is not valid") 78 | } 79 | } 80 | 81 | func TestParseDuty(t *testing.T) { 82 | data := []struct { 83 | input string 84 | d Duty 85 | hasErr bool 86 | }{ 87 | {"", 0, true}, 88 | {"0", 0, false}, 89 | {"0%", 0, false}, 90 | {"1", 1, false}, 91 | {"1%", 167772, false}, 92 | {"100%", DutyMax, false}, 93 | {"16777216", 16777216, false}, 94 | {"16777217", 0, true}, 95 | {"101%", 0, true}, 96 | {"-1", 0, true}, 97 | {"-1%", 0, true}, 98 | } 99 | for i, line := range data { 100 | if d, err := ParseDuty(line.input); d != line.d || (err != nil) != line.hasErr { 101 | t.Fatalf("line %d: Parse(%q) == %d, %q, expected %d, %t", i, line.input, d, err, line.d, line.hasErr) 102 | } 103 | } 104 | } 105 | 106 | func TestInvalid(t *testing.T) { 107 | // conn.Resource 108 | if s := INVALID.String(); s != "INVALID" { 109 | t.Fatal(s) 110 | } 111 | if err := INVALID.Halt(); err != nil { 112 | t.Fatal(err) 113 | } 114 | // pin.Pin 115 | if s := INVALID.Name(); s != "INVALID" { 116 | t.Fatal(s) 117 | } 118 | if n := INVALID.Number(); n != -1 { 119 | t.Fatal(n) 120 | } 121 | if s := INVALID.Function(); s != "" { 122 | t.Fatal(s) 123 | } 124 | // gpio.PinIn 125 | if err := INVALID.In(Float, NoEdge); err != errInvalidPin { 126 | t.Fatal(err) 127 | } 128 | if l := INVALID.Read(); l != Low { 129 | t.Fatal(l) 130 | } 131 | if INVALID.WaitForEdge(time.Minute) { 132 | t.Fatal("unexpected edge") 133 | } 134 | if p := INVALID.Pull(); p != PullNoChange { 135 | t.Fatal(p) 136 | } 137 | if p := INVALID.DefaultPull(); p != PullNoChange { 138 | t.Fatal(p) 139 | } 140 | // gpio.PinOut 141 | if err := INVALID.Out(Low); err != errInvalidPin { 142 | t.Fatal(err) 143 | } 144 | if err := INVALID.PWM(DutyMax, physic.Hertz); err != errInvalidPin { 145 | t.Fatal(err) 146 | } 147 | // pin.PinFunc 148 | if f := INVALID.(pin.PinFunc).Func(); f != pin.FuncNone { 149 | t.Fatal(f) 150 | } 151 | if f := INVALID.(pin.PinFunc).SupportedFuncs(); len(f) != 0 { 152 | t.Fatal(f) 153 | } 154 | if err := INVALID.(pin.PinFunc).SetFunc(IN_LOW); err == nil { 155 | t.Fatal("can't set func") 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /gpio/gpioreg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioreg_test 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "log" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/gpio/gpioreg" 15 | ) 16 | 17 | func Example() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // A command line tool may let the user choose a GPIO pin. 26 | name := flag.String("p", "", "GPIO pin to use") 27 | flag.Parse() 28 | if *name == "" { 29 | log.Fatal("-p is required") 30 | } 31 | p := gpioreg.ByName(*name) 32 | if p == nil { 33 | log.Fatalf("Failed to find %s", *name) 34 | } 35 | 36 | // Set the pin as output High. 37 | if err := p.Out(gpio.High); err != nil { 38 | log.Fatal(err) 39 | } 40 | } 41 | 42 | func ExampleAll() { 43 | // Make sure periph is initialized. 44 | // TODO: Use host.Init(). It is not used in this example to prevent circular 45 | // go package import. 46 | if _, err := driverreg.Init(); err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | fmt.Print("GPIO pins available:\n") 51 | for _, p := range gpioreg.All() { 52 | fmt.Printf("- %s: %s\n", p, p.Function()) 53 | } 54 | } 55 | 56 | func ExampleByName_alias() { 57 | // Make sure periph is initialized. 58 | // TODO: Use host.Init(). It is not used in this example to prevent circular 59 | // go package import. 60 | if _, err := driverreg.Init(); err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | // LCD-D2 is a pin found on the C.H.I.P. 65 | p := gpioreg.ByName("LCD-D2") 66 | if p == nil { 67 | log.Fatal("Failed to find LCD-D2") 68 | } 69 | if rp, ok := p.(gpio.RealPin); ok { 70 | fmt.Printf("%s is an alias for %s\n", p, rp.Real()) 71 | } else { 72 | fmt.Printf("%s is not an alias!\n", p) 73 | } 74 | } 75 | 76 | func ExampleByName_number() { 77 | // Make sure periph is initialized. 78 | // TODO: Use host.Init(). It is not used in this example to prevent circular 79 | // go package import. 80 | if _, err := driverreg.Init(); err != nil { 81 | log.Fatal(err) 82 | } 83 | 84 | // The string representation of a number works too. 85 | p := gpioreg.ByName("6") 86 | if p == nil { 87 | log.Fatal("Failed to find GPIO6") 88 | } 89 | fmt.Printf("%s: %s\n", p, p.Function()) 90 | } 91 | -------------------------------------------------------------------------------- /gpio/gpioreg/natsort.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioreg 6 | 7 | import ( 8 | "strconv" 9 | ) 10 | 11 | // lessNatural does a 'natural' comparison on the two strings. 12 | // 13 | // It is extracted from https://github.com/maruel/natural. 14 | func lessNatural(a, b string) bool { 15 | for { 16 | if a == b { 17 | return false 18 | } 19 | if p := commonPrefix(a, b); p != 0 { 20 | a = a[p:] 21 | b = b[p:] 22 | } 23 | if ia := digits(a); ia > 0 { 24 | if ib := digits(b); ib > 0 { 25 | // Both sides have digits. 26 | an, aerr := strconv.ParseUint(a[:ia], 10, 64) 27 | bn, berr := strconv.ParseUint(b[:ib], 10, 64) 28 | if aerr == nil && berr == nil { 29 | if an != bn { 30 | return an < bn 31 | } 32 | // Semantically the same digits, e.g. "00" == "0", "01" == "1". In 33 | // this case, only continue processing if there's trailing data on 34 | // both sides, otherwise do lexical comparison. 35 | if ia != len(a) && ib != len(b) { 36 | a = a[ia:] 37 | b = b[ib:] 38 | continue 39 | } 40 | } 41 | } 42 | } 43 | return a < b 44 | } 45 | } 46 | 47 | // commonPrefix returns the common prefix except for digits. 48 | func commonPrefix(a, b string) int { 49 | m := len(a) 50 | if n := len(b); n < m { 51 | m = n 52 | } 53 | if m == 0 { 54 | return 0 55 | } 56 | _ = a[m-1] 57 | _ = b[m-1] 58 | for i := 0; i < m; i++ { 59 | ca := a[i] 60 | cb := b[i] 61 | if (ca >= '0' && ca <= '9') || (cb >= '0' && cb <= '9') || ca != cb { 62 | return i 63 | } 64 | } 65 | return m 66 | } 67 | 68 | func digits(s string) int { 69 | for i := 0; i < len(s); i++ { 70 | c := s[i] 71 | if c < '0' || c > '9' { 72 | return i 73 | } 74 | } 75 | return len(s) 76 | } 77 | -------------------------------------------------------------------------------- /gpio/gpioreg/natsort_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Extracted from https://github.com/maruel/natural for code coverage. 6 | 7 | package gpioreg 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestLessLess(t *testing.T) { 14 | data := [][2]string{ 15 | {"", "a"}, 16 | {"a", "b"}, 17 | {"a", "aa"}, 18 | {"a0", "a1"}, 19 | {"a0", "a00"}, 20 | {"a00", "a01"}, 21 | {"a01", "a2"}, 22 | {"a01x", "a2x"}, 23 | // Only the last number matters. 24 | {"a0b00", "a00b1"}, 25 | {"a0b00", "a00b01"}, 26 | {"a00b0", "a0b00"}, 27 | {"a00b00", "a0b01"}, 28 | {"a00b00", "a0b1"}, 29 | } 30 | for _, l := range data { 31 | if !lessNatural(l[0], l[1]) { 32 | t.Fatalf("Less(%q, %q) returned false", l[0], l[1]) 33 | } 34 | } 35 | } 36 | 37 | func TestLessNot(t *testing.T) { 38 | data := [][2]string{ 39 | {"a", ""}, 40 | {"a", "a"}, 41 | {"aa", "a"}, 42 | {"b", "a"}, 43 | {"a01", "a00"}, 44 | {"a01", "a01"}, 45 | {"a1", "a1"}, 46 | {"a2", "a01"}, 47 | {"a2x", "a01x"}, 48 | {"a00b00", "a0b0"}, 49 | {"a00b01", "a0b00"}, 50 | {"a00b00", "a0b00"}, 51 | } 52 | for _, l := range data { 53 | if lessNatural(l[0], l[1]) { 54 | t.Fatalf("Less(%q, %q) returned true", l[0], l[1]) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gpio/gpiostream/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpiostream_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/conn/v3/gpio/gpiostream" 15 | "periph.io/x/conn/v3/physic" 16 | ) 17 | 18 | func ExampleBitStream() { 19 | fmt.Printf("Format is LSB-first; least significant bit first:\n") 20 | stream := gpiostream.BitStream{ 21 | Bits: []byte{0x80, 0x01, 0xAA, 0x55}, 22 | Freq: physic.MegaHertz, 23 | LSBF: true, 24 | } 25 | for _, l := range stream.Bits { 26 | fmt.Printf("0x%02X: ", l) 27 | for j := 0; j < 8; j++ { 28 | mask := byte(1) << uint(j) 29 | fmt.Printf("%4s,", gpio.Level(l&mask != 0)) 30 | if j != 7 { 31 | fmt.Printf(" ") 32 | } 33 | } 34 | fmt.Printf("\n") 35 | } 36 | fmt.Printf("\n") 37 | 38 | fmt.Printf("Format is MSB-first; most significant bit first:\n") 39 | stream = gpiostream.BitStream{ 40 | Bits: []byte{0x80, 0x01, 0xAA, 0x55}, 41 | Freq: physic.MegaHertz, 42 | LSBF: false, 43 | } 44 | for _, l := range stream.Bits { 45 | fmt.Printf("0x%02X: ", l) 46 | for j := 7; j >= 0; j-- { 47 | mask := byte(1) << uint(j) 48 | fmt.Printf("%4s,", gpio.Level(l&mask != 0)) 49 | if j != 0 { 50 | fmt.Printf(" ") 51 | } 52 | } 53 | fmt.Printf("\n") 54 | } 55 | // Output: 56 | // Format is LSB-first; least significant bit first: 57 | // 0x80: Low, Low, Low, Low, Low, Low, Low, High, 58 | // 0x01: High, Low, Low, Low, Low, Low, Low, Low, 59 | // 0xAA: Low, High, Low, High, Low, High, Low, High, 60 | // 0x55: High, Low, High, Low, High, Low, High, Low, 61 | // 62 | // Format is MSB-first; most significant bit first: 63 | // 0x80: High, Low, Low, Low, Low, Low, Low, Low, 64 | // 0x01: Low, Low, Low, Low, Low, Low, Low, High, 65 | // 0xAA: High, Low, High, Low, High, Low, High, Low, 66 | // 0x55: Low, High, Low, High, Low, High, Low, High, 67 | } 68 | 69 | func ExamplePinIn() { 70 | // Make sure periph is initialized. 71 | // TODO: Use host.Init(). It is not used in this example to prevent circular 72 | // go package import. 73 | if _, err := driverreg.Init(); err != nil { 74 | log.Fatal(err) 75 | } 76 | 77 | // Read one second of sample at 1ms resolution and print the values read. 78 | p := gpioreg.ByName("GPIO3") 79 | r, ok := p.(gpiostream.PinIn) 80 | if !ok { 81 | log.Fatalf("pin streaming is not supported on pin %s", p) 82 | } 83 | b := gpiostream.BitStream{Freq: physic.KiloHertz, Bits: make([]byte, 1000/8)} 84 | if err := r.StreamIn(gpio.PullNoChange, &b); err != nil { 85 | log.Fatal(err) 86 | } 87 | for i, l := range b.Bits { 88 | // Bits format is in MSB; the most significant bit is streamed first. 89 | for j := 7; j >= 0; j-- { 90 | mask := byte(1) << uint(j) 91 | fmt.Printf("%4s, ", gpio.Level(l&mask != 0)) 92 | } 93 | if i&1 == 1 { 94 | fmt.Printf("\n") 95 | } 96 | } 97 | } 98 | 99 | func ExamplePinOut() { 100 | // Make sure periph is initialized. 101 | // TODO: Use host.Init(). It is not used in this example to prevent circular 102 | // go package import. 103 | if _, err := driverreg.Init(); err != nil { 104 | log.Fatal(err) 105 | } 106 | 107 | // Generates a 25% duty cycle PWM at 1kHz for 5 seconds with a precision of 108 | // 1µs. 109 | p := gpioreg.ByName("GPIO3") 110 | r, ok := p.(gpiostream.PinOut) 111 | if !ok { 112 | log.Fatalf("pin streaming is not supported on pin %s", p) 113 | } 114 | b := gpiostream.Program{ 115 | Parts: []gpiostream.Stream{ 116 | &gpiostream.EdgeStream{ 117 | Freq: physic.MegaHertz, 118 | Edges: []uint16{250, 750}, 119 | }, 120 | }, 121 | Loops: 5000, 122 | } 123 | if err := r.StreamOut(&b); err != nil { 124 | log.Fatal(err) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /gpio/gpiostream/gpiostream_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpiostream 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestBitStream(t *testing.T) { 15 | var b [16]byte 16 | for i := range b { 17 | b[i] = byte(i) 18 | } 19 | s := BitStream{Freq: physic.Hertz, Bits: b[:], LSBF: true} 20 | if f := s.Frequency(); f != physic.Hertz { 21 | t.Fatal(f) 22 | } 23 | if d := s.Duration(); d != 16*8*time.Second { 24 | t.Fatal(d) 25 | } 26 | if g := s.GoString(); g != "&gpiostream.BitStream{Bits: 000102030405060708090a0b0c0d0e0f, Freq:1Hz, LSBF:true}" { 27 | t.Fatal(g) 28 | } 29 | } 30 | 31 | func TestBitStream_Empty(t *testing.T) { 32 | var b [16]byte 33 | s := BitStream{Bits: b[:]} 34 | if d := s.Duration(); d != 0 { 35 | t.Fatal(d) 36 | } 37 | } 38 | 39 | func TestEdgeStream(t *testing.T) { 40 | s := EdgeStream{Freq: physic.KiloHertz, Edges: []uint16{1000, 1}} 41 | if f := s.Frequency(); f != physic.KiloHertz { 42 | t.Fatal(f) 43 | } 44 | if d := s.Duration(); d != 1001*time.Millisecond { 45 | t.Fatal(d) 46 | } 47 | s = EdgeStream{Edges: []uint16{1000, 1}} 48 | if d := s.Duration(); d != 0 { 49 | t.Fatal(d) 50 | } 51 | } 52 | 53 | func TestProgram(t *testing.T) { 54 | s := Program{ 55 | Parts: []Stream{ 56 | &EdgeStream{Freq: physic.KiloHertz, Edges: []uint16{1000, 1}}, 57 | &BitStream{Freq: physic.Hertz, Bits: make([]byte, 100)}, 58 | }, 59 | Loops: 2, 60 | } 61 | if f := s.Frequency(); f != physic.KiloHertz { 62 | t.Fatal(f) 63 | } 64 | if d := s.Duration(); d != 2*(100*8*time.Second+1001*time.Millisecond) { 65 | t.Fatal(d) 66 | } 67 | s = Program{Loops: 0} 68 | if f := s.Frequency(); f != 0 { 69 | t.Fatal(f) 70 | } 71 | if d := s.Duration(); d != 0 { 72 | t.Fatal(d) 73 | } 74 | s = Program{Parts: []Stream{&Program{}}, Loops: -1} 75 | if f := s.Frequency(); f != 0 { 76 | t.Fatal(f) 77 | } 78 | if d := s.Duration(); d != 0 { 79 | t.Fatal(d) 80 | } 81 | } 82 | 83 | func TestProgram_Nyquist(t *testing.T) { 84 | s := Program{ 85 | Parts: []Stream{ 86 | &BitStream{Freq: 998 * physic.MilliHertz, Bits: make([]byte, 1)}, 87 | &BitStream{Freq: physic.Hertz, Bits: make([]byte, 1)}, 88 | &BitStream{Freq: 200 * physic.MilliHertz, Bits: make([]byte, 1)}, 89 | }, 90 | Loops: 1, 91 | } 92 | // TODO(maruel): This will cause small aliasing on the first BitStream. 93 | if f := s.Frequency(); f != 2*physic.Hertz { 94 | t.Fatal(f) 95 | } 96 | 97 | if d := s.Duration(); d != 56016032064*time.Nanosecond { 98 | t.Fatal(d) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /gpio/gpiotest/gpiotest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package gpiotest is meant to be used to test drivers using fake Pins. 6 | package gpiotest 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "log" 12 | "sync" 13 | "time" 14 | 15 | "github.com/jonboulle/clockwork" 16 | "periph.io/x/conn/v3/gpio" 17 | "periph.io/x/conn/v3/physic" 18 | "periph.io/x/conn/v3/pin" 19 | ) 20 | 21 | // Pin implements gpio.PinIO. 22 | // 23 | // Modify its members to simulate hardware events. 24 | type Pin struct { 25 | // These should be immutable. 26 | N string 27 | Num int 28 | Fn string // TODO(maruel): pin.Func in v4. 29 | 30 | // These are safe to use concurrently. 31 | Clock clockwork.Clock // If nil, real clock will be used. 32 | 33 | // Grab the Mutex before accessing the following members. 34 | sync.Mutex 35 | L gpio.Level // Used for both input and output 36 | P gpio.Pull 37 | EdgesChan chan gpio.Level // Use it to fake edges 38 | D gpio.Duty // PWM duty 39 | F physic.Frequency // PWM period 40 | } 41 | 42 | // String implements conn.Resource. 43 | func (p *Pin) String() string { 44 | return fmt.Sprintf("%s(%d)", p.N, p.Num) 45 | } 46 | 47 | // Halt implements conn.Resource. 48 | // 49 | // It has no effect. 50 | func (p *Pin) Halt() error { 51 | return nil 52 | } 53 | 54 | // Name implements pin.Pin. 55 | func (p *Pin) Name() string { 56 | return p.N 57 | } 58 | 59 | // Number implements pin.Pin. 60 | func (p *Pin) Number() int { 61 | return p.Num 62 | } 63 | 64 | // Function implements pin.Pin. 65 | func (p *Pin) Function() string { 66 | return p.Fn 67 | } 68 | 69 | // Func implements pin.PinFunc. 70 | func (p *Pin) Func() pin.Func { 71 | return pin.Func(p.Fn) 72 | } 73 | 74 | // SupportedFuncs implements pin.PinFunc. 75 | func (p *Pin) SupportedFuncs() []pin.Func { 76 | return []pin.Func{gpio.IN, gpio.OUT} 77 | } 78 | 79 | // SetFunc implements pin.PinFunc. 80 | func (p *Pin) SetFunc(f pin.Func) error { 81 | return errors.New("gpiotest: not supported") 82 | } 83 | 84 | // In implements gpio.PinIn. 85 | func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error { 86 | p.Lock() 87 | defer p.Unlock() 88 | p.P = pull 89 | if pull == gpio.PullDown { 90 | p.L = gpio.Low 91 | } else if pull == gpio.PullUp { 92 | p.L = gpio.High 93 | } 94 | if edge != gpio.NoEdge && p.EdgesChan == nil { 95 | return errors.New("gpiotest: please set p.EdgesChan first") 96 | } 97 | // Flush any buffered edges. 98 | for { 99 | select { 100 | case <-p.EdgesChan: 101 | default: 102 | return nil 103 | } 104 | } 105 | } 106 | 107 | // Read implements gpio.PinIn. 108 | func (p *Pin) Read() gpio.Level { 109 | p.Lock() 110 | defer p.Unlock() 111 | return p.L 112 | } 113 | 114 | // WaitForEdge implements gpio.PinIn. 115 | func (p *Pin) WaitForEdge(timeout time.Duration) bool { 116 | if p.Clock == nil { 117 | p.Clock = clockwork.NewRealClock() 118 | } 119 | 120 | if timeout == -1 { 121 | _ = p.Out(<-p.EdgesChan) 122 | return true 123 | } 124 | select { 125 | case <-p.Clock.After(timeout): 126 | return false 127 | case l := <-p.EdgesChan: 128 | _ = p.Out(l) 129 | return true 130 | } 131 | } 132 | 133 | // Pull implements gpio.PinIn. 134 | func (p *Pin) Pull() gpio.Pull { 135 | return p.P 136 | } 137 | 138 | // DefaultPull implements gpio.PinIn. 139 | func (p *Pin) DefaultPull() gpio.Pull { 140 | return p.P 141 | } 142 | 143 | // Out implements gpio.PinOut. 144 | func (p *Pin) Out(l gpio.Level) error { 145 | p.Lock() 146 | defer p.Unlock() 147 | p.L = l 148 | return nil 149 | } 150 | 151 | // PWM implements gpio.PinOut. 152 | func (p *Pin) PWM(duty gpio.Duty, f physic.Frequency) error { 153 | p.Lock() 154 | defer p.Unlock() 155 | p.D = duty 156 | p.F = f 157 | return nil 158 | } 159 | 160 | // LogPinIO logs when its state changes. 161 | type LogPinIO struct { 162 | gpio.PinIO 163 | } 164 | 165 | // Real implements gpio.RealPin. 166 | func (p *LogPinIO) Real() gpio.PinIO { 167 | return p.PinIO 168 | } 169 | 170 | // In implements gpio.PinIn. 171 | func (p *LogPinIO) In(pull gpio.Pull, edge gpio.Edge) error { 172 | log.Printf("%s.In(%s, %s)", p, pull, edge) 173 | return p.PinIO.In(pull, edge) 174 | } 175 | 176 | // Read implements gpio.PinIn. 177 | func (p *LogPinIO) Read() gpio.Level { 178 | l := p.PinIO.Read() 179 | log.Printf("%s.Read() %s", p, l) 180 | return l 181 | } 182 | 183 | // WaitForEdge implements gpio.PinIn. 184 | func (p *LogPinIO) WaitForEdge(timeout time.Duration) bool { 185 | s := time.Now() 186 | r := p.PinIO.WaitForEdge(timeout) 187 | log.Printf("%s.WaitForEdge(%s) -> %t after %s", p, timeout, r, time.Since(s)) 188 | return r 189 | } 190 | 191 | // Out implements gpio.PinOut. 192 | func (p *LogPinIO) Out(l gpio.Level) error { 193 | log.Printf("%s.Out(%s)", p, l) 194 | return p.PinIO.Out(l) 195 | } 196 | 197 | // PWM implements gpio.PinOut. 198 | func (p *LogPinIO) PWM(duty gpio.Duty, f physic.Frequency) error { 199 | log.Printf("%s.PWM(%s, %s)", p, duty, f) 200 | return p.PinIO.PWM(duty, f) 201 | } 202 | 203 | var _ gpio.PinIO = &Pin{} 204 | var _ pin.PinFunc = &Pin{} 205 | -------------------------------------------------------------------------------- /gpio/gpiotest/gpiotest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpiotest 6 | 7 | import ( 8 | "flag" 9 | "io" 10 | "log" 11 | "os" 12 | "reflect" 13 | "testing" 14 | "time" 15 | 16 | "periph.io/x/conn/v3/gpio" 17 | "periph.io/x/conn/v3/gpio/gpioreg" 18 | "periph.io/x/conn/v3/i2c" 19 | "periph.io/x/conn/v3/physic" 20 | "periph.io/x/conn/v3/pin" 21 | ) 22 | 23 | func TestPin(t *testing.T) { 24 | p := &Pin{N: "GPIO1", Num: 10, Fn: "I2C1_SDA"} 25 | // conn.Resource 26 | if s := p.String(); s != "GPIO1(10)" { 27 | t.Fatal(s) 28 | } 29 | if err := p.Halt(); err != nil { 30 | t.Fatal(err) 31 | } 32 | // pin.Pin 33 | if n := p.Number(); n != 10 { 34 | t.Fatal(n) 35 | } 36 | if n := p.Name(); n != "GPIO1" { 37 | t.Fatal(n) 38 | } 39 | if f := p.Function(); f != "I2C1_SDA" { 40 | t.Fatal(f) 41 | } 42 | // pin.PinFunc 43 | if f := p.Func(); f != i2c.SDA.Specialize(1, -1) { 44 | t.Fatal(f) 45 | } 46 | if f := p.SupportedFuncs(); !reflect.DeepEqual(f, []pin.Func{gpio.IN, gpio.OUT}) { 47 | t.Fatal(f) 48 | } 49 | if err := p.SetFunc(i2c.SCL); err == nil { 50 | t.Fatal("expected failure") 51 | } 52 | // gpio.PinIn 53 | if err := p.In(gpio.PullDown, gpio.NoEdge); err != nil { 54 | t.Fatal(err) 55 | } 56 | if l := p.Read(); l != gpio.Low { 57 | t.Fatal(l) 58 | } 59 | if err := p.In(gpio.PullUp, gpio.NoEdge); err != nil { 60 | t.Fatal(err) 61 | } 62 | if l := p.Read(); l != gpio.High { 63 | t.Fatal(l) 64 | } 65 | if pull := p.Pull(); pull != gpio.PullUp { 66 | t.Fatal(pull) 67 | } 68 | if pull := p.DefaultPull(); pull != gpio.PullUp { 69 | t.Fatal(pull) 70 | } 71 | // gpio.PinOut 72 | if err := p.Out(gpio.Low); err != nil { 73 | t.Fatal(err) 74 | } 75 | if err := p.PWM(gpio.DutyHalf, physic.KiloHertz); err != nil { 76 | t.Fatalf("unexpected failure: %v", err) 77 | } 78 | } 79 | 80 | func TestPin_edge(t *testing.T) { 81 | p := &Pin{N: "GPIO1", Num: 1, Fn: "I2C1_SDA", EdgesChan: make(chan gpio.Level, 1)} 82 | p.EdgesChan <- gpio.High 83 | if !p.WaitForEdge(-1) { 84 | t.Fatal("expected edge") 85 | } 86 | if l := p.Read(); l != gpio.High { 87 | t.Fatalf("unexpected %s", l) 88 | } 89 | if p.WaitForEdge(time.Millisecond) { 90 | t.Fatal("unexpected edge") 91 | } 92 | p.EdgesChan <- gpio.Low 93 | if !p.WaitForEdge(time.Minute) { 94 | t.Fatal("expected edge") 95 | } 96 | if l := p.Read(); l != gpio.Low { 97 | t.Fatalf("unexpected %s", l) 98 | } 99 | } 100 | 101 | func TestPin_fail(t *testing.T) { 102 | p := &Pin{N: "GPIO1", Num: 1, Fn: "I2C1_SDA"} 103 | if err := p.In(gpio.Float, gpio.BothEdges); err == nil { 104 | t.Fatal() 105 | } 106 | } 107 | 108 | func TestLogPinIO(t *testing.T) { 109 | p := &Pin{} 110 | l := &LogPinIO{p} 111 | if l.Real() != p { 112 | t.Fatal("unexpected real pin") 113 | } 114 | // gpio.PinIn 115 | if err := l.In(gpio.PullNoChange, gpio.NoEdge); err != nil { 116 | t.Fatal(err) 117 | } 118 | if v := l.Read(); v != gpio.Low { 119 | t.Fatalf("unexpected level %v", v) 120 | } 121 | if l.Pull() != gpio.PullNoChange { 122 | t.Fatal("unexpected pull") 123 | } 124 | if l.WaitForEdge(0) { 125 | t.Fatal("unexpected edge") 126 | } 127 | // gpio.PinOut 128 | if err := l.Out(gpio.High); err != nil { 129 | t.Fatal(err) 130 | } 131 | if v := l.Read(); v != gpio.High { 132 | t.Fatalf("unexpected level %v", v) 133 | } 134 | if err := l.PWM(gpio.DutyHalf, physic.KiloHertz); err != nil { 135 | t.Fatalf("unexpected failure: %v", err) 136 | } 137 | } 138 | 139 | func TestAll(t *testing.T) { 140 | if len(gpioreg.All()) != 2 { 141 | t.Fatal("expected two pins registered for test") 142 | } 143 | } 144 | 145 | func TestByName(t *testing.T) { 146 | if gpioreg.ByName("GPIO0") != nil { 147 | t.Fatal("GPIO0 doesn't exist") 148 | } 149 | if gpioreg.ByName("GPIO2") != gpio2 { 150 | t.Fatal("GPIO2 should have been found") 151 | } 152 | if gpioreg.ByName("1") != nil { 153 | t.Fatal("1 exist") 154 | } 155 | p := gpioreg.ByName("2") 156 | if p == nil { 157 | t.Fatal("2 missing") 158 | } 159 | if r, ok := p.(gpio.RealPin); !ok { 160 | t.Fatalf("unexpected alias: %v", r) 161 | } 162 | p = gpioreg.ByName("3") 163 | if p == nil { 164 | t.Fatal("3 missing") 165 | } 166 | r, ok := p.(gpio.RealPin) 167 | if !ok || r.Real().Name() != "GPIO3" { 168 | t.Fatalf("expected alias, got: %T", p) 169 | } 170 | p = r.Real() 171 | if err := p.PWM(gpio.DutyHalf, physic.KiloHertz); err != nil { 172 | t.Fatalf("unexpected failure: %v", err) 173 | } 174 | } 175 | 176 | // 177 | 178 | var ( 179 | gpio2 = &Pin{N: "GPIO2", Num: 2, Fn: "I2C1_SDA"} 180 | gpio3 = &Pin{N: "GPIO3", Num: 3, Fn: "I2C1_SCL"} 181 | ) 182 | 183 | func init() { 184 | if err := gpioreg.Register(gpio2); err != nil { 185 | panic(err) 186 | } 187 | if err := gpioreg.Register(gpio3); err != nil { 188 | panic(err) 189 | } 190 | if err := gpioreg.RegisterAlias("2", "GPIO2"); err != nil { 191 | panic(err) 192 | } 193 | if err := gpioreg.RegisterAlias("3", "GPIO3"); err != nil { 194 | panic(err) 195 | } 196 | if err := gpioreg.RegisterAlias(string(gpio2.Func()), gpio2.Name()); err != nil { 197 | panic(err) 198 | } 199 | if err := gpioreg.RegisterAlias(string(gpio3.Func()), gpio3.Name()); err != nil { 200 | panic(err) 201 | } 202 | } 203 | 204 | func TestMain(m *testing.M) { 205 | flag.Parse() 206 | if !testing.Verbose() { 207 | log.SetOutput(io.Discard) 208 | } 209 | os.Exit(m.Run()) 210 | } 211 | -------------------------------------------------------------------------------- /gpio/gpioutil/debounce.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "time" 9 | 10 | "github.com/jonboulle/clockwork" 11 | "periph.io/x/conn/v3/gpio" 12 | ) 13 | 14 | // debounced is a gpio.PinIO where reading and edge detection pass through a 15 | // debouncing algorithm. 16 | type debounced struct { 17 | // Immutable. 18 | gpio.PinIO 19 | // denoise delays state changes. It waits for this amount before reporting it. 20 | denoise time.Duration 21 | // debounce locks on after a steady state change. Once a state change 22 | // happened, don't change again for this amount of time. 23 | debounce time.Duration 24 | 25 | // Mutable. 26 | clock clockwork.Clock 27 | } 28 | 29 | // Debounce returns a debounced gpio.PinIO from a gpio.PinIO source. Only the 30 | // PinIn behavior is mutated. 31 | // 32 | // denoise is a noise filter, which waits a pin to be steady for this amount 33 | // of time BEFORE reporting the new level. 34 | // 35 | // debounce will lock on a level for this amount of time AFTER the pin changed 36 | // state, ignoring following state changes. 37 | // 38 | // Either value can be 0. 39 | func Debounce(p gpio.PinIO, denoise, debounce time.Duration, edge gpio.Edge) (gpio.PinIO, error) { 40 | if denoise == 0 && debounce == 0 { 41 | return p, nil 42 | } 43 | if err := p.In(gpio.PullNoChange, gpio.BothEdges); err != nil { 44 | return nil, err 45 | } 46 | return &debounced{ 47 | // Immutable. 48 | PinIO: p, 49 | denoise: denoise, 50 | debounce: debounce, 51 | // Mutable. 52 | clock: clockwork.NewRealClock(), 53 | }, nil 54 | } 55 | 56 | // In implements gpio.PinIO. 57 | func (d *debounced) In(pull gpio.Pull, edge gpio.Edge) error { 58 | err := d.PinIO.In(pull, gpio.BothEdges) 59 | return err 60 | } 61 | 62 | // Read implements gpio.PinIO. 63 | // 64 | // It is the smoothed out value from the underlying gpio.PinIO. 65 | func (d *debounced) Read() gpio.Level { 66 | return d.PinIO.Read() 67 | } 68 | 69 | // WaitForEdge implements gpio.PinIO. 70 | // 71 | // It is the smoothed out value from the underlying gpio.PinIO. 72 | func (d *debounced) WaitForEdge(timeout time.Duration) bool { 73 | prev := d.PinIO.Read() 74 | start := d.clock.Now() 75 | for { 76 | if timeout != -1 && d.clock.Since(start) > timeout { 77 | return false 78 | } 79 | if !d.PinIO.WaitForEdge(timeout) { 80 | // Timeout has occurred, propagate it 81 | return false 82 | } 83 | d.clock.Sleep(d.denoise) 84 | curr := d.PinIO.Read() 85 | if curr != prev { 86 | return true 87 | } 88 | } 89 | } 90 | 91 | // Halt implements gpio.PinIO. 92 | func (d *debounced) Halt() error { 93 | return nil 94 | } 95 | 96 | // Real implements gpio.RealPin. 97 | func (d *debounced) Real() gpio.PinIO { 98 | if r, ok := d.PinIO.(gpio.RealPin); ok { 99 | return r.Real() 100 | } 101 | return d.PinIO 102 | } 103 | 104 | var _ gpio.PinIO = &debounced{} 105 | -------------------------------------------------------------------------------- /gpio/gpioutil/debounce_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/jonboulle/clockwork" 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpiotest" 14 | ) 15 | 16 | func TestDebounce_Err(t *testing.T) { 17 | f := gpiotest.Pin{} 18 | if _, err := Debounce(&f, time.Second, 0, gpio.BothEdges); err == nil { 19 | t.Fatal("expected error") 20 | } 21 | } 22 | 23 | func TestDebounce_Zero(t *testing.T) { 24 | f := gpiotest.Pin{} 25 | p, err := Debounce(&f, 0, 0, gpio.BothEdges) 26 | if err != nil { 27 | t.Fatal("expected error") 28 | } 29 | if p1, ok := p.(*gpiotest.Pin); !ok || p1 != &f { 30 | t.Fatal("expected the pin to be returned as-is") 31 | } 32 | } 33 | 34 | func TestDebounce_In(t *testing.T) { 35 | f := gpiotest.Pin{EdgesChan: make(chan gpio.Level)} 36 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | if err = p.In(gpio.PullNoChange, gpio.BothEdges); err != nil { 41 | t.Fatal(err) 42 | } 43 | if p.Halt() != nil { 44 | t.Fatal(err) 45 | } 46 | } 47 | 48 | func TestDebounce_Read_Low(t *testing.T) { 49 | f := gpiotest.Pin{EdgesChan: make(chan gpio.Level)} 50 | p, err := Debounce(&f, time.Second, time.Second, gpio.BothEdges) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | if p.Read() != gpio.Low { 55 | t.Fatal("expected level") 56 | } 57 | if p.Read() != gpio.Low { 58 | t.Fatal("expected level") 59 | } 60 | } 61 | 62 | func TestDebounce_Read_High(t *testing.T) { 63 | f := gpiotest.Pin{L: gpio.High, EdgesChan: make(chan gpio.Level)} 64 | p, err := Debounce(&f, time.Second, time.Second, gpio.BothEdges) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | if p.Read() != gpio.High { 69 | t.Fatal("expected level") 70 | } 71 | if p.Read() != gpio.High { 72 | t.Fatal("expected level") 73 | } 74 | } 75 | 76 | func TestDebounce_WaitForEdge_Got(t *testing.T) { 77 | fakeClock := clockwork.NewFakeClock() 78 | f := gpiotest.Pin{ 79 | Clock: fakeClock, 80 | EdgesChan: make(chan gpio.Level, 1), 81 | } 82 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | p.(*debounced).clock = fakeClock 87 | f.Out(gpio.High) 88 | f.EdgesChan <- gpio.Low 89 | go func() { 90 | // Sleepers: 91 | // * debounce.WaitForEdge's d.Clock.Sleep 92 | // 93 | // gpiotest.WaitForEdge doesn't call sleep due to infinite timeout. 94 | const numSleepers = 1 95 | 96 | fakeClock.BlockUntil(numSleepers) 97 | fakeClock.Advance(2 * time.Second) 98 | }() 99 | if !p.WaitForEdge(-1) { 100 | t.Fatal("expected edge") 101 | } 102 | } 103 | 104 | func TestDebounce_WaitForEdge_Noise_NoEdge(t *testing.T) { 105 | fakeClock := clockwork.NewFakeClock() 106 | f := gpiotest.Pin{ 107 | Clock: fakeClock, 108 | EdgesChan: make(chan gpio.Level, 1), 109 | } 110 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 111 | if err != nil { 112 | t.Fatal(err) 113 | } 114 | p.(*debounced).clock = fakeClock 115 | f.Out(gpio.Low) 116 | 117 | go func() { 118 | // Sleepers: 119 | // * gpiotest.WaitForEdge's After 120 | // * debounce.WaitForEdge's d.Clock.Sleep 121 | const numSleepers = 2 122 | 123 | // Short high, comes back down too soon 124 | f.EdgesChan <- gpio.High 125 | fakeClock.BlockUntil(numSleepers) 126 | fakeClock.Advance(100 * time.Millisecond) 127 | f.Out(gpio.Low) 128 | fakeClock.Advance(2 * time.Second) 129 | }() 130 | 131 | if p.WaitForEdge(1 * time.Second) { 132 | t.Fatal("expected no edge") 133 | } 134 | } 135 | 136 | func TestDebounce_WaitForEdge_Noise_Edge(t *testing.T) { 137 | fakeClock := clockwork.NewFakeClock() 138 | 139 | f := gpiotest.Pin{ 140 | Clock: fakeClock, 141 | EdgesChan: make(chan gpio.Level, 2), 142 | } 143 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 144 | p.(*debounced).clock = fakeClock 145 | f.Out(gpio.Low) 146 | if err != nil { 147 | t.Fatal(err) 148 | } 149 | 150 | go func() { 151 | // Sleepers: 152 | // * gpiotest.WaitForEdge's After 153 | // * debounce.WaitForEdge's d.Clock.Sleep 154 | const numSleepers = 2 155 | 156 | // 100ms high (too short) 157 | f.EdgesChan <- gpio.High 158 | fakeClock.BlockUntil(numSleepers) 159 | f.Out(gpio.Low) 160 | fakeClock.Advance(100 * time.Millisecond) 161 | 162 | // stays high indefinitely (long enough) 163 | fakeClock.BlockUntil(numSleepers) 164 | f.Out(gpio.High) 165 | fakeClock.Advance(2 * time.Second) 166 | }() 167 | 168 | if !p.WaitForEdge(4 * time.Second) { 169 | t.Fatal("expected edge") 170 | } 171 | } 172 | 173 | func TestDebounce_WaitForEdge_Timeout(t *testing.T) { 174 | f := gpiotest.Pin{EdgesChan: make(chan gpio.Level)} 175 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 176 | if err != nil { 177 | t.Fatal(err) 178 | } 179 | if p.WaitForEdge(0) { 180 | t.Fatal("expected no edge") 181 | } 182 | } 183 | 184 | func TestDebounce_RealPin(t *testing.T) { 185 | f := gpiotest.Pin{EdgesChan: make(chan gpio.Level)} 186 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | r, ok := p.(gpio.RealPin) 191 | if !ok { 192 | t.Fatal("expected gpio.RealPin") 193 | } 194 | a, ok := r.Real().(*gpiotest.Pin) 195 | if !ok { 196 | t.Fatal("expected gpiotest.Pin") 197 | } 198 | if a != &f { 199 | t.Fatal("expected actual pin") 200 | } 201 | } 202 | 203 | func TestDebounce_RealPin_Deep(t *testing.T) { 204 | f := gpiotest.Pin{EdgesChan: make(chan gpio.Level)} 205 | p, err := Debounce(&f, time.Second, 0, gpio.BothEdges) 206 | if err != nil { 207 | t.Fatal(err) 208 | } 209 | p, err = Debounce(p, time.Second, 0, gpio.BothEdges) 210 | if err != nil { 211 | t.Fatal(err) 212 | } 213 | r, ok := p.(gpio.RealPin) 214 | if !ok { 215 | t.Fatal("expected gpio.RealPin") 216 | } 217 | a, ok := r.Real().(*gpiotest.Pin) 218 | if !ok { 219 | t.Fatal("expected gpiotest.Pin") 220 | } 221 | if a != &f { 222 | t.Fatal("expected actual pin") 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /gpio/gpioutil/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package gpioutil includes utilities to filter or augment GPIOs. 6 | package gpioutil 7 | -------------------------------------------------------------------------------- /gpio/gpioutil/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "time" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/gpio/gpioreg" 15 | "periph.io/x/conn/v3/gpio/gpioutil" 16 | "periph.io/x/conn/v3/physic" 17 | ) 18 | 19 | func ExampleDebounce() { 20 | // Make sure periph is initialized. 21 | // TODO: Use host.Init(). It is not used in this example to prevent circular 22 | // go package import. 23 | if _, err := driverreg.Init(); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | p := gpioreg.ByName("GPIO16") 28 | if p != nil { 29 | log.Fatal("please open another GPIO") 30 | } 31 | 32 | // Ignore glitches lasting less than 3ms, and ignore repeated edges within 33 | // 30ms. 34 | d, err := gpioutil.Debounce(p, 3*time.Millisecond, 30*time.Millisecond, gpio.BothEdges) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | defer d.Halt() 40 | for { 41 | if d.WaitForEdge(-1) { 42 | fmt.Println(d.Read()) 43 | } 44 | } 45 | } 46 | 47 | func ExamplePollEdge() { 48 | // Make sure periph is initialized. 49 | // TODO: Use host.Init(). It is not used in this example to prevent circular 50 | // go package import. 51 | if _, err := driverreg.Init(); err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | // Flow when it is known that the GPIO does not support edge detection. 56 | p := gpioreg.ByName("XOI-P1") 57 | if p != nil { 58 | log.Fatal("please open another GPIO") 59 | } 60 | p = gpioutil.PollEdge(p, 20*physic.Hertz) 61 | if err := p.In(gpio.PullDown, gpio.RisingEdge); err != nil { 62 | log.Fatal(err) 63 | } 64 | 65 | defer p.Halt() 66 | for { 67 | if p.WaitForEdge(-1) { 68 | fmt.Println(p.Read()) 69 | } 70 | } 71 | } 72 | 73 | func Example() { 74 | // Make sure periph is initialized. 75 | // TODO: Use host.Init(). It is not used in this example to prevent circular 76 | // go package import. 77 | if _, err := driverreg.Init(); err != nil { 78 | log.Fatal(err) 79 | } 80 | 81 | // Complete solution: 82 | // - Fallback to software polling if the GPIO doesn't support hardware edge 83 | // detection. 84 | // - Denoise and debounce the reading. 85 | // 86 | // Order is important, as Debounce() requires working edge detection. 87 | p := gpioreg.ByName("XOI-P1") 88 | if p != nil { 89 | log.Fatal("please open another GPIO") 90 | } 91 | if err := p.In(gpio.PullDown, gpio.BothEdges); err == nil { 92 | // Try to fallback into software polling, then reinitialize. 93 | p = gpioutil.PollEdge(p, 50*physic.Hertz) 94 | if err = p.In(gpio.PullDown, gpio.BothEdges); err != nil { 95 | log.Fatal(err) 96 | } 97 | } 98 | 99 | // Ignore glitches lasting less than 10ms, and ignore repeated edges within 100 | // 30ms. Make sure to not use denoiser period lower than the software poller 101 | // frequency. 102 | d, err := gpioutil.Debounce(p, 10*time.Millisecond, 30*time.Millisecond, gpio.BothEdges) 103 | if err != nil { 104 | log.Fatal(err) 105 | } 106 | 107 | defer d.Halt() 108 | for { 109 | if d.WaitForEdge(-1) { 110 | fmt.Println(d.Read()) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /gpio/gpioutil/polledge.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "time" 9 | 10 | "periph.io/x/conn/v3/gpio" 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | // pollEdge is a gpio.PinIO where edge detection is done manually. 15 | type pollEdge struct { 16 | // Immutable. 17 | gpio.PinIO 18 | // period is the delay between each poll. 19 | period time.Duration 20 | die chan struct{} 21 | 22 | // Mutable. 23 | // edge is the current edge detection. 24 | edge gpio.Edge 25 | } 26 | 27 | // PollEdge returns a gpio.PinIO which implements edge detection via polling. 28 | // 29 | // Example of GPIOs without edge detection are GPIOs accessible over an I²C 30 | // chip or over USB. 31 | // 32 | // freq must be above 0. A reasonable value is 20Hz reading. High rate 33 | // essentially means a busy loop. 34 | func PollEdge(p gpio.PinIO, freq physic.Frequency) gpio.PinIO { 35 | return &pollEdge{PinIO: p, period: freq.Period(), die: make(chan struct{}, 1)} 36 | } 37 | 38 | // In implements gpio.PinIO. 39 | func (p *pollEdge) In(pull gpio.Pull, edge gpio.Edge) error { 40 | p.edge = gpio.NoEdge 41 | err := p.PinIO.In(pull, gpio.NoEdge) 42 | if err == nil { 43 | p.edge = edge 44 | } 45 | return err 46 | } 47 | 48 | // WaitForEdge implements gpio.PinIO. 49 | func (p *pollEdge) WaitForEdge(timeout time.Duration) bool { 50 | select { 51 | case <-p.die: 52 | default: 53 | } 54 | defer func() { 55 | select { 56 | case <-p.die: 57 | default: 58 | } 59 | }() 60 | curr := p.PinIO.Read() 61 | // -1 means to wait indefinitely. 62 | if timeout >= 0 { 63 | defer time.AfterFunc(timeout, func() { 64 | p.die <- struct{}{} 65 | }).Stop() 66 | } 67 | // Sadly it's not possible to stop then restart a ticker, so we can't cache 68 | // it in the object. 69 | t := time.NewTicker(p.period) 70 | defer t.Stop() 71 | for { 72 | select { 73 | case <-t.C: 74 | n := p.PinIO.Read() 75 | if n != curr { 76 | switch p.edge { 77 | case gpio.RisingEdge: 78 | if n == gpio.High { 79 | return true 80 | } 81 | curr = n 82 | case gpio.FallingEdge: 83 | if n == gpio.Low { 84 | return true 85 | } 86 | curr = n 87 | case gpio.BothEdges: 88 | return true 89 | } 90 | } 91 | case <-p.die: 92 | return false 93 | } 94 | } 95 | } 96 | 97 | // Halt implements gpio.PinIO. 98 | // 99 | // It unblocks any WaitForEdge loop. 100 | func (p *pollEdge) Halt() error { 101 | select { 102 | // If a WaitForEdge was pending, it will be unblocked. 103 | case p.die <- struct{}{}: 104 | default: 105 | } 106 | return nil 107 | } 108 | 109 | // Real implements gpio.RealPin. 110 | func (p *pollEdge) Real() gpio.PinIO { 111 | if r, ok := p.PinIO.(gpio.RealPin); ok { 112 | return r.Real() 113 | } 114 | return p.PinIO 115 | } 116 | 117 | var _ gpio.PinIO = &pollEdge{} 118 | -------------------------------------------------------------------------------- /gpio/gpioutil/polledge_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "sync" 9 | "testing" 10 | "time" 11 | 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpiotest" 14 | "periph.io/x/conn/v3/physic" 15 | ) 16 | 17 | func TestAssumption(t *testing.T) { 18 | f := gpiotest.Pin{} 19 | if f.In(gpio.PullNoChange, gpio.BothEdges) == nil { 20 | t.Fatal("Using gpiotest.Pin in no edge support mode") 21 | } 22 | if PollEdge(&f, 20*physic.Hertz) == nil { 23 | t.Fatal("expected error") 24 | } 25 | } 26 | 27 | func TestPollEdge_Short(t *testing.T) { 28 | p := PollEdge(&gpiotest.Pin{}, physic.Hertz) 29 | if err := p.In(gpio.PullNoChange, gpio.BothEdges); err != nil { 30 | t.Fatal(err) 31 | } 32 | if err := p.Halt(); err != nil { 33 | t.Fatal(err) 34 | } 35 | // timeout triggers. 36 | if p.WaitForEdge(time.Nanosecond) { 37 | t.Fatal("unexpected edge") 38 | } 39 | } 40 | 41 | func TestPollEdge_Halt(t *testing.T) { 42 | f := pinWait{wait: make(chan struct{})} 43 | p := PollEdge(&f, physic.Hertz) 44 | go func() { 45 | // Make sure the pin was read at least once, which means the code below is 46 | // inside WaitForEdge(). 47 | <-f.wait 48 | if err := p.Halt(); err != nil { 49 | t.Error(err) 50 | } 51 | }() 52 | // p.die triggers. 53 | if p.WaitForEdge(-1) { 54 | t.Fatal("unexpected edge") 55 | } 56 | } 57 | 58 | func TestPollEdge_RisingEdge(t *testing.T) { 59 | f := pinLevels{levels: []gpio.Level{gpio.High, gpio.Low, gpio.High}} 60 | p := PollEdge(&f, physic.KiloHertz) 61 | if err := p.In(gpio.PullNoChange, gpio.RisingEdge); err != nil { 62 | t.Fatal(err) 63 | } 64 | if !p.WaitForEdge(-1) { 65 | t.Fatal("expected edge") 66 | } 67 | if len(f.levels) != 0 { 68 | t.Fatalf("unconsumed levels: %v", f.levels) 69 | } 70 | } 71 | 72 | func TestPollEdge_FallingEdge(t *testing.T) { 73 | f := pinLevels{levels: []gpio.Level{gpio.Low, gpio.High, gpio.Low}} 74 | p := PollEdge(&f, physic.KiloHertz) 75 | if err := p.In(gpio.PullNoChange, gpio.FallingEdge); err != nil { 76 | t.Fatal(err) 77 | } 78 | if !p.WaitForEdge(-1) { 79 | t.Fatal("expected edge") 80 | } 81 | if len(f.levels) != 0 { 82 | t.Fatalf("unconsumed levels: %v", f.levels) 83 | } 84 | } 85 | 86 | func TestPollEdge_BothEdges(t *testing.T) { 87 | f := pinLevels{levels: []gpio.Level{gpio.High, gpio.Low}} 88 | p := PollEdge(&f, physic.KiloHertz) 89 | if err := p.In(gpio.PullNoChange, gpio.BothEdges); err != nil { 90 | t.Fatal(err) 91 | } 92 | if !p.WaitForEdge(-1) { 93 | t.Fatal("expected edge") 94 | } 95 | if len(f.levels) != 0 { 96 | t.Fatal("unconsumed level") 97 | } 98 | } 99 | 100 | func TestPollEdge_RealPin(t *testing.T) { 101 | f := gpiotest.Pin{} 102 | p := PollEdge(&f, physic.Hertz) 103 | r, ok := p.(gpio.RealPin) 104 | if !ok { 105 | t.Fatal("expected gpio.RealPin") 106 | } 107 | a, ok := r.Real().(*gpiotest.Pin) 108 | if !ok { 109 | t.Fatal("expected gpiotest.Pin") 110 | } 111 | if a != &f { 112 | t.Fatal("expected actual pin") 113 | } 114 | } 115 | 116 | func TestPollEdge_RealPin_Deep(t *testing.T) { 117 | f := gpiotest.Pin{} 118 | p := PollEdge(PollEdge(&f, physic.Hertz), physic.Hertz) 119 | r, ok := p.(gpio.RealPin) 120 | if !ok { 121 | t.Fatal("expected gpio.RealPin") 122 | } 123 | a, ok := r.Real().(*gpiotest.Pin) 124 | if !ok { 125 | t.Fatal("expected gpiotest.Pin") 126 | } 127 | if a != &f { 128 | t.Fatal("expected actual pin") 129 | } 130 | } 131 | 132 | // 133 | 134 | type pinLevels struct { 135 | gpiotest.Pin 136 | mu sync.Mutex 137 | levels []gpio.Level 138 | } 139 | 140 | func (p *pinLevels) Read() gpio.Level { 141 | p.mu.Lock() 142 | defer p.mu.Unlock() 143 | l := p.levels[0] 144 | p.levels = p.levels[1:] 145 | return l 146 | } 147 | 148 | type pinWait struct { 149 | gpiotest.Pin 150 | wait chan struct{} 151 | once sync.Once 152 | } 153 | 154 | func (p *pinWait) Read() gpio.Level { 155 | p.once.Do(func() { 156 | p.wait <- struct{}{} 157 | }) 158 | return true 159 | } 160 | -------------------------------------------------------------------------------- /gpio/gpioutil/pulsein.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "time" 9 | 10 | "github.com/jonboulle/clockwork" 11 | "periph.io/x/conn/v3/gpio" 12 | ) 13 | 14 | // PulseIn reads a pulse (either HIGH or LOW) on a pin. 15 | // 16 | // For example, if lvl is HIGH, PulseIn() waits for the pin to go from LOW to HIGH, starts timing, 17 | // then waits for the pin to go LOW and stops timing. 18 | // 19 | // Returns the length of the pulse as a time.Duration or gives up and returns 0 20 | // if no complete pulse was received within the timeout. 21 | func PulseIn(pin gpio.PinIn, lvl gpio.Level, t time.Duration) (time.Duration, error) { 22 | return pulseInWithClock(pin, lvl, t, clockwork.NewRealClock()) 23 | } 24 | 25 | func pulseInWithClock(pin gpio.PinIn, lvl gpio.Level, t time.Duration, clock clockwork.Clock) (time.Duration, error) { 26 | e1 := gpio.FallingEdge 27 | e2 := gpio.RisingEdge 28 | 29 | if lvl == gpio.High { 30 | e1 = gpio.RisingEdge 31 | e2 = gpio.FallingEdge 32 | } 33 | 34 | if err := pin.In(gpio.PullNoChange, e1); err != nil { 35 | return 0, err 36 | } 37 | 38 | if !pin.WaitForEdge(t) { 39 | return 0, nil 40 | } 41 | 42 | now := clock.Now() 43 | 44 | if err := pin.In(gpio.PullNoChange, e2); err != nil { 45 | return 0, err 46 | } 47 | 48 | if !pin.WaitForEdge(t) { 49 | return 0, nil 50 | } 51 | 52 | return clock.Since(now), nil 53 | } 54 | -------------------------------------------------------------------------------- /gpio/gpioutil/pulsein_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpioutil 6 | 7 | import ( 8 | "errors" 9 | "testing" 10 | "time" 11 | 12 | "github.com/jonboulle/clockwork" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/gpio/gpiotest" 15 | ) 16 | 17 | func TestPulseIn_Success(t *testing.T) { 18 | edgesChan := make(chan gpio.Level) 19 | clock := clockwork.NewFakeClock() 20 | 21 | pin := pulseInPin{ 22 | sleeps: []time.Duration{0, time.Second}, 23 | Pin: gpiotest.Pin{ 24 | EdgesChan: edgesChan, 25 | L: gpio.Low, 26 | Clock: clock, 27 | }, 28 | } 29 | 30 | go func() { 31 | edgesChan <- gpio.High 32 | 33 | // There is no timeout on PulseIn so there isn't any After in WaitForEdges. 34 | // Here we simulate one in In function. 35 | clock.BlockUntil(1) 36 | clock.Advance(time.Second) 37 | 38 | edgesChan <- gpio.Low 39 | }() 40 | 41 | duration, err := pulseInWithClock(&pin, gpio.High, -1, clock) 42 | if err != nil { 43 | t.Fatal("shouldn't have any error") 44 | } 45 | 46 | if duration != time.Second { 47 | t.Fatal("it should takes 1 second") 48 | } 49 | } 50 | 51 | func TestPulseIn_Timeout_1(t *testing.T) { 52 | edgesChan := make(chan gpio.Level) 53 | clock := clockwork.NewFakeClock() 54 | 55 | pin := pulseInPin{ 56 | Pin: gpiotest.Pin{ 57 | EdgesChan: edgesChan, 58 | L: gpio.Low, 59 | Clock: clock, 60 | }, 61 | } 62 | 63 | go func() { 64 | clock.BlockUntil(1) 65 | clock.Advance(time.Second) 66 | }() 67 | 68 | duration, err := pulseInWithClock(&pin, gpio.High, time.Second, clock) 69 | if err != nil { 70 | t.Fatal("shouldn't have any error") 71 | } 72 | 73 | if duration != 0 { 74 | t.Fatal("it should returns 0 for timeout") 75 | } 76 | } 77 | 78 | func TestPulseIn_Timeout_2(t *testing.T) { 79 | edgesChan := make(chan gpio.Level) 80 | clock := clockwork.NewFakeClock() 81 | 82 | pin := pulseInPin{ 83 | Pin: gpiotest.Pin{ 84 | EdgesChan: edgesChan, 85 | L: gpio.Low, 86 | Clock: clock, 87 | }, 88 | } 89 | 90 | go func() { 91 | edgesChan <- gpio.High 92 | 93 | // there is a call for after in the first WaitForEdge and there is another one in the second WaitForEdge. 94 | clock.BlockUntil(2) 95 | 96 | clock.Advance(time.Second) 97 | }() 98 | 99 | duration, err := pulseInWithClock(&pin, gpio.High, time.Second, clock) 100 | if err != nil { 101 | t.Fatal("shouldn't have any error") 102 | } 103 | if duration != 0 { 104 | t.Fatal("it should returns 0 for timeout") 105 | } 106 | } 107 | 108 | type pulseInPin struct { 109 | gpiotest.Pin 110 | 111 | sleeps []time.Duration 112 | } 113 | 114 | func (p *pulseInPin) In(pull gpio.Pull, edge gpio.Edge) error { 115 | p.Lock() 116 | defer p.Unlock() 117 | p.P = pull 118 | if pull == gpio.PullDown { 119 | p.L = gpio.Low 120 | } else if pull == gpio.PullUp { 121 | p.L = gpio.High 122 | } 123 | 124 | if edge != gpio.NoEdge && p.EdgesChan == nil { 125 | return errors.New("gpiotest: please set p.EdgesChan first") 126 | } 127 | 128 | if len(p.sleeps) > 0 { 129 | if p.sleeps[0] != 0 { 130 | p.Clock.Sleep(p.sleeps[0]) 131 | } 132 | p.sleeps = p.sleeps[1:] 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /gpio/group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package gpio 6 | 7 | import ( 8 | "errors" 9 | "time" 10 | 11 | "periph.io/x/conn/v3" 12 | "periph.io/x/conn/v3/pin" 13 | ) 14 | 15 | type GPIOValue uint64 16 | 17 | // Implementations that don't implement specific interface methods should 18 | // return ErrGroupFeatureNotImplemented as the error to allow clients to 19 | // generically check for the condition. 20 | var ErrGroupFeatureNotImplemented = errors.New("gpio group feature not implemented") 21 | 22 | // Group is an interface that an IO device can implement to manipulate multiple 23 | // IO Pins at one time. Performing GPIO Operations in this manner can dramatically 24 | // simplify code and reduce IO Operation latency. 25 | // 26 | // Device specific code can also provide methods that return a Group that operates 27 | // on a subset of the pins the device supports. 28 | type Group interface { 29 | // The set of GPIO pins that make up this group. Implementations will 30 | // typically Use gpio.PinIO, gpio.PinIn, or gpio.PinOut as the actual return 31 | // value type. 32 | Pins() []pin.Pin 33 | // Given a specific pin offset within the group, return that pin. 34 | // For example, a pin group may be GPIO pins 3,5,7,9 in that order. 35 | // ByOffset(1) returns GPIO pin 5. 36 | ByOffset(offset int) pin.Pin 37 | // Given the unique name of a GPIO pin, return that pin. 38 | ByName(name string) pin.Pin 39 | // Given the specific GPIO pin number, return the corresponding 40 | // pin from the group. 41 | ByNumber(number int) pin.Pin 42 | // Out writes the specified bitwise value to the pins. Bit 0 corresponds to 43 | // the first pin in the set, bit 1 the second, etc. Only pins within the 44 | // group that have mask bit set are modified. For example, if you have 8 45 | // pins within the group and you want to write the value 0x0a to the lower 46 | // 4 pins, you would use a mask of 0x0f. 47 | // 48 | // If the device doesn't support write operations, implementations should 49 | // return gpio.ErrGroupFeatureNotImplemented. 50 | Out(value, mask GPIOValue) error 51 | // Read reads the pins within the group, and returns the value, ANDed with 52 | // mask. If the device doesn't support read operations, implementations 53 | // should return gpio.ErrGroupFeatureNotImplemented. 54 | Read(mask GPIOValue) (GPIOValue, error) 55 | // WaitForEdge blocks for a GPIO line change event to happen. If the does 56 | // not implement gpio.PinIn, or doesn't support this capability, 57 | // implementations should return gpio.ErrGroupFeatureNotImplemented. 58 | // 59 | // Number is the GPIO pin number within the group that had an edge change. 60 | WaitForEdge(timeout time.Duration) (number int, edge Edge, err error) 61 | // conn.Resource brings in resource.Halt(), and fmt.Stringer 62 | conn.Resource 63 | } 64 | -------------------------------------------------------------------------------- /i2c/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2c_test 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "log" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/i2c" 14 | "periph.io/x/conn/v3/i2c/i2creg" 15 | ) 16 | 17 | func Example() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // Use i2creg I²C bus registry to find the first available I²C bus. 26 | b, err := i2creg.Open("") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer b.Close() 31 | 32 | // Dev is a valid conn.Conn. 33 | d := &i2c.Dev{Addr: 23, Bus: b} 34 | 35 | // Send a command 0x10 and expect a 5 bytes reply. 36 | write := []byte{0x10} 37 | read := make([]byte, 5) 38 | if err := d.Tx(write, read); err != nil { 39 | log.Fatal(err) 40 | } 41 | fmt.Printf("%v\n", read) 42 | } 43 | 44 | func ExamplePins() { 45 | // Make sure periph is initialized. 46 | // TODO: Use host.Init(). It is not used in this example to prevent circular 47 | // go package import. 48 | if _, err := driverreg.Init(); err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | // Use i2creg I²C port registry to find the first available I²C bus. 53 | b, err := i2creg.Open("") 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | defer b.Close() 58 | 59 | // Prints out the gpio pin used. 60 | if p, ok := b.(i2c.Pins); ok { 61 | fmt.Printf("SDA: %s", p.SDA()) 62 | fmt.Printf("SCL: %s", p.SCL()) 63 | } 64 | } 65 | 66 | func ExampleAddr_flag() { 67 | var addr i2c.Addr 68 | flag.Var(&addr, "addr", "i2c device address") 69 | flag.Parse() 70 | } 71 | -------------------------------------------------------------------------------- /i2c/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2c 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | SCL pin.Func = "I2C_SCL" // Clock 12 | SDA pin.Func = "I2C_SDA" // Data 13 | ) 14 | -------------------------------------------------------------------------------- /i2c/i2c.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package i2c defines the API to communicate with devices over the I²C 6 | // protocol. 7 | // 8 | // As described in https://periph.io/x/conn/v3#hdr-Concepts, periph.io uses 9 | // the concepts of Bus, Port and Conn. 10 | // 11 | // In the package i2c, 'Port' is not exposed, since once you know the I²C 12 | // device address, there's no unconfigured Port to configure. 13 | // 14 | // Instead, the package includes the adapter 'Dev' to directly convert an I²C 15 | // bus 'i2c.Bus' into a connection 'conn.Conn' by only specifying the device 16 | // I²C address. 17 | // 18 | // See https://en.wikipedia.org/wiki/I%C2%B2C for more information. 19 | package i2c 20 | 21 | import ( 22 | "errors" 23 | "io" 24 | "strconv" 25 | 26 | "periph.io/x/conn/v3" 27 | "periph.io/x/conn/v3/gpio" 28 | "periph.io/x/conn/v3/physic" 29 | ) 30 | 31 | // Bus defines the interface a concrete I²C driver must implement. 32 | // 33 | // This interface is consummed by a device driver for a device sitting on a bus. 34 | // 35 | // This interface doesn't implement conn.Conn since a device address must be 36 | // specified. Use i2cdev.Dev as an adapter to get a conn.Conn compatible 37 | // object. 38 | type Bus interface { 39 | String() string 40 | // Tx does a transaction at the specified device address. 41 | // 42 | // Write is done first, then read. One of 'w' or 'r' can be omitted for a 43 | // unidirectional operation. 44 | Tx(addr uint16, w, r []byte) error 45 | // SetSpeed changes the bus speed, if supported. 46 | // 47 | // On linux due to the way the I²C sysfs driver is exposed in userland, 48 | // calling this function will likely affect *all* I²C buses on the host. 49 | SetSpeed(f physic.Frequency) error 50 | } 51 | 52 | // BusCloser is an I²C bus that can be closed. 53 | // 54 | // This interface is meant to be handled by the application and not the device 55 | // driver. A device driver doesn't "own" a bus, hence it must operate on a Bus, 56 | // not a BusCloser. 57 | type BusCloser interface { 58 | io.Closer 59 | Bus 60 | } 61 | 62 | // Pins defines the pins that an I²C bus interconnect is using on the host. 63 | // 64 | // It is expected that a implementer of Bus also implement Pins but this is not 65 | // a requirement. 66 | type Pins interface { 67 | // SCL returns the CLK (clock) pin. 68 | SCL() gpio.PinIO 69 | // SDA returns the DATA pin. 70 | SDA() gpio.PinIO 71 | } 72 | 73 | // Dev is a device on a I²C bus. 74 | // 75 | // It implements conn.Conn. 76 | // 77 | // It saves from repeatedly specifying the device address. 78 | type Dev struct { 79 | Bus Bus 80 | Addr uint16 81 | } 82 | 83 | func (d *Dev) String() string { 84 | s := "" 85 | if d.Bus != nil { 86 | s = d.Bus.String() 87 | } 88 | return s + "(" + strconv.Itoa(int(d.Addr)) + ")" 89 | } 90 | 91 | // Tx does a transaction by adding the device's address to each command. 92 | // 93 | // It's a wrapper for Bus.Tx(). 94 | func (d *Dev) Tx(w, r []byte) error { 95 | return d.Bus.Tx(d.Addr, w, r) 96 | } 97 | 98 | // Write writes to the I²C bus without reading, implementing io.Writer. 99 | // 100 | // It's a wrapper for Tx() 101 | func (d *Dev) Write(b []byte) (int, error) { 102 | if err := d.Tx(b, nil); err != nil { 103 | return 0, err 104 | } 105 | return len(b), nil 106 | } 107 | 108 | // Duplex always return conn.Half for I²C. 109 | func (d *Dev) Duplex() conn.Duplex { 110 | return conn.Half 111 | } 112 | 113 | // Addr is an I²C slave address. 114 | type Addr uint16 115 | 116 | // Set sets the Addr to a value represented by the string s. Values maybe in 117 | // decimal or hexadecimal form. Set implements the flag.Value interface. 118 | func (a *Addr) Set(s string) error { 119 | // Allow for only maximum of 10 bits for i2c addresses. 120 | u, err := strconv.ParseUint(s, 0, 10) 121 | if err != nil { 122 | return errI2CSetError 123 | } 124 | *a = Addr(u) 125 | return nil 126 | } 127 | 128 | // String returns an i2c.Addr as a string formated in hexadecimal. 129 | func (a Addr) String() string { 130 | return "0x" + strconv.FormatInt(int64(a), 16) 131 | } 132 | 133 | var errI2CSetError = errors.New("invalid i2c address") 134 | 135 | var _ conn.Conn = &Dev{} 136 | -------------------------------------------------------------------------------- /i2c/i2c_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2c 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "testing" 11 | 12 | "periph.io/x/conn/v3" 13 | "periph.io/x/conn/v3/physic" 14 | ) 15 | 16 | func TestDevString(t *testing.T) { 17 | d := Dev{&fakeBus{}, 12} 18 | if s := d.String(); s != "fake(12)" { 19 | t.Fatalf("got %s", s) 20 | } 21 | } 22 | 23 | func TestDevTx(t *testing.T) { 24 | exErr := errors.New("yes") 25 | b := &fakeBus{err: exErr, r: []byte{1, 2, 3}} 26 | d := Dev{b, 12} 27 | r := make([]byte, 3) 28 | w := []byte{3, 4, 5} 29 | if err := d.Tx(w, r); exErr != err { 30 | t.Fatal(err) 31 | } 32 | if !bytes.Equal(b.w, w) { 33 | t.Fatal(b.w) 34 | } 35 | expected := []byte{1, 2, 3} 36 | if !bytes.Equal(r, expected) { 37 | t.Fatalf("r: %v != %v", b.r, expected) 38 | } 39 | if b.addr != 12 { 40 | t.Fatalf("got %d", b.addr) 41 | } 42 | if i := d.Duplex(); i != conn.Half { 43 | t.Fatal(i) 44 | } 45 | } 46 | 47 | func TestDevWrite(t *testing.T) { 48 | b := &fakeBus{} 49 | d := Dev{b, 12} 50 | w := []byte{3, 4, 5} 51 | if n, err := d.Write(w); err != nil || n != 3 { 52 | t.Fatalf("got %s", err) 53 | } 54 | if !bytes.Equal(b.w, w) { 55 | t.Fatal("w") 56 | } 57 | if b.addr != 12 { 58 | t.Fatalf("got %d", b.addr) 59 | } 60 | } 61 | 62 | func TestDevWriteErr(t *testing.T) { 63 | exErr := errors.New("yes") 64 | b := &fakeBus{err: exErr} 65 | d := Dev{b, 12} 66 | w := []byte{3, 4, 5} 67 | if n, err := d.Write(w); err != exErr || n != 0 { 68 | t.Fatal(err) 69 | } 70 | if !bytes.Equal(b.w, w) { 71 | t.Fatal(b.w) 72 | } 73 | if b.addr != 12 { 74 | t.Fatal(b.addr) 75 | } 76 | } 77 | 78 | // 79 | 80 | type fakeBus struct { 81 | freq physic.Frequency 82 | err error 83 | addr uint16 84 | w, r []byte 85 | } 86 | 87 | func (f *fakeBus) Close() error { 88 | return nil 89 | } 90 | 91 | func (f *fakeBus) String() string { 92 | return "fake" 93 | } 94 | 95 | func (f *fakeBus) Tx(addr uint16, w, r []byte) error { 96 | f.addr = addr 97 | f.w = append(f.w, w...) 98 | copy(r, f.r) 99 | f.r = f.r[len(r):] 100 | return f.err 101 | } 102 | 103 | func (f *fakeBus) SetSpeed(freq physic.Frequency) error { 104 | f.freq = freq 105 | return f.err 106 | } 107 | 108 | func TestAddr_Set(t *testing.T) { 109 | tests := []struct { 110 | str string 111 | want Addr 112 | err error 113 | }{ 114 | {"0x18", 0x18, nil}, 115 | {"24", 24, nil}, 116 | {"0x3ff", 0x3ff, nil}, 117 | {"0x400", 0, errI2CSetError}, 118 | {"-1", 0, errI2CSetError}, 119 | } 120 | 121 | for _, tt := range tests { 122 | var a Addr 123 | if err := a.Set(tt.str); err != tt.err { 124 | t.Errorf("i2cAddr.Set(%s) error %v", tt.str, err) 125 | } 126 | if tt.err == nil && a != tt.want { 127 | t.Errorf("i2cAddr.Set(%s) expected %d but got %d", tt.str, tt.want, a) 128 | } 129 | } 130 | } 131 | 132 | func TestAddr_String(t *testing.T) { 133 | tests := []struct { 134 | Addr Addr 135 | want string 136 | }{ 137 | {0x01, "0x1"}, 138 | {0x24, "0x24"}, 139 | {0x3ff, "0x3ff"}, 140 | } 141 | 142 | for _, tt := range tests { 143 | if got := tt.Addr.String(); got != tt.want { 144 | t.Errorf("i2cAddr.String() expected %s but got %s", tt.want, got) 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /i2c/i2creg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2creg_test 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "log" 11 | "strings" 12 | 13 | "periph.io/x/conn/v3/driver/driverreg" 14 | "periph.io/x/conn/v3/i2c" 15 | "periph.io/x/conn/v3/i2c/i2creg" 16 | ) 17 | 18 | func Example() { 19 | // Make sure periph is initialized. 20 | // TODO: Use host.Init(). It is not used in this example to prevent circular 21 | // go package import. 22 | if _, err := driverreg.Init(); err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | // A command line tool may let the user choose a I²C port, yet default to the 27 | // first port known. 28 | name := flag.String("i2c", "", "I²C bus to use") 29 | flag.Parse() 30 | b, err := i2creg.Open(*name) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | defer b.Close() 35 | 36 | // Dev is a valid conn.Conn. 37 | d := &i2c.Dev{Addr: 23, Bus: b} 38 | 39 | // Send a command 0x10 and expect a 5 bytes reply. 40 | write := []byte{0x10} 41 | read := make([]byte, 5) 42 | if err := d.Tx(write, read); err != nil { 43 | log.Fatal(err) 44 | } 45 | fmt.Printf("%v\n", read) 46 | } 47 | 48 | func ExampleAll() { 49 | // Make sure periph is initialized. 50 | // TODO: Use host.Init(). It is not used in this example to prevent circular 51 | // go package import. 52 | if _, err := driverreg.Init(); err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | // Enumerate all I²C buses available and the corresponding pins. 57 | fmt.Print("I²C buses available:\n") 58 | for _, ref := range i2creg.All() { 59 | fmt.Printf("- %s\n", ref.Name) 60 | if ref.Number != -1 { 61 | fmt.Printf(" %d\n", ref.Number) 62 | } 63 | if len(ref.Aliases) != 0 { 64 | fmt.Printf(" %s\n", strings.Join(ref.Aliases, " ")) 65 | } 66 | 67 | b, err := ref.Open() 68 | if err != nil { 69 | fmt.Printf(" Failed to open: %v", err) 70 | } 71 | if p, ok := b.(i2c.Pins); ok { 72 | fmt.Printf(" SDA: %s", p.SDA()) 73 | fmt.Printf(" SCL: %s", p.SCL()) 74 | } 75 | if err := b.Close(); err != nil { 76 | fmt.Printf(" Failed to close: %v", err) 77 | } 78 | } 79 | } 80 | 81 | func ExampleOpen() { 82 | // Make sure periph is initialized. 83 | // TODO: Use host.Init(). It is not used in this example to prevent circular 84 | // go package import. 85 | if _, err := driverreg.Init(); err != nil { 86 | log.Fatal(err) 87 | } 88 | 89 | // On Linux, the following calls will likely open the same bus. 90 | _, _ = i2creg.Open("/dev/i2c-1") 91 | _, _ = i2creg.Open("I2C1") 92 | _, _ = i2creg.Open("1") 93 | 94 | // Opens the first default I²C bus found: 95 | _, _ = i2creg.Open("") 96 | 97 | // Wondering what to do with the opened i2c.BusCloser? Look at the package's 98 | // example above. 99 | } 100 | -------------------------------------------------------------------------------- /i2c/i2creg/i2creg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2creg 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/i2c" 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestOpen(t *testing.T) { 15 | defer reset() 16 | if _, err := Open(""); err == nil { 17 | t.Fatal("no bus registered") 18 | } 19 | if err := Register("a", []string{"x"}, 1, fakeBuser); err != nil { 20 | t.Fatal(err) 21 | } 22 | if o, err := Open(""); o == nil || err != nil { 23 | t.Fatal(o, err) 24 | } 25 | if o, err := Open("1"); o == nil || err != nil { 26 | t.Fatal(o, err) 27 | } 28 | if o, err := Open("x"); o == nil || err != nil { 29 | t.Fatal(o, err) 30 | } 31 | if o, err := Open("y"); o != nil || err == nil { 32 | t.Fatal(o, err) 33 | } 34 | } 35 | 36 | func TestDefault_NoNumber(t *testing.T) { 37 | defer reset() 38 | if err := Register("a", nil, -1, fakeBuser); err != nil { 39 | t.Fatal(err) 40 | } 41 | if o, err := Open(""); o == nil || err != nil { 42 | t.Fatal(o, err) 43 | } 44 | } 45 | 46 | func TestAll(t *testing.T) { 47 | defer reset() 48 | if a := All(); len(a) != 0 { 49 | t.Fatal(a) 50 | } 51 | if err := Register("a", nil, 1, fakeBuser); err != nil { 52 | t.Fatal(err) 53 | } 54 | if err := Register("b", nil, 2, fakeBuser); err != nil { 55 | t.Fatal(err) 56 | } 57 | if a := All(); len(a) != 2 { 58 | t.Fatal(a) 59 | } 60 | } 61 | 62 | func TestRef(t *testing.T) { 63 | out := insertRef(nil, &Ref{Name: "b"}) 64 | out = insertRef(out, &Ref{Name: "d"}) 65 | out = insertRef(out, &Ref{Name: "c"}) 66 | out = insertRef(out, &Ref{Name: "a"}) 67 | for i, l := range []string{"a", "b", "c", "d"} { 68 | if out[i].Name != l { 69 | t.Fatal(out) 70 | } 71 | } 72 | } 73 | 74 | func TestRegister(t *testing.T) { 75 | defer reset() 76 | if err := Register("a", []string{"b"}, 42, fakeBuser); err != nil { 77 | t.Fatal(err) 78 | } 79 | if Register("a", nil, -1, fakeBuser) == nil { 80 | t.Fatal("same bus name") 81 | } 82 | if Register("b", nil, -1, fakeBuser) == nil { 83 | t.Fatal("same bus alias name") 84 | } 85 | if Register("c", nil, 42, fakeBuser) == nil { 86 | t.Fatal("same bus number") 87 | } 88 | if Register("c", []string{"a"}, -1, fakeBuser) == nil { 89 | t.Fatal("same bus alias") 90 | } 91 | if Register("c", []string{"b"}, -1, fakeBuser) == nil { 92 | t.Fatal("same bus alias") 93 | } 94 | } 95 | 96 | func TestRegister_fail(t *testing.T) { 97 | defer reset() 98 | if Register("a", nil, -1, nil) == nil { 99 | t.Fatal("missing Opener") 100 | } 101 | if Register("a", nil, -2, fakeBuser) == nil { 102 | t.Fatal("bad bus number") 103 | } 104 | if Register("", nil, 42, fakeBuser) == nil { 105 | t.Fatal("missing name") 106 | } 107 | if Register("1", nil, 42, fakeBuser) == nil { 108 | t.Fatal("numeric name") 109 | } 110 | if Register("a:b", nil, 42, fakeBuser) == nil { 111 | t.Fatal("':' in name") 112 | } 113 | if Register("a", []string{"a"}, 0, fakeBuser) == nil { 114 | t.Fatal("\"a\" is already registered") 115 | } 116 | if Register("a", []string{""}, 0, fakeBuser) == nil { 117 | t.Fatal("empty alias") 118 | } 119 | if Register("a", []string{"1"}, 0, fakeBuser) == nil { 120 | t.Fatal("numeric alias") 121 | } 122 | if Register("a", []string{"a:b"}, 0, fakeBuser) == nil { 123 | t.Fatal("':' in alias") 124 | } 125 | if a := All(); len(a) != 0 { 126 | t.Fatal(a) 127 | } 128 | } 129 | 130 | func TestUnregister(t *testing.T) { 131 | defer reset() 132 | if Unregister("") == nil { 133 | t.Fatal("unregister empty") 134 | } 135 | if Unregister("a") == nil { 136 | t.Fatal("unregister non-existing") 137 | } 138 | if err := Register("a", []string{"b"}, 0, fakeBuser); err != nil { 139 | t.Fatal(err) 140 | } 141 | if err := Unregister("a"); err != nil { 142 | t.Fatal(err) 143 | } 144 | } 145 | 146 | // 147 | 148 | func fakeBuser() (i2c.BusCloser, error) { 149 | return &fakeBus{}, nil 150 | } 151 | 152 | func reset() { 153 | mu.Lock() 154 | defer mu.Unlock() 155 | byName = map[string]*Ref{} 156 | byNumber = map[int]*Ref{} 157 | byAlias = map[string]*Ref{} 158 | } 159 | 160 | type fakeBus struct { 161 | freq physic.Frequency 162 | err error 163 | addr uint16 164 | w, r []byte 165 | } 166 | 167 | func (f *fakeBus) Close() error { 168 | return nil 169 | } 170 | 171 | func (f *fakeBus) String() string { 172 | return "fake" 173 | } 174 | 175 | func (f *fakeBus) Tx(addr uint16, w, r []byte) error { 176 | f.addr = addr 177 | f.w = append(f.w, w...) 178 | copy(r, f.r) 179 | f.r = f.r[len(r):] 180 | return f.err 181 | } 182 | 183 | func (f *fakeBus) SetSpeed(freq physic.Frequency) error { 184 | f.freq = freq 185 | return f.err 186 | } 187 | -------------------------------------------------------------------------------- /i2c/i2ctest/i2ctest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package i2ctest is meant to be used to test drivers over a fake I²C bus. 6 | package i2ctest 7 | 8 | import ( 9 | "bytes" 10 | "sync" 11 | 12 | "periph.io/x/conn/v3/conntest" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/i2c" 15 | "periph.io/x/conn/v3/physic" 16 | ) 17 | 18 | // IO registers the I/O that happened on either a real or fake I²C bus. 19 | type IO struct { 20 | Addr uint16 21 | W []byte 22 | R []byte 23 | } 24 | 25 | // Record implements i2c.Bus that records everything written to it. 26 | // 27 | // This can then be used to feed to Playback to do "replay" based unit tests. 28 | // 29 | // Record doesn't implement i2c.BusCloser on purpose. 30 | type Record struct { 31 | sync.Mutex 32 | Bus i2c.Bus // Bus can be nil if only writes are being recorded. 33 | Ops []IO 34 | } 35 | 36 | func (r *Record) String() string { 37 | return "record" 38 | } 39 | 40 | // Tx implements i2c.Bus 41 | func (r *Record) Tx(addr uint16, w, read []byte) error { 42 | io := IO{Addr: addr} 43 | if len(w) != 0 { 44 | io.W = make([]byte, len(w)) 45 | copy(io.W, w) 46 | } 47 | r.Lock() 48 | defer r.Unlock() 49 | if r.Bus == nil { 50 | if len(read) != 0 { 51 | return conntest.Errorf("i2ctest: read unsupported when no bus is connected") 52 | } 53 | } else { 54 | if err := r.Bus.Tx(addr, w, read); err != nil { 55 | return err 56 | } 57 | } 58 | if len(read) != 0 { 59 | io.R = make([]byte, len(read)) 60 | copy(io.R, read) 61 | } 62 | r.Ops = append(r.Ops, io) 63 | return nil 64 | } 65 | 66 | // SetSpeed implements i2c.Bus. 67 | func (r *Record) SetSpeed(f physic.Frequency) error { 68 | if r.Bus != nil { 69 | return r.Bus.SetSpeed(f) 70 | } 71 | return nil 72 | } 73 | 74 | // SCL implements i2c.Pins. 75 | func (r *Record) SCL() gpio.PinIO { 76 | if p, ok := r.Bus.(i2c.Pins); ok { 77 | return p.SCL() 78 | } 79 | return gpio.INVALID 80 | } 81 | 82 | // SDA implements i2c.Pins. 83 | func (r *Record) SDA() gpio.PinIO { 84 | if p, ok := r.Bus.(i2c.Pins); ok { 85 | return p.SDA() 86 | } 87 | return gpio.INVALID 88 | } 89 | 90 | // Playback implements i2c.Bus and plays back a recorded I/O flow. 91 | // 92 | // While "replay" type of unit tests are of limited value, they still present 93 | // an easy way to do basic code coverage. 94 | // 95 | // Set DontPanic to true to return an error instead of panicking, which is the 96 | // default. 97 | type Playback struct { 98 | sync.Mutex 99 | Ops []IO 100 | Count int 101 | DontPanic bool 102 | SDAPin gpio.PinIO 103 | SCLPin gpio.PinIO 104 | } 105 | 106 | func (p *Playback) String() string { 107 | return "playback" 108 | } 109 | 110 | // Close implements i2c.BusCloser. 111 | // 112 | // Close() verifies that all the expected Ops have been consumed. 113 | func (p *Playback) Close() error { 114 | p.Lock() 115 | defer p.Unlock() 116 | if len(p.Ops) != p.Count { 117 | return errorf(p.DontPanic, "i2ctest: expected playback to be empty: I/O count %d; expected %d", p.Count, len(p.Ops)) 118 | } 119 | return nil 120 | } 121 | 122 | // Tx implements i2c.Bus. 123 | func (p *Playback) Tx(addr uint16, w, r []byte) error { 124 | p.Lock() 125 | defer p.Unlock() 126 | if len(p.Ops) <= p.Count { 127 | return errorf(p.DontPanic, "i2ctest: unexpected Tx() (count #%d) expecting i2ctest.IO{Addr:%d, W:%#v, R:%#v}", p.Count, addr, w, r) 128 | } 129 | if addr != p.Ops[p.Count].Addr { 130 | return errorf(p.DontPanic, "i2ctest: unexpected addr (count #%d) %d != %d", p.Count, addr, p.Ops[p.Count].Addr) 131 | } 132 | if !bytes.Equal(p.Ops[p.Count].W, w) { 133 | return errorf(p.DontPanic, "i2ctest: unexpected write (count #%d) %#v != %#v", p.Count, w, p.Ops[p.Count].W) 134 | } 135 | if len(p.Ops[p.Count].R) != len(r) { 136 | return errorf(p.DontPanic, "i2ctest: unexpected read buffer length (count #%d) %d != %d", p.Count, len(r), len(p.Ops[p.Count].R)) 137 | } 138 | copy(r, p.Ops[p.Count].R) 139 | p.Count++ 140 | return nil 141 | } 142 | 143 | // SetSpeed implements i2c.Bus. 144 | func (p *Playback) SetSpeed(f physic.Frequency) error { 145 | return nil 146 | } 147 | 148 | // SCL implements i2c.Pins. 149 | func (p *Playback) SCL() gpio.PinIO { 150 | return p.SCLPin 151 | } 152 | 153 | // SDA implements i2c.Pins. 154 | func (p *Playback) SDA() gpio.PinIO { 155 | return p.SDAPin 156 | } 157 | 158 | // 159 | 160 | // errorf is the internal implementation that optionally panic. 161 | // 162 | // If dontPanic is false, it panics instead. 163 | func errorf(dontPanic bool, format string, a ...interface{}) error { 164 | err := conntest.Errorf(format, a...) 165 | if !dontPanic { 166 | panic(err) 167 | } 168 | return err 169 | } 170 | 171 | var _ i2c.Bus = &Record{} 172 | var _ i2c.Pins = &Record{} 173 | var _ i2c.Bus = &Playback{} 174 | var _ i2c.Pins = &Playback{} 175 | -------------------------------------------------------------------------------- /i2c/i2ctest/i2ctest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2ctest 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/conntest" 11 | "periph.io/x/conn/v3/gpio" 12 | "periph.io/x/conn/v3/gpio/gpiotest" 13 | ) 14 | 15 | func TestRecord_empty(t *testing.T) { 16 | r := Record{} 17 | if s := r.String(); s != "record" { 18 | t.Fatal(s) 19 | } 20 | if err := r.SetSpeed(-100); err != nil { 21 | t.Fatal(err) 22 | } 23 | if r.Tx(1, nil, []byte{'a'}) == nil { 24 | t.Fatal("Bus is nil") 25 | } 26 | if s := r.SCL(); s != gpio.INVALID { 27 | t.Fatal(s) 28 | } 29 | if s := r.SDA(); s != gpio.INVALID { 30 | t.Fatal(s) 31 | } 32 | } 33 | 34 | func TestRecord_Tx_empty(t *testing.T) { 35 | r := Record{} 36 | if err := r.Tx(1, nil, nil); err != nil { 37 | t.Fatal(err) 38 | } 39 | if len(r.Ops) != 1 { 40 | t.Fatal(r.Ops) 41 | } 42 | if err := r.Tx(1, []byte{'a', 'b'}, nil); err != nil { 43 | t.Fatal(err) 44 | } 45 | if len(r.Ops) != 2 { 46 | t.Fatal(r.Ops) 47 | } 48 | if r.Tx(1, []byte{'a', 'b'}, []byte{'d'}) == nil { 49 | t.Fatal("Bus is nil") 50 | } 51 | if len(r.Ops) != 2 { 52 | t.Fatal(r.Ops) 53 | } 54 | } 55 | 56 | func TestPlayback(t *testing.T) { 57 | p := Playback{ 58 | SDAPin: &gpiotest.Pin{N: "DA"}, 59 | SCLPin: &gpiotest.Pin{N: "CL"}, 60 | } 61 | if s := p.String(); s != "playback" { 62 | t.Fatal(s) 63 | } 64 | if err := p.SetSpeed(-100); err != nil { 65 | t.Fatal(err) 66 | } 67 | if err := p.Close(); err != nil { 68 | t.Fatal(err) 69 | } 70 | if n := p.SDA().Name(); n != "DA" { 71 | t.Fatal(n) 72 | } 73 | if n := p.SCL().Name(); n != "CL" { 74 | t.Fatal(n) 75 | } 76 | } 77 | 78 | func TestPlayback_Close_panic(t *testing.T) { 79 | p := Playback{Ops: []IO{{W: []byte{10}}}} 80 | defer func() { 81 | v := recover() 82 | err, ok := v.(error) 83 | if !ok { 84 | t.Fatal("expected error") 85 | } 86 | if !conntest.IsErr(err) { 87 | t.Fatalf("unexpected error: %v", err) 88 | } 89 | }() 90 | _ = p.Close() 91 | t.Fatal("shouldn't run") 92 | } 93 | 94 | func TestPlayback_Tx(t *testing.T) { 95 | p := Playback{ 96 | Ops: []IO{ 97 | { 98 | Addr: 23, 99 | W: []byte{10}, 100 | R: []byte{12}, 101 | }, 102 | }, 103 | DontPanic: true, 104 | } 105 | if p.Tx(23, nil, nil) == nil { 106 | t.Fatal("missing read and write") 107 | } 108 | if p.Close() == nil { 109 | t.Fatal("Ops is not empty") 110 | } 111 | v := [1]byte{} 112 | if p.Tx(42, []byte{10}, v[:]) == nil { 113 | t.Fatal("invalid address") 114 | } 115 | if p.Tx(23, []byte{10}, make([]byte, 2)) == nil { 116 | t.Fatal("invalid read size") 117 | } 118 | if err := p.Tx(23, []byte{10}, v[:]); err != nil { 119 | t.Fatal(err) 120 | } 121 | if v[0] != 12 { 122 | t.Fatalf("expected 12, got %v", v) 123 | } 124 | if err := p.Tx(23, []byte{10}, v[:]); err == nil { 125 | t.Fatal("Playback.Ops is empty") 126 | } 127 | if err := p.Close(); err != nil { 128 | t.Fatal(err) 129 | } 130 | } 131 | 132 | func TestRecord_Playback(t *testing.T) { 133 | r := Record{ 134 | Bus: &Playback{ 135 | Ops: []IO{ 136 | { 137 | Addr: 23, 138 | W: []byte{10}, 139 | R: []byte{12}, 140 | }, 141 | }, 142 | DontPanic: true, 143 | SDAPin: &gpiotest.Pin{N: "DA"}, 144 | SCLPin: &gpiotest.Pin{N: "CL"}, 145 | }, 146 | } 147 | if err := r.SetSpeed(-100); err != nil { 148 | t.Fatal(err) 149 | } 150 | if n := r.SDA().Name(); n != "DA" { 151 | t.Fatal(n) 152 | } 153 | if n := r.SCL().Name(); n != "CL" { 154 | t.Fatal(n) 155 | } 156 | 157 | v := [1]byte{} 158 | if err := r.Tx(23, []byte{10}, v[:]); err != nil { 159 | t.Fatal(err) 160 | } 161 | if v[0] != 12 { 162 | t.Fatalf("expected 12, got %v", v) 163 | } 164 | if r.Tx(23, []byte{10}, v[:]) == nil { 165 | t.Fatal("Playback.Ops is empty") 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /i2s/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package i2s 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | SCK pin.Func = "I2S_SCK" // Clock; occasionally named BCLK 12 | WS pin.Func = "I2S_WS" // Word (Function) select; occasionally named FS or LRCLK 13 | IN pin.Func = "I2S_DIN" // Data in (e.g. microphone) 14 | OUT pin.Func = "I2S_DOUT" // Data out (e.g. speakers) 15 | MCLK pin.Func = "I2S_MCLK" // Master clock; rarely used 16 | ) 17 | -------------------------------------------------------------------------------- /i2s/i2s.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package i2s will eventually define the API to communicate with devices over 6 | // the I²S protocol. 7 | // 8 | // The protocol is meant to transfer audio. 9 | // 10 | // See https://en.wikipedia.org/wiki/I%C2%B2S for more information. 11 | package i2s 12 | -------------------------------------------------------------------------------- /jtag/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package jtag 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | TCK pin.Func = "JTAG_TCK" // Test clock 12 | TDI pin.Func = "JTAG_TDI" // Test mode data input 13 | TDO pin.Func = "JTAG_TDO" // Test mode data output 14 | TMS pin.Func = "JTAG_TMS" // Test mode select 15 | TRST pin.Func = "JTAG_TRST" // Test reset 16 | ) 17 | -------------------------------------------------------------------------------- /jtag/jtag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package jtag will eventually define the API to communicate with devices over 6 | // the JTAG protocol. 7 | // 8 | // See https://en.wikipedia.org/wiki/JTAG for background information. 9 | package jtag 10 | -------------------------------------------------------------------------------- /mmr/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package mmr_test 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "log" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/i2c" 14 | "periph.io/x/conn/v3/i2c/i2creg" 15 | "periph.io/x/conn/v3/mmr" 16 | "periph.io/x/conn/v3/onewire" 17 | "periph.io/x/conn/v3/onewire/onewirereg" 18 | ) 19 | 20 | func ExampleDev8() { 21 | // Make sure periph is initialized. 22 | // TODO: Use host.Init(). It is not used in this example to prevent circular 23 | // go package import. 24 | if _, err := driverreg.Init(); err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Open a connection, using I²C as an example: 29 | b, err := i2creg.Open("") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer b.Close() 34 | c := &i2c.Dev{Bus: b, Addr: 0xD0} 35 | 36 | d := mmr.Dev8{Conn: c, Order: binary.BigEndian} 37 | v, err := d.ReadUint8(0xD0) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | if v == 0x60 { 42 | fmt.Printf("Found bme280 on bus %s\n", b) 43 | } 44 | } 45 | 46 | func ExampleDev8_ReadStruct() { 47 | // Make sure periph is initialized. 48 | // TODO: Use host.Init(). It is not used in this example to prevent circular 49 | // go package import. 50 | if _, err := driverreg.Init(); err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | // Open a connection, using I²C as an example: 55 | b, err := i2creg.Open("") 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | defer b.Close() 60 | c := &i2c.Dev{Bus: b, Addr: 0xD0} 61 | 62 | d := mmr.Dev8{Conn: c, Order: binary.BigEndian} 63 | flags := struct { 64 | Flag16 uint16 65 | Flag8 [2]uint8 66 | }{} 67 | if err = d.ReadStruct(0xD0, &flags); err != nil { 68 | log.Fatal(err) 69 | } 70 | // Use flags.Flag16 and flags.Flag8. 71 | } 72 | 73 | func ExampleDev8_WriteStruct() { 74 | // Make sure periph is initialized. 75 | // TODO: Use host.Init(). It is not used in this example to prevent circular 76 | // go package import. 77 | if _, err := driverreg.Init(); err != nil { 78 | log.Fatal(err) 79 | } 80 | 81 | // Open a connection, using 1-wire as an example: 82 | b, err := onewirereg.Open("") 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | defer b.Close() 87 | c := &onewire.Dev{Bus: b, Addr: 0xD0} 88 | 89 | d := mmr.Dev8{Conn: c, Order: binary.LittleEndian} 90 | flags := struct { 91 | Flag16 uint16 92 | Flag8 [2]uint8 93 | }{ 94 | 0x1234, 95 | [2]uint8{1, 2}, 96 | } 97 | if err = d.WriteStruct(0xD0, &flags); err != nil { 98 | log.Fatal(err) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /onewire/crc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | // CheckCRC verifies that the last byte of the buffer contains the CRC of the previous bytes. 8 | func CheckCRC(buf []byte) bool { 9 | if len(buf) == 0 { 10 | return false 11 | } 12 | return CalcCRC(buf[:len(buf)-1]) == buf[len(buf)-1] 13 | } 14 | 15 | // CalcCRC calculates the 8-bit CRC across the buffer of bytes and returns it. 16 | // 17 | // The Dallas Semi / Maxim Integrated 1-Wire CRC calculation is described in App Note 27: 18 | // https://www.maximintegrated.com/en/app-notes/index.mvp/id/27. 19 | func CalcCRC(buf []byte) byte { 20 | var crc byte 21 | for _, b := range buf { 22 | crc = crcTable[crc^b] 23 | } 24 | return crc 25 | } 26 | 27 | // crcTable comes from https://www.maximintegrated.com/en/app-notes/index.mvp/id/27 28 | var crcTable = []byte{ 29 | 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 30 | 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 31 | 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 32 | 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 33 | 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 34 | 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 35 | 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 36 | 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 37 | 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 38 | 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 39 | 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 40 | 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 41 | 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 42 | 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 43 | 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 44 | 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53, 45 | } 46 | -------------------------------------------------------------------------------- /onewire/crc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | import "testing" 8 | 9 | func TestCheckCRC(t *testing.T) { 10 | a := []byte{1, 2, 3, 4, 5, 6, 7} 11 | c := CalcCRC(a) 12 | if c != 15 { 13 | t.Fatal(c) 14 | } 15 | b := append([]byte{}, a...) 16 | b = append(b, c) 17 | if !CheckCRC(b) { 18 | t.Fatal("expected good crc") 19 | } 20 | b[len(b)-1]++ 21 | if CheckCRC(b) { 22 | t.Fatal("expected bad crc") 23 | } 24 | if CheckCRC(nil) { 25 | t.Fatal("expected bad crc") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /onewire/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/onewire" 13 | "periph.io/x/conn/v3/onewire/onewirereg" 14 | ) 15 | 16 | func Example() { 17 | // Make sure periph is initialized. 18 | // TODO: Use host.Init(). It is not used in this example to prevent circular 19 | // go package import. 20 | if _, err := driverreg.Init(); err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // Use onewirereg 1-wire bus registry to find the first available 1-wire bus. 25 | b, err := onewirereg.Open("") 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer b.Close() 30 | 31 | // Dev is a valid conn.Conn. 32 | d := &onewire.Dev{Addr: 23, Bus: b} 33 | 34 | // Send a command and expect a 5 bytes reply. 35 | write := []byte{0x10} 36 | read := make([]byte, 5) 37 | if err := d.Tx(write, read); err != nil { 38 | log.Fatal(err) 39 | } 40 | fmt.Printf("%v\n", read) 41 | } 42 | 43 | func ExamplePins() { 44 | // Make sure periph is initialized. 45 | // TODO: Use host.Init(). It is not used in this example to prevent circular 46 | // go package import. 47 | if _, err := driverreg.Init(); err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | // Use onewirereg 1-wire bus registry to find the first available 1-wire bus. 52 | b, err := onewirereg.Open("") 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | defer b.Close() 57 | 58 | // Prints out the gpio pin used. 59 | if p, ok := b.(onewire.Pins); ok { 60 | fmt.Printf("Q: %s", p.Q()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /onewire/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | Q pin.Func = "OW_Q" // Data 12 | ) 13 | -------------------------------------------------------------------------------- /onewire/onewire_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "testing" 11 | 12 | "periph.io/x/conn/v3" 13 | ) 14 | 15 | func TestPullUp(t *testing.T) { 16 | if s := WeakPullup.String(); s != "Weak" { 17 | t.Fatal(s) 18 | } 19 | if s := StrongPullup.String(); s != "Strong" { 20 | t.Fatal(s) 21 | } 22 | } 23 | 24 | func TestNoDevicesError(t *testing.T) { 25 | e := noDevicesError("no") 26 | if !e.NoDevices() { 27 | t.Fatal("expected NoDevices") 28 | } 29 | if s := e.Error(); s != "no" { 30 | t.Fatal(s) 31 | } 32 | } 33 | 34 | func TestShortedBusError(t *testing.T) { 35 | e := shortedBusError("no") 36 | if !e.IsShorted() { 37 | t.Fatal("expected IsShorted") 38 | } 39 | if !e.BusError() { 40 | t.Fatal("expected BusError") 41 | } 42 | if s := e.Error(); s != "no" { 43 | t.Fatal(s) 44 | } 45 | } 46 | 47 | func TestBusError(t *testing.T) { 48 | e := busError("no") 49 | if !e.BusError() { 50 | t.Fatal("expected BusError") 51 | } 52 | if s := e.Error(); s != "no" { 53 | t.Fatal(s) 54 | } 55 | } 56 | 57 | func TestDevString(t *testing.T) { 58 | d := Dev{&fakeBus{}, 12} 59 | if s := d.String(); s != "fake(0x000000000000000c)" { 60 | t.Fatalf("got %s", s) 61 | } 62 | } 63 | 64 | func TestDevTx(t *testing.T) { 65 | exErr := errors.New("yes") 66 | b := &fakeBus{err: exErr, r: []byte{1, 2, 3}} 67 | d := Dev{b, 12} 68 | r := make([]byte, 3) 69 | w := []byte{3, 4, 5} 70 | if err := d.Tx(w, r); exErr != err { 71 | t.Fatalf("got %s", err) 72 | } 73 | expected := []byte{85, 12, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5} 74 | if !bytes.Equal(b.w, expected) { 75 | t.Fatal(b.w) 76 | } 77 | expected = []byte{1, 2, 3} 78 | if !bytes.Equal(r, expected) { 79 | t.Fatalf("r: %v != %v", b.r, expected) 80 | } 81 | if i := d.Duplex(); i != conn.Half { 82 | t.Fatal(i) 83 | } 84 | } 85 | 86 | func TestDevTxPower(t *testing.T) { 87 | b := nopBus("hi") 88 | d := Dev{Bus: &b, Addr: 12} 89 | if s := d.String(); s != "hi(0x000000000000000c)" { 90 | t.Fatal(s) 91 | } 92 | // TODO(maruel): Verify the output. 93 | if err := d.Tx([]byte{1}, nil); err != nil { 94 | t.Fatal(err) 95 | } 96 | if err := d.TxPower([]byte{1}, nil); err != nil { 97 | t.Fatal(err) 98 | } 99 | if v := d.Duplex(); v != conn.Half { 100 | t.Fatal(v) 101 | } 102 | } 103 | 104 | // 105 | 106 | type fakeBus struct { 107 | power Pullup 108 | err error 109 | w, r []byte 110 | } 111 | 112 | func (f *fakeBus) Close() error { 113 | return nil 114 | } 115 | 116 | func (f *fakeBus) String() string { 117 | return "fake" 118 | } 119 | 120 | func (f *fakeBus) Tx(w, r []byte, power Pullup) error { 121 | f.power = power 122 | f.w = append(f.w, w...) 123 | copy(r, f.r) 124 | f.r = f.r[len(r):] 125 | return f.err 126 | } 127 | 128 | func (f *fakeBus) Search(alarmOnly bool) ([]Address, error) { 129 | return nil, errors.New("not implemented") 130 | } 131 | 132 | // nopBus implements Bus. 133 | type nopBus string 134 | 135 | func (b *nopBus) String() string { return string(*b) } 136 | func (b *nopBus) Tx(w, r []byte, power Pullup) error { return nil } 137 | func (b *nopBus) Search(alarmOnly bool) ([]Address, error) { return nil, nil } 138 | func (b *nopBus) Close() error { return nil } 139 | -------------------------------------------------------------------------------- /onewire/onewirereg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewirereg_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "strings" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/onewire" 14 | "periph.io/x/conn/v3/onewire/onewirereg" 15 | ) 16 | 17 | func ExampleAll() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // Enumerate all 1-wire buses available and the corresponding pins. 26 | fmt.Print("1-wire buses available:\n") 27 | for _, ref := range onewirereg.All() { 28 | fmt.Printf("- %s\n", ref.Name) 29 | if ref.Number != -1 { 30 | fmt.Printf(" %d\n", ref.Number) 31 | } 32 | if len(ref.Aliases) != 0 { 33 | fmt.Printf(" %s\n", strings.Join(ref.Aliases, " ")) 34 | } 35 | 36 | b, err := ref.Open() 37 | if err != nil { 38 | fmt.Printf(" Failed to open: %v", err) 39 | } 40 | if p, ok := b.(onewire.Pins); ok { 41 | fmt.Printf(" Q: %s", p.Q()) 42 | } 43 | if err := b.Close(); err != nil { 44 | fmt.Printf(" Failed to close: %v", err) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /onewire/onewirereg/onewirereg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewirereg 6 | 7 | import ( 8 | "errors" 9 | "testing" 10 | 11 | "periph.io/x/conn/v3/onewire" 12 | ) 13 | 14 | func TestOpen(t *testing.T) { 15 | defer reset() 16 | if _, err := Open(""); err == nil { 17 | t.Fatal("no bus registered") 18 | } 19 | if err := Register("a", []string{"x"}, 1, fakeBuser); err != nil { 20 | t.Fatal(err) 21 | } 22 | if o, err := Open(""); o == nil || err != nil { 23 | t.Fatal(o, err) 24 | } 25 | if o, err := Open("1"); o == nil || err != nil { 26 | t.Fatal(o, err) 27 | } 28 | if o, err := Open("x"); o == nil || err != nil { 29 | t.Fatal(o, err) 30 | } 31 | if o, err := Open("y"); o != nil || err == nil { 32 | t.Fatal(o, err) 33 | } 34 | } 35 | 36 | func TestDefault_NoNumber(t *testing.T) { 37 | defer reset() 38 | if err := Register("a", nil, -1, fakeBuser); err != nil { 39 | t.Fatal(err) 40 | } 41 | if o, err := Open(""); o == nil || err != nil { 42 | t.Fatal(o, err) 43 | } 44 | } 45 | 46 | func TestAll(t *testing.T) { 47 | defer reset() 48 | if a := All(); len(a) != 0 { 49 | t.Fatal(a) 50 | } 51 | if err := Register("a", nil, 1, fakeBuser); err != nil { 52 | t.Fatal(err) 53 | } 54 | if err := Register("b", nil, 2, fakeBuser); err != nil { 55 | t.Fatal(err) 56 | } 57 | if a := All(); len(a) != 2 { 58 | t.Fatal(a) 59 | } 60 | } 61 | 62 | func TestRef(t *testing.T) { 63 | out := insertRef(nil, &Ref{Name: "b"}) 64 | out = insertRef(out, &Ref{Name: "d"}) 65 | out = insertRef(out, &Ref{Name: "c"}) 66 | out = insertRef(out, &Ref{Name: "a"}) 67 | for i, l := range []string{"a", "b", "c", "d"} { 68 | if out[i].Name != l { 69 | t.Fatal(out) 70 | } 71 | } 72 | } 73 | 74 | func TestRegister(t *testing.T) { 75 | defer reset() 76 | if err := Register("a", []string{"b"}, 42, fakeBuser); err != nil { 77 | t.Fatal(err) 78 | } 79 | if Register("a", nil, -1, fakeBuser) == nil { 80 | t.Fatal("same bus name") 81 | } 82 | if Register("b", nil, -1, fakeBuser) == nil { 83 | t.Fatal("same bus alias name") 84 | } 85 | if Register("c", nil, 42, fakeBuser) == nil { 86 | t.Fatal("same bus number") 87 | } 88 | if Register("c", []string{"a"}, -1, fakeBuser) == nil { 89 | t.Fatal("same bus alias") 90 | } 91 | if Register("c", []string{"b"}, -1, fakeBuser) == nil { 92 | t.Fatal("same bus alias") 93 | } 94 | } 95 | 96 | func TestRegister_fail(t *testing.T) { 97 | defer reset() 98 | if Register("a", nil, -1, nil) == nil { 99 | t.Fatal("missing Opener") 100 | } 101 | if Register("a", nil, -2, fakeBuser) == nil { 102 | t.Fatal("bad bus number") 103 | } 104 | if Register("", nil, 42, fakeBuser) == nil { 105 | t.Fatal("missing name") 106 | } 107 | if Register("1", nil, 42, fakeBuser) == nil { 108 | t.Fatal("numeric name") 109 | } 110 | if Register("a:b", nil, 42, fakeBuser) == nil { 111 | t.Fatal("':' in name") 112 | } 113 | if Register("a", []string{"a"}, 0, fakeBuser) == nil { 114 | t.Fatal("\"a\" is already registered") 115 | } 116 | if Register("a", []string{""}, 0, fakeBuser) == nil { 117 | t.Fatal("empty alias") 118 | } 119 | if Register("a", []string{"1"}, 0, fakeBuser) == nil { 120 | t.Fatal("numeric alias") 121 | } 122 | if Register("a", []string{"a:b"}, 0, fakeBuser) == nil { 123 | t.Fatal("':' in alias") 124 | } 125 | if a := All(); len(a) != 0 { 126 | t.Fatal(a) 127 | } 128 | } 129 | 130 | func TestUnregister(t *testing.T) { 131 | defer reset() 132 | if Unregister("") == nil { 133 | t.Fatal("unregister empty") 134 | } 135 | if Unregister("a") == nil { 136 | t.Fatal("unregister non-existing") 137 | } 138 | if err := Register("a", []string{"b"}, 0, fakeBuser); err != nil { 139 | t.Fatal(err) 140 | } 141 | if err := Unregister("a"); err != nil { 142 | t.Fatal(err) 143 | } 144 | } 145 | 146 | // 147 | 148 | func fakeBuser() (onewire.BusCloser, error) { 149 | return &fakeBus{}, nil 150 | } 151 | 152 | func reset() { 153 | mu.Lock() 154 | defer mu.Unlock() 155 | byName = map[string]*Ref{} 156 | byNumber = map[int]*Ref{} 157 | byAlias = map[string]*Ref{} 158 | } 159 | 160 | type fakeBus struct { 161 | power onewire.Pullup 162 | err error 163 | w, r []byte 164 | } 165 | 166 | func (f *fakeBus) Close() error { 167 | return nil 168 | } 169 | 170 | func (f *fakeBus) String() string { 171 | return "fake" 172 | } 173 | 174 | func (f *fakeBus) Tx(w, r []byte, power onewire.Pullup) error { 175 | f.power = power 176 | f.w = append(f.w, w...) 177 | copy(r, f.r) 178 | f.r = f.r[len(r):] 179 | return f.err 180 | } 181 | 182 | func (f *fakeBus) Search(alarmOnly bool) ([]onewire.Address, error) { 183 | return nil, errors.New("not implemented") 184 | } 185 | -------------------------------------------------------------------------------- /onewire/search.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | import ( 8 | "errors" 9 | "strconv" 10 | ) 11 | 12 | // BusSearcher provides the basic bus transaction necessary to search a 1-wire 13 | // bus for devices. Buses that implement this interface can be searched with 14 | // the Search function. 15 | type BusSearcher interface { 16 | Bus 17 | // SearchTriplet performs a single bit search triplet command on the bus, 18 | // waits for it to complete and returns the result. 19 | SearchTriplet(direction byte) (TripletResult, error) 20 | } 21 | 22 | // TripletResult is the result of a SearchTriplet operation. 23 | type TripletResult struct { 24 | GotZero bool // a device with a zero in the current bit position responded 25 | GotOne bool // a device with a one in the current bit position responded 26 | Taken uint8 // direction taken: 0 or 1 27 | } 28 | 29 | // Search performs a "search" cycle on the 1-wire bus and returns the addresses 30 | // of all devices on the bus if alarmOnly is false and of all devices in alarm 31 | // state if alarmOnly is true. 32 | // 33 | // If an error occurs during the search the already-discovered devices are 34 | // returned with the error. 35 | // 36 | // For a description of the search algorithm, see Maxim's AppNote 187 37 | // https://www.maximintegrated.com/en/app-notes/index.mvp/id/187 38 | // 39 | // This function is defined here so the implementation of buses that support 40 | // the BusSearcher interface can call it. Applications should call Bus.Search. 41 | func Search(bus BusSearcher, alarmOnly bool) ([]Address, error) { 42 | var devices []Address // devices we're finding 43 | lastDiscrepancy := -1 // how far we need to repeat the same ID in the next iteration 44 | var lastDevice uint64 // ID of last device found 45 | 46 | // Loop to do the search. Each iteration detects one device. 47 | for { 48 | // Issue a search command. 49 | cmd := byte(0xf0) // plain search 50 | if alarmOnly { 51 | cmd = 0xec // alarm search 52 | } 53 | if err := bus.Tx([]byte{cmd}, nil, WeakPullup); err != nil { 54 | // Expect an NoDevicesError if no device is present on the bus and 55 | // pass that back. 56 | return devices, err 57 | } 58 | 59 | // Loop to accumulate the 64 bits of an ID. 60 | discrepancy := -1 // how far we need to repeat the same ID in this iteration 61 | var device uint64 // ID of current device 62 | var idBytes [8]byte // ID of device as bytes 63 | for bit := 0; bit < 64; bit++ { 64 | // Decide which direction to search into: 0 or 1. 65 | var dir byte 66 | if bit < lastDiscrepancy { 67 | // We haven't reached the last discrepancy yet, so we 68 | // need to repeat the bits of the last device. 69 | dir = byte((lastDevice >> uint8(bit)) & 1) 70 | } else if bit == lastDiscrepancy { 71 | // We reached the bit where we picked 0 last time and 72 | // now we need 1. 73 | dir = 1 74 | } 75 | 76 | // Perform triplet operation and do an explicit error check so 77 | // we abort the search on error. 78 | result, err := bus.SearchTriplet(dir) 79 | if err != nil { 80 | return devices, err 81 | } 82 | 83 | // Check for the absence of devices on the bus. This is a 1-wire 84 | // bus error condition and we return a partial result. 85 | if !result.GotZero && !result.GotOne { 86 | return devices, errors.New("onewire: devices disappeared during search") 87 | } 88 | 89 | // Check whether we have devices responding for 0 and 1 and we 90 | // picked 0. 91 | if result.GotZero && result.GotOne && result.Taken == 0 { 92 | discrepancy = bit 93 | } 94 | 95 | // Shift a bit into the current device's ID 96 | device |= uint64(result.Taken) << uint(bit) 97 | 98 | // If we got a full byte then save it for CRC calculation. 99 | if bit&7 == 7 { 100 | idBytes[bit>>3] = byte(device >> uint(bit-7)) 101 | } 102 | } 103 | 104 | // Verify the CRC and record device if we got it right. 105 | if !CheckCRC(idBytes[:]) { 106 | // CRC error: return partial result. This is a transient error. 107 | msg := "onewire: CRC error during search, addr=[" 108 | for i, b := range idBytes { 109 | msg += strconv.Itoa(int(b)) 110 | if i != len(idBytes)-1 { 111 | msg += " " 112 | } 113 | } 114 | return devices, busError(msg + "]") 115 | } 116 | devices = append(devices, Address(device)) 117 | lastDevice = device 118 | if lastDiscrepancy = discrepancy; lastDiscrepancy == -1 { 119 | return devices, nil // we reached the last device 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /onewire/search_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package onewire 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "errors" 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | // TestSearch tests the onewire.Search function using the Playback bus preloaded 16 | // with a synthetic set of devices. 17 | func TestSearch(t *testing.T) { 18 | p := playback{ 19 | Devices: []Address{ 20 | 0x0000000000000000, 21 | 0x0000000000000001, 22 | 0x0010000000000000, 23 | 0x0000100000000000, 24 | 0xffffffffffffffff, 25 | 0xfc0000013199a928, 26 | 0xf100000131856328, 27 | }, 28 | } 29 | // Fix-up the CRC byte for each device. 30 | var buf [8]byte 31 | for i := range p.Devices { 32 | binary.LittleEndian.PutUint64(buf[:], uint64(p.Devices[i])) 33 | crc := CalcCRC(buf[:7]) 34 | p.Devices[i] = (Address(crc) << 56) | (p.Devices[i] & 0x00ffffffffffffff) 35 | } 36 | 37 | // We're doing one search operation per device, plus a last one. 38 | p.Ops = make([]IO, len(p.Devices)+1) 39 | for i := 0; i < len(p.Ops); i++ { 40 | p.Ops[i] = IO{Write: []byte{0xf0}, Pull: WeakPullup} 41 | } 42 | 43 | // Start search. 44 | if err := p.Tx([]byte{0xf0}, nil, WeakPullup); err != nil { 45 | t.Fatal(err) 46 | } 47 | 48 | // Perform search. 49 | addrs, err := p.Search(false) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | // Verify we got all devices. 55 | if len(addrs) != len(p.Devices) { 56 | t.Fatalf("expected %d devices, got %d", len(p.Devices), len(addrs)) 57 | } 58 | match: 59 | for _, ai := range p.Devices { 60 | for _, aj := range addrs { 61 | if ai == aj { 62 | continue match 63 | } 64 | } 65 | t.Errorf("expected to find %#x but didn't", ai) 66 | } 67 | if err := p.Close(); err != nil { 68 | t.Fatal(err) 69 | } 70 | } 71 | 72 | func TestSearch_Tx_err(t *testing.T) { 73 | p := playback{} 74 | if addrs, err := p.Search(true); len(addrs) != 0 || err == nil { 75 | t.Fatal("expected Tx() error") 76 | } 77 | } 78 | 79 | // 80 | 81 | type IO struct { 82 | Write []byte 83 | Read []byte 84 | Pull Pullup 85 | } 86 | 87 | // playback is stripped down a copy of onewiretest.Playback. 88 | type playback struct { 89 | Ops []IO 90 | Devices []Address 91 | inactive []bool 92 | searchBit uint 93 | } 94 | 95 | func (p *playback) String() string { 96 | return "playback" 97 | } 98 | 99 | func (p *playback) Close() error { 100 | if len(p.Ops) != 0 { 101 | return fmt.Errorf("onewiretest: expected playback to be empty: %#v", p.Ops) 102 | } 103 | return nil 104 | } 105 | 106 | func (p *playback) Tx(w, r []byte, pull Pullup) error { 107 | if len(p.Ops) == 0 { 108 | return errors.New("onewiretest: unexpected Tx()") 109 | } 110 | if !bytes.Equal(p.Ops[0].Write, w) { 111 | return fmt.Errorf("onewiretest: unexpected write %#v != %#v", w, p.Ops[0].Write) 112 | } 113 | if len(p.Ops[0].Read) != len(r) { 114 | return fmt.Errorf("onewiretest: unexpected read buffer length %d != %d", len(r), len(p.Ops[0].Read)) 115 | } 116 | if pull != p.Ops[0].Pull { 117 | return fmt.Errorf("onewiretest: unexpected pullup %s != %s", pull, p.Ops[0].Pull) 118 | } 119 | // Determine whether this starts a search and reset search state. 120 | if len(w) > 0 && w[0] == 0xf0 { 121 | p.searchBit = 0 122 | p.inactive = make([]bool, len(p.Devices)) 123 | } 124 | // Concoct response. 125 | copy(r, p.Ops[0].Read) 126 | p.Ops = p.Ops[1:] 127 | return nil 128 | } 129 | 130 | func (p *playback) Search(alarmOnly bool) ([]Address, error) { 131 | return Search(p, alarmOnly) 132 | } 133 | 134 | func (p *playback) SearchTriplet(direction byte) (TripletResult, error) { 135 | tr := TripletResult{} 136 | if p.searchBit > 63 { 137 | return tr, errors.New("onewiretest: search performs more than 64 triplet operations") 138 | } 139 | if len(p.inactive) != len(p.Devices) { 140 | return tr, errors.New("onewiretest: Devices must be initialized before starting search") 141 | } 142 | // Figure out the devices' response. 143 | for i := range p.Devices { 144 | if p.inactive[i] { 145 | continue 146 | } 147 | if (p.Devices[i]>>p.searchBit)&1 == 0 { 148 | tr.GotZero = true 149 | } else { 150 | tr.GotOne = true 151 | } 152 | } 153 | // Decide in which direction to take the search. 154 | switch { 155 | case tr.GotZero && !tr.GotOne: 156 | tr.Taken = 0 157 | case !tr.GotZero && tr.GotOne: 158 | tr.Taken = 1 159 | default: 160 | tr.Taken = direction 161 | } 162 | // Inactivate devices in the direction not taken. 163 | for i := range p.Devices { 164 | if uint8((p.Devices[i]>>p.searchBit)&1) != tr.Taken { 165 | p.inactive[i] = true 166 | } 167 | } 168 | 169 | p.searchBit++ 170 | return tr, nil 171 | } 172 | -------------------------------------------------------------------------------- /physic/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package physic declares types for physical input, outputs and measurement 6 | // units. 7 | // 8 | // This includes temperature, humidity, pressure, tension, current, etc. 9 | // 10 | // # SI units 11 | // 12 | // The supported S.I. units is a subset of the official ones. 13 | // 14 | // T tera 10¹² 1000000000000 15 | // G giga 10⁹ 1000000000 16 | // M mega 10⁶ 1000000 17 | // k kilo 10³ 1000 18 | // m milli 10⁻³ 0.001 19 | // µ,u micro 10⁻⁶ 0.000001 20 | // n nano 10⁻⁹ 0.000000001 21 | // p pico 10⁻¹² 0.000000000001 22 | package physic 23 | -------------------------------------------------------------------------------- /physic/physic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package physic 6 | 7 | import ( 8 | "time" 9 | 10 | "periph.io/x/conn/v3" 11 | ) 12 | 13 | // Env represents measurements from an environmental sensor. 14 | type Env struct { 15 | Temperature Temperature 16 | Pressure Pressure 17 | Humidity RelativeHumidity 18 | } 19 | 20 | // SenseEnv represents an environmental sensor. 21 | type SenseEnv interface { 22 | conn.Resource 23 | 24 | // Sense returns the value read from the sensor. Unsupported metrics are not 25 | // modified. 26 | Sense(env *Env) error 27 | // SenseContinuous initiates a continuous sensing at the specified interval. 28 | // 29 | // It is important to call Halt() once done with the sensing, which will turn 30 | // the device off and will close the channel. 31 | SenseContinuous(interval time.Duration) (<-chan Env, error) 32 | // Precision returns this sensor's precision. 33 | // 34 | // The env values are set to the number of bits that are significant for each 35 | // items that this sensor can measure. 36 | // 37 | // Precision is not accuracy. The sensor may have absolute and relative 38 | // errors in its measurement, that are likely well above the reported 39 | // precision. Accuracy may be improved on some sensor by using oversampling, 40 | // or doing oversampling in software. Refer to its datasheet if available. 41 | Precision(env *Env) 42 | } 43 | -------------------------------------------------------------------------------- /pin/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pin_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/gpio/gpioreg" 12 | "periph.io/x/conn/v3/pin" 13 | "periph.io/x/conn/v3/spi" 14 | "periph.io/x/conn/v3/uart" 15 | ) 16 | 17 | func ExampleBasicPin() { 18 | // Declare a basic pin, that is not a GPIO, for registration on an header. 19 | b := &pin.BasicPin{N: "Exotic"} 20 | fmt.Println(b) 21 | 22 | // Output: 23 | // Exotic 24 | } 25 | 26 | func ExampleFunc_Specialize() { 27 | // Specializes both bus and line. 28 | fmt.Println(spi.CS.Specialize(1, 2)) 29 | // Specializes only bus. 30 | fmt.Println(spi.MOSI.Specialize(1, -1)) 31 | // Specializes only line. 32 | fmt.Println(pin.Func("CSI_D").Specialize(-1, 3)) 33 | // Specializes neither. 34 | fmt.Println(pin.Func("INVALID").Specialize(-1, -1)) 35 | // Output: 36 | // SPI1_CS2 37 | // SPI1_MOSI 38 | // CSI_D3 39 | // INVALID 40 | } 41 | 42 | func ExampleFunc_Generalize() { 43 | fmt.Println(pin.Func("SPI1_CS2").Generalize()) 44 | fmt.Println(pin.Func("SPI1_MOSI").Generalize()) 45 | fmt.Println(pin.Func("CSI_D3").Generalize()) 46 | fmt.Println(pin.Func("INVALID").Generalize()) 47 | // Output: 48 | // SPI_CS 49 | // SPI_MOSI 50 | // CSI_D 51 | // INVALID 52 | } 53 | 54 | func ExamplePinFunc() { 55 | p := gpioreg.ByName("GPIO14") 56 | if p == nil { 57 | log.Fatal("not running on a raspberry pi") 58 | } 59 | pf, ok := p.(pin.PinFunc) 60 | if !ok { 61 | log.Fatal("pin.PinFunc is not implemented") 62 | } 63 | // Select UART1_TX. 64 | f := uart.TX.Specialize(1, -1) 65 | if err := pf.SetFunc(f); err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pin/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pin 6 | 7 | import ( 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // Func is a pin function. 13 | // 14 | // The Func format must be "[A-Z]+", "[A-Z]+_[A-Z]+" or exceptionally 15 | // "(In|Out)/(Low|High)". 16 | type Func string 17 | 18 | // FuncNone is returned by PinFunc.Func() for a Pin without an active 19 | // functionality. 20 | const FuncNone Func = "" 21 | 22 | // Specialize converts a "BUS_LINE" function and appends the bug number and 23 | // line number, to look like "BUS0_LINE1". 24 | // 25 | // Use -1 to not add a bus or line number. 26 | func (f Func) Specialize(b, l int) Func { 27 | if f == FuncNone { 28 | return FuncNone 29 | } 30 | if b != -1 { 31 | parts := strings.SplitN(string(f), "_", 2) 32 | if len(parts) == 1 { 33 | return FuncNone 34 | } 35 | f = Func(parts[0] + strconv.Itoa(b) + "_" + parts[1]) 36 | } 37 | if l != -1 { 38 | f += Func(strconv.Itoa(l)) 39 | } 40 | return f 41 | } 42 | 43 | // Generalize is the reverse of Specialize(). 44 | func (f Func) Generalize() Func { 45 | parts := strings.SplitN(string(f), "_", 2) 46 | f = Func(strings.TrimRightFunc(parts[0], isNum)) 47 | if len(parts) == 2 { 48 | f += "_" 49 | f += Func(strings.TrimRightFunc(parts[1], isNum)) 50 | } 51 | return f 52 | } 53 | 54 | // 55 | 56 | func isNum(r rune) bool { 57 | return r >= '0' && r <= '9' 58 | } 59 | -------------------------------------------------------------------------------- /pin/func_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pin 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestFunc(t *testing.T) { 12 | if v := FuncNone.Specialize(-1, -1); v != FuncNone { 13 | t.Fatal(v) 14 | } 15 | if v := FuncNone.Specialize(1, -1); v != FuncNone { 16 | t.Fatal(v) 17 | } 18 | if v := FuncNone.Specialize(-1, 1); v != FuncNone { 19 | t.Fatal(v) 20 | } 21 | if v := Func("A").Specialize(-1, 1); v != Func("A1") { 22 | t.Fatal(v) 23 | } 24 | if v := Func("A").Specialize(1, -1); v != FuncNone { 25 | t.Fatal(v) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pin/pin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package pin declare well known pins. 6 | // 7 | // pin is about physical pins, not about their logical function. 8 | // 9 | // While not a protocol strictly speaking, these are "well known constants". 10 | package pin 11 | 12 | import ( 13 | "errors" 14 | 15 | "periph.io/x/conn/v3" 16 | ) 17 | 18 | // These are well known pins. 19 | var ( 20 | INVALID *BasicPin // Either floating or invalid pin 21 | GROUND *BasicPin // Ground 22 | V1_8 *BasicPin // 1.8V (filtered) 23 | V2_8 *BasicPin // 2.8V (filtered) 24 | V3_3 *BasicPin // 3.3V (filtered) 25 | V5 *BasicPin // 5V (filtered) 26 | DC_IN *BasicPin // DC IN; this is normally the 5V input 27 | BAT_PLUS *BasicPin // LiPo Battery + connector 28 | ) 29 | 30 | // Pin is the minimal common interface shared between gpio.PinIO and 31 | // analog.PinIO. 32 | type Pin interface { 33 | conn.Resource 34 | // Name returns the name of the pin. 35 | Name() string 36 | // Number returns the logical pin number or a negative number if the pin is 37 | // not a GPIO, e.g. GROUND, V3_3, etc. 38 | Number() int 39 | // Function returns a user readable string representation of what the pin is 40 | // configured to do. Common case is In and Out but it can be bus specific pin 41 | // name. 42 | // 43 | // Deprecated: Use PinFunc.Func. Will be removed in v4. 44 | Function() string 45 | } 46 | 47 | // PinFunc is a supplementary interface that enables specifically querying for 48 | // the pin function. 49 | // 50 | // TODO(maruel): It will be merged into interface Pin for v4. 51 | type PinFunc interface { 52 | // Func returns the pin's current function. 53 | // 54 | // The returned value may be specialized or generalized, depending on the 55 | // actual port. For example it will likely be generalized for ports served 56 | // over USB (like a FT232H with D0 set as SPI_MOSI) but specialized for 57 | // ports on the base board (like a RPi3 with GPIO10 set as SPI0_MOSI). 58 | Func() Func 59 | // SupportedFuncs returns the possible functions this pin support. 60 | // 61 | // Do not mutate the returned slice. 62 | SupportedFuncs() []Func 63 | // SetFunc sets the pin function. 64 | // 65 | // Example use is to reallocate a RPi3's GPIO14 active function between 66 | // UART0_TX and UART1_TX. 67 | SetFunc(f Func) error 68 | } 69 | 70 | // 71 | 72 | // BasicPin implements Pin as a static pin. 73 | // 74 | // It doesn't have a usable functionality. 75 | type BasicPin struct { 76 | N string 77 | } 78 | 79 | // String implements conn.Resource. 80 | func (b *BasicPin) String() string { 81 | return b.N 82 | } 83 | 84 | // Halt implements conn.Resource. 85 | func (b *BasicPin) Halt() error { 86 | return nil 87 | } 88 | 89 | // Name implements Pin. 90 | func (b *BasicPin) Name() string { 91 | return b.N 92 | } 93 | 94 | // Number implements Pin. 95 | // 96 | // Returns -1 as pin number. 97 | func (b *BasicPin) Number() int { 98 | return -1 99 | } 100 | 101 | // Function implements Pin. 102 | // 103 | // Returns "" as pin function. 104 | func (b *BasicPin) Function() string { 105 | return "" 106 | } 107 | 108 | // Func implements PinFunc. 109 | // 110 | // Returns FuncNone as pin function. 111 | func (b *BasicPin) Func() Func { 112 | return FuncNone 113 | } 114 | 115 | // SupportedFuncs implements PinFunc. 116 | // 117 | // Returns nil. 118 | func (b *BasicPin) SupportedFuncs() []Func { 119 | return nil 120 | } 121 | 122 | // SetFunc implements PinFunc. 123 | func (b *BasicPin) SetFunc(f Func) error { 124 | return errors.New("pin: can't change static pin function") 125 | } 126 | 127 | func init() { 128 | INVALID = &BasicPin{N: "INVALID"} 129 | GROUND = &BasicPin{N: "GROUND"} 130 | V1_8 = &BasicPin{N: "1.8V"} 131 | V2_8 = &BasicPin{N: "2.8V"} 132 | V3_3 = &BasicPin{N: "3.3V"} 133 | V5 = &BasicPin{N: "5V"} 134 | DC_IN = &BasicPin{N: "DC_IN"} 135 | BAT_PLUS = &BasicPin{N: "BAT+"} 136 | } 137 | 138 | var _ Pin = INVALID 139 | var _ PinFunc = INVALID 140 | -------------------------------------------------------------------------------- /pin/pin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pin 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestInvalid(t *testing.T) { 12 | if s := INVALID.String(); s != "INVALID" { 13 | t.Fatal(s) 14 | } 15 | } 16 | 17 | func TestBasicPin(t *testing.T) { 18 | b := BasicPin{N: "Pin1"} 19 | if s := b.String(); s != "Pin1" { 20 | t.Fatal(s) 21 | } 22 | if err := b.Halt(); err != nil { 23 | t.Fatal(err) 24 | } 25 | if s := b.Name(); s != "Pin1" { 26 | t.Fatal(s) 27 | } 28 | if n := b.Number(); n != -1 { 29 | t.Fatal(-1) 30 | } 31 | if s := b.Function(); s != "" { 32 | t.Fatal(s) 33 | } 34 | if f := b.Func(); f != FuncNone { 35 | t.Fatal(f) 36 | } 37 | if f := b.SupportedFuncs(); len(f) != 0 { 38 | t.Fatal(f) 39 | } 40 | if err := b.SetFunc(Func("Out/Low")); err == nil { 41 | t.Fatal("expected failure") 42 | } 43 | } 44 | 45 | func TestV3_3(t *testing.T) { 46 | if f := V3_3.Func(); f != FuncNone { 47 | t.Fatal(f) 48 | } 49 | if f := V3_3.SupportedFuncs(); len(f) != 0 { 50 | t.Fatal(f) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pin/pinreg/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package pinreg is a registry for the physical headers (made up of pins) on 6 | // a host. 7 | package pinreg 8 | -------------------------------------------------------------------------------- /pin/pinreg/pinreg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pinreg 6 | 7 | import ( 8 | "errors" 9 | "strconv" 10 | "sync" 11 | 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/conn/v3/pin" 15 | ) 16 | 17 | // All contains all the on-board headers on a micro computer. 18 | // 19 | // The map key is the header name, e.g. "P1" or "EULER" and the value is a 20 | // slice of slice of pin.Pin. For a 2x20 header, it's going to be a slice of 21 | // [20][2]pin.Pin. 22 | func All() map[string][][]pin.Pin { 23 | mu.Lock() 24 | defer mu.Unlock() 25 | out := make(map[string][][]pin.Pin, len(allHeaders)) 26 | for k, v := range allHeaders { 27 | outV := make([][]pin.Pin, len(v)) 28 | for i, w := range v { 29 | outW := make([]pin.Pin, len(w)) 30 | copy(outW, w) 31 | outV[i] = outW 32 | } 33 | out[k] = outV 34 | } 35 | return out 36 | } 37 | 38 | // Position returns the position on a pin if found. 39 | // 40 | // The header and the pin number. Pin numbers are 1-based. 41 | // 42 | // Returns "", 0 if not connected. 43 | func Position(p pin.Pin) (string, int) { 44 | mu.Lock() 45 | defer mu.Unlock() 46 | pos := byPin[realPin(p).Name()] 47 | return pos.name, pos.number 48 | } 49 | 50 | // IsConnected returns true if the pin is on a header. 51 | func IsConnected(p pin.Pin) bool { 52 | _, i := Position(p) 53 | return i != 0 54 | } 55 | 56 | // Register registers a physical header. 57 | // 58 | // It automatically registers all gpio pins to gpioreg. 59 | func Register(name string, allPins [][]pin.Pin) error { 60 | mu.Lock() 61 | defer mu.Unlock() 62 | if _, ok := allHeaders[name]; ok { 63 | return errors.New("pinreg: header " + strconv.Quote(name) + " was already registered") 64 | } 65 | for i, line := range allPins { 66 | for j, pin := range line { 67 | if pin == nil || len(pin.Name()) == 0 { 68 | return errors.New("pinreg: invalid pin on header " + name + "[" + strconv.Itoa(i+1) + "][" + strconv.Itoa(j+1) + "]") 69 | } 70 | } 71 | } 72 | allHeaders[name] = allPins 73 | number := 1 74 | for _, line := range allPins { 75 | for _, p := range line { 76 | byPin[realPin(p).Name()] = position{name, number} 77 | number++ 78 | } 79 | } 80 | 81 | count := 0 82 | for _, row := range allPins { 83 | for _, p := range row { 84 | count++ 85 | if _, ok := p.(gpio.PinIO); ok { 86 | if err := gpioreg.RegisterAlias(name+"_"+strconv.Itoa(count), p.Name()); err != nil { 87 | // Unregister as much as possible. 88 | _ = unregister(name) 89 | return errors.New("pinreg: " + err.Error()) 90 | } 91 | } 92 | } 93 | } 94 | 95 | return nil 96 | } 97 | 98 | // Unregister removes a previously registered header. 99 | // 100 | // This can happen when an USB device, which exposed an header, is unplugged. 101 | // This is also useful for unit testing. 102 | func Unregister(name string) error { 103 | mu.Lock() 104 | defer mu.Unlock() 105 | return unregister(name) 106 | } 107 | 108 | // 109 | 110 | type position struct { 111 | name string // Header name 112 | number int // Pin number 113 | } 114 | 115 | var ( 116 | mu sync.Mutex 117 | allHeaders = map[string][][]pin.Pin{} // every known headers as per internal lookup table 118 | byPin = map[string]position{} // GPIO pin name to position 119 | ) 120 | 121 | func unregister(name string) error { 122 | if hdr, ok := allHeaders[name]; ok { 123 | var err error 124 | delete(allHeaders, name) 125 | count := 0 126 | for _, row := range hdr { 127 | for _, p := range row { 128 | count++ 129 | if _, ok := p.(gpio.PinIO); ok { 130 | if err1 := gpioreg.Unregister(name + "_" + strconv.Itoa(count)); err1 != nil && err == nil { 131 | // Continue unregistering as much as possible. 132 | err = errors.New("pinreg: " + err1.Error()) 133 | } 134 | } 135 | } 136 | } 137 | return err 138 | } 139 | return errors.New("pinreg: can't unregister unknown header name " + strconv.Quote(name)) 140 | } 141 | 142 | // realPin returns the real pin from an alias. 143 | func realPin(p pin.Pin) pin.Pin { 144 | if r, ok := p.(gpio.RealPin); ok { 145 | p = r.Real() 146 | } 147 | return p 148 | } 149 | -------------------------------------------------------------------------------- /pin/pinreg/pinreg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pinreg 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/gpio" 11 | "periph.io/x/conn/v3/gpio/gpioreg" 12 | "periph.io/x/conn/v3/gpio/gpiotest" 13 | "periph.io/x/conn/v3/pin" 14 | ) 15 | 16 | func TestAll(t *testing.T) { 17 | defer reset(t) 18 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_PIN2", Num: 2, Fn: "I2C1_SDA"} 19 | gpio3 := &gpiotest.Pin{N: "IMPROBABLE_PIN3", Num: 3, Fn: "I2C1_SCL"} 20 | p := [][]pin.Pin{ 21 | {pin.GROUND, pin.V3_3}, 22 | {gpio2, gpio3}, 23 | } 24 | if err := Register("IMPROBABLE_HEADER", p); err != nil { 25 | t.Fatal(err) 26 | } 27 | if len(allHeaders) != len(All()) { 28 | t.Fatal("unexpected register") 29 | } 30 | if err := Unregister("IMPROBABLE_HEADER"); err != nil { 31 | t.Fatal(err) 32 | } 33 | } 34 | 35 | func TestRegister_twice(t *testing.T) { 36 | defer reset(t) 37 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_PIN2", Num: 2, Fn: "I2C1_SDA"} 38 | if err := Register("IMPROBABLE_HEADER", [][]pin.Pin{{gpio2}}); err != nil { 39 | t.Fatal(err) 40 | } 41 | if err := Register("IMPROBABLE_HEADER", [][]pin.Pin{{gpio2}}); err == nil { 42 | t.Fatal("can't register twice") 43 | } 44 | if err := Unregister("IMPROBABLE_HEADER"); err != nil { 45 | t.Fatal(err) 46 | } 47 | } 48 | 49 | func TestRegister_nil(t *testing.T) { 50 | defer reset(t) 51 | if err := Register("IMPROBABLE_HEADER", [][]pin.Pin{{nil}}); err == nil { 52 | t.Fatal("can't register nil pin") 53 | } 54 | } 55 | 56 | func TestRegister_bad_pin(t *testing.T) { 57 | defer reset(t) 58 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_HEADER_1", Num: 2, Fn: "I2C1_SDA"} 59 | if err := gpioreg.Register(gpio2); err != nil { 60 | t.Fatal(err) 61 | } 62 | if Register("IMPROBABLE_HEADER", [][]pin.Pin{{gpio2}}) == nil { 63 | t.Fatal("should have failed due to alias conflict") 64 | } 65 | } 66 | 67 | func TestIsConnected(t *testing.T) { 68 | defer reset(t) 69 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_PIN2", Num: 2, Fn: "I2C1_SDA"} 70 | gpio3 := &gpiotest.Pin{N: "IMPROBABLE_PIN3", Num: 3, Fn: "I2C1_SCL"} 71 | alias := &pinAlias{Pin: &gpiotest.Pin{N: "ALIAS", Num: 4}, alias: gpio2} 72 | p := [][]pin.Pin{ 73 | {pin.GROUND, pin.V3_3}, 74 | {gpio2, gpio3}, 75 | {alias, alias}, 76 | } 77 | if err := Register("IMPROBABLE_HEADER", p); err != nil { 78 | t.Fatal(err) 79 | } 80 | if !IsConnected(pin.V3_3) { 81 | t.Fatal("V3_3 should be connected") 82 | } 83 | if IsConnected(pin.V5) { 84 | t.Fatal("V5 should not be connected") 85 | } 86 | if !IsConnected(gpio2) { 87 | t.Fatal("IMPROBABLE_PIN2 should be connected") 88 | } 89 | if err := Unregister("IMPROBABLE_HEADER"); err != nil { 90 | t.Fatal(err) 91 | } 92 | } 93 | 94 | func TestUnregister(t *testing.T) { 95 | defer reset(t) 96 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_PIN2", Num: 2, Fn: "I2C1_SDA"} 97 | if err := Register("IMPROBABLE_HEADER", [][]pin.Pin{{gpio2}}); err != nil { 98 | t.Fatal(err) 99 | } 100 | if err := Unregister("IMPROBABLE_HEADER"); err != nil { 101 | t.Fatal(err) 102 | } 103 | } 104 | 105 | func TestUnregister_unknown(t *testing.T) { 106 | defer reset(t) 107 | if Unregister("IMPROBABLE_HEADER") == nil { 108 | t.Fatal("can't unregister unregistered header") 109 | } 110 | } 111 | 112 | func TestUnregister_missing_alias(t *testing.T) { 113 | defer reset(t) 114 | gpio2 := &gpiotest.Pin{N: "IMPROBABLE_PIN2", Num: 2, Fn: "I2C1_SDA"} 115 | if err := gpioreg.Register(gpio2); err != nil { 116 | t.Fatal(err) 117 | } 118 | if err := Register("IMPROBABLE_HEADER", [][]pin.Pin{{gpio2}}); err != nil { 119 | t.Fatal(err) 120 | } 121 | if err := gpioreg.Unregister("IMPROBABLE_HEADER_1"); err != nil { 122 | t.Fatal(err) 123 | } 124 | if Unregister("IMPROBABLE_HEADER") == nil { 125 | t.Fatal("Should fail unregistering aliases") 126 | } 127 | if err := gpioreg.Unregister("IMPROBABLE_PIN2"); err != nil { 128 | t.Fatal(err) 129 | } 130 | } 131 | 132 | // 133 | 134 | func reset(t *testing.T) { 135 | mu.Lock() 136 | defer mu.Unlock() 137 | allHeaders = map[string][][]pin.Pin{} 138 | byPin = map[string]position{} 139 | // Take no chance, they could still be there, but make sure to fail the test 140 | // in this case. 141 | names := []string{"IMPROBABLE_HEADER_1", "IMPROBABLE_HEADER_2", "IMPROBABLE_HEADER_3", "IMPROBABLE_HEADER_4", "IMPROBABLE_PIN2", "IMPROBABLE_PIN3"} 142 | for _, n := range names { 143 | if gpioreg.Unregister(n) == nil { 144 | t.Fatalf("%q wasn't cleaned up correctly", n) 145 | } 146 | } 147 | } 148 | 149 | func init() { 150 | reset(nil) 151 | } 152 | 153 | type pinAlias struct { 154 | *gpiotest.Pin 155 | alias *gpiotest.Pin 156 | } 157 | 158 | func (p *pinAlias) Real() gpio.PinIO { 159 | return p.alias 160 | } 161 | -------------------------------------------------------------------------------- /spi/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package spi_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/physic" 13 | "periph.io/x/conn/v3/spi" 14 | "periph.io/x/conn/v3/spi/spireg" 15 | ) 16 | 17 | func Example() { 18 | // Make sure periph is initialized. 19 | // TODO: Use host.Init(). It is not used in this example to prevent circular 20 | // go package import. 21 | if _, err := driverreg.Init(); err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // Use spireg SPI port registry to find the first available SPI bus. 26 | p, err := spireg.Open("") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer p.Close() 31 | 32 | // Convert the spi.Port into a spi.Conn so it can be used for communication. 33 | c, err := p.Connect(physic.MegaHertz, spi.Mode3, 8) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | // Write 0x10 to the device, and read a byte right after. 39 | write := []byte{0x10, 0x00} 40 | read := make([]byte, len(write)) 41 | if err := c.Tx(write, read); err != nil { 42 | log.Fatal(err) 43 | } 44 | // Use read. 45 | fmt.Printf("%v\n", read[1:]) 46 | } 47 | 48 | func ExamplePins() { 49 | // Make sure periph is initialized. 50 | // TODO: Use host.Init(). It is not used in this example to prevent circular 51 | // go package import. 52 | if _, err := driverreg.Init(); err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | // Use spireg SPI port registry to find the first available SPI bus. 57 | p, err := spireg.Open("") 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | defer p.Close() 62 | 63 | // Convert the spi.Port into a spi.Conn so it can be used for communication. 64 | c, err := p.Connect(physic.MegaHertz, spi.Mode3, 8) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | // Prints out the gpio pin used. 70 | if p, ok := c.(spi.Pins); ok { 71 | fmt.Printf(" CLK : %s", p.CLK()) 72 | fmt.Printf(" MOSI: %s", p.MOSI()) 73 | fmt.Printf(" MISO: %s", p.MISO()) 74 | fmt.Printf(" CS : %s", p.CS()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /spi/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package spi 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | CLK pin.Func = "SPI_CLK" // Clock 12 | CS pin.Func = "SPI_CS" // Chip select 13 | MISO pin.Func = "SPI_MISO" // Master in 14 | MOSI pin.Func = "SPI_MOSI" // Master out 15 | ) 16 | -------------------------------------------------------------------------------- /spi/spi_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package spi 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestMode_String(t *testing.T) { 12 | if s := Mode(^int(0)).String(); s != "Mode3|HalfDuplex|NoCS|LSBFirst|0xffffffffffffffe0" { 13 | t.Fatal(s) 14 | } 15 | if s := Mode0.String(); s != "Mode0" { 16 | t.Fatal(s) 17 | } 18 | if s := Mode1.String(); s != "Mode1" { 19 | t.Fatal(s) 20 | } 21 | if s := Mode2.String(); s != "Mode2" { 22 | t.Fatal(s) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spi/spireg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package spireg_test 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "log" 11 | "strings" 12 | 13 | "periph.io/x/conn/v3/driver/driverreg" 14 | "periph.io/x/conn/v3/physic" 15 | "periph.io/x/conn/v3/spi" 16 | "periph.io/x/conn/v3/spi/spireg" 17 | ) 18 | 19 | func Example() { 20 | // Make sure periph is initialized. 21 | // TODO: Use host.Init(). It is not used in this example to prevent circular 22 | // go package import. 23 | if _, err := driverreg.Init(); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | // A command line tool may let the user choose a SPI port, yet default to the 28 | // first port known. 29 | name := flag.String("spi", "", "SPI port to use") 30 | flag.Parse() 31 | p, err := spireg.Open(*name) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer p.Close() 36 | 37 | // Convert the spi.Port into a spi.Conn so it can be used for communication. 38 | c, err := p.Connect(physic.MegaHertz, spi.Mode3, 8) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Write 0x10 to the device, and read a byte right after. 44 | write := []byte{0x10, 0x00} 45 | read := make([]byte, len(write)) 46 | if err := c.Tx(write, read); err != nil { 47 | log.Fatal(err) 48 | } 49 | // Use read. 50 | fmt.Printf("%v\n", read[1:]) 51 | } 52 | 53 | func ExampleAll() { 54 | // Make sure periph is initialized. 55 | // TODO: Use host.Init(). It is not used in this example to prevent circular 56 | // go package import. 57 | if _, err := driverreg.Init(); err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | // Enumerate all SPI ports available and the corresponding pins. 62 | fmt.Print("SPI ports available:\n") 63 | for _, ref := range spireg.All() { 64 | fmt.Printf("- %s\n", ref.Name) 65 | if ref.Number != -1 { 66 | fmt.Printf(" %d\n", ref.Number) 67 | } 68 | if len(ref.Aliases) != 0 { 69 | fmt.Printf(" %s\n", strings.Join(ref.Aliases, " ")) 70 | } 71 | 72 | p, err := ref.Open() 73 | if err != nil { 74 | fmt.Printf(" Failed to open: %v", err) 75 | } 76 | if p, ok := p.(spi.Pins); ok { 77 | fmt.Printf(" CLK : %s", p.CLK()) 78 | fmt.Printf(" MOSI: %s", p.MOSI()) 79 | fmt.Printf(" MISO: %s", p.MISO()) 80 | fmt.Printf(" CS : %s", p.CS()) 81 | } 82 | if err := p.Close(); err != nil { 83 | fmt.Printf(" Failed to close: %v", err) 84 | } 85 | } 86 | } 87 | 88 | func ExampleOpen() { 89 | // Make sure periph is initialized. 90 | // TODO: Use host.Init(). It is not used in this example to prevent circular 91 | // go package import. 92 | if _, err := driverreg.Init(); err != nil { 93 | log.Fatal(err) 94 | } 95 | 96 | // On Linux, the following calls will likely open the same port. 97 | _, _ = spireg.Open("/dev/spidev1.0") 98 | _, _ = spireg.Open("SPI1.0") 99 | _, _ = spireg.Open("1") 100 | 101 | // Opens the first default SPI bus found: 102 | _, _ = spireg.Open("") 103 | 104 | // Wondering what to do with the opened spi.PortCloser? Look at the package's 105 | // example above. 106 | } 107 | 108 | // 109 | -------------------------------------------------------------------------------- /spi/spireg/spireg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package spireg 6 | 7 | import ( 8 | "errors" 9 | "testing" 10 | 11 | "periph.io/x/conn/v3" 12 | "periph.io/x/conn/v3/physic" 13 | "periph.io/x/conn/v3/spi" 14 | ) 15 | 16 | func TestOpen(t *testing.T) { 17 | defer reset() 18 | if _, err := Open(""); err == nil { 19 | t.Fatal("no port registered") 20 | } 21 | if err := Register("a", []string{"x"}, 1, getFakePort); err != nil { 22 | t.Fatal(err) 23 | } 24 | if o, err := Open(""); o == nil || err != nil { 25 | t.Fatal(o, err) 26 | } 27 | if o, err := Open("1"); o == nil || err != nil { 28 | t.Fatal(o, err) 29 | } 30 | if o, err := Open("x"); o == nil || err != nil { 31 | t.Fatal(o, err) 32 | } 33 | if o, err := Open("y"); o != nil || err == nil { 34 | t.Fatal(o, err) 35 | } 36 | } 37 | 38 | func TestDefault_NoNumber(t *testing.T) { 39 | defer reset() 40 | if err := Register("a", nil, -1, getFakePort); err != nil { 41 | t.Fatal(err) 42 | } 43 | if o, err := Open(""); o == nil || err != nil { 44 | t.Fatal(o, err) 45 | } 46 | } 47 | 48 | func TestAll(t *testing.T) { 49 | defer reset() 50 | if a := All(); len(a) != 0 { 51 | t.Fatal(a) 52 | } 53 | if err := Register("a", nil, 1, getFakePort); err != nil { 54 | t.Fatal(err) 55 | } 56 | if err := Register("b", nil, 2, getFakePort); err != nil { 57 | t.Fatal(err) 58 | } 59 | if a := All(); len(a) != 2 { 60 | t.Fatal(a) 61 | } 62 | } 63 | 64 | func TestRef(t *testing.T) { 65 | out := insertRef(nil, &Ref{Name: "b"}) 66 | out = insertRef(out, &Ref{Name: "d"}) 67 | out = insertRef(out, &Ref{Name: "c"}) 68 | out = insertRef(out, &Ref{Name: "a"}) 69 | for i, l := range []string{"a", "b", "c", "d"} { 70 | if out[i].Name != l { 71 | t.Fatal(out) 72 | } 73 | } 74 | } 75 | 76 | func TestRegister(t *testing.T) { 77 | defer reset() 78 | if err := Register("a", []string{"b"}, 42, getFakePort); err != nil { 79 | t.Fatal(err) 80 | } 81 | if Register("a", nil, -1, getFakePort) == nil { 82 | t.Fatal("same port name") 83 | } 84 | if Register("b", nil, -1, getFakePort) == nil { 85 | t.Fatal("same port alias name") 86 | } 87 | if Register("c", nil, 42, getFakePort) == nil { 88 | t.Fatal("same port number") 89 | } 90 | if Register("c", []string{"a"}, -1, getFakePort) == nil { 91 | t.Fatal("same port alias") 92 | } 93 | if Register("c", []string{"b"}, -1, getFakePort) == nil { 94 | t.Fatal("same port alias") 95 | } 96 | } 97 | 98 | func TestRegister_fail(t *testing.T) { 99 | defer reset() 100 | if Register("a", nil, -1, nil) == nil { 101 | t.Fatal("missing Opener") 102 | } 103 | if Register("a", nil, -2, getFakePort) == nil { 104 | t.Fatal("bad port number") 105 | } 106 | if Register("", nil, 42, getFakePort) == nil { 107 | t.Fatal("missing name") 108 | } 109 | if Register("1", nil, 42, getFakePort) == nil { 110 | t.Fatal("numeric name") 111 | } 112 | if Register("a:b", nil, 42, getFakePort) == nil { 113 | t.Fatal("':' in name") 114 | } 115 | if Register("a", []string{"a"}, 0, getFakePort) == nil { 116 | t.Fatal("\"a\" is already registered") 117 | } 118 | if Register("a", []string{""}, 0, getFakePort) == nil { 119 | t.Fatal("empty alias") 120 | } 121 | if Register("a", []string{"1"}, 0, getFakePort) == nil { 122 | t.Fatal("numeric alias") 123 | } 124 | if Register("a", []string{"a:b"}, 0, getFakePort) == nil { 125 | t.Fatal("':' in alias") 126 | } 127 | if a := All(); len(a) != 0 { 128 | t.Fatal(a) 129 | } 130 | } 131 | 132 | func TestUnregister(t *testing.T) { 133 | defer reset() 134 | if Unregister("") == nil { 135 | t.Fatal("unregister empty") 136 | } 137 | if Unregister("a") == nil { 138 | t.Fatal("unregister non-existing") 139 | } 140 | if err := Register("a", []string{"b"}, 0, getFakePort); err != nil { 141 | t.Fatal(err) 142 | } 143 | if err := Unregister("a"); err != nil { 144 | t.Fatal(err) 145 | } 146 | } 147 | 148 | // 149 | 150 | func getFakePort() (spi.PortCloser, error) { 151 | return &fakePort{}, nil 152 | } 153 | 154 | type fakePort struct { 155 | } 156 | 157 | func (f *fakePort) String() string { 158 | return "fake" 159 | } 160 | 161 | func (f *fakePort) Close() error { 162 | return errors.New("not implemented") 163 | } 164 | 165 | func (f *fakePort) Tx(w, r []byte) error { 166 | return errors.New("not implemented") 167 | } 168 | 169 | func (f *fakePort) Duplex() conn.Duplex { 170 | return conn.DuplexUnknown 171 | } 172 | 173 | func (f *fakePort) LimitSpeed(freq physic.Frequency) error { 174 | return errors.New("not implemented") 175 | } 176 | 177 | func (f *fakePort) Connect(freq physic.Frequency, mode spi.Mode, bits int) (spi.Conn, error) { 178 | return f, errors.New("not implemented") 179 | } 180 | 181 | func (f *fakePort) TxPackets(p []spi.Packet) error { 182 | return errors.New("not implemented") 183 | } 184 | 185 | func reset() { 186 | mu.Lock() 187 | defer mu.Unlock() 188 | byName = map[string]*Ref{} 189 | byNumber = map[int]*Ref{} 190 | byAlias = map[string]*Ref{} 191 | } 192 | -------------------------------------------------------------------------------- /uart/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package uart_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/uart" 13 | "periph.io/x/conn/v3/uart/uartreg" 14 | ) 15 | 16 | func ExamplePins() { 17 | // Make sure periph is initialized. 18 | // TODO: Use host.Init(). It is not used in this example to prevent circular 19 | // go package import. 20 | if _, err := driverreg.Init(); err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // Use uartreg UART port registry to find the first available UART port. 25 | p, err := uartreg.Open("") 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer p.Close() 30 | 31 | // Prints out the gpio pin used. 32 | if p, ok := p.(uart.Pins); ok { 33 | fmt.Printf(" RX : %s", p.RX()) 34 | fmt.Printf(" TX : %s", p.TX()) 35 | fmt.Printf(" RTS: %s", p.RTS()) 36 | fmt.Printf(" CTS: %s", p.CTS()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /uart/func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package uart 6 | 7 | import "periph.io/x/conn/v3/pin" 8 | 9 | // Well known pin functionality. 10 | const ( 11 | RX pin.Func = "UART_RX" // Receive 12 | TX pin.Func = "UART_TX" // Transmit 13 | RTS pin.Func = "UART_RTS" // Request to send 14 | CTS pin.Func = "UART_CTS" // Clear to send 15 | 16 | // These are rarely used. 17 | DTR pin.Func = "UART_DTR" // Data terminal ready 18 | DSR pin.Func = "UART_DSR" // Data set ready 19 | DCD pin.Func = "UART_DCD" // Data carrier detect 20 | RI pin.Func = "UART_RI" // Ring indicator 21 | ) 22 | -------------------------------------------------------------------------------- /uart/uart.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package uart defines the UART protocol. 6 | // 7 | // As described in https://periph.io/x/conn/v3#hdr-Concepts, periph.io uses 8 | // the concepts of Bus, Port and Conn. 9 | // 10 | // In the package uart, 'Bus' is not exposed, as the protocol is primarily 11 | // point-to-point. 12 | // 13 | // Use Port.Connect() converts the uninitialized Port into a Conn. 14 | // 15 | // TODO(maruel): The Port -> Conn dance is unusual for UART users and feels 16 | // unnatural. 17 | // 18 | // TODO(maruel): UART users talks in term of bauds, not hertz. 19 | // 20 | // TODO(maruel): The LimitSpeed() function feels weird, as generally it's not 21 | // the device driver that gets to decide the speed (?) Well it 'depends'. 22 | // 23 | // There's a great implementation at https://github.com/albenik/go-serial but 24 | // it uses cgo a lot. Maybe making an adaptor and moving this into extra is the 25 | // best choice here? 26 | // 27 | // See https://en.wikipedia.org/wiki/UART for more information. 28 | package uart 29 | 30 | import ( 31 | "fmt" 32 | "io" 33 | 34 | "periph.io/x/conn/v3" 35 | "periph.io/x/conn/v3/gpio" 36 | "periph.io/x/conn/v3/physic" 37 | ) 38 | 39 | // Flow determines the data flow to use, if any. 40 | type Flow uint32 41 | 42 | const ( 43 | // NoFlow specifies that no flow control is used. 44 | NoFlow Flow = 0x10000 45 | // XOnXOff specifies XOn/XOff flow control, also called Software flow control. 46 | // 47 | // See https://en.wikipedia.org/wiki/Software_flow_control for more 48 | // information. 49 | XOnXOff Flow = 0x20000 50 | // RTSCTS specifies RTS/CTS flow contro. This uses RTS and CTS lines for flow 51 | // control, also called Hardware flow control. This enables more reliable 52 | // communication. The lines are driven Low when they are ready to receive 53 | // more data. 54 | RTSCTS Flow = 0x40000 55 | 56 | mask Flow = 0xFFFF0000 57 | ) 58 | 59 | // MakeXOnXOffFlow returns an initialized Flow to enable software based flow 60 | // control. 61 | func MakeXOnXOffFlow(xon, xoff byte) Flow { 62 | return XOnXOff | Flow(xon)<<8 | Flow(xoff) 63 | } 64 | 65 | func (f Flow) String() string { 66 | switch f { 67 | case NoFlow: 68 | return "None" 69 | case RTSCTS: 70 | return "RTS/CTS" 71 | default: 72 | if f&mask == XOnXOff { 73 | return fmt.Sprintf("XOn(%c)/XOff(%c)", byte(f>>8), byte(f)) 74 | } 75 | return fmt.Sprintf("Flow(%x)", uint32(f)) 76 | } 77 | } 78 | 79 | // Parity determines the parity bit when transmitting, if any. 80 | type Parity byte 81 | 82 | const ( 83 | // NoParity means no parity bit. 84 | NoParity Parity = 'N' 85 | // Odd means 1 when sum is odd. 86 | Odd Parity = 'O' 87 | // Even means 1 when sum is even. 88 | Even Parity = 'E' 89 | // Mark means always 1. 90 | Mark Parity = 'M' 91 | // Space means always 0. 92 | Space Parity = 'S' 93 | ) 94 | 95 | // Stop determines what stop bit to use. 96 | type Stop int8 97 | 98 | const ( 99 | // One is 1 stop bit. 100 | One Stop = 1 101 | // OneHalf is 1.5 stop bits. 102 | OneHalf Stop = 15 103 | // Two is 2 stop bits. 104 | Two Stop = 2 105 | ) 106 | 107 | // Port is the interface to be provided to device drivers. 108 | // 109 | // The device driver, that is the driver for the peripheral connected over 110 | // this port, calls Connect() to retrieve a configured connection as Conn. 111 | type Port interface { 112 | String() string 113 | // Connect sets the communication parameters of the connection for use by a 114 | // device. 115 | // 116 | // The device driver must call this function exactly once. 117 | // 118 | // f must specify the maximum rated speed by the device's spec. For example 119 | // if a device is known to not work at over 115200 bauds, it should specify 120 | // 115200Hz. 121 | // 122 | // The lowest speed between the port speed and the device speed is selected. 123 | // 124 | // There's rarely a reason to use anything else than One stop bit and 8 bits 125 | // per character. 126 | Connect(f physic.Frequency, stopBit Stop, parity Parity, flow Flow, bits int) (conn.Conn, error) 127 | } 128 | 129 | // PortCloser is a UART port that can be closed. 130 | // 131 | // This interface is meant to be handled by the application. 132 | type PortCloser interface { 133 | io.Closer 134 | Port 135 | // LimitSpeed sets the maximum port speed. 136 | // 137 | // It lets an application use a device at a lower speed than the maximum 138 | // speed as rated by the device driver. This is useful for example when the 139 | // wires are long or the connection is of poor quality, and you want to try 140 | // to run at lower speed like 19200 bauds. 141 | // 142 | // This function can be called multiple times and resets the previous value. 143 | // 0 is not a valid value for f. The lowest speed between the port speed and 144 | // the device speed is selected. 145 | LimitSpeed(f physic.Frequency) error 146 | } 147 | 148 | // Pins defines the pins that an UART bus interconnect is using on the host. 149 | // 150 | // It is expected that a implementer of Conn also implement Pins but this is 151 | // not a requirement. 152 | type Pins interface { 153 | // RX returns the receive pin. 154 | RX() gpio.PinIn 155 | // TX returns the transmit pin. 156 | TX() gpio.PinOut 157 | // RTS returns the request to send pin, if present. 158 | RTS() gpio.PinOut 159 | // CTS returns the clear to send pin, if present. 160 | CTS() gpio.PinIn 161 | } 162 | -------------------------------------------------------------------------------- /uart/uartreg/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package uartreg_test 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "log" 11 | "strings" 12 | 13 | "periph.io/x/conn/v3/driver/driverreg" 14 | "periph.io/x/conn/v3/physic" 15 | "periph.io/x/conn/v3/uart" 16 | "periph.io/x/conn/v3/uart/uartreg" 17 | ) 18 | 19 | func Example() { 20 | // Make sure periph is initialized. 21 | // TODO: Use host.Init(). It is not used in this example to prevent circular 22 | // go package import. 23 | if _, err := driverreg.Init(); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | // How a command line tool may let the user choose an UART port, yet default 28 | // to the first bus known. 29 | name := flag.String("uart", "", "UART port to use") 30 | flag.Parse() 31 | p, err := uartreg.Open(*name) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer p.Close() 36 | 37 | c, err := p.Connect(115200*physic.Hertz, uart.One, uart.NoParity, uart.RTSCTS, 8) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | if err := c.Tx([]byte("cmd"), nil); err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | 46 | func ExampleAll() { 47 | // Make sure periph is initialized. 48 | // TODO: Use host.Init(). It is not used in this example to prevent circular 49 | // go package import. 50 | if _, err := driverreg.Init(); err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | // Enumerate all UART ports available and the corresponding pins. 55 | fmt.Print("UART ports available:\n") 56 | for _, ref := range uartreg.All() { 57 | fmt.Printf("- %s\n", ref.Name) 58 | if ref.Number != -1 { 59 | fmt.Printf(" %d\n", ref.Number) 60 | } 61 | if len(ref.Aliases) != 0 { 62 | fmt.Printf(" %s\n", strings.Join(ref.Aliases, " ")) 63 | } 64 | 65 | b, err := ref.Open() 66 | if err != nil { 67 | fmt.Printf(" Failed to open: %v", err) 68 | } 69 | if p, ok := b.(uart.Pins); ok { 70 | fmt.Printf(" RX : %s", p.RX()) 71 | fmt.Printf(" TX : %s", p.TX()) 72 | fmt.Printf(" RTS: %s", p.RTS()) 73 | fmt.Printf(" CTS: %s", p.CTS()) 74 | } 75 | if err := b.Close(); err != nil { 76 | fmt.Printf(" Failed to close: %v", err) 77 | } 78 | } 79 | } 80 | 81 | func ExampleOpen() { 82 | // Make sure periph is initialized. 83 | // TODO: Use host.Init(). It is not used in this example to prevent circular 84 | // go package import. 85 | if _, err := driverreg.Init(); err != nil { 86 | log.Fatal(err) 87 | } 88 | 89 | // On linux, the following calls will likely open the same bus. 90 | _, _ = uartreg.Open("/dev/ttyUSB0") 91 | _, _ = uartreg.Open("UART0") 92 | _, _ = uartreg.Open("0") 93 | } 94 | -------------------------------------------------------------------------------- /uart/uartreg/uartreg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package uartreg 6 | 7 | import ( 8 | "errors" 9 | "sort" 10 | "testing" 11 | 12 | "periph.io/x/conn/v3" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/physic" 15 | "periph.io/x/conn/v3/uart" 16 | ) 17 | 18 | func TestOpen(t *testing.T) { 19 | defer reset() 20 | if _, err := Open(""); err == nil { 21 | t.Fatal("no bus registered") 22 | } 23 | if err := Register("a", []string{"x"}, 1, fakePorter); err != nil { 24 | t.Fatal(err) 25 | } 26 | if o, err := Open(""); o == nil || err != nil { 27 | t.Fatal(o, err) 28 | } 29 | if o, err := Open("1"); o == nil || err != nil { 30 | t.Fatal(o, err) 31 | } 32 | if o, err := Open("x"); o == nil || err != nil { 33 | t.Fatal(o, err) 34 | } 35 | if o, err := Open("y"); o != nil || err == nil { 36 | t.Fatal(o, err) 37 | } 38 | } 39 | 40 | func TestDefault_NoNumber(t *testing.T) { 41 | defer reset() 42 | if err := Register("a", nil, -1, fakePorter); err != nil { 43 | t.Fatal(err) 44 | } 45 | if o, err := Open(""); o == nil || err != nil { 46 | t.Fatal(o, err) 47 | } 48 | } 49 | 50 | func TestAll(t *testing.T) { 51 | defer reset() 52 | if a := All(); len(a) != 0 { 53 | t.Fatal(a) 54 | } 55 | if err := Register("a", nil, 1, fakePorter); err != nil { 56 | t.Fatal(err) 57 | } 58 | if err := Register("b", nil, 2, fakePorter); err != nil { 59 | t.Fatal(err) 60 | } 61 | if a := All(); len(a) != 2 { 62 | t.Fatal(a) 63 | } 64 | } 65 | 66 | func TestRefList(t *testing.T) { 67 | l := refList{&Ref{Name: "b"}, &Ref{Name: "a"}} 68 | sort.Sort(l) 69 | if l[0].Name != "a" || l[1].Name != "b" { 70 | t.Fatal(l) 71 | } 72 | } 73 | 74 | func TestRegister(t *testing.T) { 75 | defer reset() 76 | if err := Register("a", []string{"b"}, 42, fakePorter); err != nil { 77 | t.Fatal(err) 78 | } 79 | if Register("a", nil, -1, fakePorter) == nil { 80 | t.Fatal("same bus name") 81 | } 82 | if Register("b", nil, -1, fakePorter) == nil { 83 | t.Fatal("same bus alias name") 84 | } 85 | if Register("c", nil, 42, fakePorter) == nil { 86 | t.Fatal("same bus number") 87 | } 88 | if Register("c", []string{"a"}, -1, fakePorter) == nil { 89 | t.Fatal("same bus alias") 90 | } 91 | if Register("c", []string{"b"}, -1, fakePorter) == nil { 92 | t.Fatal("same bus alias") 93 | } 94 | } 95 | 96 | func TestRegister_fail(t *testing.T) { 97 | defer reset() 98 | if Register("a", nil, -1, nil) == nil { 99 | t.Fatal("missing Opener") 100 | } 101 | if Register("a", nil, -2, fakePorter) == nil { 102 | t.Fatal("bad bus number") 103 | } 104 | if Register("", nil, 42, fakePorter) == nil { 105 | t.Fatal("missing name") 106 | } 107 | if Register("1", nil, 42, fakePorter) == nil { 108 | t.Fatal("numeric name") 109 | } 110 | if Register("a:b", nil, 42, fakePorter) == nil { 111 | t.Fatal("':' in name") 112 | } 113 | if Register("a", []string{"a"}, 0, fakePorter) == nil { 114 | t.Fatal("\"a\" is already registered") 115 | } 116 | if Register("a", []string{""}, 0, fakePorter) == nil { 117 | t.Fatal("empty alias") 118 | } 119 | if Register("a", []string{"1"}, 0, fakePorter) == nil { 120 | t.Fatal("numeric alias") 121 | } 122 | if Register("a", []string{"a:b"}, 0, fakePorter) == nil { 123 | t.Fatal("':' in alias") 124 | } 125 | if a := All(); len(a) != 0 { 126 | t.Fatal(a) 127 | } 128 | } 129 | 130 | func TestUnregister(t *testing.T) { 131 | defer reset() 132 | if Unregister("") == nil { 133 | t.Fatal("unregister empty") 134 | } 135 | if Unregister("a") == nil { 136 | t.Fatal("unregister non-existing") 137 | } 138 | if err := Register("a", []string{"b"}, 0, fakePorter); err != nil { 139 | t.Fatal(err) 140 | } 141 | if err := Unregister("a"); err != nil { 142 | t.Fatal(err) 143 | } 144 | } 145 | 146 | // 147 | 148 | func fakePorter() (uart.PortCloser, error) { 149 | return &fakePort{}, nil 150 | } 151 | 152 | // fakePort implements uart.PortCloser. 153 | type fakePort struct { 154 | conn fakeConn 155 | } 156 | 157 | func (f *fakePort) String() string { 158 | return "fake" 159 | } 160 | 161 | func (f *fakePort) Close() error { 162 | return errors.New("not implemented") 163 | } 164 | 165 | func (f *fakePort) LimitSpeed(freq physic.Frequency) error { 166 | return errors.New("not implemented") 167 | } 168 | 169 | func (f *fakePort) Connect(freq physic.Frequency, stopBit uart.Stop, parity uart.Parity, flow uart.Flow, bits int) (conn.Conn, error) { 170 | return &f.conn, nil 171 | } 172 | 173 | func (f *fakePort) RX() gpio.PinIn { return f.conn.RX() } 174 | func (f *fakePort) TX() gpio.PinOut { return f.conn.TX() } 175 | func (f *fakePort) RTS() gpio.PinOut { return f.conn.RTS() } 176 | func (f *fakePort) CTS() gpio.PinIn { return f.conn.CTS() } 177 | 178 | // fakeConn implements conn.Conn. 179 | type fakeConn struct { 180 | } 181 | 182 | func (f *fakeConn) String() string { 183 | return "fake" 184 | } 185 | 186 | func (f *fakeConn) Tx(w, r []byte) error { 187 | return errors.New("not implemented") 188 | } 189 | 190 | func (f *fakeConn) Duplex() conn.Duplex { 191 | return conn.Full 192 | } 193 | 194 | func (f *fakeConn) RX() gpio.PinIn { return gpio.INVALID } 195 | func (f *fakeConn) TX() gpio.PinOut { return gpio.INVALID } 196 | func (f *fakeConn) RTS() gpio.PinOut { return gpio.INVALID } 197 | func (f *fakeConn) CTS() gpio.PinIn { return gpio.INVALID } 198 | 199 | func reset() { 200 | mu.Lock() 201 | defer mu.Unlock() 202 | byName = map[string]*Ref{} 203 | byNumber = map[int]*Ref{} 204 | byAlias = map[string]*Ref{} 205 | } 206 | 207 | // 208 | 209 | var _ uart.PortCloser = &fakePort{} 210 | var _ uart.Pins = &fakePort{} 211 | var _ conn.Conn = &fakeConn{} 212 | var _ uart.Pins = &fakeConn{} 213 | --------------------------------------------------------------------------------