forked from google/periph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
onewire.go
195 lines (169 loc) · 6.65 KB
/
onewire.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package onewire defines a Dallas Semiconductor / Maxim Integrated 1-wire bus.
//
// It includes an adapter to directly address a 1-wire device on a 1-wire bus
// without having to continuously specify the address when doing I/O. This
// enables the support of conn.Conn.
//
// References
//
// Overview: https://www.maximintegrated.com/en/app-notes/index.mvp/id/1796
//
// App notes: https://www.maximintegrated.com/en/design/techdocs/app-notes/index.mvp/id/1/c/1-Wire%26reg%3B%20Devices
package onewire
import (
"encoding/binary"
"fmt"
"io"
"periph.io/x/periph/conn"
"periph.io/x/periph/conn/gpio"
)
// Bus defines the function a concrete driver for a 1-wire bus must implement.
//
// This interface doesn't implement conn.Conn since a device address must be
// specified for each transaction. Use onewire.Dev as an adapter to get a
// conn.Conn compatible object.
type Bus interface {
// Tx performs a bus transaction, sending and receiving bytes, and
// ending by pulling the bus high either weakly or strongly depending
// on the value of power. A strong pull-up is typically required to
// power temperature conversion or EEPROM writes.
Tx(w, r []byte, power Pullup) error
// Search performs a "search" cycle on the 1-wire bus and returns the
// addresses of all devices on the bus if alarmOnly is false and of all
// devices in alarm state if alarmOnly is true.
//
// If an error occurs during the search the already-discovered devices are
// returned with the error.
//
// Bus.Search may be implemented using onewire.Search if the bus implements
// the BusSearcher interface or it may have a custom implementation, for
// example a Linux sysfs implementation should return the list of devices
// already discovered by the driver.
Search(alarmOnly bool) ([]Address, error)
}
// Address represents a 1-wire device address in little-endian format. This means
// that the family code ends up in the lower byte, the CRC in the top byte,
// and the variable address part in the middle 6 bytes. E.g. a DS18B20 device,
// which has a family code of 0x28, might have address 0x7a00000131825228.
type Address uint64
// Pullup encodes the type of pull-up used at the end of a bus transaction.
type Pullup bool
const (
// WeakPullup ends the transaction with weak pull-up
WeakPullup Pullup = false
// StrongPullup end the transaction with strong pull-up to power devices
StrongPullup Pullup = true
)
func (p Pullup) String() string {
if p {
return "Strong"
}
return "Weak"
}
// BusCloser is a 1-wire bus that can be closed.
//
// It is expected that an implementer of Bus also implement BusCloser, but
// this is not required.
type BusCloser interface {
io.Closer
Bus
}
// Pins defines the pins that a 1-wire bus interconnect is using on the host.
//
// It is expected that an implementer of Bus also implement Pins but this is
// not a requirement.
type Pins interface {
// Q returns the 1-wire Q (data) pin.
Q() gpio.PinIO
}
// NoDevicesError is an interface that should be implemented by errors that
// indicate that no devices responded with a presence pulse after a reset.
type NoDevicesError interface {
NoDevices() bool // true if no presence pulse from any device has been detected
}
// noDevicesError implements error and NoDevicesError.
type noDevicesError string
func (e noDevicesError) Error() string { return string(e) }
func (e noDevicesError) NoDevices() bool { return true }
// ShortedBusError is an interface that should be implemented by errors that
// indicate that the bus is electrically shorted (Q connected to GND).
//
// Errors that implement ShortedBusError should also implement BusError.
type ShortedBusError interface {
IsShorted() bool // true if the bus is electrically shorted
}
// shortedBusError implements error and ShortedBusError.
type shortedBusError string
func (e shortedBusError) Error() string { return string(e) }
func (e shortedBusError) IsShorted() bool { return true }
func (e shortedBusError) BusError() bool { return true }
// BusError is an interface that should be implemented by errors that
// indicate that an error occurred on the bus, for example a CRC error
// or a non-responding device. These errors often indicate an electrical
// problem with the bus and may be worth retrying.
//
// BusError also helps to differentiate 1-wire errors from errors accessing
// the 1-wire bus interface chip or circuitry, which may be located on
// an I²C bus or gpio pin.
type BusError interface {
BusError() bool // true if a bus error was detected
}
// busError implements error and BusError.
type busError string
func (e busError) Error() string { return string(e) }
func (e busError) BusError() bool { return true }
// Dev is a device on a 1-wire bus.
//
// It implements conn.Conn.
//
// Compared to Bus it saves from repeatedly specifying the device address and
// implements utility functions.
type Dev struct {
Bus Bus // the bus to which the device is connected
Addr Address // address of the device on the bus
}
// String prints the bus name followed by the device address in parenthesis.
func (d *Dev) String() string {
return fmt.Sprintf("%s(%#016x)", d.Bus, d.Addr)
}
// Tx performs a "match ROM" command on the bus to select the device
// and then transmits and receives the specified bytes. It ends by
// leaving a weak pull-up on the bus.
//
// It's a wrapper for Dev.Bus.Tx().
func (d *Dev) Tx(w, r []byte) error {
// Issue ROM match command to select the device followed by the
// bytes being written.
ww := make([]byte, 9, len(w)+9)
ww[0] = 0x55 // Match ROM
binary.LittleEndian.PutUint64(ww[1:], uint64(d.Addr))
ww = append(ww, w...)
return d.Bus.Tx(ww, r, WeakPullup)
}
// Duplex always return conn.Half for 1-wire.
func (d *Dev) Duplex() conn.Duplex {
return conn.Half
}
// TxPower performs a "match ROM" command on the bus to select the device
// and then transmits and receives the specified bytes. It ends by
// leaving a strong pull-up on the bus suitable to power devices through
// an EEPROM write or a temperature conversion.
//
// It's a wrapper for Dev.Bus.Tx().
func (d *Dev) TxPower(w, r []byte) error {
// Issue ROM match command to select the device followed by the
// bytes being written.
ww := make([]byte, 9, len(w)+9)
ww[0] = 0x55 // Match ROM
binary.LittleEndian.PutUint64(ww[1:], uint64(d.Addr))
ww = append(ww, w...)
return d.Bus.Tx(ww, r, StrongPullup)
}
// Ensure that the appropriate interfaces are implemented.
var _ conn.Conn = &Dev{}
var _ NoDevicesError = noDevicesError("")
var _ ShortedBusError = shortedBusError("")
var _ BusError = busError("")