home.social

Search

406 results for “circuitpython”

  1. All - in case you didn't see, @adafruit released #CircuiPython 8.1.0. This version fully supports the Fig Pi boards, as well as Mini SAM M4. Upgrading the firmware is as easy as booting into BOOT mode, and dragging the new firmware from circuitpython.org/board/bwshoc onto the drive that shows up. That's it!

  2. Arduino and SP0256A-AL2 – Part 3

    Following on from using an Arduino as a variable clock in Arduino and SP0256A-AL2 – Part 2, I have some ideas for a few options, but this post looks in detail at using a Raspberry Pi Pico as the clock source.

    Spoilers: it kind of works, but isn’t quite the answer I need yet…

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/bxBYCqHrZvQLwLuwYa5Z9r

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    Using a RPi Pico

    The RP2040 can be overclocked quite a bit, so generating a variable square wave up in the few MHz range should presumably be relatively straight forward. Using the built-in PIO state machines for a square wave is fairly simple and it can be done from Circuitpython or Micropython too.

    This is a complete square wave generator for GP2 that steps down from 4MHz to 2MHz in steps of 100kHz. It can optionally overclock the RPi to 250 MHz too if required.

    import time
    import microcontroller
    import board
    import rp2pio
    import adafruit_pioasm

    square = adafruit_pioasm.assemble ("""
    .program square
    set pins, 1
    set pins, 0
    """)

    RP2040Freq = 125_000_000
    #RP2040Freq = 250_000_000

    print ("RP2040 Frequency = ", microcontroller.cpu.frequency)
    microcontroller.cpu.frequency = RP2040Freq
    time.sleep(1)
    print ("New RP2040 Frequency = ", microcontroller.cpu.frequency)

    while True:
    for freq in range (4000000, 2000000, -100000):
    print("\nReqd frequency = ", freq*2)
    print("Sq frequency = ", freq)

    sm = rp2pio.StateMachine(
    square,
    frequency=freq*2,
    first_set_pin=board.GP2,
    )
    print("Actual freq = {:d}".format(sm.frequency))
    print("Actual sq freq = {:d}".format(int(sm.frequency/2)))

    time.sleep(5)
    sm.deinit()

    The PIO program itself has two instruction steps, so takes two cycles to complete, so the running frequency has to be twice the desired frequency of the square wave. It automatically keeps looping, so no additional instructions are required there.

    The state machine will run at the system speed with a 16.8 fixed point fractional clock divider. Full details can be found in section 3.5.5 “Clock Dividers” in the RP2040 datasheet.

    For certain values there might be some jitter:

    If the system clock is faster though, the amount of jitter will be less I suspect, so it is advantageous to overclock the Pico for more accurate frequencies.

    The problem with this approach is that whilst I get a nice accurate clock source with quite a good resolution across its range, every time the state machine is recreated to change the frequency, there is a “blip” in the audio from the SP0256A-AL2 whilst its clock temporarily disappears!

    An alternative approach is to use a fixed state machine frequency but include a counter in the PIO program to allow for a configurable number of steps per scan of the PIO without having to stop and restart the clock.

    The problem with this is that I am limited to a unit of the instruction time for the PIO state machine which gives a fixed overhead, in terms of the instructions required for a minimal loop, and a flexible overhead, in terms of the counter I can pass in.

    The upshot of this is that I’m tied to a certain resolution of frequency change.

    I have the following PIO code:

    .program blink
    .side_set 1
    .wrap_target
    pull noblock
    mov x, osr
    mov y, x
    set pins, 1
    lp1:
    jmp y-- lp1
    nop
    nop
    mov y, x
    set pins, 0
    lp2:
    jmp y-- lp2
    .wrap

    The “pull” will update the output shift register (OSR) either with any new value written to the state machine or the last value of the X register. This value gets copied to Y to use as a counter. This happens twice, once for when the pin is set at 1 and once for when the pin is set at 0.

    There are two nops whilst the pin is set at 1 to balance for the original pull and mov instructions at the end of the pin set to 0 cycle.

    As the Y counter value is used twice, the flexible overhead of the timing is essentially proportional to count * 2. It counts for the pin being HIGH and then for the pin being LOW.

    The fixed overhead is the cost of the original pull, two moves, the pin sets, and a single jmp per pin state – so by using the two nops to ensure the HIGH and LOW times are the same, that is 10 instruction cycles.

    I was hoping to use the “side” instruction to eliminate the two set instructions, but so far I’ve not managed to get that to work. I still don’t understand PIO…

    So for now the timing of the PIO routine is = 10 + 2 * count and the unit is the time for a single instruction, which is 1 / frequency of the PIO execution, up to a maximum frequency of the Pico’s system clock frequency.

    Using an overclocked Pico at 250MHz, the frequency range would start at the following:

    • Execution freq = Pico Sys Clock / (10 + 2 * count)
    • So when count = 0; execution freq = 250MHZ / 10 = 25 MHz

    That is far too fast for the SP0256A-AL5. In fact, I’ve found that anything over around 5MHz causes the chip problems.

    For this reason, I’m using a minimum count of 20:

    • Max execution freq = 250MHz / (10 + 2 * 20) = 5 MHz

    Plotting execution frequency per “count” value (starting from 20) gives the following:

    We can see the limits of the resolution at the top-end, and in fact, the first few equivalent frequencies in that range are as follows:

    CountEquivalent Frequency205,000,000214,807,692224,629,629234,464,285244,310,344

    That is giving me something like a 150-200kHz jump each time, which isn’t great, but is probably the best I can do. I would be larger if I wasn’t overclocking the Pico. It does get smaller as the count increases, but it is only really worth going down to a count value of around 120, which is around 1MHz for the resulting clock. Anything lower than that and the SP0256A-AL2 isn’t particularly useful.

    Here is the full Circuitpython code which attaches a pot to GP26 to control the frequency in the range of around 900kHz up to 5MHz. Note the scaling of the pot value (0 to 65535) by 600 prior to its use to add to the count.

    import array
    import time
    import board
    import rp2pio
    import microcontroller
    import adafruit_pioasm

    from analogio import AnalogIn
    algin = AnalogIn(board.GP26) # ADC0

    blink = adafruit_pioasm.assemble(
    """
    .program blink
    .side_set 1
    .wrap_target
    pull noblock
    mov x, osr
    mov y, x
    set pins, 1
    lp1:
    jmp y-- lp1
    nop
    nop
    mov y, x
    set pins, 0
    lp2:
    jmp y-- lp2
    .wrap

    """
    )

    RP2040Freq = 250_000_000

    microcontroller.cpu.frequency = RP2040Freq
    time.sleep(1)

    oldalgval = 0

    sm = rp2pio.StateMachine(
    blink,
    frequency=RP2040Freq,
    first_set_pin=board.GP2
    )
    sm.write(bytes(16))

    while True:
    algval = algin.value
    if (algval != oldalgval):
    oldalgval = algval
    count = 20 + int(algval / 600)
    freq = int (RP2040Freq / (10 + count*2))
    data = array.array("I", [count])
    sm.write(data)

    time.sleep(0.2)

    One problem will be the 3V3 operating levels of the Pico. The SP0256A-AL2 datasheet states the following:

    So whilst a “high logic” value for the oscillator has a minimum level of 2.5V, it also states that a minimum of 3.9V is required if driven from an external source.

    If required, something like a 74HCT14, powered by 5V, can be used to level shift the 3V3 output of the Pico to a 5V signal for use with the SP0256A-AL2.

    But in practice, I was finding the Pico worked fine as is. It is important to ensure both the Pico, Arduino and SP0256A-AL2 all have their grounds connected.

    A this point I’m just using the Pico as a programmable clock, but if I was to go this route, then it would make sense to have the Pico drive the SP0256A-AL2 too and forgo the Arduino.

    Closing Thoughts

    So I have two choices if I want to use a Raspberry Pi Pico:

    • Go for smooth changes of frequency, but with less resolution, especially at the higher frequencies.
    • Go for more accurate resolution across the range but accept there will be blips when the clock changes which will be heard in the audio.

    Neither is a perfect solution, but it shows the principles are valid. Also, using two microcontrollers is a bit over the top, so if I was to move to using a Pico, I’d probably want to find a way to drive the SP0256A from the Pico directly too and skip using an Arduino.

    One benefit of that would be that I can time the frequency changes to coincide with silence in the speaking should I wish to, avoiding the possibility of major audio blips.

    But I also have a few other options to try, which I’ll come back to in a future post.

    Kevin

    #arduinoUno #pio #raspberryPiPico #sp0256aAl2

  3. Small Microcontroller Displays

    I found myself wanting several small displays connected to a microcontroller, so was doing a bit of a trade-off between various options, and was starting to lose track, so this post collects some of those thoughts together.

    It is not meant to be a comprehensive discussion of choosing small displays for projects, but more of a reflection on the displays I already have kicking around in my parts boxes!

    Most displays tend to be categorised by the driver chip they use. And then by the bus type used for their connection. So that is how I’ve grouped them here.

    A really good reference for many of the displays shown here is: https://www.lcdwiki.com/Main_Page

    I2C SSD1306 OLED

    This is usually my “go to” set of small displays. They are generally well supported and pretty easy to use.

    I generally have two variants to choose from:

    On the left is the 0.91″ 128×32 OLED SSD1306 and on the right is the 0.96″ 128×54 OLED SSD1306. Common properties of both displays:

    • Monochrome – white or blue.
    • I2C interface.
    • Usually 5V powered, but some include level shifters to allow 3V3 logic.
    • Usually includes I2C pull-ups. Might be to VCC level so be wary if using a 3V3 MCU but powering from 5V – always check the voltage level prior to connecting.
    • 128×32 usually a fixed I2C address (0x3C). 128×64 usually allows selection between 0x3C and 0x3D.
    • There are variants with VCC and GND swapped, and other variants with SDA and SCL swapped.

    Software support:

    Typical Connections:

    • VCC/GND (see note below re VCC vs logic levels).
    • SCL/SDA – I2C pins on the MCU.

    Known “gotchas”:

    • Requires a chunk of memory allocated on start-up on Arduino, which can fail if there isn’t enough dynamic memory left and make a sketch hang.
    • Can’t be written to from an interrupt routine (e.g. a timer).
    • Low-level I2C Wire library on Arduino is blocking.
    • Can sometimes be a little slow compared to alternatives.
    • Limited I2C address options, so multiple display use is limited (and also increase the memory issues).
    • As already mentioned, any I2C pull-ups may be pulled up to the VCC level or might be level shifted, so it is always worth checking if planning to use with a 3V3 microcontroller.

    Summary:

    • Cheap, pretty easy to use, and fairly universal if you want a single, small, monochrome display for simple outputs.

    Other I2C Variants

    There are some variants of the SSD1306 that sometimes pops up too for slightly larger displays:

    • SSD1315 – apparently can simply be treated as a SSD1306 and mostly it works ok.
    • SH1106 – very similar niche to SSD1306 but requires it owns driver support.

    SPI ST7735/89/96 TFT

    Whereas the SSD1306 I2C is pretty ubiquitous for monochrome displays, I’ve tended to find that SPI ST77xx displays fill a similar niche for small, full colour, non-touch, TFT displays. And there are loads of variations on the theme when it comes to these displays.

    The 7735 supports lower resolution, smaller displays, typically up to 170×320, with the 7789 for those of 240×240 or 240×320 and similar. There is also a ST7796 which I believe uses the same driver libraries for a higher 320×480 display.

    Two 7735 Displays:

    These two ST7735 displays that I have are labelled:

    • TFT 0.96″ 80×160 SPI ST7735
    • TFT 1.8″ 128×160 SPI ST7735

    These ST7789 display I have is labelled:

    • TFT 1.3″ 240×240 SPI ST7789

    I also have a display that was bought as a ST7789 labelled “TFT 2.8″ 240×320 SPI” which comes with a touch screen, but I can’t get this to work.

    Common properties:

    • SPI interface: data (SDA/MOSI/COPI), clock (SCK/SCLK), chip select (CS), data/command (SR/DC), possibly a reset.
    • Typically 65536 colours, usually encoded as 5-6-5 bit RGB patterns.
    • Have to check for 3V3 or 5V operation depending on the datasheet of the driver chip and design of the module.

    Software support:

    Typical Connections:

    • VCC/GND
    • CLK/SDA – SPI Clock and Data (Data OUT from MCU – i.e. MOSI/COPI)
    • RES – Reset
    • DC, RS – Data/Command Register Select
    • CS – Chip Select

    Known Gotchas:

    • Working out if RS means reset of the data/command pin; and not mixing up SCL/SDA with I2C!
    • Some might include a backlight control pin too for dimming or turning it off. With this not connected the display was a maximum brightness.
    • I’ve also seen talk that many of these modules themselves run at 3V3, so whilst they may include a regulator for 5V to 3V3 for power, they don’t always include level shifting for the signal pins. It seems unclear (to me at least at the moment) if it is ok to use these with a 5V microcontroller (although I have done…).
    • Some of these displays are “inverted” colour wise. The use 16-bit 5-6-5 format colours, but some are RGB, some are BGR and there might be other variants too.
    • Sometimes the initialiser for the library requires a SPI_MODE setting. My ST7789 240×240 required the Adafruit ST7789 initialisation as follows:
    #include <Adafruit_GFX.h>
    #include <Adafruit_ST7789.h>
    #include <SPI.h>

    #define TFT_CS 10 // (not used)
    #define TFT_RST 9
    #define TFT_DC 8

    Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

    void setup() {
    tft.init(240, 240, SPI_MODE2);
    }

    Note, unlike the monochrome displays, these have their own pixel framebuffer so memory use is much more efficient, even when used as a full colour display.

    Summary:

    • Cheap, pretty easy to use once the voltage levels and the pin labelling are worked out. Well supported by a number of libraries; but does require more pins for 4-wire SPI. Might need some messing around to get the right colour definitions. Otherwise a good choice if you just want a cheap, colour display with no touch.

    OLED SH1122 SPI

    There are actually both I2C and SPI versions of SH1122 displays, but I’m considering the SPI version here. The display I have is a 256×64, monochrome OLED display.

    Software Support:

    Example:

    U8G2_SH1122_256X64_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

    Typical Connections:

    • VCC/GND – mine states it can support either 3V or 5V power
    • CLK/SDA – SPI Clock and Data (Data OUT from MCU – i.e. MOSI/COPI)
    • RES – Reset
    • DC, RS – Data/Command Register Select
    • CS – Chip Select

    The _F_ constructor requires a full frame-buffer so is unlikely to work on resource constrained devices (e.g. the Arduino Uno/Nano). The _1_ constructor provides a pageable interface which allows for the updating of the display in pages, which takes longer but allows it to be used with more devices.

    There are also software SPI versions that allow the use of any GPIO pins.

    Summary:

    • A pretty neat board if these physical dimensions match what is required.
    • There are a whole pile of example sketches in the U8G2/page_buffer directory.

    ILI9341/9488 TFT

    Cheap larger displays are often driven by one of the ILI9341 or ILI9488 chips. The former supports 240×320 in full (16-bit) colour whereas the 9488 tends to support larger displays of up to 320×480 in full (24-bit) colour. Both support either a parallel (at least 8, 9, 16-bit) or serial (3 or 4 wire SPI) bus interface.

    I don’t have a lot of detailed information for this post yet, but instead will refer to:

    The ILI9341 is well supported by the Adafruit graphic libraries, but the ILI9488 is likely to require something else, as described in the above post.

    These displays are often used with touch support and will often expect to run at 3V3 logic levels.

    LCD 1602 HD44780

    This is another very common monochrome, but text only, display. They have a 4 or 8-bit parallel interface, but it is also quite common to use the with an I2C “backpack” based on the PCF8574 I2C IO expander. Boards can be cheaply bought with or without a backpack, and the backpacks are available separately too for retro fitting to displays without them.

    They are often called “1602” displays as they are two rows of 16 characters. By using custom blocks it is possible to have some simple graphics. There are LCD2004 modules too with four rows of 20 characters.

    They often come with a choice of backlight colours. White or red are particularly striking! There are some variants that come with an additional I2C controller chip built in to control the backlight and some even come with a full RGB backlight capability.

    Typical Connections:

    • VCC/GND
    • Data – either 4 or 8 bit modes support
    • E, RW, RS – enable, read/write, register select.
    • Backlight V+/GND – level is often fixed using a resistor.

    There are core Arduino libraries to support the most basic versions of these displays:

    • LiquidCrystal
    • LiquidCrystal_I2C

    Typical Gotchas:

    • The I2C backpacks often include pull-ups to VCC, yet many of these displays require 5V even if used with a 3V3 microcontroller. One option is to remove the pull-ups and add external pull-ups to 3V3.
    • If there are only blocks on the display then the communications isn’t working properly – check SDA/SCL or the control lines.
    • If there is nothing on the display or the text is obscured by blocks behind it, then the contrast is either too low or too high. I2C backpacks have a potentiometer to adjust the contrast.
    • More complex versions require an additional I2C setup phase, e.g. to turn on the backlight, which isn’t supported by the standard libraries.

    Summary:

    • Very useful if a large, high/adjustable contrast, text-only display is required.

    Others

    So I don’t forget when considering the above, I also have:

    I’ll add to the list for my own reference as I remember other odds and ends.

    Kevin

    #hd44780 #ili9341 #ili9488 #include #lcd1602 #oled #ssd1306 #st7735 #st7789 #tft

  4. Small Microcontroller Displays

    I found myself wanting several small displays connected to a microcontroller, so was doing a bit of a trade-off between various options, and was starting to lose track, so this post collects some of those thoughts together.

    It is not meant to be a comprehensive discussion of choosing small displays for projects, but more of a reflection on the displays I already have kicking around in my parts boxes!

    Most displays tend to be categorised by the driver chip they use. And then by the bus type used for their connection. So that is how I’ve grouped them here.

    A really good reference for many of the displays shown here is: https://www.lcdwiki.com/Main_Page

    I2C SSD1306 OLED

    This is usually my “go to” set of small displays. They are generally well supported and pretty easy to use.

    I generally have two variants to choose from:

    On the left is the 0.91″ 128×32 OLED SSD1306 and on the right is the 0.96″ 128×54 OLED SSD1306. Common properties of both displays:

    • Monochrome – white or blue.
    • I2C interface.
    • Usually 5V powered, but some include level shifters to allow 3V3 logic.
    • Usually includes I2C pull-ups. Might be to VCC level so be wary if using a 3V3 MCU but powering from 5V – always check the voltage level prior to connecting.
    • 128×32 usually a fixed I2C address (0x3C). 128×64 usually allows selection between 0x3C and 0x3D.
    • There are variants with VCC and GND swapped, and other variants with SDA and SCL swapped.

    Software support:

    Typical Connections:

    • VCC/GND (see note below re VCC vs logic levels).
    • SCL/SDA – I2C pins on the MCU.

    Known “gotchas”:

    • Requires a chunk of memory allocated on start-up on Arduino, which can fail if there isn’t enough dynamic memory left and make a sketch hang.
    • Can’t be written to from an interrupt routine (e.g. a timer).
    • Low-level I2C Wire library on Arduino is blocking.
    • Can sometimes be a little slow compared to alternatives.
    • Limited I2C address options, so multiple display use is limited (and also increase the memory issues).
    • As already mentioned, any I2C pull-ups may be pulled up to the VCC level or might be level shifted, so it is always worth checking if planning to use with a 3V3 microcontroller.

    Summary:

    • Cheap, pretty easy to use, and fairly universal if you want a single, small, monochrome display for simple outputs.

    Other I2C Variants

    There are some variants of the SSD1306 that sometimes pops up too for slightly larger displays:

    • SSD1315 – apparently can simply be treated as a SSD1306 and mostly it works ok.
    • SH1106 – very similar niche to SSD1306 but requires it owns driver support.

    SPI ST7735/89/96 TFT

    Whereas the SSD1306 I2C is pretty ubiquitous for monochrome displays, I’ve tended to find that SPI ST77xx displays fill a similar niche for small, full colour, non-touch, TFT displays. And there are loads of variations on the theme when it comes to these displays.

    The 7735 supports lower resolution, smaller displays, typically up to 170×320, with the 7789 for those of 240×240 or 240×320 and similar. There is also a ST7796 which I believe uses the same driver libraries for a higher 320×480 display.

    Two 7735 Displays:

    These two ST7735 displays that I have are labelled:

    • TFT 0.96″ 80×160 SPI ST7735
    • TFT 1.8″ 128×160 SPI ST7735

    These ST7789 display I have is labelled:

    • TFT 1.3″ 240×240 SPI ST7789

    I also have a display that was bought as a ST7789 labelled “TFT 2.8″ 240×320 SPI” which comes with a touch screen, but I can’t get this to work.

    Common properties:

    • SPI interface: data (SDA/MOSI/COPI), clock (SCK/SCLK), chip select (CS), data/command (SR/DC), possibly a reset.
    • Typically 65536 colours, usually encoded as 5-6-5 bit RGB patterns.
    • Have to check for 3V3 or 5V operation depending on the datasheet of the driver chip and design of the module.

    Software support:

    Typical Connections:

    • VCC/GND
    • CLK/SDA – SPI Clock and Data (Data OUT from MCU – i.e. MOSI/COPI)
    • RES – Reset
    • DC, RS – Data/Command Register Select
    • CS – Chip Select

    Known Gotchas:

    • Working out if RS means reset of the data/command pin; and not mixing up SCL/SDA with I2C!
    • Some might include a backlight control pin too for dimming or turning it off. With this not connected the display was a maximum brightness.
    • I’ve also seen talk that many of these modules themselves run at 3V3, so whilst they may include a regulator for 5V to 3V3 for power, they don’t always include level shifting for the signal pins. It seems unclear (to me at least at the moment) if it is ok to use these with a 5V microcontroller (although I have done…).
    • Some of these displays are “inverted” colour wise. The use 16-bit 5-6-5 format colours, but some are RGB, some are BGR and there might be other variants too.
    • Sometimes the initialiser for the library requires a SPI_MODE setting. My ST7789 240×240 required the Adafruit ST7789 initialisation as follows:
    #include <Adafruit_GFX.h>
    #include <Adafruit_ST7789.h>
    #include <SPI.h>

    #define TFT_CS 10 // (not used)
    #define TFT_RST 9
    #define TFT_DC 8

    Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

    void setup() {
    tft.init(240, 240, SPI_MODE2);
    }

    Note, unlike the monochrome displays, these have their own pixel framebuffer so memory use is much more efficient, even when used as a full colour display.

    Summary:

    • Cheap, pretty easy to use once the voltage levels and the pin labelling are worked out. Well supported by a number of libraries; but does require more pins for 4-wire SPI. Might need some messing around to get the right colour definitions. Otherwise a good choice if you just want a cheap, colour display with no touch.

    OLED SH1122 SPI

    There are actually both I2C and SPI versions of SH1122 displays, but I’m considering the SPI version here. The display I have is a 256×64, monochrome OLED display.

    Software Support:

    Example:

    U8G2_SH1122_256X64_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

    Typical Connections:

    • VCC/GND – mine states it can support either 3V or 5V power
    • CLK/SDA – SPI Clock and Data (Data OUT from MCU – i.e. MOSI/COPI)
    • RES – Reset
    • DC, RS – Data/Command Register Select
    • CS – Chip Select

    The _F_ constructor requires a full frame-buffer so is unlikely to work on resource constrained devices (e.g. the Arduino Uno/Nano). The _1_ constructor provides a pageable interface which allows for the updating of the display in pages, which takes longer but allows it to be used with more devices.

    There are also software SPI versions that allow the use of any GPIO pins.

    Summary:

    • A pretty neat board if these physical dimensions match what is required.
    • There are a whole pile of example sketches in the U8G2/page_buffer directory.

    ILI9341/9488 TFT

    Cheap larger displays are often driven by one of the ILI9341 or ILI9488 chips. The former supports 240×320 in full (16-bit) colour whereas the 9488 tends to support larger displays of up to 320×480 in full (24-bit) colour. Both support either a parallel (at least 8, 9, 16-bit) or serial (3 or 4 wire SPI) bus interface.

    I don’t have a lot of detailed information for this post yet, but instead will refer to:

    The ILI9341 is well supported by the Adafruit graphic libraries, but the ILI9488 is likely to require something else, as described in the above post.

    These displays are often used with touch support and will often expect to run at 3V3 logic levels.

    LCD 1602 HD44780

    This is another very common monochrome, but text only, display. They have a 4 or 8-bit parallel interface, but it is also quite common to use the with an I2C “backpack” based on the PCF8574 I2C IO expander. Boards can be cheaply bought with or without a backpack, and the backpacks are available separately too for retro fitting to displays without them.

    They are often called “1602” displays as they are two rows of 16 characters. By using custom blocks it is possible to have some simple graphics. There are LCD2004 modules too with four rows of 20 characters.

    They often come with a choice of backlight colours. White or red are particularly striking! There are some variants that come with an additional I2C controller chip built in to control the backlight and some even come with a full RGB backlight capability.

    Typical Connections:

    • VCC/GND
    • Data – either 4 or 8 bit modes support
    • E, RW, RS – enable, read/write, register select.
    • Backlight V+/GND – level is often fixed using a resistor.

    There are core Arduino libraries to support the most basic versions of these displays:

    • LiquidCrystal
    • LiquidCrystal_I2C

    Typical Gotchas:

    • The I2C backpacks often include pull-ups to VCC, yet many of these displays require 5V even if used with a 3V3 microcontroller. One option is to remove the pull-ups and add external pull-ups to 3V3.
    • If there are only blocks on the display then the communications isn’t working properly – check SDA/SCL or the control lines.
    • If there is nothing on the display or the text is obscured by blocks behind it, then the contrast is either too low or too high. I2C backpacks have a potentiometer to adjust the contrast.
    • More complex versions require an additional I2C setup phase, e.g. to turn on the backlight, which isn’t supported by the standard libraries.

    Summary:

    • Very useful if a large, high/adjustable contrast, text-only display is required.

    Others

    So I don’t forget when considering the above, I also have:

    I’ll add to the list for my own reference as I remember other odds and ends.

    Kevin

    #hd44780 #ili9341 #ili9488 #include #lcd1602 #oled #ssd1306 #st7735 #st7789 #tft

  5. Welcome to KAMPi! My Self-Built Digital Camera

    Hello everyone! I have always wanted to build my own digital camera. But not just any digital camera, one I would actually use like a regular point-and-shoot camera. I wanted something that would give me traditional feel, but still shoot in 4K. A tall order for sure, and to top it all off, I wanted to build everything myself. Well the time has come. . . Please join me in welcoming KAMPi! Check out my build below.

    Why KAMPi?

    KAMPi is short for “Kampay” which is Tagalog slang for “Kanpai” the Japanese word for cheers. It also sounds like CAMPi another way of saying Pi Cam, which is exactly what it is – a digital camera built using a Raspberry Pi mini computer.

    What’s Inside?

    I built KAMPi from the ground up: selected, wired, and soldered the hardware electronics, 3D printed the parts, and prepared the software.

    For the computer internals, I chose to use a Raspberry Pi Compute Module 5 which rides on a Waveshare Nano A. The camera imager and lens are from Raspberry Pi as well, the Raspberry Pi High Quality Camera with the 16mm Telephoto Lens. For the display I used a Pimoroni HyperPixel 4.0″ Hi-Res Display. The display is connected to the Waveshare Nano A via GPIO pins (the header of the Nano I soldered myself). Everything is packed together in a tight package shown above.

    For the power electronics I chose to use a Pimoroni LiPo SHIM I purchased from Adafruit. I wired it up with a on/off switch and connected it to a USB-C plug.

    For the trigger mechanism I chose to use Adafruit’s KB 2040 electronic board due to it’s small form factor, and also that it works over USB-C / USB serial. I also chose to use the KB 2040 because the HyperPixel 4.0 uses all the GPIO pins of the Waveshare Nano A and USB-C / USB serial seemed like a more straightforward alternative. Alternatively I could have used I2C, via the HyperPixel 4.0, but I didn’t have enough time to go that direction before the convention. The trigger itself is an illuminated pushbutton switch also from Adafruit.

    I’m no expert in 3D printing and I originally wanted the form factor to be smaller. But since the camera cable stuck out from the top, I needed to make extend the base.

    So I designed and 3D printed a cap portion to hold it all together.

    I even added a hinge and latch lock to for easy access.

    For the software, I wanted something simple. I’m running python script to take the photos. One thing to note is that the camera does not have any autofocus (which is exactly how I wanted it). That meant I needed to see what I’m shooting before taking the photo. I added a preview in the software, so I could focus the lens, and then take the shot.

    The desktop above shows the camera python script and the folder where the photos are saved. You can also see the Circuitpython mounted “disk” of the KB 2040 on the desktop. I’m also a space nerd so I chose a James Webb galaxy image as a backdrop to show off the beautiful Pimironi display. I included a fun logo and added a nice rectangle so I could easily see the program icons on the desktop too.

    KAMPi in Action

    KAMPi is so new, I haven’t been able to test it in the wild yet. But here are some raw unprocessed photos from my home test shots.

    https://www.instagram.com/reel/DMR6OegOWSn/?utm_source=ig_web_copy_link&igsh=MzRlODBiNWFlZA==

    More to come from Opensaucelive!*

    Tomorrow is Opensaucelive 2025 and I thought what better place to test and share my build there. Wow, I’m so excited to share KAMPi with everyone at Opensauce. If you see me, please do say hello. I’ll also upload some photos from KAMPi at Opensauce below:

    [*UPDATED July 20, 2025] See above sample photos I took at Opensaucelive 2025 using KAMPi. I chose the sharpest in focus images to share. Since it was my first time shooting with it, many of the photos came out blurry – which was exactly what I was expecting! I wanted KAMPi to emulate the feel of a film camera, capturing the moment. And KAMPi did just that. I’m also sure I’ll get better at taking photos with KAMPi with a litter more practice 🙂

    [*UPDATED Sept 20, 2025] Updated the description of the KB 2040 to provide additional info on why I decided to use it over I2C.

    Kampay (Cheers) for now!

    Did you like my build? Would you like to learn more about it? Let me know at the comments below!

    If you enjoyed reading this post please be sure to like, and follow us here at SKKAW.BLOG (IG: @skkaw) for more geek and pop-culture goodness.

    #Adafruit #camera #CamPi #ComputeModule5 #digitalCamera #DIY #DIYCamera #DIYDigitalCamera #KAMPi #Opensauce #OpenSauceLive #PiCam #RaspberryPi #RaspberryPiCamera #RaspberryPiComputeModule5 #RaspberryPiComputeModule5Camera #SelfMade #selfMadeDigitalCamera #selfBuilt #SelfMade #Waveshare

  6. Forbidden Planet “Krell” Display PCB Build Guide

    Here are the build notes for my Forbidden Planet “Krell” Display PCB. This post just looks at building the PCB for standalone use.

    Further posts will explore other uses for this PCB:

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers and electronics, see the Getting Started pages.

    Bill of Materials

    • Forbidden Planet “Krell” Display PCB (GitHub link below)
    • Waveshare Zero format board (more here).
    • 10x APA-106 through-hole programmable RGB LEDs pinout: IN-VCC-GND-OUT.
    • 1x 500uF electrolytic capacitor (or thereabouts).
    • 1x 47uF electrolytic capacitor.
    • Optional: 2x 9-way header sockets (full or low-profile – see notes).
    • Pin headers

    For the MIDI circuit:

    • 1x H11L1 optoisolator.
    • 1x 1N4148 or 1N914 signal diode.
    • Resistors: 1×10Ω, 1×33Ω, 1×220Ω, 1×470Ω.
    • 1x 100nF ceramic capacitor.
    • 2x 3.5mm stereo TRS sockets – pcb mount (see photo and PCB for footprint).
    • Optional: 1x 6-way DIP socket.

    For potentiometer circuit:

    • 1 or 2 x 10K pcb-mount potentiometer (see photo and PCB for footprint).
    • 1 or 2x 100nF ceramic capactiors.

    For the CV input:

    • 1x Thonkiconn style mono PCB mount jack socket.
    • Resistors: 1x22K, 1x33K.
    • 2x BAT43 Schottky diodes.

    Build Steps

    This posts describes a standalone module with two potentiometer controls and a MIDI circuit. For a EuroRack-style module with CV inputs refer to: Forbidden Planet “Krell” Display EuroRack Module.

    Taking a typical “low to high” soldering approach, this is the suggested order of assembly:

    • All diodes and resistors.
    • DIP socket (if used) and TRS sockets.
    • Disc capacitors.
    • LEDs on rear of the board.
    • 9-way headers (if used).
    • Additional pin headers (if used).
    • Electrolytic capacitors.
    • Potentiometers on rear of the board.

    Here are some build photos.

    When it comes to adding the LEDs it is critical to get them in the correct pin order. These boards are designed for LEDs with two long and two shorter legs, with the pins in the order:

    • Short: IN
    • Short: VCC
    • Long: GND
    • Long: OUT

    The pins need to be slightly bent to fit in the staggered footprint which means it isn’t possible to push the LEDs flush with the PCB. It is worth taking a little care to get them all to approximately the same height and vertically aligned.

    Hopefully it goes without saying to be careful of rubbing the hot soldering iron tip on any of the existing plastic components.

    As the footprint for the Waveshare Zero is 2.54mm too wide, it is advantageous to use a Waveshare Zero format board to help angle-in the pin headers prior to soldering.

    If using full height headers there will probably be enough flex to do this afterwards. If using low-profile headers then it will be necessary to get the angle correct prior to soldering.

    In the following note how the large capacitor has been bent over to lie flat.

    Also, I didn’t have a 500uF or higher, so used a 470uF in a 10mm diameter package.

    Testing

    I recommend performing the general tests described here: PCBs.

    Here is some test CircuitPython code that will check the functionality of the board with a Waveshare Zero type device. This was used with a Pimoroni Tiny2040 (which has two less pins to the Waveshare Zero devices).

    Analog Input

    This tests the potentiometers:

    import time
    import board
    from analogio import AnalogIn

    analog_in1 = AnalogIn(board.A2)
    analog_in2 = AnalogIn(board.A3)

    while True:
    print(analog_in1.value,"\t",analog_in2.value)
    time.sleep(0.1)

    On turning each of the potentiometers a value between 0 and 65536 should be printed to the serial console. Note: Mine never seems to get below 256…

    LEDs

    This can be used to test the LEDs. Requires the following libraries from the Adafruit Circuitpython Library Bundle:

    • neopixel.mpy
    • adafruit_pioasm.mpy (presumably only required on RP2040 based boards)
    • adafruit_pixelbuf.mpy
    import time
    import board
    import neopixel

    pixel_pin1 = board.GP2
    pixel_pin2 = board.GP3
    num_pixels = 5

    pixels1 = neopixel.NeoPixel(pixel_pin1, num_pixels, brightness=0.3, auto_write=False, pixel_order=neopixel.RGB)
    pixels2 = neopixel.NeoPixel(pixel_pin2, num_pixels, brightness=0.3, auto_write=False, pixel_order=neopixel.RGB)

    while True:
    for col in [(255,0,0),(0,255,0),(0,0,255),(0,0,0)]:
    for pix in range(5):
    pixels1[pix] = col
    pixels1.show()
    time.sleep(0.5)

    pixels2[pix] = col
    pixels2.show()
    time.sleep(0.5)

    time.sleep(3)

    This will light each LED in turn alternating between the upper and lower sets of LEDs and then leave them off for three seconds.

    MIDI IN and OUT

    This requires the Adafruit MIDI library, which requires the following directory from the Adafruit Circuitpython Library Bundle:

    • adafruit_midi/*
    import board
    import digitalio
    import busio
    import adafruit_midi
    from adafruit_midi.note_off import NoteOff
    from adafruit_midi.note_on import NoteOn

    uart = busio.UART(tx=board.GP0, rx=board.GP1, baudrate=31250, timeout=0.001)
    midi = adafruit_midi.MIDI(midi_in=uart, midi_out=uart)

    while True:
    msg = midi.receive()
    if (msg is not None):
    if (isinstance(msg, NoteOn)):
    print (msg)
    print ("Note On: \t",msg.note,"\t",msg.velocity)
    midi.send(msg)
    if (isinstance(msg, NoteOff)):
    print ("Note Off:\t",msg.note,"\t",msg.velocity)
    midi.send(msg)

    This will print out any received NoteOn and NoteOff messages (and only those) on the MIDI IN port and send them back out over the MIDI OUT port.

    PCB Errata

    There are the following issues with this PCB:

    •  The aforementioned Waveshare Zero footprint error.

    Enhancements:

    •  It might have been useful to position the Waveshare board so that the USB connector could be presented to the edge of the board and thus left exposed when used with a case.

    Find it on GitHub here.

    Closing Thoughts

    That is the basics of the board covered. Next will be a discussion of the alternative EuroRack supporting configuration and the physical builds for both versions.

    Kevin

    #APA106 #Krell #midi #pcb #waveshare

  7. Forbidden Planet “Krell” Display PCB Build Guide

    Here are the build notes for my Forbidden Planet “Krell” Display PCB. This post just looks at building the PCB for standalone use.

    Further posts will explore other uses for this PCB:

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers and electronics, see the Getting Started pages.

    Bill of Materials

    • Forbidden Planet “Krell” Display PCB (GitHub link below)
    • Waveshare Zero format board (more here).
    • 10x APA-106 through-hole programmable RGB LEDs pinout: IN-VCC-GND-OUT.
    • 1x 500uF electrolytic capacitor (or thereabouts).
    • 1x 47uF electrolytic capacitor.
    • Optional: 2x 9-way header sockets (full or low-profile – see notes).
    • Pin headers

    For the MIDI circuit:

    • 1x H11L1 optoisolator.
    • 1x 1N4148 or 1N914 signal diode.
    • Resistors: 1×10Ω, 1×33Ω, 1×220Ω, 1×470Ω.
    • 1x 100nF ceramic capacitor.
    • 2x 3.5mm stereo TRS sockets – pcb mount (see photo and PCB for footprint).
    • Optional: 1x 6-way DIP socket.

    For potentiometer circuit:

    • 1 or 2 x 10K pcb-mount potentiometer (see photo and PCB for footprint).
    • 1 or 2x 100nF ceramic capactiors.

    For the CV input:

    • 1x Thonkiconn style mono PCB mount jack socket.
    • Resistors: 1x22K, 1x33K.
    • 2x BAT43 Schottky diodes.

    Build Steps

    This posts describes a standalone module with two potentiometer controls and a MIDI circuit. For a EuroRack-style module with CV inputs refer to: Forbidden Planet “Krell” Display EuroRack Module.

    Taking a typical “low to high” soldering approach, this is the suggested order of assembly:

    • All diodes and resistors.
    • DIP socket (if used) and TRS sockets.
    • Disc capacitors.
    • LEDs on rear of the board.
    • 9-way headers (if used).
    • Additional pin headers (if used).
    • Electrolytic capacitors.
    • Potentiometers on rear of the board.

    Here are some build photos.

    When it comes to adding the LEDs it is critical to get them in the correct pin order. These boards are designed for LEDs with two long and two shorter legs, with the pins in the order:

    • Short: IN
    • Short: VCC
    • Long: GND
    • Long: OUT

    The pins need to be slightly bent to fit in the staggered footprint which means it isn’t possible to push the LEDs flush with the PCB. It is worth taking a little care to get them all to approximately the same height and vertically aligned.

    Hopefully it goes without saying to be careful of rubbing the hot soldering iron tip on any of the existing plastic components.

    As the footprint for the Waveshare Zero is 2.54mm too wide, it is advantageous to use a Waveshare Zero format board to help angle-in the pin headers prior to soldering.

    If using full height headers there will probably be enough flex to do this afterwards. If using low-profile headers then it will be necessary to get the angle correct prior to soldering.

    In the following note how the large capacitor has been bent over to lie flat.

    Also, I didn’t have a 500uF or higher, so used a 470uF in a 10mm diameter package.

    Testing

    I recommend performing the general tests described here: PCBs.

    Here is some test CircuitPython code that will check the functionality of the board with a Waveshare Zero type device. This was used with a Pimoroni Tiny2040 (which has two less pins to the Waveshare Zero devices).

    Analog Input

    This tests the potentiometers:

    import time
    import board
    from analogio import AnalogIn

    analog_in1 = AnalogIn(board.A2)
    analog_in2 = AnalogIn(board.A3)

    while True:
    print(analog_in1.value,"\t",analog_in2.value)
    time.sleep(0.1)

    On turning each of the potentiometers a value between 0 and 65536 should be printed to the serial console. Note: Mine never seems to get below 256…

    LEDs

    This can be used to test the LEDs. Requires the following libraries from the Adafruit Circuitpython Library Bundle:

    • neopixel.mpy
    • adafruit_pioasm.mpy (presumably only required on RP2040 based boards)
    • adafruit_pixelbuf.mpy
    import time
    import board
    import neopixel

    pixel_pin1 = board.GP2
    pixel_pin2 = board.GP3
    num_pixels = 5

    pixels1 = neopixel.NeoPixel(pixel_pin1, num_pixels, brightness=0.3, auto_write=False, pixel_order=neopixel.RGB)
    pixels2 = neopixel.NeoPixel(pixel_pin2, num_pixels, brightness=0.3, auto_write=False, pixel_order=neopixel.RGB)

    while True:
    for col in [(255,0,0),(0,255,0),(0,0,255),(0,0,0)]:
    for pix in range(5):
    pixels1[pix] = col
    pixels1.show()
    time.sleep(0.5)

    pixels2[pix] = col
    pixels2.show()
    time.sleep(0.5)

    time.sleep(3)

    This will light each LED in turn alternating between the upper and lower sets of LEDs and then leave them off for three seconds.

    MIDI IN and OUT

    This requires the Adafruit MIDI library, which requires the following directory from the Adafruit Circuitpython Library Bundle:

    • adafruit_midi/*
    import board
    import digitalio
    import busio
    import adafruit_midi
    from adafruit_midi.note_off import NoteOff
    from adafruit_midi.note_on import NoteOn

    uart = busio.UART(tx=board.GP0, rx=board.GP1, baudrate=31250, timeout=0.001)
    midi = adafruit_midi.MIDI(midi_in=uart, midi_out=uart)

    while True:
    msg = midi.receive()
    if (msg is not None):
    if (isinstance(msg, NoteOn)):
    print (msg)
    print ("Note On: \t",msg.note,"\t",msg.velocity)
    midi.send(msg)
    if (isinstance(msg, NoteOff)):
    print ("Note Off:\t",msg.note,"\t",msg.velocity)
    midi.send(msg)

    This will print out any received NoteOn and NoteOff messages (and only those) on the MIDI IN port and send them back out over the MIDI OUT port.

    PCB Errata

    There are the following issues with this PCB:

    •  The aforementioned Waveshare Zero footprint error.

    Enhancements:

    •  It might have been useful to position the Waveshare board so that the USB connector could be presented to the edge of the board and thus left exposed when used with a case.

    Find it on GitHub here.

    Closing Thoughts

    That is the basics of the board covered. Next will be a discussion of the alternative EuroRack supporting configuration and the physical builds for both versions.

    Kevin

    #APA106 #Krell #midi #pcb #waveshare

  8. One last thing I definitely wanted for my PicoDexed was the option for PWM output. This post is by way of a short coda detailing how to do PWM on a Raspberry Pi Pico.

    • Part 1 where I work out how to build Synth_Dexed using the Pico SDK and get some sounds coming out.
    • Part 2 where I take a detailed look at the performance with a diversion into the workings of the pico_audio library and floating point maths on the pico, on the way.
    • Part 3 where I managed to get up to 16-note polyphony, by overclocking, and some basic serial MIDI support.
    • Part 4 for the full MIDI implementation, voice loading, SysEx control and USB-MIDI.
    • Part 5 details different options for building hardware to run PicoDexed.

    The latest code can be found on GitHub here: https://github.com/diyelectromusic/picodexed

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    PWM Output Circuit

    The official “Hardware Design with the RP2040” guide includes a PWM circuit as follows (see section 3.4.1).

    Several others have used a slightly simplified version of this circuit, essentially omitting the buffer stage (U3) and perhaps only implementing a single channel. See for example – Raspberry Pi Pico PWM Audio Output Circuit (Jeremy S. Cook) and “pico-pwm-audio” (Robin Grosset).

    An alternative is this circuit used by Tod Kurt for his Circuitpython-tricks“The output circuitry to get line-out levels is a simple 10:1 voltage-divider and a 1uF capacitor to recenter the signal around 0 volts”:

    I’m going with Jeremy S. Cook and Robin Grosset and using the following (diagram from here):

    This is my solderless breadboard version of the above:

    Raspberry Pi Pico Audio PWM

    There appears to be two ways of getting audio style PWM signals out of a Pico:

    • Use the PWM peripherals.
    • Use the PIO.

    It would appear that Raspberry Pi’s pico_audio library in the pico-extras repository uses PIO. I haven’t found a clear explanation as to why the built-in PWM peripherals haven’t been used.

    Here are some other resources that show alternative descriptions of PWM for audion on a Pico:

    As I’m using the pico_audio library for I2S audio, I’m going to use the default PWM pico_audio library too.

    To initialise the pico_audio PWM library requires the following:

    const audio_pwm_channel_config_t config =
    {
    .core = {
    .base_pin = base_pin,
    .dma_channel = dma_ch,
    .pio_sm = pio_sm
    },
    .pattern = 3,
    };

    const struct audio_format *output_format;
    output_format = audio_pwm_setup(pAudioFormat, -1, &config);
    bool status = audio_pwm_default_connect(pBufferPool, false);
    audio_pwm_set_enabled(true);

    Everything else is the same as for I2S.

    At present, overclocking the Pico causes the PWM frequencies to be messed up, so for now the recommended configuration is 8-note polyphony, 24000 sample rate and no overclocking.

    The build uses GPIO 20 for PWM.

    Closing Thoughts

    I’ve uploaded a prototype PWM UF2 file to GitHub too now in case anyone wants to give that a go too: https://github.com/diyelectromusic/picodexed

    Hopefully there is enough information in the above to get something up and running.

    Kevin

    https://diyelectromusic.wordpress.com/2024/02/19/raspberry-pi-pico-synth_dexed-part-6/

    #dx7 #midi #picodexed #pwm #raspberryPiPico #usbMidi

  9. One last thing I definitely wanted for my PicoDexed was the option for PWM output. This post is by way of a short coda detailing how to do PWM on a Raspberry Pi Pico.

    • Part 1 where I work out how to build Synth_Dexed using the Pico SDK and get some sounds coming out.
    • Part 2 where I take a detailed look at the performance with a diversion into the workings of the pico_audio library and floating point maths on the pico, on the way.
    • Part 3 where I managed to get up to 16-note polyphony, by overclocking, and some basic serial MIDI support.
    • Part 4 for the full MIDI implementation, voice loading, SysEx control and USB-MIDI.
    • Part 5 details different options for building hardware to run PicoDexed.

    The latest code can be found on GitHub here: https://github.com/diyelectromusic/picodexed

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    PWM Output Circuit

    The official “Hardware Design with the RP2040” guide includes a PWM circuit as follows (see section 3.4.1).

    Several others have used a slightly simplified version of this circuit, essentially omitting the buffer stage (U3) and perhaps only implementing a single channel. See for example – Raspberry Pi Pico PWM Audio Output Circuit (Jeremy S. Cook) and “pico-pwm-audio” (Robin Grosset).

    An alternative is this circuit used by Tod Kurt for his Circuitpython-tricks“The output circuitry to get line-out levels is a simple 10:1 voltage-divider and a 1uF capacitor to recenter the signal around 0 volts”:

    I’m going with Jeremy S. Cook and Robin Grosset and using the following (diagram from here):

    This is my solderless breadboard version of the above:

    Raspberry Pi Pico Audio PWM

    There appears to be two ways of getting audio style PWM signals out of a Pico:

    • Use the PWM peripherals.
    • Use the PIO.

    It would appear that Raspberry Pi’s pico_audio library in the pico-extras repository uses PIO. I haven’t found a clear explanation as to why the built-in PWM peripherals haven’t been used.

    Here are some other resources that show alternative descriptions of PWM for audion on a Pico:

    As I’m using the pico_audio library for I2S audio, I’m going to use the default PWM pico_audio library too.

    To initialise the pico_audio PWM library requires the following:

    const audio_pwm_channel_config_t config =
    {
    .core = {
    .base_pin = base_pin,
    .dma_channel = dma_ch,
    .pio_sm = pio_sm
    },
    .pattern = 3,
    };

    const struct audio_format *output_format;
    output_format = audio_pwm_setup(pAudioFormat, -1, &config);
    bool status = audio_pwm_default_connect(pBufferPool, false);
    audio_pwm_set_enabled(true);

    Everything else is the same as for I2S.

    At present, overclocking the Pico causes the PWM frequencies to be messed up, so for now the recommended configuration is 8-note polyphony, 24000 sample rate and no overclocking.

    The build uses GPIO 20 for PWM.

    Closing Thoughts

    I’ve uploaded a prototype PWM UF2 file to GitHub too now in case anyone wants to give that a go too: https://github.com/diyelectromusic/picodexed

    Hopefully there is enough information in the above to get something up and running.

    Kevin

    https://diyelectromusic.wordpress.com/2024/02/19/raspberry-pi-pico-synth_dexed-part-6/

    #dx7 #midi #picodexed #pwm #raspberryPiPico #usbMidi

  10. One last thing I definitely wanted for my PicoDexed was the option for PWM output. This post is by way of a short coda detailing how to do PWM on a Raspberry Pi Pico.

    • Part 1 where I work out how to build Synth_Dexed using the Pico SDK and get some sounds coming out.
    • Part 2 where I take a detailed look at the performance with a diversion into the workings of the pico_audio library and floating point maths on the pico, on the way.
    • Part 3 where I managed to get up to 16-note polyphony, by overclocking, and some basic serial MIDI support.
    • Part 4 for the full MIDI implementation, voice loading, SysEx control and USB-MIDI.
    • Part 5 details different options for building hardware to run PicoDexed.

    The latest code can be found on GitHub here: https://github.com/diyelectromusic/picodexed

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    PWM Output Circuit

    The official “Hardware Design with the RP2040” guide includes a PWM circuit as follows (see section 3.4.1).

    Several others have used a slightly simplified version of this circuit, essentially omitting the buffer stage (U3) and perhaps only implementing a single channel. See for example – Raspberry Pi Pico PWM Audio Output Circuit (Jeremy S. Cook) and “pico-pwm-audio” (Robin Grosset).

    An alternative is this circuit used by Tod Kurt for his Circuitpython-tricks“The output circuitry to get line-out levels is a simple 10:1 voltage-divider and a 1uF capacitor to recenter the signal around 0 volts”:

    I’m going with Jeremy S. Cook and Robin Grosset and using the following (diagram from here):

    This is my solderless breadboard version of the above:

    Raspberry Pi Pico Audio PWM

    There appears to be two ways of getting audio style PWM signals out of a Pico:

    • Use the PWM peripherals.
    • Use the PIO.

    It would appear that Raspberry Pi’s pico_audio library in the pico-extras repository uses PIO. I haven’t found a clear explanation as to why the built-in PWM peripherals haven’t been used.

    Here are some other resources that show alternative descriptions of PWM for audion on a Pico:

    As I’m using the pico_audio library for I2S audio, I’m going to use the default PWM pico_audio library too.

    To initialise the pico_audio PWM library requires the following:

    const audio_pwm_channel_config_t config =
    {
    .core = {
    .base_pin = base_pin,
    .dma_channel = dma_ch,
    .pio_sm = pio_sm
    },
    .pattern = 3,
    };

    const struct audio_format *output_format;
    output_format = audio_pwm_setup(pAudioFormat, -1, &config);
    bool status = audio_pwm_default_connect(pBufferPool, false);
    audio_pwm_set_enabled(true);

    Everything else is the same as for I2S.

    At present, overclocking the Pico causes the PWM frequencies to be messed up, so for now the recommended configuration is 8-note polyphony, 24000 sample rate and no overclocking.

    The build uses GPIO 20 for PWM.

    Closing Thoughts

    I’ve uploaded a prototype PWM UF2 file to GitHub too now in case anyone wants to give that a go too: https://github.com/diyelectromusic/picodexed

    Hopefully there is enough information in the above to get something up and running.

    Kevin

    https://diyelectromusic.wordpress.com/2024/02/19/raspberry-pi-pico-synth_dexed-part-6/

    #dx7 #midi #picodexed #pwm #raspberryPiPico #usbMidi

  11. I was browsing some of the newer ATtiny chips the other day and started to make a note of some of their properties and it made me realise I actually have quite a few different microcontrollers at my disposal and many more I could be having a look at.

    But having committed to not attempting to get hold of every variant of every device to put a MIDI interface on it, I thought it would still be worth a post summarising some of the features to make selecting them in the future a little easier.

    There are many comparison charts and tables online, but this is my own summary of the things that are important to me right now in terms of using them for musical purposes.

    Note: I think the data is correct at the time of writing. Feel free to let me know of any mistakes. Also feel free to let me know what microcontrollers you use for music, and why, in the comments.

    8-bit Microcontrollers

    MCUFreqPWRGPIOADCPWMDACCommsRAMFlashATmega328P16MHz2.7-5.5V236/860UART, I2C, SPI2K32KATmega32U416MHz2.7-5.5V261280UART, I2C, SPI, USB2.5K32KATtiny858/20MHz2.7-5.5V6460USI5128KATtiny8812MHz2.7-5.5V28620I2C, SPI5128KATtiny21(2|4)
    ATtiny41(2|4|6)20MHz1.8-5.5V6|12
    6|12|186|10
    6|10|1261UART, I2C, SPI128
    2562K
    4K

    32-bit Microcontrollers

    MCUFreqPWRFPUGPIOADCPWMDACCommsRAMFlashSAMD21 (M0+)48MHz1.6-3.6VN30/381430?1SERCOM, I2S, USB4-32K32-256KSAMD51 (M4)120MHz1.6-3.6VY513237?1SERCOM, I2S, USB128-256256-1024KRP2040 (2xM0+)133MHz3.3VN304110UART, I2C, SPI, USB, PIO264KexternalESP32 (LX6)160MHz3.0-3.6VY3418162UART, I2C, SPI, I2S, Wi-Fi, BT0-2M0-4MESP32-S2 (LX7)240MHz2.8-3.6VN?432080UART, I2C, SPI, I2S, Wi-Fi0-2M0-2-4MESP32-S3 (2xLX7)240MHz3.0-3.6VY452080UART, I2C, SPI, I2S, Wi-Fi, BT0-2-8-16M0-4-8MESP32-A1S (2xLX6)240MHx3.0-3.6VY?14??2UART, I2C, SPI, I2S520K+4M0?

    Points of Note

    • The ATmega and ATtiny devices are all 8-bit AVR architecture and might be either 3V3 or 5V operation depending on the device. Whereas the others are all 32-bit, 3V3 operation, and either ARM or Tensilica Xtensa architectures.
    • The SAMD51, ESP32 and ESP32-S3 are all interesting as they include a floating point unit, which might be useful if I get into requiring mathematical synthesis.
    • ATtiny2xx, ATtiny4xx, SAMD21, SAMD51, ESP32 all include a DAC which would be really useful for generating control voltages.
    • ATmega32U4, SAMD21, SAMD51, RP2040 all support USB directly.
    • The last one is an interesting device. The ESP32-A1S is a single module that includes an ESP32 and a CODEC module. More recent versions use the ES8388 and support two audio in/out channels. There is an Espressif Audio Development Framework for use with all ESP32-based devices.

    Other MCUs of possible interest might include some of the newer RISC-V devices (e.g. ESP32-C3), the STM32 device range (the higher performing devices include floating point support, for example), the Teensy boards (which have a strong following for audio applications), and even running with the broadcom devices used on the various Raspberry Pis in “bare metal” mode.

    A key tradeoff already would be choosing between a more powerful, probably 32-bit, 3V3 logic devices or a less capable 5V device.

    Development Boards

    I’m unlikely to be working with a microcontroller directly though, given my own level of knowledge, so I’m probably going to be looking at some kind of development board.

    The following could all be possibilities if I’m happy running at 3V3.

    Note, many of the form-factors, e.g. Adafruit’s QT Py or Feather, support most of the architectures – but not all are listed – just those I have or might consider. I’ve also added in some other boards that I know are often used (or shout about being used) for audio applications.

    Prices are approximate at time of writing (Feb 2024).

    BoardMCUArchSpeedRAM/FlashFPUGPIOADCPWMI2SDACCostRPi PicoRP20402xM0+133MHz264K/2MN27316PIO0£4XIAOSAMD21M0+48MHz32K/256KN14111111£6XIAORP20402xM0+133MHz264K/2MN11411PIO0£6XIAOESP32-S32xLX7240MHz8M/8MY1191110£8XIAOESP32-C3RISC-V160MHz4K/4M?114110£6QT PySAMD21M0+48MHz32K/256KN119911£9QT PyRP20402xM0+125MHz264K/8MN13413PIO0£10QT PyESP32-S32xLX7240MHz512K+2M/4MY13101310£15TrinketSAMD21M0+48MHz32K/256KN55211£9ItsyBitsySAMD21M0+48MHz32K/256KN23111311£12ItsyBitsySAMD51M4120MHz192K/512K+2MN2371812£15FeatherSAMD21M0+48MHz32K/256KN2062011£19FeatherSAMD51M4120MHz192K/512K+2MY2161612£23FeatherRP20402xM0+125MHz264K/8MN21416PIO0£12FeatherESP32-S32xLX7240MHz2M/4MY2162110£17BananaPicoWESP32-S32xLX7240MHz512K/2M+8MY2718810£4WROOM32ESP322xLX6<240MHz500K/448K+4MY34152512£3Teensy 3.6MK66FX1M4F180MHz256K/1MY64252212N/ATeensy 4.0IMXRT1062M7600MHz1M/2MY40143120£26Teensy 4.1IMXRT1062M7600MHz1M/8MY55183520£30Arduino MKR ZeroSAMD21M0+48MHz32K/256KN2271311£30Arduino Giga R1STM32H747XM7
    M4480MHz
    240MHz1M/2M?761413?2£70

    It is interesting to note which boards support a DAC and which support I2S, both very useful for audio applications and the number of ADCs is relevant too.

    Boards specifically designed for audio processing, which I’ve no direct experience of, include:

    • Pico ADK – A RP2040 based “audio development kit” with 8 ADCs and SPI DAC.
    • Daisy Seed – an ARM Cortex-M7 with audio IO designed for DSP and audio applications (£35)
    • Bela and Bela Mini – designed for use with Beaglebone for real-time, low-latency audio processing (~£130-£160).

    And it is worth noting that the Teensy has many features well suited to audio processing, including a dedicated software audio toolkit (see below).

    Software Audio Frameworks

    There are a number of software frameworks for use with some of the above for audio processing:

    There is a bit of discussion about these here: Arduino Audio and MIDI Frameworks.

    Closing Thoughts

    I expect this page will evolve with new information, but it will be good to have a single post to refer back to.

    Kevin

    https://diyelectromusic.wordpress.com/2024/05/07/selecting-microcontrollers-for-music/

    #adafruit #arduino #attiny #esp32 #raspberryPi #raspberryPiPico #samd21 #samd51 #teensy

  12. The story so far…

    • In Part 1 I work out how to build Synth_Dexed using the Pico SDK and get some sounds coming out.
    • In Part 2 I take a detailed look at the performance with a diversion into the workings of the pico_audio library and floating point maths on the pico, on the way.

    This post describes how I’ve set things up for some further development and the decisions I’ve made to get to the point where it can receive MIDI and actually be somewhat playable within the limitations of 10 note polyphony, a 24000 sample rate, and a single voice only on MIDI channel 1!

    Update: By overclocking the Pico to 250MHz I can do 16 note polyphony at 24000 or 8 note polyphony at 48000!

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    Optimised Dexed->getSamples

    I left things in part 2 noting that Dexed itself is essentially a fully integer-implemented synth engine, so why did I need the floating point calculations. I concluded it is all due to the filter that has been added, which is based on the LP filter code from https://obxd.wordpress.com/ which was added in Dexed, but wasn’t in the original “music synthesizer for Android“.

    So I’ve decided not to bother with it. If I feel like it is really missing out, then I have stumbled across the following which looks promising: https://beammyselfintothefuture.wordpress.com/2015/02/16/simple-c-code-for-resonant-lpf-hpf-filters-and-high-low-shelving-eqs/

    So, here is my integer-only version of Dexed->getSamples.

    void Dexed::getSamples(int16_t* buffer, uint16_t n_samples)
    {
    if (refreshVoice)
    {
    for (uint8_t i = 0; i < max_notes; i++)
    {
    if ( voices[i].live )
    voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity, voices[i].porta, &controllers);
    }
    lfo.reset(data + 137);
    refreshVoice = false;
    }

    for (uint16_t i = 0; i < n_samples; ++i)
    {
    buffer[i] = 0;
    }

    for (uint16_t i = 0; i < n_samples; i += _N_)
    {
    AlignedBuf<int32_t, _N_> audiobuf;

    for (uint8_t j = 0; j < _N_; ++j)
    {
    audiobuf.get()[j] = 0;
    }

    int32_t lfovalue = lfo.getsample();
    int32_t lfodelay = lfo.getdelay();

    for (uint8_t note = 0; note < max_notes; note++)
    {
    if (voices[note].live)
    {
    voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers);

    for (uint8_t j = 0; j < _N_; ++j)
    {
    int16_t tmp = audiobuf.get()[j] >> 16;
    buffer[i + j] += tmp;
    audiobuf.get()[j] = 0;
    }
    }
    }
    }
    }

    With this in place, I appear to be able to comfortably cope with 8-note polyphony. At least with my test chords.

    Debug Output

    Now before I go too far, I want a simple way to get some output out of the device. The Pico Getting Started documentation gives an example of how to get some standard output (stdio) working. There are two options for this output (see chapter 4 “Saying “Hello World” in C”):

    • Using the built-in USB serial port.
    • Outputting to the UART serial port.

    To use USB requires building in TinyUSB, but I’m planning on using that later. It also adds quite a lot of overhead apparently, so the default is to output to the serial port via GP0 (TX) and GP1 (RX). All that is required is to find a way to connect this up to a computer or terminal device.

    There are several options: some kind of 3V3 supporting USB<->Serial converter – there are several, based on the CH240 of FTDI devices for example, although not many of the cheap ones are 3V3 compatible (don’t use a 5V board it could damage the Pico!); or using a native Raspberry Pi development environment, then simply using GPIO directly to connect the Pico to the Pi’s UART.

    It is also possible to use the picoprobe firmware running on another Pico I believe, but I haven’t tried that. It wasn’t totally clear to me if that supports the USB to serial link, although it is strongly implied. The official Raspberry Pi Debug Probe definitely does however, but I haven’t got one of those at the moment.

    I initially opted to use another Pico as a serial to USB gateway by running Circuitpython and the following script on boot by saving it as code.py:

    import board
    import busio
    import digitalio

    uart = busio.UART(tx=board.GP0, rx=board.GP1, baudrate=115200, timeout=0.1)

    while True:
    readbytes = uart.read()
    if readbytes != None:
    print (''.join([chr(b) for b in readbytes]))

    Now this just needs connected to the Pico running PicoDexed as follows:

    PicoDexed        Debug Pico
       GP0    <---->    GP1
       GND    <---->    GND

    As this is running Circuitpython it means I also get the CIRCUITPY virtual drive appear and mounted too which isn’t ideal but not really a big issue.

    Then I had a rummage in my Pico drawer looking for a neater solution and found a Waveshare RP2040-One that I’d forgotten I had! This is perfect as it has a USB plug at one end (via a shaped PCB) and GPIO at the other, including pins connected to UART 0.

    I dropped Micropython onto the board this time, with the following script.

    import time
    from machine import UART, Pin

    # Use one of the GPIO as a GND pin for the serial
    gndpin = Pin(11, Pin.OUT)
    gndpin.value(0)

    print ("Initialising UART 0 on pins gnd=11, tx=12, rx=13...")
    uart = UART(0, baudrate=115200, tx=Pin(12), rx=Pin(13))
    print ("Ready")

    while True:
    # Read raw data version
    rxdata = bytes()
    while uart.any() > 0:
    rxdata += uart.read(1)
    time.sleep_ms(10)

    if rxdata:
    print(rxdata.decode('utf-8'))

    To keep the connections simple, I used GPIO 11 as an additional GND pin as there is only one on the board and it isn’t so convenient.

    PicoDexed        RP2040-One
       GP0    <---->    GP13
       GND    <---->    GP11

    To ensure the code can output text just needs something like the following:

    #include <stdio.h>

    void main () {
      stdio_init_all();
      printf("PicoDexed...");
    }

    Then with both devices connected to my virtual Ubuntu Linux machine, I can run minicom (once installed – it isn’t installed by default):

    $ sudo minicom -b 115200 -D /dev/ttyACM0

    Here is the output.

    Welcome to minicom 2.8

    OPTIONS: I18n
    Port /dev/ttyACM0, 13:27:28
    Press CTRL-A Z for help on special keys

    PicoDexed...

    Connecting PIO I2S audio
    Copying mono to mono at 24000 Hz

    Note, to exit minicom use CTRL-A then X.

    Alternatively I could use PuTTY on Windows on the COM port associated with the “debugging” Pico.

    At some point I’ll probably need to set up proper SWD debugging, but this should do for the time being.

    I might also need to switch UARTs if I want to use UART 0 for MIDI, but apparently there are some defines that can be changed in the CMakeLists.txt file:

    target_compile_definitions(picodexed PRIVATE
    PICO_DEFAULT_UART=0
    PICO_DEFAULT_UART_TX_PIN=0
    PICO_DEFAULT_UART_RX_PIN=1
    )

    PicoDexed design

    It is time to start thinking seriously if I can turn this into something interesting or not, so borrowing from some of the design principles encapsulated in MiniDexed, I’ve now got a project that looks as follows:

    • main.cpp -> Basic IO, initialisation and main update loop.
    • picodexed.cpp -> The core synthesizer wrapper with the following key interface:
      • CPicoDexed::Init -> perform all the required synthesizer initialisation.
      • CPicoDexed::Process -> perform a single “tick” of the synthesizer functions, including updating the sample buffers from Dexed.
    • mididevice.cpp, serialmidi.cpp, usbmidi.cpp -> placeholder classes that will eventually support MIDI message passing and a serial and USB MIDI interface. This borrows heavily from the way it is done in MiniDexed. These classes will support the following interface:
      • CSerialMIDI::Init -> Initialise the hardware (same for USB).
      • CSerialMIDI::Process -> poll the hardware (same for USB).
      • CMIDIDevice::MIDIMessageHandler -> will be called by the lower-level devices when a MIDI message is ready to be processed. Once parsed, it will trigger calls into the PicoDexed main synthesizer to update its state.
    • soundevice.cpp -> Encapsulating the interface to the pico_audio library to use I2S audio, with the following key interface:
      • CSoundDevice::Init -> Set the sample rate and I2S interface pins.
      • CSoundDevice::Update -> Fill the sample buffer using the provided callback, which will be a call to the Dexed->getSamples code above.
    • config.h -> contains some system-wide configuration items, such as sample rate and polyphony.

    PicoDexed will include the functions required to control the synthesizer. Examples include keydown and keyup functions for when MIDI NoteOn and NoteOff messages, and so on. It also includes a means to set the MIDI channel and to load a voice.

    I don’t know yet if the MIDI handling will be interrupt driven or polled. I need to read up on how the Pico SDK handles USB and serial data, but I suspect a polled interface should be fine for my purposes as long as it doesn’t hold up the sample calculations, buffer filling, and sample playing.

    With my first pass of this code, there is no external interface – it is still playing a test chord only. But at least most of the structure is now in place to hook it up to MIDI.

    The “to do” list so far:

    • Ideally find a way to better manage the Synth_Dexed changes. I should submit a PR to Holger, the creator of Synth_Dexed and discuss some conditional compilation steps.
    • Hook up USB MIDI so that the Pico can act as a MIDI device and play the synth that way.
    • Hook up serial MIDI too.
    • Implement volume. Without the filter there is currently no volume changing.
    • Implement some basic voice and bank loading.
    • Connect up some more core MIDI functionality for program change, BANKSEL, channel volume, master volume, and so on.
    • Think about how best to utilise the second core – in theory it should be possible to expand it to 16-note polyphony by using both cores. Or an alternative might be two instances of Synth_Dexed running, so making a second tone generator.

    MIDI/Serial Handling

    Rather than jump into USB, I’ve opted to get serial MIDI working first. The serial port handling I’ve implemented in serialmidi.cpp borrows heavily from the “advanced” UART example: https://github.com/raspberrypi/pico-examples/tree/master/uart/uart_advanced

    It is interrupt driven and shares a simple circular buffer with the main Read function based on the implementation described here: https://embedjournal.com/implementing-circular-buffer-embedded-c/.

    The basic design of the serial MIDI interface is as follows:

    Interrupt Handler:
      Empty the serial hardware of data writing it to the circular buffer

    Init function:
      Initialise the UART as per the uart_advanced example
      Install the interrupt handler and enable interrupts

    Process function:
      Call the MIDI device data parser to piece together any MIDI messages
      Call the MIDI device msg handler to handle any complete MIDI messages

    Read function:
      Read the next byte out of the circular buffer

    There is a common MIDI device that the serial MIDI device inherits from (and that I plan to also use with USB MIDI support when I get that far). This has the following essential functionality:

    MIDIParser:
      Read a byte from the transport interface (e.g. the serial MIDI Read)
      IF starting a new message THEN
        Initialise MIDI msg structures
        IF a single byte message THEN
          Fill in MIDI msg structures for single-byte message
          return TRUE
        IF there is a valid Running Status byte stored THEN
          IF this now completes a valid two-byte msg THEN
            Fill in MIDI msg structures for a two-byte message
            return TRUE
      Otherwise process as a two or three byte message
      IF message now complete THEN
        Fill in MIDI msg structures
        return TRUE
      return FALSE

    MIDI Message Parser:
      IF MIDI msg already processed THEN return
      IF on correct channel or OMNI THEN
        Based on received MIDI command:
          Extract parameters
          Call appropriate picoDexed function
      Mark MIDI msg as processed.

    I had a fun bug where in the serial handling, I was writing to a byte one-out in the circular buffer which meant that the MIDI handling largely worked, but only when using a controller with ActiveSensing – basically the reception of the extra byte “pushed through” the previous message. But it was a bit unresponsive, and occasionally a note of a chord would sound after the others.

    I spent the better part of a day instrumenting the code, attempting to work out where the delays might be coming from. Eventually I got so fed up with the active sensing reception clouding my analysis (and triggering my scope when I didn’t want it to) that I filtered it out in the serial interrupt routine – so as early as I could.

    This the made the delay a whole pile worse! That was the point I realised it was continually essentially one message behind. As a consequence I had another look at the buffer handling and that was when I realised the mistake.

    Multicore support

    My initial thought on the above problem was that it was a performance issue – that the MIDI handling wasn’t responsive enough. So I pushed ahead and moved all the synthesis code over to the second core. This is something I wanted to do anyway as I always had the plan of splitting the functionality across the two cores.

    To enable multicore support requires including pico_multicore in the list of libaries in the CMakeLists.txt file and then it should largely be a case of doing the following:

    #include "pico/multicore.h"

    void core1_entry (void)
    {
      // stuff to do to initialise core 1

      while (1)
      {
        // Stuff to do repeatedly on core 1
      }
    }

    // Rest of "normal" (core 0) initialisation code
    multicore_launch_core1 (core1_entry);

    The question is where to enable this. Eventually I settled on implementing this in picoDexed itself to split out the ProcessSound function over to the second core. This required the following:

    • PicoDexed::Init – initialise multi-core support and start the second core running.
    • PicoDexed::Process – no longer calls ProcessSound.
    • PicoDexed::core1_entry – now calls ProcessSound in an infinite loop.

    In order to ensure that I don’t get Dexed into an inconsistent state, I’ve protected the calls into Dexed from the Dexed_Adaptor with spinlocks (mirroring what was happing in MiniDexed) as shown in the following extract:

    class CDexedAdapter : public Dexed
    {
    public:
    CDexedAdapter (uint8_t maxnotes, int rate)
    : Dexed (maxnotes, rate)
    {
    spinlock_num = spin_lock_claim_unused(true);
    spinlock = spin_lock_init(spinlock_num);
    }

    void getSamples (int16_t* buffer, uint16_t n_samples)
    {
    spin_lock_unsafe_blocking(spinlock);
    Dexed::getSamples (buffer, n_samples);
    spin_unlock_unsafe(spinlock);
    }

    private:
    int spinlock_num;
    spin_lock_t *spinlock;
    }

    Spinlocks are described in chapter 4.1.19 of the RPi C/C++ SDK and are part of the hardware_sync library.

    In order to ensure that the spin_locks are not held too long, and to allow things like keyup/down events to be registered in a timely manner and not hold up core 0 whilst core 1 is calculating samples, I’ve now reduced the sample buffer to 64 bytes.

    As core 1 is essentially free-running calculating samples now, I figured it wouldn’t make much difference how many samples are calculated in each “chunk” but going to 64 from 256 gives four times the number of break points in the cycle where other events can be processed between the spin_locks.

    Once consequence of running multi-core seems to be that I can now push the polyphony up to 10 simultaneous notes without any artefacts.

    If I can find a way to keep some of the sound generation on core 0 too, I might be able to increase that even further, although getting 6 additional sound engines running to get up to the magic 16 note polyphony might be stretching things still. The trick will be finding a way to trigger and mix samples from the sound generators in the two cores, as all of that current happens within Dexed itself.

    Overclocking the Pico

    There have been a number of experiments already in seeing how far a Pico can be pushed. There is a standard API call to set the system clock: set_sys_clock_khz(), although not all values can be accurately configured. But general wisdom seems to be that running the Pico at 250MHz isn’t a big deal…

    Of course, at this point it is running outside of the “normal” spec, so the long term effects may well reduce the life of the Pico…

    But by doing this, the Pico is now running twice as fast and so can now easily cope with 16 note polyphony at a sample rate of 24000, or up the sample rate to 48000 and stick with 8 note polyphony.

    It might even raise the possibility of running two tone generators, one on each core! It really does open up a wide range of possibilities!

    Closing Thoughts

    I’m really pleased with the progress so far. I was starting to think there wouldn’t be a usable combination possible, but 10-note polyphony at a sample rate of 24000 isn’t too bad for a 133MHz CPU with no FPU.

    I think my basic design goal would be for something usable with a MIDI controller. I’m not looking to implement a UI like there is with MiniDexed as part of this build. But I do need a bit more MIDI functionality first and I would like to find a way to squeeze some sample calculations out of core 0 when it isn’t handling MIDI.

    I also want to get USB MIDI up and running too. I’m not sure if I want to push for both device and host USB support though. I’ll see how complicated it all is!

    In the video, I’ve used my Raspberry Pi Pico MIDI Proto Expander. It just needs the addition of the Pimoroni audio board and it is ready to go!

    Of course the key question is: would I recommend this to anyone? Answer: no! No way – get yourself a Raspberry Pi Zero V2 and run a full-blown set of 8 DX7s using MiniDexed 🙂

    Still for me, this is a bit of fun a really good excuse to do something that’s been on my “to do” list for ages – start getting to grips with the Raspberry Pi Pico C/C++ SDK and the RP2040.

    Kevin

    https://diyelectromusic.wordpress.com/2024/02/04/raspberry-pi-pico-synth_dexed-part-3/

    #dx7 #midi #picodexed #raspberryPiPico

  13. Wrapped up my latest custom #keyboard today. It’s a single handed keeb designed exclusively for playing #ArcRaiders . Designed in TinkerCad and #3dprinted on my #Prusa mk4. Then hand painted with Panduro Hobby Acrylics and dusted with weathering powders. Running #kmk on a #seeed Xiao #rp2040. #circuipython #kmk