mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
222 lines
5.4 KiB
Go
222 lines
5.4 KiB
Go
// Package wire implements the Dallas Semiconductor Corp.'s 1-wire bus system.
|
||
//
|
||
// Wikipedia: https://en.wikipedia.org/wiki/1-Wire
|
||
package onewire // import "tinygo.org/x/drivers/onewire"
|
||
|
||
import (
|
||
"errors"
|
||
"machine"
|
||
"time"
|
||
)
|
||
|
||
// OneWire ROM commands
|
||
const (
|
||
READ_ROM uint8 = 0x33
|
||
MATCH_ROM uint8 = 0x55
|
||
SKIP_ROM uint8 = 0xCC
|
||
SEARCH_ROM uint8 = 0xF0
|
||
)
|
||
|
||
// Device wraps a connection to an 1-Wire devices.
|
||
type Device struct {
|
||
p machine.Pin
|
||
}
|
||
|
||
// Config wraps a configuration to an 1-Wire devices.
|
||
type Config struct{}
|
||
|
||
// Errors list
|
||
var (
|
||
errNoPresence = errors.New("Error: OneWire. No devices on the bus.")
|
||
errTooManyDevices = errors.New("Error: OneWire. Too many devices on the bus.")
|
||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||
)
|
||
|
||
// New creates a new GPIO 1-Wire connection.
|
||
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
|
||
func New(p machine.Pin) Device {
|
||
return Device{
|
||
p: p,
|
||
}
|
||
}
|
||
|
||
// Configure initializes the protocol.
|
||
func (d *Device) Configure(config Config) {}
|
||
|
||
// Reset pull DQ line low, then up.
|
||
func (d Device) Reset() error {
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||
time.Sleep(480 * time.Microsecond)
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||
time.Sleep(70 * time.Microsecond)
|
||
precence := d.p.Get()
|
||
time.Sleep(410 * time.Microsecond)
|
||
if precence {
|
||
return errNoPresence
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// WriteBit transmits a bit to 1-Wire bus.
|
||
func (d Device) WriteBit(data uint8) {
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||
if data&1 == 1 { // Send '1'
|
||
time.Sleep(5 * time.Microsecond)
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||
time.Sleep(60 * time.Microsecond)
|
||
} else { // Send '0'
|
||
time.Sleep(60 * time.Microsecond)
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||
time.Sleep(5 * time.Microsecond)
|
||
}
|
||
}
|
||
|
||
// Write transmits a byte as bit array to 1-Wire bus. (LSB first)
|
||
func (d Device) Write(data uint8) {
|
||
for i := 0; i < 8; i++ {
|
||
d.WriteBit(data)
|
||
data >>= 1
|
||
}
|
||
}
|
||
|
||
// ReadBit receives a bit from 1-Wire bus.
|
||
func (d Device) ReadBit() (data uint8) {
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||
time.Sleep(3 * time.Microsecond)
|
||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||
time.Sleep(8 * time.Microsecond)
|
||
if d.p.Get() {
|
||
data = 1
|
||
}
|
||
time.Sleep(60 * time.Microsecond)
|
||
return data
|
||
}
|
||
|
||
// Read receives a byte from 1-Wire bus. (LSB first)
|
||
func (d Device) Read() (data uint8) {
|
||
for i := 0; i < 8; i++ {
|
||
data >>= 1
|
||
data |= d.ReadBit() << 7
|
||
}
|
||
return data
|
||
}
|
||
|
||
// ReadAddress receives a 64-bit unique ROM ID from Device. (LSB first)
|
||
// Note: use this if there is only one slave device on the bus.
|
||
func (d Device) ReadAddress() ([]uint8, error) {
|
||
var romid = make([]uint8, 8)
|
||
if err := d.Reset(); err != nil {
|
||
return nil, err
|
||
}
|
||
d.Write(READ_ROM)
|
||
for i := 0; i < 8; i++ {
|
||
romid[i] = d.Read()
|
||
}
|
||
if d.Сrc8(romid) != 0 {
|
||
return nil, errReadAddress
|
||
}
|
||
return romid, nil
|
||
}
|
||
|
||
// Select selects the address of the device for communication
|
||
func (d Device) Select(romid []uint8) error {
|
||
if err := d.Reset(); err != nil {
|
||
return err
|
||
}
|
||
if len(romid) == 0 {
|
||
d.Write(SKIP_ROM)
|
||
return nil
|
||
}
|
||
d.Write(MATCH_ROM)
|
||
for i := 0; i < 8; i++ {
|
||
d.Write(romid[i])
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Search searches for all devices on the bus.
|
||
// Note: max 32 slave devices per bus
|
||
func (d Device) Search(cmd uint8) ([][]uint8, error) {
|
||
var (
|
||
bit, bit_c uint8 = 0, 0
|
||
bitOffset uint8 = 0
|
||
lastZero uint8 = 0
|
||
lastFork uint8 = 0
|
||
lastAddress = make([]uint8, 8)
|
||
romIDs = make([][]uint8, 32) //
|
||
romIndex uint8 = 0
|
||
)
|
||
|
||
for i := range romIDs {
|
||
romIDs[i] = make([]uint8, 8)
|
||
}
|
||
|
||
for ok := true; ok; ok = (lastFork != 0) {
|
||
if err := d.Reset(); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// send search command to bus
|
||
d.Write(cmd)
|
||
|
||
lastZero = 0
|
||
|
||
for bitOffset = 0; bitOffset < 64; bitOffset++ {
|
||
bit = d.ReadBit() // read first address bit
|
||
bit_c = d.ReadBit() // read second (complementary) address bit
|
||
|
||
if bit == 1 && bit_c == 1 { // no device
|
||
return nil, errNoPresence
|
||
}
|
||
|
||
if bit == 0 && bit_c == 0 { // collision
|
||
if bitOffset == lastFork {
|
||
bit = 1
|
||
}
|
||
if bitOffset < lastFork {
|
||
bit = (lastAddress[bitOffset>>3] >> (bitOffset & 0x07)) & 1
|
||
}
|
||
if bit == 0 {
|
||
lastZero = bitOffset
|
||
}
|
||
}
|
||
|
||
if bit == 0 {
|
||
lastAddress[bitOffset>>3] &= ^(1 << (bitOffset & 0x07))
|
||
} else {
|
||
lastAddress[bitOffset>>3] |= (1 << (bitOffset & 0x07))
|
||
}
|
||
d.WriteBit(bit)
|
||
}
|
||
if d.Сrc8(lastAddress) != 0 {
|
||
continue
|
||
}
|
||
|
||
lastFork = lastZero
|
||
copy(romIDs[romIndex], lastAddress)
|
||
romIndex++
|
||
if romIndex >= 32 {
|
||
return romIDs, errTooManyDevices
|
||
}
|
||
}
|
||
return romIDs[:romIndex:romIndex], nil
|
||
}
|
||
|
||
// Crc8 compute a Dallas Semiconductor 8 bit CRC.
|
||
func (_ Device) Сrc8(buffer []uint8) (crc uint8) {
|
||
// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0
|
||
// Tiny 2x16 entry CRC table created by Arjen Lentz
|
||
// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table
|
||
crc8_table := [...]uint8{
|
||
0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83,
|
||
0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41,
|
||
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
|
||
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74,
|
||
}
|
||
for i := 0; i < len(buffer); i++ {
|
||
crc = buffer[i] ^ crc // just re-using crc as intermediate
|
||
crc = crc8_table[crc&0x0f] ^ crc8_table[16+((crc>>4)&0x0f)]
|
||
}
|
||
return crc
|
||
}
|