mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 22:13:39 +00:00
b14ee51ff6
This refactors and corrects the SPI implentation for the ESP32C3 and ESP32S3 processors. There was a lot of duplicated code, as well as some errors such as incorrectly calculating speed on the esp32c3 implementation. This will also be helpful when adding additional processors that use very similar peripheral registers. Signed-off-by: deadprogram <ron@hybridgroup.com>
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
//go:build esp32s3 || esp32c3
|
|
|
|
package machine
|
|
|
|
import (
|
|
"runtime/volatile"
|
|
)
|
|
|
|
// SPIConfig is used to store config info for SPI.
|
|
type SPIConfig struct {
|
|
Frequency uint32
|
|
SCK Pin // Serial Clock
|
|
SDO Pin // Serial Data Out (MOSI)
|
|
SDI Pin // Serial Data In (MISO)
|
|
CS Pin // Chip Select (optional)
|
|
LSBFirst bool // MSB is default
|
|
Mode uint8 // Mode0 is default
|
|
}
|
|
|
|
// freqToClockDiv computes the SPI bus clock divider register value.
|
|
// SPI peripherals on ESP32-C3 and ESP32-S3 are clocked from the APB bus
|
|
// (pplClockFreq, 80 MHz on both chips).
|
|
func freqToClockDiv(hz uint32) uint32 {
|
|
if hz >= pplClockFreq { // maximum frequency
|
|
return 1 << 31
|
|
}
|
|
if hz < (pplClockFreq / (16 * 64)) { // minimum frequency
|
|
return 15<<18 | 63<<12 | 31<<6 | 63 // pre=15, n=63
|
|
}
|
|
|
|
// Iterate all 16 prescaler options looking for an exact match
|
|
// or the smallest error.
|
|
var bestPre, bestN, bestErr uint32
|
|
bestN = 1
|
|
bestErr = 0xffffffff
|
|
q := uint32(float32(pplClockFreq)/float32(hz) + float32(0.5))
|
|
for p := uint32(0); p < 16; p++ {
|
|
n := q/(p+1) - 1
|
|
if n < 1 { // prescaler became too large, stop enum
|
|
break
|
|
}
|
|
if n > 63 { // prescaler too small, skip to next
|
|
continue
|
|
}
|
|
|
|
freq := pplClockFreq / ((p + 1) * (n + 1))
|
|
if freq == hz { // exact match
|
|
return p<<18 | n<<12 | (n/2)<<6 | n
|
|
}
|
|
|
|
var err uint32
|
|
if freq < hz {
|
|
err = hz - freq
|
|
} else {
|
|
err = freq - hz
|
|
}
|
|
if err < bestErr {
|
|
bestErr = err
|
|
bestPre = p
|
|
bestN = n
|
|
}
|
|
}
|
|
|
|
return bestPre<<18 | bestN<<12 | (bestN/2)<<6 | bestN
|
|
}
|
|
|
|
// spiTxFillBuffer writes data from w into the 16-word (64-byte) SPI
|
|
// hardware transfer buffer. Unused words are zeroed so that no stale
|
|
// data from a previous transfer is sent when w is shorter than 64 bytes.
|
|
func spiTxFillBuffer(buf *[16]volatile.Register32, w []byte) {
|
|
if len(w) >= 64 {
|
|
// We can fill the entire 64-byte transfer buffer with data.
|
|
// This loop is slightly faster than the loop below.
|
|
for i := 0; i < 16; i++ {
|
|
word := uint32(w[i*4]) | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24
|
|
buf[i].Set(word)
|
|
}
|
|
} else {
|
|
// We can't fill the entire transfer buffer, so we need to be a bit
|
|
// more careful.
|
|
// Note that parts of the transfer buffer that aren't used still
|
|
// need to be set to zero, otherwise we might be transferring
|
|
// garbage from a previous transmission if w is smaller than r.
|
|
for i := 0; i < 16; i++ {
|
|
var word uint32
|
|
if i*4+3 < len(w) {
|
|
word |= uint32(w[i*4+3]) << 24
|
|
}
|
|
if i*4+2 < len(w) {
|
|
word |= uint32(w[i*4+2]) << 16
|
|
}
|
|
if i*4+1 < len(w) {
|
|
word |= uint32(w[i*4+1]) << 8
|
|
}
|
|
if i*4+0 < len(w) {
|
|
word |= uint32(w[i*4+0]) << 0
|
|
}
|
|
buf[i].Set(word)
|
|
}
|
|
}
|
|
}
|