From 14828afd648253af552f9449d81988e91e375b68 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Wed, 9 Feb 2022 21:49:33 -0500 Subject: [PATCH 1/5] machine/esp32c3: implement i2c for esp32-c3 --- src/machine/i2c.go | 2 +- src/machine/machine_esp32c3.go | 325 +++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 1 deletion(-) diff --git a/src/machine/i2c.go b/src/machine/i2c.go index 5fdf2d5266..27bd85daf7 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -1,4 +1,4 @@ -//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062 +//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062 || esp32c3 package machine diff --git a/src/machine/machine_esp32c3.go b/src/machine/machine_esp32c3.go index 1ad3099806..7418626087 100644 --- a/src/machine/machine_esp32c3.go +++ b/src/machine/machine_esp32c3.go @@ -111,6 +111,10 @@ func (p Pin) outFunc() *volatile.Register32 { return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4)) } +func (p Pin) pinReg() *volatile.Register32 { + return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + uintptr(p)*4))) +} + // inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input // function selection. func inFunc(signal uint32) *volatile.Register32 { @@ -596,3 +600,324 @@ func (usbdev *USB_DEVICE) flush() { for usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 { } } + +// I2C code + +var ( + I2C0 = &I2C{} +) + +type I2C struct{} + +// I2CConfig is used to store config info for I2C. +type I2CConfig struct { + Frequency uint32 // in Hz + SCL Pin + SDA Pin +} + +const ( + clkXTAL = 0 + clkFOSC = 1 + clkXTALFrequency = uint32(40e6) + clkFOSCFrequency = uint32(17.5e6) + i2cClkSourceFrequency = clkXTALFrequency + i2cClkSource = clkXTAL +) + +func (i2c *I2C) Configure(config I2CConfig) error { + i2c.initClock(config) + i2c.initNoiseFilter() + i2c.initPins(config) + i2c.initFrequency(config) + i2c.startMaster() + return nil +} + +// go: inline +func (i2c *I2C) initClock(config I2CConfig) { + // reset I2C clock + esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(1) + esp.SYSTEM.SetPERIP_CLK_EN0_EXT0_CLK_EN(1) + esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(0) + // disable interrupts + esp.I2C.INT_ENA.ClearBits(0x3fff) + esp.I2C.INT_CLR.ClearBits(0x3fff) + + esp.I2C.SetCLK_CONF_SCLK_SEL(i2cClkSource) + esp.I2C.SetCLK_CONF_SCLK_ACTIVE(1) + esp.I2C.SetCLK_CONF_SCLK_DIV_NUM(i2cClkSourceFrequency / (config.Frequency * 1024)) + esp.I2C.SetCTR_CLK_EN(1) +} + +// go: inline +func (i2c *I2C) initNoiseFilter() { + esp.I2C.FILTER_CFG.Set(0x377) +} + +// go: inline +func (i2c *I2C) initPins(config I2CConfig) { + var muxConfig uint32 + const function = 1 // function 1 is just GPIO + + // SDA + muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos + // Make this pin an input pin (always). + muxConfig |= esp.IO_MUX_GPIO_FUN_IE + // Set drive strength: 0 is lowest, 3 is highest. + muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos + config.SDA.mux().Set(muxConfig) + config.SDA.outFunc().Set(54) + inFunc(54).Set(uint32(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | config.SDA)) + config.SDA.Set(true) + // Configure the pad with the given IO mux configuration. + config.SDA.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) + + esp.GPIO.ENABLE.SetBits(1 << int(config.SDA)) + esp.I2C.SetCTR_SDA_FORCE_OUT(1) + + // SCL + muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos + // Make this pin an input pin (always). + muxConfig |= esp.IO_MUX_GPIO_FUN_IE + // Set drive strength: 0 is lowest, 3 is highest. + muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos + config.SCL.mux().Set(muxConfig) + config.SCL.outFunc().Set(53) + inFunc(53).Set(uint32(config.SCL)) + config.SCL.Set(true) + // Configure the pad with the given IO mux configuration. + config.SCL.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) + + esp.GPIO.ENABLE.SetBits(1 << int(config.SCL)) + esp.I2C.SetCTR_SCL_FORCE_OUT(1) +} + +// go: inline +func (i2c *I2C) initFrequency(config I2CConfig) { + + clkmDiv := i2cClkSourceFrequency/(config.Frequency*1024) + 1 + sclkFreq := i2cClkSourceFrequency / clkmDiv + halfCycle := sclkFreq / config.Frequency / 2 + //SCL + sclLow := halfCycle + sclWaitHigh := uint32(0) + if config.Frequency > 50000 { + sclWaitHigh = halfCycle / 8 // compensate the time when freq > 50K + } + sclHigh := halfCycle - sclWaitHigh + // SDA + sdaHold := halfCycle / 4 + sda_sample := halfCycle / 2 + setup := halfCycle + hold := halfCycle + + esp.I2C.SetSCL_LOW_PERIOD(sclLow - 1) + esp.I2C.SetSCL_HIGH_PERIOD(sclHigh) + esp.I2C.SetSCL_HIGH_PERIOD_SCL_WAIT_HIGH_PERIOD(25) + esp.I2C.SetSCL_RSTART_SETUP_TIME(setup) + esp.I2C.SetSCL_STOP_SETUP_TIME(setup) + esp.I2C.SetSCL_START_HOLD_TIME(hold - 1) + esp.I2C.SetSCL_STOP_HOLD_TIME(hold - 1) + esp.I2C.SetSDA_SAMPLE_TIME(sda_sample) + esp.I2C.SetSDA_HOLD_TIME(sdaHold) +} + +// go: inline +func (i2c *I2C) startMaster() { + // FIFO mode for data + esp.I2C.SetFIFO_CONF_NONFIFO_EN(0) + // Reset TX & RX buffers + esp.I2C.SetFIFO_CONF_RX_FIFO_RST(1) + esp.I2C.SetFIFO_CONF_RX_FIFO_RST(0) + esp.I2C.SetFIFO_CONF_TX_FIFO_RST(1) + esp.I2C.SetFIFO_CONF_TX_FIFO_RST(0) + // set timeout value + esp.I2C.TO.Set(0x10) + // enable master mode + esp.I2C.CTR.Set(0x113) + esp.I2C.SetCTR_CONF_UPGATE(1) + resetMaster() +} + +// go: inline +func resetMaster() { + // reset FSM + esp.I2C.SetCTR_FSM_RST(1) + // clear the bus + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(9) + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_EN(1) + esp.I2C.SetSCL_STRETCH_CONF_SLAVE_SCL_STRETCH_EN(1) + esp.I2C.SetCTR_CONF_UPGATE(1) + esp.I2C.FILTER_CFG.Set(0x377) + // wait for SCL_RST_SLV_EN + for esp.I2C.GetSCL_SP_CONF_SCL_RST_SLV_EN() != 0 { + } + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(0) +} + +type i2cCommandType = uint32 +type i2cAck = uint32 + +const ( + i2cCMD_RSTART i2cCommandType = 6 << 11 + i2cCMD_WRITE i2cCommandType = 1<<11 | 1<<8 // WRITE + ack_check_en + i2cCMD_READ i2cCommandType = 3<<11 | 1<<8 // READ + ack_check_en + i2cCMD_READLAST i2cCommandType = 3<<11 | 5<<8 // READ + ack_check_en + NACK + i2cCMD_STOP i2cCommandType = 2 << 11 + i2cCMD_END i2cCommandType = 4 << 11 +) + +type i2cCommand struct { + cmd i2cCommandType + data []byte + head int +} + +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { + const intMask = esp.I2C_INT_CLR_END_DETECT_INT_CLR_Msk | esp.I2C_INT_CLR_TRANS_COMPLETE_INT_CLR_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk + esp.I2C.INT_CLR.SetBits(intMask) + esp.I2C.INT_ENA.SetBits(intMask) + esp.I2C.SetCTR_CONF_UPGATE(1) + + defer func() { + esp.I2C.INT_CLR.SetBits(intMask) + esp.I2C.INT_ENA.ClearBits(intMask) + }() + + timeoutNS := int64(timeoutMS) * 1000000 + needAddress := true + needRestart := false + var readTo []byte + for cmdIdx, reg := 0, &esp.I2C.COMD0; cmdIdx < len(cmd); { + c := &cmd[cmdIdx] + + switch c.cmd { + case i2cCMD_RSTART: + reg.Set(i2cCMD_RSTART) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + cmdIdx++ + + case i2cCMD_WRITE: + count := 32 + if needAddress { + needAddress = false + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr) & 0x7f) << 1) + count-- + esp.I2C.SLAVE_ADDR.Set(uint32(addr)) + esp.I2C.SetCTR_CONF_UPGATE(1) + } + for ; count > 0 && c.head < len(c.data); count, c.head = count-1, c.head+1 { + esp.I2C.SetFIFO_DATA_FIFO_RDATA(uint32(c.data[c.head])) + } + reg.Set(i2cCMD_WRITE | uint32(32-count)) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + + if c.head < len(c.data) { + reg.Set(i2cCMD_END) + reg = nil + } else { + cmdIdx++ + } + needRestart = true + + case i2cCMD_READ: + if needAddress { + needAddress = false + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) + esp.I2C.SLAVE_ADDR.Set(uint32(addr)) + reg.Set(i2cCMD_WRITE | 1) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + } + if needRestart { + // We need to send RESTART again after i2cCMD_WRITE. + reg.Set(i2cCMD_RSTART) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg.Set(i2cCMD_WRITE | 1) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) + needRestart = false + } + count := 32 + bytes := len(c.data) - c.head + // Only last byte in sequence must be sent with ACK set to 1 to indicate end of data. + split := bytes <= count + if split { + bytes-- + } + if bytes > 32 { + bytes = 32 + } + reg.Set(i2cCMD_READ | uint32(bytes)) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + + if split { + reg.Set(i2cCMD_READLAST | 1) + reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte + cmdIdx++ + } else { + reg.Set(i2cCMD_END) + readTo = c.data[c.head : c.head+bytes] + reg = nil + } + + case i2cCMD_STOP: + reg.Set(i2cCMD_STOP) + reg = nil + cmdIdx++ + } + if reg == nil { + // transmit now + esp.I2C.SetCTR_CONF_UPGATE(1) + esp.I2C.SetCTR_TRANS_START(1) + end := nanotime() + timeoutNS + var mask uint32 + for mask = esp.I2C.INT_STATUS.Get(); mask&intMask == 0; mask = esp.I2C.INT_STATUS.Get() { + if nanotime() > end { + if readTo != nil { + return errI2CReadTimeout + } + return errI2CWriteTimeout + } + } + if mask == esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk { + if readTo != nil { + return errI2CReadTimeout + } + return errI2CWriteTimeout + } + esp.I2C.INT_CLR.SetBits(intMask) + for i := 0; i < len(readTo); i++ { + readTo[i] = byte(esp.I2C.GetFIFO_DATA_FIFO_RDATA() & 0xff) + c.head++ + } + readTo = nil + reg = &esp.I2C.COMD0 + } + } + return nil +} + +// 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) (err error) { + // timeout in microseconds. + const timeout = 100 * 1000 // 40ms is a reasonable time for a real-time system. + + cmd := make([]i2cCommand, 0, 8) + cmd = append(cmd, i2cCommand{cmd: i2cCMD_RSTART}) + if len(w) > 0 { + cmd = append(cmd, i2cCommand{cmd: i2cCMD_WRITE, data: w}) + } + if len(r) > 0 { + cmd = append(cmd, i2cCommand{cmd: i2cCMD_READ, data: r}) + } + cmd = append(cmd, i2cCommand{cmd: i2cCMD_STOP}) + + return i2c.transmit(addr, cmd, timeout) +} From d1f81103866d157fabf6b422c96ec40942cf525d Mon Sep 17 00:00:00 2001 From: deadprogram Date: Tue, 28 Nov 2023 15:06:56 +0100 Subject: [PATCH 2/5] machine/esp32c3: handle defaults for I2C configuration Signed-off-by: deadprogram --- src/machine/machine_esp32c3.go | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/machine/machine_esp32c3.go b/src/machine/machine_esp32c3.go index 7418626087..bced646aad 100644 --- a/src/machine/machine_esp32c3.go +++ b/src/machine/machine_esp32c3.go @@ -626,6 +626,16 @@ const ( ) func (i2c *I2C) Configure(config I2CConfig) error { + if config.Frequency == 0 { + config.Frequency = 400 * KHz + } + if config.SCL == 0 { + config.SCL = SCL_PIN + } + if config.SDA == 0 { + config.SDA = SDA_PIN + } + i2c.initClock(config) i2c.initNoiseFilter() i2c.initPins(config) @@ -634,7 +644,7 @@ func (i2c *I2C) Configure(config I2CConfig) error { return nil } -// go: inline +//go:inline func (i2c *I2C) initClock(config I2CConfig) { // reset I2C clock esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(1) @@ -650,12 +660,12 @@ func (i2c *I2C) initClock(config I2CConfig) { esp.I2C.SetCTR_CLK_EN(1) } -// go: inline +//go:inline func (i2c *I2C) initNoiseFilter() { esp.I2C.FILTER_CFG.Set(0x377) } -// go: inline +//go:inline func (i2c *I2C) initPins(config I2CConfig) { var muxConfig uint32 const function = 1 // function 1 is just GPIO @@ -693,7 +703,7 @@ func (i2c *I2C) initPins(config I2CConfig) { esp.I2C.SetCTR_SCL_FORCE_OUT(1) } -// go: inline +//go:inline func (i2c *I2C) initFrequency(config I2CConfig) { clkmDiv := i2cClkSourceFrequency/(config.Frequency*1024) + 1 @@ -723,7 +733,7 @@ func (i2c *I2C) initFrequency(config I2CConfig) { esp.I2C.SetSDA_HOLD_TIME(sdaHold) } -// go: inline +//go:inline func (i2c *I2C) startMaster() { // FIFO mode for data esp.I2C.SetFIFO_CONF_NONFIFO_EN(0) @@ -740,7 +750,7 @@ func (i2c *I2C) startMaster() { resetMaster() } -// go: inline +//go:inline func resetMaster() { // reset FSM esp.I2C.SetCTR_FSM_RST(1) @@ -907,7 +917,7 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { // bytes and stores them in r, and generates a stop condition on the bus. func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) { // timeout in microseconds. - const timeout = 100 * 1000 // 40ms is a reasonable time for a real-time system. + const timeout = 40 // 40ms is a reasonable time for a real-time system. cmd := make([]i2cCommand, 0, 8) cmd = append(cmd, i2cCommand{cmd: i2cCMD_RSTART}) @@ -921,3 +931,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) { return i2c.transmit(addr, cmd, timeout) } + +func (i2c *I2C) SetBaudRate(br uint32) error { + return nil +} From 037f83218af339940845fa4edbb1edebf3e0ee2f Mon Sep 17 00:00:00 2001 From: deadprogram Date: Tue, 28 Nov 2023 19:49:35 +0100 Subject: [PATCH 3/5] machine/esp32c3: corrected implementation for error handling and when to expect NACK Signed-off-by: deadprogram --- src/machine/machine_esp32c3.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/machine/machine_esp32c3.go b/src/machine/machine_esp32c3.go index bced646aad..a2ba69ccac 100644 --- a/src/machine/machine_esp32c3.go +++ b/src/machine/machine_esp32c3.go @@ -788,7 +788,7 @@ type i2cCommand struct { func nanotime() int64 func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { - const intMask = esp.I2C_INT_CLR_END_DETECT_INT_CLR_Msk | esp.I2C_INT_CLR_TRANS_COMPLETE_INT_CLR_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk + const intMask = esp.I2C_INT_STATUS_END_DETECT_INT_ST_Msk | esp.I2C_INT_STATUS_TRANS_COMPLETE_INT_ST_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk esp.I2C.INT_CLR.SetBits(intMask) esp.I2C.INT_ENA.SetBits(intMask) esp.I2C.SetCTR_CONF_UPGATE(1) @@ -801,6 +801,7 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { timeoutNS := int64(timeoutMS) * 1000000 needAddress := true needRestart := false + readLast := false var readTo []byte for cmdIdx, reg := 0, &esp.I2C.COMD0; cmdIdx < len(cmd); { c := &cmd[cmdIdx] @@ -808,7 +809,7 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { switch c.cmd { case i2cCMD_RSTART: reg.Set(i2cCMD_RSTART) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg = nextAddress(reg) cmdIdx++ case i2cCMD_WRITE: @@ -824,7 +825,7 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { esp.I2C.SetFIFO_DATA_FIFO_RDATA(uint32(c.data[c.head])) } reg.Set(i2cCMD_WRITE | uint32(32-count)) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg = nextAddress(reg) if c.head < len(c.data) { reg.Set(i2cCMD_END) @@ -840,14 +841,16 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) esp.I2C.SLAVE_ADDR.Set(uint32(addr)) reg.Set(i2cCMD_WRITE | 1) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg = nextAddress(reg) } if needRestart { // We need to send RESTART again after i2cCMD_WRITE. reg.Set(i2cCMD_RSTART) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + + reg = nextAddress(reg) reg.Set(i2cCMD_WRITE | 1) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + + reg = nextAddress(reg) esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) needRestart = false } @@ -862,11 +865,12 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { bytes = 32 } reg.Set(i2cCMD_READ | uint32(bytes)) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg = nextAddress(reg) if split { + readLast = true reg.Set(i2cCMD_READLAST | 1) - reg = (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(reg)) + 4))) + reg = nextAddress(reg) readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte cmdIdx++ } else { @@ -894,7 +898,10 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { return errI2CWriteTimeout } } - if mask == esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk { + switch { + case mask&esp.I2C_INT_STATUS_NACK_INT_ST_Msk != 0 && !readLast: + return errI2CAckExpected + case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0: if readTo != nil { return errI2CReadTimeout } @@ -935,3 +942,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) { func (i2c *I2C) SetBaudRate(br uint32) error { return nil } + +func nextAddress(reg *volatile.Register32) *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(reg), 4)) +} From 107b53804fc79e7a6a1e495a257fb27a0e8b18ce Mon Sep 17 00:00:00 2001 From: deadprogram Date: Tue, 28 Nov 2023 23:54:58 +0100 Subject: [PATCH 4/5] build: can only build boards with board files for pin mapping Signed-off-by: deadprogram --- GNUmakefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 32d5d4fecf..2bc62d771e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -754,9 +754,7 @@ ifneq ($(XTENSA), 0) $(TINYGO) build -size short -o test.bin -target mch2022 examples/serial @$(MD5SUM) test.bin endif - $(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial - @$(MD5SUM) test.bin - $(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial + $(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/serial @$(MD5SUM) test.bin $(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial @$(MD5SUM) test.bin From f6b58df78426fa7665d4ff071946704c34cfcc0b Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 29 Nov 2023 09:56:34 +0100 Subject: [PATCH 5/5] machine/esp32c3: move i2c implementation into separate file to skip m5stamp-c3 since it does not appear to expose those pins Signed-off-by: deadprogram --- src/machine/i2c.go | 2 +- src/machine/machine_esp32c3.go | 346 ---------------------------- src/machine/machine_esp32c3_i2c.go | 353 +++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+), 347 deletions(-) create mode 100644 src/machine/machine_esp32c3_i2c.go diff --git a/src/machine/i2c.go b/src/machine/i2c.go index 27bd85daf7..5c4d1a5c0b 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -1,4 +1,4 @@ -//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062 || esp32c3 +//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062 || (esp32c3 && !m5stamp_c3) package machine diff --git a/src/machine/machine_esp32c3.go b/src/machine/machine_esp32c3.go index a2ba69ccac..cc41531871 100644 --- a/src/machine/machine_esp32c3.go +++ b/src/machine/machine_esp32c3.go @@ -600,349 +600,3 @@ func (usbdev *USB_DEVICE) flush() { for usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 { } } - -// I2C code - -var ( - I2C0 = &I2C{} -) - -type I2C struct{} - -// I2CConfig is used to store config info for I2C. -type I2CConfig struct { - Frequency uint32 // in Hz - SCL Pin - SDA Pin -} - -const ( - clkXTAL = 0 - clkFOSC = 1 - clkXTALFrequency = uint32(40e6) - clkFOSCFrequency = uint32(17.5e6) - i2cClkSourceFrequency = clkXTALFrequency - i2cClkSource = clkXTAL -) - -func (i2c *I2C) Configure(config I2CConfig) error { - if config.Frequency == 0 { - config.Frequency = 400 * KHz - } - if config.SCL == 0 { - config.SCL = SCL_PIN - } - if config.SDA == 0 { - config.SDA = SDA_PIN - } - - i2c.initClock(config) - i2c.initNoiseFilter() - i2c.initPins(config) - i2c.initFrequency(config) - i2c.startMaster() - return nil -} - -//go:inline -func (i2c *I2C) initClock(config I2CConfig) { - // reset I2C clock - esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(1) - esp.SYSTEM.SetPERIP_CLK_EN0_EXT0_CLK_EN(1) - esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(0) - // disable interrupts - esp.I2C.INT_ENA.ClearBits(0x3fff) - esp.I2C.INT_CLR.ClearBits(0x3fff) - - esp.I2C.SetCLK_CONF_SCLK_SEL(i2cClkSource) - esp.I2C.SetCLK_CONF_SCLK_ACTIVE(1) - esp.I2C.SetCLK_CONF_SCLK_DIV_NUM(i2cClkSourceFrequency / (config.Frequency * 1024)) - esp.I2C.SetCTR_CLK_EN(1) -} - -//go:inline -func (i2c *I2C) initNoiseFilter() { - esp.I2C.FILTER_CFG.Set(0x377) -} - -//go:inline -func (i2c *I2C) initPins(config I2CConfig) { - var muxConfig uint32 - const function = 1 // function 1 is just GPIO - - // SDA - muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos - // Make this pin an input pin (always). - muxConfig |= esp.IO_MUX_GPIO_FUN_IE - // Set drive strength: 0 is lowest, 3 is highest. - muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos - config.SDA.mux().Set(muxConfig) - config.SDA.outFunc().Set(54) - inFunc(54).Set(uint32(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | config.SDA)) - config.SDA.Set(true) - // Configure the pad with the given IO mux configuration. - config.SDA.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) - - esp.GPIO.ENABLE.SetBits(1 << int(config.SDA)) - esp.I2C.SetCTR_SDA_FORCE_OUT(1) - - // SCL - muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos - // Make this pin an input pin (always). - muxConfig |= esp.IO_MUX_GPIO_FUN_IE - // Set drive strength: 0 is lowest, 3 is highest. - muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos - config.SCL.mux().Set(muxConfig) - config.SCL.outFunc().Set(53) - inFunc(53).Set(uint32(config.SCL)) - config.SCL.Set(true) - // Configure the pad with the given IO mux configuration. - config.SCL.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) - - esp.GPIO.ENABLE.SetBits(1 << int(config.SCL)) - esp.I2C.SetCTR_SCL_FORCE_OUT(1) -} - -//go:inline -func (i2c *I2C) initFrequency(config I2CConfig) { - - clkmDiv := i2cClkSourceFrequency/(config.Frequency*1024) + 1 - sclkFreq := i2cClkSourceFrequency / clkmDiv - halfCycle := sclkFreq / config.Frequency / 2 - //SCL - sclLow := halfCycle - sclWaitHigh := uint32(0) - if config.Frequency > 50000 { - sclWaitHigh = halfCycle / 8 // compensate the time when freq > 50K - } - sclHigh := halfCycle - sclWaitHigh - // SDA - sdaHold := halfCycle / 4 - sda_sample := halfCycle / 2 - setup := halfCycle - hold := halfCycle - - esp.I2C.SetSCL_LOW_PERIOD(sclLow - 1) - esp.I2C.SetSCL_HIGH_PERIOD(sclHigh) - esp.I2C.SetSCL_HIGH_PERIOD_SCL_WAIT_HIGH_PERIOD(25) - esp.I2C.SetSCL_RSTART_SETUP_TIME(setup) - esp.I2C.SetSCL_STOP_SETUP_TIME(setup) - esp.I2C.SetSCL_START_HOLD_TIME(hold - 1) - esp.I2C.SetSCL_STOP_HOLD_TIME(hold - 1) - esp.I2C.SetSDA_SAMPLE_TIME(sda_sample) - esp.I2C.SetSDA_HOLD_TIME(sdaHold) -} - -//go:inline -func (i2c *I2C) startMaster() { - // FIFO mode for data - esp.I2C.SetFIFO_CONF_NONFIFO_EN(0) - // Reset TX & RX buffers - esp.I2C.SetFIFO_CONF_RX_FIFO_RST(1) - esp.I2C.SetFIFO_CONF_RX_FIFO_RST(0) - esp.I2C.SetFIFO_CONF_TX_FIFO_RST(1) - esp.I2C.SetFIFO_CONF_TX_FIFO_RST(0) - // set timeout value - esp.I2C.TO.Set(0x10) - // enable master mode - esp.I2C.CTR.Set(0x113) - esp.I2C.SetCTR_CONF_UPGATE(1) - resetMaster() -} - -//go:inline -func resetMaster() { - // reset FSM - esp.I2C.SetCTR_FSM_RST(1) - // clear the bus - esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(9) - esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_EN(1) - esp.I2C.SetSCL_STRETCH_CONF_SLAVE_SCL_STRETCH_EN(1) - esp.I2C.SetCTR_CONF_UPGATE(1) - esp.I2C.FILTER_CFG.Set(0x377) - // wait for SCL_RST_SLV_EN - for esp.I2C.GetSCL_SP_CONF_SCL_RST_SLV_EN() != 0 { - } - esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(0) -} - -type i2cCommandType = uint32 -type i2cAck = uint32 - -const ( - i2cCMD_RSTART i2cCommandType = 6 << 11 - i2cCMD_WRITE i2cCommandType = 1<<11 | 1<<8 // WRITE + ack_check_en - i2cCMD_READ i2cCommandType = 3<<11 | 1<<8 // READ + ack_check_en - i2cCMD_READLAST i2cCommandType = 3<<11 | 5<<8 // READ + ack_check_en + NACK - i2cCMD_STOP i2cCommandType = 2 << 11 - i2cCMD_END i2cCommandType = 4 << 11 -) - -type i2cCommand struct { - cmd i2cCommandType - data []byte - head int -} - -//go:linkname nanotime runtime.nanotime -func nanotime() int64 - -func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { - const intMask = esp.I2C_INT_STATUS_END_DETECT_INT_ST_Msk | esp.I2C_INT_STATUS_TRANS_COMPLETE_INT_ST_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk - esp.I2C.INT_CLR.SetBits(intMask) - esp.I2C.INT_ENA.SetBits(intMask) - esp.I2C.SetCTR_CONF_UPGATE(1) - - defer func() { - esp.I2C.INT_CLR.SetBits(intMask) - esp.I2C.INT_ENA.ClearBits(intMask) - }() - - timeoutNS := int64(timeoutMS) * 1000000 - needAddress := true - needRestart := false - readLast := false - var readTo []byte - for cmdIdx, reg := 0, &esp.I2C.COMD0; cmdIdx < len(cmd); { - c := &cmd[cmdIdx] - - switch c.cmd { - case i2cCMD_RSTART: - reg.Set(i2cCMD_RSTART) - reg = nextAddress(reg) - cmdIdx++ - - case i2cCMD_WRITE: - count := 32 - if needAddress { - needAddress = false - esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr) & 0x7f) << 1) - count-- - esp.I2C.SLAVE_ADDR.Set(uint32(addr)) - esp.I2C.SetCTR_CONF_UPGATE(1) - } - for ; count > 0 && c.head < len(c.data); count, c.head = count-1, c.head+1 { - esp.I2C.SetFIFO_DATA_FIFO_RDATA(uint32(c.data[c.head])) - } - reg.Set(i2cCMD_WRITE | uint32(32-count)) - reg = nextAddress(reg) - - if c.head < len(c.data) { - reg.Set(i2cCMD_END) - reg = nil - } else { - cmdIdx++ - } - needRestart = true - - case i2cCMD_READ: - if needAddress { - needAddress = false - esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) - esp.I2C.SLAVE_ADDR.Set(uint32(addr)) - reg.Set(i2cCMD_WRITE | 1) - reg = nextAddress(reg) - } - if needRestart { - // We need to send RESTART again after i2cCMD_WRITE. - reg.Set(i2cCMD_RSTART) - - reg = nextAddress(reg) - reg.Set(i2cCMD_WRITE | 1) - - reg = nextAddress(reg) - esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) - needRestart = false - } - count := 32 - bytes := len(c.data) - c.head - // Only last byte in sequence must be sent with ACK set to 1 to indicate end of data. - split := bytes <= count - if split { - bytes-- - } - if bytes > 32 { - bytes = 32 - } - reg.Set(i2cCMD_READ | uint32(bytes)) - reg = nextAddress(reg) - - if split { - readLast = true - reg.Set(i2cCMD_READLAST | 1) - reg = nextAddress(reg) - readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte - cmdIdx++ - } else { - reg.Set(i2cCMD_END) - readTo = c.data[c.head : c.head+bytes] - reg = nil - } - - case i2cCMD_STOP: - reg.Set(i2cCMD_STOP) - reg = nil - cmdIdx++ - } - if reg == nil { - // transmit now - esp.I2C.SetCTR_CONF_UPGATE(1) - esp.I2C.SetCTR_TRANS_START(1) - end := nanotime() + timeoutNS - var mask uint32 - for mask = esp.I2C.INT_STATUS.Get(); mask&intMask == 0; mask = esp.I2C.INT_STATUS.Get() { - if nanotime() > end { - if readTo != nil { - return errI2CReadTimeout - } - return errI2CWriteTimeout - } - } - switch { - case mask&esp.I2C_INT_STATUS_NACK_INT_ST_Msk != 0 && !readLast: - return errI2CAckExpected - case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0: - if readTo != nil { - return errI2CReadTimeout - } - return errI2CWriteTimeout - } - esp.I2C.INT_CLR.SetBits(intMask) - for i := 0; i < len(readTo); i++ { - readTo[i] = byte(esp.I2C.GetFIFO_DATA_FIFO_RDATA() & 0xff) - c.head++ - } - readTo = nil - reg = &esp.I2C.COMD0 - } - } - return nil -} - -// 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) (err error) { - // timeout in microseconds. - const timeout = 40 // 40ms is a reasonable time for a real-time system. - - cmd := make([]i2cCommand, 0, 8) - cmd = append(cmd, i2cCommand{cmd: i2cCMD_RSTART}) - if len(w) > 0 { - cmd = append(cmd, i2cCommand{cmd: i2cCMD_WRITE, data: w}) - } - if len(r) > 0 { - cmd = append(cmd, i2cCommand{cmd: i2cCMD_READ, data: r}) - } - cmd = append(cmd, i2cCommand{cmd: i2cCMD_STOP}) - - return i2c.transmit(addr, cmd, timeout) -} - -func (i2c *I2C) SetBaudRate(br uint32) error { - return nil -} - -func nextAddress(reg *volatile.Register32) *volatile.Register32 { - return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(reg), 4)) -} diff --git a/src/machine/machine_esp32c3_i2c.go b/src/machine/machine_esp32c3_i2c.go new file mode 100644 index 0000000000..ad17d7c2ee --- /dev/null +++ b/src/machine/machine_esp32c3_i2c.go @@ -0,0 +1,353 @@ +//go:build esp32c3 && !m5stamp_c3 + +package machine + +import ( + "device/esp" + "runtime/volatile" + "unsafe" +) + +var ( + I2C0 = &I2C{} +) + +type I2C struct{} + +// I2CConfig is used to store config info for I2C. +type I2CConfig struct { + Frequency uint32 // in Hz + SCL Pin + SDA Pin +} + +const ( + clkXTAL = 0 + clkFOSC = 1 + clkXTALFrequency = uint32(40e6) + clkFOSCFrequency = uint32(17.5e6) + i2cClkSourceFrequency = clkXTALFrequency + i2cClkSource = clkXTAL +) + +func (i2c *I2C) Configure(config I2CConfig) error { + if config.Frequency == 0 { + config.Frequency = 400 * KHz + } + if config.SCL == 0 { + config.SCL = SCL_PIN + } + if config.SDA == 0 { + config.SDA = SDA_PIN + } + + i2c.initClock(config) + i2c.initNoiseFilter() + i2c.initPins(config) + i2c.initFrequency(config) + i2c.startMaster() + return nil +} + +//go:inline +func (i2c *I2C) initClock(config I2CConfig) { + // reset I2C clock + esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(1) + esp.SYSTEM.SetPERIP_CLK_EN0_EXT0_CLK_EN(1) + esp.SYSTEM.SetPERIP_RST_EN0_EXT0_RST(0) + // disable interrupts + esp.I2C.INT_ENA.ClearBits(0x3fff) + esp.I2C.INT_CLR.ClearBits(0x3fff) + + esp.I2C.SetCLK_CONF_SCLK_SEL(i2cClkSource) + esp.I2C.SetCLK_CONF_SCLK_ACTIVE(1) + esp.I2C.SetCLK_CONF_SCLK_DIV_NUM(i2cClkSourceFrequency / (config.Frequency * 1024)) + esp.I2C.SetCTR_CLK_EN(1) +} + +//go:inline +func (i2c *I2C) initNoiseFilter() { + esp.I2C.FILTER_CFG.Set(0x377) +} + +//go:inline +func (i2c *I2C) initPins(config I2CConfig) { + var muxConfig uint32 + const function = 1 // function 1 is just GPIO + + // SDA + muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos + // Make this pin an input pin (always). + muxConfig |= esp.IO_MUX_GPIO_FUN_IE + // Set drive strength: 0 is lowest, 3 is highest. + muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos + config.SDA.mux().Set(muxConfig) + config.SDA.outFunc().Set(54) + inFunc(54).Set(uint32(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | config.SDA)) + config.SDA.Set(true) + // Configure the pad with the given IO mux configuration. + config.SDA.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) + + esp.GPIO.ENABLE.SetBits(1 << int(config.SDA)) + esp.I2C.SetCTR_SDA_FORCE_OUT(1) + + // SCL + muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos + // Make this pin an input pin (always). + muxConfig |= esp.IO_MUX_GPIO_FUN_IE + // Set drive strength: 0 is lowest, 3 is highest. + muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos + config.SCL.mux().Set(muxConfig) + config.SCL.outFunc().Set(53) + inFunc(53).Set(uint32(config.SCL)) + config.SCL.Set(true) + // Configure the pad with the given IO mux configuration. + config.SCL.pinReg().SetBits(esp.GPIO_PIN_PIN_PAD_DRIVER) + + esp.GPIO.ENABLE.SetBits(1 << int(config.SCL)) + esp.I2C.SetCTR_SCL_FORCE_OUT(1) +} + +//go:inline +func (i2c *I2C) initFrequency(config I2CConfig) { + + clkmDiv := i2cClkSourceFrequency/(config.Frequency*1024) + 1 + sclkFreq := i2cClkSourceFrequency / clkmDiv + halfCycle := sclkFreq / config.Frequency / 2 + //SCL + sclLow := halfCycle + sclWaitHigh := uint32(0) + if config.Frequency > 50000 { + sclWaitHigh = halfCycle / 8 // compensate the time when freq > 50K + } + sclHigh := halfCycle - sclWaitHigh + // SDA + sdaHold := halfCycle / 4 + sda_sample := halfCycle / 2 + setup := halfCycle + hold := halfCycle + + esp.I2C.SetSCL_LOW_PERIOD(sclLow - 1) + esp.I2C.SetSCL_HIGH_PERIOD(sclHigh) + esp.I2C.SetSCL_HIGH_PERIOD_SCL_WAIT_HIGH_PERIOD(25) + esp.I2C.SetSCL_RSTART_SETUP_TIME(setup) + esp.I2C.SetSCL_STOP_SETUP_TIME(setup) + esp.I2C.SetSCL_START_HOLD_TIME(hold - 1) + esp.I2C.SetSCL_STOP_HOLD_TIME(hold - 1) + esp.I2C.SetSDA_SAMPLE_TIME(sda_sample) + esp.I2C.SetSDA_HOLD_TIME(sdaHold) +} + +//go:inline +func (i2c *I2C) startMaster() { + // FIFO mode for data + esp.I2C.SetFIFO_CONF_NONFIFO_EN(0) + // Reset TX & RX buffers + esp.I2C.SetFIFO_CONF_RX_FIFO_RST(1) + esp.I2C.SetFIFO_CONF_RX_FIFO_RST(0) + esp.I2C.SetFIFO_CONF_TX_FIFO_RST(1) + esp.I2C.SetFIFO_CONF_TX_FIFO_RST(0) + // set timeout value + esp.I2C.TO.Set(0x10) + // enable master mode + esp.I2C.CTR.Set(0x113) + esp.I2C.SetCTR_CONF_UPGATE(1) + resetMaster() +} + +//go:inline +func resetMaster() { + // reset FSM + esp.I2C.SetCTR_FSM_RST(1) + // clear the bus + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(9) + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_EN(1) + esp.I2C.SetSCL_STRETCH_CONF_SLAVE_SCL_STRETCH_EN(1) + esp.I2C.SetCTR_CONF_UPGATE(1) + esp.I2C.FILTER_CFG.Set(0x377) + // wait for SCL_RST_SLV_EN + for esp.I2C.GetSCL_SP_CONF_SCL_RST_SLV_EN() != 0 { + } + esp.I2C.SetSCL_SP_CONF_SCL_RST_SLV_NUM(0) +} + +type i2cCommandType = uint32 +type i2cAck = uint32 + +const ( + i2cCMD_RSTART i2cCommandType = 6 << 11 + i2cCMD_WRITE i2cCommandType = 1<<11 | 1<<8 // WRITE + ack_check_en + i2cCMD_READ i2cCommandType = 3<<11 | 1<<8 // READ + ack_check_en + i2cCMD_READLAST i2cCommandType = 3<<11 | 5<<8 // READ + ack_check_en + NACK + i2cCMD_STOP i2cCommandType = 2 << 11 + i2cCMD_END i2cCommandType = 4 << 11 +) + +type i2cCommand struct { + cmd i2cCommandType + data []byte + head int +} + +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { + const intMask = esp.I2C_INT_STATUS_END_DETECT_INT_ST_Msk | esp.I2C_INT_STATUS_TRANS_COMPLETE_INT_ST_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk + esp.I2C.INT_CLR.SetBits(intMask) + esp.I2C.INT_ENA.SetBits(intMask) + esp.I2C.SetCTR_CONF_UPGATE(1) + + defer func() { + esp.I2C.INT_CLR.SetBits(intMask) + esp.I2C.INT_ENA.ClearBits(intMask) + }() + + timeoutNS := int64(timeoutMS) * 1000000 + needAddress := true + needRestart := false + readLast := false + var readTo []byte + for cmdIdx, reg := 0, &esp.I2C.COMD0; cmdIdx < len(cmd); { + c := &cmd[cmdIdx] + + switch c.cmd { + case i2cCMD_RSTART: + reg.Set(i2cCMD_RSTART) + reg = nextAddress(reg) + cmdIdx++ + + case i2cCMD_WRITE: + count := 32 + if needAddress { + needAddress = false + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr) & 0x7f) << 1) + count-- + esp.I2C.SLAVE_ADDR.Set(uint32(addr)) + esp.I2C.SetCTR_CONF_UPGATE(1) + } + for ; count > 0 && c.head < len(c.data); count, c.head = count-1, c.head+1 { + esp.I2C.SetFIFO_DATA_FIFO_RDATA(uint32(c.data[c.head])) + } + reg.Set(i2cCMD_WRITE | uint32(32-count)) + reg = nextAddress(reg) + + if c.head < len(c.data) { + reg.Set(i2cCMD_END) + reg = nil + } else { + cmdIdx++ + } + needRestart = true + + case i2cCMD_READ: + if needAddress { + needAddress = false + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) + esp.I2C.SLAVE_ADDR.Set(uint32(addr)) + reg.Set(i2cCMD_WRITE | 1) + reg = nextAddress(reg) + } + if needRestart { + // We need to send RESTART again after i2cCMD_WRITE. + reg.Set(i2cCMD_RSTART) + + reg = nextAddress(reg) + reg.Set(i2cCMD_WRITE | 1) + + reg = nextAddress(reg) + esp.I2C.SetFIFO_DATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1) + needRestart = false + } + count := 32 + bytes := len(c.data) - c.head + // Only last byte in sequence must be sent with ACK set to 1 to indicate end of data. + split := bytes <= count + if split { + bytes-- + } + if bytes > 32 { + bytes = 32 + } + reg.Set(i2cCMD_READ | uint32(bytes)) + reg = nextAddress(reg) + + if split { + readLast = true + reg.Set(i2cCMD_READLAST | 1) + reg = nextAddress(reg) + readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte + cmdIdx++ + } else { + reg.Set(i2cCMD_END) + readTo = c.data[c.head : c.head+bytes] + reg = nil + } + + case i2cCMD_STOP: + reg.Set(i2cCMD_STOP) + reg = nil + cmdIdx++ + } + if reg == nil { + // transmit now + esp.I2C.SetCTR_CONF_UPGATE(1) + esp.I2C.SetCTR_TRANS_START(1) + end := nanotime() + timeoutNS + var mask uint32 + for mask = esp.I2C.INT_STATUS.Get(); mask&intMask == 0; mask = esp.I2C.INT_STATUS.Get() { + if nanotime() > end { + if readTo != nil { + return errI2CReadTimeout + } + return errI2CWriteTimeout + } + } + switch { + case mask&esp.I2C_INT_STATUS_NACK_INT_ST_Msk != 0 && !readLast: + return errI2CAckExpected + case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0: + if readTo != nil { + return errI2CReadTimeout + } + return errI2CWriteTimeout + } + esp.I2C.INT_CLR.SetBits(intMask) + for i := 0; i < len(readTo); i++ { + readTo[i] = byte(esp.I2C.GetFIFO_DATA_FIFO_RDATA() & 0xff) + c.head++ + } + readTo = nil + reg = &esp.I2C.COMD0 + } + } + return nil +} + +// 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) (err error) { + // timeout in microseconds. + const timeout = 40 // 40ms is a reasonable time for a real-time system. + + cmd := make([]i2cCommand, 0, 8) + cmd = append(cmd, i2cCommand{cmd: i2cCMD_RSTART}) + if len(w) > 0 { + cmd = append(cmd, i2cCommand{cmd: i2cCMD_WRITE, data: w}) + } + if len(r) > 0 { + cmd = append(cmd, i2cCommand{cmd: i2cCMD_READ, data: r}) + } + cmd = append(cmd, i2cCommand{cmd: i2cCMD_STOP}) + + return i2c.transmit(addr, cmd, timeout) +} + +func (i2c *I2C) SetBaudRate(br uint32) error { + return nil +} + +func nextAddress(reg *volatile.Register32) *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(reg), 4)) +}