In weAutSys user respectively application specific software comes as threads.
These threads as well as the system threads are implemented as protothreads. The following user / application threads are or may be defined
A weAutSys / weAut_01 application is implemented by writing one or more of those threads (and usually nothing else). The weAutSys' user, customer or application programmer provides these threads by writing a protothread function of type ptfnct_t
f()
(see pt.h).
From list above the first one, the initialisation thread function appInitThreadF(), must be thus provided, even if doing almost nothing.
And at least one of the others has to be implemented and registered in the appInitThreadF() function. Otherwise the whole thing would run without any process control effects to the external world interfaces.
No thread must run in one flow longer than 20 % of its tick period or 3 ms, whatever is shorter.
The said 3 ms limit is critical and must be reduced to 300 µs *2) if the following should hold:
If a task takes longer as the time limits stated above, the thread must interrupt itself by Protothreads blocking or yielding operations *3) at due intervals. A software running longer than 100 ms without yielding will effectively detriment the work of the system. Running longer than 250 ms without yielding will cause a reset, i.e. abort.
The background of these basic rules is that Protothreads and hence weAutSys is a non preemptive OS (for 1 CPU core). So no (user or system) thread will take over from another except at the said Protothreads operations, i.e. yielding, blocking and exiting. Nor will any thread run (really or quasi) parallel with another. This simplifies a big lot *1), at the cost of the strict rules for processor time usage above.
Remark 1: The only remaining complication, the interrupts, are handled by weAutSys. It is highly discouraged for customer software to introduce additional interrupts. It should be kept in mind that Protothreads operations and semaphores are "intra-Protothreads" only and not suitable to synchronise with interrupts.
Remark 2: If the 1ms application thread is critical in said (300 µs) sense, refrain from using SMC and especially file system operations while automation cycles are on.
Remark 3: This set of rules put a lot of trust and responsibility to the application programmer. This is adequate for embedded automation systems in the light of the software quality required for process control systems anyhow. This assumption would fail in a universal purpose computer system (like a PC).
Functions | |
ptfnct_t | app100msThreadF (struct mThr_data_t *uthr_data) |
The user / application specific 100 ms thread. | |
ptfnct_t | app10msThreadF (struct mThr_data_t *uthr_data) |
The user / application specific 10 ms thread. | |
ptfnct_t | app1msThreadF (struct mThr_data_t *uthr_data) |
The user / application specific 1 ms thread. | |
ptfnct_t | app1sThreadF (struct mThr_data_t *uthr_data) |
The user / application specific one second thread. | |
ptfnct_t | appBgTskThreadF (void) |
The user / application specific background task thread (the function) | |
ptfnct_t | appInitThreadF (struct pt *pt) |
The user / application specific initialisation thread. | |
ptfnct_t | appSerInpThreadF (struct thr_data_t *serInpThread) |
The user / application serial input processing thread. | |
p2ptFun | registerAppBgTskThread (p2ptFun threadF) |
Register a user / application background task thread. | |
p2ptFunC | registerAppSerInpThread (p2ptFunC threadF) |
Register a user / application serial input processing thread. | |
Variables | |
struct mThr_data_t | app100msThread |
The user / application 100ms thread. | |
struct mThr_data_t | app10msThread |
The user / application 10ms thread. | |
struct mThr_data_t | app1msThread |
The user / application 1ms thread. | |
struct mThr_data_t | app1sThread |
The user / application 100ms thread. | |
struct mThr_data_t | appBgTskThread |
The user / application specific background task thread. | |
struct thr_data_t | appSerInpThread |
The user / application serial input handling thread. |
p2ptFunC registerAppSerInpThread | ( | p2ptFunC | threadF | ) |
Register a user / application serial input processing thread.
The thread so registered will be scheduled if the serial input buffer contains a line feed (and hence a line to interpret as command) or is nearly full.
It is expected that the (user's / customer's) implementation reads one line (resp. all the input causing the near overflow) and interprets it according to its own specification. The function setCliLine() can be used to hand an input line to system and application command line interpreters.
threadF | the thread function to register; |
ptfnct_t appSerInpThreadF | ( | struct thr_data_t * | serInpThread | ) |
The user / application serial input processing thread.
If something is to be done in the user / application software for characters or lines received via serial input an input processing thread (i.e. a protothread function) must be provided therefore in an application specific source file and that thread function has to be registered according to this declaration.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
The parameter this function (if registered) would be called with will (at present) point to appSerInpThread. Its flag will be set by system software as described there. Its up to the implementation to read (buffered) UART input and act on it accordingly. If the UART connects to a human operated terminal one would normally use the the variable part of the parameter thread structure as command line interpreter (CLI) structure by help of the setCliLine() function.
serInpThread |
p2ptFun registerAppBgTskThread | ( | p2ptFun | threadF | ) |
Register a user / application background task thread.
The thread so registered thread will be scheduled at lowest priority.
threadF | the thread to register; |
ptfnct_t appBgTskThreadF | ( | void | ) |
The user / application specific background task thread (the function)
If something can or has to be done with the lowest priority (and in CPU usage partitioning < 2ms (!)) it may be done the background thread.
In that case a protothread function must be provided therefore in an application specific source file and that thread function has to be registered accordingly.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
ptfnct_t appInitThreadF | ( | struct pt * | pt | ) |
The user / application specific initialisation thread.
This protothread function must be provided in an user / application specific source file. This thread is scheduled in one to three phases divided by yield points or until it exits.
Phase 1: pre system init.
In this phase user variables and data structures may be set as well as system variables controlling system initialisation.
Phase 2: post system init.
In this phase serial communication is available.
Phase 3: post persistence init.
In this phase persistent configuration data have been fetched and may be modified before being used in following phases.
Phase 4: post Ethernet init.
In this phase the driver and the stack are initialised. This does not necessarily mean the link is up or the valid IP address is set already.
Nevertheless communication might be prepared and listen calls might be made here.
Phase 5: post ? ? (future use) init.
In this phase is presently empty.
Phase 6: reschedule until exited.
From this phase on the initialisation thread will be rescheduled until it exits. This allows for any initialisation work whatever complicated or time consuming.
Once exited the initialisation thread will never be rescheduled. This is already true for all above phases.
It is essential that all user threads (cyclic, CLI etc.) be registered here.
The weAut_01 board allows some hardware variants by
The user's initialisation thread is the (main) place to adapt to such and other variants.
pt | pointer to protothread structure |
ptfnct_t app1sThreadF | ( | struct mThr_data_t * | uthr_data | ) |
The user / application specific one second thread.
If something is to be done in the user / application one second thread a protothread function must be provided therefore in an application specific source file and that thread function has to be registered accordingly.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
uthr_data | pointer to extended protothread structure |
setStatusLedRd(ON); // Test: only blink blink
setTestPin1(OFF); // Test: execution time measurement
ptfnct_t app100msThreadF | ( | struct mThr_data_t * | uthr_data | ) |
The user / application specific 100 ms thread.
If something is to be done in the user / application 100 milliseconds thread a protothread function must be provided therefore in an application specific source file and that thread function has to be registered accordingly.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
uthr_data | pointer to extended protothread structure |
ptfnct_t app10msThreadF | ( | struct mThr_data_t * | uthr_data | ) |
The user / application specific 10 ms thread.
If something is to be done in the user / application 10 milliseconds thread a protothread function must be provided therefore in an application specific source file and that thread function has to be registered accordingly.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
uthr_data | pointer to extended protothread structure |
(! doDriverOK())
ptfnct_t app1msThreadF | ( | struct mThr_data_t * | uthr_data | ) |
The user / application specific 1 ms thread.
If something is to be done in the user / application 1 milliseconds thread a protothread function must be provided therefore in an application specific source file and that thread function has to be registered accordingly. Best care should be taken to have the CPU load between two yield or block points well below 150µs.
For that function any name can be chosen. This declaration is provided just for convenience and harmonisation.
uthr_data | pointer to extended protothread structure |
struct mThr_data_t app10msThread |
The user / application 10ms thread.
The flag value is the number of periods the scheduled thread should work on. It is normally 1 except for delays due to processor overload. The flag must be reset by the thread function
struct mThr_data_t app1msThread |
The user / application 1ms thread.
The flag value is the number of periods the scheduled thread should work on. It might well be greater than 1 as processor load might very well inhibit this thread to be scheduled at every single milliseconds tick. The flag must be reset by the thread function.
struct mThr_data_t app100msThread |
The user / application 100ms thread.
The flag value is the number of periods the scheduled thread should work on. It is normally 1 except for delays due to processor overload. This flag must be reset by the thread function.
struct mThr_data_t app1sThread |
The user / application 100ms thread.
The flag value is the number of periods the scheduled thread should work on. It is normally 1 except for delays due to processor overload. This flag must be reset by the thread function.
struct thr_data_t appSerInpThread |
The user / application serial input handling thread.
This thread handles serial (UART0) input if the user software registers an appropriate function therefore.
The flag will be set by system software, whenever
a) the UART receives a line feed or return code, i.e. when uartInRetBufferd() would return >= 1 or
b) the UART input buffer is filled to (near full) a level, i.e. when the the sender would be stopped by flow control (if activated).
appSerInpThread.flag will be cleared by system software if the UART input buffer gets empty.
It is up to the registered user thread function to reset the flag on other conditions if appropriate.