sam: add support for pin change interrupts
Этот коммит содержится в:
		
							родитель
							
								
									19c7965fc5
								
							
						
					
					
						коммит
						c72f9eb08c
					
				
					 2 изменённых файлов: 138 добавлений и 0 удалений
				
			
		
							
								
								
									
										10
									
								
								src/examples/pininterrupt/circuitplay-express.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										10
									
								
								src/examples/pininterrupt/circuitplay-express.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | // +build circuitplay_express | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import "machine" | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	buttonMode      = machine.PinInputPulldown | ||||||
|  | 	buttonPinChange = machine.PinFalling | ||||||
|  | ) | ||||||
|  | @ -33,6 +33,26 @@ const ( | ||||||
| 	PinInputPulldown PinMode = 12 | 	PinInputPulldown PinMode = 12 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type PinChange uint8 | ||||||
|  | 
 | ||||||
|  | // Pin change interrupt constants for SetInterrupt. | ||||||
|  | const ( | ||||||
|  | 	PinRising  PinChange = sam.EIC_CONFIG_SENSE0_RISE | ||||||
|  | 	PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL | ||||||
|  | 	PinToggle  PinChange = sam.EIC_CONFIG_SENSE0_BOTH | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Callbacks to be called for pins configured with SetInterrupt. Unfortunately, | ||||||
|  | // we also need to keep track of which interrupt channel is used by which pin, | ||||||
|  | // as the only alternative would be iterating through all pins. | ||||||
|  | // | ||||||
|  | // We're using the magic constant 16 here because the SAM D21 has 16 interrupt | ||||||
|  | // channels configurable for pins. | ||||||
|  | var ( | ||||||
|  | 	interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set! | ||||||
|  | 	pinCallbacks  [16]func(Pin) | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| const ( | const ( | ||||||
| 	pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00 | 	pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00 | ||||||
| 	pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00 | 	pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00 | ||||||
|  | @ -144,6 +164,114 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // SetInterrupt sets an interrupt to be executed when a particular pin changes | ||||||
|  | // state. | ||||||
|  | // | ||||||
|  | // This call will replace a previously set callback on this pin. You can pass a | ||||||
|  | // nil func to unset the pin change interrupt. If you do so, the change | ||||||
|  | // parameter is ignored and can be set to any value (such as 0). | ||||||
|  | func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { | ||||||
|  | 	// Most pins follow a common pattern where the EXTINT value is the pin | ||||||
|  | 	// number modulo 16. However, there are a few exceptions, as you can see | ||||||
|  | 	// below. | ||||||
|  | 	extint := uint8(0) | ||||||
|  | 	switch p { | ||||||
|  | 	case PA08: | ||||||
|  | 		// Connected to NMI. This is not currently supported. | ||||||
|  | 		return ErrInvalidInputPin | ||||||
|  | 	case PA24: | ||||||
|  | 		extint = 12 | ||||||
|  | 	case PA25: | ||||||
|  | 		extint = 13 | ||||||
|  | 	case PA27: | ||||||
|  | 		extint = 15 | ||||||
|  | 	case PA28: | ||||||
|  | 		extint = 8 | ||||||
|  | 	case PA30: | ||||||
|  | 		extint = 10 | ||||||
|  | 	case PA31: | ||||||
|  | 		extint = 11 | ||||||
|  | 	default: | ||||||
|  | 		// All other pins follow a normal pattern. | ||||||
|  | 		extint = uint8(p) % 16 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if callback == nil { | ||||||
|  | 		// Disable this pin interrupt (if it was enabled). | ||||||
|  | 		sam.EIC.INTENCLR.Set(1 << extint) | ||||||
|  | 		if pinCallbacks[extint] != nil { | ||||||
|  | 			pinCallbacks[extint] = nil | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if pinCallbacks[extint] != nil { | ||||||
|  | 		// The pin was already configured. | ||||||
|  | 		// To properly re-configure a pin, unset it first and set a new | ||||||
|  | 		// configuration. | ||||||
|  | 		return ErrNoPinChangeChannel | ||||||
|  | 	} | ||||||
|  | 	pinCallbacks[extint] = callback | ||||||
|  | 	interruptPins[extint] = p | ||||||
|  | 
 | ||||||
|  | 	if sam.EIC.CTRL.Get() == 0 { | ||||||
|  | 		// EIC peripheral has not yet been initialized. Initialize it now. | ||||||
|  | 
 | ||||||
|  | 		// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is | ||||||
|  | 		// enabled by default, so doesn't have to be re-enabled. The other is | ||||||
|  | 		// required for detecting edges and must be enabled manually. | ||||||
|  | 		sam.GCLK.CLKCTRL.Set(sam.GCLK_CLKCTRL_ID_EIC<<sam.GCLK_CLKCTRL_ID_Pos | | ||||||
|  | 			sam.GCLK_CLKCTRL_GEN_GCLK0<<sam.GCLK_CLKCTRL_GEN_Pos | | ||||||
|  | 			sam.GCLK_CLKCTRL_CLKEN) | ||||||
|  | 
 | ||||||
|  | 		// should not be necessary (CLKCTRL is not synchronized) | ||||||
|  | 		for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		sam.EIC.CTRL.Set(sam.EIC_CTRL_ENABLE) | ||||||
|  | 		for sam.EIC.STATUS.HasBits(sam.EIC_STATUS_SYNCBUSY) { | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the | ||||||
|  | 	// sense value (filter bit set to 0, sense bits set to the change value). | ||||||
|  | 	addr := &sam.EIC.CONFIG0 | ||||||
|  | 	if extint >= 8 { | ||||||
|  | 		addr = &sam.EIC.CONFIG1 | ||||||
|  | 	} | ||||||
|  | 	pos := (extint % 8) * 4 // bit position in register | ||||||
|  | 	addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos) | ||||||
|  | 
 | ||||||
|  | 	// Enable external interrupt for this pin. | ||||||
|  | 	sam.EIC.INTENSET.Set(1 << extint) | ||||||
|  | 
 | ||||||
|  | 	// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they | ||||||
|  | 	// were set before). This avoids clearing the pin pull mode while | ||||||
|  | 	// configuring the pin interrupt. | ||||||
|  | 	p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | (p.getPinCfg() & (sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN))) | ||||||
|  | 	if p&1 > 0 { | ||||||
|  | 		// odd pin, so save the even pins | ||||||
|  | 		val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk | ||||||
|  | 		p.setPMux(val | (sam.PORT_PMUX0_PMUXO_A << sam.PORT_PMUX0_PMUXO_Pos)) | ||||||
|  | 	} else { | ||||||
|  | 		// even pin, so save the odd pins | ||||||
|  | 		val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk | ||||||
|  | 		p.setPMux(val | (sam.PORT_PMUX0_PMUXE_A << sam.PORT_PMUX0_PMUXE_Pos)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	interrupt.New(sam.IRQ_EIC, func(interrupt.Interrupt) { | ||||||
|  | 		flags := sam.EIC.INTFLAG.Get() | ||||||
|  | 		sam.EIC.INTFLAG.Set(flags)      // clear interrupt | ||||||
|  | 		for i := uint(0); i < 16; i++ { // there are 16 channels | ||||||
|  | 			if flags&(1<<i) != 0 { | ||||||
|  | 				pinCallbacks[i](interruptPins[i]) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}).Enable() | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // InitADC initializes the ADC. | // InitADC initializes the ADC. | ||||||
| func InitADC() { | func InitADC() { | ||||||
| 	// ADC Bias Calibration | 	// ADC Bias Calibration | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem