@@ -33,6 +33,26 @@ const (
3333 PinInputPulldown PinMode = 12
3434)
3535
36+ type PinChange uint8
37+
38+ // Pin change interrupt constants for SetInterrupt.
39+ const (
40+ PinRising PinChange = sam .EIC_CONFIG_SENSE0_RISE
41+ PinFalling PinChange = sam .EIC_CONFIG_SENSE0_FALL
42+ PinToggle PinChange = sam .EIC_CONFIG_SENSE0_BOTH
43+ )
44+
45+ // Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
46+ // we also need to keep track of which interrupt channel is used by which pin,
47+ // as the only alternative would be iterating through all pins.
48+ //
49+ // We're using the magic constant 16 here because the SAM D21 has 16 interrupt
50+ // channels configurable for pins.
51+ var (
52+ interruptPins [16 ]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
53+ pinCallbacks [16 ]func (Pin )
54+ )
55+
3656const (
3757 pinPadMapSERCOM0Pad0 byte = (0x10 << 1 ) | 0x00
3858 pinPadMapSERCOM1Pad0 byte = (0x20 << 1 ) | 0x00
@@ -144,6 +164,114 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
144164 return
145165}
146166
167+ // SetInterrupt sets an interrupt to be executed when a particular pin changes
168+ // state.
169+ //
170+ // This call will replace a previously set callback on this pin. You can pass a
171+ // nil func to unset the pin change interrupt. If you do so, the change
172+ // parameter is ignored and can be set to any value (such as 0).
173+ func (p Pin ) SetInterrupt (change PinChange , callback func (Pin )) error {
174+ // Most pins follow a common pattern where the EXTINT value is the pin
175+ // number modulo 16. However, there are a few exceptions, as you can see
176+ // below.
177+ extint := uint8 (0 )
178+ switch p {
179+ case PA08 :
180+ // Connected to NMI. This is not currently supported.
181+ return ErrInvalidInputPin
182+ case PA24 :
183+ extint = 12
184+ case PA25 :
185+ extint = 13
186+ case PA27 :
187+ extint = 15
188+ case PA28 :
189+ extint = 8
190+ case PA30 :
191+ extint = 10
192+ case PA31 :
193+ extint = 11
194+ default :
195+ // All other pins follow a normal pattern.
196+ extint = uint8 (p ) % 16
197+ }
198+
199+ if callback == nil {
200+ // Disable this pin interrupt (if it was enabled).
201+ sam .EIC .INTENCLR .Set (1 << extint )
202+ if pinCallbacks [extint ] != nil {
203+ pinCallbacks [extint ] = nil
204+ }
205+ return nil
206+ }
207+
208+ if pinCallbacks [extint ] != nil {
209+ // The pin was already configured.
210+ // To properly re-configure a pin, unset it first and set a new
211+ // configuration.
212+ return ErrNoPinChangeChannel
213+ }
214+ pinCallbacks [extint ] = callback
215+ interruptPins [extint ] = p
216+
217+ if sam .EIC .CTRL .Get () == 0 {
218+ // EIC peripheral has not yet been initialized. Initialize it now.
219+
220+ // The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
221+ // enabled by default, so doesn't have to be re-enabled. The other is
222+ // required for detecting edges and must be enabled manually.
223+ sam .GCLK .CLKCTRL .Set (sam .GCLK_CLKCTRL_ID_EIC << sam .GCLK_CLKCTRL_ID_Pos |
224+ sam .GCLK_CLKCTRL_GEN_GCLK0 << sam .GCLK_CLKCTRL_GEN_Pos |
225+ sam .GCLK_CLKCTRL_CLKEN )
226+
227+ // should not be necessary (CLKCTRL is not synchronized)
228+ for sam .GCLK .STATUS .HasBits (sam .GCLK_STATUS_SYNCBUSY ) {
229+ }
230+
231+ sam .EIC .CTRL .Set (sam .EIC_CTRL_ENABLE )
232+ for sam .EIC .STATUS .HasBits (sam .EIC_STATUS_SYNCBUSY ) {
233+ }
234+ }
235+
236+ // Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
237+ // sense value (filter bit set to 0, sense bits set to the change value).
238+ addr := & sam .EIC .CONFIG0
239+ if extint >= 8 {
240+ addr = & sam .EIC .CONFIG1
241+ }
242+ pos := (extint % 8 ) * 4 // bit position in register
243+ addr .Set ((addr .Get () &^ (0xf << pos )) | uint32 (change )<< pos )
244+
245+ // Enable external interrupt for this pin.
246+ sam .EIC .INTENSET .Set (1 << extint )
247+
248+ // Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
249+ // were set before). This avoids clearing the pin pull mode while
250+ // configuring the pin interrupt.
251+ p .setPinCfg (sam .PORT_PINCFG0_PMUXEN | (p .getPinCfg () & (sam .PORT_PINCFG0_INEN | sam .PORT_PINCFG0_PULLEN )))
252+ if p & 1 > 0 {
253+ // odd pin, so save the even pins
254+ val := p .getPMux () & sam .PORT_PMUX0_PMUXE_Msk
255+ p .setPMux (val | (sam .PORT_PMUX0_PMUXO_A << sam .PORT_PMUX0_PMUXO_Pos ))
256+ } else {
257+ // even pin, so save the odd pins
258+ val := p .getPMux () & sam .PORT_PMUX0_PMUXO_Msk
259+ p .setPMux (val | (sam .PORT_PMUX0_PMUXE_A << sam .PORT_PMUX0_PMUXE_Pos ))
260+ }
261+
262+ interrupt .New (sam .IRQ_EIC , func (interrupt.Interrupt ) {
263+ flags := sam .EIC .INTFLAG .Get ()
264+ sam .EIC .INTFLAG .Set (flags ) // clear interrupt
265+ for i := uint (0 ); i < 16 ; i ++ { // there are 16 channels
266+ if flags & (1 << i ) != 0 {
267+ pinCallbacks [i ](interruptPins [i ])
268+ }
269+ }
270+ }).Enable ()
271+
272+ return nil
273+ }
274+
147275// InitADC initializes the ADC.
148276func InitADC () {
149277 // ADC Bias Calibration
0 commit comments