📄 opex.c
字号:
////////////////////////////////////////////////////////////////
//
// OPEX.c
// (C) 2004 Steve Childress stevech@san.rr.com
#include "OPEX.h"
extern void user_idle(void); // may not exist in user program
void OPEX_sched_insert_ordered(OPEX_TCB *); // herein
BYTE OPEX_sched_flags;
// queue head pointers
OPEX_TCB *schedQ=NULL; // list of those scheduled for a future time
OPEX_TCB *pendingQ=NULL; // list of those held here until sched moves to schedQ
OPEX_TCB *flagwaitQ=NULL; // list of those held here until sched moves to schedQ
BYTE OPEX_sched_started;
void(*idle_funcp)(void); // pointer to user defined idle function
////////////////////////////////////////////////////////////////
// Access or change state of a flag and unsuspend the first func waiting on
// that flag and bit. A second or Nth task will remain suspended, to
// permit an ever-present task waiting on a flag bit. This avoids
// loss of flag change events, if two tasks are used.
//
// OK to call from ISR (can be interrupted and called by ISR)
void OPEX_sched_change_flag(BYTE *flags, BYTE bitno, BYTE state)
{
OPEX_TCB *f, *f1;
BYTE b, bitno_mask;
BYTE iflag;
bitno_mask = 1 << (bitno & 7);
// save master interrupt flag from SREG
iflag = bit_is_set(SREG, 7); // 0 if called by ISR
cli(); // <<<<<<<<<<< don't let ISR interrupt or reenter this
b = *flags & bitno_mask; // get relevant current flag bit
if (state != 0) // alter per passed argument
*flags |= bitno_mask; // bit set
else
*flags &= ~bitno_mask; // bit clear
// unsuspend any functions waiting on certain bits in the flag to change state
f = flagwaitQ; // head of queue
while (f) { // unsuspend first tasks waiting on that flag to change state
f1 = f->next; // retain linked list
if (f->flags == flags) { // this task's flag pointer same as arg passed?
if ((bitno_mask & f->flagmask) != 0) { // waiting on this bit to change?
OPEX_sched_queue_remove(&flagwaitQ, f);
OPEX_sched_queue_insert_head(&pendingQ, f); // fast code, since an ISR can call this
break; // just do the first waiting task.
}
}
f = f1; // follow flagwaitQ list
}
//}
if (iflag) // restore interrupt flag
sei();
}
////////////////////////////////////////////////////////////////
// Wait on one or any of several flag bits to be accessed
// DOES NOT RETURN
void OPEX_sched_on_flag(BYTE *flagp, BYTE mask)
{
OPEX_TCB *f;
f = schedQ; // the current function
f->flags = flagp; // note the flag pointer and bitmask
f->flagmask = mask;
if (OPEX_sched_queue_remove(&schedQ, f) != NULL) // move from sched queue to wait queue
OPEX_sched_queue_insert_head(&flagwaitQ, f);
longjmp(OPEX_sched_jmp, 0); // go run next in queue
}
//////////////////////////////////////////////////////////////////
// generic remove from list (queue)
// returns passed arg f if f was in the queue, else NULL
// OK to call from ISR
OPEX_TCB *OPEX_sched_queue_remove(OPEX_TCB **qhead, OPEX_TCB *f)
{
OPEX_TCB *q, *qprev;
BYTE iflag;
qprev = NULL;
iflag = bit_is_set(SREG, 7); // in case ISR has called
cli(); // preclude ISR access
q = *qhead;
if (q != NULL) {
if (f == q)
*qhead = f->next; // remove from first in list
else {
while (q != NULL) { // find the passed OPEX_TCB in the queue
qprev = q;
q = q->next; // next in list
if (f == q) {
qprev->next = f->next; // unlink from mid-list
break;
}
}
}
}
if (iflag)
sei();
if (q == NULL)
return(NULL);
return(f);
}
#if 0 // not needed <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//////////////////////////////////////////////////////////////////
// generic append to list (queue)
// OK to call from ISR
void OPEX_sched_queue_append(OPEX_TCB **qhead, OPEX_TCB *f1)
{
OPEX_TCB *f;
BYTE iflag;
f1->next = NULL;
iflag = bit_is_set(SREG, 7);
cli(); // preclude ISR access
f = *qhead;
if (f == NULL) {
*qhead = f1; // head of list
}
else { // append
while (f->next != NULL) // find end of list
f = f->next;
f->next = f1; // append to queue
}
if (iflag)
sei();
}
#endif
//////////////////////////////////////////////////////////////////
// generic insert to list head
// OK to call from ISR
void OPEX_sched_queue_insert_head(OPEX_TCB **qhead, OPEX_TCB *f)
{
BYTE iflag;
iflag = bit_is_set(SREG, 7);
cli(); // preclude ISR access
if (*qhead == NULL) {
*qhead = f; // is head of list
f->next = NULL;
}
else { // insert to become head of list
f->next = *qhead;
*qhead = f;
}
if (iflag)
sei();
}
//////////////////////////////////////////////////////////////////
// Add to scheduler time-ordered queue.
// does not alter interrupt enable flag. caller must do so.
//
// DO NOT call from ISR - Runs with interrupts enabled.
//
void OPEX_sched_insert_ordered(OPEX_TCB *fnew)
{
OPEX_TCB *q, *qPrev;
if (schedQ == NULL) { // empty queue?
schedQ = fnew;
fnew->next = NULL;
}
else { // insert into queue in time order
q = schedQ; // head of queue
qPrev = NULL; // previous member of queue list
do { // scan the list
if (OPEX_compare_dt(&fnew->when, &q->when) < 0) { // insert here?
if (qPrev == NULL) { // inserting at head?
fnew->next = schedQ; // yes, make new 1st in queue
schedQ = fnew;
}
else { // no, inserting 2nd to nth in list
fnew->next = qPrev->next;
qPrev->next = fnew;
}
return; // all done
}
qPrev = q; // try next queue entry
q = q->next;
}
while (q != NULL);
qPrev->next = fnew; // append to tail of queue
fnew->next = NULL; // last in queue
}
}
//////////////////////////////////////////////////////////////////
// New scheduler item and place it in the queue
OPEX_TCB *OPEX_sched_new( void (*funcp)(OPEX_TCB *func), char *name)
{
OPEX_TCB *f;
f = (OPEX_TCB *) malloc(sizeof(OPEX_TCB)); // get memory
if (f != 0) {
f->name = ( (name != NULL) ? name : "<noName>" );
f->funcp = funcp; // address of function to run
f->pinfo = 0; // user data
f->state = 0;
f->malloc_list = NULL;
OPEX_now(&f->when); // when to run it (now)
OPEX_sched_insert_ordered(f); // insert by time
}
return(f); // 0 if malloc failed
}
//////////////////////////////////////////////////////////////////
// Kill a specified task.
// Returns NULL if task was found
// returns its arg if not found.
// does not return if task killed is the active task.
void OPEX_sched_kill(OPEX_TCB *f)
{
BYTE isActiveTask = 0;
if (f != NULL) {
isActiveTask = (f == schedQ);
if (OPEX_sched_queue_remove(&schedQ, f) != NULL) { // look in all queues
while (f->malloc_list) { // release all allocated memory for this task
OPEX_free(f, (char *)f->malloc_list);
}
free(f);
if (isActiveTask) { // if OPEX_sched_start has been called and this was running task
if (OPEX_sched_started) { // task killed itself same as OPEX_sched_quit()
sei(); // make sure
longjmp(OPEX_sched_jmp, 0); // go run next in queue
}
}
}
else
if (OPEX_sched_queue_remove(&flagwaitQ, f)) // assume it's in this list
free(f);
}
}
//////////////////////////////////////////////////////////////////
// Kill current task.
//
void OPEX_sched_quit(void)
{
OPEX_sched_kill(schedQ); // the active task
}
//////////////////////////////////////////////////
// Move a func from one queue to another
void OPEX_sched_make_pending(OPEX_TCB *f)
{
if (OPEX_sched_queue_remove(&schedQ, f) != NULL)
OPEX_sched_queue_insert_head(&pendingQ, f);
}
//////////////////////////////////////////////////
// schedule to run again, argument is absolute time
void OPEX_sched_schedAt(OPEX_TCB *f, BYTE year, BYTE month, BYTE day, BYTE hour, BYTE minute, BYTE second, BYTE tick)
{
OPEX_date_stuff(&f->when, year, month, day, hour, minute, second, tick);
OPEX_sched_queue_remove(&schedQ, f); // reinsert it in the ordered queue.
OPEX_sched_insert_ordered(f);
if (f == schedQ)
longjmp(OPEX_sched_jmp, 0); // go run next in queue
}
//////////////////////////////////////////////////
// schedule to run laster, argument is delta time
void OPEX_sched_resched(OPEX_TCB *f, BYTE days, BYTE hours, BYTE minutes, BYTE seconds, BYTE ticks)
{
OPEX_date_add(&f->when, days, hours, minutes, seconds, ticks);
if (OPEX_sched_queue_remove(&schedQ, f) != NULL) // reinsert it in the time-ordered queue.
OPEX_sched_insert_ordered(f);
if (f == schedQ)
longjmp(OPEX_sched_jmp, 0); // go run next in queue
}
///////////////////////////////////////////////////
// place the current time in the state informaion
void OPEX_sched_now(OPEX_TCB *f)
{
OPEX_now(&f->when); // copies date/time
}
///////////////////////////////////////////////////
///////////////////////////////////////////////////
///////////////////////////////////////////////////
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// real-time scheduler queue monitoring
// main() must call this, after creating some scheduled functions
void OPEX_sched_start(void)
{
OPEX_TCB *f;
BYTE n1;
if (OPEX_sched_started)
return; // hmmm, shouldn't have called (recusive)
// create a jump buffer state
// <<<<<<<<<<< OPEX_sched_qwit() comes here <<<<<<<<<<<<
avr_setjmp(); // used by OPEX_sched_quit() to come here
OPEX_sched_started = 1;
n1 = time.tick;
do { // while some task is waiting
// move pending queue, if any, to scheduler's time sorted queue
// interrupt routines can place things in the pending queue
while (pendingQ != NULL) { // move all from pending to sched's queue
cli(); // preclude access by ISR
f = pendingQ;
pendingQ = f->next; // remove from queue
sei();
OPEX_sched_now(f); // get time of day, f will run soonest
OPEX_sched_insert_ordered(f); // put in time-sorted queue
}
if (schedQ != NULL) { // any tasks waiting in this queue? (versus waitflagQ)
// run ALL functions which are due in this time tick
while (OPEX_compare_dt(&schedQ->when, &time) < 0) { // if it's time to run the head of queue
n1 = time.tick; // remember which tick we're in
// run the queue head func who's time has come due.
// Remove this from the queue.
// execute the function.
// if the function does a OPEX_sched_quit, it won't return from the call, below.
f = schedQ; // get head of queue
(f->funcp)(f); // call the function, passing its OPEX_TCB
// if the function calls OPEX_sched_quit, it won't return. see OPEX_sched_quit().
if (schedQ == f) { // if this func didn't get moved to another queue
schedQ = f->next; // remove from list
OPEX_sched_insert_ordered(f); // reinsert in queue sorted by time
}
}
// No more to run in this tick, so use up rest of tick's time
if (idle_funcp != NULL) {
while (n1 == time.tick) { // during rest of this tick (if any)
if (pendingQ != NULL) // ISR can add to queue, so stop running idle_func()
break;
else
(idle_funcp)(); // call a user routine (please return quickly)
}
}
}
cli();
n1 = ( (schedQ != NULL) || (pendingQ != NULL) || (flagwaitQ != NULL) );
sei();
}
while (n1); // do-while
OPEX_sched_started = 0; // no work to do, so return.
}
/////////////////////////////////////
// remember the pointer to a function as the idle process
// or zero if none.
void OPEX_sched_setIdleFunc(void(*func))
{
idle_funcp = func;
}
/////////////////////////////////////
// Inform scheduler that the date/time have changed
void OPEX_sched_time_changed(void)
{
OPEX_TCB *f = schedQ;
while (f != NULL) { // change all to run now
OPEX_sched_now(f);
f = f->next;
}
// items in the Pendinq queue will get new time when they come ready
}
void OPEX_free(OPEX_TCB *f, char *p)
{
size_t *p0, *p1, *prev;
if (p) {
p0 = (size_t *)(p - sizeof(size_t) - sizeof(size_t));
// find the block in the allocated list
p1 = (size_t *) f->malloc_list;
prev = (size_t *)&f->malloc_list;
while (*p1) {
if (p0 == (size_t *)*p1)
break;
else {
prev = p1;
p1 = (size_t *)*p1;
}
}
if (p1) {
*prev = *p1; // unlink from list
free(p1); // free the block
}
}
}
//////////////////////////////////////////////
// memory allocator
// gets specified block of memory and
// remembers the size and address in a linked list on the OPEX_TCB.
// so that OPEX_sched_free() can release the memory
char *OPEX_malloc(OPEX_TCB *f, int sz)
{
size_t *p;
unsigned int n;
if (sz != 0) {
n = sz + (2 * sizeof(size_t));
if ((p = (size_t *)malloc(sz + n)) != NULL) {
*p = (size_t)f->malloc_list; // place in linked list
f->malloc_list = p;
p[1] = sz;
return( (char *)&p[2] );
}
}
return(0);
}
///////////////////////////////////////////////
// Return OPEX_TCB for task in a certain list
OPEX_TCB *get_named1(OPEX_TCB *q, char *p)
{
OPEX_TCB *f;
if (p != NULL) {
f = q;
while (f)
if (strcmp(f->name, p) == 0)
return(f);
else
f = f->next;
}
return(NULL);
}
///////////////////////////////
// Return the OPEX_TCB of a named task - RAM pointer
OPEX_TCB *OPEX_sched_get_named(char *p)
{
OPEX_TCB *f;
if ((f = get_named1(schedQ, p)) != NULL)
return(f);
return (get_named1(flagwaitQ, p));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -