robocar-pca9685/vendor/periph.io/x/host/v3/ftdi/i2c.go

296 lines
7.0 KiB
Go

// Copyright 2017 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.
// This functionality requires MPSSE.
//
// Interfacing I²C:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_113_FTDI_Hi_Speed_USB_To_I2C_Example.pdf
//
// Implementation based on
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
//
// Page 18: MPSSE does not automatically support clock stretching for I²C.
package ftdi
import (
"context"
"errors"
"fmt"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic"
)
const i2cSCL = 1 // D0
const i2cSDAOut = 2 // D1
const i2cSDAIn = 4 // D2
type i2cBus struct {
f *FT232H
pullUp bool
}
// Close stops I²C mode, returns to high speed mode, disable tri-state.
func (d *i2cBus) Close() error {
d.f.mu.Lock()
err := d.stopI2C()
d.f.mu.Unlock()
return err
}
// Duplex implements conn.Conn.
func (d *i2cBus) Duplex() conn.Duplex {
return conn.Half
}
func (d *i2cBus) String() string {
return d.f.String()
}
// SetSpeed implements i2c.Bus.
func (d *i2cBus) SetSpeed(f physic.Frequency) error {
if f > 10*physic.MegaHertz {
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 10MHz", f)
}
if f < 100*physic.Hertz {
return fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.KiloHertz?", f)
}
d.f.mu.Lock()
defer d.f.mu.Unlock()
_, err := d.f.h.MPSSEClock(f * 2 / 3)
return err
}
// Tx implements i2c.Bus.
func (d *i2cBus) Tx(addr uint16, w, r []byte) error {
d.f.mu.Lock()
defer d.f.mu.Unlock()
if err := d.setI2CStart(); err != nil {
return err
}
a := [1]byte{byte(addr)}
if err := d.writeBytes(a[:]); err != nil {
return err
}
if len(w) != 0 {
if err := d.writeBytes(w); err != nil {
return err
}
}
if len(r) != 0 {
if err := d.readBytes(r); err != nil {
return err
}
}
if err := d.setI2CStop(); err != nil {
return err
}
return d.setI2CLinesIdle()
}
// SCL implements i2c.Pins.
func (d *i2cBus) SCL() gpio.PinIO {
return d.f.D0
}
// SDA implements i2c.Pins.
func (d *i2cBus) SDA() gpio.PinIO {
return d.f.D1
}
// setupI2C initializes the MPSSE to the state to run an I²C transaction.
//
// Defaults to 400kHz.
//
// When pullUp is true; output alternates between Out(Low) and In(PullUp).
//
// when pullUp is false; pins are set in Tristate so Out(High) becomes float
// instead of drive High. Low still drives low. That's called open collector.
func (d *i2cBus) setupI2C(pullUp bool) error {
if pullUp {
return errors.New("d2xx: PullUp will soon be implemented")
}
// TODO(maruel): We could set these only *during* the I²C operation, which
// would make more sense.
f := 400 * physic.KiloHertz
clk := ((30 * physic.MegaHertz / f) - 1) * 2 / 3
buf := [4 + 3]byte{
clock3Phase,
clock30MHz, byte(clk), byte(clk >> 8),
}
cmd := buf[:4]
if !d.pullUp {
// TODO(maruel): Do not mess with other GPIOs tristate.
cmd = append(cmd, dataTristate, 7, 0)
}
if _, err := d.f.h.Write(cmd); err != nil {
return err
}
d.f.usingI2C = true
d.pullUp = pullUp
return d.setI2CLinesIdle()
}
// stopI2C resets the MPSSE to a more "normal" state.
func (d *i2cBus) stopI2C() error {
// Resets to 30MHz.
buf := [4 + 3]byte{
clock2Phase,
clock30MHz, 0, 0,
}
cmd := buf[:4]
if !d.pullUp {
// TODO(maruel): Do not mess with other GPIOs tristate.
cmd = append(cmd, dataTristate, 0, 0)
}
_, err := d.f.h.Write(cmd)
d.f.usingI2C = false
return err
}
// setI2CLinesIdle sets all D0 and D1 lines high.
//
// Does not touch D3~D7.
func (d *i2cBus) setI2CLinesIdle() error {
const mask = 0xFF &^ (i2cSCL | i2cSDAOut | i2cSDAIn)
// TODO(maruel): d.pullUp
d.f.dbus.direction = d.f.dbus.direction&mask | i2cSCL | i2cSDAOut
d.f.dbus.value = d.f.dbus.value & mask
cmd := [...]byte{gpioSetD, d.f.dbus.value | i2cSCL | i2cSDAOut, d.f.dbus.direction}
_, err := d.f.h.Write(cmd[:])
return err
}
// setI2CStart starts an I²C transaction.
//
// Does not touch D3~D7.
func (d *i2cBus) setI2CStart() error {
// TODO(maruel): d.pullUp
dir := d.f.dbus.direction
v := d.f.dbus.value
// Assumes last setup was d.setI2CLinesIdle(), e.g. D0 and D1 are high, so
// skip this.
//
// Runs the command 4 times as a way to delay execution.
cmd := [...]byte{
// SCL high, SDA low for 600ns
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
// SCL low, SDA low
gpioSetD, v, dir,
gpioSetD, v, dir,
gpioSetD, v, dir,
}
_, err := d.f.h.Write(cmd[:])
return err
}
// setI2CStop completes an I²C transaction.
//
// Does not touch D3~D7.
func (d *i2cBus) setI2CStop() error {
// TODO(maruel): d.pullUp
dir := d.f.dbus.direction
v := d.f.dbus.value
// Runs the command 4 times as a way to delay execution.
cmd := [...]byte{
// SCL low, SDA low
gpioSetD, v, dir,
gpioSetD, v, dir,
gpioSetD, v, dir,
gpioSetD, v, dir,
// SCL high, SDA low
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
gpioSetD, v | i2cSCL, dir,
// SCL high, SDA high
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
}
_, err := d.f.h.Write(cmd[:])
return err
}
// writeBytes writes multiple bytes within an I²C transaction.
//
// Does not touch D3~D7.
func (d *i2cBus) writeBytes(w []byte) error {
// TODO(maruel): d.pullUp
dir := d.f.dbus.direction
v := d.f.dbus.value
// TODO(maruel): WAT?
if err := d.f.h.Flush(); err != nil {
return err
}
// TODO(maruel): Implement both with and without NAK check.
var r [1]byte
cmd := [...]byte{
// Data out, the 0 will be replaced with the byte.
dataOut | dataOutFall, 0, 0, 0,
// Set back to idle.
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
// Read ACK/NAK.
dataIn | dataBit, 0,
flush,
}
for _, c := range w {
cmd[3] = c
if _, err := d.f.h.Write(cmd[:]); err != nil {
return err
}
if _, err := d.f.h.ReadAll(context.Background(), r[:]); err != nil {
return err
}
if r[0]&1 == 0 {
return errors.New("got NAK")
}
}
return nil
}
// readBytes reads multiple bytes within an I²C transaction.
//
// Does not touch D3~D7.
func (d *i2cBus) readBytes(r []byte) error {
// TODO(maruel): d.pullUp
dir := d.f.dbus.direction
v := d.f.dbus.value
cmd := [...]byte{
// Read 8 bits.
dataIn | dataBit, 7,
// Send ACK/NAK.
dataOut | dataOutFall | dataBit, 0, 0,
// Set back to idle.
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
// Force read buffer flush. This is only necessary if NAK are not ignored.
flush,
}
for i := range r {
if i == len(r)-1 {
// NAK.
cmd[4] = 0x80
}
if _, err := d.f.h.Write(cmd[:]); err != nil {
return err
}
if _, err := d.f.h.ReadAll(context.Background(), r[i:1]); err != nil {
return err
}
}
return nil
}
var _ i2c.BusCloser = &i2cBus{}
var _ i2c.Pins = &i2cBus{}