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

📄 chapter3.htm

📁 嵌入式软件开发.rar
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<head>
<title>Chapter 3   Some Useful Classes</title>
</head>
<body>
<h1>Some Useful Classes</h1>
<p>
	One fact discourages many experienced programmers of microcontrollers when 
they examine Object Oriented Programming.  Most texts and articles on OOP are directed 
toward data structures that are converted to objects.  Linked lists, tree structures, 
containers, even complex arithmetic are data structure items that have little use in the 
world of the  microcontroller.  Unfortunately, OOP can have great impact on the design 
and programming of these embedded controllers, and most programmers never allow 
these advantages to be employed in their programs.  In these cases, the class probably best 
represents a peripheral or perhaps a control function.  It should be the goal of the 
programmer to provide a clean, reusable, easy to understand and  maintain program that 
accurately meets the need of the program specification.  We must consider the good and 
bad of OOP and other techniques when writing our code and then decide the 
programming approach that best suites each individual application.  In this chapter, we 
will look at several potential classes that can be used with  microcontrollers.  Most of 
these items will be used to either interface with on-board peripherals or provide exclusive 
access to a peripheral from each of several tasks running in the machine.  
<p>	Several tasks?  As we work through the remainder of this text, the concept of 
multiple tasks will be explored  and developed.  To begin, let us simply admit that a single 
controller can process several independent programs simultaneously or at least appear to 
execute these various programs at the same time.  There will be no attempt in this text to 
develop a complete multitasking operating system.  However, we shall use multiple tasks 
that must work cooperatively to accomplish the goal of the program.  Therefore, some 
concepts that belong to multitasking operating systems will show up in our programs.  As 
an aside, if the program being developed requires many of the features found in a standard 
multitasking operating system, it is probably the best choice to purchase one of the several 
available operating systems to use on the machine.  These programs are complicated, 
large, and very specialized and they have been developed by specialists with vast 
experience in the field.  It makes little sense to redevelop all of this code when it has 
already been done for you.  To begin with though, let us look at some features that will be 
needed by the basic system and see how  these features can be used as classes from which 
we can create useful objects.
<p>	In the Chapter 2, we saw how a class can be derived that will support <a href="chapter2.htm#polymorphism"> inheritance 
and virtual functions</a>.  There is a cost to our making these features available to the class.  
The earilier version that allowed several instantiations of each object had only the 
attributes contained within  the class structure.  The more general class had to contain not 
only all of the class attributes, but each object also contained pointers to the methods 
pertaining to the class.  When instances of objects are created, memory space for the 
object location is taken from the program heap.  The heap is an  undedicated  block of 
RAM accessable by the program.  In a case where there is  an operating system, the  
allocation of heap space  is made by the operating system, not the program.  Every 
instantiation of an object requires RAM, and usually RAM is a limited resource on any 
microcontroller.  Therefore, when using objects, the programmer will usually attempt to 
minimize the RAM usage.
<p>	On the other hand, the objects that use the least RAM can provide neither 
inheritance nor virtual functions.  Another  important feature is lost.  Recall in the  later 
implementations of  our class methods, it was possible to make the methods static, thus 
limiting them to file scope.  The method names could be used elsewhere in the programs 
with no danger of multiple defined function names at link time.  This latter problem can be 
easily overcome by using method names that are unlikely to be found elsewhere in any 
program.  The class  name should be a part of the name of any method  attached to the 
class. 

<h2><a name="the_semaphore">The Semaphore</a></h2>
<p> 
	A class that is very useful is the  semaphore.  A semaphore is  a flag, or token, that 
can be made a part of any resource.  When a process or a task needs exclusive access to a 
resource, it must attach the resource semaphore before any access to the resource is 
permitted.  The semaphore is then assigned to the resource.  As long as the resource is 
attached to the initial process, no other process can access the resource.  When finished 
with the resource, the semaphore is released and another process can use the resource.
<p>	Listed below is a header file for a semaphore.  The class structure contains only 
one member, namely sem.  There are four methods.  Each method has the word 
semaphore in its name.  There should  be little danger of  multiple definitions with these 
names.  At this time, there is no intent to use semaphore as a base class.  Therefore, the 
use of global functions for the semaphore methods is indicated.  This approach will use the 
least memory space for each instance of a semaphore.  The function calls will all be direct 
to the functions rather than through pointers contained in the object.  Therefore, the actual 
code will be simpler and smaller than would be obtained if we had used the more 
complicated form of the class.
<pre><code>
 
	#ifndef SEMAPHORE_H
	#define SEMAPHORE_H

	#include "defines.h"   

	typedef struct semaphor
	{
	    Boolean sem; 
	}Semaphore;

	Boolean attach_semaphore(Semaphore *);
	void release_semaphore(struct semaphor *);
	Boolean semaphore_status(struct semaphor*);
	void wait_for_semaphore(struct semaphor*);

	Semaphore* semaphore_(void);
	void semaphore__(Semaphore *);
	#endif
 
</pre></code>
<p>	The implementation file is shown below.  The first method listed is 
attach_semaphore( ).  There are a couple of pieces of business that must be taken care of 
in this method that  has no counterpart in the language C or C++.  Therefore, we will use 
assembly language inserts in the C program.  The first line of code is actually two 
assembly instructions.  The first pushes the content of the condition code register on the  
stack for later access.  It then performs the logical OR of the condition code register 
contents with a value 0x00e0.  Those familiar with the MC68HC16 will recognize that this 
operation has disabled the interrupts of the microcontroller.  This approach avoids a 
potential problem that could allow two processes attached to the same semaphore in error.   
<pre><code>
 
	#include "semaphor.h"

	Boolean attach_semaphore(Semaphore *s)
	{
	    _asm("pshm ccr\norp #00e0H\n"); /*disable the interrupts */
	    if(s-&gtsem==FALSE)
	    {
	        s-&gtsem=TRUE;
	        _asm("pulm ccr\n");  /* enanble interrupts */
	        return TRUE;
	    }
	    else
	    {    
	        _asm("pulm ccr\n");  /* enanble interrupts */
	        return FALSE;
	    }
	}

	void release_semaphore(Semaphore *s)
	{
	    s-&gtsem=FALSE;
	}

	Boolean semaphore_status(Semaphore *s)
	{
	    return s-&gtsem;
	}

	void wait_for_semaphore(Semaphore *s)
	{
	    while(semaphore_status(s));
	    attach_semaphore(s);
	}

	Semaphore *semaphore_(void)
	{
	    Semaphore *this

	    if(!(this=(Semaphore*)malloc(sizeof(Semaphore))))    
			error_handler();
	    this-&gtsem=FALSE; /* default is unattached */
	    return this;
	}

	void semaphore__(Semaphore *this)
	{
	    free( this);
	}
 
</pre></code>
<p>	After the interrupts are disabled, the content of s-&gtsem is examined, and if it is 
FALSE it is set to TRUE.  It is at this point in the code that a problem could have  
occured.  Suppose that the program is executing the line of code
<pre><code>
  
	if(s-&gtsem==FALSE)
 
</pre></code>
<p>
The compiled version of this code is shown below.  The two instructions on lines 18 and 
19 load the contents of  s-&gtsem  into the d register and the conditional branch is then taken 
depending on the value of  s-&gtsem .  Assume that it is FALSE.  Suppose that  during the 
execution  of the code in line 19, an interrupt would occur.  Further suppose that as a 
result of that interrupt, another process is launched and it needs the use of the resource 
protected by the semaphore being attached in the code being examined.  The second 
process would access s-&gtsem and find it to be FALSE because the  first process had not 
yet marked the semaphore as TRUE.  The second process would then claim the 
semaphore and the resource as its own and proceed.  As time passes and program 
executes, the first process would eventually regain control of the program and would start 
execution where it left off.  At that point, process 1 would discover that s-&gtsem is 
FALSE, the value saved just prior to the interrupt, and would claim the semaphore as its 
own and proceed forward to disaster because two process would each have exclusive 
access to the same resource.  
<pre><code>
 
	17                ;    6      if(s-&gtsem==FALSE)
	18    000A  CD02  	ldy	OFST+2,x
	19    000C  9500  	ldd	0,y
	20    000E  B606  	bne	L1
 
</pre></code><p>
	The line of code that disables all of the system interrupts will prevent the 
simultaneous access of more than one process to an exclusive resource.  The interrupts are 
disables while lines 18 and 19 above are executed so, the problem described above cannot 
happen.  Immediately following the test and set of the semaphore, the interrupts are 
enabled so that normal processing can continue.  
<p>	The remainder of the semaphore methods are straight forward.  The semaphore is 
available whenever s-&gtsem is FALSE.  release_semaphore( ) simply sets s-&gtsem to 
FALSE so that the semaphore can be made available to another process.  The method 
semaphore_status( ) returns the Boolean value of s-&gtsem.  Finally wait_for_semaphore( ) 
holds the calling process in the wait_for_semaphore( ) method until the semaphore 
becomes available.  When the semaphore becomes available, it is immediately given to the 
waiting process and control is returned to that process.   
<p>	The <a href="chapter1.htm#constructor">constructor</a> allocates memory space for the semaphore and sets its value to 
FALSE.  The destructor destroys the semaphore.  
<p>	There are other types of semaphores called counting sempahores which can be 
readily implemented by the above means.  We will use the above simple semaphore in 
several instances later in this text.  The semaphore will be used whenever it is necessary to 
grant exclusive access of a resource to one of several competing processes, each of which, 
requires the resource.


<h2><a name="linked_list">Linked List</a></h2>
<p>
	Many of the object oriented programming texts stress creation of objects that are 
in reality data structures.  One seen often is the code associated with a list of some sort.  
Lists, queues, stacks, singly linked list, doubly linked list, circular lists, etc. are all 
derivatives of  common list operations.  These lists are easily implemented as classes and 
lend themselves to object oriented programming.
<p>	We will start with a very practical list operation.  This class will be a doubly linked 
list, and it will make use of some advanced programming techniques to simplify the code 
being generated.  Such a list will contain three separate classes.  The first will be a class 
that when instantiated will be an object to be stored in the list. The first class is called an 
object.  The second class implements a simple node that contains a pointer to the object 
being stored, and two pointers needed to implement the links.  This class is named <a href="chapter3.htm#link_class">link</a>.  
Finally the <a href="chapter3.htm#linklist_class">linklist class</a> contains the collections of links and the code necessary to add, 
delete, and exercise the objects linked together in the linked list.  

<h3><a name="object_class">Object Class</a></h3>

 <p>	It is important that flexibility be maintained when creating different types of 
classes.  It is possible, for example, that one could specify the type of object that is to be 
stored in our linked list in some detail.  Such an approach would lock the linked list into 
the use of the specified object only, and modifications of the content of the list would 
force serious reprogramming.  Such an approach is quite inflexible and should be avoided 
whenever possible. 
<p>	The single most effective mechanism for maintaining flexibility in the program is to 
use pointers to objects that are being used in all outside objects rather than the objects 
themselves.  A pointer is simply a single entry in an object while an object could be many 
bytes of code.  Therefore, additional instantiations of the object will be smaller because the 
object need not contain a reproduction of the object but rather a pointer to it is sufficient.  
You will find pointers used extensively in the following sections.  
<p>	The base object is a thing to be put into the list.  That object can be anything, and 
we will see instances later where the base object can be a delay in time or perhaps a 
representation of a task that must execute sometime.  It is not necessary that the objects 
stored on the list be of the same type.  When all of the objects are of the same type, there 
will probably be some common operation that each object will require.  These common 
operations should be methods for the base object.  Any attempt to associate the common 
operations with the link or the link list objects complicates the code and makes it 
extremely difficult to sort out any errors.  
<p>	For this example, we are going to create a link list of objects that merely create a 
delay.  At the end of the specified delay, the computer bell operation will be executed.  
Therefore, you can hear how the program is progressing.  Such an object needs one 
attribute, the amount of time for the delay, and one method that will cause the delay to be 
executed.  The following header file defines such an object.  The attribute is named count, 
and the method is named exercise.  Note that the method is defined in the structure 
content definition statement as a pointer to a function.    
<pre><code>
	#ifndef OBJECT_H
	#define OBJECT_H

	#include "defines.h"

	#define OBJECT	 \
		void (*exercise)(struct object*);	\
		long count;

	typedef struct object
	{
		OBJECT
	} Object;


	Object *object_(long count);
	void object__(Object *);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -