Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4027
Скачиваний: 1
328 Chapter 6 Large Microcontrollers
two types of overflow that can occur. The first is when an addition operation, which should always add fractions, causes an overflow from bit 30 to 31. This type of overflow is reversible because the arithmetic that caused the overflow cannot cause more than a 1-bit overflow error, and bits AM[34-31] are there to absorb this type of overflow. The contents of this register are signed, so that bit 35 of AM is the sign bit. The maximum value that can be placed in the 36-bit AM register is 0x7ffffffff. This value has a decimal value of 15.999969482. The minimum value is 0x800000000 correspond ing to –16. Whenever arithmetic involves the bits AM[34-31] the EV bit will be set, and the program will know that the bits in AM[35-31] are the signed integer part of the number. If successive operations cause the overflow to disappear, the EV bit will be reset.
Another situation is less tractable. Suppose an overflow occurs as a result of an arithmetic operation into the AM that causes bit 34 to overflow into bit 35. This type of error is not reversible as is the case above. When this overflow occurs, the MV bit is set to notify the pro gram of the result. An internal latch known as the sign latch SL will contain the value of A[35] after the overflow has occurred. There fore, SL is the complement of the sign-bit when the overflow occurred.
To communicate with the DSP portion of the MC68HC16, we have to use assembly language. In keeping with the basic rule to use C whenever possible, the approach to be taken will be to create C-call- able functions that access these important capabilities. What features should we include in these functions? Obviously, all possible func tions cannot be conceived. A set of functions that will embody the most important features of the DSP capabilities will be written.
Let us examine how this compiler transfers parameters to a func tion. When more than one parameter is passed, the parameters are pushed on the stack starting with the right-most parameter in the function argument list. The leftmost parameter is put into the D reg ister. If a single parameter is passed, it is placed in the D register prior to the function call. Within the function, the D and X register is saved on the stack, and the stack pointer is decremented by an amount needed to provide space for all of the function variables. At that point, the stack pointer is transferred into the X register. An example of this code is shown below. This little function receives three parameters and merely puts these values into three local variable locations.
Digital Signal Processor Operations |
329 |
void dot_product(char length, int* xdata, int* ydata)
{
int i,*xp, *yp; xp=xdata; yp=ydata; i=length;
}
Listing 6-8: Sample Parameter Handling Function
The compiled version of the above function is shown below. The first important instruction is the .even assembly directive that causes the code to follow to start on an even boundary. Code must be started at an even address. Note in the program above that three parameters are passed to the function. After the contents of the X and the D registers are saved, the stack pointer is decremented by 6 to provide space for the variables i, *xp, and *yp. The two pointer param eters require 16 bits each, and the character parameter needs only 8 bits. The program, in favor of greater speed, will store the variable i in a 16-bit location even though i is only 8 bits wide. At this time, the value contained in the stack pointer is transferred into the X reg ister. During this transfer, the value will be automatically incremented by two so that the contents of the X register will be pointed to the last value stored on the stack rather than to the next empty location on the stack as is found in the stack pointer.
1; Compilateur C pour MC68HC16 (COSMIC-France)
2.include “macro.h16”
3.list +
4.psect _text
5; 1 void dot_product(char length, int* xdata, int* ydata)
6; 2 {
7.even
8_dot_product:
9pshm x,d
10ais #-6
11tsx
12.set OFST=6
13; 3 int i,*xp, *yp;
14; 4
15; 5 xp=xdata;
16ldd OFST+8,x
17std OFST-4,x
18; 6 yp=ydata;
19ldd OFST+10,x
330 Chapter 6 Large Microcontrollers
20 std OFST-6,x
21 ; 7 i=length;
22 ldab OFST+3,x
23clra
24std OFST-2,x
25; 8
26; 9 }
27ldx 6,x
28ais #10
29rts
30.public _dot_product
31.even
32.psect _data
33.even
34.psect _bss
35.even
36.end
Listing 6-9: Compiled Version Of Parameter-Handling Function
The diagram shown in Figure 6-4 will help you visualize what is happening here. On the right side of this diagram you will find the location at which the stack pointer is pointed at various times during the function call and its execution. On the left side of the diagram, you will see the locations pointed to by the X register. An offset named OFST is established by the program. This offset will have a value equal to the space emptied on the stack with the ais instruc tion: in other words, in this case OFST will be 6. In every case from this point forward in the program, variable and parameter accesses will be indexed relative to the X register, and the total offset from the X register will be a value OFST+k where k is a positive or negative value that corresponds to the address of the parameter being accessed. For example, the instruction
ldd OFST+8,x
will load the value at x+OFST+8 which you can see in Figure 6.4 is the value *xdata. This value is stored at the location x+OFST-4 by the instruction
std OFST-4,x
which is the location where xp is stored on the stack. Remember that each word on the stack is two bytes, so that all of the offsets and address will be even numbers.
Digital Signal Processor Operations |
331 |
The code in lines 16 through 20 above saves the values of xdata and ydata in xp and yp respectively. The operations shown in lines 21 through 24 save the 8-bit value found in the B register to the 16-bit location at x+OFST-2. To be certain that the value is not corrupted by some garbage value in the A register, the clra instruc tion is inserted prior to the time that the value is saved.
Stack Contents
X after tsx |
*yp |
||
*xp |
|||
X + OFST |
length |
||
X |
|||
X at rts |
D |
||
CCR |
|||
PC |
|||
*xdata |
|||
*ydata |
|||
SP after ais #-6
SP after pushm x, d SP after function call SP before function call
Figure 6-4: Stack Contents During Function Operation
After the closing brace of the function in the above listing, two important operations take place. First, the contents contained in the X register on entry to the function are restored by the instruction
ldx 6,x
and the stack pointer content is restored to the value it contained when the function was entered. At this point in the program, an rts instruction will return the program control to the instruction follow ing the jsr or bsr instruction used to enter the function code originally.
When the function return is executed, the registers D, E, Y, Z, and CCR are all undefined. A 16-bit return from the function will be returned in the D register, and a 32-bit return will be contained in the E and the D register. The least significant portion of the return is in the D register.
332 Chapter 6 Large Microcontrollers
What is a dot_product( )? In vector algebra, a dot product, or a scalar product, operates on all of the members of two vectors and returns a single scalar result. This value is the magnitude of the projection of one vector on the other. The familiar arithmetic form for a dot product is
n–1
c = ∑ ak bk
0
Note that all corresponding members of the two vectors are mul tiplied and summed. The result is a single number. Another important calculation needed to be accomplished by a DSP is called convolu tion. A convolution is the time domain operation of a filter. Most of the time, a designer thinks of a filter as operating on the different frequencies of the signal being processed. In the frequency domain, at every frequency the filter has a gain which is complex. “Complex” in this case means the gain has two dimensions that can be thought of as magnitude and phase. The signal also has a similar two-dimensional description in frequency. At each frequency, the magnitude of the filter gain multiplies the magnitude portion of the signal, and thephase of the filter gain adds to the corresponding phase of the signal. There are easy ways to treat this operation in the frequency domain. In fact, the design of most filters takes place in the frequency domain.
However, the frequency domain is an artifact that we can never really get our hands on. In reality, the signals we must deal with are varying voltages or currents. These varying signals can be continu ous, or when converted to a tractable form for operation in a computer, they are a series of samples. Let us call them xk. Here x is the value of the signal at sample points k. Now k might be thought of as re lated to time, and in fact different values of k do correspond to samples taken at different times. Usually, k corresponds to samples taken pe riodically at carefully spaced, equal intervals.
A filter in the time domain has what is called a weighting func tion. The weighting function is indeed the Fourier transform of the complex frequency response of the filter. In the continuous domain, there is a mathematical trick that allows the weighting function to be shown. A function called a Dirac Delta function is defined as a func tion that is 0 everywhere except at one point. The integral across this point is one. Such a function really does not exist. However, if such
Digital Signal Processor Operations |
333 |
a function were delivered into the input of a filter, the output of the filter would be the filter weighting function.
As we move to the digital realm, the Dirac Delta function is re placed by the Kroniker Delta function. This simple function, δk , is 0 for all values of k except for k = 0 where its value is 1. If this func tion is sent through a digital filter, the output observed is the weighting function of the filter.
In both cases, analog and digital, the frequency response of the filter is the Fourier transform of the filter weighting function. This duality between the frequency response and the time domain response makes it possible to design filters to accomplish what is really de sired. Usually, the filter specification is best established in the frequency domain. The designer knows what frequencies are to be passed or rejected by the filter. There has been a long history in the field of passive network synthesis devoted to the “approximation problem.” How does one specify a filter to meet accurately a desired frequency response? This problem has led to many sophisticated approaches to the specification by mathematics of a frequency re sponse to meet the system need. More important, these frequency responses have a nature that can be realized by a finite collection of passive electrical components—resistors, capacitors, and inductors. In other words, these frequency responses are realizable.
A series of mathematical transformations exist that can be used to transform frequency responses directly to filter weighting func tions. We will not go into these transformations here, but will refer you to Elliott for practical means to specify the weighting functions for digital filters.7 A more general text on this subject is by Antoniou.8 The time domain response calculation is called a convolution. If there is a signal xk applied to a filter with a weighting function hk there will be an output from the filter at each time sample, and this output will be called yk . The relations between these parameters are given by the equation
n–1
yk = ∑ xk –i hi i=0
7Elliott, Douglas F., Handbook of Digital Signal Processing and Applications: Academic Press Inc. 1987
8Antoniou, Andreas, Digital Filter Analysis and Design: McGraw Hill, 1979
334 Chapter 6 Large Microcontrollers
The convolution is little more than a series of dot products, one dot product for each output sample. Therefore, if you have a function that will calculate a dot product, it can also calculate a convolution.
An item of consequence is the use of modular arithmetic in calcu lation of the data addresses. Often times, it is desirable to traverse an array and return to its beginning automatically when the end is reached. Modular arithmetic allows this type of operation. When working in combination with an unusual step value, modular arithmetic will per mit the collection of coefficients from a rectangular array placed in linear memory space. Here the step value refers to one of the numbers associated with the mac or the rmac instruction. These two values are called xo and yo. This value is used to increment the address of the corresponding register whenever data is loaded into either the H or the I register. The location of the array in memory should be placed at special location in memory. This location is discussed below. The ef fective address for the next value to be placed in the X after the value is incremented by the xo register is given by
IX = (IX)&~XMASK | ((IX)+xo)&XMASK
Note that XMASK here is an 8-bit mask that defines the length of the circular array. The array length must be a power of two less than or equal to 256. The value placed in XMASK is one less than the array length. When the contents of IX, or (IX), is anded with the comple ment of the sign extended value of XMASK, the value that is left is the starting address of the array. The second term above causes the con tents in IX to be incremented. As the value in IX is replaced by ((IX)+ xo)&XMASK after each multiply and accumulate opera tion. Since XMASK must contain a number that is one less than a power of two raised to the n: its least significant n bits are 1. The value placed in IX is the address with the n least significant bits masked off. When these two values are ORed together, an address is created that will range from the beginning to the end of the array and then back to the beginning in steps of xo. The requirements to make this scheme work are:
1.The array length must be a power of two less than or equal to 256.
2.The array must begin on a specific address. This address is any value where the least n bits are zero. Here n is determined by
Digital Signal Processor Operations |
335 |
n = log 2 (array length)
or
array length = 2 n
3.The value in xo must be equal to the step between adjacent data samples in the array.
This scheme can be used to move, in a modular manner, around multiple-dimensional arrays as well as one-dimensional arrays. Let us now modify the earlier code to show how a circular buffer can be used to advantage.
The above compilations use the default memory model for the Cosmic C compiler for the MC68HC16. This computer has a memory addressing space of 20 bits. In the design of the part, the basic address registers were all made 16 bits wide, and the additional bits required to address the total space were placed in extension registers. Therefore, for each of the addressing registers, the stack pointer, the program counter, and the index registers X, Y and Z, there is a 4-bit extension register. These registers are called SK, PK, XK, YK, and ZK respec tively. As mentioned earlier, several of these extension registers are initialized upon reset, and the remainder must be initialized prior to the execution of the main program. Usually, when a calculation alters a value in an extension register, this change takes place seamlessly. It is typically not necessary to worry about the contents of the extension registers. Recall in the code for the initialization routine listed above, crts.s, that the values for XK and ZK were set to 0 and the value placed in YK was 0xf. The reason for this choice is that the compiler automatically uses the Y register as an offset when calculating the ad dresses listed in the various header files. Since all of these registers are in the highest memory page, the YK value of 0xf is appropriate.
The default memory model is called the compact model, and the code is compiled in the compact model so that all code, data, and stack memory space is contained within one 65 kilobyte (K) memory bank. Therefore, the initialized values of the extension registers need never change. On the other hand, it is sometimes necessary to change the values in these registers, and it is desirable to have the compiler take care of this bookkeeping when needed. All of the additional memory models will provide this tracking of the extension registers. These models are: small, one 65 K bank for code and one 65 K bank for data
336 Chapter 6 Large Microcontrollers
and stack; program, multiple 65 K banks for code and one 65 K bank for data and stack; data, one 65 K bank for code and multiple 65K banks for data and stack; and finally far, multiple 65 K banks for code, data, and stack. With each of these memory models, it is necessary to keep track and often change the contents of the extension registers. As a result, with these models, it is necessary to alter slightly the stacking sequence. Such a sequence is shown in Figure 6-5. The function call that will cause the stack to be arranged as is shown in the figure below has four arguments. From left to right these arguments are xlen, *xdata, ylen, and *ydata. The lengths are simple character values, and the pointers in this case must be 20-bit values. If a function call to this routine is compiled with the small memory model, or any other model with the exception of the compact model, the stacking will include the extension registers as shown below.
Stack Contents
X after tsx |
XK |
||
X |
|||
SP at rts |
D |
||
CCR |
|||
PC |
|||
*xdatak |
|||
*xdata |
|||
ylen |
|||
*ydatak |
|||
*ydata |
|||
SP after XK is saved SP after pushm x, d SP after function call
SP before function call
Figure 6-5: Stacking Sequence That Passes Extension Registers
The B register, which is the righthand side of the D register, will contain the left-most parameter when the function is called. There fore, the parameter xlen will be found in the least significant bits of the location labeled D in the above stack outline. The routine shown below will make use of both the X and the Y registers. The compiler allows for all registers with the exception of the X register to be