Dual Core Programming for the Raspberry Pi Pico, ESP32, and ESP32-S3 Using the Arduino IDE

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello my name is Gary Sims and this is Gary explained now I hope you've had a chance to look at some of my videos about the power and the performance of different microcontroller balls so that's like the Raspberry Pi Pico Arduino board cortex M3 call it M4 balls blue pill black blue and so on I've got a couple of videos about that including the latest esp32 C3 board which has a risk 5 processor now the next step in that series is to measure the power and the performance of microcontrollers that have two chords dual core and that includes the rp2040 which is in the Raspberry Pi Pico the esp32 and the esp32 S3 so in this video I want to look at how you can program those three different microcontrollers from within Arduino and use both cores so if you want to find out more please well let me explain okay Cinema will be jumping over to the Arduino IDE we're doing some coding to look at how you use both of these cores at the surface the way you do it on the Raspberry Pi Pico is different to how you do it on the esp32 so I'm going to show you both of them and then in the end we're going to come with a unified sketch that actually works on both systems you can use that as a template it will be available on my GitHub repository and you can take that and start using it yourself knowing it will work on whatever microcontroller board you pick from esp32 or from uh from all the Raspberry Pi Pika or one of the compatible board for example and I'm also going to take a quick look at mutexes so you can see how data can be passed from one core to the other okay let's get cracking okay so let's start with an example of how you do dual core programming using a Raspberry Pi Pico or more specifically the Raspberry Pi 2040 microcontroller because in fact in this case I'm using the seed rp2040 boards rated tiny little ball very very nice and I'm going to use that completely compatible of course in terms of software with the Raspberry Pi Pico now normally in an Arduino sketch you would have set up and then you would have a loop and that's pretty simple so setup gets run once at the very beginning and then Loop gets run can round and round and round and round again forever and ever as the microcontroller is running now when you want to have dual core set up what you actually have is you have a setup and then you have a setup one so this is for core zero and this is for core one in fact it says here Arduino code will automatically normally execute on core zero and the second core sitting idle and in a low power State when you have set up one that gets run on core one so what we've got here well what we normally do is we just set up the serial that's easy enough and I've got a pro uh two variables count zero and count one one is going to go from zero up whichever's going from a thousand downwards and we're just going to print them both out using each core to do that very very simple work of course for the Raspberry Pi Pico to do but just gives us an example so here is our normal Loop thing and all we do is we just say well print out on the serial Loop and then count zero so that's all we're going to do and then we're going to add one to to count zero if it gets to a thousand put it back to zero against we're going to keep going zero all up to a thousand zero up to a thousand just keep going random round and round and we're just going to delay for one second uh before we do it again so we don't just get loads and loads of fast scrolling stuff but here's the thing now if we add Loop 1 then this automatically gets run on the second chords that literally that simple you just need to run this second function automatically on the second chord what we're going to do is we're going to print out Loop one and we're printing out count one A different variable and we're doing minus here and we're going to delay for a different amount of time so you can see they're both running kind of not synchronized they're running out of sync with each other one every two and a half seconds one every second and they just get run so really simple look at that not much code we just got set up set up one Loop and loop one and that's it that's all that's all you need and you're actually up and running doing dual core programming on a Raspberry Pi Pico okay let's run that and see it in action okay so here we can see it running and basically we can see that Loop which if you remember back to our code is just this one here that's what's running inside normal Loop prints out two three numbers at a time and then you get another one from Loop one a bigger number nine nine five nine nine four because it's going back so that's here Loop one is being printed out and because it's right it's pausing every two and a half seconds he's running every second so they're going to be interleaved two or three numbers there's two here's three here's two again and that's happened so that's now happening one on each core the bigger numbers are on one chord the smaller numbers on the other core and that's it you didn't ever do anything you just literally created a second Loop and it actually worked out of the box okay so how do we do the same thing on an esp32 board or an ESP uh S3 as an ap32 S3 board both of which are dual core boards from uh expressive uh system so we have to do something slightly different expressive systems in fact are all built on top of free rtos and they've kind of taken that and added in this kind of Arduino compatibility but underneath there's some free art of stuff going on but of course let's just start with the easy stuff there is of course a normal setup function and we have a normal Loop function very very similar to what we had with the Raspberry Pi p Eco one thing to note here that's different is I'm using this new function called export get core ID and this is a function that the ESP uh 32 framework provides to Arduino from free artist another standard free artos call but you get it here on this side it just tells you which core are you running on so here we're just saying print with the loop and then we're going to print out the core number so we know which one this is this chord this is running on and that's exactly the same here we're just sleeping for 1000 while we're doing all the counting there and look here we've also got a loop one exactly the same and again we're going to print out to find out which loop it's on by getting the number and it's doing from a thousand bound to zero with a delay of time so this part of the code is very very similar to what we have in the Raspberry Pi Pico except we've added in this thing to get the core ID now I've also if you look at it we've actually got this set up one as well which sets count one to a thousand but to do this I've actually had to put in an extra bit of plumbing and that plumbing with find here at the top and what's that here well first of all we need to define a task handle this is something that comes from free rtos for the set for Loop number one for that second core and then I've created a special function here called ESP Loop one that does what it calls setup one and then in an infinite Loop it calls Loop one so I've kind of provided the plumbing here for the ability to run a function over and over and over again inside of loop one and call setup one just once and we've called that Loop ESP Loop one and then there's a bit more here you add in this function call here which is basically again a call to something similar to what you're getting three Autos it's a function they've brought over for the Arduino for the esp32 but basically create a task what task well ESP Loop one that's what we've just defined up here okay and it's gonna have this Handler this uh the handle the variable to know about it is task list one which I defined up here and here's the important thing I want you to rally on whatever the Arduino code is not running this is a macro that's defined that tells you where the Arduino code is running on core zero or on call one and whatever core is running on running on the opposite one in fact look pin two task two core uh pin task two core I just put that there because that's a bit of a typo they shouldn't say that so it runs it on the opposite one to whatever it's running on so if you include this call to uh create pinned task and you include this little bit of Plumbing here then down here you can automatically use setup one and loop one and it runs exactly the same so there's a bit of code that you can actually take a Pico kind of stuff and you can get it running on your esp32 board in fact in a minute we're going to look at how you combine all this together so you get template code that runs on on both systems without any problem okay so let's run that what we're going to see we're going to see Loop zero and loop one one of them going from zero upwards I'm going from a thousand downwards just as we saw with the Pico so let's run that and see what happens okay so here it is running and we can see exactly the same thing we've got the higher numbers going down running on Loop zero and we've got the other code starting from running on Loop one now that's interesting because actually it was the other way around uh when we did it on the Pico and the reason for that is you've got this setting up here tools Arduino runs on and actually it runs on core one by default now you can switch this to core zero and because we're using that clever thing here I just say whatever the Arduino is running on do put this other function on the other core it you can have it either way around it automatically copes with it so if you use that little trick it will cope with the Arduino Loop is running on core one or on core zero your other loop will run on the other core automatically so that's just great way of getting that just working without him to worry about which core things are running on okay so what I've got here now is a unified template code that you can use all this of course will be in my GitHub repository that will allow you to run both RP 2040 and esp32 and esp32 S3 uh all the same code on Dual Core without them to worry about which platform because I built all that in here so first of all what's this first bit here so what we're saying to the compiler is if it's esp32 platform or it's the rp24 and there are two ways of recognizing that then it's fine if not print out a message at compile time saying this is unsupported uh platforms if you try to compile this for let's say the blue pill the black pill you're going to get that message come out so that you know that you're doing things uh wrong you can't do it like that and then what we do here is we we intermingle the code with some definition so for example if we know we're running on the ESP platform then we put in this Plumbing that I put in earlier the ESP Loop one and the task handle and calling Loop one forever but it only includes that code if we're using the esp32 or the esp32 S3 and then likewise if we are actually using the raspberry pi picot or one of its compatibles then we need to include the free rtos header because that's also supported and one other clever thing I use this export get core idea which is only available on the esp32 platforms but on the Raspberry Pi you actually use get core num so I've actually made these two synonyms when you call this on the Raspberry Pi I'll actually call this and then we get both functions and again we don't need to start worrying about which platform we're on to which call do I make we'll just start writing the same code and then again back in here in the setup similar thing we only call the X task create pinned to core if we're running on an esp32 if we're not running on esp32 then Loop one is automatically called by the Arduino implementation and then down here as you can see everything is the same even the call to export uh get core ID because on the Raspberry Pi Pico it'll call the get num core get corn I'm sorry and it will just work so here we are exactly the same code and now this will compile and run on either platform okay let me give you two demos just to show you that so now we're currently on esp32 so let's just compile and run that so here we can see running on an esp32 board that generic code just compiles out the box and as you can see we've got the numbers going uh up and some going down depending on which core it's running okay run the same program on the Pico won't be any different look exactly the same and that's of course the whole point it will look exactly the same okay let's put a Pico in there and try this so here we can see the same code running now on the Raspberry Pi Pico uh loop zero Loop one one's going from a thousand downwards one's going upwards exactly the same so now you've got some template code that you could just use for all your projects that you want to do dual core stuff on it doesn't matter whether you've gone with the ESP the esp32 sorry esp32 S3 all the raspberry Pico you can get to work just by using uh this setup setup one Loop and loop one and the code just works as long as you use this extra Plumbing up here that I've put in okay now the one thing that remains is how can you get uh the two cores to maybe you know get some data sharing between them let's have a look at that and again a unified model that works on both platforms okay so here is our unified dual core example with locking now what's locking mean well basically if you've got two cores running independently and they want to share data you've got to make sure they don't bump into each other they make sure they don't uh you know try to do the same thing at the same time it's called raise conditions it's called the read writer problem if I've got a video here on this channel about the dining philosopher's problem very similar problem two things independently are trying to grab hold of the same information if you do want a video on semaphore's mutexes and critical sections and general synchronization between two threads whether that's on Windows Linux or on you know on Arduino do let me know and I'll do a video just about that but what have we got in here all the same stuff here everything's the same one difference is here is that I've had to include this uh header file semaphore server 4.h here uh for the Arduino for the esp32 it automatically included and now here we've got a new number which is called R num it's going to be a random number and basically one thread is going to set it and the other thread is going to read it and so now you can kind of see that we're passing information between the thread one will actually read it and the other will write to it so to do that I've basically got a quick dirty hack of a function here that actually does random numbers it's interesting enough this used to be the random function number function that was in vax VMS for many many years it produces reasonable random numbers not what you'd want to use for encryption but good enough for just about anything else so that's just a nice little function there that does that and so if I call simple random long I'll get a random long number now here's where the interesting part starts we've got some locking code here and I end it down here to show the end of lock encode so you can see what the code is now we've got a few things going on in here first of all we have to create this semaphore in fact it's going to be a mutex but in free rtos it still calls it a semaphore because they're base one on the other I'm calling it big lock okay okay so I've got some lock helper functions here that allow us to kind of lock and unlock this mutex based on a semaphore now of course I could do this in C plus plus with a class and member functions absolutely that would work as well I'm just sticking here with some very simple C functions so basically we've got big lock lock and it goes into this while loop it says if the lock has been created of course that's the first thing we want to make sure it's actually active and there it says if it's been created and you try to take the lock if it fails just wait one millisecond go around try again now most of the time it succeeds straight away because we're dealing with milliseconds here and both processes are kind of just you know they're not exactly thrashing it out here but this is the whole point you need to make sure you protect the resources so it just goes around here and tries to get the lock and once it won't leave here until it has a lock so that's really important once you call Big lock lock it goes into that function it doesn't return until it has the lock and that means only one of the two cores can have the lock at one time they both can't own it at the same time so you lock it you say I'm having that and then we've got the inverse function here unlock and that calls this free uh free rtos function give semaphore give which releases the lock so give it back okay so here you take it and here you give it of course you don't have to wait for the give because once you've given it that's it you just give it and you you carry on your way so there's no Loop here so these are two ways of locking and unlocking now we've got some functions here set our num remember our num is this number that we've got up here and we're going to use a random number to set it so set our num what does it do it calls the lock if it can't get the lock straight away it'll wait a millisecond keep going around until it can get the lock okay and then it says okay set the r num to whatever number we passed in here which we'll see in a minute it's gonna be a random number and once you've actually do that unlock it again so basically you say hold on lock it set the number unlock it and while it's got the lock the other thread can't be doing anything with it and we've got the same for the get the r number so we lock it okay then we say find out what it is once you've got it you can unlock it and then return and say here's the number that I got now two things a note here often you don't need this for read okay this is what's called the reader's writer's problem if you don't have it for read the worst thing that can happen is that it's been updated and you get the old value because it was in the middle of reading it when it got changed Now read is generally Atomic but it won't get so we won't get a corrupt number but it could be one sequence behind if you want to make sure it's not like that you need to lock it the other thing to note is is make sure that you assign this to a variable local robot and then return it after you've unlocked if in here you just did return R num then it would never unlock and it would your program would lock up freeze because it wouldn't be able to go any further because the lock would be permanently held and no other function will be able to go any further and that's the end of the Locking code so what do we do well we have our normal boilerplate code here for setup setup number one and all we're doing is extra in here is I'm saying set the r number two simple random long that's our little random number function set the r num which means it would get the lock set the r num to that number unlock it okay and in the other loop we're actually I've changed the printf here to actually print out the result of get arnum in Brackets there so it's going around and it says I want to read the number lock take the lock read it unlock it and go on if they ever both come at the same time to try to change and read it one of them will have to wait while the other one completes and that way you make sure that nothing actually uh until water any corruption happens in your in your code okay let's run the code and just see it running okay so here we can see it running and basically Loop zero which is counting upwards that sets our num and loop one reads it and remember I've got that print out printf statement here that print out get on them so we know it's changing every time and how is it changing well because Loop zero is changing and loop one is reading it so there you go a synchronization or using the same bit of memory and what's happening is that one thread is changing it and the other thread is reading it and they're locked to make sure that there's no Collision going on between them okay that's it my name is Gary Sims and this is Gary expander we hope you enjoyed this video if you did please do give it a thumbs up if you like these kind of videos why not stick around by subscribing to the channel also if you've got any ideas about what else I'd like to see in this series of videos please let me know in the comments below don't forget you can follow me on Twitter at Gary explained and I also have a monthly newsletter go over to Gary explains.com type in your mail address no spam but you will get the newsletter okay that that's it I'll see in the next one [Music]
Info
Channel: Gary Explains
Views: 27,338
Rating: undefined out of 5
Keywords: Gary Explains, Tech, Explanation, Tutorial, Dual-core, Multi-core, Multicore, Multiprocessing, Multitasking, Arduino, Arduino IDE, C/C++, Locks, Semaphores, Semaphore, Mutex, Mutexes, Unlock, Critical Section, Core 0, Core 1, Core 2, 2 Cores, Two cores, Dual core, Microcontroller, RP2040, ESP32, ESP32-S3, Raspberry Pi Pico, Seeed XIAO RP2040
Id: w5YigjvSaF4
Channel Id: undefined
Length: 19min 47sec (1187 seconds)
Published: Tue Oct 04 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.