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 }