chore: bump periph.io dependencies

This commit is contained in:
2022-01-02 23:29:08 +01:00
parent 3a07aabde0
commit 7bc5560cdc
208 changed files with 12350 additions and 13205 deletions

285
vendor/periph.io/x/host/v3/.gohci.yml generated vendored
View File

@@ -17,44 +17,61 @@ workers:
- -benchtime=1000ms
- -benchmem
- ./...
# Test in advance.
- cmd:
# Test in advance: devices
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/devices
dir: ..
- cmd:
- dir: ../devices
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../devices
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../devices
- cmd:
- -t
- ./...
- dir: ../devices
cmd:
- go
- test
- -short
- ./...
dir: ../devices
# Test in advance.
- cmd:
# Test in advance: cmd
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/cmd
dir: ..
- cmd:
- dir: ../cmd
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../cmd
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../cmd
- cmd:
- -t
- ./...
- dir: ../cmd
cmd:
- go
- test
- -short
- ./...
dir: ../cmd
# Test commands.
- cmd:
- dir: ../cmd
cmd:
- go
- install
- -v
@@ -63,7 +80,6 @@ workers:
- ./periph-info
- ./periph-smoketest
- ./spi-list
dir: ../cmd
- cmd:
- periph-info
- cmd:
@@ -93,44 +109,61 @@ workers:
- -benchtime=1000ms
- -benchmem
- ./...
# Test in advance.
- cmd:
# Test in advance: devices
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/devices
dir: ..
- cmd:
- dir: ../devices
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../devices
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../devices
- cmd:
- -t
- ./...
- dir: ../devices
cmd:
- go
- test
- -short
- ./...
dir: ../devices
# Test in advance.
- cmd:
# Test in advance: cmd
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/cmd
dir: ..
- cmd:
- dir: ../cmd
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../cmd
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../cmd
- cmd:
- -t
- ./...
- dir: ../cmd
cmd:
- go
- test
- -short
- ./...
dir: ../cmd
# Test commands.
- cmd:
- dir: ../cmd
cmd:
- go
- install
- -v
@@ -140,7 +173,6 @@ workers:
- ./periph-info
- ./periph-smoketest
- ./spi-list
dir: ../cmd
- cmd:
- periph-info
- cmd:
@@ -183,44 +215,61 @@ workers:
- -benchtime=1000ms
- -benchmem
- ./...
# Test in advance.
- cmd:
# Test in advance: devices
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/devices
dir: ..
- cmd:
- dir: ../devices
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../devices
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../devices
- cmd:
- -t
- ./...
- dir: ../devices
cmd:
- go
- test
- -short
- ./...
dir: ../devices
# Test in advance.
- cmd:
# Test in advance: cmd
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/cmd
dir: ..
- cmd:
- dir: ../cmd
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../cmd
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../cmd
- cmd:
- -t
- ./...
- dir: ../cmd
cmd:
- go
- test
- -short
- ./...
dir: ../cmd
# Test commands.
- cmd:
- dir: ../cmd
cmd:
- go
- install
- -v
@@ -230,7 +279,6 @@ workers:
- ./periph-info
- ./periph-smoketest
- ./spi-list
dir: ../cmd
- cmd:
- periph-info
- cmd:
@@ -277,6 +325,11 @@ workers:
- periph-smoketest
- bcm283x
- -quick
- cmd:
- periph-smoketest
- ftdi
- -type
- ft232h
# Old MacBook Pro on 10.9.
- name: mbp
@@ -289,44 +342,61 @@ workers:
- -benchtime=1000ms
- -benchmem
- ./...
# Test in advance.
- cmd:
# Test in advance: devices
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/devices
dir: ..
- cmd:
- dir: ../devices
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../devices
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../devices
- cmd:
- -t
- ./...
- dir: ../devices
cmd:
- go
- test
- -short
- ./...
dir: ../devices
# Test in advance.
- cmd:
# Test in advance: cmd
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/cmd
dir: ..
- cmd:
- dir: ../cmd
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../cmd
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../cmd
- cmd:
- -t
- ./...
- dir: ../cmd
cmd:
- go
- test
- -short
- ./...
dir: ../cmd
# Test commands.
- cmd:
- dir: ../cmd
cmd:
- go
- install
- -v
@@ -336,7 +406,6 @@ workers:
- ./periph-info
- ./periph-smoketest
- ./spi-list
dir: ../cmd
- cmd:
- periph-info
- cmd:
@@ -349,11 +418,11 @@ workers:
- i2c-list
- cmd:
- spi-list
# - cmd:
# - periph-smoketest
# - ftdi
# - -type
# - ft232r
- cmd:
- periph-smoketest
- ftdi
- -type
- ft232r
# Laptop on Windows 10.
- name: win10
@@ -366,44 +435,61 @@ workers:
- -benchtime=1000ms
- -benchmem
- ./...
# Test in advance.
- cmd:
# Test in advance: devices
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/devices
dir: ..
- cmd:
- dir: ../devices
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../devices
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../devices
- cmd:
- -t
- ./...
- dir: ../devices
cmd:
- go
- test
- -short
- ./...
dir: ../devices
# Test in advance.
- cmd:
# Test in advance: cmd
- dir: ..
cmd:
- git
- clone
- --depth
- 1
- https://github.com/periph/cmd
dir: ..
- cmd:
- dir: ../cmd
cmd:
- go
- mod
- edit
- -replace=periph.io/x/host/v3=../host
- dir: ../cmd
cmd:
- go
- get
- periph.io/x/host/v3@${GIT_SHA}
dir: ../cmd
- cmd:
- -t
- ./...
- dir: ../cmd
cmd:
- go
- test
- -short
- ./...
dir: ../cmd
# Test commands.
- cmd:
- dir: ../cmd
cmd:
- go
- install
- -v
@@ -413,7 +499,6 @@ workers:
- ./periph-info
- ./periph-smoketest
- ./spi-list
dir: ../cmd
- cmd:
- periph-info
- cmd:
@@ -426,8 +511,8 @@ workers:
- i2c-list
- cmd:
- spi-list
# - cmd:
# - periph-smoketest
# - ftdi
# - -type
# - ft232h
- cmd:
- periph-smoketest
- ftdi
- -type
- ft232h

View File

@@ -9,8 +9,7 @@ get an [invite here](https://invite.slack.golangbridge.org/).
[![mascot](https://raw.githubusercontent.com/periph/website/master/site/static/img/periph-mascot-280.png)](https://periph.io/)
[![PkgGoDev](https://pkg.go.dev/badge/periph.io/x/host/v3)](https://pkg.go.dev/periph.io/x/host/v3)
[![Coverage
Status](https://codecov.io/gh/periph/host/graph/badge.svg)](https://codecov.io/gh/periph/host)
[![codecov](https://codecov.io/gh/periph/host/branch/main/graph/badge.svg?token=RX9O1CPQHU)](https://codecov.io/gh/periph/host)
## Example

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build arm64
// +build arm64
package allwinner

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm && !arm64
// +build !arm,!arm64
package allwinner

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package am335x

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build arm64
// +build arm64
package bcm283x

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm && !arm64
// +build !arm,!arm64
package bcm283x

View File

@@ -406,8 +406,8 @@ func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
// page 101.
// However, BCM2711 uses a simpler way of setting pull resistors, reference at
// https://github.com/raspberrypi/documentation/blob/master/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
// page 84 and 95 ~ 98.
// https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf
// page 65 and 73 ~ 76.
// If we are running on a newer chip such as BCM2711, set Pull directly.
if !drvGPIO.useLegacyPull {
@@ -431,7 +431,10 @@ func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
case gpio.Float:
pullState = 0
}
drvGPIO.gpioMemory.pullRegister[offset] = pullState << uint((p.number%16)<<1)
bitOffset := 2 * uint(p.number%16)
previous := drvGPIO.gpioMemory.pullRegister[offset] & ^(3 << bitOffset)
drvGPIO.gpioMemory.pullRegister[offset] = previous | (pullState << bitOffset)
} else {
// Set Pull
switch pull {
@@ -1024,9 +1027,8 @@ type function uint8
// Mapping as
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
// pages 90-91.
// And
// https://github.com/raspberrypi/documentation/blob/master/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
// pages 83-84.
// And https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf pages
// 65 and 73 ~ 76.
type gpioMap struct {
// 0x00 RW GPIO Function Select 0 (GPIO0-9)
// 0x04 RW GPIO Function Select 1 (GPIO10-19)

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package black

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package bone

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package green

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package chip

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package cpu

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build arm64
// +build arm64
package distro

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm && !arm64
// +build !arm,!arm64
package distro

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package distro

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package fs

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build mips || mipsle
// +build mips mipsle
package fs

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !mips && !mipsle
// +build !mips,!mipsle
package fs

View File

@@ -1,878 +0,0 @@
// 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.
package ftdi
import (
"context"
"errors"
"strconv"
"sync"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
)
// Info is the information gathered about the connected FTDI device.
//
// The data is gathered from the USB descriptor.
type Info struct {
// Opened is true if the device was successfully opened.
Opened bool
// Type is the FTDI device type.
//
// The value can be "FT232H", "FT232R", etc.
//
// An empty string means the type is unknown.
Type string
// VenID is the vendor ID from the USB descriptor information. It is expected
// to be 0x0403 (FTDI).
VenID uint16
// DevID is the product ID from the USB descriptor information. It is
// expected to be one of 0x6001, 0x6006, 0x6010, 0x6014.
DevID uint16
}
// Dev represents one FTDI device.
//
// There can be multiple FTDI devices connected to a host.
//
// The device may also export one or multiple of I²C, SPI buses. You need to
// either cast into the right hardware, but more simply use the i2creg / spireg
// bus/port registries.
type Dev interface {
// conn.Resource
String() string
Halt() error
// Info returns information about an opened device.
Info(i *Info)
// Header returns the GPIO pins exposed on the chip.
Header() []gpio.PinIO
// SetSpeed sets the base clock for all I/O transactions.
//
// The device defaults to its fastest speed.
SetSpeed(f physic.Frequency) error
// EEPROM returns the EEPROM content.
EEPROM(ee *EEPROM) error
// WriteEEPROM updates the EEPROM. Must be used carefully.
WriteEEPROM(ee *EEPROM) error
// EraseEEPROM erases the EEPROM. Must be used carefully.
EraseEEPROM() error
// UserArea reads and return the EEPROM part that can be used to stored user
// defined values.
UserArea() ([]byte, error)
// WriteUserArea updates the user area in the EEPROM.
//
// If the length of ua is less than the available space, is it zero extended.
WriteUserArea(ua []byte) error
}
// broken represents a device that couldn't be opened correctly.
//
// It returns an error message to help the user diagnose issues.
type broken struct {
index int
err error
name string
}
func (b *broken) String() string {
return b.name
}
func (b *broken) Halt() error {
return nil
}
func (b *broken) Info(i *Info) {
i.Opened = false
}
func (b *broken) Header() []gpio.PinIO {
return nil
}
func (b *broken) SetSpeed(f physic.Frequency) error {
return b.err
}
func (b *broken) EEPROM(ee *EEPROM) error {
return b.err
}
func (b *broken) WriteEEPROM(ee *EEPROM) error {
return b.err
}
func (b *broken) EraseEEPROM() error {
return b.err
}
func (b *broken) UserArea() ([]byte, error) {
return nil, b.err
}
func (b *broken) WriteUserArea(ua []byte) error {
return b.err
}
// generic represents a generic FTDI device.
//
// It is used for the models that this package doesn't fully support yet.
type generic struct {
// Immutable after initialization.
index int
h *handle
name string
}
func (f *generic) String() string {
return f.name
}
// Halt implements conn.Resource.
//
// This halts all operations going through this device.
func (f *generic) Halt() error {
return f.h.Reset()
}
// Info returns information about an opened device.
func (f *generic) Info(i *Info) {
i.Opened = true
i.Type = f.h.t.String()
i.VenID = f.h.venID
i.DevID = f.h.devID
}
// Header returns the GPIO pins exposed on the chip.
func (f *generic) Header() []gpio.PinIO {
return nil
}
func (f *generic) SetSpeed(freq physic.Frequency) error {
// TODO(maruel): Doc says the actual speed is 16x, confirm.
return f.h.SetBaudRate(freq)
}
func (f *generic) EEPROM(ee *EEPROM) error {
return f.h.ReadEEPROM(ee)
/*
if f.ee.Raw == nil {
if err := f.h.readEEPROM(&f.ee); err != nil {
return nil
}
if f.ee.Raw == nil {
// It's a fresh new device. Devices bought via Adafruit already have
// their EEPROM programmed with Adafruit branding but devices sold by
// CJMCU are not. Since d2xxGetDeviceInfo() above succeeded, we know the
// device type via the USB descriptor, which is sufficient to load the
// driver, which permits to program the EEPROM to "bootstrap" it.
f.ee.Raw = []byte{}
}
}
*ee = f.ee
return nil
*/
}
func (f *generic) WriteEEPROM(ee *EEPROM) error {
// TODO(maruel): Compare with the cached EEPROM, and only update the
// different values if needed so reduce the EEPROM wear.
// f.h.h.d2xxWriteEE()
return f.h.WriteEEPROM(ee)
}
func (f *generic) EraseEEPROM() error {
return f.h.EraseEEPROM()
}
func (f *generic) UserArea() ([]byte, error) {
return f.h.ReadUA()
}
func (f *generic) WriteUserArea(ua []byte) error {
return f.h.WriteUA(ua)
}
//
func newFT232H(g generic) (*FT232H, error) {
f := &FT232H{
generic: g,
cbus: gpiosMPSSE{h: g.h, cbus: true},
dbus: gpiosMPSSE{h: g.h},
c8: invalidPin{num: 16, n: g.name + ".C8"}, // , dp: gpio.PullUp
c9: invalidPin{num: 17, n: g.name + ".C9"}, // , dp: gpio.PullUp
}
f.cbus.init(f.name)
f.dbus.init(f.name)
for i := range f.dbus.pins {
f.hdr[i] = &f.dbus.pins[i]
}
for i := range f.cbus.pins {
f.hdr[i+8] = &f.cbus.pins[i]
}
// TODO(maruel): C8 and C9 can be used when their mux in the EEPROM is set to
// ft232hCBusIOMode.
f.hdr[16] = &f.c8
f.hdr[17] = &f.c9
f.D0 = f.hdr[0]
f.D1 = f.hdr[1]
f.D2 = f.hdr[2]
f.D3 = f.hdr[3]
f.D4 = f.hdr[4]
f.D5 = f.hdr[5]
f.D6 = f.hdr[6]
f.D7 = f.hdr[7]
f.C0 = f.hdr[8]
f.C1 = f.hdr[9]
f.C2 = f.hdr[10]
f.C3 = f.hdr[11]
f.C4 = f.hdr[12]
f.C5 = f.hdr[13]
f.C6 = f.hdr[14]
f.C7 = f.hdr[15]
f.C8 = f.hdr[16]
f.C9 = f.hdr[17]
// This function forces all pins as inputs.
if err := f.h.InitMPSSE(); err != nil {
return nil, err
}
f.s.c.f = f
f.i.f = f
return f, nil
}
// FT232H represents a FT232H device.
//
// It implements Dev.
//
// The FT232H has 1024 bytes output buffer and 1024 bytes input buffer. It
// supports 512 bytes USB packets.
//
// The device can be used in a few different modes, two modes are supported:
//
// - D0~D3 as a serial protocol (MPSEE), supporting I²C and SPI (and eventually
// UART), In this mode, D4~D7 and C0~C7 can be used as synchronized GPIO.
//
// - D0~D7 as a synchronous 8 bits bit-bang port. In this mode, only a few pins
// on CBus are usable in slow mode.
//
// Each group of pins D0~D7 and C0~C7 can be changed at once in one pass via
// DBus() or CBus().
//
// This enables usage as an 8 bit parallel port.
//
// Pins C8 and C9 can only be used in 'slow' mode via EEPROM and are currently
// not implemented.
//
// Datasheet
//
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf
type FT232H struct {
generic
D0 gpio.PinIO // Clock output
D1 gpio.PinIO // Data out
D2 gpio.PinIO // Data in
D3 gpio.PinIO // Chip select
D4 gpio.PinIO
D5 gpio.PinIO
D6 gpio.PinIO
D7 gpio.PinIO
C0 gpio.PinIO
C1 gpio.PinIO
C2 gpio.PinIO
C3 gpio.PinIO
C4 gpio.PinIO
C5 gpio.PinIO
C6 gpio.PinIO
C7 gpio.PinIO
C8 gpio.PinIO // Not implemented
C9 gpio.PinIO // Not implemented
hdr [18]gpio.PinIO
cbus gpiosMPSSE
dbus gpiosMPSSE
c8 invalidPin // gpio.PullUp
c9 invalidPin // gpio.PullUp
mu sync.Mutex
usingI2C bool
usingSPI bool
i i2cBus
s spiMPSEEPort
// TODO(maruel): Technically speaking, a SPI port could be hacked up too in
// sync bit-bang but there's less point when MPSEE is available.
}
// Header returns the GPIO pins exposed on the chip.
func (f *FT232H) Header() []gpio.PinIO {
out := make([]gpio.PinIO, len(f.hdr))
copy(out, f.hdr[:])
return out
}
func (f *FT232H) SetSpeed(freq physic.Frequency) error {
// TODO(maruel): When using MPSEE, use the MPSEE command. If using sync
// bit-bang, use SetBaudRate().
// TODO(maruel): Doc says the actual speed is 16x, confirm.
return f.h.SetBaudRate(freq)
}
// CBus sets the values of C0 to C7 in the specified direction and value.
//
// 0 direction means input, 1 means output.
func (f *FT232H) CBus(direction, value byte) error {
return f.h.MPSSECBus(direction, value)
}
// DBus sets the values of D0 to d7 in the specified direction and value.
//
// 0 direction means input, 1 means output.
//
// This function must be used to set Clock idle level.
func (f *FT232H) DBus(direction, value byte) error {
return f.h.MPSSEDBus(direction, value)
}
// CBusRead reads the values of C0 to C7.
func (f *FT232H) CBusRead() (byte, error) {
return f.h.MPSSECBusRead()
}
// DBusRead reads the values of D0 to D7.
func (f *FT232H) DBusRead() (byte, error) {
return f.h.MPSSEDBusRead()
}
// I2C returns an I²C bus over the AD bus.
//
// pull can be either gpio.PullUp or gpio.Float. The recommended pull up
// resistors are 10kΩ for 100kHz and 2kΩ for 400kHz when using Float. The
// GPIO's pull up is 75kΩ, which may require using a lower speed for signal
// reliability. Optimal pull up resistor calculation depends on the capacitance.
//
// It uses D0, D1 and D2.
//
// D0 is SCL. It must to be pulled up externally.
//
// D1 and D2 are used for SDA. D1 is the output using open drain, D2 is the
// input. D1 and D2 must be wired together and must be pulled up externally.
//
// It is recommended to set the mode to 245 FIFO in the EEPROM of the FT232H.
//
// The FIFO mode is recommended because it allows the ADbus lines to start as
// tristate. If the chip starts in the default UART mode, then the ADbus lines
// will be in the default UART idle states until the application opens the port
// and configures it as MPSSE. Care should also be taken that the RD# input on
// ACBUS is not asserted in this initial state as this can cause the FIFO lines
// to drive out.
func (f *FT232H) I2C(pull gpio.Pull) (i2c.BusCloser, error) {
if pull != gpio.PullUp && pull != gpio.Float {
return nil, errors.New("d2xx: I²C pull can only be PullUp or Float")
}
f.mu.Lock()
defer f.mu.Unlock()
if f.usingI2C {
return nil, errors.New("d2xx: already using I²C")
}
if f.usingSPI {
return nil, errors.New("d2xx: already using SPI")
}
if err := f.i.setupI2C(pull == gpio.PullUp); err != nil {
_ = f.i.stopI2C()
return nil, err
}
return &f.i, nil
}
// SPI returns a SPI port over the AD bus.
//
// It uses D0, D1, D2 and D3. D0 is the clock, D1 the output (MOSI), D2 is the
// input (MISO) and D3 is CS line.
func (f *FT232H) SPI() (spi.PortCloser, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.usingI2C {
return nil, errors.New("d2xx: already using I²C")
}
if f.usingSPI {
return nil, errors.New("d2xx: already using SPI")
}
// Don't mark it as being used yet. It only become used once Connect() is
// called.
return &f.s, nil
}
//
func newFT232R(g generic) (*FT232R, error) {
f := &FT232R{
generic: g,
dbus: [...]dbusPinSync{{num: 0}, {num: 1}, {num: 2}, {num: 3}, {num: 4}, {num: 5}, {num: 6}, {num: 7}},
cbus: [...]cbusPin{{num: 8, p: gpio.PullUp}, {num: 9, p: gpio.PullUp}, {num: 10, p: gpio.PullUp}, {num: 11, p: gpio.Float}},
}
// Use the UART names, as this is how all FT232R boards are marked.
dnames := [...]string{"TX", "RX", "RTS", "CTS", "DTR", "DSR", "DCD", "RI"}
for i := range f.dbus {
f.dbus[i].n = f.name + "." + dnames[i]
f.dbus[i].bus = f
f.hdr[i] = &f.dbus[i]
}
for i := range f.cbus {
f.cbus[i].n = f.name + ".C" + strconv.Itoa(i)
f.cbus[i].bus = f
f.hdr[i+8] = &f.cbus[i]
}
f.D0 = f.hdr[0]
f.D1 = f.hdr[1]
f.D2 = f.hdr[2]
f.D3 = f.hdr[3]
f.D4 = f.hdr[4]
f.D5 = f.hdr[5]
f.D6 = f.hdr[6]
f.D7 = f.hdr[7]
f.TX = f.hdr[0]
f.RX = f.hdr[1]
f.RTS = f.hdr[2]
f.CTS = f.hdr[3]
f.DTR = f.hdr[4]
f.DSR = f.hdr[5]
f.DCD = f.hdr[6]
f.RI = f.hdr[7]
f.C0 = f.hdr[8]
f.C1 = f.hdr[9]
f.C2 = f.hdr[10]
f.C3 = f.hdr[11]
// Default to 3MHz.
if err := f.h.SetBaudRate(3 * physic.MegaHertz); err != nil {
return nil, err
}
// Set all CBus pins as input.
if err := f.h.SetBitMode(0, bitModeCbusBitbang); err != nil {
return nil, err
}
// And read their value.
// TODO(maruel): Sadly this is impossible to know which pin is input or
// output, but we could try to guess, as the call above may generate noise on
// the line which could interfere with the device connected.
var err error
if f.cbusnibble, err = f.h.GetBitMode(); err != nil {
return nil, err
}
// Set all DBus as asynchronous bitbang, everything as input.
if err := f.h.SetBitMode(0, bitModeAsyncBitbang); err != nil {
return nil, err
}
// And read their value.
var b [1]byte
if _, err := f.h.ReadAll(context.Background(), b[:]); err != nil {
return nil, err
}
f.dvalue = b[0]
f.s.c.f = f
return f, nil
}
// FT232R represents a FT232RL/FT232RQ device.
//
// It implements Dev.
//
// Not all pins may be physically connected on the header!
//
// Adafruit's version only has the following pins connected: RX, TX, RTS and
// CTS.
//
// SparkFun's version exports all pins *except* (inexplicably) the CBus ones.
//
// The FT232R has 128 bytes output buffer and 256 bytes input buffer.
//
// Pin C4 can only be used in 'slow' mode via EEPROM and is currently not
// implemented.
//
// Datasheet
//
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
type FT232R struct {
generic
// Pin and their alias to the Dn pins for user convenience. Each pair points
// to the exact same pin.
D0, TX gpio.PinIO // Transmit; SPI_MOSI
D1, RX gpio.PinIO // Receive; SPI_MISO
D2, RTS gpio.PinIO // Request To Send Control Output / Handshake signal; SPI_CLK
D3, CTS gpio.PinIO // Clear to Send Control input / Handshake signal; SPI_CS
D4, DTR gpio.PinIO // Data Terminal Ready Control Output / Handshake signal
D5, DSR gpio.PinIO // Data Set Ready Control Input / Handshake signal
D6, DCD gpio.PinIO // Data Carrier Detect Control input
D7, RI gpio.PinIO // Ring Indicator Control Input. When remote wake up is enabled in the internal EEPROM taking RI# low can be used to resume the PC USB host controller from suspend.
// The CBus pins are slower to use, but can drive an high load, like a LED.
C0 gpio.PinIO
C1 gpio.PinIO
C2 gpio.PinIO
C3 gpio.PinIO
dbus [8]dbusPinSync
cbus [4]cbusPin
hdr [12]gpio.PinIO
// Mutable.
mu sync.Mutex
usingSPI bool
usingCBus bool
s spiSyncPort
dmask uint8 // 0 input, 1 output
dvalue uint8
cbusnibble uint8 // upper nibble is I/O control, lower nibble is values.
}
// Header returns the GPIO pins exposed on the chip.
func (f *FT232R) Header() []gpio.PinIO {
out := make([]gpio.PinIO, len(f.hdr))
copy(out, f.hdr[:])
return out
}
// SetDBusMask sets all D0~D7 input or output mode at once.
//
// mask is the input/output pins to use. A bit value of 0 sets the
// corresponding pin to an input, a bit value of 1 sets the corresponding pin
// to an output.
//
// It should be called before calling Tx().
func (f *FT232R) SetDBusMask(mask uint8) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.usingSPI {
return errors.New("d2xx: already using SPI")
}
return f.setDBusMaskLocked(mask)
}
// Tx does synchronized read-then-write on all the D0~D7 GPIOs.
//
// SetSpeed() determines the pace at which the I/O is done.
//
// SetDBusMask() determines which bits are interpreted in the w and r byte
// slice. w has its significant value masked by 'mask' and r has its
// significant value masked by '^mask'.
//
// Input sample is done *before* updating outputs. So r[0] is sampled before
// w[0] is used. The last w byte should be duplicated if an addition read is
// desired.
//
// On the Adafruit cable, only the first 4 bits D0(TX), D1(RX), D2(RTS) and
// D3(CTS) are connected. This is just enough to create a full duplex SPI bus!
func (f *FT232R) Tx(w, r []byte) error {
if len(w) != 0 {
if len(r) != 0 && len(w) != len(r) {
return errors.New("d2xx: length of buffer w and r must match")
}
} else if len(r) == 0 {
return errors.New("d2xx: at least one of w or r must be passed")
}
f.mu.Lock()
defer f.mu.Unlock()
if f.usingSPI {
return errors.New("d2xx: already using SPI")
}
return f.txLocked(w, r)
}
// SPI returns a SPI port over the first 4 pins.
//
// It uses D0(TX), D1(RX), D2(RTS) and D3(CTS). D2(RTS) is the clock, D0(TX)
// the output (MOSI), D1(RX) is the input (MISO) and D3(CTS) is CS line.
func (f *FT232R) SPI() (spi.PortCloser, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.usingSPI {
return nil, errors.New("d2xx: already using SPI")
}
// Don't mark it as being used yet. It only become used once Connect() is
// called.
return &f.s, nil
}
// setDBusMaskLocked is the locked version of SetDBusMask.
func (f *FT232R) setDBusMaskLocked(mask uint8) error {
if mask != f.dmask {
if err := f.h.SetBitMode(mask, bitModeAsyncBitbang); err != nil {
return err
}
f.dmask = mask
}
return nil
}
func (f *FT232R) txLocked(w, r []byte) error {
// Investigate FT232R clock issue:
// http://developer.intra2net.com/mailarchive/html/libftdi/2010/msg00240.html
// The FT232R has 128 bytes TX buffer and 256 bytes RX buffer. Chunk into 64
// bytes chunks. That's half the buffer size of the TX buffer and permits
// pipelining and removes the risk of buffer overrun. This is important
// otherwise there's huge gaps due to the USB transmit overhead.
// TODO(maruel): Determine what's optimal via experimentation.
chunk := 64
var scratch [128]byte
if len(w) == 0 {
// Read only.
for i := range scratch {
scratch[i] = f.dvalue
}
for len(r) != 0 {
// TODO(maruel): Optimize.
c := len(r)
if c > chunk {
c = chunk
}
if _, err := f.h.Write(scratch[:c]); err != nil {
return err
}
if _, err := f.h.ReadAll(context.Background(), r[:c]); err != nil {
return err
}
r = r[c:]
}
} else if len(r) == 0 {
// Write only.
// The first write is 128 bytes to fill the buffer.
chunk = 128
for len(w) != 0 {
c := len(w)
if c > chunk {
c = chunk
}
if _, err := f.h.Write(w[:c]); err != nil {
return err
}
w = w[c:]
chunk = 64
}
/*
// Let the USB drive pace it.
if _, err := f.h.Write(w); err != nil {
return err
}
*/
} else {
// R/W.
// Always write one 'w' ahead.
// The first write is 128 bytes to fill the buffer.
chunk = 128
cw := len(w)
if cw > chunk {
cw = chunk
}
if _, err := f.h.Write(w[:cw]); err != nil {
return err
}
w = w[cw:]
chunk = 64
for len(r) != 0 {
// Read then write.
cr := len(r)
if cr > chunk {
cr = chunk
}
if _, err := f.h.ReadAll(context.Background(), r[:cr]); err != nil {
return err
}
r = r[cr:]
cw = len(w)
if cw > chunk {
cw = chunk
}
if cw != 0 {
if _, err := f.h.Write(w[:cw]); err != nil {
return err
}
w = w[cw:]
}
}
}
return nil
}
// dbusSyncGPIOFunc implements dbusSync. It returns the function of a GPIO
// pin.
func (f *FT232R) dbusSyncGPIOFunc(n int) string {
f.mu.Lock()
defer f.mu.Unlock()
if f.usingSPI {
switch n {
case 0:
return "SPI_MOSI" // TX
case 1:
return "SPI_MISO" // RX
case 2:
return "SPI_CLK" // RTS
case 3:
return "SPI_CS" // CTS
}
}
mask := uint8(1 << uint(n))
if f.dmask&mask != 0 {
return "Out/" + gpio.Level(f.dvalue&mask != 0).String()
}
return "In/" + f.dbusSyncReadLocked(n).String()
}
// dbusSyncGPIOIn implements dbusSync.
func (f *FT232R) dbusSyncGPIOIn(n int) error {
f.mu.Lock()
defer f.mu.Unlock()
// TODO(maruel): if f.usingSPI && n < 4.
mask := uint8(1 << uint(n))
if f.dmask&mask == 0 {
// Already input.
return nil
}
v := f.dmask &^ mask
if err := f.h.SetBitMode(v, bitModeAsyncBitbang); err != nil {
return err
}
f.dmask = v
return nil
}
// dbusSyncGPIORead implements dbusSync.
func (f *FT232R) dbusSyncGPIORead(n int) gpio.Level {
f.mu.Lock()
defer f.mu.Unlock()
return f.dbusSyncReadLocked(n)
}
func (f *FT232R) dbusSyncReadLocked(n int) gpio.Level {
// In synchronous mode, to read we must write first to for a sample.
b := [1]byte{f.dvalue}
if _, err := f.h.Write(b[:]); err != nil {
return gpio.Low
}
mask := uint8(1 << uint(n))
if _, err := f.h.ReadAll(context.Background(), b[:]); err != nil {
return gpio.Low
}
f.dvalue = b[0]
return f.dvalue&mask != 0
}
// dbusSyncGPIOOut implements dbusSync.
func (f *FT232R) dbusSyncGPIOOut(n int, l gpio.Level) error {
f.mu.Lock()
defer f.mu.Unlock()
mask := uint8(1 << uint(n))
if f.dmask&mask != 1 {
// Was input.
v := f.dmask | mask
if err := f.h.SetBitMode(v, bitModeAsyncBitbang); err != nil {
return err
}
f.dmask = v
}
return f.dbusSyncGPIOOutLocked(n, l)
}
func (f *FT232R) dbusSyncGPIOOutLocked(n int, l gpio.Level) error {
b := [1]byte{f.dvalue}
if _, err := f.h.Write(b[:]); err != nil {
return err
}
f.dvalue = b[0]
// In synchronous mode, we must read after writing to flush the buffer.
if _, err := f.h.Write(b[:]); err != nil {
return err
}
return nil
}
// cBusGPIOFunc implements cBusGPIO.
func (f *FT232R) cBusGPIOFunc(n int) string {
f.mu.Lock()
defer f.mu.Unlock()
fmask := uint8(0x10 << uint(n))
vmask := uint8(1 << uint(n))
if f.cbusnibble&fmask != 0 {
return "Out/" + gpio.Level(f.cbusnibble&vmask != 0).String()
}
return "In/" + f.cBusReadLocked(n).String()
}
// cBusGPIOIn implements cBusGPIO.
func (f *FT232R) cBusGPIOIn(n int) error {
f.mu.Lock()
defer f.mu.Unlock()
fmask := uint8(0x10 << uint(n))
if f.cbusnibble&fmask == 0 {
// Already input.
return nil
}
v := f.cbusnibble &^ fmask
if err := f.h.SetBitMode(v, bitModeCbusBitbang); err != nil {
return err
}
f.cbusnibble = v
return nil
}
// cBusGPIORead implements cBusGPIO.
func (f *FT232R) cBusGPIORead(n int) gpio.Level {
f.mu.Lock()
defer f.mu.Unlock()
return f.cBusReadLocked(n)
}
func (f *FT232R) cBusReadLocked(n int) gpio.Level {
v, err := f.h.GetBitMode()
if err != nil {
return gpio.Low
}
f.cbusnibble = v
vmask := uint8(1 << uint(n))
return f.cbusnibble&vmask != 0
}
// cBusGPIOOut implements cBusGPIO.
func (f *FT232R) cBusGPIOOut(n int, l gpio.Level) error {
f.mu.Lock()
defer f.mu.Unlock()
fmask := uint8(0x10 << uint(n))
vmask := uint8(1 << uint(n))
v := f.cbusnibble | fmask
if l {
v |= vmask
} else {
v &^= vmask
}
if f.cbusnibble == v {
// Was already in the right mode.
return nil
}
if err := f.h.SetBitMode(v, bitModeCbusBitbang); err != nil {
return err
}
f.cbusnibble = v
return nil
}
//
var _ conn.Resource = Dev(nil)

View File

@@ -1,20 +0,0 @@
// 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.
// Package ftdi implements support for popular FTDI devices.
//
// The supported devices (FT232h/FT232r) implement support for various
// protocols like the GPIO, I²C, SPI, UART, JTAG.
//
// More details
//
// See https://periph.io/device/ftdi/ for more details, and how to configure
// the host to be able to use this driver.
//
// Datasheets
//
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
//
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf
package ftdi

View File

@@ -1,213 +0,0 @@
// Copyright 2018 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 ftdi
import (
"strconv"
"sync"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/pin"
"periph.io/x/conn/v3/pin/pinreg"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/d2xx"
)
// All enumerates all the connected FTDI devices.
func All() []Dev {
drv.mu.Lock()
defer drv.mu.Unlock()
out := make([]Dev, len(drv.all))
copy(out, drv.all)
return out
}
//
// open opens a FTDI device.
//
// Must be called with mu held.
func open(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (Dev, error) {
h, err := openHandle(opener, i)
if err != nil {
return nil, err
}
if err := h.Init(); err != nil {
// setupCommon() takes the device in its previous state. It could be in an
// unexpected state, so try resetting it first.
if err := h.Reset(); err != nil {
_ = h.Close()
return nil, err
}
if err := h.Init(); err != nil {
_ = h.Close()
return nil, err
}
// The second attempt worked.
}
// Makes a copy of the handle.
g := generic{index: i, h: h, name: h.t.String()}
if i > 0 {
// When more than one device is present, add "(index)" suffix.
// TODO(maruel): Using the serial number would be nicer than a number.
g.name += "(" + strconv.Itoa(i) + ")"
}
// Makes a copy of the generic instance.
switch g.h.t {
case DevTypeFT232H:
f, err := newFT232H(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
case DevTypeFT2232H:
f, err := newFT232H(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
case DevTypeFT232R:
f, err := newFT232R(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
default:
return &g, nil
}
}
// registerDev registers the header and supported buses and ports in the
// relevant registries.
func registerDev(d Dev, multi bool) error {
name := d.String()
hdr := d.Header()
// Register the GPIOs.
for _, p := range hdr {
if err := gpioreg.Register(p); err != nil {
return err
}
}
if !multi {
// Register shorthands.
// The "." used here vs the "_" used in pinreg is unfortunate. Investigate
// a better way.
prefix := len(name) + 1
for _, p := range hdr {
n := p.Name()
if err := gpioreg.RegisterAlias(n[prefix:], n); err != nil {
return err
}
}
}
// Register the header.
raw := make([][]pin.Pin, len(hdr))
for i := range hdr {
raw[i] = []pin.Pin{hdr[i]}
}
if err := pinreg.Register(name, raw); err != nil {
return err
}
switch t := d.(type) {
case *FT232H:
// Register I²C without pull up.
if err := i2creg.Register(name, nil, -1, func() (i2c.BusCloser, error) { return t.I2C(gpio.Float) }); err != nil {
return err
}
if err := spireg.Register(name, nil, -1, t.SPI); err != nil {
return err
}
// TODO(maruel): UART
case *FT232R:
// TODO(maruel): SPI, UART
}
return nil
}
// driver implements driver.Impl.
type driver struct {
mu sync.Mutex
all []Dev
d2xxOpen func(i int) (d2xx.Handle, d2xx.Err)
numDevices func() (int, error)
}
func (d *driver) String() string {
return "ftdi"
}
func (d *driver) Prerequisites() []string {
return nil
}
func (d *driver) After() []string {
return nil
}
func (d *driver) Init() (bool, error) {
num, err := d.numDevices()
if err != nil {
return true, err
}
multi := num > 1
for i := 0; i < num; i++ {
// TODO(maruel): Close the device one day. :)
if dev, err1 := open(d.d2xxOpen, i); err1 == nil {
d.all = append(d.all, dev)
if err = registerDev(dev, multi); err != nil {
return true, err
}
} else {
// Create a shallow broken handle, so the user can learn how to fix the
// problem.
//
// TODO(maruel): On macOS with a FT232R, calling two processes in a row
// often results in a broken device on the second process. Figure out why
// and make it more resilient.
err = err1
// The serial number is not available so what can be listed is limited.
// TODO(maruel): Add VID/PID?
name := "broken#" + strconv.Itoa(i) + ": " + err.Error()
d.all = append(d.all, &broken{index: i, err: err, name: name})
}
}
return true, err
}
func (d *driver) reset() {
d.mu.Lock()
defer d.mu.Unlock()
d.all = nil
// open is mocked in tests.
d.d2xxOpen = d2xx.Open
// numDevices is mocked in tests.
d.numDevices = numDevices
// The d2xx can hang for up to the timeout under certain circumstances and the
// Go profiler is not very useful to find the source, so use manual logging
// to see where time it spent.
//d.d2xxOpen = func(i int) (d2xx.Handle, int) {
// h, e := d2xxOpen(i)
// return &d2xxLoggingHandle{h}, e
//}
}
func init() {
if d2xx.Available {
drv.reset()
driverreg.MustRegister(&drv)
}
}
var drv driver

View File

@@ -1,368 +0,0 @@
// Copyright 2018 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 ftdi
import (
"errors"
"fmt"
"unsafe"
)
// EEPROM is the unprocessed EEPROM content.
//
// The EEPROM is in 3 parts: the defined struct, the 4 strings and the rest
// which is used as an 'user area'. The size of the user area depends on the
// length of the strings. The user area content is not included in this struct.
type EEPROM struct {
// Raw is the raw EEPROM content. It excludes the strings.
Raw []byte
// The following condition must be true: len(Manufacturer) + len(Desc) <= 40.
Manufacturer string
ManufacturerID string
Desc string
Serial string
}
// Validate checks that the data is good.
func (e *EEPROM) Validate() error {
// Verify that the values are set correctly.
if len(e.Manufacturer) > 40 {
return errors.New("ftdi: Manufacturer is too long")
}
if len(e.ManufacturerID) > 40 {
return errors.New("ftdi: ManufacturerID is too long")
}
if len(e.Desc) > 40 {
return errors.New("ftdi: Desc is too long")
}
if len(e.Serial) > 40 {
return errors.New("ftdi: Serial is too long")
}
if len(e.Manufacturer)+len(e.Desc) > 40 {
return errors.New("ftdi: length of Manufacturer plus Desc is too long")
}
return nil
}
func (e *EEPROM) AsHeader() *EEPROMHeader {
// sizeof(EEPROMHeader)
if len(e.Raw) < 16 {
return nil
}
return (*EEPROMHeader)(unsafe.Pointer(&e.Raw[0]))
}
// AsFT232H returns the Raw data aliased as EEPROMFT232H.
func (e *EEPROM) AsFT232H() *EEPROMFT232H {
// sizeof(EEPROMFT232H)
if len(e.Raw) < 44 {
return nil
}
return (*EEPROMFT232H)(unsafe.Pointer(&e.Raw[0]))
}
// AsFT2232H returns the Raw data aliased as EEPROMFT2232H.
func (e *EEPROM) AsFT2232H() *EEPROMFT2232H {
// sizeof(EEPROMFT2232H)
if len(e.Raw) < 40 {
return nil
}
return (*EEPROMFT2232H)(unsafe.Pointer(&e.Raw[0]))
}
// AsFT232R returns the Raw data aliased as EEPROMFT232R.
func (e *EEPROM) AsFT232R() *EEPROMFT232R {
// sizeof(EEPROMFT232R)
if len(e.Raw) < 32 {
return nil
}
return (*EEPROMFT232R)(unsafe.Pointer(&e.Raw[0]))
}
// FT232hCBusMux is stored in the FT232H EEPROM to control each CBus pin.
type FT232hCBusMux uint8
const (
// TriSt-PU; Sets in Tristate (pull up) (C0~C6, C8, C9) on 75kΩ.
FT232hCBusTristatePullUp FT232hCBusMux = 0x00
// TXLED#; Pulses low when transmitting data (C0~C6, C8, C9).
FT232hCBusTxLED FT232hCBusMux = 0x01
// RXLED#; Pulses low when receiving data (C0~C6, C8, C9).
FT232hCBusRxLED FT232hCBusMux = 0x02
// TX&RXLED#; Pulses low when either receiving or transmitting data (C0~C6,
// C8, C9).
FT232hCBusTxRxLED FT232hCBusMux = 0x03
// PWREN#; Output is low after the device has been configured by USB, then
// high during USB suspend mode (C0~C6, C8, C9).
//
// Must be used with an external 10kΩ pull up.
FT232hCBusPwrEnable FT232hCBusMux = 0x04
// SLEEP#; Goes low during USB suspend mode (C0~C6, C8, C9).
FT232hCBusSleep FT232hCBusMux = 0x05
// DRIVE1; Drives pin to logic 0 (C0~C6, C8, C9).
FT232hCBusDrive0 FT232hCBusMux = 0x06
// DRIVE1; Drives pin to logic 1 (C0, C5, C6, C8, C9).
FT232hCBusDrive1 FT232hCBusMux = 0x07
// I/O Mode; CBus bit-bang mode option (C5, C6, C8, C9).
FT232hCBusIOMode FT232hCBusMux = 0x08
// TXDEN; Tx Data Enable. Used with RS485 level converters to enable the line
// driver during data transmit. It is active one bit time before the start
// bit up to until the end of the stop bit (C0~C6, C8, C9).
FT232hCBusTxdEnable FT232hCBusMux = 0x09
// CLK30 30MHz clock output (C0, C5, C6, C8, C9).
FT232hCBusClk30 FT232hCBusMux = 0x0A
// CLK15 15MHz clock output (C0, C5, C6, C8, C9).
FT232hCBusClk15 FT232hCBusMux = 0x0B
// CLK7.5 7.5MHz clock output (C0, C5, C6, C8, C9).
FT232hCBusClk7_5 FT232hCBusMux = 0x0C
)
const ft232hCBusMuxName = "FT232hCBusTristatePullUpFT232hCBusTxLEDFT232hCBusRxLEDFT232hCBusTxRxLEDFT232hCBusPwrEnableFT232hCBusSleepFT232hCBusDrive0FT232hCBusDrive1FT232hCBusIOModeFT232hCBusTxdEnableFT232hCBusClk30FT232hCBusClk15FT232hCBusClk7_5"
var fr232hCBusMuxIndex = [...]uint8{0, 24, 39, 54, 71, 90, 105, 121, 137, 153, 172, 187, 202, 218}
func (f FT232hCBusMux) String() string {
if f >= FT232hCBusMux(len(fr232hCBusMuxIndex)-1) {
return fmt.Sprintf("FT232hCBusMux(%d)", f)
}
return ft232hCBusMuxName[fr232hCBusMuxIndex[f]:fr232hCBusMuxIndex[f+1]]
}
// FT232rCBusMux is stored in the FT232R EEPROM to control each CBus pin.
type FT232rCBusMux uint8
const (
// TXDEN; Tx Data Enable. Used with RS485 level converters to enable the line
// driver during data transmit. It is active one bit time before the start
// bit up to until the end of the stop bit (C0~C4).
FT232rCBusTxdEnable FT232rCBusMux = 0x00
// PWREN#; Output is low after the device has been configured by USB, then
// high during USB suspend mode (C0~C4).
//
// Must be used with an external 10kΩ pull up.
FT232rCBusPwrEnable FT232rCBusMux = 0x01
// RXLED#; Pulses low when receiving data (C0~C4).
FT232rCBusRxLED FT232rCBusMux = 0x02
// TXLED#; Pulses low when transmitting data (C0~C4).
FT232rCBusTxLED FT232rCBusMux = 0x03
// TX&RXLED#; Pulses low when either receiving or transmitting data (C0~C4).
FT232rCBusTxRxLED FT232rCBusMux = 0x04
// SLEEP# Goes low during USB suspend mode (C0~C4).
FT232rCBusSleep FT232rCBusMux = 0x05
// CLK48 48Mhz +/-0.7% clock output (C0~C4).
FT232rCBusClk48 FT232rCBusMux = 0x06
// CLK24 24Mhz clock output (C0~C4).
FT232rCBusClk24 FT232rCBusMux = 0x07
// CLK12 12Mhz clock output (C0~C4).
FT232rCBusClk12 FT232rCBusMux = 0x08
// CLK6 6Mhz +/-0.7% clock output (C0~C4).
FT232rCBusClk6 FT232rCBusMux = 0x09
// CBitBangI/O; CBus bit-bang mode option (C0~C3).
FT232rCBusIOMode FT232rCBusMux = 0x0A
// BitBangWRn; CBus WR# strobe output (C0~C3).
FT232rCBusBitBangWR FT232rCBusMux = 0x0B
// BitBangRDn; CBus RD# strobe output (C0~C3).
FT232rCBusBitBangRD FT232rCBusMux = 0x0C
)
const ft232rCBusMuxName = "FT232rCBusTxdEnableFT232rCBusPwrEnableFT232rCBusRxLEDFT232rCBusTxLEDFT232rCBusTxRxLEDFT232rCBusSleepFT232rCBusClk48FT232rCBusClk24FT232rCBusClk12FT232rCBusClk6FT232rCBusIOModeFT232rCBusBitBangWRFT232rCBusBitBangRD"
var ft232rCBusMuxIndex = [...]uint8{0, 19, 38, 53, 68, 85, 100, 115, 130, 145, 159, 175, 194, 213}
func (f FT232rCBusMux) String() string {
if f >= FT232rCBusMux(len(ft232rCBusMuxIndex)-1) {
return fmt.Sprintf("FT232rCBusMux(%d)", f)
}
return ft232rCBusMuxName[ft232rCBusMuxIndex[f]:ft232rCBusMuxIndex[f+1]]
}
// EEPROMHeader is the common header found on FTDI devices.
//
// It is 16 bytes long.
type EEPROMHeader struct {
DeviceType DevType // 0x00 FTxxxx device type to be programmed
VendorID uint16 // 0x04 Defaults to 0x0403; can be changed
ProductID uint16 // 0x06 Defaults to 0x6001 for FT232R, 0x6014 for FT232H, relevant value
SerNumEnable uint8 // 0x07 bool Non-zero if serial number to be used
Unused0 uint8 // 0x08 For alignment.
MaxPower uint16 // 0x0A 0mA < MaxPower <= 500mA
SelfPowered uint8 // 0x0C bool 0 = bus powered, 1 = self powered
RemoteWakeup uint8 // 0x0D bool 0 = not capable, 1 = capable; RI# low will wake host in 20ms.
PullDownEnable uint8 // 0x0E bool Non zero if pull down in suspend enabled
Unused1 uint8 // 0x0F For alignment.
}
// EEPROMFT232H is the EEPROM layout of a FT232H device.
//
// It is 44 bytes long.
type EEPROMFT232H struct {
EEPROMHeader
// FT232H specific.
ACSlowSlew uint8 // 0x10 bool Non-zero if AC bus pins have slow slew
ACSchmittInput uint8 // 0x11 bool Non-zero if AC bus pins are Schmitt input
ACDriveCurrent uint8 // 0x12 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
ADSlowSlew uint8 // 0x13 bool Non-zero if AD bus pins have slow slew
ADSchmittInput uint8 // 0x14 bool Non-zero if AD bus pins are Schmitt input
ADDriveCurrent uint8 // 0x15 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
Cbus0 FT232hCBusMux // 0x16
Cbus1 FT232hCBusMux // 0x17
Cbus2 FT232hCBusMux // 0x18
Cbus3 FT232hCBusMux // 0x19
Cbus4 FT232hCBusMux // 0x1A
Cbus5 FT232hCBusMux // 0x1B
Cbus6 FT232hCBusMux // 0x1C
Cbus7 FT232hCBusMux // 0x1D C7 is limited a sit can only do 'suspend on C7 low'. Defaults pull down.
Cbus8 FT232hCBusMux // 0x1E
Cbus9 FT232hCBusMux // 0x1F
FT1248Cpol uint8 // 0x20 bool FT1248 clock polarity - clock idle high (true) or clock idle low (false)
FT1248Lsb uint8 // 0x21 bool FT1248 data is LSB (true), or MSB (false)
FT1248FlowControl uint8 // 0x22 bool FT1248 flow control enable
IsFifo uint8 // 0x23 bool Non-zero if Interface is 245 FIFO
IsFifoTar uint8 // 0x24 bool Non-zero if Interface is 245 FIFO CPU target
IsFastSer uint8 // 0x25 bool Non-zero if Interface is Fast serial
IsFT1248 uint8 // 0x26 bool Non-zero if Interface is FT1248
PowerSaveEnable uint8 // 0x27 bool Suspect on ACBus7 low.
DriverType uint8 // 0x28 bool 0 is D2XX, 1 is VCP
Unused2 uint8 // 0x29
Unused3 uint16 // 0x30
}
func (e *EEPROMFT232H) Defaults() {
// As found on Adafruit device.
e.ACDriveCurrent = 4
e.ADDriveCurrent = 4
e.Cbus0 = FT232hCBusTristatePullUp
e.Cbus1 = FT232hCBusTristatePullUp
e.Cbus2 = FT232hCBusTristatePullUp
e.Cbus3 = FT232hCBusTristatePullUp
e.Cbus4 = FT232hCBusTristatePullUp
e.Cbus5 = FT232hCBusTristatePullUp
e.Cbus6 = FT232hCBusTristatePullUp
e.Cbus7 = FT232hCBusTristatePullUp
e.Cbus8 = FT232hCBusDrive1
e.Cbus9 = FT232hCBusDrive0
}
// EEPROMFT2232H is the EEPROM layout of a FT2232H device.
//
// It is 40 bytes long.
type EEPROMFT2232H struct {
EEPROMHeader
// FT232H specific.
ALSlowSlew uint8 // 0x10 bool non-zero if AL pins have slow slew
ALSchmittInput uint8 // 0x11 bool non-zero if AL pins are Schmitt input
ALDriveCurrent uint8 // 0x12 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
AHSlowSlew uint8 // 0x13 bool non-zero if AH pins have slow slew
AHSchmittInput uint8 // 0x14 bool non-zero if AH pins are Schmitt input
AHDriveCurrent uint8 // 0x15 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
BLSlowSlew uint8 // 0x16 bool non-zero if BL pins have slow slew
BLSchmittInput uint8 // 0x17 bool non-zero if BL pins are Schmitt input
BLDriveCurrent uint8 // 0x18 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
BHSlowSlew uint8 // 0x19 bool non-zero if BH pins have slow slew
BHSchmittInput uint8 // 0x1A bool non-zero if BH pins are Schmitt input
BHDriveCurrent uint8 // 0x1B Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
AIsFifo uint8 // 0x1C bool non-zero if interface is 245 FIFO
AIsFifoTar uint8 // 0x1D bool non-zero if interface is 245 FIFO CPU target
AIsFastSer uint8 // 0x1E bool non-zero if interface is Fast serial
BIsFifo uint8 // 0x1F bool non-zero if interface is 245 FIFO
BIsFifoTar uint8 // 0x20 bool non-zero if interface is 245 FIFO CPU target
BIsFastSer uint8 // 0x21 bool non-zero if interface is Fast serial
PowerSaveEnable uint8 // 0x22 bool non-zero if using BCBUS7 to save power for self-powered designs
ADriverType uint8 // 0x23 bool
BDriverType uint8 // 0x24 bool
Unused2 uint8 // 0x25
Unused3 uint16 // 0x26
}
// EEPROMFT232R is the EEPROM layout of a FT232R device.
//
// It is 32 bytes long.
type EEPROMFT232R struct {
EEPROMHeader
// FT232R specific.
IsHighCurrent uint8 // 0x10 bool High Drive I/Os; 3mA instead of 1mA (@3.3V)
UseExtOsc uint8 // 0x11 bool Use external oscillator
InvertTXD uint8 // 0x12 bool
InvertRXD uint8 // 0x13 bool
InvertRTS uint8 // 0x14 bool
InvertCTS uint8 // 0x15 bool
InvertDTR uint8 // 0x16 bool
InvertDSR uint8 // 0x17 bool
InvertDCD uint8 // 0x18 bool
InvertRI uint8 // 0x19 bool
Cbus0 FT232rCBusMux // 0x1A Default ft232rCBusTxLED
Cbus1 FT232rCBusMux // 0x1B Default ft232rCBusRxLED
Cbus2 FT232rCBusMux // 0x1C Default ft232rCBusTxdEnable
Cbus3 FT232rCBusMux // 0x1D Default ft232rCBusPwrEnable
Cbus4 FT232rCBusMux // 0x1E Default ft232rCBusSleep
DriverType uint8 // 0x1F bool 0 is D2XX, 1 is VCP
}
func (e *EEPROMFT232R) Defaults() {
// As found on Adafruit device.
e.Cbus0 = FT232rCBusTxLED
e.Cbus1 = FT232rCBusRxLED
e.Cbus2 = FT232rCBusTxdEnable
e.Cbus3 = FT232rCBusPwrEnable
e.Cbus4 = FT232rCBusSleep
e.DriverType = 1
}
//
// DevType is the FTDI device type.
type DevType uint32
const (
DevTypeFTBM DevType = iota // 0
DevTypeFTAM
DevTypeFT100AX
DevTypeUnknown // 3
DevTypeFT2232C
DevTypeFT232R // 5
DevTypeFT2232H
DevTypeFT4232H
DevTypeFT232H // 8
DevTypeFTXSeries
DevTypeFT4222H0
DevTypeFT4222H1_2
DevTypeFT4222H3
DevTypeFT4222Prog
DevTypeFT900
DevTypeFT930
DevTypeFTUMFTPD3A
)
// EEPROMSize returns the size of the EEPROM for this device.
func (d DevType) EEPROMSize() int {
switch d {
case DevTypeFT232H:
// sizeof(EEPROMFT232H)
return 44
case DevTypeFT2232H:
// sizeof(EEPROMFT2232H)
return 40
case DevTypeFT232R:
// sizeof(EEPROMFT232R)
return 32
default:
return 256
}
}
const devTypeName = "FTBMFTAMFT100AXUnknownFT2232CFT232RFT2232HFT4232HFT232HFTXSeriesFT4222H0FT4222H1/2FT4222H3FT4222ProgFT900FT930FTUMFTPD3A"
var devTypeIndex = [...]uint8{0, 4, 8, 15, 22, 29, 35, 42, 49, 55, 64, 72, 82, 90, 100, 105, 110, 120}
func (d DevType) String() string {
if d >= DevType(len(devTypeIndex)-1) {
d = DevTypeUnknown
}
return devTypeName[devTypeIndex[d]:devTypeIndex[d+1]]
}

View File

@@ -1,5 +0,0 @@
// Copyright 2021 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 ftdi

View File

@@ -1,241 +0,0 @@
// 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.
// Emulate independent GPIOs.
package ftdi
import (
"errors"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
)
// dbusSync is the handler of a synchronous bitbang on DBus.
//
// More details at:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_232R-01_Bit_Bang_Mode_Available_For_FT232R_and_Ft245R.pdf
type dbusSync interface {
dbusSyncGPIOFunc(n int) string
dbusSyncGPIOIn(n int) error
dbusSyncGPIORead(n int) gpio.Level
dbusSyncGPIOOut(n int, l gpio.Level) error
}
// dbusPinSync represents a GPIO on a synchronous bitbang DBus.
//
// It is immutable and stateless.
type dbusPinSync struct {
n string
num int
bus dbusSync
}
// String implements conn.Resource.
func (s *dbusPinSync) String() string {
return s.n
}
// Halt implements conn.Resource.
func (s *dbusPinSync) Halt() error {
return nil
}
// Name implements pin.Pin.
func (s *dbusPinSync) Name() string {
return s.n
}
// Number implements pin.Pin.
func (s *dbusPinSync) Number() int {
return s.num
}
// Function implements pin.Pin.
func (s *dbusPinSync) Function() string {
return s.bus.dbusSyncGPIOFunc(s.num)
}
// In implements gpio.PinIn.
func (s *dbusPinSync) In(pull gpio.Pull, e gpio.Edge) error {
if e != gpio.NoEdge {
// We could support it on D5.
return errors.New("d2xx: edge triggering is not supported")
}
if pull != gpio.PullUp && pull != gpio.PullNoChange {
// EEPROM has a PullDownEnable flag.
return errors.New("d2xx: pull is not supported")
}
return s.bus.dbusSyncGPIOIn(s.num)
}
// Read implements gpio.PinIn.
func (s *dbusPinSync) Read() gpio.Level {
return s.bus.dbusSyncGPIORead(s.num)
}
// WaitForEdge implements gpio.PinIn.
func (s *dbusPinSync) WaitForEdge(t time.Duration) bool {
return false
}
// DefaultPull implements gpio.PinIn.
func (s *dbusPinSync) DefaultPull() gpio.Pull {
// 200kΩ
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
// p. 24
return gpio.PullUp
}
// Pull implements gpio.PinIn.
func (s *dbusPinSync) Pull() gpio.Pull {
return gpio.PullUp
}
// Out implements gpio.PinOut.
func (s *dbusPinSync) Out(l gpio.Level) error {
return s.bus.dbusSyncGPIOOut(s.num, l)
}
// PWM implements gpio.PinOut.
func (s *dbusPinSync) PWM(d gpio.Duty, f physic.Frequency) error {
return errors.New("d2xx: not implemented")
}
/*
func (s *dbusPinSync) Drive() physic.ElectricCurrent {
// optionally 3
//return s.bus.ee.DDriveCurrent * physic.MilliAmpere
return physic.MilliAmpere
}
func (s *dbusPinSync) SlewLimit() bool {
//return s.bus.ee.DSlowSlew
return false
}
func (s *dbusPinSync) Hysteresis() bool {
//return s.bus.ee.DSchmittInput
return true
}
*/
//
// cBusGPIO is the handler of a CBus bitbang bus.
//
// This is an asynchronous mode.
//
// More details at:
// http://www.ftdichip.com/Support/Knowledgebase/index.html?cbusbitbangmode.htm
type cBusGPIO interface {
cBusGPIOFunc(n int) string
cBusGPIOIn(n int) error
cBusGPIORead(n int) gpio.Level
cBusGPIOOut(n int, l gpio.Level) error
}
// cbusPin represents a GPIO on a CBus bitbang bus.
//
// It is immutable and stateless.
type cbusPin struct {
n string
num int
p gpio.Pull
bus cBusGPIO
}
// String implements conn.Resource.
func (c *cbusPin) String() string {
return c.n
}
// Halt implements conn.Resource.
func (c *cbusPin) Halt() error {
return nil
}
// Name implements pin.Pin.
func (c *cbusPin) Name() string {
return c.n
}
// Number implements pin.Pin.
func (c *cbusPin) Number() int {
return c.num
}
// Function implements pin.Pin.
func (c *cbusPin) Function() string {
return c.bus.cBusGPIOFunc(c.num)
}
// In implements gpio.PinIn.
func (c *cbusPin) In(pull gpio.Pull, e gpio.Edge) error {
if e != gpio.NoEdge {
// We could support it on D5.
return errors.New("d2xx: edge triggering is not supported")
}
if pull != c.p && pull != gpio.PullNoChange {
// EEPROM has a PullDownEnable flag.
return errors.New("d2xx: pull is not supported")
}
return c.bus.cBusGPIOIn(c.num)
}
// Read implements gpio.PinIn.
func (c *cbusPin) Read() gpio.Level {
return c.bus.cBusGPIORead(c.num)
}
// WaitForEdge implements gpio.PinIn.
func (c *cbusPin) WaitForEdge(t time.Duration) bool {
return false
}
// DefaultPull implements gpio.PinIn.
func (c *cbusPin) DefaultPull() gpio.Pull {
// 200kΩ
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
// p. 24
return c.p
}
// Pull implements gpio.PinIn.
func (c *cbusPin) Pull() gpio.Pull {
return c.p
}
// Out implements gpio.PinOut.
func (c *cbusPin) Out(l gpio.Level) error {
return c.bus.cBusGPIOOut(c.num, l)
}
// PWM implements gpio.PinOut.
func (c *cbusPin) PWM(d gpio.Duty, f physic.Frequency) error {
return errors.New("d2xx: not implemented")
}
/*
func (c *cbusPin) Drive() physic.ElectricCurrent {
// optionally 3
//return c.bus.ee.CDriveCurrent * physic.MilliAmpere
return physic.MilliAmpere
}
func (c *cbusPin) SlewLimit() bool {
//return c.bus.ee.CSlowSlew
return false
}
func (c *cbusPin) Hysteresis() bool {
//return c.bus.ee.CSchmittInput
return true
}
*/
var _ gpio.PinIO = &dbusPinSync{}
var _ gpio.PinIO = &cbusPin{}

View File

@@ -1,382 +0,0 @@
// Copyright 2021 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 ftdi
import (
"context"
"errors"
"fmt"
"io"
"periph.io/x/conn/v3/physic"
"periph.io/x/d2xx"
)
//
// bitMode is used by SetBitMode to change the chip behavior.
type bitMode uint8
const (
// Resets all Pins to their default value
bitModeReset bitMode = 0x00
// Sets the DBus to asynchronous bit-bang.
bitModeAsyncBitbang bitMode = 0x01
// Switch to MPSSE mode (FT2232, FT2232H, FT4232H and FT232H).
bitModeMpsse bitMode = 0x02
// Sets the DBus to synchronous bit-bang (FT232R, FT245R, FT2232, FT2232H,
// FT4232H and FT232H).
bitModeSyncBitbang bitMode = 0x04
// Switch to MCU host bus emulation (FT2232, FT2232H, FT4232H and FT232H).
bitModeMcuHost bitMode = 0x08
// Switch to fast opto-isolated serial mode (FT2232, FT2232H, FT4232H and
// FT232H).
bitModeFastSerial bitMode = 0x10
// Sets the CBus in 4 bits bit-bang mode (FT232R and FT232H)
// In this case, upper nibble controls which pin is output/input, lower
// controls which of outputs are high and low.
bitModeCbusBitbang bitMode = 0x20
// Single Channel Synchronous 245 FIFO mode (FT2232H and FT232H).
bitModeSyncFifo bitMode = 0x40
)
// numDevices returns the number of detected devices.
func numDevices() (int, error) {
num, e := d2xx.CreateDeviceInfoList()
if e != 0 {
return 0, toErr("GetNumDevices initialization failed", e)
}
return num, nil
}
func openHandle(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (*handle, error) {
h, e := opener(i)
if e != 0 {
return nil, toErr("Open", e)
}
d := &handle{h: h}
t, vid, did, e := h.GetDeviceInfo()
if e != 0 {
_ = d.Close()
return nil, toErr("GetDeviceInfo", e)
}
d.t = DevType(t)
d.venID = vid
d.devID = did
return d, nil
}
// handle is a thin wrapper around the low level d2xx device handle to make it
// more go-idiomatic.
//
// It also implements many utility functions to help with initialization and
// device management.
type handle struct {
// It is just above 'handle' which directly maps to D2XX function calls.
//
// Dev converts the int error type into Go native error and handles higher
// level functionality like reading and writing to the USB connection.
//
// The content of the struct is immutable after initialization.
h d2xx.Handle
t DevType
venID uint16
devID uint16
}
func (h *handle) Close() error {
// Not yet called.
return toErr("Close", h.h.Close())
}
// Init is the general setup for common devices.
//
// It tries first the 'happy path' which doesn't reset the device. By doing so,
// the goal is to reduce the amount of glitches on the GPIO pins, on a best
// effort basis. On all devices, the GPIOs are still reset as inputs, since
// there is no way to determine if each GPIO is an input or output.
func (h *handle) Init() error {
// Driver: maximum packet size. Note that this clears any data in the buffer,
// so it is good to do it immediately after a reset. The 'out' parameter is
// ignored.
// TODO(maruel): The FT232H doc claims a 512 byte packets support in hi-speed
// mode, which means that this would likely be better to use this value.
if e := h.h.SetUSBParameters(65536, 0); e != 0 {
return toErr("SetUSBParameters", e)
}
// Driver: Set I/O timeouts to 15 sec. The reason is that we want the
// timeouts to be very visible, at least as the driver is being developed.
if e := h.h.SetTimeouts(15000, 15000); e != 0 {
return toErr("SetTimeouts", e)
}
// Not sure: Disable event/error characters.
if e := h.h.SetChars(0, false, 0, false); e != 0 {
return toErr("SetChars", e)
}
// Not sure: Latency timer at 1ms.
if e := h.h.SetLatencyTimer(1); e != 0 {
return toErr("SetLatencyTimer", e)
}
// Not sure: Turn on flow control to synchronize IN requests.
if e := h.h.SetFlowControl(); e != 0 {
return toErr("SetFlowControl", e)
}
// Just in case. It's a very small cost.
return h.Flush()
}
// Reset resets the device.
func (h *handle) Reset() error {
if e := h.h.ResetDevice(); e != 0 {
return toErr("Reset", e)
}
if err := h.SetBitMode(0, bitModeReset); err != nil {
return err
}
// USB/driver: Flush any pending read buffer that had been sent by the device
// before it reset. Do not return any error there, as the device may spew a
// read error right after being initialized.
_ = h.Flush()
return nil
}
// GetBitMode returns the current bit mode.
//
// This is device-dependent.
func (h *handle) GetBitMode() (byte, error) {
l, e := h.h.GetBitMode()
if e != 0 {
return 0, toErr("GetBitMode", e)
}
return l, nil
}
// SetBitMode change the mode of operation of the device.
//
// mask sets which pins are inputs and outputs for bitModeCbusBitbang.
func (h *handle) SetBitMode(mask byte, mode bitMode) error {
return toErr("SetBitMode", h.h.SetBitMode(mask, byte(mode)))
}
// Flush flushes any data left in the read buffer.
func (h *handle) Flush() error {
var buf [128]byte
for {
p, err := h.Read(buf[:])
if err != nil {
return err
}
if p == 0 {
return nil
}
}
}
// Read returns as much as available in the read buffer without blocking.
func (h *handle) Read(b []byte) (int, error) {
// GetQueueStatus() 60µs is relatively slow compared to Read() 4µs,
// but surprisingly if GetQueueStatus() is *not* called, Read()
// becomes largely slower (800µs).
//
// TODO(maruel): This asks for more perf testing before settling on the best
// solution.
// TODO(maruel): Investigate FT_GetStatus().
p, e := h.h.GetQueueStatus()
if p == 0 || e != 0 {
return int(p), toErr("Read/GetQueueStatus", e)
}
v := int(p)
if v > len(b) {
v = len(b)
}
n, e := h.h.Read(b[:v])
return n, toErr("Read", e)
}
// ReadAll blocks to return all the data.
//
// Similar to ioutil.ReadAll() except that it will stop if the context is
// canceled.
func (h *handle) ReadAll(ctx context.Context, b []byte) (int, error) {
// TODO(maruel): Use FT_SetEventNotification() instead of looping when
// waiting for bytes.
for offset := 0; offset != len(b); {
if ctx.Err() != nil {
return offset, io.EOF
}
chunk := len(b) - offset
if chunk > 4096 {
chunk = 4096
}
n, err := h.Read(b[offset : offset+chunk])
if offset += n; err != nil {
return offset, err
}
}
return len(b), nil
}
// WriteFast writes to the USB device.
//
// In practice this takes at least 0.1ms, which limits the effective rate.
//
// There's no guarantee that the data is all written, so it is important to
// check the return value.
func (h *handle) WriteFast(b []byte) (int, error) {
n, e := h.h.Write(b)
return n, toErr("Write", e)
}
// Write blocks until all data is written.
func (h *handle) Write(b []byte) (int, error) {
for offset := 0; offset != len(b); {
chunk := len(b) - offset
if chunk > 4096 {
chunk = 4096
}
p, err := h.WriteFast(b[offset : offset+chunk])
if err != nil {
return offset + p, err
}
if p != 0 {
offset += p
}
}
return len(b), nil
}
// ReadEEPROM reads the EEPROM.
func (h *handle) ReadEEPROM(ee *EEPROM) error {
// The raw data size must be exactly what the device contains.
eepromSize := h.t.EEPROMSize()
if len(ee.Raw) < eepromSize {
ee.Raw = make([]byte, eepromSize)
} else if len(ee.Raw) > eepromSize {
ee.Raw = ee.Raw[:eepromSize]
}
ee2 := d2xx.EEPROM{Raw: ee.Raw}
e := h.h.EEPROMRead(uint32(h.t), &ee2)
ee.Manufacturer = ee2.Manufacturer
ee.ManufacturerID = ee2.ManufacturerID
ee.Desc = ee2.Desc
ee.Serial = ee2.Serial
if e != 0 {
// 15 == FT_EEPROM_NOT_PROGRAMMED
if e != 15 {
return toErr("EEPROMRead", e)
}
// It's a fresh new device. Devices bought via Adafruit already have
// their EEPROM programmed with Adafruit branding but fake devices sold by
// CJMCU are not. Since GetDeviceInfo() above succeeded, we know the
// device type via the USB descriptor, which is sufficient to load the
// driver, which permits to program the EEPROM to "bootstrap" it.
//
// Fill it with an empty yet valid EEPROM content. We don't want to set
// VenID or DevID to 0! Nobody would do that, right?
ee.Raw = make([]byte, h.t.EEPROMSize())
hdr := ee.AsHeader()
hdr.DeviceType = h.t
hdr.VendorID = h.venID
hdr.ProductID = h.devID
}
return nil
}
// WriteEEPROM programs the EEPROM.
func (h *handle) WriteEEPROM(ee *EEPROM) error {
if err := ee.Validate(); err != nil {
return err
}
if len(ee.Raw) != 0 {
hdr := ee.AsHeader()
if hdr == nil {
return errors.New("ftdi: unexpected EEPROM header size")
}
if hdr.DeviceType != h.t {
return errors.New("ftdi: unexpected device type set while programming EEPROM")
}
if hdr.VendorID != h.venID {
return errors.New("ftdi: unexpected VenID set while programming EEPROM")
}
if hdr.ProductID != h.devID {
return errors.New("ftdi: unexpected DevID set while programming EEPROM")
}
}
ee2 := d2xx.EEPROM{
Raw: ee.Raw,
Manufacturer: ee.Manufacturer,
ManufacturerID: ee.ManufacturerID,
Desc: ee.Desc,
Serial: ee.Serial,
}
return toErr("EEPROMWrite", h.h.EEPROMProgram(&ee2))
}
// EraseEEPROM erases all the EEPROM.
//
// Will fail on FT232R and FT245R.
func (h *handle) EraseEEPROM() error {
return toErr("EraseEE", h.h.EraseEE())
}
// ReadUA reads the EEPROM user area.
//
// May return nil when there's nothing programmed yet.
func (h *handle) ReadUA() ([]byte, error) {
size, e := h.h.EEUASize()
if e != 0 {
return nil, toErr("EEUASize", e)
}
if size == 0 {
// Happens on uninitialized EEPROM.
return nil, nil
}
b := make([]byte, size)
if e := h.h.EEUARead(b); e != 0 {
return nil, toErr("EEUARead", e)
}
return b, nil
}
// WriteUA writes to the EEPROM user area.
func (h *handle) WriteUA(ua []byte) error {
size, e := h.h.EEUASize()
if e != 0 {
return toErr("EEUASize", e)
}
if size == 0 {
return errors.New("ftdi: please program EEPROM first")
}
if size < len(ua) {
return fmt.Errorf("ftdi: maximum user area size is %d bytes", size)
}
if size != len(ua) {
b := make([]byte, size)
copy(b, ua)
ua = b
}
if e := h.h.EEUAWrite(ua); e != 0 {
return toErr("EEUAWrite", e)
}
return nil
}
// SetBaudRate sets the baud rate.
func (h *handle) SetBaudRate(f physic.Frequency) error {
if f >= physic.GigaHertz {
return errors.New("ftdi: baud rate too high")
}
v := uint32(f / physic.Hertz)
return toErr("SetBaudRate", h.h.SetBaudRate(v))
}
//
func toErr(s string, e d2xx.Err) error {
if e == 0 {
return nil
}
return errors.New("ftdi: " + s + ": " + e.String())
}

View File

@@ -1,295 +0,0 @@
// 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{}

View File

@@ -1,452 +0,0 @@
// 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.
// MPSSE is Multi-Protocol Synchronous Serial Engine
//
// MPSSE basics:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_135_MPSSE_Basics.pdf
//
// MPSSE and MCU emulation modes:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
package ftdi
import (
"context"
"errors"
"fmt"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
)
const (
// TDI/TDO serial operation synchronised on clock edges.
//
// Long streams (default):
// - [1, 65536] bytes (length is sent minus one, requires 8 bits multiple)
// <op>, <LengthLow-1>, <LengthHigh-1>, <byte0>, ..., <byteN>
//
// Short streams (dataBit is specified):
// - [1, 8] bits
// <op>, <Length-1>, <byte>
//
// When both dataOut and dataIn are specified, one of dataOutFall or
// dataInFall should be specified, at least for most sane protocols.
//
// Flags:
dataOut byte = 0x10 // Enable output, default on +VE (Rise)
dataIn byte = 0x20 // Enable input, default on +VE (Rise)
dataOutFall byte = 0x01 // instead of Rise
dataInFall byte = 0x04 // instead of Rise
dataLSBF byte = 0x08 // instead of MSBF
dataBit byte = 0x02 // instead of Byte
// Data line drives low when the data is 0 and tristates on data 1. This is
// used with I²C.
// <op>, <ADBus pins>, <ACBus pins>
dataTristate byte = 0x9E
// TSM operation (for JTAG).
//
// - Send bits 6 to 0 to the TMS pin using LSB or MSB.
// - Bit 7 is passed to TDI/DO before the first clock of TMS and is held
// static for the duration of TMS clocking.
//
// <op>, <Length>, <byte>
tmsOutLSBFRise byte = 0x4A
tmsOutLSBFFall byte = 0x4B
tmsIOLSBInRise byte = 0x6A
tmsIOLSBInFall byte = 0x6B
// Unclear: 0x6E and 0x6F
// GPIO operation.
//
// - Operates on 8 GPIOs at a time, e.g. C0~C7 or D0~D7.
// - Direction 1 means output, 0 means input.
//
// <op>, <value>, <direction>
gpioSetD byte = 0x80
gpioSetC byte = 0x82
// <op>, returns <value>
gpioReadD byte = 0x81
gpioReadC byte = 0x83
// Internal loopback.
//
// Connects TDI and TDO together.
internalLoopbackEnable byte = 0x84
internalLoopbackDisable byte = 0x85
// Clock.
//
// The TCK/SK has a 50% duty cycle.
//
// The inactive clock state can be set via the gpioSetD command and control
// bit 0.
//
// By default, the base clock is 6MHz via a 5x divisor. On
// FT232H/FT2232H/FT4232H, the 5x divisor can be disabled.
clock30MHz byte = 0x8A
clock6MHz byte = 0x8B
// Sets clock divisor.
//
// The effective value depends if clock30MHz was sent or not.
//
// - 0(1) 6MHz / 30MHz
// - 1(2) 3MHz / 15MHz
// - 2(3) 2MHz / 10MHz
// - 3(4) 1.5MHz / 7.5MHz
// - 4(5) 1.25MHz / 6MHz
// - ...
// - 0xFFFF(65536) 91.553Hz / 457.763Hz
//
// <op>, <valueL-1>, <valueH-1>
clockSetDivisor byte = 0x86
// Uses 3 phases data clocking: data is valid on both clock edges. Needed
// for I²C.
clock3Phase byte = 0x8C
// Uses normal 2 phases data clocking.
clock2Phase byte = 0x8D
// Enables clock even while not doing any operation. Used with JTAG.
// Enables the clock between [1, 8] pulses.
// <op>, <length-1>
clockOnShort byte = 0x8E
// Enables the clock between [8, 524288] pulses in 8 multiples.
// <op>, <lengthL-1>, <lengthH-1>
clockOnLong byte = 0x8F
// Enables clock until D5 is high or low. Used with JTAG.
clockUntilHigh byte = 0x94
clockUntilLow byte = 0x95
// <op>, <lengthL-1>, <lengthH-1> in 8 multiples.
clockUntilHighLong byte = 0x9C
clockUntilLowLong byte = 0x9D
// Enables adaptive clocking. Used with JTAG.
//
// This causes the controller to wait for D7 signal state as an ACK.
clockAdaptive byte = 0x96
// Disables adaptive clocking.
clockNormal byte = 0x97
// CPU mode.
//
// Access the device registers like a memory mapped device.
//
// <op>, <addrLow>
cpuReadShort byte = 0x90
// <op>, <addrHi>, <addrLow>
cpuReadFar byte = 0x91
// <op>, <addrLow>, <data>
cpuWriteShort byte = 0x92
// <op>, <addrHi>, <addrLow>, <data>
cpuWriteFar byte = 0x91
// Buffer operations.
//
// Flush the buffer back to the host.
flush byte = 0x87
// Wait until D5 (JTAG) or I/O1 (CPU) is high. Once it is detected as
// high, the MPSSE engine moves on to process the next instruction.
waitHigh byte = 0x88
waitLow byte = 0x89
)
// InitMPSSE sets the device into MPSSE mode.
//
// This requires a f232h, ft2232, ft2232h or a ft4232h.
//
// Use only one of Init or InitMPSSE.
func (h *handle) InitMPSSE() error {
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
// Pre-state:
// - Write EEPROM i.IsFifo = true so the device DBus is started in tristate.
// Try to verify the MPSSE controller without initializing it first. This is
// the 'happy path', which enables reusing the device is its current state
// without affecting current GPIO state.
if h.mpsseVerify() != nil {
// Do a full reset. Just trying to set the MPSSE controller will
// likely not work. That's a layering violation (since the retry with reset
// is done in driver.go) but we've survived worse things...
//
// TODO(maruel): This is not helping in practice, this need to be fine
// tuned.
if err := h.Reset(); err != nil {
return err
}
if err := h.Init(); err != nil {
return err
}
if err := h.SetBitMode(0, bitModeMpsse); err != nil {
return err
}
if err := h.mpsseVerify(); err != nil {
return err
}
}
// Initialize MPSSE to a known state.
// Reset the clock since it is impossible to read back the current clock rate.
// Reset all the GPIOs are inputs since it is impossible to read back the
// state of each GPIO (if they are input or output).
cmd := []byte{
clock30MHz, clockNormal, clock2Phase, internalLoopbackDisable,
gpioSetC, 0x00, 0x00,
gpioSetD, 0x00, 0x00,
}
if _, err := h.Write(cmd); err != nil {
return err
}
// Success!!
return nil
}
// mpsseVerify sends an invalid MPSSE command and verifies the returned value
// is incorrect.
//
// In practice this takes around 2ms.
func (h *handle) mpsseVerify() error {
var b [16]byte
for _, v := range []byte{0xAA, 0xAB} {
// Write a bad command and ensure it returned correctly.
// Unlike what the application note proposes, include a flush op right
// after. Without the flush, the device will only flush after the delay
// specified to SetLatencyTimer. The flush removes this unneeded wait,
// which enables increasing the delay specified to SetLatencyTimer.
b[0] = v
b[1] = flush
if _, err := h.Write(b[:2]); err != nil {
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
}
// Sometimes, especially right after a reset, the device spews a few bytes.
// Discard them. This significantly increases the odds of a successful
// initialization.
p, e := h.h.GetQueueStatus()
if e != 0 {
return toErr("Read/GetQueueStatus", e)
}
for p > 2 {
l := int(p) - 2
if l > len(b) {
l = len(b)
}
// Discard the overflow bytes.
ctx, cancel := context200ms()
defer cancel()
if _, err := h.ReadAll(ctx, b[:l]); err != nil {
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
}
p -= uint32(l)
}
// Custom implementation, as we want to flush any stray byte.
ctx, cancel := context200ms()
defer cancel()
if _, err := h.ReadAll(ctx, b[:2]); err != nil {
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
}
// 0xFA means invalid command, 0xAA is the command echoed back.
if b[0] != 0xFA || b[1] != v {
return fmt.Errorf("d2xx: mpsseVerify: failed test for byte %#x: %#x", v, b)
}
}
return nil
}
//
// MPSSERegRead reads the memory mapped registers from the device.
func (h *handle) MPSSERegRead(addr uint16) (byte, error) {
// Unlike most other operations, the uint16 byte order is <hi>, <lo>.
b := [...]byte{cpuReadFar, byte(addr >> 8), byte(addr), flush}
if _, err := h.Write(b[:]); err != nil {
return 0, err
}
ctx, cancel := context200ms()
defer cancel()
_, err := h.ReadAll(ctx, b[:1])
return b[0], err
}
// MPSSEClock sets the clock at the closest value and returns it.
func (h *handle) MPSSEClock(f physic.Frequency) (physic.Frequency, error) {
// TODO(maruel): Memory clock and skip if the same value.
clk := clock30MHz
base := 30 * physic.MegaHertz
div := base / f
if div >= 65536 {
clk = clock6MHz
base /= 5
div = base / f
if div >= 65536 {
return 0, errors.New("d2xx: clock frequency is too low")
}
}
b := [...]byte{clk, clockSetDivisor, byte(div - 1), byte((div - 1) >> 8)}
_, err := h.Write(b[:])
return base / div, err
}
// mpsseTxOp returns the right MPSSE command byte for the stream.
func mpsseTxOp(w, r bool, ew, er gpio.Edge, lsbf bool) byte {
op := byte(0)
if lsbf {
op |= dataLSBF
}
if w {
op |= dataOut
if ew == gpio.FallingEdge {
op |= dataOutFall
}
}
if r {
op |= dataIn
if er == gpio.FallingEdge {
op |= dataInFall
}
}
return op
}
// MPSSETx runs a transaction on the clock on pins D0, D1 and D2.
//
// It can only do it on a multiple of 8 bits.
func (h *handle) MPSSETx(w, r []byte, ew, er gpio.Edge, lsbf bool) error {
l := len(w)
if len(w) != 0 {
// TODO(maruel): This is easy to fix by daisy chaining operations.
if len(w) > 65536 {
return errors.New("d2xx: write buffer too long; max 65536")
}
}
if len(r) != 0 {
if len(r) > 65536 {
return errors.New("d2xx: read buffer too long; max 65536")
}
if l != 0 && len(r) != l {
return errors.New("d2xx: mismatched buffer lengths")
}
l = len(r)
}
// The FT232H has 1Kb Tx and Rx buffers. So partial writes should be done.
// TODO(maruel): Test.
// Flush can be useful if rbits != 0.
op := mpsseTxOp(len(w) != 0, len(r) != 0, ew, er, lsbf)
cmd := []byte{op, byte(l - 1), byte((l - 1) >> 8)}
cmd = append(cmd, w...)
if len(r) != 0 {
cmd = append(cmd, flush)
}
if _, err := h.Write(cmd); err != nil {
return err
}
if len(r) != 0 {
ctx, cancel := context200ms()
defer cancel()
_, err := h.ReadAll(ctx, r)
return err
}
return nil
}
// MPSSETxShort runs a transaction on the clock pins D0, D1 and D2 for a byte
// or less: between 1 and 8 bits.
func (h *handle) MPSSETxShort(w byte, wbits, rbits int, ew, er gpio.Edge, lsbf bool) (byte, error) {
op := byte(dataBit)
if lsbf {
op |= dataLSBF
}
l := wbits
if wbits != 0 {
if wbits > 8 {
return 0, errors.New("d2xx: write buffer too long; max 8")
}
op |= dataOut
if ew == gpio.FallingEdge {
op |= dataOutFall
}
}
if rbits != 0 {
if rbits > 8 {
return 0, errors.New("d2xx: read buffer too long; max 8")
}
op |= dataIn
if er == gpio.FallingEdge {
op |= dataInFall
}
if l != 0 && rbits != l {
return 0, errors.New("d2xx: mismatched buffer lengths")
}
l = rbits
}
b := [3]byte{op, byte(l - 1)}
cmd := b[:2]
if wbits != 0 {
cmd = append(cmd, w)
}
if rbits != 0 {
cmd = append(cmd, flush)
}
if _, err := h.Write(cmd); err != nil {
return 0, err
}
if rbits != 0 {
ctx, cancel := context200ms()
defer cancel()
_, err := h.ReadAll(ctx, b[:1])
return b[0], err
}
return 0, nil
}
// MPSSECBus operates on 8 GPIOs at a time C0~C7.
//
// Direction 1 means output, 0 means input.
func (h *handle) MPSSECBus(mask, value byte) error {
b := [...]byte{gpioSetC, value, mask}
_, err := h.Write(b[:])
return err
}
// MPSSEDBus operates on 8 GPIOs at a time D0~D7.
//
// Direction 1 means output, 0 means input.
func (h *handle) MPSSEDBus(mask, value byte) error {
b := [...]byte{gpioSetD, value, mask}
_, err := h.Write(b[:])
return err
}
// MPSSECBusRead reads all the CBus pins C0~C7.
func (h *handle) MPSSECBusRead() (byte, error) {
b := [...]byte{gpioReadC, flush}
if _, err := h.Write(b[:]); err != nil {
return 0, err
}
ctx, cancel := context200ms()
defer cancel()
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
return 0, err
}
return b[0], nil
}
// MPSSEDBusRead reads all the DBus pins D0~D7.
func (h *handle) MPSSEDBusRead() (byte, error) {
b := [...]byte{gpioReadD, flush}
if _, err := h.Write(b[:]); err != nil {
return 0, err
}
ctx, cancel := context200ms()
defer cancel()
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
return 0, err
}
return b[0], nil
}
func context200ms() (context.Context, func()) {
return context.WithTimeout(context.Background(), 200*time.Millisecond)
}

View File

@@ -1,205 +0,0 @@
// 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.
package ftdi
import (
"errors"
"fmt"
"strconv"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
)
// gpiosMPSSE is a slice of 8 GPIO pins driven via MPSSE.
//
// This permits keeping a cache.
type gpiosMPSSE struct {
// Immutable.
h *handle
cbus bool // false if D bus
pins [8]gpioMPSSE
// Cache of values
direction byte
value byte
}
func (g *gpiosMPSSE) init(name string) {
s := "D"
if g.cbus {
s = "C"
}
// Configure pulls; pull ups are 75kΩ.
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_184%20FTDI%20Device%20Input%20Output%20Pin%20States.pdf
// has a good table.
// D0, D2 and D4 go in high impedance before going into pull up.
// TODO(maruel): The pull on CBus depends on EEPROM!
for i := range g.pins {
g.pins[i].a = g
g.pins[i].n = name + "." + s + strconv.Itoa(i)
g.pins[i].num = i
g.pins[i].dp = gpio.PullUp
}
if g.cbus {
// That's just the default EEPROM value.
g.pins[7].dp = gpio.PullDown
}
}
func (g *gpiosMPSSE) in(n int) error {
if g.h == nil {
return errors.New("d2xx: device not open")
}
g.direction = g.direction & ^(1 << uint(n))
if g.cbus {
return g.h.MPSSECBus(g.direction, g.value)
}
return g.h.MPSSEDBus(g.direction, g.value)
}
func (g *gpiosMPSSE) read() (byte, error) {
if g.h == nil {
return 0, errors.New("d2xx: device not open")
}
var err error
if g.cbus {
g.value, err = g.h.MPSSECBusRead()
} else {
g.value, err = g.h.MPSSEDBusRead()
}
return g.value, err
}
func (g *gpiosMPSSE) out(n int, l gpio.Level) error {
if g.h == nil {
return errors.New("d2xx: device not open")
}
g.direction = g.direction | (1 << uint(n))
if l {
g.value |= 1 << uint(n)
} else {
g.value &^= 1 << uint(n)
}
if g.cbus {
return g.h.MPSSECBus(g.direction, g.value)
}
return g.h.MPSSEDBus(g.direction, g.value)
}
//
// gpioMPSSE is a GPIO pin on a FTDI device driven via MPSSE.
//
// gpioMPSSE implements gpio.PinIO.
//
// It is immutable and stateless.
type gpioMPSSE struct {
a *gpiosMPSSE
n string
num int
dp gpio.Pull
}
// String implements pin.Pin.
func (g *gpioMPSSE) String() string {
return g.n
}
// Name implements pin.Pin.
func (g *gpioMPSSE) Name() string {
return g.n
}
// Number implements pin.Pin.
func (g *gpioMPSSE) Number() int {
return g.num
}
// Function implements pin.Pin.
func (g *gpioMPSSE) Function() string {
s := "Out/"
m := byte(1 << uint(g.num))
if g.a.direction&m == 0 {
s = "In/"
_, _ = g.a.read()
}
return s + gpio.Level(g.a.value&m != 0).String()
}
// Halt implements gpio.PinIO.
func (g *gpioMPSSE) Halt() error {
return nil
}
// In implements gpio.PinIn.
func (g *gpioMPSSE) In(pull gpio.Pull, e gpio.Edge) error {
if e != gpio.NoEdge {
// We could support it on D5.
return errors.New("d2xx: edge triggering is not supported")
}
if pull != g.dp && pull != gpio.PullNoChange {
// TODO(maruel): This needs to be redone:
// - EEPROM values FT232hCBusTristatePullUp and FT232hCBusPwrEnable can be
// used to control individual CBus pins.
// - dataTristate enables gpio.Float when set to output High, but I don't
// know if it will enable reading the value (?). This needs to be
// confirmed.
return fmt.Errorf("d2xx: pull %s is not supported; try %s", pull, g.dp)
}
return g.a.in(g.num)
}
// Read implements gpio.PinIn.
func (g *gpioMPSSE) Read() gpio.Level {
v, _ := g.a.read()
return gpio.Level(v&(1<<uint(g.num)) != 0)
}
// WaitForEdge implements gpio.PinIn.
func (g *gpioMPSSE) WaitForEdge(t time.Duration) bool {
return false
}
// DefaultPull implements gpio.PinIn.
func (g *gpioMPSSE) DefaultPull() gpio.Pull {
return g.dp
}
// Pull implements gpio.PinIn. The resistor is 75kΩ.
func (g *gpioMPSSE) Pull() gpio.Pull {
// See In() for the challenges.
return g.dp
}
// Out implements gpio.PinOut.
func (g *gpioMPSSE) Out(l gpio.Level) error {
return g.a.out(g.num, l)
}
// PWM implements gpio.PinOut.
func (g *gpioMPSSE) PWM(d gpio.Duty, f physic.Frequency) error {
return errors.New("d2xx: not implemented")
}
/*
func (g *gpioMPSSE) Drive() physic.ElectricCurrent {
//return g.a.ee.CDriveCurrent * physic.MilliAmpere
return 2 * physic.MilliAmpere
}
func (g *gpioMPSSE) SlewLimit() bool {
//return g.a.ee.CSlowSlew
return false
}
func (g *gpioMPSSE) Hysteresis() bool {
//return g.a.ee.DSchmittInput
return true
}
*/
var _ gpio.PinIO = &gpioMPSSE{}

View File

@@ -1,85 +0,0 @@
// 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.
// Emulate independent GPIOs.
package ftdi
import (
"errors"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
)
// invalidPin is a non-working (not implemented) pin on a FTDI device.
//
// invalidPin implements gpio.PinIO.
type invalidPin struct {
n string
num int
}
// String implements pin.Pin.
func (p *invalidPin) String() string {
return p.n
}
// Name implements pin.Pin.
func (p *invalidPin) Name() string {
return p.n
}
// Number implements pin.Pin.
func (p *invalidPin) Number() int {
return p.num
}
// Function implements pin.Pin.
func (p *invalidPin) Function() string {
return "N/A"
}
// Halt implements gpio.PinIO.
func (p *invalidPin) Halt() error {
return nil
}
// In implements gpio.PinIn.
func (p *invalidPin) In(pull gpio.Pull, e gpio.Edge) error {
return errors.New("d2xx: to be implemented")
}
// Read implements gpio.PinIn.
func (p *invalidPin) Read() gpio.Level {
return gpio.Low
}
// WaitForEdge implements gpio.PinIn.
func (p *invalidPin) WaitForEdge(t time.Duration) bool {
return false
}
// Pull implements gpio.PinIn.
func (p *invalidPin) Pull() gpio.Pull {
return gpio.PullNoChange
}
// DefaultPull implements gpio.PinIn.
func (p *invalidPin) DefaultPull() gpio.Pull {
return gpio.PullNoChange
}
// Out implements gpio.PinOut.
func (p *invalidPin) Out(l gpio.Level) error {
return errors.New("d2xx: to be implemented")
}
// PWM implements gpio.PinOut.
func (p *invalidPin) PWM(d gpio.Duty, f physic.Frequency) error {
return errors.New("d2xx: to be implemented")
}
var _ gpio.PinIO = &invalidPin{}

View File

@@ -1,697 +0,0 @@
// 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 SPI:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_114_FTDI_Hi_Speed_USB_To_SPI_Example.pdf
//
// Implementation based on
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_180_FT232H%20MPSSE%20Example%20-%20USB%20Current%20Meter%20using%20the%20SPI%20interface.pdf
package ftdi
import (
"context"
"errors"
"fmt"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
)
// spiMPSEEPort is an SPI port over a FTDI device in MPSSE mode using the data
// command on the AD bus.
type spiMPSEEPort struct {
c spiMPSEEConn
// Mutable.
maxFreq physic.Frequency
}
func (s *spiMPSEEPort) Close() error {
s.c.f.mu.Lock()
s.c.f.usingSPI = false
s.maxFreq = 0
s.c.edgeInvert = false
s.c.clkActiveLow = false
s.c.noCS = false
s.c.lsbFirst = false
s.c.halfDuplex = false
s.c.f.mu.Unlock()
return nil
}
func (s *spiMPSEEPort) String() string {
return s.c.f.String()
}
// Connect implements spi.Port.
func (s *spiMPSEEPort) Connect(f physic.Frequency, m spi.Mode, bits int) (spi.Conn, error) {
if f > physic.GigaHertz {
return nil, fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 30MHz", f)
}
if f > 30*physic.MegaHertz {
// TODO(maruel): Figure out a way to communicate that the speed was lowered.
// https://github.com/google/periph/issues/255
f = 30 * physic.MegaHertz
}
if f < 100*physic.Hertz {
return nil, fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
}
if bits&7 != 0 {
return nil, errors.New("d2xx: bits must be multiple of 8")
}
if bits != 8 {
return nil, errors.New("d2xx: implement bits per word above 8")
}
s.c.f.mu.Lock()
defer s.c.f.mu.Unlock()
s.c.noCS = m&spi.NoCS != 0
s.c.halfDuplex = m&spi.HalfDuplex != 0
s.c.lsbFirst = m&spi.LSBFirst != 0
m &^= spi.NoCS | spi.HalfDuplex | spi.LSBFirst
if s.c.halfDuplex {
return nil, errors.New("d2xx: spi.HalfDuplex is not yet supported (implementing wouldn't be too hard, please submit a PR")
}
if m < 0 || m > 3 {
return nil, errors.New("d2xx: unknown spi mode")
}
s.c.edgeInvert = m&1 != 0
s.c.clkActiveLow = m&2 != 0
if s.maxFreq == 0 || f < s.maxFreq {
// TODO(maruel): We could set these only *during* the SPI operation, which
// would make more sense.
if _, err := s.c.f.h.MPSSEClock(f); err != nil {
return nil, err
}
s.maxFreq = f
}
s.c.resetIdle()
if err := s.c.f.h.MPSSEDBus(s.c.f.dbus.direction, s.c.f.dbus.value); err != nil {
return nil, err
}
s.c.f.usingSPI = true
return &s.c, nil
}
// LimitSpeed implements spi.Port.
func (s *spiMPSEEPort) LimitSpeed(f physic.Frequency) error {
if f > physic.GigaHertz {
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 30MHz", f)
}
if f > 30*physic.MegaHertz {
f = 30 * physic.MegaHertz
}
if f < 100*physic.Hertz {
return errors.New("d2xx: minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?")
}
s.c.f.mu.Lock()
defer s.c.f.mu.Unlock()
if s.maxFreq != 0 && s.maxFreq <= f {
return nil
}
s.maxFreq = f
// TODO(maruel): We could set these only *during* the SPI operation, which
// would make more sense.
_, err := s.c.f.h.MPSSEClock(s.maxFreq)
return err
}
// CLK returns the SCK (clock) pin.
func (s *spiMPSEEPort) CLK() gpio.PinOut {
return s.c.CLK()
}
// MOSI returns the SDO (master out, slave in) pin.
func (s *spiMPSEEPort) MOSI() gpio.PinOut {
return s.c.MOSI()
}
// MISO returns the SDI (master in, slave out) pin.
func (s *spiMPSEEPort) MISO() gpio.PinIn {
return s.c.MISO()
}
// CS returns the CSN (chip select) pin.
func (s *spiMPSEEPort) CS() gpio.PinOut {
return s.c.CS()
}
type spiMPSEEConn struct {
// Immutable.
f *FT232H
// Initialized at Connect().
edgeInvert bool // CPHA=1
clkActiveLow bool // CPOL=1
noCS bool // CS line is not changed
lsbFirst bool // Default is MSB first
halfDuplex bool // 3 wire mode
}
func (s *spiMPSEEConn) String() string {
return s.f.String()
}
func (s *spiMPSEEConn) Tx(w, r []byte) error {
var p = [1]spi.Packet{{W: w, R: r}}
return s.TxPackets(p[:])
}
func (s *spiMPSEEConn) Duplex() conn.Duplex {
// TODO(maruel): Support half if there's a need.
return conn.Full
}
func (s *spiMPSEEConn) TxPackets(pkts []spi.Packet) error {
// Verification.
for _, p := range pkts {
if p.KeepCS {
return errors.New("d2xx: implement spi.Packet.KeepCS")
}
if p.BitsPerWord&7 != 0 {
return errors.New("d2xx: bits must be a multiple of 8")
}
if p.BitsPerWord != 0 && p.BitsPerWord != 8 {
return errors.New("d2xx: implement spi.Packet.BitsPerWord")
}
if err := verifyBuffers(p.W, p.R); err != nil {
return err
}
}
s.f.mu.Lock()
defer s.f.mu.Unlock()
const clk = byte(1) << 0
const mosi = byte(1) << 1
const miso = byte(1) << 2
const cs = byte(1) << 3
s.resetIdle()
idle := s.f.dbus.value
start1 := idle
if !s.noCS {
start1 &^= cs
}
// In mode 0 and 2, start2 is not needed.
start2 := start1
stop := idle
if s.edgeInvert {
// This is needed to 'prime' the clock.
start2 ^= clk
// With mode 1 and 3, keep the clock steady while CS is being deasserted to
// not create a spurious clock.
stop ^= clk
}
ew := gpio.FallingEdge
er := gpio.RisingEdge
if s.edgeInvert {
ew, er = er, ew
}
if s.clkActiveLow {
// TODO(maruel): Not sure.
ew, er = er, ew
}
// FT232H claims 512 USB packet support, so to reduce the chatter over USB,
// try to make all I/O be aligned on this amount. This also removes the need
// for heap usage. The idea is to always trail reads by one buffer. This is
// fine as the device has 1024 byte read buffer. Operations look like this:
// W, W, R, W, R, W, R, R
// This enables reducing the I/O gaps between USB packets as the device is
// always busy with operations.
var buf [512]byte
cmd := buf[:0]
keptCS := false
// Loop, without increasing the index.
for _, p := range pkts {
if len(p.W) == 0 && len(p.R) == 0 {
continue
}
// TODO(maruel): s.halfDuplex.
if !keptCS {
for i := 0; i < 5; i++ {
cmd = append(cmd, gpioSetD, idle, s.f.dbus.direction)
}
for i := 0; i < 5; i++ {
cmd = append(cmd, gpioSetD, start1, s.f.dbus.direction)
}
}
if s.edgeInvert {
// This is needed to 'prime' the clock.
for i := 0; i < 5; i++ {
cmd = append(cmd, gpioSetD, start2, s.f.dbus.direction)
}
}
op := mpsseTxOp(len(p.W) != 0, len(p.R) != 0, ew, er, s.lsbFirst)
// Do an I/O loop. We can mutate p here because it is a copy.
// TODO(maruel): Have the pipeline cross the packet boundary.
if len(p.W) == 0 {
// Have the write buffer point to the read one. This saves from
// allocating memory. The side effect is that it will write whatever
// happened to be in the read buffer.
p.W = p.R[:]
}
pendingRead := 0
for len(p.W) != 0 {
// op, sizelo, sizehi.
chunk := len(buf) - 3 - len(cmd)
if l := len(p.W); chunk > l {
chunk = l
}
cmd = append(cmd, op, byte(chunk-1), byte((chunk-1)>>8))
cmd = append(cmd, p.W[:chunk]...)
p.W = p.W[chunk:]
if _, err := s.f.h.WriteFast(cmd); err != nil {
return err
}
cmd = buf[:0]
// TODO(maruel): Read 62 bytes at a time?
// Delay reading by 512 bytes.
if pendingRead >= 512 {
if len(p.R) != 0 {
// Align reads on 512 bytes exactly, aligned on USB packet size.
if _, err := s.f.h.ReadAll(context.Background(), p.R[:512]); err != nil {
return err
}
p.R = p.R[512:]
pendingRead -= 512
}
}
pendingRead += chunk
}
// Do not forget to read whatever is pending.
// TODO(maruel): Investigate if a flush helps.
if len(p.R) != 0 {
// Send a flush to not wait for data.
cmd = append(cmd, flush)
if _, err := s.f.h.WriteFast(cmd); err != nil {
return err
}
cmd = buf[:0]
if _, err := s.f.h.ReadAll(context.Background(), p.R); err != nil {
return err
}
}
// TODO(maruel): Inject this in the write if it fits (it will generally
// do). That will save one USB I/O, which is not insignificant.
keptCS = p.KeepCS
if !keptCS {
cmd = append(cmd, flush)
for i := 0; i < 5; i++ {
cmd = append(cmd, gpioSetD, stop, s.f.dbus.direction)
}
for i := 0; i < 5; i++ {
cmd = append(cmd, gpioSetD, idle, s.f.dbus.direction)
}
if _, err := s.f.h.WriteFast(cmd); err != nil {
return err
}
cmd = buf[:0]
}
}
return nil
}
// CLK returns the SCK (clock) pin.
func (s *spiMPSEEConn) CLK() gpio.PinOut {
return s.f.D0
}
// MOSI returns the SDO (master out, slave in) pin.
func (s *spiMPSEEConn) MOSI() gpio.PinOut {
return s.f.D1
}
// MISO returns the SDI (master in, slave out) pin.
func (s *spiMPSEEConn) MISO() gpio.PinIn {
return s.f.D2
}
// CS returns the CSN (chip select) pin.
func (s *spiMPSEEConn) CS() gpio.PinOut {
return s.f.D3
}
// resetIdle sets D0~D3. D0, D1 and D3 are output but only touch D3 is CS is
// used.
func (s *spiMPSEEConn) resetIdle() {
const clk = byte(1) << 0
const mosi = byte(1) << 1
const miso = byte(1) << 2
const cs = byte(1) << 3
if !s.noCS {
s.f.dbus.direction &= 0xF0
s.f.dbus.direction |= cs
s.f.dbus.value &= 0xF0
s.f.dbus.value |= cs
} else {
s.f.dbus.value &= 0xF8
s.f.dbus.direction &= 0xF8
}
s.f.dbus.direction |= mosi | clk
if s.clkActiveLow {
// Clock idles high.
s.f.dbus.value |= clk
}
}
//
// spiSyncPort is an SPI port over a FTDI device in synchronous bit-bang mode.
type spiSyncPort struct {
c spiSyncConn
// Mutable.
maxFreq physic.Frequency
}
func (s *spiSyncPort) Close() error {
s.c.f.mu.Lock()
s.c.f.usingSPI = false
s.maxFreq = 0
s.c.edgeInvert = false
s.c.clkActiveLow = false
s.c.noCS = false
s.c.lsbFirst = false
s.c.halfDuplex = false
s.c.f.mu.Unlock()
return nil
}
func (s *spiSyncPort) String() string {
return s.c.f.String()
}
const ft232rMaxSpeed = 3 * physic.MegaHertz
// Connect implements spi.Port.
func (s *spiSyncPort) Connect(f physic.Frequency, m spi.Mode, bits int) (spi.Conn, error) {
if f > physic.GigaHertz {
return nil, fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 1.5MHz", f)
}
if f > ft232rMaxSpeed/2 {
// TODO(maruel): Figure out a way to communicate that the speed was lowered.
// https://github.com/google/periph/issues/255
f = ft232rMaxSpeed / 2
}
if f < 100*physic.Hertz {
return nil, fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
}
if bits&7 != 0 {
return nil, errors.New("d2xx: bits must be multiple of 8")
}
if bits != 8 {
return nil, errors.New("d2xx: implement bits per word above 8")
}
s.c.f.mu.Lock()
defer s.c.f.mu.Unlock()
s.c.noCS = m&spi.NoCS != 0
s.c.halfDuplex = m&spi.HalfDuplex != 0
s.c.lsbFirst = m&spi.LSBFirst != 0
m &^= spi.NoCS | spi.HalfDuplex | spi.LSBFirst
if s.c.halfDuplex {
return nil, errors.New("d2xx: spi.HalfDuplex is not yet supported (implementing wouldn't be too hard, please submit a PR")
}
if m < 0 || m > 3 {
return nil, errors.New("d2xx: unknown spi mode")
}
s.c.edgeInvert = m&1 != 0
s.c.clkActiveLow = m&2 != 0
if s.maxFreq == 0 || f < s.maxFreq {
if err := s.c.f.SetSpeed(f * 2); err != nil {
return nil, err
}
s.maxFreq = f
}
// D0, D2 and D3 are output. D4~D7 are kept as-is.
const mosi = byte(1) << 0 // TX
const miso = byte(1) << 1 // RX
const clk = byte(1) << 2 // RTS
const cs = byte(1) << 3 // CTS
mask := mosi | clk | cs | (s.c.f.dmask & 0xF0)
if err := s.c.f.setDBusMaskLocked(mask); err != nil {
return nil, err
}
// TODO(maruel): Combine both following calls if possible. We'd shave off a
// few ms.
if !s.c.noCS {
// CTS/SPI_CS is active low.
if err := s.c.f.dbusSyncGPIOOutLocked(3, gpio.High); err != nil {
return nil, err
}
}
if s.c.clkActiveLow {
// RTS/SPI_CLK is active low.
if err := s.c.f.dbusSyncGPIOOutLocked(2, gpio.High); err != nil {
return nil, err
}
}
s.c.f.usingSPI = true
return &s.c, nil
}
// LimitSpeed implements spi.Port.
func (s *spiSyncPort) LimitSpeed(f physic.Frequency) error {
if f > physic.GigaHertz {
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 1.5MHz", 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.MegaHertz?", f)
}
s.c.f.mu.Lock()
defer s.c.f.mu.Unlock()
if s.maxFreq != 0 && s.maxFreq <= f {
return nil
}
if err := s.c.f.SetSpeed(f * 2); err == nil {
s.maxFreq = f
}
return nil
}
// CLK returns the SCK (clock) pin.
func (s *spiSyncPort) CLK() gpio.PinOut {
return s.c.CLK()
}
// MOSI returns the SDO (master out, slave in) pin.
func (s *spiSyncPort) MOSI() gpio.PinOut {
return s.c.MOSI()
}
// MISO returns the SDI (master in, slave out) pin.
func (s *spiSyncPort) MISO() gpio.PinIn {
return s.c.MISO()
}
// CS returns the CSN (chip select) pin.
func (s *spiSyncPort) CS() gpio.PinOut {
return s.c.CS()
}
type spiSyncConn struct {
// Immutable.
f *FT232R
// Initialized at Connect().
edgeInvert bool // CPHA=1
clkActiveLow bool // CPOL=1
noCS bool // CS line is not changed
lsbFirst bool // Default is MSB first
halfDuplex bool // 3 wire mode
}
func (s *spiSyncConn) String() string {
return s.f.String()
}
func (s *spiSyncConn) Tx(w, r []byte) error {
var p = [1]spi.Packet{{W: w, R: r}}
return s.TxPackets(p[:])
}
func (s *spiSyncConn) Duplex() conn.Duplex {
// TODO(maruel): Support half if there's a need.
return conn.Full
}
func (s *spiSyncConn) TxPackets(pkts []spi.Packet) error {
// We need to 'expand' each bit 2 times * 8 bits, which leads
// to a 16x memory usage increase. Adds 5 samples before and after.
totalW := 0
totalR := 0
for _, p := range pkts {
if p.KeepCS {
return errors.New("d2xx: implement spi.Packet.KeepCS")
}
if p.BitsPerWord&7 != 0 {
return errors.New("d2xx: bits must be a multiple of 8")
}
if p.BitsPerWord != 0 && p.BitsPerWord != 8 {
return errors.New("d2xx: implement spi.Packet.BitsPerWord")
}
if err := verifyBuffers(p.W, p.R); err != nil {
return err
}
// TODO(maruel): Correctly calculate offsets.
if len(p.W) != 0 {
totalW += 2 * 8 * len(p.W)
}
if len(p.R) != 0 {
totalR += 2 * 8 * len(p.R)
}
}
// Create a large, single chunk.
var we, re []byte
if totalW != 0 {
totalW += 10
we = make([]byte, 0, totalW)
}
if totalR != 0 {
totalR += 10
re = make([]byte, totalR)
}
const mosi = byte(1) << 0 // TX
const miso = byte(1) << 1 // RX
const clk = byte(1) << 2 // RTS
const cs = byte(1) << 3 // CTS
s.f.mu.Lock()
defer s.f.mu.Unlock()
// https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Data_transmission
csActive := s.f.dvalue & s.f.dmask & 0xF0
csIdle := csActive
if !s.noCS {
csIdle = csActive | cs
}
clkIdle := csActive
clkActive := clkIdle | clk
if s.clkActiveLow {
clkActive, clkIdle = clkIdle, clkActive
csIdle |= clk
}
// Start of tx; assert CS if needed.
we = append(we, csIdle, clkIdle, clkIdle, clkIdle, clkIdle)
for _, p := range pkts {
if len(p.W) == 0 && len(p.R) == 0 {
continue
}
// TODO(maruel): s.halfDuplex.
for _, b := range p.W {
for j := uint(0); j < 8; j++ {
// For each bit, handle clock phase and data phase.
bit := byte(0)
if !s.lsbFirst {
// MSBF
if b&(0x80>>j) != 0 {
bit = mosi
}
} else {
// LSBF
if b&(1<<j) != 0 {
bit = mosi
}
}
if !s.edgeInvert {
// Mode0/2; CPHA=0
we = append(we, clkIdle|bit, clkActive|bit)
} else {
// Mode1/3; CPHA=1
we = append(we, clkActive|bit, clkIdle|bit)
}
}
}
}
// End of tx; deassert CS.
we = append(we, clkIdle, clkIdle, clkIdle, clkIdle, csIdle)
if err := s.f.txLocked(we, re); err != nil {
return err
}
// Extract data from re into r.
for _, p := range pkts {
// TODO(maruel): Correctly calculate offsets.
if len(p.W) == 0 && len(p.R) == 0 {
continue
}
// TODO(maruel): halfDuplex.
for i := range p.R {
// For each bit, read at the right data phase.
b := byte(0)
for j := 0; j < 8; j++ {
if re[5+i*8*2+j*2+1]&byte(1)<<1 != 0 {
if !s.lsbFirst {
// MSBF
b |= 0x80 >> uint(j)
} else {
// LSBF
b |= 1 << uint(j)
}
}
}
p.R[i] = b
}
}
return nil
}
// CLK returns the SCK (clock) pin.
func (s *spiSyncConn) CLK() gpio.PinOut {
return s.f.D2 // RTS
}
// MOSI returns the SDO (master out, slave in) pin.
func (s *spiSyncConn) MOSI() gpio.PinOut {
return s.f.D0 // TX
}
// MISO returns the SDI (master in, slave out) pin.
func (s *spiSyncConn) MISO() gpio.PinIn {
return s.f.D1 // RX
}
// CS returns the CSN (chip select) pin.
func (s *spiSyncConn) CS() gpio.PinOut {
return s.f.D3 // CTS
}
//
func verifyBuffers(w, r []byte) error {
if len(w) != 0 {
if len(r) != 0 {
if len(w) != len(r) {
return errors.New("d2xx: both buffers must have the same size")
}
}
// TODO(maruel): When the buffer is >64Kb, cut it in parts and do not
// request a flush. Still try to read though.
if len(w) > 65536 {
return errors.New("d2xx: maximum buffer size is 64Kb")
}
} else if len(r) != 0 {
// TODO(maruel): Remove, this is not a problem.
if len(r) > 65536 {
return errors.New("d2xx: maximum buffer size is 64Kb")
}
}
return nil
}
var _ spi.PortCloser = &spiMPSEEPort{}
var _ spi.Conn = &spiMPSEEConn{}
var _ spi.PortCloser = &spiSyncPort{}
var _ spi.Conn = &spiSyncConn{}

4
vendor/periph.io/x/host/v3/host.go generated vendored
View File

@@ -6,7 +6,9 @@ package host
import (
"periph.io/x/conn/v3/driver/driverreg"
_ "periph.io/x/host/v3/ftdi"
// TODO(maruel): For now do not include ftdi by default. It's not stable
// enough to warrant being included.
// _ "periph.io/x/host/v3/ftdi"
)
// Init calls driverreg.Init() and returns it as-is.

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm
// +build !arm
package odroidc1

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build arm64
// +build arm64
package pine64

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm && !arm64
// +build !arm,!arm64
package pine64

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package pmem

View File

@@ -384,6 +384,7 @@ const (
memory1GB revisionCode = 2 << memoryShift
memory2GB revisionCode = 3 << memoryShift
memory4GB revisionCode = 4 << memoryShift
memory8GB revisionCode = 5 << memoryShift
sonyUK revisionCode = 0 << manufacturerShift
egoman revisionCode = 1 << manufacturerShift
@@ -413,6 +414,9 @@ const (
boardReserved revisionCode = 0xf << boardShift
boardCM3Plus revisionCode = 0x10 << boardShift
board4B revisionCode = 0x11 << boardShift
boardZero2W revisionCode = 0x12 << boardShift
board400 revisionCode = 0x13 << boardShift
boardCM4 revisionCode = 0x14 << boardShift
)
// features represents the different features on various Raspberry Pi boards.
@@ -502,6 +506,14 @@ func (f *features) init(v uint32) error {
f.hdrAudio = true
f.audioLeft41 = true
f.hdrHDMI = true
case boardZero2W:
f.hdrP1P40 = true
f.hdrHDMI = true
case board400:
f.hdrP1P40 = true
f.hdrHDMI = true
case boardCM4:
// Compute Module does not have a SODIMM header.
default:
return fmt.Errorf("rpi: unknown hardware version: 0x%x", r)
}

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build arm64
// +build arm64
package rpi

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !arm && !arm64
// +build !arm,!arm64
package rpi

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package sysfs

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
//go:build !linux
// +build !linux
package sysfs