//go:build stm32f4 || stm32f1 package machine // I2C implementation for 'older' STM32 MCUs, including the F1 and F4 series // of MCUs. import ( "device/stm32" "unsafe" ) const ( flagOVR = 0x00010800 flagAF = 0x00010400 flagARLO = 0x00010200 flagBERR = 0x00010100 flagTXE = 0x00010080 flagRXNE = 0x00010040 flagSTOPF = 0x00010010 flagADD10 = 0x00010008 flagBTF = 0x00010004 flagADDR = 0x00010002 flagSB = 0x00010001 flagDUALF = 0x00100080 flagGENCALL = 0x00100010 flagTRA = 0x00100004 flagBUSY = 0x00100002 flagMSL = 0x00100001 ) func (i2c *I2C) hasFlag(flag uint32) bool { const mask = 0x0000FFFF if uint8(flag>>16) == 1 { return i2c.Bus.SR1.HasBits(flag & mask) } else { return i2c.Bus.SR2.HasBits(flag & mask) } } func (i2c *I2C) clearFlag(flag uint32) { const mask = 0x0000FFFF i2c.Bus.SR1.Set(^(flag & mask)) } // clearFlagADDR reads both status registers to clear any pending ADDR flags. func (i2c *I2C) clearFlagADDR() { i2c.Bus.SR1.Get() i2c.Bus.SR2.Get() } func (i2c *I2C) waitForFlag(flag uint32, set bool) bool { const tryMax = 10000 hasFlag := false for i := 0; !hasFlag && i < tryMax; i++ { hasFlag = i2c.hasFlag(flag) == set } return hasFlag } func (i2c *I2C) waitForFlagOrError(flag uint32, set bool) bool { const tryMax = 10000 hasFlag := false for i := 0; !hasFlag && i < tryMax; i++ { if hasFlag = i2c.hasFlag(flag) == set; !hasFlag { // check for ACK failure if i2c.hasFlag(flagAF) { // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) // clear pending flags i2c.clearFlag(flagAF) return false } else if i2c.hasFlag(flagSTOPF) { // clear stop flag i2c.clearFlag(flagSTOPF) return false } } } return hasFlag } type transferOption uint32 const ( frameFirst = 0x00000001 frameFirstAndNext = 0x00000002 frameNext = 0x00000004 frameFirstAndLast = 0x00000008 frameLastNoStop = 0x00000010 frameLast = 0x00000020 frameNoOption = 0xFFFF0000 ) // I2C fast mode (Fm) duty cycle const ( DutyCycle2 = 0 DutyCycle16x9 = 1 ) // I2CConfig is used to store config info for I2C. type I2CConfig struct { Frequency uint32 SCL Pin SDA Pin DutyCycle uint8 } // Configure is intended to setup the STM32 I2C interface. func (i2c *I2C) Configure(config I2CConfig) error { // The following is the required sequence in controller mode. // 1. Program the peripheral input clock in I2C_CR2 Register in order to // generate correct timings // 2. Configure the clock control registers // 3. Configure the rise time register // 4. Program the I2C_CR1 register to enable the peripheral // 5. Set the START bit in the I2C_CR1 register to generate a Start condition // disable I2C interface before any configuration changes i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE) // reset I2C bus i2c.Bus.CR1.SetBits(stm32.I2C_CR1_SWRST) i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_SWRST) // enable clock for I2C enableAltFuncClock(unsafe.Pointer(i2c.Bus)) // init pins if config.SCL == 0 && config.SDA == 0 { config.SCL = I2C0_SCL_PIN config.SDA = I2C0_SDA_PIN } i2c.configurePins(config) // default to 100 kHz (Sm, standard mode) if no frequency is set if config.Frequency == 0 { config.Frequency = 100 * KHz } // configure I2C input clock i2c.Bus.CR2.SetBits(i2c.getFreqRange(config)) // configure rise time i2c.Bus.TRISE.Set(i2c.getRiseTime(config)) // configure clock control i2c.Bus.CCR.Set(i2c.getSpeed(config)) // disable GeneralCall and NoStretch modes i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ENGC | stm32.I2C_CR1_NOSTRETCH) // enable I2C interface i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE) return nil } func (i2c *I2C) Tx(addr uint16, w, r []byte) error { if err := i2c.controllerTransmit(addr, w); nil != err { return err } if len(r) > 0 { if err := i2c.controllerReceive(addr, r); nil != err { return err } } return nil } func (i2c *I2C) controllerTransmit(addr uint16, w []byte) error { if !i2c.waitForFlag(flagBUSY, false) { return errI2CBusReadyTimeout } // disable POS i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS) pos := 0 rem := len(w) // send peripheral address if err := i2c.controllerRequestWrite(addr, frameNoOption); nil != err { return err } // clear ADDR flag i2c.clearFlagADDR() for rem > 0 { // wait for TXE flag set if !i2c.waitForFlagOrError(flagTXE, true) { return errI2CAckExpected } // write data to DR i2c.Bus.DR.Set(uint32(w[pos])) // update counters pos++ rem-- if i2c.hasFlag(flagBTF) && rem != 0 { // write data to DR i2c.Bus.DR.Set(uint32(w[pos])) // update counters pos++ rem-- } // wait for transfer finished flag BTF set if !i2c.waitForFlagOrError(flagBTF, true) { return errI2CWriteTimeout } } // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) return nil } func (i2c *I2C) controllerRequestWrite(addr uint16, option transferOption) error { if frameFirstAndLast == option || frameFirst == option || frameNoOption == option { // generate start condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START) } else if false /* (hi2c->PreviousState == I2C_STATE_MASTER_BUSY_RX) */ { // generate restart condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START) } // ensure start bit is set if !i2c.waitForFlag(flagSB, true) { return errI2CSignalStartTimeout } // send peripheral address i2c.Bus.DR.Set(uint32(addr) << 1) // wait for address ACK from peripheral if !i2c.waitForFlagOrError(flagADDR, true) { return errI2CSignalStartTimeout } return nil } func (i2c *I2C) controllerReceive(addr uint16, r []byte) error { if !i2c.waitForFlag(flagBUSY, false) { return errI2CBusReadyTimeout } // disable POS i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS) pos := 0 rem := len(r) // send peripheral address if err := i2c.controllerRequestRead(addr, frameNoOption); nil != err { return err } switch rem { case 0: // clear ADDR flag i2c.clearFlagADDR() // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) case 1: // disable ACK i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK) // clear ADDR flag i2c.clearFlagADDR() // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) case 2: // disable ACK i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK) // enable POS i2c.Bus.CR1.SetBits(stm32.I2C_CR1_POS) // clear ADDR flag i2c.clearFlagADDR() default: // enable ACK i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK) // clear ADDR flag i2c.clearFlagADDR() } for rem > 0 { switch rem { case 1: // wait until RXNE flag is set if !i2c.waitForFlagOrError(flagRXNE, true) { return errI2CReadTimeout } // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- case 2: // wait until transfer finished flag BTF is set if !i2c.waitForFlag(flagBTF, true) { return errI2CReadTimeout } // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- case 3: // wait until transfer finished flag BTF is set if !i2c.waitForFlag(flagBTF, true) { return errI2CReadTimeout } // disable ACK i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK) // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- // wait until transfer finished flag BTF is set if !i2c.waitForFlag(flagBTF, true) { return errI2CReadTimeout } // generate stop condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP) // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- default: // wait until RXNE flag is set if !i2c.waitForFlagOrError(flagRXNE, true) { return errI2CReadTimeout } // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- if i2c.hasFlag(flagBTF) { // read data from DR r[pos] = byte(i2c.Bus.DR.Get()) // update counters pos++ rem-- } } } return nil } func (i2c *I2C) controllerRequestRead(addr uint16, option transferOption) error { // enable ACK i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK) if frameFirstAndLast == option || frameFirst == option || frameNoOption == option { // generate start condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START) } else if false /* (hi2c->PreviousState == I2C_STATE_MASTER_BUSY_TX) */ { // generate restart condition i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START) } // ensure start bit is set if !i2c.waitForFlag(flagSB, true) { return errI2CSignalStartTimeout } // send peripheral address i2c.Bus.DR.Set(uint32(addr)<<1 | 1) // wait for address ACK from peripheral if !i2c.waitForFlagOrError(flagADDR, true) { return errI2CSignalStartTimeout } return nil }