mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-12 15:03:41 +00:00
4f4d7976c6
* machine/uart: add core support for multiple UARTs by allowing for multiple RingBuffers * machine/uart: complete core support for multiple UARTs * machine/uart: no need to store pointer to UART, better to treat like I2C and SPI * machine/uart: increase ring buffer size to 128 bytes * machine/uart: improve godocs comments and use comma-ok idiom for buffer Put/Get methods
756 lines
18 KiB
Go
756 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 (
|
|
GPIO_INPUT = 0 // Input mode
|
|
GPIO_OUTPUT_10MHz = 1 // Output mode, max speed 10MHz
|
|
GPIO_OUTPUT_2MHz = 2 // Output mode, max speed 2MHz
|
|
GPIO_OUTPUT_50MHz = 3 // Output mode, max speed 50MHz
|
|
GPIO_OUTPUT = GPIO_OUTPUT_2MHz
|
|
|
|
GPIO_INPUT_MODE_ANALOG = 0 // Input analog mode
|
|
GPIO_INPUT_MODE_FLOATING = 4 // Input floating mode
|
|
GPIO_INPUT_MODE_PULL_UP_DOWN = 8 // Input pull up/down mode
|
|
GPIO_INPUT_MODE_RESERVED = 12 // Input mode (reserved)
|
|
|
|
GPIO_OUTPUT_MODE_GP_PUSH_PULL = 0 // Output mode general purpose push/pull
|
|
GPIO_OUTPUT_MODE_GP_OPEN_DRAIN = 4 // Output mode general purpose open drain
|
|
GPIO_OUTPUT_MODE_ALT_PUSH_PULL = 8 // Output mode alt. purpose push/pull
|
|
GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN = 12 // Output mode alt. purpose open drain
|
|
)
|
|
|
|
func (p GPIO) getPort() *stm32.GPIO_Type {
|
|
switch p.Pin / 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 GPIO) enableClock() {
|
|
switch p.Pin / 16 {
|
|
case 0:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPAEN
|
|
case 1:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPBEN
|
|
case 2:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPCEN
|
|
case 3:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPDEN
|
|
case 4:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPEEN
|
|
case 5:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPFEN
|
|
case 6:
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPGEN
|
|
default:
|
|
panic("machine: unknown port")
|
|
}
|
|
}
|
|
|
|
// Configure this pin with the given configuration.
|
|
func (p GPIO) Configure(config GPIOConfig) {
|
|
// Configure the GPIO pin.
|
|
p.enableClock()
|
|
port := p.getPort()
|
|
pin := p.Pin % 16
|
|
pos := p.Pin % 8 * 4
|
|
if pin < 8 {
|
|
port.CRL = stm32.RegValue((uint32(port.CRL) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
|
} else {
|
|
port.CRH = stm32.RegValue((uint32(port.CRH) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
|
}
|
|
}
|
|
|
|
// Set the pin to high or low.
|
|
// Warning: only use this on an output pin!
|
|
func (p GPIO) Set(high bool) {
|
|
port := p.getPort()
|
|
pin := p.Pin % 16
|
|
if high {
|
|
port.BSRR = 1 << pin
|
|
} else {
|
|
port.BSRR = 1 << (pin + 16)
|
|
}
|
|
}
|
|
|
|
// UART
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
}
|
|
|
|
var (
|
|
// USART1 is the first hardware serial port on the STM32.
|
|
// Both UART0 and UART1 refer to USART1.
|
|
UART0 = UART{Buffer: NewRingBuffer()}
|
|
UART1 = &UART0
|
|
)
|
|
|
|
// 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 PB6:
|
|
// use alternate TX/RX pins PB6/PB7 via AFIO mapping
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_AFIOEN
|
|
stm32.AFIO.MAPR |= stm32.AFIO_MAPR_USART1_REMAP
|
|
GPIO{PB6}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
|
GPIO{PB7}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
|
default:
|
|
// use standard TX/RX pins PA9 and PA10
|
|
GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
|
GPIO{UART_RX_PIN}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
|
}
|
|
|
|
// Enable USART1 clock
|
|
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_USART1EN
|
|
|
|
// Set baud rate
|
|
uart.SetBaudRate(config.BaudRate)
|
|
|
|
// Enable USART1 port.
|
|
stm32.USART1.CR1 = stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE
|
|
|
|
// Enable RX IRQ.
|
|
arm.SetPriority(stm32.IRQ_USART1, 0xc0)
|
|
arm.EnableIRQ(stm32.IRQ_USART1)
|
|
}
|
|
|
|
// SetBaudRate sets the communication speed for the UART.
|
|
func (uart UART) SetBaudRate(br uint32) {
|
|
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
|
divider := CPU_FREQUENCY / br
|
|
stm32.USART1.BRR = stm32.RegValue(divider)
|
|
}
|
|
|
|
// WriteByte writes a byte of data to the UART.
|
|
func (uart UART) WriteByte(c byte) error {
|
|
stm32.USART1.DR = stm32.RegValue(c)
|
|
|
|
for (stm32.USART1.SR & stm32.USART_SR_TXE) == 0 {
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//go:export USART1_IRQHandler
|
|
func handleUART1() {
|
|
UART1.Receive(byte((stm32.USART1.DR & 0xFF)))
|
|
}
|
|
|
|
// 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 uint8
|
|
MOSI uint8
|
|
MISO uint8
|
|
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 |= stm32.RCC_APB2ENR_SPI1EN
|
|
|
|
var conf uint16
|
|
|
|
// set frequency
|
|
switch config.Frequency {
|
|
case 125000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_128
|
|
case 250000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_64
|
|
case 500000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_32
|
|
case 1000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_16
|
|
case 2000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_8
|
|
case 4000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_4
|
|
case 8000000:
|
|
conf |= stm32.SPI_BaudRatePrescaler_2
|
|
default:
|
|
conf |= stm32.SPI_BaudRatePrescaler_128
|
|
}
|
|
|
|
// 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 = stm32.RegValue(conf)
|
|
|
|
// init pins
|
|
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
|
|
|
// enable SPI interface
|
|
spi.Bus.CR1 |= 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 = stm32.RegValue(w)
|
|
|
|
// Wait until transmit complete
|
|
for (spi.Bus.SR & stm32.SPI_SR_TXE) == 0 {
|
|
}
|
|
|
|
// Wait until receive complete
|
|
for (spi.Bus.SR & stm32.SPI_SR_RXNE) == 0 {
|
|
}
|
|
|
|
// Wait until SPI is not busy
|
|
for (spi.Bus.SR & stm32.SPI_SR_BSY) > 0 {
|
|
}
|
|
|
|
// Return received data from SPI data register
|
|
return byte(spi.Bus.DR), nil
|
|
}
|
|
|
|
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|
if sck == 0 {
|
|
sck = SPI0_SCK_PIN
|
|
}
|
|
if mosi == 0 {
|
|
mosi = SPI0_MOSI_PIN
|
|
}
|
|
if miso == 0 {
|
|
miso = SPI0_MISO_PIN
|
|
}
|
|
|
|
GPIO{sck}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
|
GPIO{mosi}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
|
GPIO{miso}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
|
}
|
|
|
|
// 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 uint8
|
|
SDA uint8
|
|
}
|
|
|
|
// 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 |= 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 |= stm32.RCC_APB2ENR_AFIOEN
|
|
stm32.AFIO.MAPR |= stm32.AFIO_MAPR_I2C1_REMAP
|
|
default:
|
|
// use default I2C1 pins PB6/PB7
|
|
config.SDA = SDA_PIN
|
|
config.SCL = SCL_PIN
|
|
}
|
|
|
|
GPIO{config.SDA}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN})
|
|
GPIO{config.SCL}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN})
|
|
|
|
// Disable the selected I2C peripheral to configure
|
|
i2c.Bus.CR1 &^= stm32.I2C_CR1_PE
|
|
|
|
// pclk1 clock speed is main frequency divided by PCK1 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 36MhZ clock.
|
|
pclk1Mhz := pclk1 / 1000000
|
|
i2c.Bus.CR2 |= stm32.RegValue(pclk1Mhz)
|
|
|
|
switch config.Frequency {
|
|
case TWI_FREQ_100KHZ:
|
|
// Normal mode speed calculation
|
|
ccr := pclk1 / (config.Frequency * 2)
|
|
i2c.Bus.CCR = stm32.RegValue(ccr)
|
|
|
|
// duty cycle 2
|
|
i2c.Bus.CCR &^= stm32.I2C_CCR_DUTY
|
|
|
|
// frequency standard mode
|
|
i2c.Bus.CCR &^= stm32.I2C_CCR_F_S
|
|
|
|
// Set Maximum Rise Time for standard mode
|
|
i2c.Bus.TRISE = stm32.RegValue(pclk1Mhz)
|
|
|
|
case TWI_FREQ_400KHZ:
|
|
// Fast mode speed calculation
|
|
ccr := pclk1 / (config.Frequency * 3)
|
|
i2c.Bus.CCR = stm32.RegValue(ccr)
|
|
|
|
// duty cycle 2
|
|
i2c.Bus.CCR &^= stm32.I2C_CCR_DUTY
|
|
|
|
// frequency fast mode
|
|
i2c.Bus.CCR |= stm32.I2C_CCR_F_S
|
|
|
|
// Set Maximum Rise Time for fast mode
|
|
i2c.Bus.TRISE = stm32.RegValue(((pclk1Mhz * 300) / 1000))
|
|
}
|
|
|
|
// re-enable the selected I2C peripheral
|
|
i2c.Bus.CR1 |= 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 &^= stm32.I2C_CR1_ACK
|
|
|
|
// clear timeout here
|
|
timeout := i2cTimeout
|
|
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
|
|
|
timeout = i2cTimeout
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_RxNE) == 0 {
|
|
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)
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
|
|
case 2:
|
|
// enable pos
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_POS
|
|
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1 |= 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&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read 2 bytes")
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
|
|
|
// read the 2 bytes by reading twice.
|
|
r[0] = byte(i2c.Bus.DR)
|
|
r[1] = byte(i2c.Bus.DR)
|
|
|
|
// wait for stop
|
|
return i2c.waitForStop()
|
|
|
|
case 3:
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1 |= 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&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read clear address")
|
|
}
|
|
}
|
|
|
|
// Enable ACK of received data
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_ACK
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
|
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 &^= stm32.I2C_CR1_ACK
|
|
|
|
// read the first byte
|
|
r[0] = byte(i2c.Bus.DR)
|
|
|
|
timeout = 1000
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read 3 bytes")
|
|
}
|
|
}
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
|
|
|
// read the last 2 bytes by reading twice.
|
|
r[1] = byte(i2c.Bus.DR)
|
|
r[2] = byte(i2c.Bus.DR)
|
|
|
|
// 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&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
|
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 |= stm32.I2C_CR1_ACK
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
|
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)
|
|
}
|
|
|
|
// wait for btf. we need a longer timeout here than normal.
|
|
timeout = 1000
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on read more than 3 bytes")
|
|
}
|
|
}
|
|
|
|
// Disable ACK of received data
|
|
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
|
|
|
// get third from last byte
|
|
r[len(r)-3] = byte(i2c.Bus.DR)
|
|
|
|
// Generate stop condition
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
|
|
|
// get second from last byte
|
|
r[len(r)-2] = byte(i2c.Bus.DR)
|
|
|
|
timeout = i2cTimeout
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_RxNE) == 0 {
|
|
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)
|
|
|
|
// 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 & stm32.I2C_SR2_BUSY) > 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C busy on start")
|
|
}
|
|
}
|
|
|
|
// clear stop
|
|
i2c.Bus.CR1 &^= stm32.I2C_CR1_STOP
|
|
|
|
// Generate start condition
|
|
i2c.Bus.CR1 |= stm32.I2C_CR1_START
|
|
|
|
// Wait for I2C EV5 aka SB flag.
|
|
timeout = i2cTimeout
|
|
for (i2c.Bus.SR1 & stm32.I2C_SR1_SB) == 0 {
|
|
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 |= 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 & stm32.I2C_SR1_STOPF) > 0 {
|
|
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 = stm32.RegValue(data)
|
|
|
|
// Wait for I2C EV6 event.
|
|
// Destination device acknowledges address
|
|
timeout := i2cTimeout
|
|
if write {
|
|
// EV6 which is ADDR flag.
|
|
for i2c.Bus.SR1&stm32.I2C_SR1_ADDR == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on send write address")
|
|
}
|
|
}
|
|
|
|
timeout = i2cTimeout
|
|
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY|stm32.I2C_SR2_TRA) == 0 {
|
|
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 & stm32.I2C_SR1_ADDR) == 0 {
|
|
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 = stm32.RegValue(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&stm32.I2C_SR1_TxE == 0 {
|
|
timeout--
|
|
if timeout == 0 {
|
|
return errors.New("I2C timeout on write")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|