
Calling errors.New in an error path causes a heap allocation at an already unfortunate moment. It is more efficient to create these error values in globals and return these constant globals. If these errors are not used (because the related code was optimized out), the globals will also be optimized out.
657 строки
15 КиБ
Go
657 строки
15 КиБ
Go
// +build stm32,stm32f103xx
|
|
|
|
package machine
|
|
|
|
// Peripheral abstraction layer for the stm32.
|
|
|
|
import (
|
|
"device/stm32"
|
|
"runtime/interrupt"
|
|
"unsafe"
|
|
)
|
|
|
|
func CPUFrequency() uint32 {
|
|
return 72000000
|
|
}
|
|
|
|
const (
|
|
PinInput PinMode = 0 // Input mode
|
|
PinOutput10MHz PinMode = 1 // Output mode, max speed 10MHz
|
|
PinOutput2MHz PinMode = 2 // Output mode, max speed 2MHz
|
|
PinOutput50MHz PinMode = 3 // Output mode, max speed 50MHz
|
|
PinOutput PinMode = PinOutput2MHz
|
|
|
|
PinInputModeAnalog PinMode = 0 // Input analog mode
|
|
PinInputModeFloating PinMode = 4 // Input floating mode
|
|
PinInputModePullUpDown PinMode = 8 // Input pull up/down mode
|
|
PinInputModeReserved PinMode = 12 // Input mode (reserved)
|
|
|
|
PinOutputModeGPPushPull PinMode = 0 // Output mode general purpose push/pull
|
|
PinOutputModeGPOpenDrain PinMode = 4 // Output mode general purpose open drain
|
|
PinOutputModeAltPushPull PinMode = 8 // Output mode alt. purpose push/pull
|
|
PinOutputModeAltOpenDrain PinMode = 12 // Output mode alt. purpose open drain
|
|
)
|
|
|
|
// Configure this pin with the given I/O settings.
|
|
// stm32f1xx uses different technique for setting the GPIO pins than the stm32f407
|
|
func (p Pin) Configure(config PinConfig) {
|
|
// Configure the GPIO pin.
|
|
p.enableClock()
|
|
port := p.getPort()
|
|
pin := uint8(p) % 16
|
|
pos := (pin % 8) * 4
|
|
if pin < 8 {
|
|
port.CRL.ReplaceBits(uint32(config.Mode), 0xf, pos)
|
|
} else {
|
|
port.CRH.ReplaceBits(uint32(config.Mode), 0xf, pos)
|
|
}
|
|
}
|
|
|
|
func (p Pin) getPort() *stm32.GPIO_Type {
|
|
switch p / 16 {
|
|
case 0:
|
|
return stm32.GPIOA
|
|
case 1:
|
|
return stm32.GPIOB
|
|
case 2:
|
|
return stm32.GPIOC
|
|
case 3:
|
|
return stm32.GPIOD
|
|
case 4:
|
|
return stm32.GPIOE
|
|
case 5:
|
|
return stm32.GPIOF
|
|
case 6:
|
|
return stm32.GPIOG
|
|
default:
|
|
panic("machine: unknown port")
|
|
}
|
|
}
|
|
|
|
// enableClock enables the clock for this desired GPIO port.
|
|
func (p Pin) enableClock() {
|
|
switch p / 16 {
|
|
case 0:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPAEN)
|
|
case 1:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPBEN)
|
|
case 2:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPCEN)
|
|
case 3:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPDEN)
|
|
case 4:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPEEN)
|
|
case 5:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPFEN)
|
|
case 6:
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPGEN)
|
|
default:
|
|
panic("machine: unknown port")
|
|
}
|
|
}
|
|
|
|
// Enable peripheral clock. Expand to include all the desired peripherals
|
|
func enableAltFuncClock(bus unsafe.Pointer) {
|
|
if bus == unsafe.Pointer(stm32.USART1) {
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
} else if bus == unsafe.Pointer(stm32.USART2) {
|
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
|
} else if bus == unsafe.Pointer(stm32.I2C1) {
|
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
|
} else if bus == unsafe.Pointer(stm32.SPI1) {
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
}
|
|
}
|
|
|
|
//---------- UART related types and code
|
|
|
|
// UART representation
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
Bus *stm32.USART_Type
|
|
Interrupt interrupt.Interrupt
|
|
}
|
|
|
|
// Configure the TX and RX pins
|
|
func (uart UART) configurePins(config UARTConfig) {
|
|
|
|
// pins
|
|
switch config.TX {
|
|
case UART_ALT_TX_PIN:
|
|
// use alternate TX/RX pins via AFIO mapping
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
|
|
if uart.Bus == stm32.USART1 {
|
|
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_USART1_REMAP)
|
|
} else if uart.Bus == stm32.USART2 {
|
|
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_USART2_REMAP)
|
|
}
|
|
default:
|
|
// use standard TX/RX pins PA9 and PA10
|
|
}
|
|
config.TX.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
config.RX.Configure(PinConfig{Mode: PinInputModeFloating})
|
|
}
|
|
|
|
// Determine the divisor for USARTs to get the given baudrate
|
|
func (uart UART) getBaudRateDivisor(br uint32) uint32 {
|
|
|
|
// Note: PCLK2 (from APB2) used for USART1 and PCLK1 for USART2, 3, 4, 5
|
|
var divider uint32
|
|
if uart.Bus == stm32.USART1 {
|
|
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
|
divider = CPUFrequency() / br
|
|
} else {
|
|
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
|
divider = CPUFrequency() / 2 / br
|
|
}
|
|
return divider
|
|
}
|
|
|
|
//---------- SPI related types and code
|
|
|
|
type SPI struct {
|
|
Bus *stm32.SPI_Type
|
|
}
|
|
|
|
// There are 3 SPI interfaces on the STM32F103xx.
|
|
// Since the first interface is named SPI1, both SPI0 and SPI1 refer to SPI1.
|
|
// TODO: implement SPI2 and SPI3.
|
|
var (
|
|
SPI1 = SPI{Bus: stm32.SPI1}
|
|
SPI0 = SPI1
|
|
)
|
|
|
|
// Set baud rate for SPI
|
|
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
|
var conf uint32
|
|
|
|
// set frequency dependent on PCLK2 prescaler (div 1)
|
|
switch config.Frequency {
|
|
case 125000:
|
|
// Note: impossible to achieve lower frequency with current PCLK2!
|
|
conf |= stm32.SPI_BaudRatePrescaler_256
|
|
case 250000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_256
|
|
case 500000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_128
|
|
case 1000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_64
|
|
case 2000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_32
|
|
case 4000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_16
|
|
case 8000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_8
|
|
default:
|
|
conf |= stm32.SPI_BaudRatePrescaler_256
|
|
}
|
|
return conf
|
|
}
|
|
|
|
// Configure SPI pins for input output and clock
|
|
func (spi SPI) configurePins(config SPIConfig) {
|
|
config.SCK.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
config.MOSI.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
config.MISO.Configure(PinConfig{Mode: PinInputModeFloating})
|
|
}
|
|
|
|
//---------- I2C related types and code
|
|
|
|
type I2C struct {
|
|
Bus *stm32.I2C_Type
|
|
}
|
|
|
|
// There are 2 I2C interfaces on the STM32F103xx.
|
|
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
|
|
// TODO: implement I2C2.
|
|
var (
|
|
I2C1 = I2C{Bus: stm32.I2C1}
|
|
I2C0 = I2C1
|
|
)
|
|
|
|
// I2CConfig is used to store config info for I2C.
|
|
type I2CConfig struct {
|
|
Frequency uint32
|
|
SCL Pin
|
|
SDA Pin
|
|
}
|
|
|
|
// Configure is intended to setup the I2C interface.
|
|
func (i2c I2C) Configure(config I2CConfig) {
|
|
// Default I2C bus speed is 100 kHz.
|
|
if config.Frequency == 0 {
|
|
config.Frequency = TWI_FREQ_100KHZ
|
|
}
|
|
|
|
// enable clock for I2C
|
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
|
|
|
// I2C1 pins
|
|
switch config.SDA {
|
|
case PB9:
|
|
config.SCL = PB8
|
|
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
|
|
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_I2C1_REMAP)
|
|
default:
|
|
// use default I2C1 pins PB6/PB7
|
|
config.SDA = SDA_PIN
|
|
config.SCL = SCL_PIN
|
|
}
|
|
|
|
config.SDA.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
|
config.SCL.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
|
|
|
// Disable the selected I2C peripheral to configure
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
|
|
|
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
|
pclk1 := CPUFrequency() / 2
|
|
|
|
// set freqency range to PCLK1 clock speed in MHz
|
|
// aka setting the value 36 means to use 36 MHz clock
|
|
pclk1Mhz := pclk1 / 1000000
|
|
i2c.Bus.CR2.SetBits(pclk1Mhz)
|
|
|
|
switch config.Frequency {
|
|
case TWI_FREQ_100KHZ:
|
|
// Normal mode speed calculation
|
|
ccr := pclk1 / (config.Frequency * 2)
|
|
i2c.Bus.CCR.Set(ccr)
|
|
|
|
// duty cycle 2
|
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
|
|
|
// frequency standard mode
|
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_F_S)
|
|
|
|
// Set Maximum Rise Time for standard mode
|
|
i2c.Bus.TRISE.Set(pclk1Mhz)
|
|
|
|
case TWI_FREQ_400KHZ:
|
|
// Fast mode speed calculation
|
|
ccr := pclk1 / (config.Frequency * 3)
|
|
i2c.Bus.CCR.Set(ccr)
|
|
|
|
// duty cycle 2
|
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
|
|
|
// frequency fast mode
|
|
i2c.Bus.CCR.SetBits(stm32.I2C_CCR_F_S)
|
|
|
|
// Set Maximum Rise Time for fast mode
|
|
i2c.Bus.TRISE.Set(((pclk1Mhz * 300) / 1000))
|
|
}
|
|
|
|
// re-enable the selected I2C peripheral
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
|
|
}
|
|
|
|
// Tx does a single I2C transaction at the specified address.
|
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
|
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|
var err error
|
|
if len(w) != 0 {
|
|
// start transmission for writing
|
|
err = i2c.signalStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// send address
|
|
err = i2c.sendAddress(uint8(addr), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, b := range w {
|
|
err = i2c.WriteByte(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// sending stop here for write
|
|
err = i2c.signalStop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(r) != 0 {
|
|
// re-start transmission for reading
|
|
err = i2c.signalStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 1 byte
|
|
switch len(r) {
|
|
case 1:
|
|
// send address
|
|
err = i2c.sendAddress(uint8(addr), false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
|
|
|
// clear timeout here
|
|
timeout := i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
|
|
|
timeout = i2cTimeout
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// Read and return data byte from I2C data register
|
|
r[0] = byte(i2c.Bus.DR.Get())
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
|
|
case 2:
|
|
// enable pos
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_POS)
|
|
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
|
|
|
// send address
|
|
err = i2c.sendAddress(uint8(addr), false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// clear address here
|
|
timeout := i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
|
|
|
// read the 2 bytes by reading twice.
|
|
r[0] = byte(i2c.Bus.DR.Get())
|
|
r[1] = byte(i2c.Bus.DR.Get())
|
|
|
|
// wait for stop
|
|
err = i2c.waitForStop()
|
|
|
|
//disable pos
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
|
|
|
|
return err
|
|
|
|
case 3:
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
|
|
|
// send address
|
|
err = i2c.sendAddress(uint8(addr), false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// clear address here
|
|
timeout := i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
|
|
|
// read the first byte
|
|
r[0] = byte(i2c.Bus.DR.Get())
|
|
|
|
timeout = 1000
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
|
|
|
// read the last 2 bytes by reading twice.
|
|
r[1] = byte(i2c.Bus.DR.Get())
|
|
r[2] = byte(i2c.Bus.DR.Get())
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
|
|
default:
|
|
// more than 3 bytes of data to read
|
|
|
|
// send address
|
|
err = i2c.sendAddress(uint8(addr), false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// clear address here
|
|
timeout := i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(r)-3; i++ {
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// read the next byte
|
|
r[i] = byte(i2c.Bus.DR.Get())
|
|
}
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
|
|
|
// get third from last byte
|
|
r[len(r)-3] = byte(i2c.Bus.DR.Get())
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
|
|
|
// get second from last byte
|
|
r[len(r)-2] = byte(i2c.Bus.DR.Get())
|
|
|
|
timeout = i2cTimeout
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CReadTimeout
|
|
}
|
|
}
|
|
|
|
// get last byte
|
|
r[len(r)-1] = byte(i2c.Bus.DR.Get())
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const i2cTimeout = 500
|
|
|
|
// signalStart sends a start signal.
|
|
func (i2c I2C) signalStart() error {
|
|
// Wait until I2C is not busy
|
|
timeout := i2cTimeout
|
|
for i2c.Bus.SR2.HasBits(stm32.I2C_SR2_BUSY) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CSignalStartTimeout
|
|
}
|
|
}
|
|
|
|
// clear stop
|
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_STOP)
|
|
|
|
// Generate start condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START)
|
|
|
|
// Wait for I2C EV5 aka SB flag.
|
|
timeout = i2cTimeout
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_SB) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CSignalStartTimeout
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// signalStop sends a stop signal and waits for it to succeed.
|
|
func (i2c I2C) signalStop() error {
|
|
// Generate stop condition
|
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
}
|
|
|
|
// waitForStop waits after a stop signal.
|
|
func (i2c I2C) waitForStop() error {
|
|
// Wait until I2C is stopped
|
|
timeout := i2cTimeout
|
|
for i2c.Bus.SR1.HasBits(stm32.I2C_SR1_STOPF) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CSignalStopTimeout
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Send address of device we want to talk to
|
|
func (i2c I2C) sendAddress(address uint8, write bool) error {
|
|
data := (address << 1)
|
|
if !write {
|
|
data |= 1 // set read flag
|
|
}
|
|
|
|
i2c.Bus.DR.Set(uint32(data))
|
|
|
|
// Wait for I2C EV6 event.
|
|
// Destination device acknowledges address
|
|
timeout := i2cTimeout
|
|
if write {
|
|
// EV6 which is ADDR flag.
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
timeout = i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY | stm32.I2C_SR2_TRA) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
} else {
|
|
// I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED which is ADDR flag.
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteByte writes a single byte to the I2C bus.
|
|
func (i2c I2C) WriteByte(data byte) error {
|
|
// Send data byte
|
|
i2c.Bus.DR.Set(uint32(data))
|
|
|
|
// Wait for I2C EV8_2 when data has been physically shifted out and
|
|
// output on the bus.
|
|
// I2C_EVENT_MASTER_BYTE_TRANSMITTED is TXE flag.
|
|
timeout := i2cTimeout
|
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_TxE) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|