github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_esp32c3_spi.go (about) 1 //go:build esp32c3 2 3 package machine 4 5 // On the C3 variant, SPI2 is a general purpose SPI controller. SPI0 and SPI1 6 // are used internally to access the ESP32-C3’s attached flash memory. Due to 7 // different registers between SPI2 and the other SPI ports, this driver 8 // currently supports only the the general purpose FSPI SPI2 controller. 9 // https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/spi_master.html 10 11 import ( 12 "device/esp" 13 "errors" 14 "runtime/volatile" 15 "unsafe" 16 ) 17 18 const ( 19 SPI_MODE0 = uint8(0) 20 SPI_MODE1 = uint8(1) 21 SPI_MODE2 = uint8(2) 22 SPI_MODE3 = uint8(3) 23 24 FSPICLK_IN_IDX = uint32(63) 25 FSPICLK_OUT_IDX = uint32(63) 26 FSPIQ_IN_IDX = uint32(64) 27 FSPIQ_OUT_IDX = uint32(64) 28 FSPID_IN_IDX = uint32(65) 29 FSPID_OUT_IDX = uint32(65) 30 FSPIHD_IN_IDX = uint32(66) 31 FSPIHD_OUT_IDX = uint32(66) 32 FSPIWP_IN_IDX = uint32(67) 33 FSPIWP_OUT_IDX = uint32(67) 34 FSPICS0_IN_IDX = uint32(68) 35 FSPICS0_OUT_IDX = uint32(68) 36 FSPICS1_OUT_IDX = uint32(69) 37 FSPICS2_OUT_IDX = uint32(70) 38 FSPICS3_OUT_IDX = uint32(71) 39 FSPICS4_OUT_IDX = uint32(72) 40 FSPICS5_OUT_IDX = uint32(73) 41 ) 42 43 var ( 44 ErrInvalidSPIBus = errors.New("machine: SPI bus is invalid") 45 ErrInvalidSPIMode = errors.New("machine: SPI mode is invalid") 46 ) 47 48 // Serial Peripheral Interface on the ESP32-C3. 49 type SPI struct { 50 Bus *esp.SPI2_Type 51 } 52 53 var ( 54 // SPI0 and SPI1 are reserved for use by the caching system etc. 55 SPI2 = SPI{esp.SPI2} 56 ) 57 58 // SPIConfig is used to store config info for SPI. 59 type SPIConfig struct { 60 Frequency uint32 61 SCK Pin // Serial Clock 62 SDO Pin // Serial Data Out (MOSI) 63 SDI Pin // Serial Data In (MISO) 64 CS Pin // Chip Select (optional) 65 LSBFirst bool // MSB is default 66 Mode uint8 // SPI_MODE0 is default 67 } 68 69 // Compute the SPI bus frequency from the CPU frequency. 70 func freqToClockDiv(hz uint32) uint32 { 71 fcpu := CPUFrequency() 72 if hz >= fcpu { // maximum frequency 73 return 1 << 31 74 } 75 if hz < (fcpu / (16 * 64)) { // minimum frequency 76 return 15<<18 | 63<<12 | 31<<6 | 63 // pre=15, n=63 77 } 78 79 // iterate looking for an exact match 80 // or iterate all 16 prescaler options 81 // looking for the smallest error 82 var bestPre, bestN, bestErr uint32 83 bestN = 1 84 bestErr = 0xffffffff 85 q := uint32(float32(pplClockFreq)/float32(hz) + float32(0.5)) 86 for p := uint32(0); p < 16; p++ { 87 n := q/(p+1) - 1 88 if n < 1 { // prescaler became too large, stop enum 89 break 90 } 91 if n > 63 { // prescaler too small, skip to next 92 continue 93 } 94 95 freq := fcpu / ((p + 1) * (n + 1)) 96 if freq == hz { // exact match 97 return p<<18 | n<<12 | (n/2)<<6 | n 98 } 99 100 var err uint32 101 if freq < hz { 102 err = hz - freq 103 } else { 104 err = freq - hz 105 } 106 if err < bestErr { 107 bestErr = err 108 bestPre = p 109 bestN = n 110 } 111 } 112 113 return bestPre<<18 | bestN<<12 | (bestN/2)<<6 | bestN 114 } 115 116 // Configure and make the SPI peripheral ready to use. 117 func (spi SPI) Configure(config SPIConfig) error { 118 // right now this is only setup to work for the esp32c3 spi2 bus 119 if spi.Bus != esp.SPI2 { 120 return ErrInvalidSPIBus 121 } 122 123 // periph module reset 124 esp.SYSTEM.SetPERIP_RST_EN0_SPI2_RST(1) 125 esp.SYSTEM.SetPERIP_RST_EN0_SPI2_RST(0) 126 127 // periph module enable 128 esp.SYSTEM.SetPERIP_CLK_EN0_SPI2_CLK_EN(1) 129 esp.SYSTEM.SetPERIP_RST_EN0_SPI2_RST(0) 130 131 // init the spi2 bus 132 spi.Bus.SLAVE.Set(0) 133 spi.Bus.MISC.Set(0) 134 spi.Bus.USER.Set(0) 135 spi.Bus.USER1.Set(0) 136 spi.Bus.CTRL.Set(0) 137 spi.Bus.CLK_GATE.Set(0) 138 spi.Bus.DMA_CONF.Set(0) 139 spi.Bus.SetDMA_CONF_RX_AFIFO_RST(1) 140 spi.Bus.SetDMA_CONF_BUF_AFIFO_RST(1) 141 spi.Bus.CLOCK.Set(0) 142 143 // clear data buf 144 spi.Bus.SetW0(0) 145 spi.Bus.SetW1(0) 146 spi.Bus.SetW2(0) 147 spi.Bus.SetW3(0) 148 spi.Bus.SetW4(0) 149 spi.Bus.SetW5(0) 150 spi.Bus.SetW6(0) 151 spi.Bus.SetW7(0) 152 spi.Bus.SetW8(0) 153 spi.Bus.SetW9(0) 154 spi.Bus.SetW10(0) 155 spi.Bus.SetW11(0) 156 spi.Bus.SetW12(0) 157 spi.Bus.SetW13(0) 158 spi.Bus.SetW14(0) 159 spi.Bus.SetW15(0) 160 161 // start the spi2 bus 162 spi.Bus.SetCLK_GATE_CLK_EN(1) 163 spi.Bus.SetCLK_GATE_MST_CLK_SEL(1) 164 spi.Bus.SetCLK_GATE_MST_CLK_ACTIVE(1) 165 spi.Bus.SetDMA_CONF_SLV_TX_SEG_TRANS_CLR_EN(1) 166 spi.Bus.SetDMA_CONF_SLV_RX_SEG_TRANS_CLR_EN(1) 167 spi.Bus.SetDMA_CONF_DMA_SLV_SEG_TRANS_EN(0) 168 spi.Bus.SetUSER_USR_MOSI(1) 169 spi.Bus.SetUSER_USR_MISO(1) 170 spi.Bus.SetUSER_DOUTDIN(1) 171 172 // set spi2 data mode 173 switch config.Mode { 174 case SPI_MODE0: 175 spi.Bus.SetMISC_CK_IDLE_EDGE(0) 176 spi.Bus.SetUSER_CK_OUT_EDGE(0) 177 case SPI_MODE1: 178 spi.Bus.SetMISC_CK_IDLE_EDGE(0) 179 spi.Bus.SetUSER_CK_OUT_EDGE(1) 180 case SPI_MODE2: 181 spi.Bus.SetMISC_CK_IDLE_EDGE(1) 182 spi.Bus.SetUSER_CK_OUT_EDGE(1) 183 case SPI_MODE3: 184 spi.Bus.SetMISC_CK_IDLE_EDGE(1) 185 spi.Bus.SetUSER_CK_OUT_EDGE(0) 186 default: 187 return ErrInvalidSPIMode 188 } 189 190 // set spi2 bit order 191 if config.LSBFirst { 192 spi.Bus.SetCTRL_WR_BIT_ORDER(1) // LSB first 193 spi.Bus.SetCTRL_RD_BIT_ORDER(1) 194 } else { 195 spi.Bus.SetCTRL_WR_BIT_ORDER(0) // MSB first 196 spi.Bus.SetCTRL_RD_BIT_ORDER(0) 197 } 198 199 // configure SPI bus clock 200 spi.Bus.CLOCK.Set(freqToClockDiv(config.Frequency)) 201 202 // configure esp32c3 gpio pin matrix 203 config.SDI.Configure(PinConfig{Mode: PinInput}) 204 inFunc(FSPIQ_IN_IDX).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.SDI)) 205 config.SDO.Configure(PinConfig{Mode: PinOutput}) 206 config.SDO.outFunc().Set(FSPID_OUT_IDX) 207 config.SCK.Configure(PinConfig{Mode: PinOutput}) 208 config.SCK.outFunc().Set(FSPICLK_OUT_IDX) 209 if config.CS != NoPin { 210 config.CS.Configure(PinConfig{Mode: PinOutput}) 211 config.CS.outFunc().Set(FSPICS0_OUT_IDX) 212 } 213 214 return nil 215 } 216 217 // Transfer writes/reads a single byte using the SPI interface. If you need to 218 // transfer larger amounts of data, Tx will be faster. 219 func (spi SPI) Transfer(w byte) (byte, error) { 220 spi.Bus.SetMS_DLEN_MS_DATA_BITLEN(7) 221 222 spi.Bus.SetW0(uint32(w)) 223 224 // Send/receive byte. 225 spi.Bus.SetCMD_UPDATE(1) 226 for spi.Bus.GetCMD_UPDATE() != 0 { 227 } 228 229 spi.Bus.SetCMD_USR(1) 230 for spi.Bus.GetCMD_USR() != 0 { 231 } 232 233 // The received byte is stored in W0. 234 return byte(spi.Bus.GetW0()), nil 235 } 236 237 // Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read 238 // interface, there must always be the same number of bytes written as bytes read. 239 // This is accomplished by sending zero bits if r is bigger than w or discarding 240 // the incoming data if w is bigger than r. 241 func (spi SPI) Tx(w, r []byte) error { 242 toTransfer := len(w) 243 if len(r) > toTransfer { 244 toTransfer = len(r) 245 } 246 247 for toTransfer > 0 { 248 // Chunk 64 bytes at a time. 249 chunkSize := toTransfer 250 if chunkSize > 64 { 251 chunkSize = 64 252 } 253 254 // Fill tx buffer. 255 transferWords := (*[16]volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(&spi.Bus.W0)))) 256 if len(w) >= 64 { 257 // We can fill the entire 64-byte transfer buffer with data. 258 // This loop is slightly faster than the loop below. 259 for i := 0; i < 16; i++ { 260 word := uint32(w[i*4]) | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24 261 transferWords[i].Set(word) 262 } 263 } else { 264 // We can't fill the entire transfer buffer, so we need to be a bit 265 // more careful. 266 // Note that parts of the transfer buffer that aren't used still 267 // need to be set to zero, otherwise we might be transferring 268 // garbage from a previous transmission if w is smaller than r. 269 for i := 0; i < 16; i++ { 270 var word uint32 271 if i*4+3 < len(w) { 272 word |= uint32(w[i*4+3]) << 24 273 } 274 if i*4+2 < len(w) { 275 word |= uint32(w[i*4+2]) << 16 276 } 277 if i*4+1 < len(w) { 278 word |= uint32(w[i*4+1]) << 8 279 } 280 if i*4+0 < len(w) { 281 word |= uint32(w[i*4+0]) << 0 282 } 283 transferWords[i].Set(word) 284 } 285 } 286 287 // Do the transfer. 288 spi.Bus.SetMS_DLEN_MS_DATA_BITLEN(uint32(chunkSize)*8 - 1) 289 290 spi.Bus.SetCMD_UPDATE(1) 291 for spi.Bus.GetCMD_UPDATE() != 0 { 292 } 293 294 spi.Bus.SetCMD_USR(1) 295 for spi.Bus.GetCMD_USR() != 0 { 296 } 297 298 // Read rx buffer. 299 rxSize := 64 300 if rxSize > len(r) { 301 rxSize = len(r) 302 } 303 for i := 0; i < rxSize; i++ { 304 r[i] = byte(transferWords[i/4].Get() >> ((i % 4) * 8)) 305 } 306 307 // Cut off some part of the output buffer so the next iteration we will 308 // only send the remaining bytes. 309 if len(w) < chunkSize { 310 w = nil 311 } else { 312 w = w[chunkSize:] 313 } 314 if len(r) < chunkSize { 315 r = nil 316 } else { 317 r = r[chunkSize:] 318 } 319 toTransfer -= chunkSize 320 } 321 322 return nil 323 }