genuinequality

Download free music MP3s on genuine quality, the world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, The world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, stats, charts, biographies and concerts. stats, charts, biographies and concerts.

Saturday, September 6, 2025

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 …
Read on blog or Reader
Site logo image Simple DIY Electronic Music Projects Read on blog or Reader

Arduino and SP0256A-AL2 – Part 3

By Kevin on September 6, 2025

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...

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:

Count Equivalent Frequency
20 5,000,000
21 4,807,692
22 4,629,629
23 4,464,285
24 4,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

Comment
Like
You can also reply to this email to leave a comment.

Simple DIY Electronic Music Projects © 2025.
Unsubscribe or manage your email subscriptions.

WordPress.com and Jetpack Logos

Get the Jetpack app

Subscribe, bookmark, and get real‑time notifications - all from one app!

Download Jetpack on Google Play Download Jetpack from the App Store
WordPress.com Logo and Wordmark title=

Automattic, Inc.
60 29th St. #343, San Francisco, CA 94110

Posted by BigPalaceNews at 3:07 AM
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest

No comments:

Post a Comment

Newer Post Older Post Home
Subscribe to: Post Comments (Atom)

Search This Blog

About Me

BigPalaceNews
View my complete profile

Blog Archive

  • September (27)
  • August (116)
  • July (96)
  • June (100)
  • May (105)
  • April (95)
  • March (131)
  • February (111)
  • January (104)
  • December (98)
  • November (87)
  • October (126)
  • September (104)
  • August (97)
  • July (112)
  • June (113)
  • May (132)
  • April (162)
  • March (150)
  • February (342)
  • January (232)
  • December (260)
  • November (149)
  • October (179)
  • September (371)
  • August (379)
  • July (360)
  • June (385)
  • May (391)
  • April (395)
  • March (419)
  • February (356)
  • January (437)
  • December (438)
  • November (400)
  • October (472)
  • September (460)
  • August (461)
  • July (469)
  • June (451)
  • May (464)
  • April (506)
  • March (483)
  • February (420)
  • January (258)
  • December (197)
  • November (145)
  • October (117)
  • September (150)
  • August (132)
  • July (133)
  • June (117)
  • May (190)
  • January (48)
Powered by Blogger.