⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 opex.c

📁 学完单片机就要使用实时系统在单片机上运行
💻 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 + -