github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_gpio.go (about)

     1  //go:build rp2040
     2  
     3  package machine
     4  
     5  import (
     6  	"device/rp"
     7  	"runtime/interrupt"
     8  	"runtime/volatile"
     9  	"unsafe"
    10  )
    11  
    12  type ioType struct {
    13  	status volatile.Register32
    14  	ctrl   volatile.Register32
    15  }
    16  
    17  type irqCtrl struct {
    18  	intE [4]volatile.Register32
    19  	intF [4]volatile.Register32
    20  	intS [4]volatile.Register32
    21  }
    22  
    23  type ioBank0Type struct {
    24  	io                 [30]ioType
    25  	intR               [4]volatile.Register32
    26  	proc0IRQctrl       irqCtrl
    27  	proc1IRQctrl       irqCtrl
    28  	dormantWakeIRQctrl irqCtrl
    29  }
    30  
    31  var ioBank0 = (*ioBank0Type)(unsafe.Pointer(rp.IO_BANK0))
    32  
    33  type padsBank0Type struct {
    34  	voltageSelect volatile.Register32
    35  	io            [30]volatile.Register32
    36  }
    37  
    38  var padsBank0 = (*padsBank0Type)(unsafe.Pointer(rp.PADS_BANK0))
    39  
    40  // pinFunc represents a GPIO function.
    41  //
    42  // Each GPIO can have one function selected at a time.
    43  // Likewise, each peripheral input (e.g. UART0 RX) should only be  selected
    44  // on one GPIO at a time. If the same peripheral input is connected to multiple GPIOs,
    45  // the peripheral sees the logical OR of these GPIO inputs.
    46  type pinFunc uint8
    47  
    48  // GPIO function selectors
    49  const (
    50  	fnJTAG pinFunc = 0
    51  	fnSPI  pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO
    52  	fnUART pinFunc = 2
    53  	fnI2C  pinFunc = 3
    54  	// Connect a PWM slice to GPIO. There are eight PWM slices,
    55  	// each with two outputchannels (A/B). The B pin can also be used as an input,
    56  	// for frequency and duty cyclemeasurement
    57  	fnPWM pinFunc = 4
    58  	// Software control of GPIO, from the single-cycle IO (SIO) block.
    59  	// The SIO function (F5)must be selected for the processors to drive a GPIO,
    60  	// but the input is always connected,so software can check the state of GPIOs at any time.
    61  	fnSIO pinFunc = 5
    62  	// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces,
    63  	// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs.
    64  	// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected,
    65  	// so the PIOs canalways see the state of all pins.
    66  	fnPIO0, fnPIO1 pinFunc = 6, 7
    67  	// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040,
    68  	// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter.
    69  	// e.g. Output: optional integer divide
    70  	fnGPCK pinFunc = 8
    71  	// USB power control signals to/from the internal USB controller
    72  	fnUSB  pinFunc = 9
    73  	fnNULL pinFunc = 0x1f
    74  
    75  	fnXIP pinFunc = 0
    76  )
    77  
    78  const (
    79  	PinOutput PinMode = iota
    80  	PinInput
    81  	PinInputPulldown
    82  	PinInputPullup
    83  	PinAnalog
    84  	PinUART
    85  	PinPWM
    86  	PinI2C
    87  	PinSPI
    88  	PinPIO0
    89  	PinPIO1
    90  )
    91  
    92  func (p Pin) PortMaskSet() (*uint32, uint32) {
    93  	return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_SET)), 1 << p
    94  }
    95  
    96  // set drives the pin high
    97  func (p Pin) set() {
    98  	mask := uint32(1) << p
    99  	rp.SIO.GPIO_OUT_SET.Set(mask)
   100  }
   101  
   102  func (p Pin) PortMaskClear() (*uint32, uint32) {
   103  	return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_CLR)), 1 << p
   104  }
   105  
   106  // clr drives the pin low
   107  func (p Pin) clr() {
   108  	mask := uint32(1) << p
   109  	rp.SIO.GPIO_OUT_CLR.Set(mask)
   110  }
   111  
   112  // xor toggles the pin
   113  func (p Pin) xor() {
   114  	mask := uint32(1) << p
   115  	rp.SIO.GPIO_OUT_XOR.Set(mask)
   116  }
   117  
   118  // get returns the pin value
   119  func (p Pin) get() bool {
   120  	return rp.SIO.GPIO_IN.HasBits(1 << p)
   121  }
   122  
   123  func (p Pin) ioCtrl() *volatile.Register32 {
   124  	return &ioBank0.io[p].ctrl
   125  }
   126  
   127  func (p Pin) padCtrl() *volatile.Register32 {
   128  	return &padsBank0.io[p]
   129  }
   130  
   131  func (p Pin) pullup() {
   132  	p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PUE)
   133  	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
   134  }
   135  
   136  func (p Pin) pulldown() {
   137  	p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PDE)
   138  	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
   139  }
   140  
   141  func (p Pin) pulloff() {
   142  	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
   143  	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
   144  }
   145  
   146  // setSlew sets pad slew rate control.
   147  // true sets to fast. false sets to slow.
   148  func (p Pin) setSlew(sr bool) {
   149  	p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
   150  }
   151  
   152  // setSchmitt enables or disables Schmitt trigger.
   153  func (p Pin) setSchmitt(trigger bool) {
   154  	p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
   155  }
   156  
   157  // setFunc will set pin function to fn.
   158  func (p Pin) setFunc(fn pinFunc) {
   159  	// Set input enable, Clear output disable
   160  	p.padCtrl().ReplaceBits(rp.PADS_BANK0_GPIO0_IE,
   161  		rp.PADS_BANK0_GPIO0_IE_Msk|rp.PADS_BANK0_GPIO0_OD_Msk, 0)
   162  
   163  	// Zero all fields apart from fsel; we want this IO to do what the peripheral tells it.
   164  	// This doesn't affect e.g. pullup/pulldown, as these are in pad controls.
   165  	p.ioCtrl().Set(uint32(fn) << rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos)
   166  }
   167  
   168  // init initializes the gpio pin
   169  func (p Pin) init() {
   170  	mask := uint32(1) << p
   171  	rp.SIO.GPIO_OE_CLR.Set(mask)
   172  	p.clr()
   173  }
   174  
   175  // Configure configures the gpio pin as per mode.
   176  func (p Pin) Configure(config PinConfig) {
   177  	if p == NoPin {
   178  		return
   179  	}
   180  	p.init()
   181  	mask := uint32(1) << p
   182  	switch config.Mode {
   183  	case PinOutput:
   184  		p.setFunc(fnSIO)
   185  		rp.SIO.GPIO_OE_SET.Set(mask)
   186  	case PinInput:
   187  		p.setFunc(fnSIO)
   188  		p.pulloff()
   189  	case PinInputPulldown:
   190  		p.setFunc(fnSIO)
   191  		p.pulldown()
   192  	case PinInputPullup:
   193  		p.setFunc(fnSIO)
   194  		p.pullup()
   195  	case PinAnalog:
   196  		p.setFunc(fnNULL)
   197  		p.pulloff()
   198  	case PinUART:
   199  		p.setFunc(fnUART)
   200  	case PinPWM:
   201  		p.setFunc(fnPWM)
   202  	case PinI2C:
   203  		// IO config according to 4.3.1.3 of rp2040 datasheet.
   204  		p.setFunc(fnI2C)
   205  		p.pullup()
   206  		p.setSchmitt(true)
   207  		p.setSlew(false)
   208  	case PinSPI:
   209  		p.setFunc(fnSPI)
   210  	case PinPIO0:
   211  		p.setFunc(fnPIO0)
   212  	case PinPIO1:
   213  		p.setFunc(fnPIO1)
   214  	}
   215  }
   216  
   217  // Set drives the pin high if value is true else drives it low.
   218  func (p Pin) Set(value bool) {
   219  	if p == NoPin {
   220  		return
   221  	}
   222  	if value {
   223  		p.set()
   224  	} else {
   225  		p.clr()
   226  	}
   227  }
   228  
   229  // Get reads the pin value.
   230  func (p Pin) Get() bool {
   231  	return p.get()
   232  }
   233  
   234  // PinChange represents one or more trigger events that can happen on a given GPIO pin
   235  // on the RP2040. ORed PinChanges are valid input to most IRQ functions.
   236  type PinChange uint8
   237  
   238  // Pin change interrupt constants for SetInterrupt.
   239  const (
   240  	// Edge falling
   241  	PinFalling PinChange = 4 << iota
   242  	// Edge rising
   243  	PinRising
   244  
   245  	PinToggle = PinFalling | PinRising
   246  )
   247  
   248  // Callbacks to be called for pins configured with SetInterrupt.
   249  var (
   250  	pinCallbacks [2][_NUMBANK0_GPIOS]func(Pin)
   251  	setInt       [2][_NUMBANK0_GPIOS]bool
   252  )
   253  
   254  // SetInterrupt sets an interrupt to be executed when a particular pin changes
   255  // state. The pin should already be configured as an input, including a pull up
   256  // or down if no external pull is provided.
   257  //
   258  // This call will replace a previously set callback on this pin. You can pass a
   259  // nil func to unset the pin change interrupt. If you do so, the change
   260  // parameter is ignored and can be set to any value (such as 0).
   261  func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
   262  	if p == NoPin {
   263  		return nil
   264  	}
   265  	if p > 31 || p < 0 {
   266  		return ErrInvalidInputPin
   267  	}
   268  	core := CurrentCore()
   269  	if callback == nil {
   270  		// disable current interrupt
   271  		p.setInterrupt(change, false)
   272  		pinCallbacks[core][p] = nil
   273  		return nil
   274  	}
   275  
   276  	if pinCallbacks[core][p] != nil {
   277  		// Callback already configured. Should disable callback by passing a nil callback first.
   278  		return ErrNoPinChangeChannel
   279  	}
   280  	p.setInterrupt(change, true)
   281  	pinCallbacks[core][p] = callback
   282  
   283  	if setInt[core][p] {
   284  		// interrupt has already been set. Exit.
   285  		return nil
   286  	}
   287  	interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable()
   288  	irqSet(rp.IRQ_IO_IRQ_BANK0, true)
   289  	return nil
   290  }
   291  
   292  // gpioHandleInterrupt finds the corresponding pin for the interrupt.
   293  // C SDK equivalent of gpio_irq_handler
   294  func gpioHandleInterrupt(intr interrupt.Interrupt) {
   295  
   296  	core := CurrentCore()
   297  	var gpio Pin
   298  	for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ {
   299  		var base *irqCtrl
   300  		switch core {
   301  		case 0:
   302  			base = &ioBank0.proc0IRQctrl
   303  		case 1:
   304  			base = &ioBank0.proc1IRQctrl
   305  		}
   306  
   307  		statreg := base.intS[gpio>>3].Get()
   308  		change := getIntChange(gpio, statreg)
   309  		if change != 0 {
   310  			gpio.acknowledgeInterrupt(change)
   311  			callback := pinCallbacks[core][gpio]
   312  			if callback != nil {
   313  				callback(gpio)
   314  			}
   315  		}
   316  	}
   317  }
   318  
   319  // events returns the bit representation of the pin change for the rp2040.
   320  func (change PinChange) events() uint32 {
   321  	return uint32(change)
   322  }
   323  
   324  // intBit is the bit storage form of a PinChange for a given Pin
   325  // in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet).
   326  func (p Pin) ioIntBit(change PinChange) uint32 {
   327  	return change.events() << (4 * (p % 8))
   328  }
   329  
   330  // Acquire interrupt data from a INT status register.
   331  func getIntChange(p Pin, status uint32) PinChange {
   332  	return PinChange(status>>(4*(p%8))) & 0xf
   333  }
   334  
   335  // UART on the RP2040
   336  var (
   337  	UART0  = &_UART0
   338  	_UART0 = UART{
   339  		Buffer: NewRingBuffer(),
   340  		Bus:    rp.UART0,
   341  	}
   342  
   343  	UART1  = &_UART1
   344  	_UART1 = UART{
   345  		Buffer: NewRingBuffer(),
   346  		Bus:    rp.UART1,
   347  	}
   348  )
   349  
   350  func init() {
   351  	UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
   352  	UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
   353  }