diff --git a/src/machine/i2c.go b/src/machine/i2c.go index 3b1b4dd4d..a710e2ac9 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -37,6 +37,9 @@ var ( errI2COverflow = errors.New("I2C receive buffer overflow") errI2COverread = errors.New("I2C transmit buffer overflow") errI2CNotImplemented = errors.New("I2C operation not yet implemented") + errI2CNoDevices = errors.New("i2c: bus has no devices") // simulator only + errI2CMultipleDevices = errors.New("i2c: bus has address conflict") // simulator only + errI2CWrongAddress = errors.New("i2c: bus has devices but none with this address") // simulator only ) // I2CTargetEvent reflects events on the I2C bus diff --git a/src/machine/machine_atmega328p_simulator.go b/src/machine/machine_atmega328p_simulator.go new file mode 100644 index 000000000..b69423a52 --- /dev/null +++ b/src/machine/machine_atmega328p_simulator.go @@ -0,0 +1,5 @@ +//go:build !baremetal && (arduino || arduino_nano) + +package machine + +var I2C0 = &I2C{Bus: 0, PinsSDA: []Pin{PC4}, PinsSCL: []Pin{PC5}} diff --git a/src/machine/machine_atsamd21_simulator.go b/src/machine/machine_atsamd21_simulator.go index 2a22533ec..17ff0ca9d 100644 --- a/src/machine/machine_atsamd21_simulator.go +++ b/src/machine/machine_atsamd21_simulator.go @@ -43,3 +43,21 @@ var TCC2 = &timerType{ {PA01, PA13, PA17}, // channel 1 }, } + +var ( + // According to the datasheet, only some pins have I2C support. However it + // looks like many boards just use any SERCOM I2C instance, even if the + // datasheet says those don't support I2C. I guess they do work in practice, + // then. + // These are: + // * PA00/PA01 for the Adafruit Circuit Playground Express (I2C1, SERCOM1). + // * PB02/PB03 for the Adafruit Circuit Playground Express (I2C0, SERCOM5). + // * PB08/PB09 for the Arduino Nano 33 IoT (I2C0, SERCOM4). + // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf + sercomI2CM0 = &I2C{Bus: 0, PinsSDA: []Pin{PA08}, PinsSCL: []Pin{PA09}} + sercomI2CM1 = &I2C{Bus: 1, PinsSDA: []Pin{PA00, PA16}, PinsSCL: []Pin{PA01, PA17}} + sercomI2CM2 = &I2C{Bus: 2, PinsSDA: []Pin{PA08, PA12}, PinsSCL: []Pin{PA09, PA13}} + sercomI2CM3 = &I2C{Bus: 3, PinsSDA: []Pin{PA16, PA22}, PinsSCL: []Pin{PA17, PA23}} + sercomI2CM4 = &I2C{Bus: 4, PinsSDA: []Pin{PA12, PB08, PB12}, PinsSCL: []Pin{PA13, PB09, PB13}} + sercomI2CM5 = &I2C{Bus: 5, PinsSDA: []Pin{PA22, PB02, PB16, PB30}, PinsSCL: []Pin{PA23, PB03, PB17, PB31}} +) diff --git a/src/machine/machine_fe310_simulator.go b/src/machine/machine_fe310_simulator.go new file mode 100644 index 000000000..2b9e21907 --- /dev/null +++ b/src/machine/machine_fe310_simulator.go @@ -0,0 +1,5 @@ +//go:build !baremetal && hifive1b + +package machine + +var I2C0 = &I2C{Bus: 0, PinsSDA: []Pin{P12}, PinsSCL: []Pin{P13}} diff --git a/src/machine/machine_generic.go b/src/machine/machine_generic.go index 20bded88a..4e2812de4 100644 --- a/src/machine/machine_generic.go +++ b/src/machine/machine_generic.go @@ -4,6 +4,8 @@ package machine import ( "crypto/rand" + "errors" + "slices" ) // Dummy machine package that calls out to external functions. @@ -227,7 +229,9 @@ func adcRead(pin Pin) uint16 // I2C is a generic implementation of the Inter-IC communication protocol. type I2C struct { - Bus uint8 + Bus uint8 + PinsSCL []Pin + PinsSDA []Pin } // I2CConfig is used to store config info for I2C. @@ -239,7 +243,21 @@ type I2CConfig struct { // Configure is intended to setup the I2C interface. func (i2c *I2C) Configure(config I2CConfig) error { - i2cConfigure(i2c.Bus, config.SCL, config.SDA) + if i2c.PinsSCL != nil { + matchSCL := slices.Index(i2c.PinsSCL, config.SCL) >= 0 + matchSDA := slices.Index(i2c.PinsSDA, config.SDA) >= 0 + if !matchSCL && !matchSDA { + return errors.New("i2c: SCL and SDA pins are incorrect for this I2C instance") + } else if !matchSCL { + return errors.New("i2c: SCL pin is incorrect for this I2C instance") + } else if !matchSDA { + return errors.New("i2c: SDA pin is incorrect for this I2C instance") + } + } + if config.Frequency == 0 { + config.Frequency = 100 * KHz + } + i2cConfigure(i2c.Bus, config.SCL, config.SDA, config.Frequency) return nil } @@ -261,19 +279,29 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error { rptr = &r[0] rlen = len(r) } - i2cTransfer(i2c.Bus, wptr, wlen, rptr, rlen) - // TODO: do something with the returned error code. - return nil + errCode := i2cTransfer(i2c.Bus, addr, wptr, wlen, rptr, rlen) + switch errCode { + case 0: + return nil + case 1: + return errI2CNoDevices + case 2: + return errI2CMultipleDevices + case 3: + return errI2CWrongAddress + default: + return errI2CBusError // unknown error code + } } //export __tinygo_i2c_configure -func i2cConfigure(bus uint8, scl Pin, sda Pin) +func i2cConfigure(bus uint8, scl Pin, sda Pin, frequency uint32) //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 +func i2cTransfer(bus uint8, addr uint16, w *byte, wlen int, r *byte, rlen int) int type UART struct { Bus uint8 @@ -336,15 +364,6 @@ var ( sercomUSART4 = UART{4} sercomUSART5 = UART{5} - sercomI2CM0 = &I2C{0} - sercomI2CM1 = &I2C{1} - sercomI2CM2 = &I2C{2} - sercomI2CM3 = &I2C{3} - sercomI2CM4 = &I2C{4} - sercomI2CM5 = &I2C{5} - sercomI2CM6 = &I2C{6} - sercomI2CM7 = &I2C{7} - sercomSPIM0 = &SPI{0} sercomSPIM1 = &SPI{1} sercomSPIM2 = &SPI{2} diff --git a/src/machine/machine_generic_peripherals.go b/src/machine/machine_generic_peripherals.go index 6c95c206c..84efc53f1 100644 --- a/src/machine/machine_generic_peripherals.go +++ b/src/machine/machine_generic_peripherals.go @@ -10,5 +10,4 @@ var ( UART1 = hardwareUART1 SPI0 = &SPI{0} SPI1 = &SPI{1} - I2C0 = &I2C{0} ) diff --git a/src/machine/machine_nrf51_simulator.go b/src/machine/machine_nrf51_simulator.go new file mode 100644 index 000000000..4adabb786 --- /dev/null +++ b/src/machine/machine_nrf51_simulator.go @@ -0,0 +1,6 @@ +//go:build !baremetal && (microbit || pca10031 || hw_651) + +package machine + +var I2C0 = &I2C{Bus: 0} +var I2C1 = &I2C{Bus: 1} diff --git a/src/machine/machine_nrf52840_simulator.go b/src/machine/machine_nrf52840_simulator.go index f04f05c47..97b2afd8c 100644 --- a/src/machine/machine_nrf52840_simulator.go +++ b/src/machine/machine_nrf52840_simulator.go @@ -58,3 +58,6 @@ var PWM3 = &timerType{ nil, // channel 3 }, } + +var I2C0 = &I2C{Bus: 0} +var I2C1 = &I2C{Bus: 1} diff --git a/src/machine/machine_rp2040_simulator.go b/src/machine/machine_rp2040_simulator.go index 6c1d106a8..b8e6adc4d 100644 --- a/src/machine/machine_rp2040_simulator.go +++ b/src/machine/machine_rp2040_simulator.go @@ -94,3 +94,15 @@ var PWM7 = &timerType{ {GPIO15}, // channel B (1) }, } + +var I2C0 = &I2C{ + Bus: 0, + PinsSCL: []Pin{GPIO1, GPIO5, GPIO9, GPIO13, GPIO17, GPIO21}, + PinsSDA: []Pin{GPIO0, GPIO4, GPIO8, GPIO12, GPIO16, GPIO20}, +} + +var I2C1 = &I2C{ + Bus: 0, + PinsSCL: []Pin{GPIO3, GPIO7, GPIO11, GPIO15, GPIO19, GPIO27}, + PinsSDA: []Pin{GPIO2, GPIO6, GPIO10, GPIO14, GPIO18, GPIO26}, +}