Multi Thread Coding on the Raspberry Pi Pico in MicroPython - Threads, Locks and problems!

  Переглядів 34,403

Bytes N Bits

Bytes N Bits

День тому

The Raspberry Pi Pico uses the RP2040 microcontroller chip which has two ARM Cortex M0+ cores. Usually when we code we only use a single core. This leaves half of the processing power idle.
In this tutorial I'll show you how to run code in parallel on the second core and how to correctly manage and share resources between the two threads.
I'll also show you how I got around some of the bugs in the threading package, which is still in the experimental stage.
Don't forget to check out my project page for more details and links to all the code used in the video.
bytesnbits.co.uk/pi-pico-mult...
You can also access the code at the project GitHub repository at
github.com/getis/pi-pico-spi-...
0:00 Introduction
1:56 Multi thread coding
3:26 Threading Package
6:06 Simple dual core example
8:50 Using global variables to share data
12:03 Using classes to share data
15:56 Using Locks or Semaphores
19:02 Lock example code
23:07 Polling the Lock
26:32 SPI LCD example
28:59 Bug in thread package
30:27 Running second thread on demand
36:00 Conclusion

КОМЕНТАРІ: 81
@robdriessen7675
@robdriessen7675 Рік тому
Have followed your tutorials with great interest; excellent work, very well explained. Thank you very much for sharing your insights. Rob
@BytesNBits
@BytesNBits Рік тому
Great to hear you're enjoying them.
@andreafavero71
@andreafavero71 11 місяців тому
Liked your tutorial, with a stream of examples from understanding the basics to a real case usage. I was searching threading related info for Raspberry Pi Pico, and this tutorial nicely appeared: I'm sure I'll give it a try. Thank you very much. Andrea
@BytesNBits
@BytesNBits 11 місяців тому
Great. Definitely put multi core into your programming toolbox.
@manuelcarriedo4404
@manuelcarriedo4404 Рік тому
Excellent explanation and great video. Thank you for helping us to better understand threading on Python.
@BytesNBits
@BytesNBits Рік тому
Hi. No problem. I hope you're found it useful!
@mandelbro777
@mandelbro777 Рік тому
Great explanation for beginners. Really loved the video.
@BytesNBits
@BytesNBits Рік тому
Glad you liked it
@1Geek4Ever1
@1Geek4Ever1 Рік тому
This was on point! Thanks so much. Worth every second of it!!
@BytesNBits
@BytesNBits Рік тому
Glad it was helpful!
@1Geek4Ever1
@1Geek4Ever1 Рік тому
@@BytesNBits today I was able to output some sound with a buzzer and some animation in the oled screen both at the same time thanks to your video. Im not sure if there is other way to do it but at least is working with threads.
@BytesNBits
@BytesNBits Рік тому
@@1Geek4Ever1 Great! There are a number of ways to solve any issue. Go with the one that works for you.
@L2-Lagrange
@L2-Lagrange Місяць тому
Very useful tutorial.
@BytesNBits
@BytesNBits Місяць тому
Glad it was helpful!
@anispinner
@anispinner 2 роки тому
You're awesome. Thank you for what you do
@BytesNBits
@BytesNBits 2 роки тому
You're very welcome.
@jeffschroeder4805
@jeffschroeder4805 Рік тому
It would be interesting to compare the maximum frame rates of each method to determine how much more efficient each was compared to using only one core. There must be some overhead involved in switching cores that could be quite significant.
@BytesNBits
@BytesNBits Рік тому
Hi. I did do some work on that in making this little RP2040 console - ukposts.infoQusjn49OLL4. I do plan a video based around it but work has been quite hectic for the past few months. The core switching isn't such an issue. Once you get your threads up and running they really do work in parallel. The key is in dividing up the tasks and making sure that one doesn't block the other when accessing resources etc.
@PeetHobby
@PeetHobby 7 місяців тому
Thanks for the great video. 👍
@BytesNBits
@BytesNBits 7 місяців тому
Thanks for watching!
@zakarialbouhmadi3060
@zakarialbouhmadi3060 8 місяців тому
THANK YOU SIR FOR THIS DETAILED EXPLANATION :)))))))))))))) Greetings from Morocco
@BytesNBits
@BytesNBits 8 місяців тому
Glad it was helpful!
@himdimzma
@himdimzma Рік тому
Great video
@BytesNBits
@BytesNBits Рік тому
Glad you enjoyed it
@yahmk3978
@yahmk3978 11 місяців тому
Thank you!
@BytesNBits
@BytesNBits 11 місяців тому
Hope you're finding these useful.
@homelessrobot
@homelessrobot 9 місяців тому
then theres the PIO stuff. they basically function as 'event computers' that generate CPU interrupts based on the interaction between a state machine and the input stream. This makes it easy for drivers to be efficient because there is no impedance gap to cross converting from a raw data stream to contextually meaningful IO events. While the PIO is figuring out what events it should tell the CPU about happening on the IO pins, the CPU is free to do whatever it likes, and is not occupied doing this work itself like it would be in many other computing environments.
@BytesNBits
@BytesNBits 9 місяців тому
Thanks for the info. PIO is something I intend to look at in some upcoming videos.
@umutkayacan7659
@umutkayacan7659 2 роки тому
a 40 min vid,let me get the snacks!
@BytesNBits
@BytesNBits 2 роки тому
Great. Hope you enjoy it! Some popcorn and a coke for me!
@lanehauck8720
@lanehauck8720 Рік тому
Thank you for the time and insight you provide with your channel. If each thread waits for the other one to finish its access to the frame buffer, what is the advantage of using two cores? The renderer can only do a little work (some Trig maybe) before its main job of accessing the frame buffer. The two cores would indeed run in parallel if the frame buffer were double-buffered, but of course this is not possible with the small Pico RAM size.
@BytesNBits
@BytesNBits Рік тому
You're right about the double buffering but this setup can still work well. The idea here is that you split your code into an update phase and a render phase. The update phase is the parallel part so you try to put as much of the processing load into that. The bouncing box demo doesn't really have any update processing but the Asteroids game (I hope that's shown in the video!) performs all the polygon rotations, collision detection, etc. leaving only the actual drawing of lines into the buffer for the render phase. You can actually make some great multi thread savings this way.
@lanehauck8720
@lanehauck8720 Рік тому
@@BytesNBits Great answer, thanks.
@sambeard4428
@sambeard4428 Рік тому
Great thorough explanation, thanks for that. One point of critique, I'm not a big fan of the 'live drawing' animation style found at the beginning of the video. It adds a lot of visual noise and especially distracts from your speaking. This however is a personal preference but imo worth mentioning
@BytesNBits
@BytesNBits Рік тому
Glad you enjoyed the video. Sorry the whiteboard animations are not your cup of tea.
@awesomegamer31
@awesomegamer31 Рік тому
Wondering if you have any more info on how to use the REPL tool in PyCharm - for example, flashing a program to it causes it to run immediately, but when opening REPL the keyboard interrupt ends it. Using the soft reboot is enough to reset it, but it's an issue using the socket library for my web sever as I get the EADDRINUSE OSError, essentially that the static IP address I want to bind to is already being used, but by the instance that was interrupted. You seem to be able to just upload and start it in a few examples here? Plus the terminal window you use just says 'Local', mine has a path to "...\venv\Scripts\python.exe". And obviously I can't flash a new version of the file unless I close the instance of REPL terminal as the COM port is in use... If you know of any sources online about how to use REPL to debug beyond the official documentation (which is not great) I would appreciate it a ton :)
@BytesNBits
@BytesNBits Рік тому
Hi. Make sure you're using the latest version of PyCharm. They fixed a number of REPL bugs a few months ago. After that make sure you've got the project set up and the extra Micropython add ons installed. If you upload a file called main.py it will auto execute, or if one already exists that will execute. Try saving the main entry point for your code as something different , e.g mycode.py, and delete any main.py. Once you upload you'll be able to REPL onto the device and start the code manually, import mycode.
@HitAndMissLab
@HitAndMissLab Рік тому
What is that IDE that you are using? As well could this MicroPython setup you are using guarantee timing precision during execution?
@BytesNBits
@BytesNBits Рік тому
Hi. I use PyCharm for my coding. There is a free community version that does everything you need. Make sure you install the MicroPython plugin so it can talk to the Pico. MicroPython is an interpreted language so is much slower than C++. It is aware of time and you can use this to make sure things happen at the correct time. If you are trying to get very tight timing e.g. some sort of signal timing, then I'd probably go with C, or have a play with the PIO system.
@billyheng4824
@billyheng4824 Рік тому
Will there be any issue when apply the same method to all the other cores available ? Assumming earch cores handle one box redenering around the LCD and running around. The use case is appilcable for max. 8 application running concuerrently or different input running curently ?
@BytesNBits
@BytesNBits Рік тому
Hi. The RP2040 only has 2 cores for you to use. You can only run 1 thread per core. If you are using a processor with more cores then the same method would apply.
@arturertel
@arturertel 7 місяців тому
i am trying to program a clock (micropython, raspberry pi pico). my goal is to be able to set the time while the clock is ticking. is this possible or is it a contradiction.
@BytesNBits
@BytesNBits 7 місяців тому
That should be fine. Your code will need to take account of the time reset in the way it tracks time. I'll leave that to you to work out!
@mexiquenfrance6084
@mexiquenfrance6084 Рік тому
I was having the same crashes where my rpi got frozen using the second core... The way I fixed it was using a "return 0" at the end of the function instead of the GC.
@BytesNBits
@BytesNBits Рік тому
I hadn't considered that. I'll give it a go. Thanks for the tip.
@samneggs1
@samneggs1 2 роки тому
_thread has been worked on since the official 1.18 version. Try using a recent nightly build, it appears much more robust.
@BytesNBits
@BytesNBits 2 роки тому
Hi. Thanks for the tip. I'll give that a go.
@arnolduk123
@arnolduk123 Рік тому
I'd be interested to see the code running on a single thread and compare any advantages as both your threads are locking. Each thread is waiting for the other to finish, so in effect only a single thread is running code while the other waits. Why not use 2 buffers and a signal from the drawing thread to force the display thread to update one of the buffers ?
@BytesNBits
@BytesNBits Рік тому
Hi. There's not enough memory for a double buffer so the single buffer becomes a bottleneck. Only the redraw to buffer can't run in parallel so the idea is to keep that section as fast as possible by doing all the calculations etc. in the parallel update code. The dual core advantage only starts working as the complexity of the game mechanics increases. In the boxes demo the model updates aren't very processor intensive so not a great increase in speed from dual cores, but the Asteroids code processes all the screen objects as line vectors with full polygon collision detection so we do get quite a speed boost.
@arnolduk123
@arnolduk123 Рік тому
@@BytesNBits Ahh, yes, MicroPython taking memory and resources. Probably more practical to use pure C++. What you have now though, is good enough and works pretty well.
@enricorossini
@enricorossini 6 місяців тому
I’m not sure about the fact that the second thread is running on the second core. I’ve already used threads in my projects but with an ESP32-C3, so a single core microcontroller and it runs without problems.
@BytesNBits
@BytesNBits 6 місяців тому
The Pico handles threads differently to the ESP32 in MicroPython. On the ESP32 MicroPython code can only run on one of the cores so your threads are running as tasks on the same core. The Pico allows you to run MicroPython on both cores (mostly) but can also use the asyncio package to get multiple tasks per core.
@fendtbluebird4701
@fendtbluebird4701 Рік тому
FreeRTOS and Multicore.
@BytesNBits
@BytesNBits Рік тому
Thanks for the tip. I haven't played around with RTOS yet.
@graydrake6855
@graydrake6855 5 місяців тому
even if makes the example understandable, you should mention that it's never a good idea to do a sleep() while you hold a lock, which another thread may be waiting on
@BytesNBits
@BytesNBits 5 місяців тому
Thanks for the tip.
@V1P3RSlab
@V1P3RSlab Рік тому
Hello, wanna try your example, but only got a 1.8 tft spi (128x260) (the red one with sd card reader) and no MISO nor MISI but only A0 and SDA ... how can i adapt teh code?
@BytesNBits
@BytesNBits Рік тому
Hi. Are you sure this is an SPI lcd? Those connections sound more like part of an I2C interface.
@V1P3RSlab
@V1P3RSlab Рік тому
@@BytesNBits they sell like "AZDelivery Display SPI TFT 128 x 160 Pixel" :-(
@BytesNBits
@BytesNBits Рік тому
@@V1P3RSlab I think A0 is the DC pin. SDA is MOSI. There is no MISO as this isn't really needed for the TFT panel. The other pins should match the video. I hope this helps.
@V1P3RSlab
@V1P3RSlab Рік тому
@@BytesNBits trying, i let you know :D
@V1P3RSlab
@V1P3RSlab Рік тому
@@BytesNBits no way. Using pycharm i got some compile errors on library versions, using thonny, i adapt the code but receive the "unsupported display"
@arnolduk123
@arnolduk123 Рік тому
WOW, Gary Lineker coding pico's, what next, Gazza debugging Arduino's 😄
@BytesNBits
@BytesNBits Рік тому
?????
@arnolduk123
@arnolduk123 Рік тому
@@BytesNBits Sorry, google Gary Lineker (England footballer), you look like him 😀
@jyvben1520
@jyvben1520 2 роки тому
Is the buffer a string of 75KB ? are 2 buffers used ?
@BytesNBits
@BytesNBits 2 роки тому
Hi. Basically yes - 75KB of single byte integers / characters. There's not enough memory for 2 buffers so the processes have to share the one memory block. Double buffering would be great but not really an option without more RAM.
@jyvben1520
@jyvben1520 2 роки тому
@@BytesNBits how many colours are used ? could 4 bits be enough ?
@BytesNBits
@BytesNBits 2 роки тому
@@jyvben1520 Hi. For the LCD panel I was using it's 16bits per pixel. You can store the colour info in memory in any way you want but you have to expand it out to the full 16 bits to send to the LCD. Pure Python isn't fast enough to do this on the Pico.
@user-um3ui1gu9t
@user-um3ui1gu9t Рік тому
Where did you got the idea that multithreading is implemented using the 2 cores ? There is nothing in the documentation that can lead to this conclusion Please explain why you think so. Also explain what happens if you declare 3 threads...
@BytesNBits
@BytesNBits Рік тому
Hi. The Micropython threading package assumes one thread per core. I know in more powerful processors you can run multiple threads per cores but that doesn't seem to be implimented in MicroPython.
@user-um3ui1gu9t
@user-um3ui1gu9t Рік тому
@@BytesNBits Again, there is no such thing in the documentation The Micropython documentation points to CPython documentation and CPython does not know about multicore. Beside, MicroPython runs also on the ESP32 which has only one core... Please post the web page adress where you found this info Thanks
@BytesNBits
@BytesNBits Рік тому
@@user-um3ui1gu9t Thanks for your comments and tips.
@user-um3ui1gu9t
@user-um3ui1gu9t Рік тому
@@BytesNBits Finaly, I checked the core assumption using a simple script which reads the CPUID register from the RP2040 and you're right, the first thread runs on core 1 Adding a second thread as you mentioned leads to an error Here is the script for those who are interested : ----------------------------------------------------------- import time, _thread from machine import mem32,Pin led = machine.Pin(25, machine.Pin.OUT) def Core0(): while True: led.on() time.sleep(5) #read CPUID print("Core0 = ",mem32[0xd0000000]) def Core1(): while True: led.off() time.sleep(5) #read CPUID print("Core1 = ",mem32[0xd0000000]) _thread.start_new_thread(Core1, ( )) time.sleep(1) Core0() ----------------------------------------------- Core0 output is 0 and Core1 output is 1 Regards
@kippie80
@kippie80 10 місяців тому
No thanks. I’ve no tolerance for garbage collection.
@BytesNBits
@BytesNBits 10 місяців тому
The package is still in development. Hopefully this is just a work around until the bugs get ironed out.
@diegoalonso8573
@diegoalonso8573 11 місяців тому
Lol ... Couldn't get a harder way to complicate a basic example??? 😂😂😂😂
Small, full power RP2040 boards - Seeed Studio XIAO-RP2040
11:42
Bytes N Bits
Переглядів 13 тис.
In-depth: Raspberry Pi Pico's PIO - programmable I/O!
17:19
stacksmashing
Переглядів 126 тис.
I PUT MY ARMOR ON (Creeper) (PG Version)
00:19
Sam Green
Переглядів 6 млн
Что будет с кроссовком?
00:35
Аришнев
Переглядів 2 млн
Installation of Bliss OS 15.9 in Legacy Bios mode
7:03
Electronic Category
Переглядів 172
Raspberry Pi Pico - PIO explained
50:01
Slador
Переглядів 24 тис.
UART | Raspberry Pi Pico Workshop: Chapter 4.2
11:57
Core Electronics
Переглядів 777
#372 How to use the two Cores of the Pi Pico? And how fast are Interrupts?
14:25
Writing fast and efficient MicroPython
31:42
PyCon AU
Переглядів 66 тис.
Use BOTH Cores  |  Dual Core Programming on the Raspberry Pi Pico
7:49
Low Level Learning
Переглядів 37 тис.
The Raspberry Pi Pico WAS Overrated! But that changed!
10:18
GreatScott!
Переглядів 580 тис.
Use the very attractive new ATTINY chips for your projects
14:34
Andreas Spiess
Переглядів 190 тис.