feather-stm32f405: add I2C support (#1378)
* machine/stm32f405: add initial I2C support
Этот коммит содержится в:
родитель
bb146edb47
коммит
3eb33dff5d
4 изменённых файлов: 573 добавлений и 5 удалений
|
@ -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() {}
|
||||
|
|
|
@ -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
Обычный файл
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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче