diff options
author | Kenneth Bell <[email protected]> | 2021-03-19 22:45:56 -0700 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-03-21 11:25:10 +0100 |
commit | b5205cc3ca598eba12be9552ab29f64178a30f37 (patch) | |
tree | 1d57acf65469c2919a0b5ae24225fa15f1ee805f | |
parent | a075cbedf5a7d7de2834ffc3632503b798e53d49 (diff) | |
download | tinygo-b5205cc3ca598eba12be9552ab29f64178a30f37.tar.gz tinygo-b5205cc3ca598eba12be9552ab29f64178a30f37.zip |
stm32: move f103 (bluepill) to common i2c code
-rw-r--r-- | src/machine/board_bluepill.go | 4 | ||||
-rw-r--r-- | src/machine/board_nucleof103rb.go | 4 | ||||
-rw-r--r-- | src/machine/machine_stm32_i2c.go | 2 | ||||
-rw-r--r-- | src/machine/machine_stm32f103.go | 464 |
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 } |