Description
I am trying to (mis)use Rpi Pico as a scientific data acquisition platform, imagine a 500 kSpS cheap oscilloscope. It is easy to set up communication with μPython: writing data to stdout on the Rpi Pico, and reading data from /dev/ttyACM0 (or other quasi-serial port) in the computer.
Its direct USB support would promise decent speed up to 1 MB/s, which would be just fine for the task. In practice, there are tighter bottlenecks described below. I made a little script for Rpi Pico, flooding the USB with data, and measuring the speed.
My code for μPython in Rpi Pico:
from machine import Pin
import time, machine, usys, gc, micropython
machine.freq(125000000*2)
LED = machine.Pin(25, machine.Pin.OUT)
LED.value(1); time.sleep(.5); LED.value(0) # no resets detected
while True:
for a in range(1000):
try:
usys.stdout.buffer.write('x'*10000)
except:
LED.value(1); time.sleep(.2); LED.value(0) # no errors detected
time.sleep(1) # try allowing USB stack to flush (no effect on later speed)
My code to receive the data in the computer:
#!/usr/bin/python3
import sys, serial, time
port = serial.Serial('/dev/ttyACM0')
c = 0
while True:
t0=time.time()
raw = port.read(10000)
t1= time.time()
tooktime = t1-t0
print(f'{c:5d} receiving 10 kB now took', tooktime )
c+=1
First observation is that on Rpi Pico clocked at 250 MHz, one has to avoid using print()
as it is slow (some 30−50 kB/s). Calling usys.stdout.write()
is better (up to 200 kB/s), but usys.stdout.buffer.write()
can initially transmit 10kB in 0.015 s, getting close to the USB "Full Speed" throughput limit (i.e. ~600 kB/s). So I happily use the latter function for data.
Second observation: The trouble is that this speed drops continously - after few seconds of transmission, transmitting 10kB takes 0.050 s (i.e. only 200 kB/s). Then suddenly the transmit time returns to its initial value and starts growing linearly again. This repeats with the transmit times forming a sawtooth pattern.
Here I paste part of the printout on my computer where the drop in transmit time happened:
...
116 receiving 10 kB now took 0.051924705505371094
117 receiving 10 kB now took 0.05239605903625488
118 receiving 10 kB now took 0.05241680145263672
119 receiving 10 kB now took 0.05560755729675293
120 receiving 10 kB now took 0.04527783393859863
121 receiving 10 kB now took 0.015183687210083008
122 receiving 10 kB now took 0.015230655670166016
123 receiving 10 kB now took 0.01515960693359375
124 receiving 10 kB now took 0.015021562576293945
125 receiving 10 kB now took 0.015263795852661133
...
Complementary observations:
2a) Without overclocking, Rpi Pico running at 125 MHz, the transmit speeds are roughly half of what I wrote above, so the Rpi Pico is the bottleneck here.
2b) When transmit speed goes high, I thought the chip resets, but it is not the case (LED does not blink).
2c) If I put a random time.sleep(1)
after some hundreds of transmission cycles, the transmit speed does not change much after the delay and continues to grow steadily when delay expires (so it is probably not a USB buffer error).
2d) Between transmitting cycles, I observed no significant changes in the micropython.mem_info()
printout. So it is probably not a memory issue either.
2e) The period of main transmission loop (as measured on oscilloscope) is proportional to the time required to receive data in computer. So no packets are probably dropped.
Achieving fast USB transmission is a generally useful task and micropython on Rpi Pico does surprisingly well. At least initially. Can anybody give me a hint to get constantly high USB throughput, please?