aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKenneth Bell <[email protected]>2021-03-19 22:45:56 -0700
committerRon Evans <[email protected]>2021-03-21 11:25:10 +0100
commitb5205cc3ca598eba12be9552ab29f64178a30f37 (patch)
tree1d57acf65469c2919a0b5ae24225fa15f1ee805f
parenta075cbedf5a7d7de2834ffc3632503b798e53d49 (diff)
downloadtinygo-b5205cc3ca598eba12be9552ab29f64178a30f37.tar.gz
tinygo-b5205cc3ca598eba12be9552ab29f64178a30f37.zip
stm32: move f103 (bluepill) to common i2c code
-rw-r--r--src/machine/board_bluepill.go4
-rw-r--r--src/machine/board_nucleof103rb.go4
-rw-r--r--src/machine/machine_stm32_i2c.go2
-rw-r--r--src/machine/machine_stm32f103.go464
4 files changed, 46 insertions, 428 deletions
diff --git a/src/machine/board_bluepill.go b/src/machine/board_bluepill.go
index e3055eec4..51e389349 100644
--- a/src/machine/board_bluepill.go
+++ b/src/machine/board_bluepill.go
@@ -85,6 +85,6 @@ const (
// I2C pins
const (
- SDA_PIN = PB7
- SCL_PIN = PB6
+ I2C0_SDA_PIN = PB7
+ I2C0_SCL_PIN = PB6
)
diff --git a/src/machine/board_nucleof103rb.go b/src/machine/board_nucleof103rb.go
index f3be4d7f7..8908864d8 100644
--- a/src/machine/board_nucleof103rb.go
+++ b/src/machine/board_nucleof103rb.go
@@ -120,6 +120,6 @@ const (
// I2C pins
const (
- SCL_PIN = PB6
- SDA_PIN = PB7
+ I2C0_SCL_PIN = PB6
+ I2C0_SDA_PIN = PB7
)
diff --git a/src/machine/machine_stm32_i2c.go b/src/machine/machine_stm32_i2c.go
index fbee11d8e..4642b3fc2 100644
--- a/src/machine/machine_stm32_i2c.go
+++ b/src/machine/machine_stm32_i2c.go
@@ -1,4 +1,4 @@
-// +build stm32,!stm32f103,!stm32f7x2,!stm32l5x2,!stm32l0
+// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l0
package machine
diff --git a/src/machine/machine_stm32f103.go b/src/machine/machine_stm32f103.go
index 2de299c58..ae8113e72 100644
--- a/src/machine/machine_stm32f103.go
+++ b/src/machine/machine_stm32f103.go
@@ -197,10 +197,6 @@ func (spi SPI) configurePins(config SPIConfig) {
//---------- 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.
@@ -209,451 +205,73 @@ var (
I2C0 = I2C1
)
-// I2CConfig is used to store config info for I2C.
-type I2CConfig struct {
- Frequency uint32
- SCL Pin
- SDA Pin
+type I2C struct {
+ Bus *stm32.I2C_Type
}
-// Configure is intended to setup the I2C interface.
-func (i2c I2C) Configure(config I2CConfig) error {
- // 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
+func (i2c I2C) configurePins(config I2CConfig) {
+ if config.SDA == PB9 {
// 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)
-
+func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
// 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)
-
- return nil
+ return pclk1 / 1000000
}
-// 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
- }
+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
}
- 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
+ return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
}
-const i2cTimeout = 1000
-
-// 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
- }
+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
}
-
- // 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
+ sm := func(pclk uint32, freq uint32) uint32 { // standard mode (Sm)
+ if s := ccr(pclk, freq, 2); s < 4 {
+ return 4
+ } else {
+ return s
}
}
-
- 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
+ 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
}
}
-
- 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
- }
- }
+ clock := CPUFrequency() / 2
+ if config.Frequency <= 100000 {
+ return sm(clock, config.Frequency)
} 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
- }
+ 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
}
}
-
- 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
}