Skip to content

Commit

Permalink
machine/i2c: add interface check and implementation where missing for…
Browse files Browse the repository at this point in the history
… SetBaudRate() (tinygo-org#3406)

* machine/i2c: add interface check and placeholder implementation where missing for SetBaudRate()
  • Loading branch information
deadprogram authored and crypto-smoke committed Feb 14, 2024
1 parent a057d3c commit eb6e8ea
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 42 deletions.
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 {
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

0 comments on commit eb6e8ea

Please sign in to comment.