// CrossWorks Tasking Library. // // Copyright (c) 2004-2011 Rowley Associates Limited. // // This file may be distributed under the terms of the License Agreement // provided with this software. // // THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. #ifndef CrossWorks_Tasking_Library #define CrossWorks_Tasking_Library #ifdef __cplusplus extern "C" { #endif // // Task Support // /*! \brief Task states \ingroup Types \synopsis \desc \b \this defines the states the task can be on. \table{1.5in,*} || Constant | Description || CTL_STATE_RUNNABLE | Task can run. || CTL_STATE_TIMER_WAIT | Waiting for a time value. || CTL_STATE_EVENT_WAIT_ALL | Waiting for all events to be set. || CTL_STATE_EVENT_WAIT_ALL_AC | Waiting for all events to be set with auto clear. || CTL_STATE_EVENT_WAIT_ANY | Waiting for any events to be set. || CTL_STATE_EVENT_WAIT_ANY_AC | Waiting for any events to be set with auto clear. || CTL_STATE_SEMAPHORE_WAIT | Task is waiting for a semaphore. || CTL_STATE_MESSAGE_QUEUE_POST_WAIT | Task is waiting to post to a message queue. || CTL_STATE_MESSAGE_QUEUE_RECEIVE_WAIT | Task is waiting to receive from a message queue. || CTL_STATE_MUTEX_WAIT | Task is waiting for a mutex. || CTL_STATE_SUSPENDED | Task cannot run. \endtable */ typedef enum { CTL_STATE_RUNNABLE = 0x00, // Can run CTL_STATE_TIMER_WAIT = 0x01, // Waiting for a time value CTL_STATE_EVENT_WAIT_ALL = 0x02, // Waiting for all events to be set CTL_STATE_EVENT_WAIT_ALL_AC = 0x04, // Waiting for all events to be set with auto clear CTL_STATE_EVENT_WAIT_ANY = 0x06, // Waiting for any events to be set CTL_STATE_EVENT_WAIT_ANY_AC = 0x08, // Waiting for any events to be set with auto clear CTL_STATE_SEMAPHORE_WAIT = 0x0A, // Task is waiting for a semaphore CTL_STATE_MESSAGE_QUEUE_POST_WAIT = 0x0C, // Task is waiting to post to a message queue CTL_STATE_MESSAGE_QUEUE_RECEIVE_WAIT = 0x0E, // Task is waiting to receive from a message queue CTL_STATE_MUTEX_WAIT = 0x10, // Task is waiting for a mutex CTL_STATE_SUSPENDED = 0x80 // Task cannot run } CTL_STATE_t; /*! \group Types */ /*! \brief Time definition \ingroup Types \synopsis \desc \b \this defines the base type for times that CTL uses. */ typedef unsigned long CTL_TIME_t; /*! \brief Event set definition \ingroup Types \synopsis \desc \b \this defines an event set. Event sets are word sized 16 or 32 depending on the machine. */ typedef unsigned CTL_EVENT_SET_t; /*! \endgroup Types */ /*! \group Tasks */ /*! \brief Task struct definition \ingroup Types \synopsis \desc \b \this defines the task structure. The task structure contains: \table{1.5in,*} || Member | Description || stack_pointer | the saved register state of the task when it is not scheduled || priority | the priority of the task || state | the state of task CTL_STATE_RUNNABLE or (CTL_STATE_*_WAIT_* \| CTL_STATE_TIMER_WAIT) or CTL_STATE_SUSPENDED || timeout_occured | 1 if a wait timed out otherwise 0 - when state is CTL_RUNNABLE || next | next pointer for wait queue || timeout | wait timeout value or time slice value when the task is executing || wait_object | the event set, semaphore, message queue or mutex to wait on || wait_events | the events to wait for || thread_errno | thread specific errno || data | task specific data pointer || execution_time | number of ticks the task has executed for || stack_start | the start (lowest address) of the stack || name | task name \endtable */ typedef struct CTL_TASK_s { unsigned *stack_pointer; // don't move this - assembly code knows about it unsigned char priority; unsigned char state; unsigned char timeout_occured; struct CTL_TASK_s *next; CTL_TIME_t timeout; void *wait_object; CTL_EVENT_SET_t wait_events; int thread_errno; void *data; CTL_TIME_t execution_time; unsigned *stack_start; const char *name; } CTL_TASK_t; /*! \brief Create the initial task \synopsis \desc \b \this turns the main program into a task. This function takes a pointer in \a task to the \b CTL_TASK_t structure that represents the main task, its \a priority (0 is the lowest priority, 255 the highest), and a zero-terminated string pointed by \a name. On return from this function global interrupts will be enabled. \notes The function must be called before any other CrossWorks tasking library calls are made. */ void ctl_task_init(CTL_TASK_t *task, unsigned char priority, const char *name); /*! \brief Start a task \synopsis \desc \b \this takes a pointer in \a task to the \b CTL_TASK_t structure that represents the task. The \a priority can be zero for the lowest priority up to 255 which is the highest. The \a entrypoint parameter is the function that the task will execute which has the \a parameter passed to it. \a name is a pointer to a zero-terminated string used for debug purposes. The start of the memory used to hold the stack that the task will execute in is \a stack and the size of the memory is supplied in \a stack_size_in_words. On systems that have two stacks (e.g. Atmel AVR) then the \a call_size_in_words parameter must be set to specify the number of stack elements to use for the call stack. */ void ctl_task_run(CTL_TASK_t *task, unsigned char priority, void (*entrypoint)(void *), // entrypoint the task starts executing - parameter is passed to it void *parameter, // parameter to pass to entrypoint const char *name, unsigned stack_size_in_words, // note that the stack should be allocated in words not bytes unsigned *stack, unsigned call_size_in_words); // optional parameter if call stack is separate from data stack /*! \brief Remove a task from the task list \synopsis \desc \b \this removes the task \a task from the waiting task list. Once you you have removed a task the only way to re-introduce it to the system is to call \b ctl_task_restore. */ void ctl_task_remove(CTL_TASK_t *task); /*! \brief Put back a task on to the task list \synopsis \desc \b \this adds a task \a task that was removed (using \b ctl_task_remove) onto the task list and do scheduling. */ void ctl_task_restore(CTL_TASK_t *task); /*! \brief Terminate the executing task \synopsis \desc \b \this terminates the currently executing task and schedules the next runnable task. */ void ctl_task_die(void); /*! \brief Set the priority of a task \synopsis \desc \b \this changes the priority of \a task to \a priority. The priority can be 0, the lowest priority, to 255, which is the highest priority. You can change the priority of the currently executing task by passing \b ctl_task_executing as the \a task parameter. \b \this returns the previous priority of the task. */ unsigned char ctl_task_set_priority(CTL_TASK_t *task, unsigned char priority); /*! \brief Cause a reschedule \synopsis \desc \b \this causes a reschedule to occur. This can be used by tasks of the same priority to share the CPU without using timeslicing. */ void ctl_task_reschedule(void); /*! \endgroup */ /*! \group Timer */ // // Timer support // /*! \brief Atomically return the current time \synopsis \desc \b \this atomically reads the value of \b ctl_current_time. */ CTL_TIME_t ctl_get_current_time(void); /*! \brief Wait until timeout has occurred \synopsis \desc \b \this takes the \a timeout (not the duration) as a parameter and suspends the calling task until the current time reaches the timeout. \note \b \this must not be called from an interrupt service routine. */ void ctl_timeout_wait(CTL_TIME_t timeout); /*! \brief Increment tick timer \synopsis \desc \b \this increments \b ctl_current_time by the number held in \b ctl_time_increment and does rescheduling. This function should be called from a periodic interrupt service routine. \note \b \this must only be invoked by an interrupt service routine. */ void ctl_increment_tick_from_isr(void); /*! \endgroup */ /*! \brief Type of wait \ingroup Types \synopsis \desc \b \this defines the type of timeout for a blocking function call. \table{1.5in,*} || Constant | Description || CTL_TIMEOUT_NONE | No timeout \-- block indefinitely. || CTL_TIMEOUT_INFINITE | Identical to \b CTL_TIMEOUT_NONE. || CTL_TIMEOUT_ABSOLUTE | The timeout is an absolute time. || CTL_TIMEOUT_DELAY | The timeout is relative to the current time. || CTL_TIMEOUT_NOW | The timeout happens immediately \-- no rescheduling occurs. \endtable */ typedef enum { CTL_TIMEOUT_NONE, // no timeout - block indefinitely CTL_TIMEOUT_INFINITE = CTL_TIMEOUT_NONE, CTL_TIMEOUT_ABSOLUTE, // the timeout is an absolute time CTL_TIMEOUT_DELAY, // the timeout is a delay CTL_TIMEOUT_NOW // the timeout happens immediately - no rescheduling occurs } CTL_TIMEOUT_t; #define CTL_TIMEOUT_INFINITE CTL_TIMEOUT_NONE // // Event support // /*! \group Event sets */ /*! \brief Initialize an event set \synopsis \desc \b \this initializes the event set \a e with the \a set values. */ void ctl_events_init(CTL_EVENT_SET_t *e, CTL_EVENT_SET_t set); /*! \brief Set and clear events in an event set \synopsis \desc \b \this sets the events defined by \a set and clears the events defined by \a clear of the event set pointed to by \a e. \b \this will then search the task list, matching tasks that are waiting on the event set \a e and make them runnable if the match is successful. \sa \ref ctl_events_pulse. */ void ctl_events_set_clear(CTL_EVENT_SET_t *e, CTL_EVENT_SET_t set, CTL_EVENT_SET_t clear); /*! \brief Pulse events in an event set \synopsis \desc \b \this will set the events defined by \a set_then_clear in the event set pointed to by \a e. \b \this will then search the task list, matching tasks that are waiting on the event set \a e, and make them runnable if the match is successful. The events defined by \a set_then_clear are then cleared. \sa \ref ctl_events_set_clear. */ void ctl_events_pulse(CTL_EVENT_SET_t *e, CTL_EVENT_SET_t set_then_clear); /*! \brief Event set wait types \ingroup Types \synopsis \desc \b \this defines how to wait for an event set. \table{1.5in,*} || Constant | Description || CTL_EVENT_WAIT_ANY_EVENTS | Wait for any of the specified events to be set in the event set. || CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR | Wait for any of the specified events to be set in the event set and reset (clear) them. || CTL_EVENT_WAIT_ALL_EVENTS | Wait for all of the specified events to be set in the event set. || CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR | Wait for all of the specified events to be set in the event set and reset (clear) them. \endtable \sa \ref ctl_events_wait */ typedef enum { CTL_EVENT_WAIT_ANY_EVENTS = CTL_STATE_EVENT_WAIT_ANY, CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR = CTL_STATE_EVENT_WAIT_ANY_AC, CTL_EVENT_WAIT_ALL_EVENTS = CTL_STATE_EVENT_WAIT_ALL, CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR = CTL_STATE_EVENT_WAIT_ALL_AC } CTL_EVENT_WAIT_TYPE_t; /*! \brief Wait for events in an event set \synopsis \desc \b \this waits for \a events to be set (value 1) in the event set pointed to by \a eventSet with an optional \a timeout applied if \a timeoutType is non-zero. The \a waitType can be one of: \item \b CTL_EVENT_WAIT_ANY_EVENTS \-- wait for any of \a events in \a eventSet to be set. \item \b CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR \-- wait for any of \a events in \a eventSet to be set and reset (clear) them. \item \b CTL_EVENT_WAIT_ALL_EVENTS \-- wait for all \a events in \b{*eventSet} to be set. \item \b CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR \-- wait for all \a events in \b eventSet to be set and reset (clear) them. \b \this returns the value pointed to by \a eventSet before any auto-clearing occurred or zero if the \a timeout occurred. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_events_wait(CTL_EVENT_WAIT_TYPE_t type, CTL_EVENT_SET_t *eventSet, CTL_EVENT_SET_t events, CTL_TIMEOUT_t t, CTL_TIME_t timeout); /*! \endgroup */ // // Semaphore support // /*! \group Semaphores */ /*! \brief Semaphore definition \ingroup Types \synopsis \desc \b \this defines the semaphore type. Semaphores are held in one word, 16 or 32 bits depending on the machine. */ typedef unsigned CTL_SEMAPHORE_t; /*! \brief Initialize a semaphore \synopsis \desc \b \this initializes the semaphore pointed to by \a s to \a value. */ void ctl_semaphore_init(CTL_SEMAPHORE_t *s, unsigned value); /*! \brief Signal a semaphore \synopsis \desc \b \this signals the semaphore pointed to by \a s. If tasks are waiting for the semaphore then the highest priority task will be made runnable. If no tasks are waiting for the semaphore then the semaphore value will be incremented. */ void ctl_semaphore_signal(CTL_SEMAPHORE_t *s); /*! \brief Wait for a semaphore \synopsis \desc \b \this waits for the semaphore pointed to by \a s to be non-zero. If the semaphore is zero then the caller will block unless \a timeoutType is non-zero and the current time reaches the \a timeout value. If the timeout occurred \b \this returns zero otherwise it returns one. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_semaphore_wait(CTL_SEMAPHORE_t *s, CTL_TIMEOUT_t t, CTL_TIME_t timeout); /*! \endgroup */ // // Message queue support. // /*! \group Message queues */ /*! \brief Message queue struct definition \ingroup Types \synopsis \desc \b \this defines the message queue structure. The message queue structure contains: \table{1.5in,*} || Member | Description || q | pointer to the array of message queue objects || s | size of the array of message queue objects || front | the next element to leave the message queue || n | the number of elements in the message queue || e | the event set to use for the not empty and not full events || notempty | the event number for a not empty event || notfull | the event number for a not full event \endtable */ typedef struct { void **q; unsigned s; unsigned front; unsigned n; CTL_EVENT_SET_t *e; CTL_EVENT_SET_t notempty; CTL_EVENT_SET_t notfull; } CTL_MESSAGE_QUEUE_t; /*! \brief Initialize a message queue \synopsis \desc \b \this is given a pointer to the message queue to initialize in \a q. The array that will be used to implement the message queue pointed to by \a queue and its size in \a queue_size are also supplied. */ void ctl_message_queue_init(CTL_MESSAGE_QUEUE_t *q, // message queue pointer void **queue, // pointer to the queue unsigned queue_size); // the number of elements in the queue /*! \brief Associate events with the not-full and not-empty state of a message queue \synopsis \desc \b \this registers events in the event set \a e that are set when the message queue \a q becomes \a notempty or becomes \a notfull. No scheduling will occur with this operation, you need to do this before waiting for events. */ void ctl_message_queue_setup_events(CTL_MESSAGE_QUEUE_t *q, // message queue pointer CTL_EVENT_SET_t *e, // event set CTL_EVENT_SET_t notempty, CTL_EVENT_SET_t notfull); // event set on not_empty /*! \brief Return number of free elements in a message queue \synopsis \desc \b \this returns the number of free elements in the message queue \a q. */ unsigned ctl_message_queue_num_free(CTL_MESSAGE_QUEUE_t *q); /*! \brief Return number of used elements in a message queue \synopsis \desc \b \this returns the number of used elements in the message queue \a q. */ unsigned ctl_message_queue_num_used(CTL_MESSAGE_QUEUE_t *q); /*! \brief Post message to a message queue \synopsis \desc \b \this posts \a message to the message queue pointed to by \a q. If the message queue is full then the caller will block until the message can be posted or, if \a timeoutType is non-zero, the current time reaches \a timeout value. \b \this returns zero if the timeout occurred otherwise it returns one. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_message_queue_post(CTL_MESSAGE_QUEUE_t *q, // message queue pointer void *message, // the message to post CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Post message to a message queue without blocking \synopsis \desc \b \this posts \a message to the message queue pointed to by \a q. If the message queue is full then the function will return zero otherwise it will return one. */ unsigned ctl_message_queue_post_nb(CTL_MESSAGE_QUEUE_t *q, // message queue pointer void *message); // the message to post /*! \brief Receive message from a message queue \synopsis \desc \b \this pops the oldest message in the message queue pointed to by \a q into the memory pointed to by \a message. \b \this will block if no messages are available unless \a timeoutType is non-zero and the current time reaches the \a timeout value. \b \this returns zero if a timeout occurs otherwise 1. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_message_queue_receive(CTL_MESSAGE_QUEUE_t *q, // message queue pointer void **message, // pointer to message receiver CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Receive message from a message queue without blocking \synopsis \desc \b \this pops the oldest message in the message queue pointed to by \a q into the memory pointed to by \a message. If no messages are available the function returns zero otherwise it returns 1. */ unsigned ctl_message_queue_receive_nb(CTL_MESSAGE_QUEUE_t *q, // message queue pointer void **message); // pointer to message receiver /*! \brief Post messages to a message queue \synopsis \desc \b \this posts \a n \a messages to the message queue pointed to by \a q. The caller will block until the messages can be posted or, if \a timeoutType is non-zero, the current time reaches \a timeout value. \b \this returns the number of messages that were posted. \note \b \this must not be called from an interrupt service routine. \b \this function does not guarantee that the messages will be all be posted to the message queue atomically. If you have multiple tasks posting (multiple messages) to the same message queue then you may get unexpected results. */ unsigned ctl_message_queue_post_multi(CTL_MESSAGE_QUEUE_t *q, // message queue pointer unsigned n, // the number of messages to post void **messages, // the messages to post CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Post messages to a message queue without blocking \synopsis \desc \b \this posts \a n \a messages to the message queue pointed to by \a m. \b \this returns the number of messages that were posted. */ unsigned ctl_message_queue_post_multi_nb(CTL_MESSAGE_QUEUE_t *q, // message queue pointer unsigned n, // the number of messages to post void **messages); // the message to post /*! \brief Receive messages from a message queue \synopsis \desc \b \this pops the oldest \a n messages in the message queue pointed to by \a q into the memory pointed to by \a message. \b \this will block until all the messages are available unless \a timeoutType is non-zero and the current time reaches the \a timeout value. \b \this returns the number of messages that were received. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_message_queue_receive_multi(CTL_MESSAGE_QUEUE_t *q, // message queue pointer unsigned n, // the number of messages to receive void **messages, // pointer to message array CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Receive messages from a message queue without blocking \synopsis \desc \b \this pops the oldest \a n messages in the message queue pointed to by \a q into the memory pointed to by \a message. \b \this returns the number of messages that were received. */ unsigned ctl_message_queue_receive_multi_nb(CTL_MESSAGE_QUEUE_t *q, // message queue pointer unsigned n, // the number of messages to receive void **messages); // pointer to message array /*! \endgroup */ // // Byte queue support - same as message queue but specialized for bytes // /*! \group Bytes queues */ /*! \brief Byte queue struct definition \ingroup Types \synopsis \desc \b \this defines the byte queue structure. The byte queue structure contains: \table{1.5in,*} || Member | Description || q | pointer to the array of bytes || s | size of the array of bytes || front | the next byte to leave the byte queue || n | the number of elements in the byte queue || e | the event set to use for the not empty and not full events || notempty | the event number for a not empty event || notfull | the event number for a not full event \endtable */ typedef struct { unsigned char *q; unsigned s; unsigned front; unsigned n; CTL_EVENT_SET_t *e; CTL_EVENT_SET_t notempty; CTL_EVENT_SET_t notfull; } CTL_BYTE_QUEUE_t; /*! \brief Initialize a byte queue \synopsis \desc \b \this is given a pointer to the byte queue to initialize in \a q. The array that will be used to implement the byte queue pointed to by \a queue and its size in \a queue_size are also supplied. */ void ctl_byte_queue_init(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned char *queue, // pointer to the queue unsigned queue_size); // the number of bytes in the queue /*! \brief Associate events with the not-full and not-empty state of a byte queue \synopsis \desc \b \this registers events in the event set \a e that are set when the byte queue \a q becomes \a notempty or becomes \a notfull. No scheduling will occur with this operation, you need to do this before waiting for events. */ void ctl_byte_queue_setup_events(CTL_BYTE_QUEUE_t *q, // byte queue pointer CTL_EVENT_SET_t *e, // event set CTL_EVENT_SET_t notempty, CTL_EVENT_SET_t notfull); /*! \brief Return number of free bytes in a byte queue \synopsis \desc \b \this returns the number of free bytes in the byte queue \b q. */ unsigned ctl_byte_queue_num_free(CTL_BYTE_QUEUE_t *q); /*! \brief Return number of used bytes in a byte queue \synopsis \desc \b \this returns the number of used elements in the byte queue \b q. */ unsigned ctl_byte_queue_num_used(CTL_BYTE_QUEUE_t *q); /*! \brief Post byte to a byte queue \synopsis \desc \b \this posts \a b to the byte queue pointed to by \a q. If the byte queue is full then the caller will block until the byte can be posted or, if \a timeoutType is non-zero, the current time reaches \a timeout value. \b \this returns zero if the timeout occurred otherwise it returns one. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_byte_queue_post(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned char b, // the byte to post CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Post byte to a byte queue without blocking \synopsis \desc \b \this posts \a b to the byte queue pointed to by \a q. If the byte queue is full then the function will return zero otherwise it will return one. */ unsigned ctl_byte_queue_post_nb(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned char b); // the byte to post /*! \brief Receive a byte from a byte queue \synopsis \desc \b \this pops the oldest byte in the byte queue pointed to by \a q into the memory pointed to by \a b. \b \this will block if no bytes are available unless \a timeoutType is non-zero and the current time reaches the \a timeout value. \b \this returns zero if a timeout occurs otherwise 1. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_byte_queue_receive(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned char *b, // pointer to byte receiver CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Receive a byte from a byte queue without blocking \synopsis \desc \b \this pops the oldest byte in the byte queue pointed to by \a m into the memory pointed to by \a b. If no bytes are available the function returns zero otherwise it returns 1. */ unsigned ctl_byte_queue_receive_nb(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned char *b); // pointer to byte receiver /*! \brief Post bytes to a byte queue \synopsis \desc \b \this posts \a n bytes to the byte queue pointed to by \a q. The caller will block until the bytes can be posted or, if \a timeoutType is non-zero, the current time reaches \a timeout value. \b \this returns the number of bytes that were posted. \note \b \this must not be called from an interrupt service routine. \b \this does not guarantee that the bytes will be all be posted to the byte queue atomically. If you have multiple tasks posting (multiple bytes) to the same byte queue then you may get unexpected results. */ unsigned ctl_byte_queue_post_multi(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned n, // the number of bytes to post unsigned char *b, // the bytes to post CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Post bytes to a byte queue without blocking \synopsis \desc \b \this posts \a n bytes to the byte queue pointed to by \a q. \b \this returns the number of bytes that were posted. */ unsigned ctl_byte_queue_post_multi_nb(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned n, // the number of bytes to post unsigned char *b); // the bytes to post /*! \brief Receive multiple bytes from a byte queue \synopsis \desc \b \this pops the oldest \a n bytes in the byte queue pointed to by \a q into the memory pointed at by \a b. \b \this will block until the number of bytes are available unless \a timeoutType is non-zero and the current time reaches the \a timeout value. \b \this returns the number of bytes that have been received. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_byte_queue_receive_multi(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned n, // the number of bytes to receive unsigned char *b, // pointer to byte array CTL_TIMEOUT_t t, // type of timeout CTL_TIME_t timeout); // timeout /*! \brief Receive multiple bytes from a byte queue without blocking \synopsis \desc \b \this pops the oldest \a n bytes in the byte queue pointed to by \a q into the memory pointed to by \a b. \b \this returns the number of bytes that have been received. */ unsigned ctl_byte_queue_receive_multi_nb(CTL_BYTE_QUEUE_t *q, // byte queue pointer unsigned n, // the number of bytes to receive unsigned char *b); // pointer to byte array /*! \endgroup */ // // Mutex support // /*! \group Mutexes */ /*! \brief Mutex struct definition \ingroup Types \synopsis \desc \b \this defines the mutex structure. The mutex structure contains: \table{1.5in,*} || Member | Description || lock_count | number of times the mutex has been locked || locking_task | the task that has locked the mutex || locking_task_priority | the priority of the task at the time it locked the mutex \endtable */ typedef struct { unsigned lock_count; CTL_TASK_t *locking_task; unsigned locking_task_priority; } CTL_MUTEX_t; /*! \brief Initialize a mutex \synopsis \desc \b \this initializes the mutex pointed to by \a m. */ void ctl_mutex_init(CTL_MUTEX_t *m); /*! \brief Unlock a mutex \synopsis \desc \b \this unlocks the mutex pointed to by \a m. The mutex must have previously been locked by the calling task. If the calling task's priority has been raised (by another task calling \b \this whilst the mutex was locked), then the calling tasks priority will be restored. \note \b \this must not be called from an interrupt service routine. */ void ctl_mutex_unlock(CTL_MUTEX_t *m); /*! \brief Lock a mutex \synopsis \desc \b \this locks the mutex pointed to by \a m to the calling task. If the mutex is already locked by the calling task then the mutex lock count is incremented. If the mutex is already locked by a different task then the caller will block until the mutex is unlocked. In this case, if the priority of the task that has locked the mutex is less than that of the caller the priority of the task that has locked the mutex is raised to that of the caller whilst the mutex is locked. If \a timeoutType is non-zero and the current time reaches the \a timeout value before the lock is acquired the function returns zero otherwise it returns one. \note \b \this must not be called from an interrupt service routine. */ unsigned ctl_mutex_lock(CTL_MUTEX_t *m, CTL_TIMEOUT_t t, CTL_TIME_t timeout); /*! \endgroup Mutex */ // // Global interrupts support // /*! \group Interrupts */ /*! \brief Enable/disable interrupts \synopsis \desc \b \this disables or enables global interrupts according to the state \b enable. If \b enable is zero, interrupts are disabled and if \b enable is non-zero, interrupts are enabled. If \this is called and interrupts are already disabled then it will return 0. If \this is called and interrupts are enabled then it will return non-zero which may or may not represent the true interrupt disabled state. \this is used to provide exclusive access to CTL data structures the implementation of it may or may not disable global interrupts. */ int ctl_global_interrupts_set(int enable); #if defined(__CROSSWORKS_DOCUMENTATION) || defined(_lint) /*! \brief Disable global interrupts \synopsis \desc \b \this disables global interrupts. If \this is called and interrupts are already disabled then it will return 0. If \this is called and interrupts are enabled then it will return non-zero which may or may not represent the true interrupt disabled state. \this is used to provide exclusive access to CTL data structures the implementation of it may or may not disable global interrupts. */ int ctl_global_interrupts_disable(void); /*! \brief Enable global interrupts \synopsis \desc \b \this enables global interrupts. \this is used to provide exclusive access to CTL data structures the implementation of it may or may not disable global interrupts. */ void ctl_global_interrupts_enable(void); #else #if !defined(__NO_USE_INTRINSICS__) && defined(__CROSSWORKS_ARM) && ((defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_6__)) && !defined(__thumb__)) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) #include <intrinsics.h> #define ctl_global_interrupts_disable()\ (__disable_irq() == 0) #define ctl_global_interrupts_enable()\ __enable_irq() #elif !defined(__NO_USE_INTRINSICS__) && defined(__CROSSWORKS_ARM) && defined(__ARM_ARCH_6M__) #define ctl_global_interrupts_disable()\ ({\ int __primask;\ __asm__ __volatile__("mrs %[__primask], primask\n"\ "cpsid i\n"\ : [__primask] "=r" (__primask));\ (__primask == 0);\ }) #define ctl_global_interrupts_enable()\ ({\ __asm__ __volatile__("cpsie i\n");\ }) #elif !defined(__NO_USE_INTRINSICS__) && defined(__CROSSWORKS_ARM) && (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) #define ctl_global_interrupts_disable()\ ({\ int __basepri, __tmp;\ __asm__ __volatile__("mrs %[__basepri], basepri\n"\ "mov %[__tmp], #0x80\n"\ "msr basepri, %[__tmp]\n"\ : [__basepri] "=r" (__basepri), [__tmp] "=r" (__tmp));\ ((__basepri & 0x80) == 0);\ }) #define ctl_global_interrupts_enable()\ ({\ int __tmp;\ __asm__ __volatile__("mov %[__tmp], #0x00\n"\ "msr basepri, %[__tmp]\n"\ : [__tmp] "=r" (__tmp));\ }) #elif !defined(__NO_USE_INTRINSICS__) && defined(__CROSSWORKS_AVR) #include <inavr.h> #define ctl_global_interrupts_disable()\ (__bic_SR_register(0x80) & 0x80) #define ctl_global_interrupts_enable()\ __bis_SR_register(0x80) #elif !defined(__NO_USE_INTRINSICS__) && defined(__CROSSWORKS_MSP430) #include <inmsp.h> #define ctl_global_interrupts_disable()\ (__bic_SR_register(8) & (8)) #define ctl_global_interrupts_enable()\ __bis_SR_register(8) #else #define ctl_global_interrupts_disable() ctl_global_interrupts_set(0) #define ctl_global_interrupts_enable() ctl_global_interrupts_set(1) #endif #endif /*! \endgroup */ // // Error handling // /*! \group Error handling */ /*! \brief Error cause \ingroup Types \synopsis \desc \b \this defines the set of errors that are detected by the CrossWorks tasking library; the errors are reported by a call to \b ctl_handle_error. \table{1.5in,*} || Constant | Description || CTL_ERROR_NO_TASKS_TO_RUN | A reschedule has occurred but there are no tasks which are runnable. || CTL_UNSUPPORTED_CALL_FROM_ISR | An interrupt service routine has called a tasking library function that could block or is otherwise unsupported when called from inside an interrupt service routine. || CTL_MUTEX_UNLOCK_CALL_ERROR | A task called \a ctl_mutex_unlock passing a mutex which it has not locked, or which a different task holds a lock on. Only the task that successfully acquired a lock on a mutex can unlock that mutex. || CTL_UNSPECIFIED_ERROR | An unspecified error has occurred. \endtable */ typedef enum { CTL_ERROR_NO_TASKS_TO_RUN, CTL_UNSUPPORTED_CALL_FROM_ISR, CTL_MUTEX_UNLOCK_CALL_ERROR, CTL_UNSPECIFIED_ERROR } CTL_ERROR_CODE_t; /*! \brief Handle a CTL error condition \synopsis \desc \b \this is a function that you must supply in your application that handles errors detected by the CrossWorks tasking library. The errors that can be reported in \a e are are described in \ref CTL_ERROR_CODE_t. */ void ctl_handle_error(CTL_ERROR_CODE_t __e); /*! \endgroup */ // // Memory area support // /*! \group Memory areas */ /*! \brief Memory area struct definition \ingroup Types \synopsis \desc \b \this defines the memory area structure. The memory area structure contains: \table{1.5in,*} || Member | Description || head | the next free memory block || e | the event set containing the blockavailable event || blockavailable | the blockavailable event \endtable */ typedef struct { unsigned *head; CTL_EVENT_SET_t *e; CTL_EVENT_SET_t blockavailable; } CTL_MEMORY_AREA_t; /*! \brief Initialize a memory area \synopsis \desc \b \this is given a pointer to the memory area to initialize in \a memory_area. The array that is used to implement the memory area is pointed to by \a memory. The size of a memory block is given supplied in \a block_size_in_words and the number of block is supplied in \a num_blocks. \note \a memory must point to a block of memory that is at least \a block_size_in_words \times \a num_blocks words long. */ void ctl_memory_area_init(CTL_MEMORY_AREA_t *memory_area, // pointer to the memory area unsigned *memory, // should be block_size_in_words * num_blocks in length unsigned block_size_in_words, // size of the memory block in words unsigned num_blocks); // the number of blocks /*! \brief Set memory area events \synopsis \desc \b \this registers the events \a blockavailable in the event set \a e that are set when a block becomes available in the the memory area \a m. */ void ctl_memory_area_setup_events(CTL_MEMORY_AREA_t *m, // memory area pointer CTL_EVENT_SET_t *e, // event set CTL_EVENT_SET_t blockavailable); /*! \brief Allocate a block from a memory area \synopsis \desc \b \this allocates a block from the initialized memory area \a memory_area. \b \this returns a block of the size specified in the call to \ref ctl_memory_area_init or zero if no blocks are available. \b \this executes in constant time and is very fast. You can call \b \this from an interrupt service routine, from a task, or from initialization code. */ unsigned *ctl_memory_area_allocate(CTL_MEMORY_AREA_t *memory_area); /*! \brief Free a memory area block \synopsis \desc \b \this is given a pointer to a memory area \a memory_area which has been initialized and a \a block that has been returned by \ref ctl_memory_area_allocate. The block is returned to the memory area so that it can be allocated again. */ void ctl_memory_area_free(CTL_MEMORY_AREA_t *memory_area, unsigned *block); /*! \endgroup */ // // System state // /*! \group System state variables */ /*! \brief List of tasks sorted by priority \synopsis \desc \b \this points to the \b CTL_TASK_t structure of the highest priority task that is not executing. It is an error if \b \this is \b NULL. */ extern CTL_TASK_t *ctl_task_list; /*! \brief The task that is currently executing \synopsis \desc \b \this points to the \b CTL_TASK_t structure of the currently executing task. The \b priority field is the only field in the \b CTL_TASK_t structure that is defined for the task that is executing. It is an error if \b \this is \b NULL. */ extern CTL_TASK_t *ctl_task_executing; /*! \brief Nested interrupt count \synopsis \desc \b \this contains a count of the interrupt nesting level. This variable must be incremented immediately on entry to an interrupt service routine and decremented immediately before return from the interrupt service routine. */ extern unsigned char ctl_interrupt_count; /*! \brief Reschedule is required on last ISR exit \synopsis \desc \b \this is set to a non-zero value if a CTL call is made from an interrupt service routine that requires a task reschedule. This variable is checked and reset on exit from the last nested interrupt service routine. */ extern unsigned char ctl_reschedule_on_last_isr_exit; /*! \brief The current time in ticks \synopsis \desc \b \this holds the current time in ticks. \b \this is incremented by \b ctl_increment_ticks_from_isr. \note For portable programs without race conditions you should not read this variable directly, you should use \b ctl_get_current_time instead. As this variable is changed by an interrupt, it cannot be read atomically on processors whose word size is less than 32 bits without first disabling interrupts. That said, you can read this variable directly in your interrupt handler as long as interrupts are still disabled. */ extern CTL_TIME_t ctl_current_time; /*! \brief Time slice period in ticks \synopsis \desc \b \this contains the number of ticks to allow a task to run before it will be preemptively rescheduled by a task of the same priority. The variable is set to zero by default so that only higher priority tasks will be preemptively scheduled. */ extern CTL_TIME_t ctl_timeslice_period; /*! \brief Current time tick increment \synopsis \desc \b \this contains the value that \b ctl_current_time is incremented when \b ctl_increment_tick_from_isr is called. */ extern unsigned ctl_time_increment; /*! \brief The time (in ticks) of the last task schedule \synopsis \desc \b \this contains the time (in ticks) of the last task schedule. \desc \b \this contains the time of the last reschedule in ticks. */ extern CTL_TIME_t ctl_last_schedule_time; /*! \brief A function pointer called on a task switch \synopsis \desc \b \this contains a pointer to a function that is called (if it is set) when a task schedule occurs. The task that will be scheduled is supplied as a parameter to the function (\b ctl_task_executing will point to the currently scheduled task). Note that the callout function is called from the CTL scheduler and as such any use of CTL services whilst executing the callout function has undefined behavior. \note Because this function pointer is called in an interrupt service routine, you should assign it before interrupts are started or with interrupts turned off. */ extern void (*ctl_task_switch_callout)(CTL_TASK_t *); /*! \endgroup */ #ifdef __cplusplus } #endif #endif