From 47b667a4b89a21a82c3ca583e19496748d92e95e Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Thu, 13 Dec 2018 16:12:33 +0100 Subject: [PATCH] machine/stm32: add support for stm32f103xx UART Signed-off-by: Ron Evans --- src/device/stm32/stm32f103xx-bitfields.go | 44 ++++++ src/machine/machine_stm32.go | 60 -------- src/machine/machine_stm32f103xx.go | 161 ++++++++++++++++++++++ src/machine/uart.go | 4 +- src/runtime/runtime_stm32.go | 21 --- src/runtime/runtime_stm32f103xx.go | 56 ++++++++ 6 files changed, 264 insertions(+), 82 deletions(-) create mode 100644 src/device/stm32/stm32f103xx-bitfields.go create mode 100644 src/machine/machine_stm32f103xx.go create mode 100644 src/runtime/runtime_stm32f103xx.go diff --git a/src/device/stm32/stm32f103xx-bitfields.go b/src/device/stm32/stm32f103xx-bitfields.go new file mode 100644 index 00000000..1478074e --- /dev/null +++ b/src/device/stm32/stm32f103xx-bitfields.go @@ -0,0 +1,44 @@ +// Hand created file. DO NOT DELETE. +// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.py + +// +build stm32,stm32f103xx + +package stm32 + +const ( + // Flash Access Control Register flag values. + FLASH_ACR_LATENCY_0 = 0x00000001 + FLASH_ACR_LATENCY_1 = 0x00000002 + FLASH_ACR_LATENCY_2 = 0x00000004 + + // Reset and Clock Control Control Register flag values. + RCC_CFGR_SW_HSI = 0 + RCC_CFGR_SW_HSE = 1 + RCC_CFGR_SW_PLL = 2 + + RCC_CFGR_SWS_HSI = 0x00000000 + RCC_CFGR_SWS_HSE = 0x00000004 + RCC_CFGR_SWS_PLL = 0x00000008 + + RCC_CFGR_PPRE1_DIV_NONE = 0x00000000 + RCC_CFGR_PPRE1_DIV_2 = 0x00000400 + RCC_CFGR_PPRE1_DIV_4 = 0x00000500 + RCC_CFGR_PPRE1_DIV_8 = 0x00000600 + RCC_CFGR_PPRE1_DIV_16 = 0x00000700 + + RCC_CFGR_PLLMUL_2 = 0x00000000 + RCC_CFGR_PLLMUL_3 = 0x00040000 + RCC_CFGR_PLLMUL_4 = 0x00080000 + RCC_CFGR_PLLMUL_5 = 0x000C0000 + RCC_CFGR_PLLMUL_6 = 0x00100000 + RCC_CFGR_PLLMUL_7 = 0x00140000 + RCC_CFGR_PLLMUL_8 = 0x00180000 + RCC_CFGR_PLLMUL_9 = 0x001C0000 + RCC_CFGR_PLLMUL_10 = 0x00200000 + RCC_CFGR_PLLMUL_11 = 0x00240000 + RCC_CFGR_PLLMUL_12 = 0x00280000 + RCC_CFGR_PLLMUL_13 = 0x002C0000 + RCC_CFGR_PLLMUL_14 = 0x00300000 + RCC_CFGR_PLLMUL_15 = 0x00340000 + RCC_CFGR_PLLMUL_16 = 0x00380000 +) diff --git a/src/machine/machine_stm32.go b/src/machine/machine_stm32.go index 2c6f8bfc..6c4b4475 100644 --- a/src/machine/machine_stm32.go +++ b/src/machine/machine_stm32.go @@ -4,17 +4,8 @@ package machine // Peripheral abstraction layer for the stm32. -import ( - "device/stm32" -) - type GPIOMode uint8 -const ( - GPIO_INPUT = 0 // Input mode - GPIO_OUTPUT = 2 // Output mode, max speed 2MHz -) - const ( portA = iota * 16 portB @@ -24,54 +15,3 @@ const ( portF portG ) - -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") - } -} - -// Configure this pin with the given configuration. -func (p GPIO) Configure(config GPIOConfig) { - // Enable clock. - // Do this always, as it isn't known whether the clock has already been - // enabled. - stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPCEN - - // Configure the GPIO pin. - 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) - } -} diff --git a/src/machine/machine_stm32f103xx.go b/src/machine/machine_stm32f103xx.go new file mode 100644 index 00000000..714b6351 --- /dev/null +++ b/src/machine/machine_stm32f103xx.go @@ -0,0 +1,161 @@ +// +build stm32,stm32f103xx + +package machine + +// Peripheral abstraction layer for the stm32. + +import ( + "device/arm" + "device/stm32" +) + +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 +var ( + // USART1 is the first hardware serial port on the STM32. + // Both UART0 and UART1 refers to USART1. + UART0 = &UART{} + 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{PA9}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL}) + GPIO{PA10}.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.EnableIRQ(stm32.IRQ_USART1) +} + +// SetBaudRate sets the communication speed for the UART. +func (uart UART) SetBaudRate(br uint32) { + 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() { + bufferPut(byte((stm32.USART1.DR & 0xFF))) +} diff --git a/src/machine/uart.go b/src/machine/uart.go index 566094fb..84f3f088 100644 --- a/src/machine/uart.go +++ b/src/machine/uart.go @@ -1,4 +1,4 @@ -// +build avr nrf +// +build avr nrf stm32 package machine @@ -6,6 +6,8 @@ import "errors" type UARTConfig struct { BaudRate uint32 + TX uint8 + RX uint8 } type UART struct { diff --git a/src/runtime/runtime_stm32.go b/src/runtime/runtime_stm32.go index e068a4d4..b1eb98df 100644 --- a/src/runtime/runtime_stm32.go +++ b/src/runtime/runtime_stm32.go @@ -2,14 +2,8 @@ package runtime -import ( - "device/arm" -) - type timeUnit int64 -const tickMicros = 1 // TODO - //go:export Reset_Handler func main() { preinit() @@ -17,18 +11,3 @@ func main() { mainWrapper() abort() } - -func putchar(c byte) { - // TODO -} - -func sleepTicks(d timeUnit) { - // TODO: use a real timer here - for i := 0; i < int(d/535); i++ { - arm.Asm("") - } -} - -func ticks() timeUnit { - return 0 // TODO -} diff --git a/src/runtime/runtime_stm32f103xx.go b/src/runtime/runtime_stm32f103xx.go new file mode 100644 index 00000000..cd20758c --- /dev/null +++ b/src/runtime/runtime_stm32f103xx.go @@ -0,0 +1,56 @@ +// +build stm32,stm32f103xx + +package runtime + +import ( + "device/arm" + "device/stm32" + "machine" +) + +const tickMicros = 1 // TODO + +func init() { + initCLK() + machine.UART0.Configure(machine.UARTConfig{}) +} + +func putchar(c byte) { + machine.UART0.WriteByte(c) +} + +// 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 AHB1 = HCLK/2 + 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.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 + + // wait for the PLLRDY flag + for (stm32.RCC.CR & stm32.RCC_CR_PLLRDY) == 0 { + } + + stm32.RCC.CFGR |= stm32.RCC_CFGR_SW_PLL // set clock source to pll + + // wait for PLL to be CLK + for (stm32.RCC.CFGR & stm32.RCC_CFGR_SWS_PLL) == 0 { + } +} + +func sleepTicks(d timeUnit) { + // TODO: use a real timer here + for i := 0; i < int(d/535); i++ { + arm.Asm("") + } +} + +func ticks() timeUnit { + return 0 // TODO +}