diff --git a/src/device/stm32/stm32-moder-bitfields.go b/src/device/stm32/stm32-moder-bitfields.go new file mode 100644 index 00000000..02298ae0 --- /dev/null +++ b/src/device/stm32/stm32-moder-bitfields.go @@ -0,0 +1,45 @@ +// Hand created file. DO NOT DELETE. +// STM32FXXX (except stm32f1xx) bitfield definitions that are not +// auto-generated by gen-device-svd.go + +// These apply to the stm32 families that use the MODER, OTYPE amd PUPDR +// registers for managing GPIO functionality. + +// Add in other families that use the same settings, e.g. stm32f0xx, etc + +// +build stm32,!stm32f103xx + +package stm32 + +// AltFunc represents the alternate function peripherals that can be mapped to +// the GPIO ports. Since these differ by what is supported on the stm32 family +// they are defined in the more specific files +type AltFunc uint8 + +// Family-wide common post-reset AltFunc. This represents +// normal GPIO operation of the pins +const AF0_SYSTEM AltFunc = 0 + +const ( + // Register values for the chip + // GPIOx_MODER + GPIOModeInput = 0 + GPIOModeOutputGeneral = 1 + GPIOModeOutputAltFunc = 2 + GPIOModeAnalog = 3 + + // GPIOx_OTYPER + GPIOOutputTypePushPull = 0 + GPIOOutputTypeOpenDrain = 1 + + // GPIOx_OSPEEDR + GPIOSpeedLow = 0 + GPIOSpeedMid = 1 + GPIOSpeedHigh = 2 // Note: this is also low speed on stm32f0, see RM0091 + GPIOSpeedVeryHigh = 3 + + // GPIOx_PUPDR + GPIOPUPDRFloating = 0 + GPIOPUPDRPullUp = 1 + GPIOPUPDRPullDown = 2 +) diff --git a/src/device/stm32/stm32f407xx-altfunc-bitfields.go b/src/device/stm32/stm32f407xx-altfunc-bitfields.go new file mode 100644 index 00000000..809cb203 --- /dev/null +++ b/src/device/stm32/stm32f407xx-altfunc-bitfields.go @@ -0,0 +1,26 @@ +// These are the supported alternate function numberings on the stm32f407 +// +build stm32,stm32f407 + +// Alternate function settings on the stm32f4xx series + +package stm32 + +const ( + // Alternative peripheral pin functions + // AF0_SYSTEM is defined im the common bitfields package + AF1_TIM1_2 AltFunc = 1 + AF2_TIM3_4_5 = 2 + AF3_TIM8_9_10_11 = 3 + AF4_I2C1_2_3 = 4 + AF5_SPI1_SPI2 = 5 + AF6_SPI3 = 6 + AF7_USART1_2_3 = 7 + AF8_USART4_5_6 = 8 + AF9_CAN1_CAN2_TIM12_13_14 = 9 + AF10_OTG_FS_OTG_HS = 10 + AF11_ETH = 11 + AF12_FSMC_SDIO_OTG_HS_1 = 12 + AF13_DCMI = 13 + AF14 = 14 + AF15_EVENTOUT = 15 +) diff --git a/src/machine/machine_stm32.go b/src/machine/machine_stm32.go index 89fed477..b1ee97aa 100644 --- a/src/machine/machine_stm32.go +++ b/src/machine/machine_stm32.go @@ -5,3 +5,38 @@ package machine // Peripheral abstraction layer for the stm32. type PinMode uint8 + +// Peripheral operations sequence: +// 1. Enable the clock to the alternate function. +// 2. Enable clock to corresponding GPIO +// 3. Attach the alternate function. +// 4. Configure the input-output port and pins (of the corresponding GPIOx) to match the AF . +// 5. If desired enable the nested vector interrupt control to generate interrupts. +// 6. Program the AF/peripheral for the required configuration (eg baud rate for a USART) . + +// Given that the stm32 family has the AF and GPIO on different registers based on the chip, +// use the main function here for configuring, and use hooks in the more specific chip +// definition files +// Also, the stm32f1xx series handles things differently from the stm32f0/2/3/4 + +// ---------- General pin operations ---------- + +// Set the pin to high or low. +// Warning: only use this on an output pin! +func (p Pin) Set(high bool) { + port := p.getPort() + pin := uint8(p) % 16 + if high { + port.BSRR.Set(1 << pin) + } else { + port.BSRR.Set(1 << (pin + 16)) + } +} + +// Get returns the current value of a GPIO pin. +func (p Pin) Get() bool { + port := p.getPort() + pin := uint8(p) % 16 + val := port.IDR.Get() & (1 << pin) + return (val > 0) +} diff --git a/src/machine/machine_stm32_moder_gpio.go b/src/machine/machine_stm32_moder_gpio.go new file mode 100644 index 00000000..5ce265a6 --- /dev/null +++ b/src/machine/machine_stm32_moder_gpio.go @@ -0,0 +1,128 @@ +// +build stm32,!stm32f103xx + +package machine + +import ( + "device/stm32" +) + +// GPIO for the stm32 families except the stm32f1xx which uses a simpler but +// less flexible mechanism. Extend the +build directive above to exclude other +// models in the stm32f1xx series as necessary + +const ( + // Mode Flag + PinOutput PinMode = 0 + PinInput PinMode = PinInputFloating + PinInputFloating PinMode = 1 + PinInputPulldown PinMode = 2 + PinInputPullup PinMode = 3 + + // for UART + PinModeUARTTX PinMode = 4 + PinModeUARTRX PinMode = 5 + + // for I2C + PinModeI2CSCL PinMode = 6 + PinModeI2CSDA PinMode = 7 + + // for SPI + PinModeSPICLK PinMode = 8 + PinModeSPIMOSI PinMode = 9 + PinModeSPIMISO PinMode = 10 + + // for analog/ADC + PinInputAnalog PinMode = 11 + + // for PWM + // TBD +) + +// Configure this pin with the given configuration +func (p Pin) Configure(config PinConfig) { + // Use the default system alternate function; this + // will only be used if you try to call this with + // one of the peripheral modes instead of vanilla GPIO. + p.ConfigureAltFunc(config, stm32.AF0_SYSTEM) +} + +// Configure this pin with the given configuration including alternate +// function mapping if necessary. +func (p Pin) ConfigureAltFunc(config PinConfig, altFunc stm32.AltFunc) { + // Configure the GPIO pin. + p.enableClock() + port := p.getPort() + pin := uint8(p) % 16 + pos := pin * 2 + + switch config.Mode { + + // GPIO + case PinInputFloating: + port.MODER.ReplaceBits(stm32.GPIOModeInput, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + case PinInputPulldown: + port.MODER.ReplaceBits(stm32.GPIOModeInput, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRPullDown, 0x3, pos) + case PinInputPullup: + port.MODER.ReplaceBits(stm32.GPIOModeInput, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRPullUp, 0x3, pos) + case PinOutput: + port.MODER.ReplaceBits(stm32.GPIOModeOutputGeneral, 0x3, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedHigh, 0x3, pos) + + // UART + case PinModeUARTTX: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedHigh, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRPullUp, 0x3, pos) + p.SetAltFunc(altFunc) + case PinModeUARTRX: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + + // I2C) + case PinModeI2CSCL: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OTYPER.ReplaceBits(stm32.GPIOOutputTypeOpenDrain, 0x1, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedLow, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + case PinModeI2CSDA: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OTYPER.ReplaceBits(stm32.GPIOOutputTypeOpenDrain, 0x1, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedLow, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + + // SPI + case PinModeSPICLK: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedLow, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + case PinModeSPIMOSI: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedLow, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + case PinModeSPIMISO: + port.MODER.ReplaceBits(stm32.GPIOModeOutputAltFunc, 0x3, pos) + port.OSPEEDR.ReplaceBits(stm32.GPIOSpeedLow, 0x3, pos) + port.PUPDR.ReplaceBits(stm32.GPIOPUPDRFloating, 0x3, pos) + p.SetAltFunc(altFunc) + } +} + +// SetAltFunc maps the given alternative function to the I/O pin +func (p Pin) SetAltFunc(af stm32.AltFunc) { + port := p.getPort() + pin := uint8(p) % 16 + pos := (pin % 8) * 4 + if pin < 8 { + port.AFRL.ReplaceBits(uint32(af), 0xf, pos) + } else { + port.AFRH.ReplaceBits(uint32(af), 0xf, pos) + } +} diff --git a/src/machine/machine_stm32f103xx.go b/src/machine/machine_stm32f103xx.go index 6aafb264..332900da 100644 --- a/src/machine/machine_stm32f103xx.go +++ b/src/machine/machine_stm32f103xx.go @@ -8,6 +8,7 @@ import ( "device/stm32" "errors" "runtime/interrupt" + "unsafe" ) func CPUFrequency() uint32 { @@ -32,6 +33,20 @@ const ( PinOutputModeAltOpenDrain PinMode = 12 // Output mode alt. purpose open drain ) +// Configure this pin with the given I/O settings. +// stm32f1xx uses different technique for setting the GPIO pins than the stm32f407 +func (p Pin) Configure(config PinConfig) { + // Configure the GPIO pin. + port := p.getPort() + pin := uint8(p) % 16 + pos := (pin % 8) * 4 + if pin < 8 { + port.CRL.ReplaceBits(uint32(config.Mode), 0xf, pos) + } else { + port.CRH.ReplaceBits(uint32(config.Mode), 0xf, pos) + } +} + func (p Pin) getPort() *stm32.GPIO_Type { switch p / 16 { case 0: @@ -75,40 +90,19 @@ func (p Pin) enableClock() { } } -// Configure this pin with the given configuration. -func (p Pin) Configure(config PinConfig) { - // Configure the GPIO pin. - p.enableClock() - port := p.getPort() - pin := uint8(p) % 16 - pos := uint8(p) % 8 * 4 - if pin < 8 { - port.CRL.Set((uint32(port.CRL.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos)) - } else { - port.CRH.Set((uint32(port.CRH.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos)) - } -} - -// Set the pin to high or low. -// Warning: only use this on an output pin! -func (p Pin) Set(high bool) { - port := p.getPort() - pin := uint8(p) % 16 - if high { - port.BSRR.Set(1 << pin) - } else { - port.BSRR.Set(1 << (pin + 16)) +// Enable peripheral clock. Expand to include all the desired peripherals +func enableAltFuncClock(bus unsafe.Pointer) { + if bus == unsafe.Pointer(stm32.USART1) { + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN) + } else if bus == unsafe.Pointer(stm32.USART2) { + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN) + } else if bus == unsafe.Pointer(stm32.I2C1) { + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN) + } else if bus == unsafe.Pointer(stm32.SPI1) { + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN) } } -// Get returns the current value of a GPIO pin. -func (p Pin) Get() bool { - port := p.getPort() - pin := uint8(p) % 16 - val := port.IDR.Get() & (1 << pin) - return (val > 0) -} - // UART type UART struct { Buffer *RingBuffer @@ -123,6 +117,12 @@ func (uart UART) Configure(config UARTConfig) { config.BaudRate = 115200 } + // Set the GPIO pins to defaults if they're not set + if config.TX == 0 && config.RX == 0 { + config.TX = UART_TX_PIN + config.RX = UART_RX_PIN + } + // pins switch config.TX { case UART_ALT_TX_PIN: @@ -133,20 +133,14 @@ func (uart UART) Configure(config UARTConfig) { } else if uart.Bus == stm32.USART2 { stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_USART2_REMAP) } - UART_ALT_TX_PIN.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull}) - UART_ALT_RX_PIN.Configure(PinConfig{Mode: PinInputModeFloating}) default: // use standard TX/RX pins PA9 and PA10 - UART_TX_PIN.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull}) - UART_RX_PIN.Configure(PinConfig{Mode: PinInputModeFloating}) } + config.TX.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull}) + config.RX.Configure(PinConfig{Mode: PinInputModeFloating}) // Enable USART clock - if uart.Bus == stm32.USART1 { - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN) - } else if uart.Bus == stm32.USART2 { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN) - } + enableAltFuncClock(unsafe.Pointer(uart.Bus)) // Set baud rate uart.SetBaudRate(config.BaudRate) diff --git a/src/machine/machine_stm32f407.go b/src/machine/machine_stm32f407.go index c75e3877..85d9e2b2 100644 --- a/src/machine/machine_stm32f407.go +++ b/src/machine/machine_stm32f407.go @@ -2,51 +2,18 @@ package machine -// Peripheral abstraction layer for the stm32. +// Peripheral abstraction layer for the stm32f4(07) import ( "device/stm32" "runtime/interrupt" + "unsafe" ) func CPUFrequency() uint32 { return 168000000 } -const ( - // Mode Flag - PinOutput PinMode = 0 - PinInput PinMode = PinInputFloating - PinInputFloating PinMode = 1 - PinInputPulldown PinMode = 2 - PinInputPullup PinMode = 3 - - // for UART - PinModeUartTX PinMode = 4 - PinModeUartRX PinMode = 5 - - //GPIOx_MODER - GPIO_MODE_INPUT = 0 - GPIO_MODE_GENERAL_OUTPUT = 1 - GPIO_MODE_ALTERNABTIVE = 2 - GPIO_MODE_ANALOG = 3 - - //GPIOx_OTYPER - GPIO_OUTPUT_MODE_PUSH_PULL = 0 - GPIO_OUTPUT_MODE_OPEN_DRAIN = 1 - - // GPIOx_OSPEEDR - GPIO_SPEED_LOW = 0 - GPIO_SPEED_MID = 1 - GPIO_SPEED_HI = 2 - GPIO_SPEED_VERY_HI = 3 - - // GPIOx_PUPDR - GPIO_FLOATING = 0 - GPIO_PULL_UP = 1 - GPIO_PULL_DOWN = 2 -) - func (p Pin) getPort() *stm32.GPIO_Type { switch p / 16 { case 0: @@ -98,69 +65,33 @@ func (p Pin) enableClock() { } } -// Configure this pin with the given configuration. -func (p Pin) Configure(config PinConfig) { - // Configure the GPIO pin. - p.enableClock() - port := p.getPort() - pin := uint8(p) % 16 - pos := pin * 2 - - if config.Mode == PinInputFloating { - port.MODER.Set((uint32(port.MODER.Get())&^(0x3<= 8 { - port.AFRH.Set(uint32(port.AFRH.Get())&^(0xF<