diff --git a/src/machine/board_ae_rp2040.go b/src/machine/board_ae_rp2040.go index 716cf72b31..816e697ec0 100644 --- a/src/machine/board_ae_rp2040.go +++ b/src/machine/board_ae_rp2040.go @@ -2,6 +2,11 @@ package machine +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} + // GPIO pins const ( GP0 Pin = GPIO0 @@ -31,9 +36,6 @@ const ( GP27 Pin = GPIO27 GP28 Pin = GPIO28 GP29 Pin = GPIO29 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // I2C Default pins on Raspberry Pico. diff --git a/src/machine/board_amken-max14-rp2350b.go b/src/machine/board_amken-max14-rp2350b.go new file mode 100644 index 0000000000..aa656420f8 --- /dev/null +++ b/src/machine/board_amken-max14-rp2350b.go @@ -0,0 +1,127 @@ +//go:build amken_max14 + +// This file contains the pin mappings for the Amken Max14 Intelligent Motion controller board. +// + +package machine + +//const ( +// // Onboard crystal oscillator frequency, in MHz. +// xoscFreq uint32 = 12 // MHz +//) + +func init() { + SysClockFrequency = Freq240MHz + xoscFreq = Freq12MHz +} + +// I2C Default pins on Raspberry Pico. +const ( + I2C0_SDA_PIN = NoPin + I2C0_SCL_PIN = NoPin + + I2C1_SDA_PIN = GPIO34 + I2C1_SCL_PIN = GPIO35 + + I2C_SDA_PIN = I2C1_SDA_PIN + I2C_SCL_PIN = I2C1_SCL_PIN +) + +// SPI default pins +const ( + // Default Serial Clock Bus 0 for SPI communications + SPI0_SCK_PIN = GPIO42 + // Default Serial Out Bus 0 for SPI communications + SPI0_SDO_PIN = GPIO43 // Tx + // Default Serial In Bus 0 for SPI communications + SPI0_SDI_PIN = GPIO40 // Rx + + // Default Serial Clock Bus 1 for SPI communications + SPI1_SCK_PIN = GPIO2 + // Default Serial Out Bus 1 for SPI communications + SPI1_SDO_PIN = GPIO3 // Tx + // Default Serial In Bus 1 for SPI communications + SPI1_SDI_PIN = GPIO0 // Rx +) + +// UART pins +const ( + UART0_TX_PIN = GPIO0 + UART0_RX_PIN = GPIO1 + UART_TX_PIN = UART0_TX_PIN + UART_RX_PIN = UART0_RX_PIN +) + +var DefaultUART = UART0 + +var StepperCS = [8]Pin{ + GPIO36, GPIO37, GPIO38, GPIO39, + GPIO20, GPIO24, GPIO27, GPIO28, +} + +const ( + COMM_CS_PIN = GPIO1 + + MOTOR1_CS = GPIO36 + MOTOR2_CS = GPIO37 + MOTOR3_CS = GPIO38 + MOTOR4_CS = GPIO39 + MOTOR5_CS = GPIO20 + MOTOR6_CS = GPIO24 + MOTOR7_CS = GPIO27 + MOTOR8_CS = GPIO28 + + MOTOR1_DIR_PIN = GPIO9 + MOTOR2_DIR_PIN = GPIO13 + MOTOR3_DIR_PIN = GPIO15 + MOTOR4_DIR_PIN = GPIO17 + MOTOR5_DIR_PIN = GPIO19 + MOTOR6_DIR_PIN = GPIO23 + MOTOR7_DIR_PIN = GPIO25 + MOTOR8_DIR_PIN = GPIO30 + + MOTOR1_STEP_PIN = GPIO10 + MOTOR2_STEP_PIN = GPIO12 + MOTOR3_STEP_PIN = GPIO14 + MOTOR4_STEP_PIN = GPIO16 + MOTOR5_STEP_PIN = GPIO18 + MOTOR6_STEP_PIN = GPIO22 + MOTOR7_STEP_PIN = GPIO26 + MOTOR8_STEP_PIN = GPIO29 +) + +const ( + A2D1 = ADC6 + A2D2 = ADC5 +) + +const ( + END_STOP_MUX1 = GPIO33 + END_STOP_MUX2 = GPIO32 + END_STOP_MUX3 = GPIO31 +) + +const ( + PWM_5V_1 = GPIO8 + PWM_5V_2 = GPIO11 + LOWPWR_PWM_24V_1 = GPIO21 + LOWPWR_PWM_24V_2 = GPIO47 + HIPWR_PWM_24V = GPIO7 + MEDPWR_PWM_24V = GPIO6 +) + +const ( + NEOPIXEL_1 = GPIO41 + NEOPIXEL_2 = GPIO44 +) + +// USB identifiers +const ( + usb_STRING_PRODUCT = "Max14 Intelligent Motion Controller" + usb_STRING_MANUFACTURER = "AmkenLLC" +) + +var ( + usb_VID uint16 = 0x2E8A + usb_PID uint16 = 0x7303 +) diff --git a/src/machine/board_badger2040-w.go b/src/machine/board_badger2040-w.go index d0982653b3..7cbae7f694 100644 --- a/src/machine/board_badger2040-w.go +++ b/src/machine/board_badger2040-w.go @@ -69,9 +69,10 @@ SPI0_CS_PIN Pin = QSPI_CS ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // USB CDC identifiers const ( diff --git a/src/machine/board_badger2040.go b/src/machine/board_badger2040.go index 73f802a909..ccbb038e78 100644 --- a/src/machine/board_badger2040.go +++ b/src/machine/board_badger2040.go @@ -67,10 +67,10 @@ SPI0_CS_PIN Pin = QSPI_CS */ ) -// Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // USB CDC identifiers const ( diff --git a/src/machine/board_challenger_rp2040.go b/src/machine/board_challenger_rp2040.go index 9a85aa0aef..8318c4d852 100644 --- a/src/machine/board_challenger_rp2040.go +++ b/src/machine/board_challenger_rp2040.go @@ -2,11 +2,13 @@ package machine +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} + const ( LED = GPIO24 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // GPIO Pins diff --git a/src/machine/board_elecrow-rp2040-w5.go b/src/machine/board_elecrow-rp2040-w5.go index 1ea0181d1f..d303221675 100644 --- a/src/machine/board_elecrow-rp2040-w5.go +++ b/src/machine/board_elecrow-rp2040-w5.go @@ -39,9 +39,6 @@ const ( // Onboard LED LED Pin = GPIO25 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // I2C Default pins on Raspberry Pico. @@ -92,3 +89,8 @@ var ( usb_VID uint16 = 0x2E8A usb_PID uint16 = 0x000A ) + +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_elecrow-rp2350-w5.go b/src/machine/board_elecrow-rp2350-w5.go index 80a8436444..cdb1fb50f6 100644 --- a/src/machine/board_elecrow-rp2350-w5.go +++ b/src/machine/board_elecrow-rp2350-w5.go @@ -39,9 +39,6 @@ const ( // Onboard LED LED Pin = GPIO25 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // I2C Default pins on Raspberry Pico. @@ -92,3 +89,8 @@ var ( usb_VID uint16 = 0x2E8A usb_PID uint16 = 0x000F ) + +func init() { + SysClockFrequency = Freq150MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_feather_rp2040.go b/src/machine/board_feather_rp2040.go index 44091e56e9..15a1c31a63 100644 --- a/src/machine/board_feather_rp2040.go +++ b/src/machine/board_feather_rp2040.go @@ -2,8 +2,10 @@ package machine -// Onboard crystal oscillator frequency, in MHz. -const xoscFreq = 12 // MHz +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // GPIO Pins const ( diff --git a/src/machine/board_gopher-badge.go b/src/machine/board_gopher-badge.go index 7af27118b2..0165e05c2a 100644 --- a/src/machine/board_gopher-badge.go +++ b/src/machine/board_gopher-badge.go @@ -61,11 +61,6 @@ const ( SPI1_SDI_PIN Pin = NoPin ) -// Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) - // USB CDC identifiers const ( usb_STRING_PRODUCT = "Gopher Badge" @@ -88,3 +83,8 @@ const ( ) var DefaultUART = UART1 + +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_kb2040.go b/src/machine/board_kb2040.go index 1a6f353623..d25acd70f7 100644 --- a/src/machine/board_kb2040.go +++ b/src/machine/board_kb2040.go @@ -2,8 +2,10 @@ package machine -// Onboard crystal oscillator frequency, in MHz. -const xoscFreq = 12 // MHz +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // GPIO Pins const ( diff --git a/src/machine/board_macropad-rp2040.go b/src/machine/board_macropad-rp2040.go index 78bd2b749e..4dbaca97d1 100644 --- a/src/machine/board_macropad-rp2040.go +++ b/src/machine/board_macropad-rp2040.go @@ -4,9 +4,6 @@ package machine const ( NeopixelCount = 12 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) const ( @@ -85,3 +82,8 @@ var ( usb_VID uint16 = 0x239A usb_PID uint16 = 0x8107 ) + +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_nano-rp2040.go b/src/machine/board_nano-rp2040.go index 8155523134..c30b6649ac 100644 --- a/src/machine/board_nano-rp2040.go +++ b/src/machine/board_nano-rp2040.go @@ -96,9 +96,10 @@ const ( ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // USB CDC identifiers // https://github.com/arduino/ArduinoCore-mbed/blob/master/variants/NANO_RP2040_CONNECT/pins_arduino.h diff --git a/src/machine/board_pico.go b/src/machine/board_pico.go index efbd6ef7dc..7affd1d582 100644 --- a/src/machine/board_pico.go +++ b/src/machine/board_pico.go @@ -33,9 +33,6 @@ const ( // Onboard LED LED Pin = GPIO25 - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // I2C Default pins on Raspberry Pico. @@ -86,3 +83,8 @@ var ( usb_VID uint16 = 0x2E8A usb_PID uint16 = 0x000A ) + +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_pico2.go b/src/machine/board_pico2.go index 327c542fbc..7fd3539b8d 100644 --- a/src/machine/board_pico2.go +++ b/src/machine/board_pico2.go @@ -2,6 +2,11 @@ package machine +func init() { + SysClockFrequency = Freq150MHz + xoscFreq = Freq12MHz +} + // GPIO pins const ( GP0 Pin = GPIO0 @@ -34,8 +39,8 @@ const ( // Onboard LED LED Pin = GPIO25 - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz + //// Onboard crystal oscillator frequency, in MHz. + //xoscFreq = 12 // MHz ) // I2C Default pins on Raspberry Pico. diff --git a/src/machine/board_qtpy_rp2040.go b/src/machine/board_qtpy_rp2040.go index 3eabf0c9b6..2f9b56b213 100644 --- a/src/machine/board_qtpy_rp2040.go +++ b/src/machine/board_qtpy_rp2040.go @@ -2,8 +2,10 @@ package machine -// Onboard crystal oscillator frequency, in MHz. -const xoscFreq = 12 // MHz +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // GPIO Pins const ( diff --git a/src/machine/board_thingplus_rp2040.go b/src/machine/board_thingplus_rp2040.go index 48292d261e..780fc5f7fb 100644 --- a/src/machine/board_thingplus_rp2040.go +++ b/src/machine/board_thingplus_rp2040.go @@ -2,8 +2,10 @@ package machine -// Onboard crystal oscillator frequency, in MHz. -const xoscFreq = 12 // MHz +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // GPIO Pins const ( diff --git a/src/machine/board_thumby.go b/src/machine/board_thumby.go index f89a8b7059..32e2e2280c 100644 --- a/src/machine/board_thumby.go +++ b/src/machine/board_thumby.go @@ -50,9 +50,10 @@ const ( ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // USB CDC identifiers const ( diff --git a/src/machine/board_tiny2350.go b/src/machine/board_tiny2350.go index f04fa061b6..f2f130bd25 100644 --- a/src/machine/board_tiny2350.go +++ b/src/machine/board_tiny2350.go @@ -27,9 +27,6 @@ const ( LED_GREEN Pin = GPIO19 LED_BLUE Pin = GPIO20 LED = LED_RED - - // Onboard crystal oscillator frequency, in MHz. - xoscFreq = 12 // MHz ) // I2C Default pins on Tiny2350. @@ -80,3 +77,8 @@ var ( usb_VID uint16 = 0x2E8A usb_PID uint16 = 0x000F ) + +func init() { + SysClockFrequency = Freq150MHz + xoscFreq = Freq12MHz +} diff --git a/src/machine/board_trinkey_qt2040.go b/src/machine/board_trinkey_qt2040.go index 1a49c6d927..86da940872 100644 --- a/src/machine/board_trinkey_qt2040.go +++ b/src/machine/board_trinkey_qt2040.go @@ -13,8 +13,10 @@ package machine -// Onboard crystal oscillator frequency, in MHz -const xoscFreq = 12 // MHz +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // Onboard LEDs const ( diff --git a/src/machine/board_tufty2040.go b/src/machine/board_tufty2040.go index 57d244f28b..be04c52498 100644 --- a/src/machine/board_tufty2040.go +++ b/src/machine/board_tufty2040.go @@ -59,9 +59,10 @@ const ( ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // USB CDC identifiers const ( diff --git a/src/machine/board_waveshare-rp2040-zero.go b/src/machine/board_waveshare-rp2040-zero.go index 00ddc53a51..b3de66fecb 100644 --- a/src/machine/board_waveshare-rp2040-zero.go +++ b/src/machine/board_waveshare-rp2040-zero.go @@ -76,9 +76,10 @@ const ( ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // UART pins const ( diff --git a/src/machine/board_waveshare_rp2040_tiny.go b/src/machine/board_waveshare_rp2040_tiny.go index a3ef354041..5575088227 100644 --- a/src/machine/board_waveshare_rp2040_tiny.go +++ b/src/machine/board_waveshare_rp2040_tiny.go @@ -93,9 +93,10 @@ const ( ) // Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // UART pins const ( diff --git a/src/machine/board_xiao-rp2040.go b/src/machine/board_xiao-rp2040.go index b010314557..1109660084 100644 --- a/src/machine/board_xiao-rp2040.go +++ b/src/machine/board_xiao-rp2040.go @@ -63,10 +63,10 @@ const ( SPI1_SDI_PIN Pin = NoPin ) -// Onboard crystal oscillator frequency, in MHz. -const ( - xoscFreq = 12 // MHz -) +func init() { + SysClockFrequency = Freq133MHz + xoscFreq = Freq12MHz +} // UART pins const ( diff --git a/src/machine/machine_rp2040_adc.go b/src/machine/machine_rp2040_adc.go new file mode 100644 index 0000000000..2b733ac823 --- /dev/null +++ b/src/machine/machine_rp2040_adc.go @@ -0,0 +1,51 @@ +//go:build rp2040 || rp2350a + +package machine + +import ( + "errors" +) + +const ( + adc0_CH ADCChannel = iota + adc1_CH + adc2_CH + adc3_CH // Note: GPIO29 not broken out on pico board + adcTempSensor // Internal temperature sensor channel +) + +// GetADCChannel returns the channel associated with the ADC pin. +func (a ADC) GetADCChannel() (c ADCChannel, err error) { + err = nil + switch a.Pin { + case ADC0: + c = adc0_CH + case ADC1: + c = adc1_CH + case ADC2: + c = adc2_CH + case ADC3: + c = adc3_CH + default: + err = errors.New("no ADC channel for pin value") + } + return c, err +} + +// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one. +func (c ADCChannel) Pin() (p Pin, err error) { + err = nil + switch c { + case adc0_CH: + p = ADC0 + case adc1_CH: + p = ADC1 + case adc2_CH: + p = ADC2 + case adc3_CH: + p = ADC3 + default: + err = errors.New("no associated pin for channel") + } + return p, err +} diff --git a/src/machine/machine_rp2040_pwm.go b/src/machine/machine_rp2040_pwm.go index f72d6dd031..b3b5c0f6c0 100644 --- a/src/machine/machine_rp2040_pwm.go +++ b/src/machine/machine_rp2040_pwm.go @@ -1,63 +1,16 @@ -//go:build rp2040 +//go:build rp2040 || rp2350a package machine -import ( - "device/rp" - "errors" - "math" - "runtime/volatile" - "unsafe" -) - -var ( - ErrBadPeriod = errors.New("period outside valid range 8ns..268ms") -) - const ( maxPWMPins = 29 ) -// pwmGroup is one PWM peripheral, which consists of a counter and two output -// channels. You can set the frequency using SetPeriod, -// but only for all the channels in this PWM peripheral at once. -// -// div: integer value to reduce counting rate by. Must be greater than or equal to 1. -// -// cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle) -// and the 16 MSBs are Channel B's level. -// -// top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff. -// -// csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used. -// csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel. -// csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk. -// csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active. -// -// ctr: PWM counter value. -type pwmGroup struct { - CSR volatile.Register32 - DIV volatile.Register32 - CTR volatile.Register32 - CC volatile.Register32 - TOP volatile.Register32 -} - -// Equivalent of -// -// var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:] -// return &pwmSlice[index] -// -// 0x14 is the size of a pwmGroup. -func getPWMGroup(index uintptr) *pwmGroup { - return (*pwmGroup)(unsafe.Add(unsafe.Pointer(rp.PWM), 0x14*index)) -} - // Hardware Pulse Width Modulation (PWM) API -// PWM peripherals available on RP2040. Each peripheral has 2 pins available for +// PWM peripherals available on RP2040 and RP2350A. Each peripheral has 2 pins available for // a total of 16 available PWM outputs. Some pins may not be available on some boards. // -// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or +// The PWM block has 8 identical slices. Each slice can drive two PWM output signals, or // measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable // PWM outputs. All 30 GPIOs can be driven by the PWM block // @@ -78,333 +31,3 @@ var ( PWM6 = getPWMGroup(6) PWM7 = getPWMGroup(7) ) - -// Configure enables and configures this PWM. -func (pwm *pwmGroup) Configure(config PWMConfig) error { - return pwm.init(config, true) -} - -// Channel returns a PWM channel for the given pin. If pin does -// not belong to PWM peripheral ErrInvalidOutputPin error is returned. -// It also configures pin as PWM output. -func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) { - if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() { - return 3, ErrInvalidOutputPin - } - pin.Configure(PinConfig{PinPWM}) - return pwmGPIOToChannel(pin), nil -} - -// Peripheral returns the RP2040 PWM peripheral which ranges from 0 to 7. Each -// PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program. -// This number corresponds to the package's PWM0 throughout PWM7 handles -func PWMPeripheral(pin Pin) (sliceNum uint8, err error) { - if pin > maxPWMPins { - return 0, ErrInvalidOutputPin - } - return pwmGPIOToSlice(pin), nil -} - -// returns the number of the pwm peripheral (0-7) -func (pwm *pwmGroup) peripheral() uint8 { - return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14) -} - -// SetPeriod updates the period of this PWM peripheral in nanoseconds. -// To set a particular frequency, use the following formula: -// -// period = 1e9 / frequency -// -// Where frequency is in hertz. If you use a period of 0, a period -// that works well for LEDs will be picked. -// -// SetPeriod will try not to modify TOP if possible to reach the target period. -// If the period is unattainable with current TOP SetPeriod will modify TOP -// by the bare minimum to reach the target period. It will also enable phase -// correct to reach periods above 130ms. -func (p *pwmGroup) SetPeriod(period uint64) error { - if period == 0 { - period = 1e5 - } - return p.setPeriod(period) -} - -// Top returns the current counter top, for use in duty cycle calculation. -// -// The value returned here is hardware dependent. In general, it's best to treat -// it as an opaque value that can be divided by some number and passed to Set -// (see Set documentation for more information). -func (p *pwmGroup) Top() uint32 { - return p.getWrap() -} - -// Counter returns the current counter value of the timer in this PWM -// peripheral. It may be useful for debugging. -func (p *pwmGroup) Counter() uint32 { - return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos -} - -// Period returns the used PWM period in nanoseconds. -func (p *pwmGroup) Period() uint64 { - periodPerCycle := cpuPeriod() - top := p.getWrap() - phc := p.getPhaseCorrect() - Int, frac := p.getClockDiv() - // Line below can overflow if operations done without care. - return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*periodPerCycle) / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16) -} - -// SetInverting sets whether to invert the output of this channel. -// Without inverting, a 25% duty cycle would mean the output is high for 25% of -// the time and low for the rest. Inverting flips the output as if a NOT gate -// was placed at the output, meaning that the output would be 25% low and 75% -// high with a duty cycle of 25%. -func (p *pwmGroup) SetInverting(channel uint8, inverting bool) { - channel &= 1 - p.setInverting(channel, inverting) -} - -// Set updates the channel value. This is used to control the channel duty -// cycle, in other words the fraction of time the channel output is high (or low -// when inverted). For example, to set it to a 25% duty cycle, use: -// -// pwm.Set(channel, pwm.Top() / 4) -// -// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel, -// pwm.Top()) will set the output to high, assuming the output isn't inverted. -func (p *pwmGroup) Set(channel uint8, value uint32) { - val := uint16(value) - channel &= 1 - p.setChanLevel(channel, val) -} - -// Get current level (last set by Set). Default value on initialization is 0. -func (p *pwmGroup) Get(channel uint8) (value uint32) { - channel &= 1 - return uint32(p.getChanLevel(channel)) -} - -// SetTop sets TOP control register. Max value is 16bit (0xffff). -func (p *pwmGroup) SetTop(top uint32) { - p.setWrap(uint16(top)) -} - -// SetCounter sets counter control register. Max value is 16bit (0xffff). -// Useful for synchronising two different PWM peripherals. -func (p *pwmGroup) SetCounter(ctr uint32) { - p.CTR.Set(ctr) -} - -// Enable enables or disables PWM peripheral channels. -func (p *pwmGroup) Enable(enable bool) { - p.enable(enable) -} - -// IsEnabled returns true if peripheral is enabled. -func (p *pwmGroup) IsEnabled() (enabled bool) { - return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0 -} - -// Initialise a PWM with settings from a configuration object. -// If start is true then PWM starts on initialization. -func (pwm *pwmGroup) init(config PWMConfig, start bool) error { - // Not enable Phase correction - pwm.setPhaseCorrect(false) - - // Clock mode set by default to Free running - pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV) - - // Set Output polarity (false/false) - pwm.setInverting(0, false) - pwm.setInverting(1, false) - - // Set wrap. The highest value the counter will reach before returning to zero, also known as TOP. - pwm.setWrap(0xffff) - // period is set after TOP (Wrap). - err := pwm.SetPeriod(config.Period) - if err != nil { - return err - } - // period already set beforea - // Reset counter and compare (pwm level set to zero) - pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET - pwm.CC.Set(0) // PWM_CH0_CC_RESET - - pwm.enable(start) - return nil -} - -func (pwm *pwmGroup) setPhaseCorrect(correct bool) { - pwm.CSR.ReplaceBits(boolToBit(correct)< maxPeriod || period < 8 { - return ErrBadPeriod - } - if period > maxPeriod/2 { - pwm.setPhaseCorrect(true) // Must enable Phase correct to reach large periods. - } - - // clearing above expression: - // DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation - // where cycles must be converted to time: - // target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle - periodPerCycle := uint64(cpuPeriod()) - phc := uint64(pwm.getPhaseCorrect()) - rhs := 16 * period / ((1 + phc) * periodPerCycle * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided - whole := rhs / 16 - frac := rhs % 16 - switch { - case whole > 0xff: - whole = 0xff - case whole == 0: - // whole calculation underflowed so setting to minimum - // permissible value in DIV_INT register. - whole = 1 - frac = 0 - } - - // Step 2 is acquiring a better top value. Clearing the equation: - // TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1 - top := 16*period/((16*whole+frac)*periodPerCycle*(1+phc)) - 1 - if top > maxTop { - top = maxTop - } - pwm.SetTop(uint32(top)) - pwm.setClockDiv(uint8(whole), uint8(frac)) - return nil -} - -// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits). -// frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point -// fractional number. -// -// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16) -func (pwm *pwmGroup) setClockDiv(Int, frac uint8) { - pwm.DIV.ReplaceBits((uint32(frac)<> pos) - return level -} - -func (pwm *pwmGroup) getWrap() (top uint32) { - return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos -} - -func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) { - return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos -} - -func (pwm *pwmGroup) getClockDiv() (Int, frac uint8) { - div := pwm.DIV.Get() - return uint8((div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos), uint8((div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos) -} - -// pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO. -// gpio must be less than 30. Returns the PWM slice number that controls the specified GPIO. -func pwmGPIOToSlice(gpio Pin) (slicenum uint8) { - return (uint8(gpio) >> 1) & 7 -} - -// Determine the PWM channel that is attached to the specified GPIO. -// Each slice 0 to 7 has two channels, A and B. -func pwmGPIOToChannel(gpio Pin) (channel uint8) { - return uint8(gpio) & 1 -} diff --git a/src/machine/machine_rp2350b_adc.go b/src/machine/machine_rp2350b_adc.go new file mode 100644 index 0000000000..1172f3e1a2 --- /dev/null +++ b/src/machine/machine_rp2350b_adc.go @@ -0,0 +1,71 @@ +//go:build rp2350b + +package machine + +import ( + "errors" +) + +const ( + adc0_CH ADCChannel = iota + adc1_CH + adc2_CH + adc3_CH // Note: GPIO29 not broken out on pico board + adc4_CH + adc5_CH + adc6_CH + adc7_CH + adcTempSensor // Internal temperature sensor channel +) + +// GetADCChannel returns the channel associated with the ADC pin. +func (a ADC) GetADCChannel() (c ADCChannel, err error) { + err = nil + switch a.Pin { + case ADC0: + c = adc0_CH + case ADC1: + c = adc1_CH + case ADC2: + c = adc2_CH + case ADC3: + c = adc3_CH + case ADC4: + c = adc4_CH + case ADC5: + c = adc5_CH + case ADC6: + c = adc6_CH + case ADC7: + c = adc7_CH + default: + err = errors.New("no ADC channel for pin value") + } + return c, err +} + +// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one. +func (c ADCChannel) Pin() (p Pin, err error) { + err = nil + switch c { + case adc0_CH: + p = ADC0 + case adc1_CH: + p = ADC1 + case adc2_CH: + p = ADC2 + case adc3_CH: + p = ADC3 + case adc4_CH: + p = ADC4 + case adc5_CH: + p = ADC5 + case adc6_CH: + p = ADC6 + case adc7_CH: + p = ADC7 + default: + err = errors.New("no associated pin for channel") + } + return p, err +} diff --git a/src/machine/machine_rp2350b_pwm.go b/src/machine/machine_rp2350b_pwm.go new file mode 100644 index 0000000000..c5de16659c --- /dev/null +++ b/src/machine/machine_rp2350b_pwm.go @@ -0,0 +1,37 @@ +//go:build rp2350b + +package machine + +const ( + maxPWMPins = 47 +) + +// Hardware Pulse Width Modulation (PWM) API +// 12 PWM peripherals available on RP2350B. Each peripheral has 2 pins available for +// a total of 24 available PWM outputs. Some pins may not be available on some boards. +// +// The PWM block has 12 identical slices. Each slice can drive two PWM output signals, or +// measure the frequency or duty cycle of an input signal. This gives a total of up to 24 controllable +// PWM outputs. All 48 GPIOs can be driven by the PWM block +// +// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a +// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of +// time spent at the high signal level is known as the duty cycle of the signal. +// +// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then +// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after +// reaching TOP, until it reaches 0 again. +var ( + PWM0 = getPWMGroup(0) + PWM1 = getPWMGroup(1) + PWM2 = getPWMGroup(2) + PWM3 = getPWMGroup(3) + PWM4 = getPWMGroup(4) + PWM5 = getPWMGroup(5) + PWM6 = getPWMGroup(6) + PWM7 = getPWMGroup(7) + PWM8 = getPWMGroup(8) + PWM9 = getPWMGroup(9) + PWM10 = getPWMGroup(10) + PWM11 = getPWMGroup(11) +) diff --git a/src/machine/machine_rp2_xosc.go b/src/machine/machine_rp2_2040_xosc.go similarity index 97% rename from src/machine/machine_rp2_xosc.go rename to src/machine/machine_rp2_2040_xosc.go index c9ce58300b..92acad0fa1 100644 --- a/src/machine/machine_rp2_xosc.go +++ b/src/machine/machine_rp2_2040_xosc.go @@ -1,4 +1,4 @@ -//go:build rp2040 || rp2350 +//go:build rp2040 package machine diff --git a/src/machine/machine_rp2_2350.go b/src/machine/machine_rp2_2350.go index 4e12bebe35..127a6ee7da 100644 --- a/src/machine/machine_rp2_2350.go +++ b/src/machine/machine_rp2_2350.go @@ -1,4 +1,4 @@ -//go:build rp2350 +//go:build rp2350a || rp2350b package machine @@ -126,11 +126,19 @@ func (p Pin) Configure(config PinConfig) { return } p.init() - mask := uint32(1) << p + mask := uint32(1) << (p % 32) // Calculate mask based on pin number (adjust for GPIO_HI) + switch config.Mode { case PinOutput: p.setFunc(fnSIO) - rp.SIO.GPIO_OE_SET.Set(mask) + if p >= 32 { + // For pins 32 and above + rp.SIO.GPIO_HI_OE_SET.Set(mask) + } else { + // For pins below 32 + rp.SIO.GPIO_OE_SET.Set(mask) + } + case PinInput: p.setFunc(fnSIO) p.pulloff() @@ -201,7 +209,7 @@ func (clks *clocksType) initRTC() {} // No RTC on RP2350. func (clks *clocksType) initTicks() { rp.TICKS.SetTIMER0_CTRL_ENABLE(0) - rp.TICKS.SetTIMER0_CYCLES(12) + rp.TICKS.SetTIMER0_CYCLES(uint32(xoscFreq)) rp.TICKS.SetTIMER0_CTRL_ENABLE(1) } diff --git a/src/machine/machine_rp2_2350_xosc.go b/src/machine/machine_rp2_2350_xosc.go new file mode 100644 index 0000000000..81c475bd7b --- /dev/null +++ b/src/machine/machine_rp2_2350_xosc.go @@ -0,0 +1,61 @@ +//go:build rp2350 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +// On some boards, the XOSC can take longer than usual to stabilize. On such +// boards, this is needed to avoid a hard fault on boot/reset. Refer to +// PICO_XOSC_STARTUP_DELAY_MULTIPLIER in the Pico SDK for additional details. +const XOSC_STARTUP_DELAY_MULTIPLIER = 64 + +type xoscType struct { + ctrl volatile.Register32 + status volatile.Register32 + dormant volatile.Register32 + startup volatile.Register32 + reserved [3 - 3*rp2350ExtraReg]volatile.Register32 + count volatile.Register32 +} + +var xosc = (*xoscType)(unsafe.Pointer(rp.XOSC)) + +// init initializes the crystal oscillator system. +// +// This function will block until the crystal oscillator has stabilised. +func (osc *xoscType) init() { + + // Choose the correct FREQ_RANGE value from the enumerations. + // Note: these ranges come from the RP2 datasheet’s "XOSC_CTRL FREQ_RANGE" table: + // 0xaa0 => 1..15 MHz + // 0xaa1 => 10..30 MHz + // 0xaa2 => 25..60 MHz + // 0xaa3 => 40..100 MHz + var ctrlVal uint32 + if xoscFreq <= 15 { + ctrlVal = rp.XOSC_CTRL_FREQ_RANGE_1_15MHZ + } else if xoscFreq <= 30 { + ctrlVal = rp.XOSC_CTRL_FREQ_RANGE_10_30MHZ + } else if xoscFreq <= 60 { + ctrlVal = rp.XOSC_CTRL_FREQ_RANGE_25_60MHZ + } else if xoscFreq <= 100 { + ctrlVal = rp.XOSC_CTRL_FREQ_RANGE_40_100MHZ + } else { + panic("unsupported freq") + } + osc.ctrl.Set(ctrlVal) + // Set xosc startup delay + delay := (((xoscFreq * MHz) / 1000) + 128) / 256 * XOSC_STARTUP_DELAY_MULTIPLIER + osc.startup.Set(uint32(delay)) + + // Set the enable bit now that we have set freq range and startup delay + osc.ctrl.SetBits(rp.XOSC_CTRL_ENABLE_ENABLE << rp.XOSC_CTRL_ENABLE_Pos) + + // Wait for xosc to be stable + for !osc.status.HasBits(rp.XOSC_STATUS_STABLE) { + } +} diff --git a/src/machine/machine_rp2_2350b_pins.go b/src/machine/machine_rp2_2350b_pins.go new file mode 100644 index 0000000000..9acaae009c --- /dev/null +++ b/src/machine/machine_rp2_2350b_pins.go @@ -0,0 +1,65 @@ +//go:build rp2350b + +package machine + +const ( + // GPIO pins + GPIO0 Pin = 0 // peripherals: PWM0 channel A + GPIO1 Pin = 1 // peripherals: PWM0 channel B + GPIO2 Pin = 2 // peripherals: PWM1 channel A + GPIO3 Pin = 3 // peripherals: PWM1 channel B + GPIO4 Pin = 4 // peripherals: PWM2 channel A + GPIO5 Pin = 5 // peripherals: PWM2 channel B + GPIO6 Pin = 6 // peripherals: PWM3 channel A + GPIO7 Pin = 7 // peripherals: PWM3 channel B + GPIO8 Pin = 8 // peripherals: PWM4 channel A + GPIO9 Pin = 9 // peripherals: PWM4 channel B + GPIO10 Pin = 10 // peripherals: PWM5 channel A + GPIO11 Pin = 11 // peripherals: PWM5 channel B + GPIO12 Pin = 12 // peripherals: PWM6 channel A + GPIO13 Pin = 13 // peripherals: PWM6 channel B + GPIO14 Pin = 14 // peripherals: PWM7 channel A + GPIO15 Pin = 15 // peripherals: PWM7 channel B + GPIO16 Pin = 16 // peripherals: PWM0 channel A + GPIO17 Pin = 17 // peripherals: PWM0 channel B + GPIO18 Pin = 18 // peripherals: PWM1 channel A + GPIO19 Pin = 19 // peripherals: PWM1 channel B + GPIO20 Pin = 20 // peripherals: PWM2 channel A + GPIO21 Pin = 21 // peripherals: PWM2 channel B + GPIO22 Pin = 22 // peripherals: PWM3 channel A + GPIO23 Pin = 23 // peripherals: PWM3 channel B + GPIO24 Pin = 24 // peripherals: PWM4 channel A + GPIO25 Pin = 25 // peripherals: PWM4 channel B + GPIO26 Pin = 26 // peripherals: PWM5 channel A + GPIO27 Pin = 27 // peripherals: PWM5 channel B + GPIO28 Pin = 28 // peripherals: PWM6 channel A + GPIO29 Pin = 29 // peripherals: PWM6 channel B + GPIO30 Pin = 30 // peripherals: PWM7 channel A + GPIO31 Pin = 31 // peripherals: PWM7 channel B + GPIO32 Pin = 32 // peripherals: PWM8 channel A + GPIO33 Pin = 33 // peripherals: PWM8 channel B + GPIO34 Pin = 34 // peripherals: PWM9 channel A + GPIO35 Pin = 35 // peripherals: PWM9 channel B + GPIO36 Pin = 36 // peripherals: PWM10 channel A + GPIO37 Pin = 37 // peripherals: PWM10 channel B + GPIO38 Pin = 38 // peripherals: PWM11 channel A + GPIO39 Pin = 39 // peripherals: PWM11 channel B + GPIO40 Pin = 40 // peripherals: PWM8 channel A + GPIO41 Pin = 41 // peripherals: PWM8 channel B + GPIO42 Pin = 42 // peripherals: PWM9 channel A + GPIO43 Pin = 43 // peripherals: PWM9 channel B + GPIO44 Pin = 44 // peripherals: PWM10 channel A + GPIO45 Pin = 45 // peripherals: PWM10 channel B + GPIO46 Pin = 46 // peripherals: PWM11 channel A + GPIO47 Pin = 47 // peripherals: PWM11 channel B + + // Analog pins + ADC0 Pin = GPIO40 + ADC1 Pin = GPIO41 + ADC2 Pin = GPIO42 + ADC3 Pin = GPIO43 + ADC4 Pin = GPIO44 + ADC5 Pin = GPIO45 + ADC6 Pin = GPIO46 + ADC7 Pin = GPIO47 +) diff --git a/src/machine/machine_rp2_adc.go b/src/machine/machine_rp2_adc.go index 13504d719f..a024e58726 100644 --- a/src/machine/machine_rp2_adc.go +++ b/src/machine/machine_rp2_adc.go @@ -4,22 +4,12 @@ package machine import ( "device/rp" - "errors" "sync" ) // ADCChannel is the ADC peripheral mux channel. 0-4. type ADCChannel uint8 -// ADC channels. Only ADC_TEMP_SENSOR is public. The other channels are accessed via Machine.ADC objects -const ( - adc0_CH ADCChannel = iota - adc1_CH - adc2_CH - adc3_CH // Note: GPIO29 not broken out on pico board - adcTempSensor // Internal temperature sensor channel -) - // Used to serialise ADC sampling var adcLock sync.Mutex @@ -56,24 +46,6 @@ func (a ADC) Get() uint16 { return 0 } -// GetADCChannel returns the channel associated with the ADC pin. -func (a ADC) GetADCChannel() (c ADCChannel, err error) { - err = nil - switch a.Pin { - case ADC0: - c = adc0_CH - case ADC1: - c = adc1_CH - case ADC2: - c = adc2_CH - case ADC3: - c = adc3_CH - default: - err = errors.New("no ADC channel for pin value") - } - return c, err -} - // Configure sets the channel's associated pin to analog input mode. // The powered on temperature sensor increases ADC_AVDD current by approximately 40 μA. func (c ADCChannel) Configure(config ADCConfig) error { @@ -126,21 +98,3 @@ func waitForReady() { for !rp.ADC.CS.HasBits(rp.ADC_CS_READY) { } } - -// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one. -func (c ADCChannel) Pin() (p Pin, err error) { - err = nil - switch c { - case adc0_CH: - p = ADC0 - case adc1_CH: - p = ADC1 - case adc2_CH: - p = ADC2 - case adc3_CH: - p = ADC3 - default: - err = errors.New("no associated pin for channel") - } - return p, err -} diff --git a/src/machine/machine_rp2_clocks.go b/src/machine/machine_rp2_clocks.go index cc152a7f82..9e0f45cb5f 100644 --- a/src/machine/machine_rp2_clocks.go +++ b/src/machine/machine_rp2_clocks.go @@ -9,8 +9,88 @@ import ( "unsafe" ) +var SysClockFrequency SysFrequency // in MHz +var xoscFreq xtalFreq + +type SysFrequency uint32 +type xtalFreq uint32 + +const ( + Freq8MHz xtalFreq = 8 + Freq12MHz xtalFreq = 12 + Freq20MHz xtalFreq = 20 + Freq24MHz xtalFreq = 24 + Freq25MHz xtalFreq = 25 + Freq40MHz xtalFreq = 40 + Freq48MHz SysFrequency = 48 // USB + Freq50MHz SysFrequency = 50 // Good PWM Frequency + Freq100MHz SysFrequency = 100 // Good PWM Frequency + Freq125MHz SysFrequency = 125 // Valid frequency + Freq133MHz SysFrequency = 133 // Valid frequency + Freq150MHz SysFrequency = 150 // Valid frequency + Freq175MHz SysFrequency = 175 // Valid frequency + Freq200MHz SysFrequency = 200 // Good PWM Frequency + Freq225MHz SysFrequency = 225 // Valid frequency + Freq240MHz SysFrequency = 240 // Good PWM Frequency + Freq250MHz SysFrequency = 250 // Good PWM Frequency + Freq275MHz SysFrequency = 275 // Valid frequency + Freq300MHz SysFrequency = 300 // Good PWM Frequency +) + +type PLLConfig struct { + xoscFreq uint32 + sysFreq SysFrequency + refdiv uint32 + fbdiv uint32 + postDiv1 uint32 + postDiv2 uint32 +} + +// Flattened map with composite keys of frequencies drieved from SDK vcocalc.py +// [50,100,125,133,150,175,200,225,240,250,275,300]. +// These frequencies are stable at core voltage of 1.1v +var pllConfigMap = map[[2]uint32]PLLConfig{ + {12, 48}: {12, 48, 1, 120, 6, 5}, + {12, 50}: {12, 50, 1, 125, 6, 5}, + {12, 100}: {12, 100, 1, 125, 5, 3}, + {12, 125}: {12, 125, 1, 125, 6, 2}, + {12, 133}: {12, 133, 1, 133, 6, 2}, + {12, 150}: {12, 150, 1, 125, 5, 2}, + {12, 175}: {12, 175, 2, 175, 6, 1}, + {12, 200}: {12, 200, 1, 100, 6, 1}, + {12, 225}: {12, 225, 2, 225, 6, 1}, + {12, 240}: {12, 240, 1, 120, 6, 1}, + {12, 250}: {12, 250, 1, 125, 6, 1}, + {12, 300}: {12, 300, 1, 125, 5, 1}, + {40, 48}: {40, 48, 1, 36, 6, 5}, + {40, 50}: {40, 50, 2, 75, 6, 5}, + {40, 100}: {40, 100, 1, 40, 4, 4}, + {40, 125}: {40, 125, 2, 75, 6, 2}, + {40, 133}: {40, 133, 4, 133, 5, 2}, + {40, 150}: {40, 150, 2, 75, 5, 2}, + {40, 200}: {40, 200, 1, 40, 4, 2}, + {40, 225}: {40, 225, 8, 315, 7, 1}, + {40, 240}: {40, 240, 1, 36, 6, 1}, + {40, 250}: {40, 250, 2, 75, 6, 1}, + {40, 300}: {40, 300, 2, 75, 5, 1}, +} + +func getClockConfig(xoscFreq, sysMHz uint32) (PLLConfig, uint32) { + // Default configuration as fallback + defaultConfig := PLLConfig{12, 48, 1, 120, 6, 5} + cfg, exists := pllConfigMap[[2]uint32{xoscFreq, sysMHz}] + if !exists { + return defaultConfig, calculateVcoFreq(defaultConfig) + } + return cfg, calculateVcoFreq(cfg) +} + +func calculateVcoFreq(cfg PLLConfig) uint32 { + return (cfg.xoscFreq / cfg.refdiv) * cfg.fbdiv * MHz +} + func CPUFrequency() uint32 { - return 125 * MHz + return uint32(SysClockFrequency * MHz) } // Returns the period of a clock cycle for the raspberry pi pico in nanoseconds. @@ -148,7 +228,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) { // Must be called before any other clock function. func (clks *clocksType) init() { // Start the watchdog tick - Watchdog.startTick(xoscFreq) + Watchdog.startTick(uint32(xoscFreq)) // Disable resus that may be enabled from previous software rp.CLOCKS.SetCLK_SYS_RESUS_CTRL_CLEAR(0) @@ -164,28 +244,32 @@ func (clks *clocksType) init() { clks.clk[clkRef].ctrl.ClearBits(rp.CLOCKS_CLK_REF_CTRL_SRC_Msk) for !clks.clk[clkRef].selected.HasBits(0x1) { } - - // Configure PLLs - // REF FBDIV VCO POSTDIV - // pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz - // pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz - pllSys.init(1, 1500*MHz, 6, 2) - pllUSB.init(1, 480*MHz, 5, 2) - + //// Configure PLLs + cfg, vco := getClockConfig(uint32(xoscFreq), uint32(SysClockFrequency)) + pllSys.init(cfg.refdiv, vco, cfg.postDiv1, cfg.postDiv2) + // Configure USB + cfg, vco = getClockConfig(uint32(xoscFreq), 48) + pllUSB.init(cfg.refdiv, vco, cfg.postDiv1, cfg.postDiv2) + + // We need to ensure clk_ref <= 25 MHz. We'll compute an integer divider: + // minimal integer divisor so that (xoscFreq / refDiv) <= 25 + // i.e. refDiv >= xoscFreq / 25 + // We'll do a "ceiling" integer division: (xoscFreq + 24)/25 + refDiv := (xoscFreq + 24) / 25 + var refFreq = xoscFreq / refDiv // Configure clocks - // clkRef = xosc (12MHz) / 1 = 12MHz cref := clks.clock(clkRef) cref.configure(rp.CLOCKS_CLK_REF_CTRL_SRC_XOSC_CLKSRC, 0, // No aux mux - 12*MHz, - 12*MHz) + uint32(xoscFreq), + uint32(refFreq)) - // clkSys = pllSys (125MHz) / 1 = 125MHz + // Configure clkSys csys := clks.clock(clkSys) csys.configure(rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX, rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_CLKSRC_PLL_SYS, - 125*MHz, - 125*MHz) + uint32(SysClockFrequency*MHz), + uint32(SysClockFrequency*MHz)) // clkUSB = pllUSB (48MHz) / 1 = 48MHz cusb := clks.clock(clkUSB) @@ -204,13 +288,19 @@ func (clks *clocksType) init() { clks.initRTC() // clkPeri = clkSys. Used as reference clock for Peripherals. - // No dividers so just select and enable. - // Normally choose clkSys or clkUSB. + // Cap at 150MHZ + periFreq := SysClockFrequency + if SysClockFrequency > 150 { + periFreq = 150 + } + cperi := clks.clock(clkPeri) - cperi.configure(0, + cperi.configure( + 0, // no glitchless mux rp.CLOCKS_CLK_PERI_CTRL_AUXSRC_CLK_SYS, - 125*MHz, - 125*MHz) + uint32(SysClockFrequency*MHz), // source is clk_sys + uint32(periFreq*MHz), // final freq is min(sysClkMHz, 150) + ) clks.initTicks() } diff --git a/src/machine/machine_rp2_gpio.go b/src/machine/machine_rp2_gpio.go index d49d12f2ba..d3fe45e60b 100644 --- a/src/machine/machine_rp2_gpio.go +++ b/src/machine/machine_rp2_gpio.go @@ -63,8 +63,14 @@ func (p Pin) PortMaskSet() (*uint32, uint32) { // set drives the pin high func (p Pin) set() { - mask := uint32(1) << p - rp.SIO.GPIO_OUT_SET.Set(mask) + mask := uint32(1) << (p % 32) + if p >= 32 { + // For HI pins (32 and above) + rp.SIO.GPIO_HI_OUT_SET.Set(mask) + } else { + // For regular pins + rp.SIO.GPIO_OUT_SET.Set(mask) + } } func (p Pin) PortMaskClear() (*uint32, uint32) { @@ -73,19 +79,38 @@ func (p Pin) PortMaskClear() (*uint32, uint32) { // clr drives the pin low func (p Pin) clr() { - mask := uint32(1) << p - rp.SIO.GPIO_OUT_CLR.Set(mask) + mask := uint32(1) << (p % 32) + if p >= 32 { + // For HI pins (32 and above) + rp.SIO.GPIO_HI_OUT_CLR.Set(mask) + } else { + // For regular pins + 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) + mask := uint32(1) << (p % 32) + if p >= 32 { + // For HI pins (32 and above) + rp.SIO.GPIO_HI_OUT_XOR.Set(mask) + } else { + // For regular pins + 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) + mask := uint32(1) << (p % 32) + if p >= 32 { + // For HI pins (32 and above) + return rp.SIO.GPIO_HI_IN.HasBits(mask) + } else { + // For regular pins + return rp.SIO.GPIO_IN.HasBits(mask) + } } func (p Pin) ioCtrl() *volatile.Register32 { @@ -132,10 +157,16 @@ func (p Pin) setFunc(fn pinFunc) { p.ioCtrl().Set(uint32(fn) << rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos) } -// init initializes the gpio pin +// init initializes the GPIO pin func (p Pin) init() { - mask := uint32(1) << p - rp.SIO.GPIO_OE_CLR.Set(mask) + mask := uint32(1) << (p % 32) + if p >= 32 { + // For HI pins (32 and above) + rp.SIO.GPIO_HI_OE_CLR.Set(mask) + } else { + // For regular pins + rp.SIO.GPIO_OE_CLR.Set(mask) + } p.clr() } diff --git a/src/machine/machine_rp2_pins.go b/src/machine/machine_rp2_pins.go index 93f2d50a01..f15268f8e2 100644 --- a/src/machine/machine_rp2_pins.go +++ b/src/machine/machine_rp2_pins.go @@ -1,4 +1,4 @@ -//go:build rp2040 || rp2350 || ae_rp2040 || badger2040 || challenger_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || tufty2040 || waveshare_rp2040_zero || xiao_rp2040 +//go:build rp2040 || rp2350a package machine diff --git a/src/machine/machine_rp2_pll.go b/src/machine/machine_rp2_pll.go index f409768ab3..95b4fc561b 100644 --- a/src/machine/machine_rp2_pll.go +++ b/src/machine/machine_rp2_pll.go @@ -30,7 +30,7 @@ var ( // // Post Divider 2, postDiv2 with range 1-7. func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) { - refFreq := xoscFreq / refdiv + refFreq := uint32(xoscFreq) / refdiv // What are we multiplying the reference clock by to get the vco freq // (The regs are called div, because you divide the vco output and compare it to the refclk) diff --git a/src/machine/machine_rp2_pwm.go b/src/machine/machine_rp2_pwm.go new file mode 100644 index 0000000000..ec2451fce5 --- /dev/null +++ b/src/machine/machine_rp2_pwm.go @@ -0,0 +1,388 @@ +//go:build rp2040 || rp2350 + +package machine + +import ( + "device/rp" + "errors" + "math" + "runtime/volatile" + "unsafe" +) + +var ( + ErrBadPeriod = errors.New("period outside valid range 8ns..268ms") +) + +// pwmGroup is one PWM peripheral, which consists of a counter and two output +// channels. You can set the frequency using SetPeriod, +// but only for all the channels in this PWM peripheral at once. +// +// div: integer value to reduce counting rate by. Must be greater than or equal to 1. +// +// cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle) +// and the 16 MSBs are Channel B's level. +// +// top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff. +// +// csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used. +// csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel. +// csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk. +// csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active. +// +// ctr: PWM counter value. +type pwmGroup struct { + CSR volatile.Register32 + DIV volatile.Register32 + CTR volatile.Register32 + CC volatile.Register32 + TOP volatile.Register32 +} + +// Equivalent of +// +// var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:] +// return &pwmSlice[index] +// +// 0x14 is the size of a pwmGroup. +func getPWMGroup(index uintptr) *pwmGroup { + return (*pwmGroup)(unsafe.Add(unsafe.Pointer(rp.PWM), 0x14*index)) +} + +// Configure enables and configures this PWM. +func (pwm *pwmGroup) Configure(config PWMConfig) error { + return pwm.init(config, true) +} + +// Channel returns a PWM channel for the given pin. If pin does +// not belong to PWM peripheral ErrInvalidOutputPin error is returned. +// It also configures pin as PWM output. +func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) { + if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() { + return 3, ErrInvalidOutputPin + } + pin.Configure(PinConfig{PinPWM}) + return pwmGPIOToChannel(pin), nil +} + +// PWMPeripheral Peripheral returns the RP2040/Rp2350A PWM peripheral which ranges from 0 to 7 and 0 to 11 on RP2350B. Each +// PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program. +// This number corresponds to the package's PWM0 through PWM7 (or PWM11) handles +func PWMPeripheral(pin Pin) (sliceNum uint8, err error) { + if pin > maxPWMPins { + return 0, ErrInvalidOutputPin + } + return pwmGPIOToSlice(pin), nil +} + +// returns the number of the pwm peripheral (0-7) +func (pwm *pwmGroup) peripheral() uint8 { + return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14) +} + +// SetPeriod updates the period of this PWM peripheral in nanoseconds. +// To set a particular frequency, use the following formula: +// +// period = 1e9 / frequency +// +// Where frequency is in hertz. If you use a period of 0, a period +// that works well for LEDs will be picked. +// +// SetPeriod will try not to modify TOP if possible to reach the target period. +// If the period is unattainable with current TOP SetPeriod will modify TOP +// by the bare minimum to reach the target period. It will also enable phase +// correct to reach periods above 130ms. +func (p *pwmGroup) SetPeriod(period uint64) error { + if period == 0 { + period = 1e5 + } + return p.setPeriod(period) +} + +// Top returns the current counter top, for use in duty cycle calculation. +// +// The value returned here is hardware dependent. In general, it's best to treat +// it as an opaque value that can be divided by some number and passed to Set +// (see Set documentation for more information). +func (p *pwmGroup) Top() uint32 { + return p.getWrap() +} + +// Counter returns the current counter value of the timer in this PWM +// peripheral. It may be useful for debugging. +func (p *pwmGroup) Counter() uint32 { + return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos +} + +// Period returns the used PWM period in nanoseconds. +func (p *pwmGroup) Period() uint64 { + periodPerCycle := cpuPeriod() + top := p.getWrap() + phc := p.getPhaseCorrect() + Int, frac := p.getClockDiv() + // Line below can overflow if operations done without care. + return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*periodPerCycle) / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16) +} + +// SetInverting sets whether to invert the output of this channel. +// Without inverting, a 25% duty cycle would mean the output is high for 25% of +// the time and low for the rest. Inverting flips the output as if a NOT gate +// was placed at the output, meaning that the output would be 25% low and 75% +// high with a duty cycle of 25%. +func (p *pwmGroup) SetInverting(channel uint8, inverting bool) { + channel &= 1 + p.setInverting(channel, inverting) +} + +// Set updates the channel value. This is used to control the channel duty +// cycle, in other words the fraction of time the channel output is high (or low +// when inverted). For example, to set it to a 25% duty cycle, use: +// +// pwm.Set(channel, pwm.Top() / 4) +// +// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel, +// pwm.Top()) will set the output to high, assuming the output isn't inverted. +func (p *pwmGroup) Set(channel uint8, value uint32) { + val := uint16(value) + channel &= 1 + p.setChanLevel(channel, val) +} + +// Get current level (last set by Set). Default value on initialization is 0. +func (p *pwmGroup) Get(channel uint8) (value uint32) { + channel &= 1 + return uint32(p.getChanLevel(channel)) +} + +// SetTop sets TOP control register. Max value is 16bit (0xffff). +func (p *pwmGroup) SetTop(top uint32) { + p.setWrap(uint16(top)) +} + +// SetCounter sets counter control register. Max value is 16bit (0xffff). +// Useful for synchronising two different PWM peripherals. +func (p *pwmGroup) SetCounter(ctr uint32) { + p.CTR.Set(ctr) +} + +// Enable enables or disables PWM peripheral channels. +func (p *pwmGroup) Enable(enable bool) { + p.enable(enable) +} + +// IsEnabled returns true if peripheral is enabled. +func (p *pwmGroup) IsEnabled() (enabled bool) { + return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0 +} + +// Initialise a PWM with settings from a configuration object. +// If start is true then PWM starts on initialization. +func (pwm *pwmGroup) init(config PWMConfig, start bool) error { + // Not enable Phase correction + pwm.setPhaseCorrect(false) + + // Clock mode set by default to Free running + pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV) + + // Set Output polarity (false/false) + pwm.setInverting(0, false) + pwm.setInverting(1, false) + + // Set wrap. The highest value the counter will reach before returning to zero, also known as TOP. + pwm.setWrap(0xffff) + // period is set after TOP (Wrap). + err := pwm.SetPeriod(config.Period) + if err != nil { + return err + } + // period already set beforea + // Reset counter and compare (pwm level set to zero) + pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET + pwm.CC.Set(0) // PWM_CH0_CC_RESET + + pwm.enable(start) + return nil +} + +func (pwm *pwmGroup) setPhaseCorrect(correct bool) { + pwm.CSR.ReplaceBits(boolToBit(correct)< maxPeriod || period < 8 { + return ErrBadPeriod + } + if period > maxPeriod/2 { + pwm.setPhaseCorrect(true) // Must enable Phase correct to reach large periods. + } + + // clearing above expression: + // DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation + // where cycles must be converted to time: + // target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle + periodPerCycle := uint64(cpuPeriod()) + phc := uint64(pwm.getPhaseCorrect()) + rhs := 16 * period / ((1 + phc) * periodPerCycle * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided + whole := rhs / 16 + frac := rhs % 16 + switch { + case whole > 0xff: + whole = 0xff + case whole == 0: + // whole calculation underflowed so setting to minimum + // permissible value in DIV_INT register. + whole = 1 + frac = 0 + } + + // Step 2 is acquiring a better top value. Clearing the equation: + // TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1 + top := 16*period/((16*whole+frac)*periodPerCycle*(1+phc)) - 1 + if top > maxTop { + top = maxTop + } + pwm.SetTop(uint32(top)) + pwm.setClockDiv(uint8(whole), uint8(frac)) + return nil +} + +// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits). +// frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point +// fractional number. +// +// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16) +func (pwm *pwmGroup) setClockDiv(Int, frac uint8) { + pwm.DIV.ReplaceBits((uint32(frac)<> pos) + return level +} + +func (pwm *pwmGroup) getWrap() (top uint32) { + return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos +} + +func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) { + return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos +} + +func (pwm *pwmGroup) getClockDiv() (Int, frac uint8) { + div := pwm.DIV.Get() + return uint8((div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos), uint8((div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos) +} + +// Determine the PWM channel that is attached to the specified GPIO. +// Each slice 0 to 7 has two channels, A and B. +func pwmGPIOToChannel(gpio Pin) (channel uint8) { + return uint8(gpio) & 1 +} + +// pwmGPIOToSlice determines the PWM slice number that controls the specified GPIO pin. +// For the RP2350B, slices are grouped in the following way: +// - GPIOs 0–15 and 16–31 map to slices PWM0–PWM7 (8 slices). +// - GPIOs 32–39 and 40–47 map to slices PWM8–PWM11 (4 slices). +func pwmGPIOToSlice(gpio Pin) (slicenum uint8) { + if gpio < 32 { + // First 32 GPIOs (PWM0–PWM7), slice repeats every 16 + return uint8((gpio / 2) % 8) + } else { + // GPIOs 32–47 (PWM8–PWM11), slice repeats every 8 + return uint8(8 + ((gpio-32)/2)%4) + } +} diff --git a/targets/amken-max14.json b/targets/amken-max14.json new file mode 100644 index 0000000000..bff46c9176 --- /dev/null +++ b/targets/amken-max14.json @@ -0,0 +1,8 @@ +{ + "inherits": [ + "rp2350" + ], + "build-tags": ["amken_max14", "rp2350b"], + "serial-port": ["2e8a:000f"], + "default-stack-size": 8192 +} diff --git a/targets/elecrow-rp2350.json b/targets/elecrow-rp2350.json index aecc86723b..2eab7cc28c 100644 --- a/targets/elecrow-rp2350.json +++ b/targets/elecrow-rp2350.json @@ -1,6 +1,6 @@ { "inherits": ["rp2350"], - "build-tags": ["elecrow_rp2350"], + "build-tags": ["elecrow_rp2350", "rp2350a"], "serial-port": ["2e8a:000f"], "default-stack-size": 8192, "ldflags": [ diff --git a/targets/pico-2w.json b/targets/pico-2w.json new file mode 100644 index 0000000000..60f6e3f05b --- /dev/null +++ b/targets/pico-2w.json @@ -0,0 +1,4 @@ +{ + "inherits": ["pico2"], + "build-tags": ["pico-w", "cyw43439"] +} diff --git a/targets/pico2.json b/targets/pico2.json index dfdac71ee0..3e4cfed07e 100644 --- a/targets/pico2.json +++ b/targets/pico2.json @@ -2,7 +2,7 @@ "inherits": [ "rp2350" ], - "build-tags": ["pico2"], + "build-tags": ["pico2", "rp2350a"], "serial-port": ["2e8a:000A"], "default-stack-size": 8192 } diff --git a/targets/tiny2350.json b/targets/tiny2350.json index 185be33202..86f2a66775 100644 --- a/targets/tiny2350.json +++ b/targets/tiny2350.json @@ -2,6 +2,6 @@ "inherits": [ "rp2350" ], - "build-tags": ["tiny2350"], + "build-tags": ["tiny2350", "rp2350a"], "serial-port": ["2e8a:000f"] }