Hi Neil
It is correct that the task can not wake itself when it is not running, but the task can be woken by other tasks, timers, messages or events (
by timer or event or another task setting it to the UTASKER_GO state).
When a task is in the UTASKER_GO state it is running continuously. I write 'polling' because it is not typical to run a task in the UTASKER_GO state since the task is running as often as it can get scheduled - which is usually only used (for short periods of time) when some hardware is being 'polled'.
At the bottom I have added a response to the question about whether a task can be halted.
Here is a suggestion as to how you can solve your concrete requirement. It uses one task with timer and input queue, started immediately once on start up and then on events only. There is also no reason why various other jobs can not be performed in the same task.
#define OWN_TASK MY_TASK
#define INITIALISATION 0
#define IDLE 1
#define PERFORM_SEQUENCE 2
#define E_TIMER_TIMEOUT 1
#define E_START_SEQUENCE 1
void fnMyTask(TTASKTABLE *ptrTaskTable) // Task configured to run once at start up and then sleep
{
static unsigned char ucSerialInput[RX_BUFFER_SIZE]; // static buffer for collecting UART data
static int iSerialRxLenth = 0; // length of collected UART data
static int iRunTimes;
static int iTaskState = INITIALISATION;
static QUEUE_HANDLE SerialPortID;
static QUEUE_HANDLE IICPortID;
QUEUE_HANDLE PortIDInternal = ptrTaskTable->TaskID; // queue ID for task input
unsigned char ucInputMessage[RX_BUFFER_SIZE]; // reserve space for receiving messages
if (INITIALISATION == iTaskState) { // configure interfaces on initialisation
SerialPortID = fnConfigureAndOpenUART();
IICPortID = fnConfigureAndOpenIIC();
iTaskState = IDLE;
}
while ( fnRead( PortIDInternal, ucInputMessage, HEADER_LENGTH )) { // check input queue
switch ( ucInputMessage[ MSG_SOURCE_TASK ] ) { // switch depending on message source
case TIMER_EVENT: // timer event
if (E_TIMER_TIMEOUT == ucInputMessage[ MSG_TIMER_EVENT ]) { // no reception from serial port, quit present sequence
iTaskState = IDLE; // return to idle state
}
break;
case INTERRUPT_EVENT: // interrupt event
if (E_START_SEQUENCE == ucInputMessage[MSG_INTERRUPT_EVENT]) { // wake up event message from RTC interrupt
if (IDLE == iTaskState) {
fnStartIIC_Read(); // start read of IIC bus
iTaskState = PERFORM_SEQUENCE; // mark that we are in the repetition sequence
iRunTimes = 20; // set repetition counter
}
}
break;
}
}
if ((PERFORM_SEQUENCE == iTaskState) && (fnMsgs(IICPortID) != 0)) { // check IIC read completions
QUEUE_TRANSFER LengthIIC;
while (LengthIIC = fnRead(IICPortID, ucInputMessage, MEDIUM_MESSAGE)) {
fnSendUART_Command(ucInputMessage, LengthIIC);
uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(2*SEC), E_TIMER_TIMEOUT );// start monitor timer
}
}
while (fnRead( SerialPortID, &ucSerialInput[iSerialRxLenth++], 1) != 0) { // collect serial port data
if (fnMessageComplete(ucSerialInput, iSerialRxLenth) != 0) {
fnProcessSerialData(ucSerialInput, iSerialRxLenth);
iSerialRxLenth = 0; // reset UART message length counter
uTaskerStopTimer(OWN_TASK); // stop monitor timer
if (iRunTimes != 0) {
iRunTimes--; // count down repetitions
fnStartIIC_Read(); // start next repetition of sequence
}
else {
iTaskState = IDLE; // this sequence complete
}
}
}
}
There a few routines that need to be filled in depending on you exact requirements:
fnConfigureAndOpenUART(); This is the standard UART open which returns the handle to the opened interface.
fnConfigureAndOpenIIC(); This is the equivalent for the IIC interface - see the document
http://www.utasker.com/docs/uTasker/uTaskerIIC.PDF for more details.
fnStartIIC_Read(); This is a routine which sends the required IIC commands to initiate the read of the required data. The 'owner' task will be woken when the complete data is ready.
fnSendUART_Command(); sends the UART data, possibly including data which has just been received via IIC.
fnMessageComplete(); is a routine which is checking whether a complete UART message has been received (the operation depends on the protocol and this assumes that each byte is collected and the content analysed until complete.
fnProcessSerialData(); This routine is called when a complete UART rx message is ready and can verify and interpret the message as required. Note that a complete reception always kills the UART monitor timer.
The operation is completely event driven. The events are
your interrupt routine can kick the sequence off by sending this
each time a complete IIC message (a read) is available the task is scheduled
each time a UART rx character is available the task is scheduled (assuming character mode)
the UART monitor timer has fired without receiving a UART answer
A few other notes:
- iTaskState is almost superfluous but it is often useful to have a state as programs develop (sometimes it comes in useful where not initially expected).
- iRunTimes could also be integrated with the state (to save the variable) but this doesn't help readability.
- The program can be simulated using the uTasker simulator (the simulator included support for the DS1307 which seems to have the same IIC address as the DS1337) so it can be easily analysed in this mode.
Important discussion about halting tasks.I have copied here a discussion about this. Basically it it about why the uTasker doesn't support pre-emptive multi-tasking including the reasons this way was chosen.
Regards
Mark
the uTasker is designed to be an easy to use operating system which is suitable for a high quantity of projects without the need of a good level of understanding of synchronisation issues. For this reason it is NOT-pre-emptive and doesn't support 'blocking'. There are a few disadvantages and some users with a lot of pre-emptive experience may find it a little restrictive. On the other hand it generally allows more efficient code and higher programming reliability due to the simplification is achieves.
Personally I have worked with several well known operating systems and have also written a pre-emptive OS for a DSP but have also recognised that about 95+% or real-world projects can easily (and often simpler) be achieved by using the 'super-loop' types strategy. I have been more surprised at the number of users who DON'T WANT (or don't need - as they usually say) an operating system. Many also have modules which they want to combine with the uTasker (such as open source protocol modules) which also assume a super-loop structure and so can be fit in with almost no effort but greatly optimised through replacement of polling tasks with the uTasker timers and interrupt event features. Would you believe that only about 2..3% of users even want to know whether the uTasker OS has pre-emptive capabilities? It has been a big eye-opener for me!!
The uTasker should offer an 'alternative' solution and not attempt to copy/replace/compete with others which are available. This is the path chosen and it has been very successful at achieving its goals (low support levels due to simplicity and low problem count due to the fact that users can not (easily) make errors which involve complex synchronisation issues).
One quick example (used in the demo project on the serial and Telnet ports) - rather than blocking on a tx queue there is a routine which checks whether there is space before sending data. If there is adequate space the driver is requested to inform when the space becomes available and the task is woken later to continue with the work. Such jobs involve a little more 'programmer' effort but are very workable in such situations. In fact a comparison of a uTasker based LAN<->RS232 product running on an ARM7 with 32k RAM achieved better and faster TCP<->RS232 throughput while performing file transfers than a well known ARM9 based device with MByte DRAM.
Other situations where time consuming calculations are performed by multiple tasks in parallel really requires a pre-emptive solution (there are methods which can and have been used to get around such issues in certain projects but these are a bit special and doesn't represent recommended or 'standard' procedures). Therefore there are certainly projects where the uTasker solution is less or NOT-suitable. It is however offered as an alternative and it is up to the user to decide its absolute suitability - there are many solutions available (with varying levels of performance, footprint size, complexity, price and support) and the uTasker solution tries to offer a simple, low-cost, well supported and open-source code one which is suitable for a large number of - but not all - projects.
Halting tasks during their execution (like sleep()) is something which is not supported. In exceptional cases, where it is the only viable solution it is possible to do this and schedule the OS itself from that code position (the OS is re-entrant) but this is the rather non-standard solution which is only used in emergencies, but can solve a critical point as long as it is restricted to only one (or maybe two) occurrences. Generally a state-event design [very well suited to communcation tasks] quite easily achieves the same results and is the recommended practice.