static void taskOnADCComplete(uint16_t initialResumeCondition) { ASSERT(initialResumeCondition == EVT_ADC_CONVERSION_COMPLETE); #ifdef DEBUG /* Test: Our ADC interrupt should be synchronous with Arduino's TIMER0_OVF (see wiring.c). */ extern volatile unsigned long timer0_overflow_count; uint32_t deltaCnt = timer0_overflow_count; #endif do { /* Test: Our ADC interrupt should be synchronous with Arduino's TIMER0_OVF. */ ASSERT(adc_noAdcResults + deltaCnt == timer0_overflow_count); /* Call the actual interrupt handler code. */ adc_onConversionComplete(); } while(rtos_waitForEvent( EVT_ADC_CONVERSION_COMPLETE | RTOS_EVT_DELAY_TIMER , /* all */ false , /* timeout */ 1 ) #ifdef DEBUG == EVT_ADC_CONVERSION_COMPLETE #endif ); /* The following assertion fires if the ADC interrupt isn't timely. The wait condition specifies a sharp timeout. True production code would be designed more failure tolerant and e.g. not specify a timeout at all. This code would cause a reset in case. */ ASSERT(false); } /* End of taskOnADCComplete */
static void taskButton(uint16_t initialResumeCondition) { ASSERT(initialResumeCondition == EVT_TRIGGER_TASK_BUTTON); do { but_onNewButtonVoltage(); } while(rtos_waitForEvent(EVT_TRIGGER_TASK_BUTTON, /* all */ false, 0)); ASSERT(false); } /* End of taskButton */
static void taskIdleFollower(uint16_t initialResumeCondition) { ASSERT(initialResumeCondition == EVT_TRIGGER_IDLE_FOLLOWER_TASK); do { dpy_display.printCpuLoad(_cpuLoad); } while(rtos_waitForEvent(EVT_TRIGGER_IDLE_FOLLOWER_TASK, /* all */ false, 0)); ASSERT(false); } /* End of taskIdleFollower */
inline boolean dpy_display_t::acquireMutex() { uint16_t gotEvtVec = rtos_waitForEvent( EVT_MUTEX_LCD | RTOS_EVT_DELAY_TIMER , /* all */ false , 1 /* unit is 2 ms */ ); /* Normally, no task will block the display longer than 2ms and the debug compilation double-checks this. Production code can nonetheless be implemented safe; in case it can simply skip display operation. */ ASSERT(gotEvtVec == EVT_MUTEX_LCD); return (gotEvtVec & EVT_MUTEX_LCD) != 0; } /* End of dpy_display_t::acquireMutex */
static void task00_class00(uint16_t initCondition) { 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); /* 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. This task has a round robin time slice of 10 tics (20 ms) only, so it should surely be interrupted during execution of delay. The other round robin task has a time slice of 4 ms. No other tasks demand the CPU significantly. Consequently, the code in delay should not be interrupted for longer than about 4 ms. Coming back here means to immediately do the next check if the demanded time has elapsed. We expect thus to not prolongue the demanded time by more than about 4 ms. */ uint32_t ti0 = millis(); delay(600 /* ms */); uint16_t dT = (uint16_t)(millis() - ti0); ASSERT(dT >= 599) ASSERT(dT < 609); /* 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. Let's have a look on the overrrun counter for this task. It might occasionally be incremented. */ if(rtos_waitForEvent( /* eventMask */ RTOS_EVT_EVENT_03 | RTOS_EVT_DELAY_TIMER , /* all */ false , /* timeout */ 1000 /* unit 2 ms */ ) == RTOS_EVT_DELAY_TIMER ) { ++ _task00_C0_cntWaitTimeout; } } } /* End of task00_class00 */
static void taskDisplayVoltage(uint16_t initialResumeCondition) { ASSERT(initialResumeCondition == EVT_TRIGGER_TASK_DISPLAY_VOLTAGE); /* The rate of the result values is about once every 133 ms, which makes the display quite nervous. And it would become even faster is the averaging constant ADC_NO_AVERAGED_SAMPLES would be lowered. Therefore we average here again to get are better readable, more stable display. The disadvantage: The state machine in module adc synchronizes switching the ADC input with the series of averaged samples. This is impossible here, which means that - in the instance of switching to another ADC input - the averaging series formed here typically consist of some samples from the former input and some from the new input. We do no longer see a sharp switch but a kind of cross fading. */ #define NO_AVERAGED_SAMPLES 5 #define SCALING_BIN_TO_V(binVal) \ ((ADC_U_REF/(double)((uint32_t)NO_AVERAGED_SAMPLES*ADC_NO_AVERAGED_SAMPLES)/1024.0) \ *(double)(binVal) \ ) static uint32_t accumuatedAdcResult_ = 0; static uint8_t noMean_ = NO_AVERAGED_SAMPLES; do { /* This low priority task needs to apply a critical section to read the result of the ADC interrupt task of high priority. */ cli(); accumuatedAdcResult_ += adc_inputVoltage; sei(); if(--noMean_ == 0) { dpy_display.printVoltage(SCALING_BIN_TO_V(accumuatedAdcResult_)); /* Start next series on averaged samples. */ noMean_ = NO_AVERAGED_SAMPLES; accumuatedAdcResult_ = 0; } } while(rtos_waitForEvent(EVT_TRIGGER_TASK_DISPLAY_VOLTAGE, /* all */ false, 0)); ASSERT(false); #undef NO_AVERAGED_SAMPLES #undef SCALING_BIN_TO_V } /* End of taskDisplayVoltage */
static RTOS_TRUE_FCT void getResource() { /* The task which calls this routine suspends itself by waiting only for the mutex which indicates ownership of the shared resource. No timeout is specified. Therefore, testing the return code of the suspend command is superflous; any value other than the mutex event would be an error of RTuinOS. Don't be confused that the same function getResource can individually suspend different tasks all calling this function - even overlappingly in time. This is what reentrant actually means: The same function code operates individually and independently for each caller. Only to prove this statement we declared the functions as RTOS_TRUE_FCT. Otherwise gcc would simply inline it and there was no issue with reentrance at all. */ #ifdef DEBUG uint16_t postedEvtVec = #endif rtos_waitForEvent(EVT_MUTEX_OWNING_RESOURCE, false, 0); ASSERT(postedEvtVec == EVT_MUTEX_OWNING_RESOURCE); } /* End of getResource */
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 */