mirror of https://github.com/tinygo-org/tinygo.git
ardnew
4 years ago
committed by
GitHub
4 changed files with 573 additions and 5 deletions
@ -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 |
|||
} |
Loading…
Reference in new issue