@@ -44,6 +44,26 @@ const (
4444 PinInputPulldown PinMode = 18
4545)
4646
47+ type PinChange uint8
48+
49+ // Pin change interrupt constants for SetInterrupt.
50+ const (
51+ PinRising PinChange = sam .EIC_CONFIG_SENSE0_RISE
52+ PinFalling PinChange = sam .EIC_CONFIG_SENSE0_FALL
53+ PinToggle PinChange = sam .EIC_CONFIG_SENSE0_BOTH
54+ )
55+
56+ // Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
57+ // we also need to keep track of which interrupt channel is used by which pin,
58+ // as the only alternative would be iterating through all pins.
59+ //
60+ // We're using the magic constant 16 here because the SAM D21 has 16 interrupt
61+ // channels configurable for pins.
62+ var (
63+ interruptPins [16 ]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
64+ pinCallbacks [16 ]func (Pin )
65+ )
66+
4767// Hardware pins
4868const (
4969 PA00 Pin = 0
@@ -268,6 +288,146 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
268288 return
269289}
270290
291+ // SetInterrupt sets an interrupt to be executed when a particular pin changes
292+ // state.
293+ //
294+ // This call will replace a previously set callback on this pin. You can pass a
295+ // nil func to unset the pin change interrupt. If you do so, the change
296+ // parameter is ignored and can be set to any value (such as 0).
297+ func (p Pin ) SetInterrupt (change PinChange , callback func (Pin )) error {
298+ // Most pins follow a common pattern where the EXTINT value is the pin
299+ // number modulo 16. However, there are a few exceptions, as you can see
300+ // below.
301+ extint := uint8 (0 )
302+
303+ switch p {
304+ case PA08 :
305+ // Connected to NMI. This is not currently supported.
306+ return ErrInvalidInputPin
307+ case PB26 :
308+ extint = 12
309+ case PB27 :
310+ extint = 13
311+ case PB28 :
312+ extint = 14
313+ case PB29 :
314+ extint = 15
315+ default :
316+ // All other pins follow a normal pattern.
317+ extint = uint8 (p ) % 16
318+ }
319+
320+ if callback == nil {
321+ // Disable this pin interrupt (if it was enabled).
322+ sam .EIC .INTENCLR .Set (1 << extint )
323+ if pinCallbacks [extint ] != nil {
324+ pinCallbacks [extint ] = nil
325+ }
326+ return nil
327+ }
328+
329+ if pinCallbacks [extint ] != nil {
330+ // The pin was already configured.
331+ // To properly re-configure a pin, unset it first and set a new
332+ // configuration.
333+ return ErrNoPinChangeChannel
334+ }
335+ pinCallbacks [extint ] = callback
336+ interruptPins [extint ] = p
337+
338+ if (sam .EIC .CTRLA .Get () & 0x02 ) == 0 {
339+ // EIC peripheral has not yet been initialized. Initialize it now.
340+
341+ // The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
342+ // enabled by default, so doesn't have to be re-enabled. The other is
343+ // required for detecting edges and must be enabled manually.
344+ sam .GCLK .PCHCTRL [4 ].Set ((sam .GCLK_PCHCTRL_GEN_GCLK0 << sam .GCLK_PCHCTRL_GEN_Pos ) | sam .GCLK_PCHCTRL_CHEN )
345+
346+ // should not be necessary (CLKCTRL is not synchronized)
347+ for sam .GCLK .SYNCBUSY .HasBits (sam .GCLK_SYNCBUSY_GENCTRL_GCLK0 << sam .GCLK_SYNCBUSY_GENCTRL_Pos ) {
348+ }
349+ }
350+
351+ // CONFIG register is enable-protected, so disable EIC.
352+ sam .EIC .CTRLA .Set (0 )
353+
354+ // Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
355+ // sense value (filter bit set to 0, sense bits set to the change value).
356+ addr := & sam .EIC .CONFIG [0 ]
357+ if extint >= 8 {
358+ addr = & sam .EIC .CONFIG [1 ]
359+ }
360+ pos := (extint % 8 ) * 4 // bit position in register
361+ addr .Set ((addr .Get () &^ (0xf << pos )) | uint32 (change )<< pos )
362+
363+ // Enable external interrupt for this pin.
364+ sam .EIC .INTENSET .Set (1 << extint )
365+
366+ sam .EIC .CTRLA .Set (sam .EIC_CTRLA_ENABLE )
367+ for sam .EIC .SYNCBUSY .HasBits (sam .EIC_SYNCBUSY_ENABLE ) {
368+ }
369+
370+ // Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
371+ // were set before). This avoids clearing the pin pull mode while
372+ // configuring the pin interrupt.
373+ p .setPinCfg (sam .PORT_GROUP_PINCFG_PMUXEN | (p .getPinCfg () & (sam .PORT_GROUP_PINCFG_INEN | sam .PORT_GROUP_PINCFG_PULLEN )))
374+ if p & 1 > 0 {
375+ // odd pin, so save the even pins
376+ val := p .getPMux () & sam .PORT_GROUP_PMUX_PMUXE_Msk
377+ p .setPMux (val | (0 << sam .PORT_GROUP_PMUX_PMUXO_Pos ))
378+ } else {
379+ // even pin, so save the odd pins
380+ val := p .getPMux () & sam .PORT_GROUP_PMUX_PMUXO_Msk
381+ p .setPMux (val | (0 << sam .PORT_GROUP_PMUX_PMUXE_Pos ))
382+ }
383+
384+ handleEICInterrupt := func (interrupt.Interrupt ) {
385+ flags := sam .EIC .INTFLAG .Get ()
386+ sam .EIC .INTFLAG .Set (flags ) // clear interrupt
387+ for i := uint (0 ); i < 16 ; i ++ { // there are 16 channels
388+ if flags & (1 << i ) != 0 {
389+ pinCallbacks [i ](interruptPins [i ])
390+ }
391+ }
392+ }
393+ switch extint {
394+ case 0 :
395+ interrupt .New (sam .IRQ_EIC_EXTINT_0 , handleEICInterrupt ).Enable ()
396+ case 1 :
397+ interrupt .New (sam .IRQ_EIC_EXTINT_1 , handleEICInterrupt ).Enable ()
398+ case 2 :
399+ interrupt .New (sam .IRQ_EIC_EXTINT_2 , handleEICInterrupt ).Enable ()
400+ case 3 :
401+ interrupt .New (sam .IRQ_EIC_EXTINT_3 , handleEICInterrupt ).Enable ()
402+ case 4 :
403+ interrupt .New (sam .IRQ_EIC_EXTINT_4 , handleEICInterrupt ).Enable ()
404+ case 5 :
405+ interrupt .New (sam .IRQ_EIC_EXTINT_5 , handleEICInterrupt ).Enable ()
406+ case 6 :
407+ interrupt .New (sam .IRQ_EIC_EXTINT_6 , handleEICInterrupt ).Enable ()
408+ case 7 :
409+ interrupt .New (sam .IRQ_EIC_EXTINT_7 , handleEICInterrupt ).Enable ()
410+ case 8 :
411+ interrupt .New (sam .IRQ_EIC_EXTINT_8 , handleEICInterrupt ).Enable ()
412+ case 9 :
413+ interrupt .New (sam .IRQ_EIC_EXTINT_9 , handleEICInterrupt ).Enable ()
414+ case 10 :
415+ interrupt .New (sam .IRQ_EIC_EXTINT_10 , handleEICInterrupt ).Enable ()
416+ case 11 :
417+ interrupt .New (sam .IRQ_EIC_EXTINT_11 , handleEICInterrupt ).Enable ()
418+ case 12 :
419+ interrupt .New (sam .IRQ_EIC_EXTINT_12 , handleEICInterrupt ).Enable ()
420+ case 13 :
421+ interrupt .New (sam .IRQ_EIC_EXTINT_13 , handleEICInterrupt ).Enable ()
422+ case 14 :
423+ interrupt .New (sam .IRQ_EIC_EXTINT_14 , handleEICInterrupt ).Enable ()
424+ case 15 :
425+ interrupt .New (sam .IRQ_EIC_EXTINT_15 , handleEICInterrupt ).Enable ()
426+ }
427+
428+ return nil
429+ }
430+
271431// Return the register and mask to enable a given GPIO pin. This can be used to
272432// implement bit-banged drivers.
273433func (p Pin ) PortMaskSet () (* uint32 , uint32 ) {
0 commit comments