FreeRTOS - pre-emptive or not? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: October 08, 2021, 03:38:31 pm
content: I've been writing a lot of code for this for last 10 months. All working fine. I use the base priority (zero) for all threads, and always yield to RTOS with osDelay(1) when hanging around. It all works great.Someone else working on another specific part of the project (replacing the buggy as hell STM-supplied PolarSSL with MbedTLS) has spent a lot of time trying to make that work also (again, buggy as hell and as usual loads of posts all over the place from frustrated coders trying to make it work - this seems to be a pattern with most STM libs and especially SSL stuff) and it appears that this code relies on some threads having a higher priority than others, otherwise the thing just hangs. Is this a weird way to write software? If one thread is generating data and another is consuming it, should they not be same priority but using some appropriate mechanism to communicate? I struggle to think of a scenario where different priorities are the only option, and there is a price to pay because the more levels you define the more RAM it uses.I am not totally new to this. I wrote my own simple RTOS, in assembler, for Z180 and Z280, yonks ago, and it was absolutely solid and was used in loads of fair volume comms products. It facilitated easy development of complicated protocol converters. But I never implemented priority, because all critical stuff was running off interrupts, and if all the code is written properly and yields when hanging around, everything will still run. And comms products should have buffers on I/O which are interrupt driven. Of course I see that sometimes you want different priorities but often this is not needed, and could be "dangerous" because some tasks may never run at all.So I bought that book on FreeRTOS recommended here recently and been reading through it. It looks like this RTOS does time-slicing so even a hanging thread gets chopped eventually (not sure where the time delay is configured) butonly by a same or higher priority thread.This is the configCode:#define configUSE_PREEMPTION 1#define configSUPPORT_STATIC_ALLOCATION 1#define configSUPPORT_DYNAMIC_ALLOCATION 1#define configUSE_IDLE_HOOK 0#define configUSE_TICK_HOOK 0#define configCPU_CLOCK_HZ ( SystemCoreClock )#define configTICK_RATE_HZ ((TickType_t)1000)#define configMAX_PRIORITIES ( 56 )#define configMINIMAL_STACK_SIZE ((uint16_t)512)#define configTOTAL_HEAP_SIZE ((size_t)48*1024)#define configMAX_TASK_NAME_LEN ( 16 )#define configUSE_TRACE_FACILITY 1#define configUSE_16_BIT_TICKS 0#define configUSE_MUTEXES 1#define configQUEUE_REGISTRY_SIZE 8#define configUSE_RECURSIVE_MUTEXES 1#define configUSE_COUNTING_SEMAPHORES 1#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0#define configUSE_TRACE_FACILITY1#define configUSE_STATS_FORMATTING_FUNCTIONS1#define configAPPLICATION_ALLOCATED_HEAP1// to use external RTOS buffer/* Co-routine definitions. */#define configUSE_CO_ROUTINES 0#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )/* Software timer definitions. */#define configUSE_TIMERS 1#define configTIMER_TASK_PRIORITY ( 2 )#define configTIMER_QUEUE_LENGTH 10#define configTIMER_TASK_STACK_DEPTH 256/* Set the following definitions to 1 to include the API function, or zeroto exclude the API function. */#define INCLUDE_vTaskPrioritySet 1#define INCLUDE_uxTaskPriorityGet 1#define INCLUDE_vTaskDelete 1#define INCLUDE_vTaskCleanUpResources 0#define INCLUDE_vTaskSuspend 1#define INCLUDE_vTaskDelayUntil 1#define INCLUDE_vTaskDelay 1#define INCLUDE_xTaskGetSchedulerState 1#define INCLUDE_xTimerPendFunctionCall 1#define INCLUDE_xQueueGetMutexHolder 1#define INCLUDE_uxTaskGetStackHighWaterMark 1#define INCLUDE_eTaskGetState 1

author: capt bullshot
author_rank: Super Contributor
timestamp: October 08, 2021, 04:07:43 pm
content: Looks like we've got some experiences in common, since I've written my own simple (cooperative) embedded OS too, and used it in some commercial products, too.The way you've done it (yielding to the OS if you've got nothing to do) is the way this just works fine without priorities. With preemptive tasking and priorities, something more flexible and more complicated and more prone to failures (due to the complexity and required knowledge) rose. It also works, if things are done right, but IMO there's more ways to do things wrong with priorities and preemptive tasking.And yes, tasks don't have to yield to the OS at all if they're idle as they can be preempted by same or higher priority tasks. Anyway, I wouldn't recommend to do so (to not yield) as it'll always fully load the CPU and have other negative side effects (most important: other tasks could have more time). So yielding and using proper OS supplied inter-task communication (like queues, semaphores, ...) as these will yield implicitely is the correct way to use any RTOS.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 08, 2021, 05:06:53 pm
content: Not sure what your question is here, if there is any. FreeRTOS sure is preemptive.Now properly using priority with preemptive scheduling isn't trivial, but as far as I've seen, it's not specific to FreeRTOS. The latter implements pretty basic priority-based scheduling.

author: peter-h
author_rank: Super Contributor
timestamp: October 08, 2021, 07:26:21 pm
content: A few questions in my post e.g.- why is pre-emption by lower-priority threads inhibited (and not configurable afaik)- what is the time slice interval for threads which don't yield- is it smart to make use of priorities to get code work at all, and likely scenarios where somebody would do that (it seems to produce impenetrable software)I know reams have been written on multiuser etc OSs over decades (I remember debates re Windows gradually moving to pre-emptive; it wasn't in 3.1 afaik) but this is more specific.

author: capt bullshot
author_rank: Super Contributor
timestamp: October 08, 2021, 08:31:10 pm
content: Quote from: peter-h on October 08, 2021, 07:26:21 pmThis is the whole point of assigning priorities to threads. Higher priority tasks cannot be preempted by lower priority tasks. Or the other way round: A higher priority task gets executed as soon as it is required and while it is running, all lower priority tasks must wait.QuoteDepends on the configuration, could be e.g. 1ms. Often generated from a ticker interrupt. With each ticker interrupt the scheduler gets invoked and gives control to another task, depending on priorities. Within the same priority, tasks are scheduled round robin. There are tickless variants of RTOSes, too. These usually calculate the next task switch from all pending tasks and priorities and dynamically program a timer for that next scheduling event.Code:configTICK_RATE_HZ ((TickType_t)1000)QuoteNo (IMHO). Any task that has to wait for something should yield, either by sleeping or waiting for an event.You could have a task that does some independent computation all the time, but isn't critical in terms of how long it takes to calculate whatever. This task would never finish or yield at all. You'd assign this task a low priority, so other higher priority tasks that e.g. have to do some data shuffling on I/O events can execute when they're needed. These tasks would wake up on some event and go to sleep or wait for an event when their job is done.A common example of priorities would be the idle task: It has the lowest priority at all and only gets executed when all other tasks are waiting for something.

author: westfw
author_rank: Super Contributor
timestamp: October 08, 2021, 09:24:12 pm
content: QuoteOnly if it's configured that way?https://www.freertos.org/a00110.html#configUSE_PREEMPTIONHmm. Also:Quote

author: peter-h
author_rank: Super Contributor
timestamp: October 09, 2021, 07:55:04 am
content: "Depends on the configuration, could be e.g. 1ms."I think yes, it is this. The tick is 1ms and I see no separate config item for slicing hanging threads after some different time period, say 100ms.

author: mikerj
author_rank: Super Contributor
timestamp: October 09, 2021, 09:24:47 am
content: Quote from: peter-h on October 09, 2021, 07:55:04 amIf you want the thread to be suspended for some longer period then the task itself must handle this, e.g. though a call to vTaskDelay, vTaskDelayUntil or xTaskDelayUntil which will suspend the task for an integer number of system ticks. Then you have task notifications, mutexes/semaphores and queues that can be used to suspend a task and have it woken up by another task or some other event such as an interrupt.Once you get your head around the fundamental concepts of a preemptive RTOS it will make sense.Quote from: peter-h on October 09, 2021, 07:55:04 amWhat would be the point of task priorities if this were allowed to occur?

author: newbrain
author_rank: Super Contributor
timestamp: October 09, 2021, 12:14:56 pm
content: Quote from: mikerj on October 09, 2021, 09:24:47 amThere are cases where this is done, and for good reasons, namely:priority inheritance for mutexesto avoid priority inversion.This is when a high priority task is waiting on a mutex held by a low priority task - which has no chance to run as there are other higher priority tasks able to run.The mutex holding low prio task will be raised to the priority of the waiting task, interrupting the formerly-higher-but-now-lower prio tasks that were holding it from running.(hope it's clear...)Quote from: peter-h on October 08, 2021, 03:38:31 pmThis is a bit funny.Priorities are a very effective way to make sure tasks run when they are supposed to.Having them all the same might work in some cases, here, though, you have to resort to explicit yielding: IMHO this means a more rational distribution of priorities might help.What I find even more strange is the use of a delay: if you mean yield, use yield:taskYELD(), why introduce a delay - unless this is the only chance some other tasks have to run, further corroborating my suspicion above.Till now, I have only used preemption without slicing, and some priorities.As an example, I have this project where a task takes care of receiving, processing and sending out buffers on a I2S interface, others handle user input (buttons and encoder scanning) and output (update a display) plus stuff that takes long to run and has lower priority (creating and displaying an FFT, showing some statistics).The priorities are in the order above: I want to make sure that as soon as the DMA finishes filling an input buffer, this is read, processed and sent out. User input and output can be processed in the time left so it has a lower priority. The FFT and stat display runs if we are neither handling user input/output or processing the input signal.No yields, very few delays used in the various loop code.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2021, 05:11:14 pm
content: Note that there are various ways of doing preemption. A scheduler can be "tickless", for instance. I think there's a way to go tickless with FreeRTOS, IIRC.As to priorities, of course higher-priority tasks have to "yield" execution at some point to allow execution of lower-priority ones. That's the whole point, as said above.There are various ways of doing this. Making a task "sleep" is one of them. Or making it wait on some event. Of course through the use of proper functions from the OS - simple busy loops would just allow preemption by same-priority tasks.

author: peter-h
author_rank: Super Contributor
timestamp: October 09, 2021, 05:54:17 pm
content: "What I find even more strange is the use of a delay: if you mean yield, use yield: taskYELD()"Isn't osDelay(1) equivalent to taskYIELD() if the tick is 1kHz? If you call taskYIELD() will the RTOSevercome back to this task before the next 1ms tick? I can see it could be coded to do that (if say every other task has suspended itself for say 10ms) but...

author: newbrain
author_rank: Super Contributor
timestamp: October 09, 2021, 06:51:19 pm
content: Quote from: peter-h on October 09, 2021, 05:54:17 pmNot at all. Read the description for taskYIELD I linked: it can return immediately, if the calling task is the one who should run (due to priority or other task being being blocked on queues/mutexes/semaphores/events).It would be a strange RTOS if the only way to yield would waste at least one tick!Quote from: SiliconWizard on October 09, 2021, 05:11:14 pmUnless one is using slicing the kernel can be considered tickless: scheduling does not happen at ticks unless, of course, explicitly using timers or, as in peter-h case, delays.In case of slicing, and for tasks at the same priority, I'm not sure of the scheduling algorithm: round robin? It might be configurable, even.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2021, 07:26:43 pm
content: Quote from: newbrain on October 09, 2021, 06:51:19 pmBesides, delaying doesn't just yield - it's supposed to act as a delay. That means the task calling a delay function will not be rescheduled until the delay has expired, making the task effectively "inactive" during at least that much time.

author: peter-h
author_rank: Super Contributor
timestamp: October 09, 2021, 08:26:00 pm
content: Well I learnt something really usefulReplacing osDelay(1) with taskYIELD() has not seemingly broken anything but has much improved some strange timing issues I was seeing, which I worked around...I can also see one could write a "tick-less RTOS" but surely without the RTOS having a concept of time it won't know how much time to give to a task before it chops it, so the only option it would have (if say you have 10 tasks of same priority, each not yielding at all but just running some useful code) would be to execute justoneinstruction of each task before switching, which would be silly because then the RTOS would be spending say 99.9% of the CPU time running the RTOS. Well, unless the RTOS was somehow counting instructions (executed of each task) or counting CPU clocks (using that 168MHz clock counter we discussed) but then it isn't tick-less; you merely replaced the 1kHz tick with something more crude.

author: westfw
author_rank: Super Contributor
timestamp: October 09, 2021, 10:59:45 pm
content: QuoteI don't think I understand preemption without "slicing"? Surely if you have several compute-bound tasks at the same priority, you want them ALL to run, don't you? Probably round-robin?QuoteMost RTOS stuff is NOT about sharing the CPU between compute-bound tasks. It's about having "idle" tasks respond quickly ("in real time") to events that cause the task to have something it needs to do. "tickless" schedulers are used (and important) in very low-power systems, because the permit the CPU to go into low-power sleep modes when there is nothing that needs to be done, instead of continuously running the scheduler loop.QuoteWhy would you use a 1ms tick if you wanted some tasks to run for 100ms?Individual tasks determining their own runtime and then manually yield/suspend/sleeping is pretty much the definition of a cooperative scheduler, rather than preemption.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2021, 11:37:10 pm
content: Yeah, I don't get the part about "without slicing" either. Strictly speaking, any kind of multitasking program uses "slicing" in one way or another, even if it's not preemptively scheduled. Slicing is just about distributing CPU time, however it's done.As to tickless scheduling - there isn't a single definition of this. It covers a range of approaches. The common idea is that the scheduler doesn't preempt on a fixed time period (a 'tick'), but that's about it. So, the scheduler can preempt on a variable (dynamic) period depending on the state of tasks. Yes it allows to get lower power (no needless wake-up of CPU when no task needs to be running, while a typical 'ticked' scheduler would wake up the CPU every tick, even when all tasks are inactive, and then would check if one is ready to run...), but it also allows lower latency, as preemption could happen - theoretically - at any time, instead of just on the 'tick'.Downside is, it's more complex to implement and to get right. I think the 'tickless' approach used by FreeRTOS is relatively basic and mainly used for lowering power consumption.

author: newbrain
author_rank: Super Contributor
timestamp: October 10, 2021, 04:40:42 am
content: Quote from: westfw on October 09, 2021, 10:59:45 pmWell, I guess you have answered your question yourself!In my case, the real time, quick responding tasks, are IO bound (processing must still be faster than data coming in and going out, if I need to keep the I2S happy).User input is polled at a suitable rate and at lower priority. FFT plus display take what's left (aiming for 20 frames/s) and is the only task to be (depending on the load) CPU bound.Time slicing is not needed, the tasks are run when they can be unblocked, e.g. there's something in an input queue, or they get a notification.Preemption means that if a higher priority task gets ready to run while a lower priority one is running, the latter is suspended and the former gets the CPU, e.g. if in the middle of an FFT the I2S DMA completion interrupt arrives the FFT is suspended while the input frame is processed and passed to the output DMA.This works very well, while handling an I2S codec, three I2C peripherals, and an SPI for the display.

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2021, 07:39:31 am
content: The idea of the RTOS running a task when there is some input to process is something I have not tried yet. I just poll the RX queue and if there is nothing there (or not enough bytes to form a usable message) then I yield right away. But I can see this increases power consumption.How would you config the RTOS to run a task when there is say > 5 bytes in an RX queue? Something must be checking that RX queue, whether it is your task or some code in the RTOS. Presumably the right way is to hook up the RX interrupt and when rx count reaches 5, you make a call to the RTOS to schedule the data processing task to run?But unless you are putting the CPU into a low power mode most of the time, the power benefit must be minimal.

author: westfw
author_rank: Super Contributor
timestamp: October 10, 2021, 08:12:29 am
content: QuoteYep. Note that the "RTOS call" can be as simple as putting the task on a "runnable" queue.There is an interesting method I've seen, where the task provides a "scheduler test" (a pointer to a function.) To see if a task is runable, the scheduler calls the scheduler test function - since this is done from the scheduler context, it is much lower overhead than waking the task up all the way to do its own check, allows relatively complex decision making, and it doesn't require that you make all the ISRs scheduler-aware. OTOH, it has higher overhead than making the ISRs scheduler-aware, might result in security issues, and certainly allows tasks to do stupid things (mainly, to provide "expensive" tests. They need to be short, rather like ISRs.)

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2021, 09:47:50 am
content: There seem to be two ways to use an RTOS: the simple way and the elegant way.The simple way is to write the task like you would write it if nothing else was running on that CPU (except ISRs), and with an RTOS you can have a whole load of such tasks all co-existing happily. Even if they don't yield.The other way is to get ISRs to trigger RTOS tasks to run, etc.But unless you are putting the CPU to sleep most of the time, I don't think there will be a difference to power consumption, and operationally it is the same if the I/O is buffered (e.g. typical serial comms).

author: rstofer
author_rank: Super Contributor
timestamp: October 10, 2021, 03:41:52 pm
content: Quote from: peter-h on October 10, 2021, 09:47:50 amEach task can be coded without interaction with other tasks other than through FreeRTOS structures like semaphores. This modularization is worthwhile even if the RTOS bit doesn't matter. The 'super loop' can degenerate to simplywhile(1) {}rather than the massive loop typical of Arduino code in the loop() function.We don't test that a button has been pressed, we test whether the 'button-pressed' ISR has set a semaphore. A very clean interface!

author: nctnico
author_rank: Super Contributor
timestamp: October 10, 2021, 03:56:03 pm
content: Quote from: rstofer on October 10, 2021, 03:41:52 pmThat is a pretty bad example. You really don't want buttons to be connected to a hardware interrupt line if you want to keep your sanity * (unless you do filtering & debouncing in hardware). Lots of things can go wrong resulting in unpredictable CPU loads. So either you have filtering & debouncing in hardware or software. The latter requires having a process that deals with the buttons. Ofcourse that process can fire an event to a higher process to deal with the button presses.* Over the years I have encountered several software engineers that learned this the hard way. At one of my former employers a particular product showed all kinds of mysterious behaviour and the (external) software engineer wasn't really able to fix it. At some point we hooked some hardware to the inputs which caused the logic levels to flip rapidly and low and behold: the device's responsiveness slowed down to a crawl and looking at the processes it turned out the system was busy serving interrupt requests from the inputs. My reaction was quite close to 'what kind of idiot connects a button input to an interrupt?'. The software engineer then said to me: but isn't that why input pins have interrupts?

author: ogden
author_rank: Super Contributor
timestamp: October 10, 2021, 04:23:22 pm
content: Quote from: nctnico on October 10, 2021, 03:56:03 pmPolling is good for most applications except one - low power. If button-cell powered electronics wristwatch would poll it's buttons like you suggest, it's battery won't last long. Problem is not hardware interrupts, but HOW you handle them and why. In said wristwatch example smart thing to do is use hardware ISR to trigger polling sequence for a while.

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2021, 04:31:16 pm
content: "You really don't want buttons to be connected to a hardware interrupt line if you want to keep your sanity"I was just going to type the sameThere is an extra complication setting up an ISR for an input pin and making sure that every possible source of that interrupt gets cleared in the ISR, plus one has to debounce the pin (which complicates the ISR because you now have to do a state machine there to re-examine the pin after say 50ms, so have to involve a timer). It is much easier to test the pin directly in an RTOS task, with an osDelay(50) in the loop, and if the button is a 1 and was 1 50ms ago then it is pressed (etc). That keeps the CPU loading deterministic (underyourcontrol) and prevents noise on the pin hanging your system. That task will take only a few us every 50ms.Another thing is this: the person writing all the code isyou. So, while the result is "co-operative" and not "pre-emptive", it should work fine. One day I will have a play with getting ISRs to run tasks but so far I have not seen a need which would make any difference to performance. No doubt there are such cases though; probably where some eventhasto be serviced regularly (and you can't or don't want to do it in an ISR) while there is another task running which takes a lot of processing (e.g. updating a graphics display) but it doesn't matter if it gets hung up. I guess there is a lot of overlap between a) doing stuff in an ISR and b) doing the same stuff in an RTOS task with a high priority."Polling is good for most applications except one - low power. "I have a product which uses an old AVR (90S1200) running mostly in sleep mode, and it gets woken up by a timer, every few ms, does about 100 instructions, and goes back to sleep.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 10, 2021, 04:43:09 pm
content: Quote from: ogden on October 10, 2021, 04:23:22 pmNot necessarily - polling buttons every 50-100ms is enough for responsiveness, and given the code is not total bloat, checking for button state changes is some microseconds of execution time, resulting in negligible duty cycle and average current comparable to sleep mode leakages.More problematic are some sensors which need to be dealt with at kHz rate, low enough not to justify CPU being active all the time, but high enough that duty cycle of waking up and related overhead is significant. Some IMUs for example solve this by offering internal FIFOs, allowing burst processing.Polling buttons has the upside that the very necessary debouncing comes for almost free with little overhead. For example, poll every 50ms in pure timer ISR and require two successive active readings to react. One ISR handles all buttons and button-like signals.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2021, 05:23:29 pm
content: I don't see the point of using polling for buttons if power consumption is a concern and the CPU would be sleeping otherwise. Why periodically wake up the CPU (for instance, on a timer, for basic preemptive scheduling) and poll, when you can just wake it up from an interrupt triggered by button push? Debouncing can be handled in various ways in order for bouncing not to unncessarily wake up the CPU, but it is indeed something to take into account.Now of course you also have to consider what else may wake up the CPU, and how much time it takes the CPU you're using to go to a low-power mode and get out of it, to decide which approach will be most efficient.Note that you can use ISRs along with tasks when using FreeRTOS as far as I've read. In simple cases like this, I'm pretty sure this is simpler than going through hoops with tasks.(Then again, you may question the relevance of using an RTOS for simple cases, but that's yet another question...)

author: newbrain
author_rank: Super Contributor
timestamp: October 10, 2021, 05:34:38 pm
content: Quote from: peter-h on October 10, 2021, 07:39:31 amAnd, once again as often happens to me when peter-h is involved, I'm confused.Didn't you say that you have a number of CPU bound tasks?Clarification: to me CPU bound means IO on the task must wait for the CPU to finish. IO bound the opposite, the CPU is always ready to process new IO.So, if there are CPU bound task, what's the point of trying to switch to low power modes? The CPU bound task should be always ready to runAlso, consider that FreeRTOS has anIdle Task- running whenever none of the user defined tasks are (as it has the lowest possible priority).This Idle task will consume the rest of your CPU time, unless you take specific steps to configure FreeRTOS for using low power modes.For serial RX, you are on the right track, if you just want a task to wake up when at least 5 characters have been received, use the ISR to put them in a queue (in general terms - might or might not be a FreeRTOS queue) and signal the task when five have been received (e.g. using a cheap direct task notification, or a semaphore).As a general rule for inter-task communication it's better to use what the RTOS provides rather than faffing around with volatile atomic flags or variables etc. - unless you are starved for memory or CPU time, the overhead is quite tolerable and they work correctly.Remember that in FreeROTS ISRs must use special versions of the primitives (e.g. xSemaphoreGiveFromISR) and explicitly yield with portYIELD_FOM_ISR() (otherwise your task will not wake up until the next time the scheduler invoked).Other RTOS are simpler in this respect, e.g. Azure RTOS/ThreadX: there's a list of API that are allowed from ISR (most are) and that's all.For me, in most of my stuff:user inputs are always polled (encoders, buttons) the input task is woken up periodically and checks if anything has happened.I2S and SPI use DMA + interrupts, I2C just interrupts. The interrupt wakes the relevant task using the provided RTOS primitives.

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2021, 05:57:26 pm
content: " once again as often happens to me when peter-h is involved, I'm confused"I am not using sleep mode. This is a powered product so no need. I was just commenting on some other posts.The product I am working on probably takes up about 10% of CPU time.I can't get the RTOS display to work in Cube; I believe there are some code hooks one has to do. So how exactly it is done, doesn't really matter.But definitely an RTOS is needed. To make my life easier, to make life easier for others writing modules for it, and to run some genuinely complicated stuff.If/when I get to running some really complicated stuff then I will need to look at this more carefully. For example one possible version involves driving a 1024x768 or similar colour LCD, probably via SPI, low overhead there at 21mbps especially if using DMA to feed the SPI, but generating the image will need serious processing.

author: newbrain
author_rank: Super Contributor
timestamp: October 10, 2021, 06:01:39 pm
content: Thanks!Quote from: peter-h on October 10, 2021, 05:57:26 pmCannot help here - I stay as far away from Eclipse as humanly possible.Task stack display works for me very well using pyocd in Visual Studio Code, both for FreeRTOS and Azure RTOS/ThreadX (the latter, contributed by yours truly).

author: ejeffrey
author_rank: Super Contributor
timestamp: October 10, 2021, 06:10:46 pm
content: Quote from: nctnico on October 10, 2021, 03:56:03 pmIt's not really a problem to use pin change ISRs for buttons. You can handle it a couple of ways. If your ISR code is sufficiently tiny and of an appropriate priority you may be able to just do it. Maybe a single press fires a few hundred ISRs but if that isn't a big deal for your application who cares? All you are doing is executing a tiny debounce state machine. Most of the problems caused by this are if people try to actually do something in response to the button for every event. If you go this route you do need to consider the contact bounce can vary from part to part and degrade over time, so you need a very healthy margin. The more elegant option is that when a button is pressed you temporarily mask the pin change ISR and set a timer to re-enable it X ms later. This gets you the advantage of a fast initial response, even lower overhead than polling, and all without having to trade off with the wake up interval vs. risk of missing events. Obviously in a real low power situation you would want to look at the energy cost of a wake-up event and the desired response time to see if this is worth it.For push buttons it probably doesn't matter. If you can poll every 50 ms you are usually fine. That is right around the threshold of perception and it is hard to press a button faster than that. 100 ms is usually too slow, IMO. However, a lot of people give the same advice for mechanical rotary encoders and I think this is hot garbage. They need *much* higher polling rates to have acceptable performance and usually people don't poll often enough. It's OK if you can poll really fast and aren't worried about power consumption but overall it is much better to use pin change interrupts. You can either handle debouncing and decoding the rotation in the ISR with the same techniques as for buttons, or you are dead set on polling you use the pin-change interrupt to trigger a brief period of high frequency polling that times out when there are no events for a period of time.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 10, 2021, 06:27:49 pm
content: Quote from: SiliconWizard on October 10, 2021, 05:23:29 pmWhy not. It works, is trivial to implement, and as I commented, has really no downsides regarding power consumption in 99.9999% of cases. There often is some auxiliary housekeeping to do periodically, for example read ADC to check battery voltage to decide if low-voltage cutoff action is needed so all such actions can be combined to one long-interval (low power) periodic task. At 50ms interval, power consumption is meaningless unless you somehow totally manage to bloat the code.Yes, you can also wire the button to the interrupt line, then in the ISR turn off further interrupts on that line, set another timer interrupt in 50ms where you turn the button interrupt back on. This way you won't hit the problems nctnico described. Yes, why not. It's a bit more complex but a competent programmer easily pulls that off. There are many suitable ways.

author: nctnico
author_rank: Super Contributor
timestamp: October 10, 2021, 07:10:35 pm
content: Quote from: ejeffrey on October 10, 2021, 06:10:46 pmNo, it is a recipy for a disaster. Really. What you want for an embedded application is a known / pre-determined CPU load. It is not button presses that cause problems but flaky contacts, external noise sources or just bad wiring can easely produce enough interrupts to drain all your CPU cycles to just handle the ISR routine. I've seen it happen several times resulting in major problems where it comes to product reliability and perceived product reliability by the end user. Also be aware that passing susceptibility testing doesn't mean that your device will never have to deal with interference that is much stronger than is used during the EMC testing. The end user will still expect it to work!

author: westfw
author_rank: Super Contributor
timestamp: October 10, 2021, 08:38:01 pm
content: QuoteIn embedded, people write CPU-bound tasks to do IO.Code:while (UART->status & UART_DATA_AVAILABLE) ;return UART->data;That will work, more or less, in a preemptive RTOS environment, but it's not "RTOS aware", and is poor code.It will never yield in a cooperative OS environment.QuoteNot running until the next scheduler interval might be the preferred behavior. It depends on how "Real Time" you want to be.My general opinion is that a lot of people use a preemptive real time OS when they don't really need either preemption or real time behavior.

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2021, 08:45:07 pm
content: Code:while (UART->status & UART_DATA_AVAILABLE) ;return UART->data;However, it takes just 1 line to yield to RTOS if there is no data arriving. Same on TX.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 11, 2021, 06:17:49 am
content: Quote from: nctnico on October 10, 2021, 07:10:35 pmI've seen it too (in my own code decades ago, when beginner).For even somewhat competent programmer, there is no problem whatsoever: turn the interrupt off in the ISR! Set a timer to turn it back on after some predetermined time. Completely deterministic CPU load: button press, noise or contact bounce can cause only one interrupt per whatever re-enabling period you use. Not much different from the timer based button checking. OTOH as I said earlier I prefer that timer based checking like you, but it doesn'thaveto be that way, many ways to skin the cat when done right.

author: nctnico
author_rank: Super Contributor
timestamp: October 11, 2021, 10:06:31 am
content: Quote from: westfw on October 10, 2021, 08:38:01 pmI agree. It is better to write code in a way that it tells the OS no processing needs to be done for a while. A simple sleep can do the trick or by using blocking OS functions (for example when waiting for a network packet).

author: bson
author_rank: Unknown
timestamp: October 11, 2021, 07:21:00 pm
content: Quote from: peter-h on October 10, 2021, 07:39:31 amYou set the device to interrupt on a FIFO with more than 5 transfer elements. In the interrupt handler (which you write) for that device you unblock the task. The OS will provide synchronization primitives for this, be they BSD-style wait channels, Windows-style event objects, or posix style condition variables. This way one or more tasks can wait for a particular hardware state change, and the interrupt handler doesn't have to know which ones. The task then sits in a service loop, waiting for a state change, checks the device to see if it's something interesting (if not it's ignored as spurious), and if so handles it and returns to processing the next state change (or waits for one if there isn't one yet). The "check and wait" needs to be atomic (and in a good RT OS atomicity is relative, using things like scheduling priority, IPLs for lockout, and interrupt priority).

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 11, 2021, 08:53:24 pm
content: Quote from: peter-h on October 10, 2021, 05:57:26 pmWell, that sounded a bit strong. You could probably do without one. But the following explained it.Quote from: peter-h on October 10, 2021, 05:57:26 pmIf it really makes your life easier, then that would be a decent reason already. But IMO, the part dealing with making it "easier for others" (assuming this is a design for which you share the firmware and allow modifications/customization) is the most important here. I agree FreeRTOS gives a well documented and familiar environment for other developers, and requires a lot less documentation and technical support than if your firmware was 100% your own code with ad -hoc scheduling of various tasks and a bunch of ISRs that would make it harder to figure out and easier to mess.

author: ogden
author_rank: Super Contributor
timestamp: October 11, 2021, 09:07:09 pm
content: Quote from: nctnico on October 10, 2021, 07:10:35 pmFully agree. Wild IRQ storms is bad design by definition. Hardware interrupt for "bouncy mechanical contacts w/o RC filter" shall be used only as wake-up for succeeding polling debounce. When IRQ fired - disable I/O hardware interrupt, initiate polling process for some time long enough to detect successful keypress or other signal of interest. At the end of the polling - rearm hardware interrupt. This is most power-efficient way of waiting for noisy I/O signals.

author: westfw
author_rank: Super Contributor
timestamp: October 12, 2021, 12:18:14 am
content: QuoteI put together some other examples, for some "generic" preemptive OS:Code:// Simple version - we're running under an RTOS, but we're only using// the preemptive scheduler, and haven't convert our low-level code.void UARTRXISR() {buffer(UART1_info, UART1->data); // stick data into a FIFO}int getc() {if (hasData(UART1_info)) // if there's data in the FIFO, get it.return unbuffer(UART1_info); // from the FIFOreturn -1; // otherwise indicate no data.}//int main() {int c;// Bad, RTOS-unaware, compute-bound IO.while ((c = getc()) == -1); // wait for datadoSomething(c);// Slightly better codewhile ((c = getc()) == -1)yield(); // let other tasks run while we wait.doSomething(c);// Better stillwhile ((c = getc()) == -1)mssleep(50); // let other tasks run for a reasonable user-oriented timedoSomething(c);}Code://// To really support RTOS, make the ISR and getc() "RTOS enabled."//void UARTRXISR() {buffer(UART1_info, UART1->data); // put data into FIFORTOS_wakeup(UART_info->task); // move the task waiting for input to runable.}char RTOS_getc() {if (!hasData(UART1_info))RTOS_suspend(UART1_info->task); // no data means the task isn't runnable.return unbuffer(UART1_info); // if we woke up, there must be data.}//int main() {char c;c = RTOS_getc(); // Simpler as well as fully RTOS-Aware.doSomething(c);}I find it very frustrating to get something like "Arduino on FreeRTOS" when it's not clear how far this sort of chain has been followed.

author: peter-h
author_rank: Super Contributor
timestamp: October 13, 2021, 08:30:10 am
content: Code:void UARTRXISR() {buffer(UART1_info, UART1->data); // put data into FIFORTOS_wakeup(UART_info->task); // move the task waiting for input to runable.I don't think there is any great difference between the above and having an RTOS task polling the RX buffer #bytes and if zero, just yield to RTOS.The above turning the task on and off is more elegant and wastes less time, but if you yield to RTOS then other stuff will be getting done, and there is only so much that can be done, and if the CPU can't do it all then you need a faster oneThe RTOS wakeup will be many many instructions too. Will it run the task immediately, or at the next tick?

author: newbrain
author_rank: Super Contributor
timestamp: October 13, 2021, 09:04:25 am
content: Quote from: peter-h on October 13, 2021, 08:30:10 amThat depends on the RTOS, as I described above. For FreeRTOS if you want immediate scheduling you should use portYIELD_FROM_ISR(), otherwise scheduling will only happen when then RTOS kernel is entered the next time (not from an ISR), which might or might not be due to a tick and might be after a "long" time, depending on your needs.Azure RTOS will, IIRC, schedule if you use any of its API in the ISR (otherwise it has no way to know!) - it does not have special API versions for ISR.As for efficiency, using the ISR will only use the CPU when something happens, instead of continuously invoking the scheduler for yielding - a costly operation in itself as it has to decide if any of the other tasks needs waking up.As a side note FreeRTOS can also use direct to task notifications, which are very efficient WRT to queues and semaphores.

author: rstofer
author_rank: Super Contributor
timestamp: October 13, 2021, 05:42:02 pm
content: Quote from: westfw on October 12, 2021, 12:18:14 amNice examples!

author: NorthGuy
author_rank: Super Contributor
timestamp: October 13, 2021, 06:21:16 pm
content: Quote from: westfw on October 12, 2021, 12:18:14 amWhy not? Since you started using RTOS, your UART buffer has become a shared resource, so you should create a mutex to protect the resource from other threads, a la:Code:mutex_acquire(UART1_RX_mutex);buffer(UART1_info, UART1->data); // stick data into a FIFOmutex_release(UART1_RX_mutex);

author: mikerj
author_rank: Super Contributor
timestamp: October 14, 2021, 11:21:29 am
content: Quote from: NorthGuy on October 13, 2021, 06:21:16 pmIt's only a shared resource if more than one thread is using it, and that doesn't seem to be implied at all from the code snippet. If the UART is only accessed in a single thread then no mutex would be needed.

author: peter-h
author_rank: Super Contributor
timestamp: October 14, 2021, 01:33:51 pm
content: That's very true but a UART rx channel shared by several RTOS threads is very unlikely to be doing anything usefulAt a push you could have one thread doing RX and another doing TX.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 14, 2021, 05:33:30 pm
content: Quote from: mikerj on October 14, 2021, 11:21:29 amThe original example certainly implies two threads - one is buffering the data then waking the other one to "unbuffer" it. Look here:Quote from: westfw on October 12, 2021, 12:18:14 am

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 14, 2021, 06:11:37 pm
content: Quote from: peter-h on October 14, 2021, 01:33:51 pmWell, a design with several threads potentially reading from the UART would be questionable, but I think NorthGuy was more talking about sharing the buffer - I suppose in memory - with received data, and not the UART RX peripheral itself.Say one task reads incoming data from the UART, and puts it in a buffer. Then another task reads the buffer to process data. Then the buffer is effectively shared.That's typically a use-case, IMO, which should be implemented with queues. They are designed for exactly that, sharing data between tasks in a safe way, and the FIFO nature of the data here lends itself to that. No need to reinvent the wheel with a hand-implemented buffer and mutexes. Now I don't know FreeRTOS a lot, but I'm sure it has queues that can be shared without dealing with mutexes directly.

author: rstofer
author_rank: Super Contributor
timestamp: October 14, 2021, 06:17:58 pm
content: Quote from: SiliconWizard on October 14, 2021, 06:11:37 pmWhich is why I said earlier that, IMO, the peripheral itself is not a shared resource. The queue is shared and the pointers need to be protected during updates but only one process will be dealing with the peripheral device itself.Here's the 'book' on FreeRTOS Queueshttps://www.freertos.org/a00018.html

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 14, 2021, 07:03:22 pm
content: That said, even if in the above case, sharing the UART RX wouldn't make much sense, there can still be use cases for sharing a peripheral. Peripherals can definitely be shared resources - you just need to make sure there isn't another, better way of handling things than sharing one before doing it. Ideally, no more than 1 task should access any peripheral, but this rule can and sometimes has to be broken.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 14, 2021, 08:16:14 pm
content: Quote from: SiliconWizard on October 14, 2021, 07:03:22 pmI think by UART RX you mean pins or registers. The example was about buffering and unbuffering, and I expessedly said:Quote from: NorthGuy on October 13, 2021, 06:21:16 pmThe buffer is shared between the thread that writes the data to the buffer and the thread that retrieves the contents from the buffer.Aside of that, you certainly can share a single UART peripheral between different tasks. You can even dynamically re-assign pins to use the same UART peripheral module to talk to different devices connected to different pins, although typically UART modules are not in short supply.

author: peter-h
author_rank: Super Contributor
timestamp: October 15, 2021, 05:25:06 am
content: I am doing that but with SPI3, not a UART. That SPI channel is used for multiple devices: ADCs, DACs, special-purpose I/O chips, etc. I've set it up so there is a mutex around the code accessing each device (just one mutex for all of them) and there is a "last device used" value stored so if the new device is not same as last, you re-initialise the SPI and store that device # as "last".Perhaps surprisingly, it works, but unsurprisingly it took a lot of time to get it to work because the SPI controller needs some undocumented number of APB clocks (and/or the SPI clocks; the frequency is different for each device, from ~500kHz to 21MHz) to settle down. Also some devices need clock parked high, some parked low, some evidently don't care, some clock data on +ve edge, some on -ve edge, some need long timing before/after CS, etc. We had a long thread on this a while ago.I suspect most people totally avoid doing this sort of thingbut when it works, it shows the power of an RTOS and mutexes etc. Just don't try it with delta-sigma ADCs which have a 100ms conversion time

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 15, 2021, 06:02:08 pm
content: I suppose you are reusing a single SPI peripheral by reconfiguring it on the fly if needed and using separate chip select lines?That would be an example indeed.But even in such a case, you can still use a single-task approach, which is often less trouble to get right.Basically, all SPI accesses would be issued in one task dedicated to it. Then you would communicate with other tasks using queues. You can typically set up one "command" queue that each task needing SPI access would fill with SPI access parameters, and then queues for pure data exchange. That may look a bit intimidating at first, but is a lot easier to get right in the end, and scales up better IMO. Hope I was clear enough here with this approach - didn't get into details.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 16, 2021, 06:04:39 pm
content: Quote from: SiliconWizard on October 15, 2021, 06:02:08 pmThat is for sure. Abandoning RTOS is even better. With direct access to peripheral, interrupts, and DMA, you can organize everything much better than RTOS ever could, and write much less code too.But bring in few huge blocking libraries, and you cannot get by without RTOS any more. Hence the popularity.

author: ogden
author_rank: Super Contributor
timestamp: October 16, 2021, 07:12:42 pm
content: Quote from: NorthGuy on October 16, 2021, 06:04:39 pmSiliconWizard suggested dedicated task with queues for handling I/O of common peripheral - to avoid blocking of consumer tasks. If you think that better solution is to abandon RTOS - then think/learn again

author: mikerj
author_rank: Super Contributor
timestamp: October 17, 2021, 01:16:20 pm
content: Quote from: NorthGuy on October 16, 2021, 06:04:39 pmAn RTOS does not prevent direct access to peripherals, interrupts or DMA. Even if you are using a simple scheduler or a conventional superloop with state machine you would still need to provided protection to shared resources like DMA that run outside of a tasks execution.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 17, 2021, 03:45:46 pm
content: You are looking NorthGuy's comment in the wrong way. If you have already defined everything using computer science operating system terminology and mindset, and you have a problem you need to solve defined in this context, "abandon RTOS" obviously is no solution to that problem. But isn't it an X-Y problem to begin with?But NorthGuy's suggestion is a viable choice in the bigger picture. Well, not always, obviously, sometimes some specific RTOS happens to bejustthe right tool for the job. And then it matters which RTOS it is.But many if not most projects that are written on top of RTOS don't need or benefit from said RTOS if looked at on clean sheet specification level, but because they are written for RTOS, you can't get rid of it without rethinking the whole project. And that would likely be a stupid move to rewrite a project "just" to get rid of RTOS if the project works well.This is like building a brick house vs. wood frame house. Both work and result in a decent house, but in completely different ways. But a very skilled mason may not understand how to build a decent wood frame house, and a carpenter would suck at building the brick wall.I only dislike the elitism that comes with RTOS mindset, namely while people like I accept RTOS might be an acceptable idea, use it if it works, many RTOS believers force the projects into this mindset bringing all the made-up abstractions that made parallel programming on general purpose computing difficult, to relatively simple embedded projects where all these complications could be completelyavoidedin the first place. Theirclaimis there are some very difficult and complex problems regarding to parallel computing which are nicely "abstracted" by the OS, but this is mostly BS, in reality these complex problems are made up at the same time they are solved.A classic interrupt priority driver state machine is extremely simple to analyze and I have had to think about some "equivalent of mutex" exactly... zero times.But you can keep claiming I need to somehow manually and tediously implement all those mutex-equivalents and semaphore-equivalents and spinlock-equivalents and threads and complex schedulers and whatnot, but in reality, I don't have to.And I don't need to think about what scheduling delay is or what time slicing value I need to use. My interrupt priorities are configured according to the urgency of the interrupt source and the ISR starts in 12 clock cycles.Quite frankly, I believe NorthGuy hit the nail; RTOS is a very useful tool for people who learned general purpose computing, writing blocking code. There is nothing wrong with that. If that's what you do and you are old enough not to learn new skills, then RTOS is likely the right choice to work with on microcontrollers. But I for example started learning microcontrollers at 14 years old, on brand new Atmel AVR series, similar to PIC. This gives you a different mindset, and yes, it's completely extendable to largest ARM microcontrollers on the market because they fundamentally work the same.What I do not like is that many really experienced PIC-style developers who could develop nearly bug-free, simple, understandable and high performance code on any modern microcontroller, have been forced to change their thinking by RTOS and library elitism and misinformation.I'm not looking for popularity or "thank you" presses with this comment, and I understand this is only one side to the complex story.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 17, 2021, 03:56:39 pm
content: Quote from: mikerj on October 17, 2021, 01:16:20 pmWhile DMA transfer is in progress, you will have a flag which tells you that. You can poll the flag if you wish, or, if you want to react sooner, you can create an interrupt which fires immediately as the DMA transfer is finished. Moreover, you can assign priorities to interrupts. Say you can configure priorities in such a way that the DMA interrupt does happen, but not if fast ADC interrupt is in progress. Or perhaps your DMA controller may be clever enough to trigger a different DMA transfer once the first one is done. All these mechanisms are already there, and there's absolutely no need to use RTOS to duplicate them.The new thing that RTOS add, which wasn't already there, is multi-threading. It lets you run multiple threads (tasks if you wish) which can preemt each other and thereby share the CPU creating an illusion that each task runs on its own CPU. This lets you write your code in linear fashion - if you need to wait for something, you just block the thread and then, when the condition is satisfied, the thread continues automatically. Without RTOS, you would have to write non-blocking code - if you want to wait for something, you would have to find a way to continue other tasks and poll periodically for the condition.The decision between using RTOS or not should be based on whether you want to write blocking or non-blocking code. Blocking code is arguably easier to write, certainly much easier to find on the Internet. But to use it, you would have to bring in the RTOS.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 17, 2021, 04:05:14 pm
content: Again hit the nail, that's exactly the difference, and also the real reason why to use RTOS. All the BS about mutexes being needed to use peripherals and DMA and whatnot is only a smoke screen (or more likely, lack of thinking without bad intent, but I wanted to sound dramatic, sorry 'bout that).RTOS is a real simplification in code writability and readability if there are complex, long control code paths that need to run in parallel to each other. ESPECIALLY when there are NO dependencies so that no mutexes are needed. This is in contradiction to the typical claim that RTOS shines in mutexes. No, mutexes are needed when the nice "run blocking linear code in parallel" paradigm falls apart, and then it gets into all the problems with multithreaded code.For example, when I write without RTOS, my FSMs may be built with 20 functions each of which represent the code to runone state, then set the interrupt vector to the next state. On RTOS, this can be written in one loooong function with wait-for-something loops, or OS yield function calls in between, to let the scheduler to do its magic. Sometimes that's easier to read, and a few lines shorter per state. So instead of 20 functions 10 lines each so 200 LoC total, now you have one function of 150 lines.Quite hilariously though, the typical style guidance is to split such long blocking code into state machine containing of small functions such as one per state. If you do that, you have already got rid of the RTOS mindset, even if you run RTOS.

author: peter-h
author_rank: Super Contributor
timestamp: October 17, 2021, 05:50:48 pm
content: "I suppose you are reusing a single SPI peripheral by reconfiguring it on the fly if needed and using separate chip select lines?"Yes exactly."Basically, all SPI accesses would be issued in one task dedicated to it. Then you would communicate with other tasks using queues."I didn't do that. I just have different tasks using different SPI3 peripherals, and the SPI3 is mutex protected. The SPI3 controller is re-initialised if needed. The time consuming bit was that this is undocumented - nowhere does it tell you how many clocks (and which clocks) are needed before/after SPI init, before you can send out data, etc." Blocking code is arguably easier to write"It certainly is, even if you yield to RTOS whenever waiting for something. I think that's the real advantage of an RTOS. It simplifies writing code where different bits do different things.If one goes back to the earliest days of microprocessors, one didn't have an RTOS (unless one wrote one, and then it was probably very basic). One did all time critical things in an ISR, and this is still the right way to do time critical (response time critical) things today. The latency can be just a few instructions. Background stuff was done in a timer ISR - 100Hz, 1kHz, etc. The main foreground loop was probably fairly simple. But software was simple back thenToday's software is vastly more complex, especially when using libs which nobody understands and which are buggy (seems to be the case for SSL/TLS stuff) and which were written to be blocking.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 17, 2021, 06:11:23 pm
content: Yes, when having to deal with complex, broken and buggy libraries to implement simple things which take 5 nonblocking LoC without the library, life gets very difficult very quickly. The answer is obvious, adding more complexity until it somehow kinda magically works out. During this process, it's very typical that most fancy pre-requirements (nanoseconds! 10000 years MTBF!) are relieved to be able to deliver at all. For example, "robust" means it works on lab table during demonstration. "Real time" means something happens within the human timeframe of not noticing the delay (i.e., < 10ms).It doesn't have to be that way.Unless the projectreallyis complex. I mean like autonomous space shuttle that lands on the Moon while doing moonwalk better than Michael Jackson ever did level complex. In such case, you may want to leverage existing machine vision algorithms, for example, meaning actually complex libraries instead of 5-LoC ad-hoc solution. And you may want something fancier than a 1kHz tick interrupt.But FreeRTOS likely isn't the solution for that use case, either.

author: mikerj
author_rank: Super Contributor
timestamp: October 18, 2021, 10:47:50 am
content: Quote from: NorthGuy on October 17, 2021, 03:56:39 pmAnd I haven't suggested there is. I saying that shared resources who use does not start and end within a "tasks" allocated time need some form of mutual exclusion to prevent collisions and race conditions. Polling the DMA flagmightbe good enough, but equally it might not be, this is exactly the kind of thing that can cause race conditions.Quote from: NorthGuy on October 17, 2021, 03:56:39 pmThis isn't even true, there are cooperative only RTOSs and RTOSs that can be configured to only work cooperatively. A good RTOS brings far more to the game than you seem to think they do.Quote from: NorthGuy on October 17, 2021, 03:56:39 pmAgain no. Even if you write non-blocking code (e.g. state machines) then you are very likely to want some kind of priority scheme, and some way of passing data between your "tasks" or synchronising them. All the stuff that's built into an RTOS, but reinventing the wheel can be fun too.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 18, 2021, 11:04:26 am
content: Thefactis that available CPU and MCU architectures aredesignedto run without RTOS. That is why problems you think appear when working without, in reality mostly do not exist. You would see that immediately if you actually did this work. I have never ever had to even remotely think about a race condition accessing DMA. This hasn't even crossed my mind. DMA peripherals are designed to work fine from bare metal code. Interrupt priorities exist for a reason, too.But I don't need my code to be in "linear" fashion with wait-for-something-else inbetween. I'm fine writing nonblocking or "callback-like" code.The RTOS scheme is all backwards; instead of RTOS solving a problem, RTOS invents problems then solves them. If you work logically backwards thinking that "oh, RTOS solves this problem -> removing RTOS removes the solution -> problem remains", you get the logically wrong result.This is not saying RTOS does not have possible strengths, not at all. But the argument that life without is difficult because of shared resources like peripherals is just complete and utter.... I don't even need to say it out loud. This is a FUD argument. Instead, why wouldn't you concentrate on telling us what are the strengths and benefits of RTOS? Without coming up with made-up problems that supposedly happen without, because there are experienced developers who immediately see these are made-up problems.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 18, 2021, 02:03:47 pm
content: Quote from: mikerj on October 18, 2021, 10:47:50 amOh, "reinventing the wheel" again. Look at it this way. Many, if not all of these things, existed before RTOSes. So, following your logic, RTOSes are nothing more than reinvented wheels

author: Siwastaja
author_rank: Super Contributor
timestamp: October 18, 2021, 03:28:49 pm
content: "Reinventing the wheel" is one of the most misused metaphors.Really it means taking something old, that works really well and can't be improved by making large changes, yet change it fundamentally to turn it into something new that performs worse, for example make it square. This happens all the time, but "not using RTOS" is not such case. Opposite could be said arguably, though.But it follows the logic of these RTOS people. In their world view, the RTOS is the only way to do anything more complex than a LED blinker, and if you don't use it, then it logically follows that you are writing your own "RTOS equivalent" each and every time from scratch, and doing it in inferior way, hence "reinventing the wheel". If a scheduler in RTOS is 1000 LoC, the logical conclusion is, if you don't use it, you have to write your own scheduler which is also at least 1000 LoC and likely worse because it sees less testing and is done in hurry compared to a well-designed RTOS. But this is complete fallacy if and when you don't need an OS scheduler at all.We are not alone with this view. Not using RTOS can be a marketing argument showing how widely people are sick about "having to use" RTOS. For example, Fanstel markets their BC833M product family (well, basically Nordic Semiconductor microcontrollers) with this statement:"The embedded stack interface uses an asynchronous and event driven model removing the need for RTOS frameworks."source:https://static1.squarespace.com/static/561459a2e4b0b39f5cefa12e/t/60270895e454bd0fb5d24793/1613170839894/BC833M_Product_Specifications.pdfThis is actually a pretty good one sentence description how embedded MCU development is normally done. It's good they tell this because with such clear information, the actual implementation team can convince their boss that no, RTOS is not desirable in the project.