 So we've now had our play around with Writing Software and Assembly, which you can see is fairly straightforward, but is not the easiest to follow the application flow. So we now have some application processes to help you with writing the software. So the goal of this section is to become familiar with some software flow designs. So we're going to show you four different types of flow for writing your application. We're going to show you the advantages and disadvantages of each of those techniques. And we're going to show you what features are inside the STM32 to help you with each of these different techniques of software flow. So the four main flows are polling, interrupts, DMA and a real-time operating system or RTOS. And then you can combine any of those above four methods as you see fit within your application. So this is mainly what we're going to see when you write any particular piece of code. You'll normally be in the structure of one of these four or as I said a combination of one of these four. So we'll first have a look at polling. So what actually happens within polling is the micro is permanently waiting for something to happen. So you're permanently monitoring a bit or a flag to see has that flag been set before I can then proceed and go on to the next section of the software. This particular polling method works very well when the application is fairly simple and it can go through permanently waiting around. Is this being set? No, great. We'll wait until it is set. Then we'll move on to the next section. If the application doesn't have many peripherals to service, then again polling is very, very helpful. You can see exactly what's going on and you are in full control of the priority of this polling. So you can actually configure the software so that you know certain conditions have to happen before you do other conditions within the software. For those of you who've got families, you know polling very easily. It's when your child in the back seat keeps saying to you, are we there yet? So he's permanently polling you as the driver to say, are we actually have arrived at the destination? And he will keep asking that question until he actually gets to his destination or you get the car to the destination. So polling, as I say, it happens in real life as well as software and it's always out there waiting on another variable to have happened. So the flow diagram for polling. We start the software. We'll initialize our information. And then the peripheral will say, have we received the information yet? If the answer is no, it'll loop around and ask the same question again. As soon as we've received the information that we've requested, we'll process that information and then we'll go back saying, have we received the next piece of information? So it's a very simple loop and it's very easy to manage the actual software for polling. As I said, the advantages very easy to work with. It's very easy to debug and it is deterministic. It's deterministic waiting on what's going on from the other side. So you know your software will happen as soon as the outside event happens. Disadvantages are if your software gets too large or more complex, it gets very difficult to maintain the software using polling. You might get poor responsiveness depending on how you've structured your polling loops. And polling is probably the most least efficient way of writing software if there's not much going on within the actual software flow. So most of the time the process is running around this empty loop, asking the question, has the flag been set? Which is a waste of resource, it's a waste of power. Unless you've got lots of things happening in your polling routine. So if your polling is monitoring something that's coming in with every few milliseconds, then polling then is a reasonably efficient way. But if you're waiting on events that are quite spaced out, then polling becomes quite an inefficient way of doing a software flow. To improve on polling we have interrupts. For those of you who don't know what interrupts are, it's where an event will trigger a interrupt to the CPU itself and request that you go and process that interrupt at that moment in time. So your main program execution is stopped and then that interrupt service routine that you've written specifically for that event that has happened is then executed. After your service routine has been completed, your main routine will then resume and carry on doing whatever the main routine was doing. If you want to save power, then your main routine might be to put the main microcontroller to sleep or it might be doing another task. So it might be processing some ADC inputs and you're waiting for information coming in on the UART and you want to process the UART and then go back to processing the ADC results. Interrupts are very good for transferring small amounts of data. So the whole point of interrupts are they're quick, they set something, you go back to doing what you were meant to be doing. So you don't want your interrupt service routine to be too large because then that could stop whatever is running in your main flow from executing. So you've got to be prepared to make sure that your interrupt service routine is fairly small and very efficient for what you need to do. The diagram there at the bottom shows what happens. So your main routine is executing. Then we have the interrupt arriving. There is a small delay where the parameters of your main routine are pushed onto the stack to be saved. Then we execute our interrupt service routine software. Then we have again another small delay where we have the unstacking process and then our main routine will resume and we will carry on executing our main code at that point. So the flowchart for interrupts starts off exactly the same as polling where we start the process, initialize all the clocks and peripherals. Then normally you would enter some form of low power mode or go off and run some main task. As the interrupt comes in, we will service the interrupt routine and then we'll go back to that main task again, whatever we're doing or the low power mode that we're executing. So all the STM32s have got a nested vector interrupt controller. So you saw this earlier on in the slides when we were starting up the microcontroller where this vector table gets initialized. There is a section in the vector table which is the gray section. These are core dependent interrupts. So these are defined by ARM in the ARM standard and some of them you cannot change the priority of. They will always have very, very high priorities. Once you enter the blue section of our interrupt vector table, then this is where ST have defined the interrupts for each of these peripherals that we've got on the STM32. And these now can have a software priority connected to them so you can change the physical level of where it interrupts inside the software. So if you have two the same, then it will then default back to this hardware layout on what address they are. But if you don't have that many, you can set priorities via the software. Unlike all the ARM processors, all interrupts are handled in C. So you don't have to drop out of C, do something in assembler for pushing and popping off the stack. Everything can be managed in C. And it's all quite user friendly now if you're using our HAL libraries. There's no special commands that you have to enter for running any of the interrupts that we've got. We have some help for you when it comes to running interrupts. We have something called preemption, tail chaining and later rival. These are shown here. So preemption is where you set the priority of the interrupts. And your higher priority interrupt can interrupt a lower priority interrupt. So if we look at the preemption section, you can see interrupt one arrives. We have the 16 cycles of stacking that goes on. Then we start executing interrupt number one. Then IRQ2 arrives, which has a higher priority than interrupt number one. So IRQ2 can interrupt IRQ number one. So we have 16 cycles again for stacking. Then we'll process IRQ2. Then we'll have the unstacking process. Continue with IRQ number one until that's complete. Then we'll unstack IRQ1 and go back and execute our main software. That is a standard flow of preemption. Then we have tail chaining. So this is where a second interrupt of lower priority arrives while we're processing the higher priority interrupt. So in this case IRQ2 comes in, interrupts main. We have our 16 cycles of stacking again. We'll process our IRQ2 event. Then rather doing the 16 cycles of unstacking, 16 cycles of re-stacking for IRQ1. This six cycles in the middle is what we call tail chaining, which is loading all the new parameters for IRQ number one. We'll complete IRQ number one. Then we have our 16 cycles of unstacking again and then we go back to main. So it just saves you a bit of processing cycles when you receive multiple interrupts within the same period of time. Then finally we have late arrival. So late arrival is when a higher priority interrupt arrives during the stacking process. So our main dot c or main program is running. IRQ number one triggers the interrupt and starts the stacking process. During that process IRQ2 arrives. Therefore with late arrival it means the higher priority will get executed first. Tail chaining will then happen because there's a pending lower priority interrupt at that point. And then IRQ1 will then get serviced. So it looks very similar to tail chaining, but you've got this small window while the stacking process is going on where IRQ2 can still be executed ahead of IRQ1 even though IRQ1 has triggered the interrupts in the first place. Advantages of interrupts. It gives you better responsiveness. So you will jump from your main routine, execute whatever's happened and then return back to your main routine. So it can be slightly faster responsive than polling. It means you can work with low power modes a lot more. So you can put the device to sleep when there's no interrupts arriving and only awake process and then go back to sleep. And you can run with nested interrupts. So you can set your software priorities to load the interrupts how you actually want them to run. So you're in control of the flow of your software based on your priorities you set for the interrupts. The only disadvantage is that with all these interrupts going on, debugging can get slightly more complicated when it comes to following the flow of your software because the interrupts can be happening at any random time. So it's not as straightforward to follow the debug flow through the system. So as we said earlier, the interrupts are very good for small amounts of data transfer. What happens when you want to transfer larger amounts of data? So this is where DMA now comes in. DMA or Direct Memory Access is a way of transferring information from peripheral to memory or memory to peripheral without the need of CPU. There are interrupts involved, so the DMA will interrupt the CPU and you can configure when that happens. So you can either have it at half transfer or transfer complete. You can have it in a circular mode if you want. So if you want to have a continual buffer going round all the time, you can permanently keep filling the buffer or you can use it in just normal mode, which is a one-off cycle through. It's fully configurable, so you can set the data width of what you're transferring. And as I said, it's very good for transferring large amounts of data. So it is specifically designed for transferring large amounts of data. On the STM32F0, we've got 12 channels of DMA inside this device. Just like interrupts as well, we've got priority levels for each of those channels. So you can set each of those channels up to however you want to set them. Here's a quick look at the architecture of the STM32. So inside the structure of the STM32, we have two masters on our bus matrix. We've got the core itself as the bus master, and the DMA is also a bus master. The reason why the DMA is a bus master is it means it can access all of the slaves connected inside the architecture. Now the slaves are the memories, flash, SRAM, and all the peripherals. This means that the DMA can route information streaming in from any of the peripherals, ADC, UR, SPI, directly into the SRAM. So you have a data buffer in the SRAM to collect the information from each of those peripherals. While the core is either asleep, saving power, or processing another task. There is an arbiter inside the bus matrix so that the DMA cannot steal too many cycles on the bus matrix to impede what the Cortex core is itself doing during its part of the application. The flow for the DMA access is again starting off just as before where we initialize the clocks and peripherals. Then we start our DMA transfer, or our data transfer with DMA, and then either go off to process something else or enter low power mode. And just like the interrupts, we'll get our interrupt to say half complete or fully complete. We'll go off and process the DMA interrupt service routine, which tells us which task the DMA has finished. And then we come back to either low power mode or back to our normal task. So DMA is very, very similar to interrupts, but designed for large amounts of data to be transferred. Advantage is a DMA. It can transfer faster than the CPU, so it can move the data around faster because it's been specifically designed for that one task only, whereas the CPU is designed as a processor, not just a data movement block of IP. The CPU can go off and do other things while this data transfer is going on, where it can actually be more beneficial and process things faster. And DMAs can also increment target registers. So if you're filling the buffer, the DMA can automatically increment the pointer in the buffer to keep filling the buffer up until we get to complete. Disadvantages, it's not really a disadvantage. The device needs a DMA controller. All STM32s have a DMA controller, so it's not really a disadvantage. Some 8-bit devices that you've probably seen in the past don't have DMAs, therefore the disadvantage is the fact that you know DMA, that STM32 has DMAs in every single device that we make. There's also another disadvantage which is questionable as well. It could increase current consumption of your application. Yes and no. Yes, it will increase the current consumption of your application and your Cortex Core is busy doing something else because you have another set of silicon processing data or moving data around. If your Cortex Core is asleep, then it will significantly decrease your current consumption. So I won't exactly say that was a disadvantage. It will depend on what you're doing in your application. Finally, we have RTOS, a real-time operating system. It's not essential for all applications, so we're not saying that you have to use an RTOS, but it may become very, very useful when your applications become very complex or if you have lots of different tasks happening at the same time. So what an RTOS does, it tasks splits the CPU over the time. So it looks like you're processing multiple tasks simultaneously, but in reality, everything is serial. You only have one core, one CPU, but everything is now time-sliced up to service each of those tasks. There's lots of different RTOSes available for the STM32. A few of them are free of charge, like free RTOS or the Keil RTX. Most of them are professional RTOSes, and there usually will be some form of charge involved in them. The STM32 has dedicated hardware available for running an RTOS. So we have a dedicated timer called SysTick. We have two stacks, one for the process stack, one for the main stack, which is running the RTOS kernel or the RTOS core. And we've got all the dedicated interrupt vectors still available to do your normal interrupting for the peripherals. So the flow is slightly different for an RTOS. So we have the same startup, so initialization of clocks and peripherals. Then we want to initialize the RTOS itself. So set up the stack allocations for the RTOS kernel itself. Then we will initialize the task scheduler, which will set up the stack allocations for each of the tasks that we want to run. And then those tasks will run as and when they're required to for your process flow. Advantages of the RTOS, really, really good when you've got, as I said, complex applications. So if you've got lots of things going on, it can be really good. It also means you know exactly how much memory you're going to use, because when you set up each task, you've got to define the amount of memory you expect that task to actually use during its operation. Disadvantages of running an RTOS, there is an overhead. Every time you switch tasks, there is a time where you're not actually processing anything. And your memory footprint will be slightly larger. You've got the kernel itself of the RTOS. And then each task has its own dedicated section of memory. So you will be running a slightly higher memory footprint than you would in a normal application. So hopefully now you've got an understanding of all the different software flows and the designs that are available for use. These aren't just specific to the STM32, these are just general software design flows. You've now seen the advantages and disadvantages of each technique on the STM32. And you now know what you've got available to you to run your application on the STM32. So hopefully now you've now got the bigger picture to understand when we come to writing our code now in C.