mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 22:13:39 +00:00
688dd81400
Motivation: The bluepill uses USART1 as UART0 but other boards like the STM32 Nucleo boards (and disco as well) use USART2 for USB COM port. To avoid duplication of code the same pattern as in `machine_atsamd21.go` is applied where only UART-specific code is moved to `board_*.go`.
775 lines
18 KiB
Go
775 lines
18 KiB
Go
// +build stm32,stm32f103xx
|
|
|
|
package machine
|
|
|
|
// Peripheral abstraction layer for the stm32.
|
|
|
|
import (
|
|
"device/arm"
|
|
"device/stm32"
|
|
"errors"
|
|
)
|
|
|
|
const CPU_FREQUENCY = 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
|
|
)
|
|
|
|
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")
|
|
}
|
|
}
|
|
|
|
// Configure this pin with the given configuration.
|
|
func (p Pin) Configure(config PinConfig) {
|
|
// Configure the GPIO pin.
|
|
p.enableClock()
|
|
port := p.getPort()
|
|
pin := uint8(p) % 16
|
|
pos := uint8(p) % 8 * 4
|
|
if pin < 8 {
|
|
port.CRL.Set((uint32(port.CRL.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
|
} else {
|
|
port.CRH.Set((uint32(port.CRH.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
|
}
|
|
}
|
|
|
|
// Set the pin to high or low.
|
|
// Warning: only use this on an output pin!
|
|
func (p Pin) Set(high bool) {
|
|
port := p.getPort()
|
|
pin := uint8(p) % 16
|
|
if high {
|
|
port.BSRR.Set(1 << pin)
|
|
} else {
|
|
port.BSRR.Set(1 << (pin + 16))
|
|
}
|
|
}
|
|
|
|
// Get returns the current value of a GPIO pin.
|
|
func (p Pin) Get() bool {
|
|
port := p.getPort()
|
|
pin := uint8(p) % 16
|
|
val := port.IDR.Get() & (1 << pin)
|
|
return (val > 0)
|
|
}
|
|
|
|
// UART
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
Bus *stm32.USART_Type
|
|
IRQVal uint32
|
|
}
|
|
|
|
// Configure the UART.
|
|
func (uart UART) Configure(config UARTConfig) {
|
|
// Default baud rate to 115200.
|
|
if config.BaudRate == 0 {
|
|
config.BaudRate = 115200
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
UART_ALT_TX_PIN.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
UART_ALT_RX_PIN.Configure(PinConfig{Mode: PinInputModeFloating})
|
|
default:
|
|
// use standard TX/RX pins PA9 and PA10
|
|
UART_TX_PIN.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
UART_RX_PIN.Configure(PinConfig{Mode: PinInputModeFloating})
|
|
}
|
|
|
|
// Enable USART clock
|
|
if uart.Bus == stm32.USART1 {
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
} else if uart.Bus == stm32.USART2 {
|
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
|
}
|
|
|
|
// Set baud rate
|
|
uart.SetBaudRate(config.BaudRate)
|
|
|
|
// Enable USART port
|
|
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
|
|
|
// Enable RX IRQ
|
|
arm.SetPriority(uart.IRQVal, 0xc0)
|
|
arm.EnableIRQ(uart.IRQVal)
|
|
}
|
|
|
|
// SetBaudRate sets the communication speed for the UART.
|
|
func (uart UART) SetBaudRate(br 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 = CPU_FREQUENCY / br
|
|
} else {
|
|
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
|
divider = CPU_FREQUENCY / 2 / br
|
|
}
|
|
uart.Bus.BRR.Set(divider)
|
|
}
|
|
|
|
// WriteByte writes a byte of data to the UART.
|
|
func (uart UART) WriteByte(c byte) error {
|
|
uart.Bus.DR.Set(uint32(c))
|
|
|
|
for !uart.Bus.SR.HasBits(stm32.USART_SR_TXE) {
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SPI on the STM32.
|
|
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
|
|
)
|
|
|
|
// SPIConfig is used to store config info for SPI.
|
|
type SPIConfig struct {
|
|
Frequency uint32
|
|
SCK Pin
|
|
MOSI Pin
|
|
MISO Pin
|
|
LSBFirst bool
|
|
Mode uint8
|
|
}
|
|
|
|
// Configure is intended to setup the STM32 SPI1 interface.
|
|
// Features still TODO:
|
|
// - support SPI2 and SPI3
|
|
// - allow setting data size to 16 bits?
|
|
// - allow setting direction in HW for additional optimization?
|
|
// - hardware SS pin?
|
|
func (spi SPI) Configure(config SPIConfig) {
|
|
// enable clock for SPI
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
|
|
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
|
|
}
|
|
|
|
// set bit transfer order
|
|
if config.LSBFirst {
|
|
conf |= stm32.SPI_FirstBit_LSB
|
|
}
|
|
|
|
// set mode
|
|
switch config.Mode {
|
|
case 0:
|
|
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
|
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
|
case 1:
|
|
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
|
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
|
case 2:
|
|
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
|
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
|
case 3:
|
|
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
|
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
|
default: // to mode 0
|
|
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
|
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
|
}
|
|
|
|
// set to SPI master
|
|
conf |= stm32.SPI_Mode_Master
|
|
|
|
// now set the configuration
|
|
spi.Bus.CR1.Set(conf)
|
|
|
|
// init pins
|
|
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
|
|
|
// enable SPI interface
|
|
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
|
}
|
|
|
|
// Transfer writes/reads a single byte using the SPI interface.
|
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
|
// Write data to be transmitted to the SPI data register
|
|
spi.Bus.DR.Set(uint32(w))
|
|
|
|
// Wait until transmit complete
|
|
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
|
|
}
|
|
|
|
// Wait until receive complete
|
|
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
|
|
}
|
|
|
|
// Wait until SPI is not busy
|
|
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
|
|
}
|
|
|
|
// Return received data from SPI data register
|
|
return byte(spi.Bus.DR.Get()), nil
|
|
}
|
|
|
|
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
|
if sck == 0 {
|
|
sck = SPI0_SCK_PIN
|
|
}
|
|
if mosi == 0 {
|
|
mosi = SPI0_MOSI_PIN
|
|
}
|
|
if miso == 0 {
|
|
miso = SPI0_MISO_PIN
|
|
}
|
|
|
|
sck.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
mosi.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
|
miso.Configure(PinConfig{Mode: PinInputModeFloating})
|
|
}
|
|
|
|
// I2C on the STM32F103xx.
|
|
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 := uint32(CPU_FREQUENCY / 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 errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read 1 byte")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read 2 bytes")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
println("I2C timeout on read 3 bytes")
|
|
return errors.New("I2C timeout on read 3 bytes")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read 3 bytes")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
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 {
|
|
println("I2C timeout on read 3 bytes")
|
|
return errors.New("I2C timeout on read 3 bytes")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read more than 3 bytes")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on read last byte of more than 3")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C busy on start")
|
|
}
|
|
}
|
|
|
|
// 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 errors.New("I2C timeout on start")
|
|
}
|
|
}
|
|
|
|
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 {
|
|
println("I2C timeout on wait for stop signal")
|
|
return errors.New("I2C timeout on wait for stop signal")
|
|
}
|
|
}
|
|
|
|
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 errors.New("I2C timeout on send write address")
|
|
}
|
|
}
|
|
|
|
timeout = i2cTimeout
|
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY | stm32.I2C_SR2_TRA) {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on send write address")
|
|
}
|
|
}
|
|
} 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 errors.New("I2C timeout on send read address")
|
|
}
|
|
}
|
|
}
|
|
|
|
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 errors.New("I2C timeout on write")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|