Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Raspberry Pi Pico 2, Pico 2 W #377

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9d30a9d
Change the CPU_FREQ europi_config setting to a string instead of the …
chrisib Aug 9, 2024
bd9e9fd
Add additional docs for setting up the Pico 2. Untested so far, since…
chrisib Aug 11, 2024
8602d53
Disable formatting checks to keep code legibly aligned, fix some othe…
chrisib Aug 27, 2024
d64d065
Update BOM with note about RP2350
chrisib Aug 28, 2024
3d0f5f3
Implement a simplified, single-threaded version of Lutra so it doesn'…
chrisib Aug 30, 2024
50be0f7
Disable the temperature sensor in the diagnostics program for the Pic…
chrisib Sep 1, 2024
440ee41
Use the standard europi GPIO pin wrapper instances instead of creatin…
chrisib Sep 5, 2024
a909b07
Refactor the calibration script to use member functions, add some doc…
chrisib Sep 5, 2024
0268ead
String formatting
chrisib Sep 5, 2024
bf9c469
Revert to the original calibration, but don't re-construct the pins; …
chrisib Sep 5, 2024
79d25d8
Remove unused functions
chrisib Sep 5, 2024
9521bc6
Remove unused imports
chrisib Sep 5, 2024
b605113
Revert changes to de-thread Lutra; the latest Pico 2 firmware appears…
chrisib Oct 9, 2024
fb15cec
Remove superfluous newline
chrisib Oct 9, 2024
b3237d8
Copy the temperature & USB connection changes from the calibration up…
chrisib Nov 9, 2024
17a162d
Instantiate the new thermometer
chrisib Nov 9, 2024
3ceadcd
Add Pico H as an option for the Pico model
chrisib Nov 11, 2024
6e61785
Docs
chrisib Nov 11, 2024
e675ebe
Docs
chrisib Nov 11, 2024
1e11dd4
Setting Up -> Pico Setup for section header
chrisib Nov 11, 2024
8afdbc3
Merge branch 'main' into rp2350
chrisib Nov 11, 2024
71bef5d
Add "underclocked" key for CPU frequency; sets CPU frequency to 75MHz…
chrisib Dec 8, 2024
142b5b3
Add constants for the pico models, keep a space between pico & the va…
chrisib Dec 8, 2024
9669a23
Fix default input voltage
chrisib Dec 8, 2024
9001bf4
Merge branch 'main' into rp2350
chrisib Dec 8, 2024
faf3b8a
Comment restructuring
chrisib Dec 8, 2024
6711f2f
Update the configuration docs with the pico 2w option
chrisib Dec 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hardware/EuroPi/bill_of_materials.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ If buying in bulk there are likely other suppliers that are cheaper - this BOM i
| | 2 | T18 Shaft | Knobs | [Thonk](https://www.thonk.co.uk/shop/1900h-t18/)<br>**NOTE: If you buy D-Shaft potentiometers, the knobs must be 'Reverse-D-Shaft' type.<br>Any knobs which fit may be chosen, but Mouser does not stock the standard suggestion sold by Thonk**
| | 1 | 10 - 16 Pin | Eurorack Power Cable | [Thonk](https://www.thonk.co.uk/shop/eurorack-power-cables/)<br>**Note: Mouser does not stock the correct cable at time of writing. Other suppliers will provide the correct part, but always double check that the polarity is correct (red stripe is always -12V)**
| | 1 | | Micro USB Cable (Capable of Data Transfer) | [CPC](https://cpc.farnell.com/pro-signal/psg91562/lead-usb-a-male-micro-b-male-black/dp/CS32732), [The Pi Hut](https://thepihut.com/products/usb-to-micro-usb-cable-0-5m)<br>[Mouser](https://www.mouser.co.uk/ProductDetail/Teltonika/PR2US08M?qs=9vOqFld9vZXl90mLcdqZXQ%3D%3D)
| | 1 | | Raspberry Pi Pico | [The Pi Hut](https://thepihut.com/products/raspberry-pi-pico)<br>[CPC](https://cpc.farnell.com/raspberry-pi/raspberry-pi-pico/raspberry-pi-pico-rp2040-mcu-board/dp/SC17106)<br>[Mouser](https://www.mouser.co.uk/ProductDetail/Raspberry-Pi/SC0915?qs=T%252BzbugeAwjgnLi4azxXVFA%3D%3D)<br>**Note: Any official version of the Raspberry Pi Pico will work (W, H, or WH). Third party RP2040 boards may work, but there is no guarantee - always double check the pinout**
| | 1 | | Raspberry Pi Pico | [The Pi Hut](https://thepihut.com/products/raspberry-pi-pico)<br>[CPC](https://cpc.farnell.com/raspberry-pi/raspberry-pi-pico/raspberry-pi-pico-rp2040-mcu-board/dp/SC17106)<br>[Mouser](https://www.mouser.co.uk/ProductDetail/Raspberry-Pi/SC0915?qs=T%252BzbugeAwjgnLi4azxXVFA%3D%3D)<br>The Raspberry Pi Pico 2 is also usable [with additional configuration](/software/CONFIGURATION.md)<br>**Note: Any official version of the Raspberry Pi Pico will work (W, H, or WH). Third party RP2040 or RP2350 boards may work, but there is no guarantee - always double check the pinout. RP2350 boards should be [configured like the Pico 2](/software/CONFIGURATION.md)**

#### Note about OLED
The OLED has two suppliers listed, each with different pin configurations. The module supports either of these two configurations (the most common), but no others, so make sure that the one you buy, wherever you source it, has one of these two configurations.
Expand Down
16 changes: 13 additions & 3 deletions software/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ default configuration:
{
"EUROPI_MODEL": "europi",
"PICO_MODEL": "pico",
"CPU_FREQ": 250000000,
"CPU_FREQ": "overclocked",
"ROTATE_DISPLAY": false,
"DISPLAY_WIDTH": 128,
"DISPLAY_HEIGHT": 32,
Expand All @@ -29,8 +29,15 @@ default configuration:

CPU & Pico options:
- `EUROPI_MODEL` specifies the type of EuroPi module. Currently only `"europi"` is supported. Default: `"europi"`
- `PICO_MODEL` must be one of `"pico"` or `"pico w"`. Default: `"pico"`
- `CPU_FREQ` must be one of `250000000` or `125000000`. Default: `"250000000"`
- `PICO_MODEL` must be one of
- `"pico"`,
- `"pico h"`,
- `"pico w"`,
- `"pico 2"`, or
- `"pico 2w"`.
Default: `"pico"`.
- `CPU_FREQ` specifies whether or not the CPU should be overclocked. Must be one of `"overclocked"` or `"normal"`.
roryjamesallen marked this conversation as resolved.
Show resolved Hide resolved
Default: `"overclocked"`

Display options:
- `ROTATE_DISPLAY` must be one of `false` or `true`. Default: `false`
Expand Down Expand Up @@ -59,6 +66,9 @@ Power options:
- `MENU_AFTER_POWER_ON` is a boolean indicating whether or not the module should always return to the main menu when
it powers on. By default the EuroPi will re-launch the last-used program instead of returning to the main menu. Default: `false`

If you assembled your module with the Raspberry Pi Pico 2 (or a clone featuring the RP2350 microcontroller) make sure to
set the `PICO_MODEL` setting to `"pico2"`.


# Experimental configuration

Expand Down
5 changes: 3 additions & 2 deletions software/contrib/diagnostic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
k1,
k2,
oled,
europi_config,
thermometer,
)
from europi_script import EuroPiScript
import configuration
Expand All @@ -40,7 +42,6 @@
class Diagnostic(EuroPiScript):
def __init__(self):
super().__init__()
self.temp_sensor = ADC(4)
self.voltages = [
0, # min
0.5, # not 0 but still below DI's threshold
Expand All @@ -58,7 +59,7 @@ def config_points(cls):

def calc_temp(self):
# see the pico's datasheet for the details of this calculation
t = 27 - ((self.temp_sensor.read_u16() * TEMP_CONV_FACTOR) - 0.706) / 0.001721
t = thermometer.read_temperature()
if self.use_fahrenheit:
t = (t * 1.8) + 32
return t
Expand Down
13 changes: 8 additions & 5 deletions software/contrib/lutra.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ def gui_render_thread(self):
"""A thread function that handles drawing the GUI
"""
SHOW_WAVE_TIMEOUT = 3000
while True:

# To prevent the module locking up when we connect the USB for e.g. debugging, kill this thread
# if the USB state changes. Otherwise the second core will continue being busy, which makes connecting
# to the Python terminal impossible
usb_connected_at_start = usb_connected.value()
while usb_connected.value() == usb_connected_at_start:
now = time.ticks_ms()
oled.fill(0)
with self.pixel_lock:
Expand All @@ -283,11 +288,9 @@ def gui_render_thread(self):
def wave_generation_thread(self):
"""A thread function that handles the underlying math of generating the waveforms
"""
usb_connected_at_start = usb_connected.value()

# To prevent the module locking up when we connect the USB for e.g. debugging, kill this thread
# if the USB state changes. Otherwise the second core will continue being busy, which makes connecting
# to the Python terminal impossible
# if the USB state changes
usb_connected_at_start = usb_connected.value()
while usb_connected.value() == usb_connected_at_start:
# Read the digital inputs
self.digital_input_state.update()
Expand Down
26 changes: 5 additions & 21 deletions software/firmware/calibrate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from machine import Pin, ADC, PWM, freq
from time import sleep
from europi import oled, b1, b2, PIN_USB_CONNECTED, PIN_CV1, PIN_AIN
from europi import oled, b1, b2, ain, cv1, usb_connected
from europi_script import EuroPiScript
from os import stat, mkdir

Expand All @@ -12,14 +11,10 @@ def display_name(cls):
return "~Calibrate"

def main(self):
ain = ADC(Pin(PIN_AIN, Pin.IN, Pin.PULL_DOWN))
cv1 = PWM(Pin(PIN_CV1))
usb = Pin(PIN_USB_CONNECTED, Pin.IN)

def sample():
readings = []
for reading in range(256):
readings.append(ain.read_u16())
readings.append(ain.pin.read_u16())
return round(sum(readings) / 256)

def wait_for_voltage(voltage):
Expand All @@ -38,17 +33,6 @@ def text_wait(text, wait):
oled.centre_text(text)
sleep(wait)

def fill_show(colour):
oled.fill(colour)
oled.show()

def flash(flashes, period):
for flash in range(flashes):
fill_show(1)
sleep(period / 2)
fill_show(0)
sleep(period / 2)

def wait_for_b1(value):
while b1.value() != value:
sleep(0.05)
Expand All @@ -62,7 +46,7 @@ def wait_for_b1(value):

# Calibration start

if usb.value() == 1:
if usb_connected.value() == 1:
oled.centre_text("Make sure rack\npower is on\nDone: Button 1")
wait_for_b1(1)
wait_for_b1(0)
Expand Down Expand Up @@ -109,11 +93,11 @@ def wait_for_b1(value):

output_duties = [0]
duty = 0
cv1.duty_u16(duty)
cv1.pin.duty_u16(duty)
reading = sample()
for index, expected_reading in enumerate(readings[1:]):
while abs(reading - expected_reading) > 0.002 and reading < expected_reading:
cv1.duty_u16(duty)
cv1.pin.duty_u16(duty)
duty += 10
reading = sample()
output_duties.append(duty)
Expand Down
78 changes: 71 additions & 7 deletions software/firmware/europi.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
from machine import PWM
from machine import Pin
from machine import freq

from machine import mem32

from ssd1306 import SSD1306_I2C

from version import __version__

from configuration import ConfigSettings
from framebuf import FrameBuffer, MONO_HLSB
from europi_config import load_europi_config
from europi_config import load_europi_config, CPU_FREQS
from experimental.experimental_config import load_experimental_config

if sys.implementation.name == "micropython":
Expand Down Expand Up @@ -107,7 +107,7 @@
PIN_CV4 = 17
PIN_CV5 = 18
PIN_CV6 = 19
PIN_USB_CONNECTED = 24
PIN_USB_CONNECTED = 24 # Does not work on Pico 2

# Helper functions.

Expand Down Expand Up @@ -622,6 +622,64 @@ def value(self, value):
self.off()


class Thermometer:
chrisib marked this conversation as resolved.
Show resolved Hide resolved
"""
Wrapper for the temperature sensor connected to Pin 4
Reports the module's current temperature in Celsius.
If the module's temperature sensor is not working correctly, the temperature will always be reported as None
"""

# Conversion factor for converting from the raw ADC reading to sensible units
# See Raspberry Pi Pico datasheet for details
TEMP_CONV_FACTOR = 3.3 / 65535

def __init__(self):
# The Raspberry Pi Pico 2's temperature sensor doesn't work with some older
# uPython firmwares so do some basic exception handling
try:
self.pin = ADC(PIN_TEMPERATURE)
except:
self.pin = None

def read_temperature(self):
"""
Read the ADC and return the current temperature
@return The current temperature in Celsius, or None if the hardware did not initialze properly
"""
if self.pin:
# See the Pico's datasheet for the details of this calculation
return 27 - ((self.pin.read_u16() * self.TEMP_CONV_FACTOR) - 0.706) / 0.001721
else:
return None


class UsbConnection:
"""
Checks the USB terminal is connected or not
On the original Pico we can check Pin 24, but on the Pico 2 this does not work. In that case
check the SIE_STATUS register and check bit 16
"""

def __init__(self):
if europi_config.PICO_MODEL == "pico2":
self.pin = None
else:
self.pin = DigitalReader(PIN_USB_CONNECTED)

def value(self):
"""Return 0 or 1, indicating if the USB connection is disconnected or connected"""
if self.pin:
return self.pin.value()
else:
# see https://forum.micropython.org/viewtopic.php?t=10814#p59545
SIE_STATUS = 0x50110000 + 0x50
BIT_CONNECTED = 1 << 16
if mem32[SIE_STATUS] & BIT_CONNECTED:
return 1
else:
return 0


# Define all the I/O using the appropriate class and with the pins used
din = DigitalInput(PIN_DIN)
ain = AnalogueInput(PIN_AIN)
Expand All @@ -639,6 +697,12 @@ def value(self, value):
cv6 = Output(PIN_CV6)
cvs = [cv1, cv2, cv3, cv4, cv5, cv6]

# Helper object to detect if the USB cable is connected or not
usb_connected = UsbConnection()

# Helper object for reading the onboard temperature sensor
thermometer = Thermometer()

# External I2C
external_i2c = I2C(
europi_config.EXTERNAL_I2C_CHANNEL,
Expand All @@ -648,10 +712,10 @@ def value(self, value):
timeout=europi_config.EXTERNAL_I2C_TIMEOUT,
)

usb_connected = DigitalReader(PIN_USB_CONNECTED, 0)

# Overclock the Pico for improved performance.
freq(europi_config.CPU_FREQ)
# Set the desired clock speed according to the configuration
# By default this will overclock the CPU, but some users may not want to
# e.g. to lower power consumption on a very power-constrained system
freq(CPU_FREQS[europi_config.PICO_MODEL][europi_config.CPU_FREQ])

# Reset the module state upon import.
reset_state()
Loading
Loading