static void task01_class00(uint16_t initCondition) { for(;;) { uint16_t u; ++ _noLoopsTask01_C0; /* For test purpose only: This task consumes the CPU for about 50% of the cycle time. */ delay(5 /*ms*/); /* Release high priority task for a single cycle. It should continue operation before we return from the suspend function sendEvent. Check it. */ u = _noLoopsTask00_C1; rtos_sendEvent(/* eventVec */ RTOS_EVT_EVENT_00); ASSERT(u+1 == _noLoopsTask00_C1) /* Double-check that this task keep in sync with the triggered task of higher priority. */ ASSERT(_noLoopsTask01_C0 == _noLoopsTask00_C1) /* This tasks cycles with about 10 ms. This will succeed only if the other task in the same priority class does not use lengthy blocking operations. */ rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 10 /*ms*/); } } /* End of task01_class00 */
static void taskDcf(uint16_t initialResumeCondition) { do { sampleAndDecodeDcfSignal(); } while(rtos_suspendTaskTillTime(/* deltaTimeTillResume */ TASK_TIME_IN_TICS)); } /* End of taskDcf */
static void task00_class01(uint16_t initCondition) { for(;;) { ++ _noLoopsTask00_C1; /* This tasks cycles with about 2 ms. */ //u = rtos_delay(255); rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 1); } } /* End of task00_class01 */
static void task00_class00(uint16_t initCondition) { for(;;) { ++ _noLoopsTask00_C0; /* This tasks cycles with about 200ms but it is nearly always suspended and doesn't produce significant CPU load. */ rtos_delay(80); rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 100); } } /* End of task00_class00 */
static void task01_class00(uint16_t initCondition) { uint32_t tiCycle0 = millis(); for(;;) { uint16_t u; ++ _noLoopsTask01_C0; /* The next operation (Arduino delay function) takes the demanded world time in ms (as opposed to CPU time) even if it is interrupted because of an elapsed round robin counter. As this task has a round robin time slice of 4 ms, the delay operation will surely be interrupted by the other task - which may consume the CPU for up to 20 ms. The delay operation may thus return after 24 ms. */ uint32_t ti0 = millis(); delay(8 /* ms */); uint16_t dT = (uint16_t)(millis() - ti0); ASSERT(dT >= 7); ASSERT(dT <= 25); /* Release the high priority task for a single cycle. It should continue operation before we leave the suspend function here. Check it. */ ti0 = millis(); u = _noLoopsTask00_C1; rtos_sendEvent(/* eventVec */ RTOS_EVT_EVENT_00); ASSERT(u+1 == _noLoopsTask00_C1) ASSERT(_noLoopsTask01_C0 == _noLoopsTask00_C1) dT = (uint16_t)(millis() - ti0); ASSERT(dT <= 2); /* The body of this task takes up to about 26 ms (see before). If it suspends here, the other round robin task will most often become active and consume the CPU the next 20 ms. This tasks wants to cycle with 40 ms. So it'll become due while the other round robin task is active. This task will become active only after the time slice of the other task has elapsed. Exact cycle time is impossible for this task. It can even be worse if the other round robin task should be suspendend while this task suspends itself till the next multiple of 40 ms: Occasionally, the other task will resume just before this task and the activation of this task will be delayed by the full time slice duration of the other round robin task. Task overruns are unavoidable for this (ir-)regular task, but we can give an upper boundary for the cycle time, which is tested by assertion. */ rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 20 /* unit 2 ms */); uint32_t tiCycleEnd = millis(); dT = (uint16_t)(tiCycleEnd - tiCycle0); tiCycle0 = tiCycleEnd; ASSERT(dT <= 62); } } /* End of task01_class00 */
static void taskRTC(uint16_t initialResumeCondition) { ASSERT(initialResumeCondition == RTOS_EVT_ABSOLUTE_TIMER); /* Regularly call the RTC implementation at its expected rate: The RTC module exports the expected task time by a define. */ do { clk_taskRTC(); } while(rtos_suspendTaskTillTime (/* deltaTimeTillResume */ CLK_TASK_TIME_RTUINOS_STANDARD_TICS) ); ASSERT(false); } /* End of taskRTC */
static void task01_class00(uint16_t initCondition) { #define TICS_CYCLE 125 uint16_t u; uint32_t ti = millis() , tiCycle; Serial.print("task01_class00: Activated by 0x"); Serial.println(initCondition, HEX); for(u=0; u<3; ++u) blink(2); for(;;) { Serial.println("task01_class00: rtos_delay..."); u = rtos_delay(55); Serial.print("task01_class00: Released with "); Serial.println(u, HEX); Serial.println("task01_class00: Suspending..."); u = rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ TICS_CYCLE); tiCycle = millis(); Serial.print("task01_class00: Released with "); Serial.println(u, HEX); /* The system timer tic has a frequency of 490.1961 Hz. Caution: The compiler fails to recognize the constant floating point expression if there's no explicit, superfluous pair of parenthesis around it. With parenthesis it compiles just one product, without it uses several products and divisions. */ Serial.print("Cycle time: "); Serial.print((tiCycle-ti) * (100.0/1000.0 / (TICS_CYCLE/490.1961))); Serial.println("%"); Serial.print("CPU load: "); Serial.print(_cpuLoad/2); Serial.println("%"); ti = tiCycle; } #undef TICS_CYCLE } /* End of task01_class00 */
static void taskT0_C1(uint16_t initCondition) { #define TASK_TIME_T0_C1_MS 21 uint32_t cnt = 0; /* The task inspects the results of the interrupt on a regular base. */ do { /* Wait until we get the resource. */ getResource(); /* Being here, we can be sure to have the resource. Use it. */ /* The ownership of the resource needs to be independent of the status of the tasks. To prove this we suspend the task deliberately in the middle of some output operation and we use the blocking Arduino function delay that long, that we have a high probability of running into a round robin task switch before delay returned. */ Serial.print("This is task T0_C1"); Serial.print(": "); Serial.print(++cnt); Serial.print(" loops. This line of conso"); rtos_delay(TIME_IN_MS(7)); Serial.print("le output is interr"); delay(3/*ms*/); Serial.print("upted by several task de-activations"); Serial.println(". Now the resource is released again"); /* Give other tasks a chance to get the resource. */ releaseResource(); /* Here, no other task will already use the acquired resource: All concurrent tasks are of lower priority. One of them will have become due but not active yet. The while condition will now make this task inactive and the already due other one active. */ } while(rtos_suspendTaskTillTime(TIME_IN_MS(TASK_TIME_T0_C1_MS))); #undef TASK_TIME_T0_C1_MS } /* End of taskT0_C1 */
static void task01_class00(uint16_t initCondition) { for(;;) { ++ _noLoopsTask01_C0; /* For test purpose only: This task consumes the CPU for most of the cycle time and while the task is active. The call of delay produces a CPU load of about 80%. This is less self-explaining as it looks on the first glance. The Arduino function delay is implemented as loop, which compares the current system time with a target time, the desired time of return. The current system time is clocked by an interrupt independent of RTuinOS. This loop will basically run during 80 ms in a cycle of about 100 ms - but not continuously. The task of higher priority will frequently interrupt and shortly halt the loop. Therefore the 80% of CPU load do not result from this task (as it may seem) but from this task and all others which may interrupt it while it is looping inside delay. */ delay(80 /*ms*/); /* This tasks cycles with about 100ms. */ rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 50); } } /* End of task01_class00 */
static void task00_class00(uint16_t initCondition) { uint32_t ti1, ti2=0; for(;;) { ++ _noLoopsTask00_C0; /* To see the stack reserve computation working we invoke a nested sub-routine after a while. */ if(millis() > 20000ul) subRoutine(1); if(millis() > 30000ul) subRoutine(2); if(millis() > 40000ul) subRoutine(3); /* Wait for an event from the idle task. The idle task is asynchrounous and its speed depends on the system load. The behavior is thus not perfectly predictable. */ if(rtos_waitForEvent( /* eventMask */ RTOS_EVT_EVENT_03 | RTOS_EVT_DELAY_TIMER , /* all */ false , /* timeout */ 200 /*ms*/ ) == RTOS_EVT_DELAY_TIMER ) { ++ _task00_C0_cntWaitTimeout; } /* This tasks cycles with the lowest frequency, once per system timer cycle. CAUTION: Normally, this is not permitted. If the suspend time is more than half the range of the data type chosen for its system time RTuinOS is no longer capable to safely recognize task overruns. False recognitions would lead to bad task timing as the corrective action is to make the (only seemingly) late task due immediately. */ rtos_suspendTaskTillTime(/* deltaTimeTillRelease */ 0); /* A task period of more than half the system timer cycle leads to a high probability of seeing task overruns where no such overruns happen. (See RTuinOS manual.) We therefore disable the standard corrective action in case of overruns; macro RTOS_OVERRUN_TASK_IS_IMMEDIATELY_DUE is set to RTOS_FEATURE_OFF. The false overruns are counted nonetheless by rtos_getTaskOverrunCounter. Here, we implement our own overrun counter by comparing the task cycle time with the Arduino timer which coexists with the RTuinOS system timer. */ ti1 = millis(); if(ti2 > 0) { ti2 = ti1-ti2; if(ti2 < (uint32_t)(0.9*256.0*RTOS_TIC*1000.0) || ti2 > (uint32_t)(1.1*256.0*RTOS_TIC*1000.0) ) { ++ _task00_C0_trueTaskOverrunCnt; } } ti2 = ti1; /* What looks like CPU consuming floating point operations actually is a compile time operation. Here's the prove - which also produces no CPU load as it is removed by the optimizer. (Sounds contradictory but it isn't.) */ ASSERT(__builtin_constant_p((uint32_t)(0.9*256.0*RTOS_TIC*1000.0)) && __builtin_constant_p((uint32_t)(1.1*256.0*RTOS_TIC*1000.0)) ); } /* End for(ever) */ } /* End of task00_class00 */