diff --git a/src/machine/machine_stm32f407.go b/src/machine/machine_stm32f407.go index 4c928376..d59ae668 100644 --- a/src/machine/machine_stm32f407.go +++ b/src/machine/machine_stm32f407.go @@ -1,3 +1,4 @@ +//go:build stm32f407 // +build stm32f407 package machine @@ -6,6 +7,7 @@ package machine import ( "device/stm32" + "math/bits" ) func CPUFrequency() uint32 { @@ -80,57 +82,48 @@ func (spi SPI) config8Bits() { // no-op on this series } -// 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_CR1_BR_Div256 - case localFrequency < 656250: - conf = stm32.SPI_CR1_BR_Div128 - case localFrequency < 1312500: - conf = stm32.SPI_CR1_BR_Div64 - case localFrequency < 2625000: - conf = stm32.SPI_CR1_BR_Div32 - case localFrequency < 5250000: - conf = stm32.SPI_CR1_BR_Div16 - case localFrequency < 10500000: - conf = stm32.SPI_CR1_BR_Div8 - // 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_CR1_BR_Div4 - case localFrequency < 42000000: - conf = stm32.SPI_CR1_BR_Div2 - default: - // None of the specific baudrates were selected; choose the lowest speed - conf = stm32.SPI_CR1_BR_Div256 - } - - 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.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector) config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector) } +func (spi SPI) getBaudRate(config SPIConfig) uint32 { + var clock uint32 + switch spi.Bus { + case stm32.SPI1: + clock = CPUFrequency() / 2 + case stm32.SPI2, stm32.SPI3: + clock = CPUFrequency() / 4 + } + + // limit requested frequency to bus frequency and min frequency (DIV256) + freq := config.Frequency + if min := clock / 256; freq < min { + freq = min + } else if freq > clock { + freq = clock + } + + // calculate the exact clock divisor (freq=clock/div -> div=clock/freq). + // truncation is fine, since it produces a less-than-or-equal divisor, and + // thus a greater-than-or-equal frequency. + // divisors only come in consecutive powers of 2, so we can use log2 (or, + // equivalently, bits.Len - 1) to convert to respective enum value. + div := bits.Len32(clock/freq) - 1 + + // but DIV1 (2^0) is not permitted, as the least divisor is DIV2 (2^1), so + // subtract 1 from the log2 value, keeping a lower bound of 0 + if div < 0 { + div = 0 + } else if div > 0 { + div-- + } + + // finally, shift the enumerated value into position for SPI CR1 + return uint32(div) << stm32.SPI_CR1_BR_Pos +} + // -- I2C ---------------------------------------------------------------------- type I2C struct {