//go:build rp2040 package machine import ( "device/rp" "runtime/interrupt" "runtime/volatile" "unsafe" ) type ioType struct { status volatile.Register32 ctrl volatile.Register32 } type irqCtrl struct { intE [4]volatile.Register32 intF [4]volatile.Register32 intS [4]volatile.Register32 } type ioBank0Type struct { io [30]ioType intR [4]volatile.Register32 proc0IRQctrl irqCtrl proc1IRQctrl irqCtrl dormantWakeIRQctrl irqCtrl } var ioBank0 = (*ioBank0Type)(unsafe.Pointer(rp.IO_BANK0)) type padsBank0Type struct { voltageSelect volatile.Register32 io [30]volatile.Register32 } var padsBank0 = (*padsBank0Type)(unsafe.Pointer(rp.PADS_BANK0)) // pinFunc represents a GPIO function. // // Each GPIO can have one function selected at a time. // Likewise, each peripheral input (e.g. UART0 RX) should only be selected // on one GPIO at a time. If the same peripheral input is connected to multiple GPIOs, // the peripheral sees the logical OR of these GPIO inputs. type pinFunc uint8 // GPIO function selectors const ( fnJTAG pinFunc = 0 fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO fnUART pinFunc = 2 fnI2C pinFunc = 3 // Connect a PWM slice to GPIO. There are eight PWM slices, // each with two outputchannels (A/B). The B pin can also be used as an input, // for frequency and duty cyclemeasurement fnPWM pinFunc = 4 // Software control of GPIO, from the single-cycle IO (SIO) block. // The SIO function (F5)must be selected for the processors to drive a GPIO, // but the input is always connected,so software can check the state of GPIOs at any time. fnSIO pinFunc = 5 // Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces, // and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs. // The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected, // so the PIOs canalways see the state of all pins. fnPIO0, fnPIO1 pinFunc = 6, 7 // General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040, // e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter. // e.g. Output: optional integer divide fnGPCK pinFunc = 8 // USB power control signals to/from the internal USB controller fnUSB pinFunc = 9 fnNULL pinFunc = 0x1f fnXIP pinFunc = 0 ) const ( PinOutput PinMode = iota PinInput PinInputPulldown PinInputPullup PinAnalog PinUART PinPWM PinI2C PinSPI PinPIO0 PinPIO1 ) func (p Pin) PortMaskSet() (*uint32, uint32) { return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_SET)), 1 << p } // set drives the pin high func (p Pin) set() { mask := uint32(1) << p rp.SIO.GPIO_OUT_SET.Set(mask) } func (p Pin) PortMaskClear() (*uint32, uint32) { return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_CLR)), 1 << p } // clr drives the pin low func (p Pin) clr() { mask := uint32(1) << p rp.SIO.GPIO_OUT_CLR.Set(mask) } // xor toggles the pin func (p Pin) xor() { mask := uint32(1) << p rp.SIO.GPIO_OUT_XOR.Set(mask) } // get returns the pin value func (p Pin) get() bool { return rp.SIO.GPIO_IN.HasBits(1 << p) } func (p Pin) ioCtrl() *volatile.Register32 { return &ioBank0.io[p].ctrl } func (p Pin) padCtrl() *volatile.Register32 { return &padsBank0.io[p] } func (p Pin) pullup() { p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PUE) p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE) } func (p Pin) pulldown() { p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PDE) p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) } func (p Pin) pulloff() { p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE) p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) } // setSlew sets pad slew rate control. // true sets to fast. false sets to slow. func (p Pin) setSlew(sr bool) { p.padCtrl().ReplaceBits(boolToBit(sr)< 31 || p < 0 { return ErrInvalidInputPin } core := CurrentCore() if callback == nil { // disable current interrupt p.setInterrupt(change, false) pinCallbacks[core][p] = nil return nil } if pinCallbacks[core][p] != nil { // Callback already configured. Should disable callback by passing a nil callback first. return ErrNoPinChangeChannel } p.setInterrupt(change, true) pinCallbacks[core][p] = callback if setInt[core][p] { // interrupt has already been set. Exit. return nil } interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable() irqSet(rp.IRQ_IO_IRQ_BANK0, true) return nil } // gpioHandleInterrupt finds the corresponding pin for the interrupt. // C SDK equivalent of gpio_irq_handler func gpioHandleInterrupt(intr interrupt.Interrupt) { core := CurrentCore() var gpio Pin for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ { var base *irqCtrl switch core { case 0: base = &ioBank0.proc0IRQctrl case 1: base = &ioBank0.proc1IRQctrl } statreg := base.intS[gpio>>3].Get() change := getIntChange(gpio, statreg) if change != 0 { gpio.acknowledgeInterrupt(change) callback := pinCallbacks[core][gpio] if callback != nil { callback(gpio) } } } } // events returns the bit representation of the pin change for the rp2040. func (change PinChange) events() uint32 { return uint32(change) } // intBit is the bit storage form of a PinChange for a given Pin // in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet). func (p Pin) ioIntBit(change PinChange) uint32 { return change.events() << (4 * (p % 8)) } // Acquire interrupt data from a INT status register. func getIntChange(p Pin, status uint32) PinChange { return PinChange(status>>(4*(p%8))) & 0xf } // UART on the RP2040 var ( UART0 = &_UART0 _UART0 = UART{ Buffer: NewRingBuffer(), Bus: rp.UART0, } UART1 = &_UART1 _UART1 = UART{ Buffer: NewRingBuffer(), Bus: rp.UART1, } ) func init() { UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt) }