@@ -4,7 +4,9 @@ package machine
4
4
5
5
import (
6
6
"device/esp"
7
+ "errors"
7
8
"runtime/volatile"
9
+ "unsafe"
8
10
)
9
11
10
12
const peripheralClock = 80000000 // 80MHz
@@ -15,6 +17,10 @@ func CPUFrequency() uint32 {
15
17
return 160e6 // 160MHz
16
18
}
17
19
20
+ var (
21
+ ErrInvalidSPIBus = errors .New ("machine: invalid SPI bus" )
22
+ )
23
+
18
24
type PinMode uint8
19
25
20
26
const (
@@ -26,6 +32,24 @@ const (
26
32
27
33
// Configure this pin with the given configuration.
28
34
func (p Pin ) Configure (config PinConfig ) {
35
+ // Output function 256 is a special value reserved for use as a regular GPIO
36
+ // pin. Peripherals (SPI etc) can set a custom output function by calling
37
+ // lowercase configure() instead with a signal name.
38
+ p .configure (config , 256 )
39
+ }
40
+
41
+ // configure is the same as Configure, but allows for setting a specific input
42
+ // or output signal.
43
+ // Signals are always routed through the GPIO matrix for simplicity. Output
44
+ // signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
45
+ // to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
46
+ // which sets the pin to use for a particular input signal.
47
+ func (p Pin ) configure (config PinConfig , signal uint32 ) {
48
+ if p == NoPin {
49
+ // This simplifies pin configuration in peripherals such as SPI.
50
+ return
51
+ }
52
+
29
53
var muxConfig uint32 // The mux configuration.
30
54
31
55
// Configure this pin as a GPIO pin.
@@ -56,16 +80,40 @@ func (p Pin) Configure(config PinConfig) {
56
80
} else {
57
81
esp .GPIO .ENABLE1_W1TS .Set (1 << (p - 32 ))
58
82
}
83
+ // Set the signal to read the output value from. It can be a peripheral
84
+ // output signal, or the special value 256 which indicates regular GPIO
85
+ // usage.
86
+ p .outFunc ().Set (signal )
59
87
case PinInput , PinInputPullup , PinInputPulldown :
60
88
// Clear the 'output enable' bit.
61
89
if p < 32 {
62
90
esp .GPIO .ENABLE_W1TC .Set (1 << p )
63
91
} else {
64
92
esp .GPIO .ENABLE1_W1TC .Set (1 << (p - 32 ))
65
93
}
94
+ if signal != 256 {
95
+ // Signal is a peripheral function (not a simple GPIO). Connect this
96
+ // signal to the pin.
97
+ // Note that outFunc and inFunc work in the opposite direction.
98
+ // outFunc configures a pin to use a given output signal, while
99
+ // inFunc specifies a pin to use to read the signal from.
100
+ inFunc (signal ).Set (esp .GPIO_FUNC_IN_SEL_CFG_SEL | uint32 (p )<< esp .GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos )
101
+ }
66
102
}
67
103
}
68
104
105
+ // outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
106
+ // output function selection.
107
+ func (p Pin ) outFunc () * volatile.Register32 {
108
+ return (* volatile .Register32 )(unsafe .Pointer ((uintptr (unsafe .Pointer (& esp .GPIO .FUNC0_OUT_SEL_CFG )) + uintptr (p )* 4 )))
109
+ }
110
+
111
+ // inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
112
+ // function selection.
113
+ func inFunc (signal uint32 ) * volatile.Register32 {
114
+ return (* volatile .Register32 )(unsafe .Pointer ((uintptr (unsafe .Pointer (& esp .GPIO .FUNC0_IN_SEL_CFG )) + uintptr (signal )* 4 )))
115
+ }
116
+
69
117
// Set the pin to high or low.
70
118
// Warning: only use this on an output pin!
71
119
func (p Pin ) Set (value bool ) {
@@ -232,3 +280,224 @@ func (uart UART) WriteByte(b byte) error {
232
280
uart .Bus .TX_FIFO .Set (b )
233
281
return nil
234
282
}
283
+
284
+ // Serial Peripheral Interface on the ESP32.
285
+ type SPI struct {
286
+ Bus * esp.SPI_Type
287
+ }
288
+
289
+ var (
290
+ // SPI0 and SPI1 are reserved for use by the caching system etc.
291
+ SPI2 = SPI {esp .SPI2 }
292
+ SPI3 = SPI {esp .SPI3 }
293
+ )
294
+
295
+ // SPIConfig configures a SPI peripheral on the ESP32. Make sure to set at least
296
+ // SCK, SDO and SDI (possibly to NoPin if not in use). The default for LSBFirst
297
+ // (false) and Mode (0) are good for most applications. The frequency defaults
298
+ // to 1MHz if not set but can be configured up to 40MHz. Possible values are
299
+ // 40MHz and integer divisions from 40MHz such as 20MHz, 13.3MHz, 10MHz, 8MHz,
300
+ // etc.
301
+ type SPIConfig struct {
302
+ Frequency uint32
303
+ SCK Pin
304
+ SDO Pin
305
+ SDI Pin
306
+ LSBFirst bool
307
+ Mode uint8
308
+ }
309
+
310
+ // Configure and make the SPI peripheral ready to use.
311
+ func (spi SPI ) Configure (config SPIConfig ) error {
312
+ if config .Frequency == 0 {
313
+ config .Frequency = 1e6 // default to 1MHz
314
+ }
315
+
316
+ // Configure the SPI clock. This assumes a peripheral clock of 80MHz.
317
+ var clockReg uint32
318
+ if config .Frequency >= 40e6 {
319
+ // Don't use a prescaler, but directly connect to the APB clock. This
320
+ // results in a SPI clock frequency of 40MHz.
321
+ clockReg |= esp .SPI_CLOCK_CLK_EQU_SYSCLK
322
+ } else {
323
+ // Use a prescaler for frequencies below 40MHz. They will get rounded
324
+ // down to the next possible frequency (20MHz, 13.3MHz, 10MHz, 8MHz,
325
+ // 6.7MHz, 5.7MHz, 5MHz, etc).
326
+ // This code is much simpler than how ESP-IDF configures the frequency,
327
+ // but should be just as accurate. The only exception is for frequencies
328
+ // below 4883Hz, which will need special support.
329
+ if config .Frequency < 4883 {
330
+ // The current lower limit is 4883Hz.
331
+ // The hardware supports lower frequencies by setting the h and n
332
+ // variables, but that's not yet implemented.
333
+ config .Frequency = 4883
334
+ }
335
+ // The prescaler value is 40e6 / config.Frequency, but rounded up so
336
+ // that the actual frequency is never higher than the frequency
337
+ // requested in config.Frequency.
338
+ var (
339
+ pre uint32 = (40e6 + config .Frequency - 1 ) / config .Frequency
340
+ n uint32 = 2 // this value seems to equal the number of ticks per SPI clock tick
341
+ h uint32 = 1 // must be half of n according to the formula in the reference manual
342
+ l uint32 = n // must equal n according to the reference manual
343
+ )
344
+ clockReg |= (pre - 1 ) << esp .SPI_CLOCK_CLKDIV_PRE_Pos
345
+ clockReg |= (n - 1 ) << esp .SPI_CLOCK_CLKCNT_N_Pos
346
+ clockReg |= (h - 1 ) << esp .SPI_CLOCK_CLKCNT_H_Pos
347
+ clockReg |= (l - 1 ) << esp .SPI_CLOCK_CLKCNT_L_Pos
348
+ }
349
+ spi .Bus .CLOCK .Set (clockReg )
350
+
351
+ // SPI_CTRL_REG controls bit order.
352
+ var ctrlReg uint32
353
+ if config .LSBFirst {
354
+ ctrlReg |= esp .SPI_CTRL_WR_BIT_ORDER
355
+ ctrlReg |= esp .SPI_CTRL_RD_BIT_ORDER
356
+ }
357
+ spi .Bus .CTRL .Set (ctrlReg )
358
+
359
+ // SPI_CTRL2_REG, SPI_USER_REG and SPI_PIN_REG control SPI clock polarity
360
+ // (mode), among others.
361
+ var ctrl2Reg , userReg , pinReg uint32
362
+ // For mode configuration, see table 29 in the reference manual (page 128).
363
+ var delayMode uint32
364
+ switch config .Mode {
365
+ case 0 :
366
+ delayMode = 2
367
+ case 1 :
368
+ delayMode = 1
369
+ userReg |= esp .SPI_USER_CK_OUT_EDGE
370
+ case 2 :
371
+ delayMode = 1
372
+ userReg |= esp .SPI_USER_CK_OUT_EDGE
373
+ pinReg |= esp .SPI_PIN_CK_IDLE_EDGE
374
+ case 3 :
375
+ delayMode = 2
376
+ pinReg |= esp .SPI_PIN_CK_IDLE_EDGE
377
+ }
378
+ // Extra configuration necessary for correct data input at high frequencies.
379
+ // This is only necessary when MISO goes through the GPIO matrix (which it
380
+ // currently does).
381
+ if config .Frequency >= 40e6 {
382
+ // Delay mode must be set to 0 and SPI_USR_DUMMY_CYCLELEN should be set
383
+ // to 0 (the default).
384
+ userReg |= esp .SPI_USER_USR_DUMMY
385
+ } else if config .Frequency >= 20e6 {
386
+ // Nothing to do here, delay mode should be set to 0 according to the
387
+ // datasheet.
388
+ } else {
389
+ // Follow the delay mode as given in table 29 on page 128 of the
390
+ // reference manual.
391
+ // Note that this is only specified for SPI frequency of 10MHz and
392
+ // below (≤Fapb/8), so 13.3MHz appears to be left unspecified.
393
+ ctrl2Reg |= delayMode << esp .SPI_CTRL2_MOSI_DELAY_MODE_Pos
394
+ }
395
+ // Enable full-duplex communication.
396
+ userReg |= esp .SPI_USER_DOUTDIN
397
+ userReg |= esp .SPI_USER_USR_MOSI
398
+ // Write values to registers.
399
+ spi .Bus .CTRL2 .Set (ctrl2Reg )
400
+ spi .Bus .USER .Set (userReg )
401
+ spi .Bus .PIN .Set (pinReg )
402
+
403
+ // Configure pins.
404
+ // TODO: use direct output if possible, if the configured pins match the
405
+ // possible direct configurations (e.g. for SPI2, when SCK is pin 14 etc).
406
+ if spi .Bus == esp .SPI2 {
407
+ config .SCK .configure (PinConfig {Mode : PinOutput }, 8 ) // HSPICLK
408
+ config .SDI .configure (PinConfig {Mode : PinInput }, 9 ) // HSPIQ
409
+ config .SDO .configure (PinConfig {Mode : PinOutput }, 10 ) // HSPID
410
+ } else if spi .Bus == esp .SPI3 {
411
+ config .SCK .configure (PinConfig {Mode : PinOutput }, 63 ) // VSPICLK
412
+ config .SDI .configure (PinConfig {Mode : PinInput }, 64 ) // VSPIQ
413
+ config .SDO .configure (PinConfig {Mode : PinOutput }, 65 ) // VSPID
414
+ } else {
415
+ // Don't know how to configure this bus.
416
+ return ErrInvalidSPIBus
417
+ }
418
+
419
+ return nil
420
+ }
421
+
422
+ // Transfer writes/reads a single byte using the SPI interface. If you need to
423
+ // transfer larger amounts of data, Tx will be faster.
424
+ func (spi SPI ) Transfer (w byte ) (byte , error ) {
425
+ spi .Bus .MISO_DLEN .Set (7 << esp .SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos )
426
+ spi .Bus .MOSI_DLEN .Set (7 << esp .SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos )
427
+
428
+ spi .Bus .W0 .Set (uint32 (w ))
429
+
430
+ // Send/receive byte.
431
+ spi .Bus .CMD .Set (esp .SPI_CMD_USR )
432
+ for spi .Bus .CMD .Get () != 0 {
433
+ }
434
+
435
+ // The received byte is stored in W0.
436
+ return byte (spi .Bus .W0 .Get ()), nil
437
+ }
438
+
439
+ // Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
440
+ // interface, there must always be the same number of bytes written as bytes read.
441
+ // This is accomplished by sending zero bits if r is bigger than w or discarding
442
+ // the incoming data if w is bigger than r.
443
+ //
444
+ func (spi SPI ) Tx (w , r []byte ) error {
445
+ toTransfer := len (w )
446
+ if len (r ) > toTransfer {
447
+ toTransfer = len (r )
448
+ }
449
+
450
+ for toTransfer != 0 {
451
+ // Do only 64 bytes at a time.
452
+ chunkSize := toTransfer
453
+ if chunkSize > 64 {
454
+ chunkSize = 64
455
+ }
456
+
457
+ // Fill tx buffer.
458
+ transferWords := (* [16 ]volatile.Register32 )(unsafe .Pointer (uintptr (unsafe .Pointer (& spi .Bus .W0 ))))
459
+ var outBuf [16 ]uint32
460
+ txSize := 64
461
+ if txSize > len (w ) {
462
+ txSize = len (w )
463
+ }
464
+ for i := 0 ; i < txSize ; i ++ {
465
+ outBuf [i / 4 ] = outBuf [i / 4 ] | uint32 (w [i ])<< ((i % 4 )* 8 )
466
+ }
467
+ for i , word := range outBuf {
468
+ transferWords [i ].Set (word )
469
+ }
470
+
471
+ // Do the transfer.
472
+ spi .Bus .MISO_DLEN .Set ((uint32 (chunkSize )* 8 - 1 ) << esp .SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos )
473
+ spi .Bus .MOSI_DLEN .Set ((uint32 (chunkSize )* 8 - 1 ) << esp .SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos )
474
+ spi .Bus .CMD .Set (esp .SPI_CMD_USR )
475
+ for spi .Bus .CMD .Get () != 0 {
476
+ }
477
+
478
+ // Read rx buffer.
479
+ rxSize := 64
480
+ if rxSize > len (r ) {
481
+ rxSize = len (r )
482
+ }
483
+ for i := 0 ; i < rxSize ; i ++ {
484
+ r [i ] = byte (transferWords [i / 4 ].Get () >> ((i % 4 ) * 8 ))
485
+ }
486
+
487
+ // Cut off some part of the output buffer so the next iteration we will
488
+ // only send the remaining bytes.
489
+ if len (w ) < chunkSize {
490
+ w = nil
491
+ } else {
492
+ w = w [chunkSize :]
493
+ }
494
+ if len (r ) < chunkSize {
495
+ r = nil
496
+ } else {
497
+ r = r [chunkSize :]
498
+ }
499
+ toTransfer -= chunkSize
500
+ }
501
+
502
+ return nil
503
+ }
0 commit comments