machine/stm32f103xx: implmentation for I2C interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
Этот коммит содержится в:
родитель
fad4a735e6
коммит
b8c326d710
5 изменённых файлов: 481 добавлений и 8 удалений
|
@ -57,3 +57,9 @@ const (
|
|||
SPI0_MOSI_PIN = PA7
|
||||
SPI0_MISO_PIN = PA6
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB7
|
||||
SCL_PIN = PB6
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build avr nrf
|
||||
// +build avr nrf stm32f103xx
|
||||
|
||||
package machine
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package machine
|
|||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 72000000
|
||||
|
@ -143,8 +144,8 @@ func (uart UART) Configure(config UARTConfig) {
|
|||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
func (uart UART) SetBaudRate(br uint32) {
|
||||
// first divide by PCK2 prescaler (div 4) and then desired baudrate
|
||||
divider := CPU_FREQUENCY / 4 / br
|
||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||
divider := CPU_FREQUENCY / br
|
||||
stm32.USART1.BRR = stm32.RegValue(divider)
|
||||
}
|
||||
|
||||
|
@ -290,3 +291,461 @@ func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -21,15 +21,21 @@ func putchar(c byte) {
|
|||
|
||||
// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz).
|
||||
func initCLK() {
|
||||
stm32.FLASH.ACR |= stm32.FLASH_ACR_LATENCY_2 // Two wait states, per datasheet
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE1_DIV_2 // prescale PCLK1 = HCLK/2
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE2_DIV_4 // prescale PCLK2 = HCLK/4
|
||||
stm32.RCC.CR |= stm32.RCC_CR_HSEON // enable HSE clock
|
||||
stm32.FLASH.ACR |= stm32.FLASH_ACR_LATENCY_2 // Two wait states, per datasheet
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE1_DIV_2 // prescale PCLK1 = HCLK/2
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE2_DIV_NONE // prescale PCLK2 = HCLK/1
|
||||
stm32.RCC.CR |= stm32.RCC_CR_HSEON // enable HSE clock
|
||||
|
||||
// wait for the HSEREADY flag
|
||||
for (stm32.RCC.CR & stm32.RCC_CR_HSERDY) == 0 {
|
||||
}
|
||||
|
||||
stm32.RCC.CR |= stm32.RCC_CR_HSION // enable HSI clock
|
||||
|
||||
// wait for the HSIREADY flag
|
||||
for (stm32.RCC.CR & stm32.RCC_CR_HSIRDY) == 0 {
|
||||
}
|
||||
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PLLSRC // set PLL source to HSE
|
||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PLLMUL_9 // multiply by 9
|
||||
stm32.RCC.CR |= stm32.RCC_CR_PLLON // enable the PLL
|
||||
|
|
|
@ -12,5 +12,7 @@
|
|||
"extra-files": [
|
||||
"src/device/stm32/stm32f103xx.s"
|
||||
],
|
||||
"flash": "openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c 'program {hex} reset exit'"
|
||||
"flash": "openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/stlink-v2.cfg", "-f", "target/stm32f1x.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче