Browse Source

feather-stm32f405: add I2C support (#1378)

* machine/stm32f405: add initial I2C support
pull/1439/head
ardnew 4 years ago
committed by GitHub
parent
commit
3eb33dff5d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/machine/board_feather-stm32f405.go
  2. 2
      src/machine/i2c.go
  3. 481
      src/machine/machine_stm32_i2c.go
  4. 71
      src/machine/machine_stm32f405.go

24
src/machine/board_feather-stm32f405.go

@ -202,11 +202,11 @@ const (
// #===========#==========#==============#==============#=======#=======#
// | Interface | Hardware | Bus(Freq) | SDA/SCL Pins | AltFn | Alias |
// #===========#==========#==============#==============#=======#=======#
// | I2C1 | I2C1 | | D14/D15 | | ~ |
// | I2C2 | I2C2 | | D0/D1 | | ~ |
// | I2C3 | I2C1 | | D9/D10 | | ~ |
// | I2C1 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | ~ |
// | I2C2 | I2C2 | APB1(42 MHz) | D0/D1 | 4 | ~ |
// | I2C3 | I2C1 | APB1(42 MHz) | D9/D10 | 4 | ~ |
// | --------- | -------- | ------------ | ------------ | ----- | ----- |
// | I2C0 | I2C1 | | D14/D15 | | I2C1 |
// | I2C0 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | I2C1 |
// #===========#==========#==============#==============#=======#=======#
NUM_I2C_INTERFACES = 3
@ -226,4 +226,20 @@ const (
I2C_SCL_PIN = I2C0_SCL_PIN //
)
var (
I2C1 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: stm32.AF4_I2C1_2_3,
}
I2C2 = I2C{
Bus: stm32.I2C2,
AltFuncSelector: stm32.AF4_I2C1_2_3,
}
I2C3 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: stm32.AF4_I2C1_2_3,
}
I2C0 = I2C1
)
func initI2C() {}

2
src/machine/i2c.go

@ -1,4 +1,4 @@
// +build avr nrf sam stm32,!stm32f4 fe310 k210
// +build avr nrf sam stm32,!stm32f407 fe310 k210
package machine

481
src/machine/machine_stm32_i2c.go

@ -0,0 +1,481 @@
// +build stm32,!stm32f103xx,!stm32f407
package machine
// Peripheral abstraction layer for I2C on the stm32 family
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
)
// addressable represents a type that can provide fully-formatted I2C peripheral
// addresses for both read operations and write operations.
type addressable interface {
toRead() uint32
toWrite() uint32
bitSize() uint8
}
// address7Bit and address10Bit stores the unshifted original I2C peripheral address
// in an unsigned integral data type and implements the addressable interface
// to reformat addresses as required for read/write operations.
// TODO:
// add 10-bit address support
type (
address7Bit uint8
//address10Bit uint16
)
func (sa address7Bit) toRead() uint32 {
return uint32(((uint8(sa) << 1) | uint8(stm32.I2C_OAR1_ADD0)) & 0xFF)
}
func (sa address7Bit) toWrite() uint32 {
return uint32(((uint8(sa) << 1) & ^(uint8(stm32.I2C_OAR1_ADD0))) & 0xFF)
}
func (sa address7Bit) bitSize() uint8 { return 7 } // 7-bit addresses
//func (sa address10Bit) toRead() uint32 {}
//func (sa address10Bit) toWrite() uint32 {}
//func (sa address10Bit) bitSize() uint8 { return 10 } // 10-bit addresses
func readAddress7Bit(addr uint8) uint32 { return address7Bit(addr).toRead() }
func writeAddress7Bit(addr uint8) uint32 { return address7Bit(addr).toWrite() }
//func readAddress10Bit(addr uint16) uint32 { return address10Bit(addr).toRead() }
//func writeAddress10Bit(addr uint16) uint32 { return address10Bit(addr).toWrite() }
// 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) {
// 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.ClearBits(stm32.I2C_CR1_PE)
}
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
a := address7Bit(addr)
if err := i2c.controllerTransmit(a, w); nil != err {
return err
}
if err := i2c.controllerReceive(a, r); nil != err {
return err
}
return nil
}
func (i2c I2C) controllerTransmit(addr addressable, w []byte) error {
if !i2c.waitForFlag(flagBUSY, false) {
return errI2CBusReadyTimeout
}
// ensure peripheral is enabled
if !i2c.Bus.CR1.HasBits(stm32.I2C_CR1_PE) {
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
}
// 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 addressable, 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
switch addr.bitSize() {
case 7: // 7-bit peripheral address
i2c.Bus.DR.Set(addr.toWrite())
case 10: // 10-bit peripheral address
// TODO
}
// wait for address ACK from peripheral
if !i2c.waitForFlagOrError(flagADDR, true) {
return errI2CSignalStartTimeout
}
return nil
}
func (i2c I2C) controllerReceive(addr addressable, r []byte) error {
if !i2c.waitForFlag(flagBUSY, false) {
return errI2CBusReadyTimeout
}
// ensure peripheral is enabled
if !i2c.Bus.CR1.HasBits(stm32.I2C_CR1_PE) {
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
}
// 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 addressable, 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
switch addr.bitSize() {
case 7: // 7-bit peripheral address
i2c.Bus.DR.Set(addr.toRead())
case 10: // 10-bit peripheral address
// TODO
}
// wait for address ACK from peripheral
if !i2c.waitForFlagOrError(flagADDR, true) {
return errI2CSignalStartTimeout
}
return nil
}

71
src/machine/machine_stm32f405.go

@ -95,3 +95,74 @@ type I2C struct {
Bus *stm32.I2C_Type
AltFuncSelector stm32.AltFunc
}
func (i2c I2C) configurePins(config I2CConfig) {
config.SCL.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSCL}, i2c.AltFuncSelector)
config.SDA.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSDA}, i2c.AltFuncSelector)
}
func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
// all I2C interfaces are on APB1 (42 MHz)
clock := CPUFrequency() / 4
// convert to MHz
clock /= 1000000
// must be between 2 MHz (or 4 MHz for fast mode (Fm)) and 50 MHz, inclusive
var min, max uint32 = 2, 50
if config.Frequency > 10000 {
min = 4 // fast mode (Fm)
}
if clock < min {
clock = min
} else if clock > max {
clock = max
}
return clock << stm32.I2C_CR2_FREQ_Pos
}
func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
// These bits must be programmed with the maximum SCL rise time given in the
// I2C bus specification, incremented by 1.
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
// If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08
// and PCLK1 = 125 ns, therefore the TRISE[5:0] bits must be programmed with
// 09h (1000 ns / 125 ns = 8 + 1)
freqRange := i2c.getFreqRange(config)
if config.Frequency > 100000 {
// fast mode (Fm) adjustment
freqRange *= 300
freqRange /= 1000
}
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
}
func (i2c I2C) getSpeed(config I2CConfig) uint32 {
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
}
sm := func(pclk uint32, freq uint32) uint32 { // standard mode (Sm)
if s := ccr(pclk, freq, 2); s < 4 {
return 4
} else {
return s
}
}
fm := func(pclk uint32, freq uint32, duty uint8) uint32 { // fast mode (Fm)
if duty == DutyCycle2 {
return ccr(pclk, freq, 3)
} else {
return ccr(pclk, freq, 25) | stm32.I2C_CCR_DUTY
}
}
// all I2C interfaces are on APB1 (42 MHz)
clock := CPUFrequency() / 4
if config.Frequency <= 100000 {
return sm(clock, config.Frequency)
} else {
s := fm(clock, config.Frequency, config.DutyCycle)
if (s & stm32.I2C_CCR_CCR_Msk) == 0 {
return 1
} else {
return s | stm32.I2C_CCR_F_S
}
}
}

Loading…
Cancel
Save