
This makes it possible to assign I2C objects (machine.I2C0, machine.I2C1, etc.) without needing to take a pointer. This is important especially in the future when I2C may be driven using DMA and the machine.I2C type needs to store some state.
430 строки
8,9 КиБ
Go
430 строки
8,9 КиБ
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 = TWI_FREQ_100KHZ
|
|
}
|
|
|
|
// 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
|
|
}
|