mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 04:23:41 +00:00
562b4c5020
* Create "pico2-ice" target board This board has an rp2350b chip on it, as well as a Lattice Semiconductor iCE40UP5K FPGA. More details of this open hardware board here: https://pico2-ice.tinyvision.ai/ Tested on a pico2-ice board with: ~/go/bin/tinygo flash -target=pico2-ice src/examples/blinky1/blinky1.go which blinks the GREEN LED (connected to GPIO0) on this board. Signed-off-by: Tinkerer <tinkerer@zappem.net> * More silkscreen labels for pico2-ice board Reading the schematic and the rev2 board viewer: https://raw.githubusercontent.com/tinyvision-ai-inc/pico2-ice/refs/heads/main/Board/Rev2/bom/ibom.html concluded that the RP2350B GPIO pins are not labeled with these pin numbers in the silkscreen. Instead, the silkscreen refers to uses of the GPIOs or the ICE numbered pins. RP2340B devoted pins map to the A1..4 B1..4 pins on the RP PMOD connector. Also silkscreen "~0".."~6" are labeled as pins N0..N6. Signed-off-by: Tinkerer <tinkerer@zappem.net> * Added a smoketest for pico2-ice board and tidied up GPIO defs Addresses review comments from aykevl. Signed-off-by: Tinkerer <tinkerer@zappem.net>
249 lines
6.6 KiB
Go
249 lines
6.6 KiB
Go
//go:build rp2040
|
|
|
|
package machine
|
|
|
|
import (
|
|
"device/rp"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
cpuFreq = 200 * MHz
|
|
_NUMBANK0_GPIOS = 30
|
|
_NUMBANK0_IRQS = 4
|
|
_NUMIRQ = 32
|
|
rp2350ExtraReg = 0
|
|
RESETS_RESET_Msk = 0x01ffffff
|
|
initUnreset = rp.RESETS_RESET_ADC |
|
|
rp.RESETS_RESET_RTC |
|
|
rp.RESETS_RESET_SPI0 |
|
|
rp.RESETS_RESET_SPI1 |
|
|
rp.RESETS_RESET_UART0 |
|
|
rp.RESETS_RESET_UART1 |
|
|
rp.RESETS_RESET_USBCTRL
|
|
initDontReset = rp.RESETS_RESET_IO_QSPI |
|
|
rp.RESETS_RESET_PADS_QSPI |
|
|
rp.RESETS_RESET_PLL_USB |
|
|
rp.RESETS_RESET_USBCTRL |
|
|
rp.RESETS_RESET_SYSCFG |
|
|
rp.RESETS_RESET_PLL_SYS
|
|
padEnableMask = rp.PADS_BANK0_GPIO0_IE_Msk |
|
|
rp.PADS_BANK0_GPIO0_OD_Msk
|
|
)
|
|
|
|
const (
|
|
PinOutput PinMode = iota
|
|
PinInput
|
|
PinInputPulldown
|
|
PinInputPullup
|
|
PinAnalog
|
|
PinUART
|
|
PinPWM
|
|
PinI2C
|
|
PinSPI
|
|
PinPIO0
|
|
PinPIO1
|
|
)
|
|
|
|
// Analog pins on RP2040.
|
|
const (
|
|
ADC0 Pin = GPIO26
|
|
ADC1 Pin = GPIO27
|
|
ADC2 Pin = GPIO28
|
|
ADC3 Pin = GPIO29
|
|
|
|
thermADC = 30
|
|
)
|
|
|
|
const (
|
|
clkGPOUT0 clockIndex = iota // GPIO Muxing 0
|
|
clkGPOUT1 // GPIO Muxing 1
|
|
clkGPOUT2 // GPIO Muxing 2
|
|
clkGPOUT3 // GPIO Muxing 3
|
|
clkRef // Watchdog and timers reference clock
|
|
clkSys // Processors, bus fabric, memory, memory mapped registers
|
|
clkPeri // Peripheral clock for UART and SPI
|
|
clkUSB // USB clock
|
|
clkADC // ADC clock
|
|
clkRTC // Real time clock
|
|
numClocks
|
|
)
|
|
|
|
func calcClockDiv(srcFreq, freq uint32) uint32 {
|
|
// Div register is 24.8 int.frac divider so multiply by 2^8 (left shift by 8)
|
|
return uint32((uint64(srcFreq) << 8) / uint64(freq))
|
|
}
|
|
|
|
type clocksType struct {
|
|
clk [numClocks]clockType
|
|
resus struct {
|
|
ctrl volatile.Register32
|
|
status volatile.Register32
|
|
}
|
|
fc0 fc
|
|
wakeEN0 volatile.Register32
|
|
wakeEN1 volatile.Register32
|
|
sleepEN0 volatile.Register32
|
|
sleepEN1 volatile.Register32
|
|
enabled0 volatile.Register32
|
|
enabled1 volatile.Register32
|
|
intR volatile.Register32
|
|
intE volatile.Register32
|
|
intF volatile.Register32
|
|
intS volatile.Register32
|
|
}
|
|
|
|
// 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
|
|
)
|
|
|
|
// validPins confirms that the SPI pin selection is a legitimate one
|
|
// for the the 2040 chip.
|
|
func (spi *SPI) validPins(config SPIConfig) error {
|
|
var okSDI, okSDO, okSCK bool
|
|
switch spi.Bus {
|
|
case rp.SPI0:
|
|
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
|
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
|
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
|
case rp.SPI1:
|
|
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
|
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
}
|
|
switch {
|
|
case !okSDI:
|
|
return errSPIInvalidSDI
|
|
case !okSDO:
|
|
return errSPIInvalidSDO
|
|
case !okSCK:
|
|
return errSPIInvalidSCK
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Configure configures the gpio pin as per mode.
|
|
func (p Pin) Configure(config PinConfig) {
|
|
if p == NoPin {
|
|
return
|
|
}
|
|
p.init()
|
|
mask := uint32(1) << p
|
|
switch config.Mode {
|
|
case PinOutput:
|
|
p.setFunc(fnSIO)
|
|
rp.SIO.GPIO_OE_SET.Set(mask)
|
|
case PinInput:
|
|
p.setFunc(fnSIO)
|
|
p.pulloff()
|
|
case PinInputPulldown:
|
|
p.setFunc(fnSIO)
|
|
p.pulldown()
|
|
case PinInputPullup:
|
|
p.setFunc(fnSIO)
|
|
p.pullup()
|
|
case PinAnalog:
|
|
p.setFunc(fnNULL)
|
|
p.pulloff()
|
|
case PinUART:
|
|
p.setFunc(fnUART)
|
|
case PinPWM:
|
|
p.setFunc(fnPWM)
|
|
case PinI2C:
|
|
// IO config according to 4.3.1.3 of rp2040 datasheet.
|
|
p.setFunc(fnI2C)
|
|
p.pullup()
|
|
p.setSchmitt(true)
|
|
p.setSlew(false)
|
|
case PinSPI:
|
|
p.setFunc(fnSPI)
|
|
case PinPIO0:
|
|
p.setFunc(fnPIO0)
|
|
case PinPIO1:
|
|
p.setFunc(fnPIO1)
|
|
}
|
|
}
|
|
|
|
var (
|
|
timer = (*timerType)(unsafe.Pointer(rp.TIMER))
|
|
)
|
|
|
|
// Enable or disable a specific interrupt on the executing core.
|
|
// num is the interrupt number which must be in [0,31].
|
|
func irqSet(num uint32, enabled bool) {
|
|
if num >= _NUMIRQ {
|
|
return
|
|
}
|
|
irqSetMask(1<<num, enabled)
|
|
}
|
|
|
|
func irqSetMask(mask uint32, enabled bool) {
|
|
if enabled {
|
|
// Clear pending before enable
|
|
// (if IRQ is actually asserted, it will immediately re-pend)
|
|
rp.PPB.NVIC_ICPR.Set(mask)
|
|
rp.PPB.NVIC_ISER.Set(mask)
|
|
} else {
|
|
rp.PPB.NVIC_ICER.Set(mask)
|
|
}
|
|
}
|
|
|
|
func (clks *clocksType) initRTC() {
|
|
// clkRTC = pllUSB (48MHz) / 1024 = 46875Hz
|
|
crtc := clks.clock(clkRTC)
|
|
crtc.configure(0, // No GLMUX
|
|
rp.CLOCKS_CLK_RTC_CTRL_AUXSRC_CLKSRC_PLL_USB,
|
|
48*MHz,
|
|
46875)
|
|
}
|
|
|
|
func (clks *clocksType) initTicks() {} // No ticks on RP2040
|
|
|
|
// startTick starts the watchdog tick.
|
|
// cycles needs to be a divider that when applied to the xosc input,
|
|
// produces a 1MHz clock. So if the xosc frequency is 12MHz,
|
|
// this will need to be 12.
|
|
func (wd *watchdogImpl) startTick(cycles uint32) {
|
|
rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
|
|
}
|
|
|
|
func adjustCoreVoltage() bool {
|
|
if cpuFreq <= 133*MHz {
|
|
return false
|
|
}
|
|
// The rp2040 is certified to run at 200MHz with the
|
|
// core voltage set to 1150mV.
|
|
const targetVoltage = 1150
|
|
// 0b0101 maps to 800mV and each step is 50mV.
|
|
const vreg = 0b0101 + (targetVoltage-800)/50
|
|
rp.VREG_AND_CHIP_RESET.SetVREG_VSEL(vreg)
|
|
return true
|
|
}
|