diff --git a/src/machine/board_bluepill.go b/src/machine/board_bluepill.go index e9fc54de..b950c935 100644 --- a/src/machine/board_bluepill.go +++ b/src/machine/board_bluepill.go @@ -57,3 +57,9 @@ const ( SPI0_MOSI_PIN = PA7 SPI0_MISO_PIN = PA6 ) + +// I2C pins +const ( + SDA_PIN = PB7 + SCL_PIN = PB6 +) diff --git a/src/machine/i2c.go b/src/machine/i2c.go index 5dc2cdd8..7791cc65 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -1,4 +1,4 @@ -// +build avr nrf +// +build avr nrf stm32f103xx package machine diff --git a/src/machine/machine_stm32f103xx.go b/src/machine/machine_stm32f103xx.go index 8f1c2a0f..c10df792 100644 --- a/src/machine/machine_stm32f103xx.go +++ b/src/machine/machine_stm32f103xx.go @@ -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 +} diff --git a/src/runtime/runtime_stm32f103xx.go b/src/runtime/runtime_stm32f103xx.go index 21d87997..b20470be 100644 --- a/src/runtime/runtime_stm32f103xx.go +++ b/src/runtime/runtime_stm32f103xx.go @@ -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 diff --git a/targets/bluepill.json b/targets/bluepill.json index ef2c915a..cacaf343 100644 --- a/targets/bluepill.json +++ b/targets/bluepill.json @@ -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"] }