Skip to content

Commit 888d0c0

Browse files
committed
machine/esp32: address review feedback for interrupt and UART code
- Fix SetInterrupt error capture: use a package-level variable instead of a named return captured by the sync.Once closure, avoiding a closure allocation on every call. - Extract GPIO interrupt handler from inline closure to named function (handleGPIOInterrupt), matching the UART handler pattern. - Unexport ESP32-specific UART fields (txrxSignal, rtsctsSignal, parityErrorDetected, dataErrorDetected, dataOverflowDetected). - Change UART.Configure to return error, consistent with ESP32C3/C6. - Clarify why UART0 does not return early when pins are already wired. Signed-off-by: deadprogram <[email protected]>
1 parent fc17c35 commit 888d0c0

1 file changed

Lines changed: 64 additions & 53 deletions

File tree

src/machine/machine_esp32.go

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ const (
318318
// the change parameter is ignored and can be set to any value (such as 0).
319319
// If the pin is already configured with a callback, you must first unset
320320
// this pins interrupt before you can set a new callback.
321-
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
321+
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
322322
if p >= maxPin {
323323
return ErrInvalidInputPin
324324
}
@@ -342,10 +342,10 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
342342
pinCallbacks[p] = callback
343343

344344
onceSetupPinInterrupt.Do(func() {
345-
err = setupPinInterrupt()
345+
setupPinInterruptErr = setupPinInterrupt()
346346
})
347-
if err != nil {
348-
return err
347+
if setupPinInterruptErr != nil {
348+
return setupPinInterruptErr
349349
}
350350

351351
p.pinReg().Set(
@@ -358,34 +358,40 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
358358
var (
359359
pinCallbacks [maxPin]func(Pin)
360360
onceSetupPinInterrupt sync.Once
361+
setupPinInterruptErr error
361362
)
362363

363364
func setupPinInterrupt() error {
364365
esp.DPORT.SetPRO_GPIO_INTERRUPT_MAP_PRO_GPIO_INTERRUPT_PRO_MAP(cpuInterruptFromPin)
365-
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
366-
// Read and immediately clear interrupt status bits.
367-
// Clearing before processing is critical for edge-triggered CPU
368-
// interrupts: any new GPIO events that arrive during callback
369-
// execution will set fresh STATUS bits, generating a new edge
370-
// on the CPU interrupt line so they are not lost.
371-
status := esp.GPIO.STATUS.Get()
372-
status1 := esp.GPIO.STATUS1.Get()
373-
esp.GPIO.STATUS_W1TC.Set(status)
374-
esp.GPIO.STATUS1_W1TC.Set(status1)
375-
376-
// Check status for GPIO0-31
377-
for i, mask := 0, uint32(1); i < 32; i, mask = i+1, mask<<1 {
378-
if (status&mask) != 0 && pinCallbacks[i] != nil {
379-
pinCallbacks[i](Pin(i))
380-
}
366+
return interrupt.New(cpuInterruptFromPin, handleGPIOInterrupt).Enable()
367+
}
368+
369+
// handleGPIOInterrupt is the GPIO pin change interrupt handler. It must be a
370+
// plain function (not a closure) because interrupt.New is a compiler intrinsic
371+
// that does not support closures.
372+
func handleGPIOInterrupt(interrupt.Interrupt) {
373+
// Read and immediately clear interrupt status bits.
374+
// Clearing before processing is critical for edge-triggered CPU
375+
// interrupts: any new GPIO events that arrive during callback
376+
// execution will set fresh STATUS bits, generating a new edge
377+
// on the CPU interrupt line so they are not lost.
378+
status := esp.GPIO.STATUS.Get()
379+
status1 := esp.GPIO.STATUS1.Get()
380+
esp.GPIO.STATUS_W1TC.Set(status)
381+
esp.GPIO.STATUS1_W1TC.Set(status1)
382+
383+
// Check status for GPIO0-31
384+
for i, mask := 0, uint32(1); i < 32; i, mask = i+1, mask<<1 {
385+
if (status&mask) != 0 && pinCallbacks[i] != nil {
386+
pinCallbacks[i](Pin(i))
381387
}
382-
// Check status for GPIO32-39
383-
for i, mask := 32, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
384-
if (status1&mask) != 0 && pinCallbacks[i] != nil {
385-
pinCallbacks[i](Pin(i))
386-
}
388+
}
389+
// Check status for GPIO32-39
390+
for i, mask := 32, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
391+
if (status1&mask) != 0 && pinCallbacks[i] != nil {
392+
pinCallbacks[i](Pin(i))
387393
}
388-
}).Enable()
394+
}
389395
}
390396

391397
var DefaultUART = UART0
@@ -395,22 +401,22 @@ var (
395401
_UART0 = UART{
396402
Bus: esp.UART0,
397403
Buffer: NewRingBuffer(),
398-
TXRXSignal: 14,
399-
RTSCTSSignal: 15,
404+
txrxSignal: 14,
405+
rtsctsSignal: 15,
400406
}
401407
UART1 = &_UART1
402408
_UART1 = UART{
403409
Bus: esp.UART1,
404410
Buffer: NewRingBuffer(),
405-
TXRXSignal: 17,
406-
RTSCTSSignal: 18,
411+
txrxSignal: 17,
412+
rtsctsSignal: 18,
407413
}
408414
UART2 = &_UART2
409415
_UART2 = UART{
410416
Bus: esp.UART2,
411417
Buffer: NewRingBuffer(),
412-
TXRXSignal: 198,
413-
RTSCTSSignal: 199,
418+
txrxSignal: 198,
419+
rtsctsSignal: 199,
414420
}
415421

416422
onceUart = sync.Once{}
@@ -435,16 +441,17 @@ const uartInterrupts = esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA |
435441
esp.UART_INT_ENA_GLITCH_DET_INT_ENA
436442

437443
type UART struct {
438-
Bus *esp.UART_Type
439-
Buffer *RingBuffer
440-
TXRXSignal uint32
441-
RTSCTSSignal uint32
442-
ParityErrorDetected bool
443-
DataErrorDetected bool
444-
DataOverflowDetected bool
444+
Bus *esp.UART_Type
445+
Buffer *RingBuffer
446+
447+
txrxSignal uint32
448+
rtsctsSignal uint32
449+
parityErrorDetected bool
450+
dataErrorDetected bool
451+
dataOverflowDetected bool
445452
}
446453

447-
func (uart *UART) Configure(config UARTConfig) {
454+
func (uart *UART) Configure(config UARTConfig) error {
448455
if config.BaudRate == 0 {
449456
config.BaudRate = 115200
450457
}
@@ -457,6 +464,8 @@ func (uart *UART) Configure(config UARTConfig) {
457464
// the IO MUX to the USB-serial bridge. Re-routing them through the GPIO
458465
// matrix is unnecessary and can break RX, so we keep the bootloader setup
459466
// which is exactly what makes the boot log and greeting appear.
467+
// We still fall through to configure baud rate, interrupts, and the RX
468+
// FIFO even when pins are already wired.
460469
if config.TX == 0 && config.RX == 0 {
461470
switch uart.Bus {
462471
case esp.UART0:
@@ -474,33 +483,35 @@ func (uart *UART) Configure(config UARTConfig) {
474483
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
475484

476485
if config.RX != NoPin {
477-
config.RX.configure(PinConfig{Mode: PinInputPullup}, uart.TXRXSignal)
486+
config.RX.configure(PinConfig{Mode: PinInputPullup}, uart.txrxSignal)
478487
if config.InvertRX {
479-
inFunc(uart.TXRXSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos | esp.GPIO_FUNC_IN_SEL_CFG_IN_INV_SEL)
488+
inFunc(uart.txrxSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos | esp.GPIO_FUNC_IN_SEL_CFG_IN_INV_SEL)
480489
} else {
481-
inFunc(uart.TXRXSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
490+
inFunc(uart.txrxSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
482491
}
483492
}
484493

485494
if config.TX != NoPin {
486-
config.TX.configure(PinConfig{Mode: PinOutput}, uart.TXRXSignal)
495+
config.TX.configure(PinConfig{Mode: PinOutput}, uart.txrxSignal)
487496
if config.InvertTX {
488-
config.TX.outFunc().Set(uart.TXRXSignal | esp.GPIO_FUNC_OUT_SEL_CFG_INV_SEL)
497+
config.TX.outFunc().Set(uart.txrxSignal | esp.GPIO_FUNC_OUT_SEL_CFG_INV_SEL)
489498
} else {
490-
config.TX.outFunc().Set(uart.TXRXSignal)
499+
config.TX.outFunc().Set(uart.txrxSignal)
491500
}
492501
}
493502

494503
if config.RTS != NoPin {
495-
config.RTS.configure(PinConfig{Mode: PinOutput}, uart.RTSCTSSignal)
504+
config.RTS.configure(PinConfig{Mode: PinOutput}, uart.rtsctsSignal)
496505
}
497506

498507
if config.CTS != NoPin {
499-
config.CTS.configure(PinConfig{Mode: PinInputPullup}, uart.RTSCTSSignal)
508+
config.CTS.configure(PinConfig{Mode: PinInputPullup}, uart.rtsctsSignal)
500509
}
501510

502511
uart.configureInterrupt()
503512
uart.enableReceiver()
513+
514+
return nil
504515
}
505516

506517
func (uart *UART) configureInterrupt() {
@@ -551,21 +562,21 @@ func (uart *UART) serveInterrupt() {
551562
// register, due to a silicon erratum. This mirrors writeByte.
552563
b := (*volatile.Register8)(unsafe.Add(unsafe.Pointer(uart.Bus), 0x200C0000)).Get()
553564
if !uart.Buffer.Put(b) {
554-
uart.DataOverflowDetected = true
565+
uart.dataOverflowDetected = true
555566
}
556567
}
557568
}
558569
if interruptFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 {
559-
uart.ParityErrorDetected = true
570+
uart.parityErrorDetected = true
560571
}
561572
if interruptFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA != 0 {
562-
uart.DataErrorDetected = true
573+
uart.dataErrorDetected = true
563574
}
564575
if interruptFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA != 0 {
565-
uart.DataOverflowDetected = true
576+
uart.dataOverflowDetected = true
566577
}
567578
if interruptFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA != 0 {
568-
uart.DataErrorDetected = true
579+
uart.dataErrorDetected = true
569580
}
570581

571582
// Clear the interrupt status bits.

0 commit comments

Comments
 (0)