forked from TheCacophonyProject/periph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ds248x.go
172 lines (153 loc) · 5.61 KB
/
ds248x.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
// 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 ds248x controls a Maxim DS2483 or DS2482-100 1-wire interface chip over I²C.
//
// Datasheets
//
// https://www.maximintegrated.com/en/products/digital/one-wire/DS2483.html
//
// https://www.maximintegrated.com/en/products/interface/controllers-expanders/DS2482-100.html
package ds248x
import (
"errors"
"fmt"
"time"
"periph.io/x/periph/conn/i2c"
)
// PupOhm controls the strength of the passive pull-up resistor
// on the 1-wire data line. The default value is 1000Ω.
type PupOhm uint8
const (
// R500Ω passive pull-up resistor.
R500Ω = 4
// R1000Ω passive pull-up resistor.
R1000Ω = 6
)
// Opts contains options to pass to the constructor.
type Opts struct {
Addr uint16 // I²C address, default 0x18
PassivePullup bool // false:use active pull-up, true: disable active pullup
// The following options are only available on the ds2483 (not ds2482-100).
// The actual value used is the closest possible value (rounded up or down).
ResetLow time.Duration // reset low time, range 440μs..740μs
PresenceDetect time.Duration // presence detect sample time, range 58μs..76μs
Write0Low time.Duration // write zero low time, range 52μs..70μs
Write0Recovery time.Duration // write zero recovery time, range 2750ns..25250ns
PullupRes PupOhm // passive pull-up resistance, true: 500Ω, false: 1kΩ
}
// New returns a device object that communicates over I²C to the DS2482/DS2483
// controller.
//
// This device object implements onewire.Bus and can be used to
// access devices on the bus.
func New(i i2c.Bus, opts *Opts) (*Dev, error) {
addr := uint16(0x18)
if opts != nil {
switch opts.Addr {
case 0x18, 0x19, 0x20, 0x21:
addr = opts.Addr
case 0x00:
default:
return nil, errors.New("ds248x: given address not supported by device")
}
}
d := &Dev{i2c: &i2c.Dev{Bus: i, Addr: addr}}
if err := d.makeDev(opts); err != nil {
return nil, err
}
return d, nil
}
//
// defaults holds default values for optional parameters.
var defaults = Opts{
PassivePullup: false,
ResetLow: 560 * time.Microsecond,
PresenceDetect: 68 * time.Microsecond,
Write0Low: 64 * time.Microsecond,
Write0Recovery: 5250 * time.Nanosecond,
PullupRes: R1000Ω,
}
func (d *Dev) makeDev(opts *Opts) error {
// Doctor the opts to apply default values.
if opts == nil {
opts = &defaults
}
if opts.ResetLow == 0 {
opts.ResetLow = defaults.ResetLow
}
if opts.PresenceDetect == 0 {
opts.PresenceDetect = defaults.PresenceDetect
}
if opts.Write0Low == 0 {
opts.Write0Low = defaults.Write0Low
}
if opts.Write0Recovery == 0 {
opts.Write0Recovery = defaults.Write0Recovery
}
if opts.PullupRes == 0 {
opts.PullupRes = defaults.PullupRes
}
d.tReset = 2 * opts.ResetLow
d.tSlot = opts.Write0Low + opts.Write0Recovery
// Issue a reset command.
if err := d.i2c.Tx([]byte{cmdReset}, nil); err != nil {
return fmt.Errorf("ds248x: error while resetting: %s", err)
}
// Read the status register to confirm that we have a responding ds248x
var stat [1]byte
if err := d.i2c.Tx([]byte{cmdSetReadPtr, regStatus}, stat[:]); err != nil {
return fmt.Errorf("ds248x: error while reading status register: %s", err)
}
if stat[0] != 0x18 {
return fmt.Errorf("ds248x: invalid status register value: %#x, expected 0x18", stat[0])
}
// Write the device configuration register to get the chip out of reset state, immediately
// read it back to get confirmation.
d.confReg = 0xe1 // standard-speed, no strong pullup, no powerdown, active pull-up
if opts.PassivePullup {
d.confReg ^= 0x11
}
var dcr [1]byte
if err := d.i2c.Tx([]byte{cmdWriteConfig, d.confReg}, dcr[:]); err != nil {
return fmt.Errorf("ds248x: error while writing device config register: %s", err)
}
// When reading back we only get the bottom nibble
if dcr[0] != d.confReg&0x0f {
return fmt.Errorf("ds248x: failure to write device config register, wrote %#x got %#x back",
d.confReg, dcr[0])
}
// Set the read ptr to the port configuration register to determine whether we have a
// ds2483 vs ds2482-100. This will fail on devices that do not have a port config
// register, such as the ds2482-100.
d.isDS2483 = d.i2c.Tx([]byte{cmdSetReadPtr, regPCR}, nil) == nil
// Set the options for the ds2483.
if d.isDS2483 {
buf := []byte{cmdAdjPort,
byte(0x00 + ((opts.ResetLow/time.Microsecond - 430) / 20 & 0x0f)),
byte(0x20 + ((opts.PresenceDetect/time.Microsecond - 55) / 2 & 0x0f)),
byte(0x40 + ((opts.Write0Low/time.Microsecond - 51) / 2 & 0x0f)),
byte(0x60 + (((opts.Write0Recovery-1250)/2500 + 5) & 0x0f)),
byte(0x80 + (opts.PullupRes & 0x0f)),
}
if err := d.i2c.Tx(buf, nil); err != nil {
return fmt.Errorf("ds248x: error while setting port config values: %s", err)
}
}
return nil
}
const (
cmdReset = 0xf0 // reset ds248x
cmdSetReadPtr = 0xe1 // set the read pointer
cmdWriteConfig = 0xd2 // write the device configuration
cmdAdjPort = 0xc3 // adjust 1-wire port
cmd1WReset = 0xb4 // reset the 1-wire bus
cmd1WBit = 0x87 // perform a single-bit transaction on the 1-wire bus
cmd1WWrite = 0xa5 // perform a byte write on the 1-wire bus
cmd1WRead = 0x96 // perform a byte read on the 1-wire bus
cmd1WTriplet = 0x78 // perform a triplet operation (2 bit reads, a bit write)
regDCR = 0xc3 // read ptr for device configuration register
regStatus = 0xf0 // read ptr for status register
regRDR = 0xe1 // read ptr for read-data register
regPCR = 0xb4 // read ptr for port configuration register
)