stm32 add SPI for stm32f4
Этот коммит содержится в:
родитель
ac73430502
коммит
b4dddfe439
6 изменённых файлов: 217 добавлений и 93 удалений
|
@ -43,3 +43,15 @@ const (
|
|||
GPIOPUPDRPullUp = 1
|
||||
GPIOPUPDRPullDown = 2
|
||||
)
|
||||
|
||||
// SPI prescaler values fPCLK / X
|
||||
const (
|
||||
SPI_PCLK_2 = 0
|
||||
SPI_PCLK_4 = 1
|
||||
SPI_PCLK_8 = 2
|
||||
SPI_PCLK_16 = 3
|
||||
SPI_PCLK_32 = 4
|
||||
SPI_PCLK_64 = 5
|
||||
SPI_PCLK_128 = 6
|
||||
SPI_PCLK_256 = 7
|
||||
)
|
||||
|
|
|
@ -129,3 +129,30 @@ var (
|
|||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = PA5
|
||||
SPI1_MISO_PIN = PA6
|
||||
SPI1_MOSI_PIN = PA7
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_MISO_PIN = SPI1_MISO_PIN
|
||||
SPI0_MOSI_PIN = SPI1_MOSI_PIN
|
||||
)
|
||||
|
||||
// MEMs accelerometer
|
||||
const (
|
||||
MEMS_ACCEL_CS = PE3
|
||||
MEMS_ACCEL_INT1 = PE0
|
||||
MEMS_ACCEL_INT2 = PE1
|
||||
)
|
||||
|
||||
// Since the first interface is named SPI1, both SPI0 and SPI1 refer to SPI1.
|
||||
// TODO: implement SPI2 and SPI3.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: stm32.AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
|
99
src/machine/machine_stm32_spi.go
Обычный файл
99
src/machine/machine_stm32_spi.go
Обычный файл
|
@ -0,0 +1,99 @@
|
|||
// +build stm32
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for SPI on the stm32 family
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
LSBFirst bool
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
// Configure is intended to setup the STM32 SPI1 interface.
|
||||
// Features still TODO:
|
||||
// - support SPI2 and SPI3
|
||||
// - allow setting data size to 16 bits?
|
||||
// - allow setting direction in HW for additional optimization?
|
||||
// - hardware SS pin?
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
// enable clock for SPI
|
||||
enableAltFuncClock(unsafe.Pointer(spi.Bus))
|
||||
|
||||
// Get SPI baud rate based on the bus speed it's attached to
|
||||
var conf uint32 = spi.getBaudRate(config)
|
||||
|
||||
// set bit transfer order
|
||||
if config.LSBFirst {
|
||||
conf |= stm32.SPI_CR1_LSBFIRST
|
||||
}
|
||||
|
||||
// set polarity and phase on the SPI interface
|
||||
switch config.Mode {
|
||||
case Mode0:
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case Mode1:
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case Mode2:
|
||||
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case Mode3:
|
||||
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
default: // to mode 0
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
}
|
||||
|
||||
// set to SPI master
|
||||
conf |= stm32.SPI_CR1_MSTR
|
||||
|
||||
// disable MCU acting as SPI slave
|
||||
conf |= stm32.SPI_CR1_SSM | stm32.SPI_CR1_SSI
|
||||
|
||||
// now set the configuration
|
||||
spi.Bus.CR1.Set(conf)
|
||||
|
||||
// init pins
|
||||
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.MOSI = SPI0_MOSI_PIN
|
||||
config.MISO = SPI0_MISO_PIN
|
||||
}
|
||||
spi.configurePins(config)
|
||||
|
||||
// enable SPI interface
|
||||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
// Write data to be transmitted to the SPI data register
|
||||
spi.Bus.DR.Set(uint32(w))
|
||||
|
||||
// Wait until transmit complete
|
||||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
|
||||
}
|
||||
|
||||
// Wait until receive complete
|
||||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
|
||||
}
|
||||
|
||||
// Wait until SPI is not busy
|
||||
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
|
||||
}
|
||||
|
||||
// Return received data from SPI data register
|
||||
return byte(spi.Bus.DR.Get()), nil
|
||||
}
|
|
@ -148,7 +148,8 @@ func (uart UART) getBaudRateDivisor(br uint32) uint32 {
|
|||
return divider
|
||||
}
|
||||
|
||||
// SPI on the STM32.
|
||||
//---------- SPI related types and code
|
||||
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
}
|
||||
|
@ -161,26 +162,8 @@ var (
|
|||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
LSBFirst bool
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
// Configure is intended to setup the STM32 SPI1 interface.
|
||||
// Features still TODO:
|
||||
// - support SPI2 and SPI3
|
||||
// - allow setting data size to 16 bits?
|
||||
// - allow setting direction in HW for additional optimization?
|
||||
// - hardware SS pin?
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
// enable clock for SPI
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
|
||||
// set frequency dependent on PCLK2 prescaler (div 1)
|
||||
|
@ -203,82 +186,18 @@ func (spi SPI) Configure(config SPIConfig) {
|
|||
default:
|
||||
conf |= stm32.SPI_BaudRatePrescaler_256
|
||||
}
|
||||
|
||||
// set bit transfer order
|
||||
if config.LSBFirst {
|
||||
conf |= stm32.SPI_FirstBit_LSB
|
||||
}
|
||||
|
||||
// set mode
|
||||
switch config.Mode {
|
||||
case 0:
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case 1:
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case 2:
|
||||
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
case 3:
|
||||
conf |= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf |= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
default: // to mode 0
|
||||
conf &^= (1 << stm32.SPI_CR1_CPOL_Pos)
|
||||
conf &^= (1 << stm32.SPI_CR1_CPHA_Pos)
|
||||
}
|
||||
|
||||
// set to SPI master
|
||||
conf |= stm32.SPI_Mode_Master
|
||||
|
||||
// now set the configuration
|
||||
spi.Bus.CR1.Set(conf)
|
||||
|
||||
// init pins
|
||||
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
||||
|
||||
// enable SPI interface
|
||||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
return conf
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
// Write data to be transmitted to the SPI data register
|
||||
spi.Bus.DR.Set(uint32(w))
|
||||
|
||||
// Wait until transmit complete
|
||||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
|
||||
}
|
||||
|
||||
// Wait until receive complete
|
||||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
|
||||
}
|
||||
|
||||
// Wait until SPI is not busy
|
||||
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
|
||||
}
|
||||
|
||||
// Return received data from SPI data register
|
||||
return byte(spi.Bus.DR.Get()), nil
|
||||
// Configure SPI pins for input output and clock
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||
config.MOSI.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||
config.MISO.Configure(PinConfig{Mode: PinInputModeFloating})
|
||||
}
|
||||
|
||||
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
if sck == 0 {
|
||||
sck = SPI0_SCK_PIN
|
||||
}
|
||||
if mosi == 0 {
|
||||
mosi = SPI0_MOSI_PIN
|
||||
}
|
||||
if miso == 0 {
|
||||
miso = SPI0_MISO_PIN
|
||||
}
|
||||
//---------- I2C related types and code
|
||||
|
||||
sck.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||
mosi.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||
miso.Configure(PinConfig{Mode: PinInputModeFloating})
|
||||
}
|
||||
|
||||
// I2C on the STM32F103xx.
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
}
|
||||
|
|
|
@ -108,3 +108,62 @@ func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
|||
}
|
||||
return clock / baudRate
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
// SPI on the STM32Fxxx using MODER / alternate function pins
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
AltFuncSelector stm32.AltFunc
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
|
||||
localFrequency := config.Frequency
|
||||
if spi.Bus != stm32.SPI1 {
|
||||
// Assume it's SPI2 or SPI3 on APB1 at 1/2 the clock frequency of APB2, so
|
||||
// we want to pretend to request 2x the baudrate asked for
|
||||
localFrequency = localFrequency * 2
|
||||
}
|
||||
|
||||
// set frequency dependent on PCLK prescaler. Since these are rather weird
|
||||
// speeds due to the CPU freqency, pick a range up to that frquency for
|
||||
// clients to use more human-understandable numbers, e.g. nearest 100KHz
|
||||
|
||||
// These are based on APB2 clock frquency (84MHz on the discovery board)
|
||||
// TODO: also include the MCU/APB clock setting in the equation
|
||||
switch true {
|
||||
case localFrequency < 328125:
|
||||
conf = stm32.SPI_PCLK_256
|
||||
case localFrequency < 656250:
|
||||
conf = stm32.SPI_PCLK_128
|
||||
case localFrequency < 1312500:
|
||||
conf = stm32.SPI_PCLK_64
|
||||
case localFrequency < 2625000:
|
||||
conf = stm32.SPI_PCLK_32
|
||||
case localFrequency < 5250000:
|
||||
conf = stm32.SPI_PCLK_16
|
||||
case localFrequency < 10500000:
|
||||
conf = stm32.SPI_PCLK_8
|
||||
// NOTE: many SPI components won't operate reliably (or at all) above 10MHz
|
||||
// Check the datasheet of the part
|
||||
case localFrequency < 21000000:
|
||||
conf = stm32.SPI_PCLK_4
|
||||
case localFrequency < 42000000:
|
||||
conf = stm32.SPI_PCLK_2
|
||||
default:
|
||||
// None of the specific baudrates were selected; choose the lowest speed
|
||||
conf = stm32.SPI_PCLK_256
|
||||
}
|
||||
|
||||
return conf << stm32.SPI_CR1_BR_Pos
|
||||
}
|
||||
|
||||
// Configure SPI pins for input output and clock
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
||||
config.MOSI.ConfigureAltFunc(PinConfig{Mode: PinModeSPIMOSI}, spi.AltFuncSelector)
|
||||
config.MISO.ConfigureAltFunc(PinConfig{Mode: PinModeSPIMISO}, spi.AltFuncSelector)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
// +build !baremetal sam stm32,!stm32f407 fe310
|
||||
// +build !baremetal sam stm32 fe310
|
||||
|
||||
package machine
|
||||
|
||||
import "errors"
|
||||
|
||||
// SPI phase and polarity configs CPOL and CPHA
|
||||
const (
|
||||
Mode0 = 0
|
||||
Mode1 = 1
|
||||
Mode2 = 2
|
||||
Mode3 = 3
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче