📄 chapter 1 program structure -- valvano.htm
字号:
<P><CODE>short add(short x, short y){ return (x+y);}<BR>short add(x,y)short x;
short y;{ return (x+y);}<BR>short add(x,y)short x,y;{ return
(x+y);}</CODE></P></DIR>
<P>The parentheses are required even when there are no arguments. When there are
no parameters a <CODE>void</CODE> or nothing can be specified. The following
four statements are equivalent:</P>
<DIR>
<P><CODE>void
OpenSCI(void){BAUD=0x30;SCCR2=0x0C;}<BR>OpenSCI(void){BAUD=0x30;SCCR2=0x0C;}<BR>void
OpenSCI(){BAUD=0x30;SCCR2=0x0C;}<BR>OpenSCI(){BAUD=0x30;SCCR2=0x0C;}</CODE></P></DIR>
<P>I prefer to include the <CODE>void</CODE> because it is a positive statement
that there are no parameters. For more information on functions see <A
href="http://www.ece.utexas.edu/~valvano/embed/chap10/chap10.htm">Chapter
10</A>. </P>
<P>The body of a function consists of a statement that performs the work.
Normally the body is a compound statement between a {} pair. If the function has
a return parameter, then all exit points must specify what to return. In the
following median filter function shown in Listing 1-4, there are six possible
exit paths that all specify a return parameter.</P>
<P>The programs created by ICC11 and ICC12 actually begin execution at a place
called <B>_start</B>. After a power on or hardware reset, the embedded system
will initialize the stack, initialize the heap, and clear all RAM-based global
variables. After this brief initialization sequence the function named
<B>main()</B> is called. Consequently, there must be a<FONT face=Monaco>
</FONT><B>main()</B> function somewhere in the program. If you are curious about
what really happens, look in the assembly file crt11.s or crt12.s. For programs
not in an embedded environment (e.g., running on your PC) a return from
<B>main()</B> transfers control back to the operating system. As we saw earlier,
software for an embedded system usually does not quit. Software systems
developed with Hiware also perform initialization before calling
<B>main()</B>.</P>
<P><B><I><FONT face=Helvetica,Arial><A name=COMPOUND></A>Compound
Statements</FONT></I></B></P>
<P>A <I>compound statement</I> (or <I>block</I>) is a sequence of statements,
enclosed by braces, that stands in place of a single statement. Simple and
compound statements are completely interchangeable as far as the syntax of the C
language is concerned. Therefore, the statements that comprise a compound
statement may themselves be compound; that is, blocks can be nested. Thus, it is
legal to write</P>
<DIR>
<P><CODE>// 3 wide 16 bit signed median filter<BR>short median(short n1,short
n2,short
n3){<BR> if(n1>n2){<BR> if(n2>n3)<BR> return(n2); //
n1>n2,n2>n3 n1>n2>n3<BR> else{<BR> if(n1>n3)<BR> return(n3); //
n1>n2,n3>n2,n1>n3
n1>n3>n2<BR> else<BR> return(n1); //
n1>n2,n3>n2,n3>n1
n3>n1>n2<BR> }<BR> }<BR> else{<BR> if(n3>n2)<BR> return(n2); //
n2>n1,n3>n2
n3>n2>n1<BR> else{<BR> if(n1>n3)<BR> return(n3); //
n2>n1,n2>n3,n1>n3
n2>n1>n3<BR> else<BR> return(n1); //
n2>n1,n2>n3,n3>n1
n2>n3>n1<BR> }<BR> }<BR>}</CODE></P></DIR>
<P><I>Listing 1-9: Example of nested compound statements.</I></P>
<P>Although C is a free-field language, notice how the indenting has been added
to the above example. The purpose of this indenting is to make the program
easier to read. On the other hand since C is a free-field language, the
following two statements are quite different</P>
<DIR>
<P><CODE>if(n1>100) n2=100; n3=0;<BR>if(n1>100) {n2=100;
n3=0;}</CODE></P></DIR>
<P>In both cases <CODE>n2=100; </CODE>is executed if <CODE>n1>100</CODE>. In
the first case the statement <CODE>n3=0;</CODE> is always executed, while in the
second case <CODE>n3=0;</CODE> is executed only if <CODE>n1>100</CODE>.</P>
<P><B><I><FONT face=Helvetica,Arial><A name=GVARIABLES></A>Global
Variables</FONT></I></B></P>
<P>Variables declared outside of a function, like <CODE>Count</CODE> in the
following example, are properly called <I>external</I> variables because they
are defined outside of any function. While this is the standard term for these
variables, it is confusing because there is another class of external variable,
one that exists in a separately compiled source file. In this document we will
refer to variables in the present source file as <I>globals</I>, and we will
refer to variables defined in another file as externals. </P>
<P>There are two reasons to employ global variables. The first reason is data
permanence. The other reason is information sharing. Normally we pass
information from one module to another explicitly using input and output
parameters, but there are applications like interrupt programming where this
method is unavailable. For these situations, one module can store data into a
global while another module can view it. For more information on accessing
shared globals see chapters 4 and 5 of <U>Embedded Microcomputer Systems: Real
Time Interfacing</U> by Jonathan W. Valvano, Brooks/Cole Publishing Co.,
1999.</P>
<P>In the following example, we wish to maintain a counter of the number of
times <CODE>OutSCI</CODE> is called. This data must exist for the entire life of
the program. This example also illustrates that with an embedded system it is
important to initialize RAM-based globals at run time. Some C compilers like
ICC11 and ICC12 will automatically initialize globals to zero at startup. </P>
<DIR>
<P><CODE>unsigned short Count; /* number of characters
transmitted*/<BR>void OpenSCI(void) {
<BR> Count=0; /* initialize global
counter */<BR> BAUD=0x30; /* 9600 baud
*/<BR> SCCR2=0x0C;} /* enable SCI, no interrupts */<BR>#define
TDRE 0x80<BR>void OutSCI(unsigned char
Data){<BR> Count=Count+1; /*
incremented each time */ <BR> while ((SCSR & TDRE) == 0); /* Wait
for TDRE to be set */ <BR> SCDR=Data; }
/*
then output */</CODE></P></DIR>
<P><I>Listing 1-10: A global variable contains permanent information</I></P>
<P> </P>
<P>Although the following two examples are equivalent, I like the second case
because its operation is more self-evident. In both cases the global is
allocated in RAM, and initialized at the start of the program to 1.</P>
<DIR>
<P><CODE>short Flag=1;<BR>void main(void) { <BR>/* main body goes here */<BR>}
</CODE></P></DIR>
<P><I>Listing 1-11: A global variable initialized at run time by the
compiler</I></P>
<DIR>
<P><CODE>short Flag;<BR>void main(void) { Flag=1;<BR>/* main body goes here
*/<BR>} </CODE></P></DIR>
<P><I>Listing 1-12: A global variable initialized at run time by the
compiler</I></P>
<P>From a programmer's point of view, we usually treat the I/O ports in the same
category as global variables because they exist permanently and support shared
access.</P>
<P><B><I><FONT face=Helvetica,Arial><A name=LVARIABLES></A>Local
Variables</FONT></I></B></P>
<P>Local variables are very important in C programming. They contain temporary
information that is accessible only within a narrow scope. We can define local
variables at the start of a compound statement. We call these <I>local
variables</I> since they are known only to the block in which they appear, and
to subordinate blocks. The following statement adjusts<CODE> x </CODE>and
<CODE>y </CODE>such that <CODE>x </CODE>contains the smaller number and <CODE>y
</CODE>contains the larger one. If a swap is required then the local variable
<CODE>z </CODE>is used.</P>
<DIR>
<P><CODE>if(x>y){ short z; /* create a temporary
variable */ <BR> z=x; x=y; y=z; /* swap
x and y
*/<BR>} /*
then destroy z */</CODE></P></DIR>
<P>Notice that the local variable z is declared within the compound statement.
Unlike globals, which are said to be <I>static</I>, locals are created
dynamically when their block is entered, and they cease to exist when control
leaves the block. Furthermore, local names supersede the names of globals and
other locals declared at higher levels of nesting. Therefore, locals may be used
freely without regard to the names of other variables. Although two global
variables can not use the same name, a local variable of one block can use the
same name as a local variable in another block. Programming errors and confusion
can be avoided by understanding these conventions.</P>
<P><B><I><FONT face=Helvetica,Arial><A name=SOURCE></A>Source
Files</FONT></I></B></P>
<P>Our programs may consist of source code located in more than one file. The
simplest method of combining the parts together is to use the #include
preprocessor directive. Another method is to compile the source files
separately, then combine the separate object files as the program is being
linked with library modules. The linker/library method should be used when the
programs are large, and only small pieces are changed at a time. On the other
hand, most embedded system applications are small enough to use the simple
method. In this way we will compile the entire system whenever changes are made.
Remember that a function or variable must be defined or declared before it can
be used. The following example is one method of dividing our simple example into
multiple files.</P>
<DIR>
<P><CODE>/* ****file HC11.H ************ */<BR>#define PORTC *(unsigned char
volatile *)(0x1003)<BR>#define DDRC *(unsigned char volatile
*)(0x1007)<BR>#define BAUD *(unsigned char volatile *)(0x102B)<BR>#define SCCR2
*(unsigned char volatile *)(0x102D)<BR>#define SCSR *(unsigned char volatile
*)(0x102E)<BR>#define SCDR *(unsigned char volatile *)(0x102F)</CODE></P></DIR>
<P><I>Listing 1-13: Header file for 6811 I/O ports</I></P>
<DIR>
<P><CODE>/* ****file SCI11.H ************ */<BR>void OpenSCI(void);<BR>void
OutSCI(unsigned char);</CODE></P></DIR>
<P><I>Listing 1-14: Header file for the SCI interface</I></P>
<DIR>
<P><CODE>/* ****file SCI11.C ************ */<BR>void OpenSCI(void) {
<BR> BAUD=0x30; /* 9600 baud
*/<BR> SCCR2=0x0C;} /* enable SCI, no interrupts */<BR>/* Data
is 8 bit value to send out serial port */<BR>#define TDRE 0x80<BR>void
OutSCI(unsigned char Data){<BR> while ((SCSR & TDRE) == 0); /*
Wait for TDRE to be set */ <BR> SCDR=Data; } /* then output
*/</CODE></P></DIR>
<P><I>Listing 1-15: Implementation file for the SCI interface</I></P>
<DIR>
<P><CODE>/* ****file VECTOR.C ************ */<BR></CODE><FONT
face="Courier,Courier New" size=2>extern void _start(); /* entry point in
crt11.s */</FONT><CODE><BR></CODE><FONT face="Courier,Courier New"
size=2>#pragma abs_address:0xfffe</FONT><CODE><BR></CODE><FONT
face="Courier,Courier New" size=2>void (*reset_vector[])()
={_start};</FONT><CODE><BR></CODE><FONT face="Courier,Courier New"
size=2>#pragma end_abs_address</FONT></P></DIR>
<P><I>Listing 1-16: Reset vector</I></P>
<DIR>
<P><CODE>/* ****file MY.C ************ */<BR>/* Translates parallel input data
to serial outputs */<BR>#include "HC11.H"<BR>#include "SCI11.H"<BR>void
main(void){ unsigned char Info;<BR> OpenSCI(); /* turn on SCI
serial port */<BR> DDRC=0x00; /* specify Port C as input
*/<BR> while(1){<BR> Info=PORTC;
/* input 8 bits from parallel port C
*/<BR> OutSCI(Info);}} /* output 8 bits to serial
port */<BR>#include "SCI11.C"<BR>#include "VECTOR.C"</CODE></P></DIR>
<P><I>Listing 1-17: Main program file for this system</I></P>
<P>With Hiware, we do not need the VECTOR.C file or the line <CODE>#include
"VECTOR.C"</CODE>. This division is a clearly a matter of style. I make the
following general statement about good programming style. </P>
<ADDRESS>"If the software is easy to understand, debug, and change, then it is
written with good style"</ADDRESS>
<P>While the main focus of this document is on C syntax, it would be improper to
neglect all style issues. This system was divided using the following
principles:</P>
<DIR>
<P>Define the I/O ports in a HC11.H or HC12.H header file<FONT
face=Monaco><BR></FONT>For each module place the user-callable prototypes in a
*.H header file<FONT face=Monaco><BR></FONT>For each module place the
implementations in a *.C program file<FONT face=Monaco><BR></FONT>In the main
program file, include the header files first<FONT face=Monaco><BR></FONT>In the
main program file, include the implementation files last</P></DIR>
<P>Breaking a software system into files has a lot of advantages. The first
reason is code reuse. Consider the code in this example. If a SCI output
function is needed in another application, then it would be a simple matter to
reuse the SCI11.H and SCI11.C files. The next advantage is c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -