You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devices/bitbang/i2c.go

259 lines
5.9 KiB
Go

// 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.
// Specification
//
// http://www.nxp.com/documents/user_manual/UM10204.pdf
package bitbang
import (
"errors"
"fmt"
"runtime"
"sync"
"time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/host/cpu"
)
// SkipAddr can be used to skip the address from being sent.
const SkipAddr uint16 = 0xFFFF
// New returns an object that communicates I²C over two pins.
//
// BUG(maruel): It is close to working but not yet, the signal is incorrect
// during ACK.
//
// It has two special features:
// - Special address SkipAddr can be used to skip the address from being
// communicated
// - An arbitrary speed can be used
func New(clk gpio.PinIO, data gpio.PinIO, f physic.Frequency) (*I2C, error) {
// Spec calls to idle at high. Page 8, section 3.1.1.
// Set SCL as pull-up.
if err := clk.In(gpio.PullUp, gpio.NoEdge); err != nil {
return nil, err
}
if err := clk.Out(gpio.High); err != nil {
return nil, err
}
// Set SDA as pull-up.
if err := data.In(gpio.PullUp, gpio.NoEdge); err != nil {
return nil, err
}
if err := data.Out(gpio.High); err != nil {
return nil, err
}
i := &I2C{
scl: clk,
sda: data,
halfCycle: f.Period() / 2,
}
return i, nil
}
// I2C represents an I²C master implemented as bit-banging on 2 GPIO pins.
type I2C struct {
mu sync.Mutex
scl gpio.PinIO // Clock line
sda gpio.PinIO // Data line
halfCycle time.Duration
}
func (i *I2C) String() string {
return fmt.Sprintf("bitbang/i2c(%s, %s)", i.scl, i.sda)
}
// Close implements i2c.BusCloser.
func (i *I2C) Close() error {
return nil
}
// Tx implements i2c.Bus.
func (i *I2C) Tx(addr uint16, w, r []byte) error {
i.mu.Lock()
defer i.mu.Unlock()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
//syscall.Setpriority(which, who, prio)
i.start()
defer i.stop()
if addr != SkipAddr {
if addr > 0xFF {
// Page 15, section 3.1.11 10-bit addressing
// TODO(maruel): Implement if desired; prefix 0b11110xx.
return errors.New("bitbang-i2c: invalid address")
}
// Page 13, section 3.1.10 The slave address and R/W bit
addr <<= 1
if len(r) == 0 {
addr |= 1
}
ack, err := i.writeByte(byte(addr))
if err != nil {
return err
}
if !ack {
return errors.New("bitbang-i2c: got NACK")
}
}
for _, b := range w {
ack, err := i.writeByte(b)
if err != nil {
return err
}
if !ack {
return errors.New("bitbang-i2c: got NACK")
}
}
for x := range r {
var err error
r[x], err = i.readByte()
if err != nil {
return err
}
}
return nil
}
// SetSpeed implements i2c.Bus.
func (i *I2C) SetSpeed(f physic.Frequency) error {
i.mu.Lock()
defer i.mu.Unlock()
i.halfCycle = f.Period() / 2
return nil
}
// SCL implements i2c.Pins.
func (i *I2C) SCL() gpio.PinIO {
return i.scl
}
// SDA implements i2c.Pins.
func (i *I2C) SDA() gpio.PinIO {
return i.sda
}
//
// "When CLK is a high level and DIO changes from high to low level, data input
// starts."
//
// Ends with SDA and SCL low.
//
// Lasts 1/2 cycle.
func (i *I2C) start() {
// Page 9, section 3.1.4 START and STOP conditions
// In multi-master mode, it would have to sense SDA first and after the sleep.
_ = i.sda.Out(gpio.Low)
i.sleepHalfCycle()
_ = i.scl.Out(gpio.Low)
}
// "When CLK is a high level and DIO changes from low level to high level, data
// input ends."
//
// Lasts 3/2 cycle.
func (i *I2C) stop() {
// Page 9, section 3.1.4 START and STOP conditions
_ = i.scl.Out(gpio.Low)
i.sleepHalfCycle()
_ = i.scl.Out(gpio.High)
i.sleepHalfCycle()
_ = i.sda.Out(gpio.High)
// TODO(maruel): This sleep could be skipped, assuming we wait for the next
// transfer if too quick to happen.
i.sleepHalfCycle()
}
// writeByte writes 8 bits then waits for ACK.
//
// Expects SDA and SCL low.
//
// Ends with SDA low and SCL high.
//
// Lasts 9 cycles.
func (i *I2C) writeByte(b byte) (bool, error) {
// Page 9, section 3.1.3 Data validity
// "The data on te SDA line must be stable during the high period of the
// clock."
// Page 10, section 3.1.5 Byte format
for x := 0; x < 8; x++ {
_ = i.sda.Out(b&byte(1<<byte(7-x)) != 0)
i.sleepHalfCycle()
// Let the device read SDA.
// TODO(maruel): Support clock stretching, the device may keep the line low.
_ = i.scl.Out(gpio.High)
i.sleepHalfCycle()
_ = i.scl.Out(gpio.Low)
}
// Page 10, section 3.1.6 ACK and NACK
// 9th clock is ACK.
i.sleepHalfCycle()
// SCL was already set as pull-up. PullNoChange
if err := i.scl.In(gpio.PullUp, gpio.NoEdge); err != nil {
return false, err
}
// SDA was already set as pull-up.
if err := i.sda.In(gpio.PullUp, gpio.NoEdge); err != nil {
return false, err
}
// Implement clock stretching, the device may keep the line low.
for i.scl.Read() == gpio.Low {
i.sleepHalfCycle()
}
// ACK == Low.
ack := i.sda.Read() == gpio.Low
if err := i.scl.Out(gpio.Low); err != nil {
return false, err
}
if err := i.sda.Out(gpio.Low); err != nil {
return false, err
}
return ack, nil
}
// readByte reads 8 bits and an ACK.
//
// Expects SDA and SCL low.
//
// Ends with SDA low and SCL high.
//
// Lasts 9 cycles.
func (i *I2C) readByte() (byte, error) {
var b byte
if err := i.sda.In(gpio.PullUp, gpio.NoEdge); err != nil {
return b, err
}
for x := 0; x < 8; x++ {
i.sleepHalfCycle()
// TODO(maruel): Support clock stretching, the device may keep the line low.
_ = i.scl.Out(gpio.High)
i.sleepHalfCycle()
if i.sda.Read() == gpio.High {
b |= byte(1) << byte(7-x)
}
_ = i.scl.Out(gpio.Low)
}
if err := i.sda.Out(gpio.Low); err != nil {
return 0, err
}
i.sleepHalfCycle()
_ = i.scl.Out(gpio.High)
i.sleepHalfCycle()
return b, nil
}
// sleep does a busy loop to act as fast as possible.
func (i *I2C) sleepHalfCycle() {
cpu.Nanospin(i.halfCycle)
}
var _ i2c.Bus = &I2C{}