mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
aac959eb7b
This should make the software I2C driver more portable, and potentially usable on chips that aren't yet supported. More importantly, it avoids some guesswork in the various chip-specific timing code.
289 lines
6.1 KiB
Go
289 lines
6.1 KiB
Go
package i2csoft
|
|
|
|
import (
|
|
"errors"
|
|
"machine"
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers/delay"
|
|
)
|
|
|
|
// I2C is an I2C implementation by Software. Since it is implemented by
|
|
// software, it can be used with microcontrollers that do not have I2C
|
|
// function. This is not efficient but works around broken or missing drivers.
|
|
type I2C struct {
|
|
scl machine.Pin
|
|
sda machine.Pin
|
|
nack bool
|
|
baudrate uint32
|
|
}
|
|
|
|
// I2CConfig is used to store config info for I2C.
|
|
type I2CConfig struct {
|
|
Frequency uint32
|
|
SCL machine.Pin
|
|
SDA machine.Pin
|
|
}
|
|
|
|
var (
|
|
errSI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
|
|
)
|
|
|
|
// New returns the i2csoft driver. For the arguments, specify the pins to be
|
|
// used as SCL and SDA. As I2C is implemented in software, any GPIO pin can be
|
|
// specified.
|
|
func New(sclPin, sdaPin machine.Pin) *I2C {
|
|
return &I2C{
|
|
scl: sclPin,
|
|
sda: sdaPin,
|
|
baudrate: 100e3,
|
|
}
|
|
}
|
|
|
|
// Configure is intended to setup the I2C interface.
|
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
|
// Default I2C bus speed is 100 kHz.
|
|
if config.Frequency != 0 {
|
|
i2c.SetBaudRate(config.Frequency)
|
|
}
|
|
|
|
// This exists for compatibility with machine.I2CConfig. SCL and SDA must
|
|
// be set at the same time. Because Pin(0) is sometimes set, it is not
|
|
// checked for 0.
|
|
if config.SCL != config.SDA {
|
|
i2c.scl = config.SCL
|
|
i2c.sda = config.SDA
|
|
}
|
|
|
|
// enable pins
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.sda.High()
|
|
i2c.scl.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.scl.High()
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetBaudRate sets the communication speed for the I2C.
|
|
func (i2c *I2C) SetBaudRate(br uint32) {
|
|
// At this time, the value of i2c.baudrate is ignored because it is fixed
|
|
// at 100 kHz. SetBaudrate() is exist for compatibility with machine.I2C.
|
|
i2c.baudrate = br
|
|
}
|
|
|
|
// Tx does a single I2C transaction at the specified address.
|
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
|
i2c.nack = false
|
|
if len(w) != 0 {
|
|
// send start/address for write
|
|
i2c.sendAddress(addr, true)
|
|
|
|
// wait until transmission complete
|
|
|
|
// ACK received (0: ACK, 1: NACK)
|
|
if i2c.nack {
|
|
i2c.signalStop()
|
|
return errSI2CAckExpected
|
|
}
|
|
|
|
// write data
|
|
for _, b := range w {
|
|
i2c.writeByte(b)
|
|
}
|
|
|
|
i2c.signalStop()
|
|
}
|
|
if len(r) != 0 {
|
|
// send start/address for read
|
|
i2c.sendAddress(addr, false)
|
|
|
|
// wait transmission complete
|
|
|
|
// ACK received (0: ACK, 1: NACK)
|
|
if i2c.nack {
|
|
i2c.signalStop()
|
|
return errSI2CAckExpected
|
|
}
|
|
|
|
// read first byte
|
|
r[0] = i2c.readByte()
|
|
for i := 1; i < len(r); i++ {
|
|
// Send an ACK
|
|
|
|
i2c.signalRead()
|
|
|
|
// Read data and send the ACK
|
|
r[i] = i2c.readByte()
|
|
}
|
|
|
|
// Send NACK to end transmission
|
|
i2c.sendNack()
|
|
|
|
i2c.signalStop()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// writeByte writes a single byte to the I2C bus.
|
|
func (i2c *I2C) writeByte(data byte) {
|
|
// Send data byte
|
|
i2c.scl.Low()
|
|
i2c.sda.High()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.wait()
|
|
|
|
for i := 0; i < 8; i++ {
|
|
i2c.scl.Low()
|
|
if ((data >> (7 - i)) & 1) == 1 {
|
|
i2c.sda.High()
|
|
} else {
|
|
i2c.sda.Low()
|
|
}
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
|
|
i2c.scl.Low()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
|
|
i2c.nack = i2c.sda.Get()
|
|
|
|
i2c.wait()
|
|
|
|
// wait until transmission successful
|
|
}
|
|
|
|
// sendAddress sends the address and start signal
|
|
func (i2c *I2C) sendAddress(address uint16, write bool) {
|
|
data := (address << 1)
|
|
if !write {
|
|
data |= 1 // set read flag
|
|
}
|
|
|
|
i2c.scl.High()
|
|
i2c.sda.Low()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
for i := 0; i < 8; i++ {
|
|
i2c.scl.Low()
|
|
if ((data >> (7 - i)) & 1) == 1 {
|
|
i2c.sda.High()
|
|
} else {
|
|
i2c.sda.Low()
|
|
}
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
|
|
i2c.scl.Low()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
|
|
i2c.nack = i2c.sda.Get()
|
|
|
|
i2c.wait()
|
|
|
|
// wait until bus ready
|
|
}
|
|
|
|
func (i2c *I2C) signalStop() {
|
|
i2c.scl.Low()
|
|
i2c.sda.Low()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.sda.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
|
|
func (i2c *I2C) signalRead() {
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.Low()
|
|
i2c.sda.Low()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
|
|
func (i2c *I2C) readByte() byte {
|
|
var data byte
|
|
for i := 0; i < 8; i++ {
|
|
i2c.scl.Low()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
if i2c.sda.Get() {
|
|
data |= 1 << (7 - i)
|
|
}
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
return data
|
|
}
|
|
|
|
func (i2c *I2C) sendNack() {
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.Low()
|
|
i2c.sda.High()
|
|
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
i2c.wait()
|
|
i2c.wait()
|
|
i2c.scl.High()
|
|
i2c.wait()
|
|
i2c.wait()
|
|
}
|
|
|
|
// WriteRegister transmits first the register and then the data to the
|
|
// peripheral device.
|
|
//
|
|
// Many I2C-compatible devices are organized in terms of registers. This method
|
|
// is a shortcut to easily write to such registers. Also, it only works for
|
|
// devices with 7-bit addresses, which is the vast majority.
|
|
func (i2c *I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
|
buf := make([]uint8, len(data)+1)
|
|
buf[0] = register
|
|
copy(buf[1:], data)
|
|
return i2c.Tx(uint16(address), buf, nil)
|
|
}
|
|
|
|
// ReadRegister transmits the register, restarts the connection as a read
|
|
// operation, and reads the response.
|
|
//
|
|
// Many I2C-compatible devices are organized in terms of registers. This method
|
|
// is a shortcut to easily read such registers. Also, it only works for devices
|
|
// with 7-bit addresses, which is the vast majority.
|
|
func (i2c *I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
|
return i2c.Tx(uint16(address), []byte{register}, data)
|
|
}
|
|
|
|
// wait waits for half the time of the SCL operation interval.
|
|
func (i2c *I2C) wait() {
|
|
delay.Sleep(50 * time.Microsecond) // half of a 100kHz cycle (50µs)
|
|
}
|