Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

machine/i2c: add interface check and implementation where missing for SetBaudRate() #3406

Merged
merged 8 commits into from
Oct 14, 2023
14 changes: 13 additions & 1 deletion src/machine/i2c.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062

package machine

import (
"errors"
)

// If you are getting a compile error on this line please check to see you've
// correctly implemented the methods on the I2C type. They must match
// the i2cController interface method signatures type to type perfectly.
// If not implementing the I2C type please remove your target from the build tags
// at the top of this file.
var _ interface { // 2
Configure(config I2CConfig) error
Tx(addr uint16, w, r []byte) error
SetBaudRate(br uint32) error
} = (*I2C)(nil)

// TWI_FREQ is the I2C bus speed. Normally either 100 kHz, or 400 kHz for high-speed bus.
//
// Deprecated: use 100 * machine.KHz or 400 * machine.KHz instead.
Expand All @@ -25,6 +36,7 @@ var (
errI2CBusError = errors.New("I2C bus error")
errI2COverflow = errors.New("I2C receive buffer overflow")
errI2COverread = errors.New("I2C transmit buffer overflow")
errI2CNotImplemented = errors.New("I2C operation not yet implemented")
)

// I2CTargetEvent reflects events on the I2C bus
Expand Down
7 changes: 6 additions & 1 deletion src/machine/machine_atmega.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ func (i2c *I2C) Configure(config I2CConfig) error {
// Activate internal pullups for twi.
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))

return i2c.SetBaudRate(config.Frequency)
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// Initialize twi prescaler and bit rate.
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))

// twi bit rate formula from atmega128 manual pg. 204:
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
// NOTE: TWBR should be 10 or higher for controller mode.
// It is 72 for a 16mhz board with 100kHz TWI
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
avr.TWBR.Set(uint8(((CPUFrequency() / br) - 16) / 2))

// Enable twi module.
avr.TWCR.Set(avr.TWCR_TWEN)
Expand Down
5 changes: 3 additions & 2 deletions src/machine/machine_atsamd21.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,12 +732,13 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the communication speed for the I2C.
func (i2c *I2C) SetBaudRate(br uint32) {
// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
i2c.Bus.BAUD.Set(baud)
return nil
}

// Tx does a single I2C transaction at the specified address.
Expand Down
5 changes: 3 additions & 2 deletions src/machine/machine_atsamd51.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,12 +1228,13 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the communication speed for the I2C.
func (i2c *I2C) SetBaudRate(br uint32) {
// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// Synchronous arithmetic baudrate, via Adafruit SAMD51 implementation:
// sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
baud := SERCOM_FREQ_REF/(2*br) - 1
i2c.Bus.BAUD.Set(baud)
return nil
}

// Tx does a single I2C transaction at the specified address.
Expand Down
18 changes: 13 additions & 5 deletions src/machine/machine_fe310.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,10 @@ type I2CConfig struct {
SDA Pin
}

var i2cClockFrequency uint32 = 32000000

// Configure is intended to setup the I2C interface.
func (i2c *I2C) Configure(config I2CConfig) error {
var i2cClockFrequency uint32 = 32000000
if config.Frequency == 0 {
config.Frequency = 100 * KHz
}
Expand All @@ -241,7 +242,17 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SCL = I2C0_SCL_PIN
}

var prescaler = i2cClockFrequency/(5*config.Frequency) - 1
i2c.SetBaudRate(config.Frequency)

config.SDA.Configure(PinConfig{Mode: PinI2C})
config.SCL.Configure(PinConfig{Mode: PinI2C})

return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
var prescaler = i2cClockFrequency/(5*br) - 1

// disable controller before setting the prescale registers
i2c.Bus.CTR.ClearBits(sifive.I2C_CTR_EN)
Expand All @@ -253,9 +264,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
// enable controller
i2c.Bus.CTR.SetBits(sifive.I2C_CTR_EN)

config.SDA.Configure(PinConfig{Mode: PinI2C})
config.SCL.Configure(PinConfig{Mode: PinI2C})

return nil
}

Expand Down
9 changes: 9 additions & 0 deletions src/machine/machine_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the I2C frequency.
func (i2c *I2C) SetBaudRate(br uint32) error {
i2cSetBaudRate(i2c.Bus, br)
return nil
}

// Tx does a single I2C transaction at the specified address.
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
Expand All @@ -117,6 +123,9 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
//export __tinygo_i2c_configure
func i2cConfigure(bus uint8, scl Pin, sda Pin)

//export __tinygo_i2c_set_baud_rate
func i2cSetBaudRate(bus uint8, br uint32)

//export __tinygo_i2c_transfer
func i2cTransfer(bus uint8, w *byte, wlen int, r *byte, rlen int) int

Expand Down
19 changes: 13 additions & 6 deletions src/machine/machine_k210.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,19 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SCL.SetFPIOAFunction(FUNC_I2C2_SCLK)
}

div := CPUFrequency() / config.Frequency / 16
i2c.SetBaudRate(config.Frequency)

i2c.Bus.INTR_MASK.Set(0)
i2c.Bus.DMA_CR.Set(0x03)
i2c.Bus.DMA_RDLR.Set(0)
i2c.Bus.DMA_TDLR.Set(0x4)

return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
div := CPUFrequency() / br / 16

// Disable controller before setting the prescale register.
i2c.Bus.ENABLE.Set(0)
Expand All @@ -574,11 +586,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
i2c.Bus.SS_SCL_HCNT.Set(uint32(div))
i2c.Bus.SS_SCL_LCNT.Set(uint32(div))

i2c.Bus.INTR_MASK.Set(0)
i2c.Bus.DMA_CR.Set(0x03)
i2c.Bus.DMA_RDLR.Set(0)
i2c.Bus.DMA_TDLR.Set(0x4)

return nil
}

Expand Down
33 changes: 14 additions & 19 deletions src/machine/machine_mimxrt1062_i2c.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,6 @@ package machine

import (
"device/nxp"
"errors"
)

var (
errI2CWriteTimeout = errors.New("I2C timeout during write")
errI2CReadTimeout = errors.New("I2C timeout during read")
errI2CBusReadyTimeout = errors.New("I2C timeout on bus ready")
errI2CSignalStartTimeout = errors.New("I2C timeout on signal start")
errI2CSignalReadTimeout = errors.New("I2C timeout on signal read")
errI2CSignalStopTimeout = errors.New("I2C timeout on signal stop")
errI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
errI2CBusError = errors.New("I2C bus error")
errI2CNotConfigured = errors.New("I2C interface is not yet configured")
)

// I2CConfig is used to store config info for I2C.
Expand Down Expand Up @@ -150,7 +137,7 @@ func (i2c *I2C) setPins(c I2CConfig) (sda, scl Pin) {
}

// Configure is intended to setup an I2C interface for transmit/receive.
func (i2c *I2C) Configure(config I2CConfig) {
func (i2c *I2C) Configure(config I2CConfig) error {
// init pins
sda, scl := i2c.setPins(config)

Expand All @@ -169,6 +156,14 @@ func (i2c *I2C) Configure(config I2CConfig) {

// reset clock and registers, and enable LPI2C module interface
i2c.reset(freq)

return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c I2C) SetBaudRate(br uint32) error {
// TODO: implement
return errI2CNotImplemented
}

func (i2c I2C) Tx(addr uint16, w, r []byte) error {
Expand Down Expand Up @@ -212,13 +207,13 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
return nil
}

// WriteRegister transmits first the register and then the data to the
// WriteRegisterEx 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 {
func (i2c I2C) WriteRegisterEx(address uint8, register uint8, data []byte) error {
Comment on lines -215 to +216
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why WriteRegisterEx and ReadRegisterEx?
If it's to avoid a duplicate method, I think this is a better solution: #4130

option := transferOption{
flags: transferDefault, // transfer options bit mask (0 = normal transfer)
peripheral: uint16(address), // 7-bit peripheral address
Expand All @@ -232,13 +227,13 @@ func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
return nil
}

// ReadRegister transmits the register, restarts the connection as a read
// ReadRegisterEx 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 {
func (i2c I2C) ReadRegisterEx(address uint8, register uint8, data []byte) error {
option := transferOption{
flags: transferDefault, // transfer options bit mask (0 = normal transfer)
peripheral: uint16(address), // 7-bit peripheral address
Expand Down Expand Up @@ -560,7 +555,7 @@ func (i2c *I2C) controllerReceive(rxBuffer []byte) resultFlag {
return result
}

// controllerReceive performs a polling transmit transfer on the I2C bus.
// controllerTransmit performs a polling transmit transfer on the I2C bus.
func (i2c *I2C) controllerTransmit(txBuffer []byte) resultFlag {
txSize := len(txBuffer)
for txSize > 0 {
Expand Down
24 changes: 18 additions & 6 deletions src/machine/machine_nrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,8 @@ func (i2c *I2C) Configure(config I2CConfig) error {
i2c.setPins(config.SCL, config.SDA)

i2c.mode = config.Mode

if i2c.mode == I2CModeController {
if config.Frequency >= 400*KHz {
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
} else {
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
}
i2c.SetBaudRate(config.Frequency)

i2c.enableAsController()
} else {
Expand All @@ -262,6 +257,23 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the I2C frequency. It has the side effect of also
// enabling the I2C hardware if disabled beforehand.
//
//go:inline
func (i2c *I2C) SetBaudRate(br uint32) error {
switch {
case br >= 400*KHz:
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K400)
case br >= 250*KHz:
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K250)
default:
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K100)
}

return nil
}

// signalStop sends a stop signal to the I2C peripheral and waits for confirmation.
func (i2c *I2C) signalStop() error {
tries := 0
Expand Down
6 changes: 6 additions & 0 deletions src/machine/machine_stm32_i2c_reva.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// TODO: implement
return errI2CNotImplemented
}

func (i2c *I2C) Tx(addr uint16, w, r []byte) error {

if err := i2c.controllerTransmit(addr, w); nil != err {
Expand Down
6 changes: 6 additions & 0 deletions src/machine/machine_stm32_i2c_revb.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// TODO: implement
return errI2CNotImplemented
}

func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
if len(w) > 0 {
if err := i2c.controllerTransmit(addr, w); nil != err {
Expand Down
Loading