Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4048
Скачиваний: 1
Digital Signal Processor Operations |
337 |
altered by any function call. Therefore, it is necessary to save the contents of the X register prior to altering this register.
; int circular_conv(char xlen, int* xdata, char ylen, int* ydata)
.even
.set xlen=5
.set xk=11
.set xdata=12
.set ylen=15
.set yk=17
.set ydata=18
_circular_conv: pshm x,d
txK ; save old xk pshm d
tsx
clrm ; clear the m register clra ; put ylen into e register ldab ylen,x ; ylen
addb #-1 ; decrement count to get correct tde ; number of iterations
ldaa xlen,x ; xlen is the circular part asla ; of the convolution. Create
coma ; a mask that is the compliment clrb ; of twice the length.
tdmsk ; put xlen in XMASK ldab yk,x
tbyk
ldy ydata,x ; ydata with yk extension pshm x ; save x
ldab xk,x tbxk
ldx xdata,x ; xdata with the xk extension ldhi ; load H and I registers for mpy rmac 2,2 ; do rmac ylen times
tmer ; send result to e and then pulm x ; restore x
ldx 2,x ; restore it to its original value
338 Chapter 6 Large Microcontrollers
pulm d ; restore old xk tbxk
ais #4 ; fix the stack ted ; data to be returned rts ; return
.public _circular_conv
.end
Listing 6-10: Circular Convolution Routine In Assembly Language
Variable names get lost from the program when executing an assembly language program called from C. Therefore, when a func tion is entered, the names of the variables are replaced by offsets from a table saved by the compiler. Programming functions without the aid labels and variable names requires careful attention to the details of stacking and unstacking these data. It is recommended that when a program is to be prepared, an abbreviated version like the function dot_product( ) above—which will guide the program mer in setting up the variable locations in memory—be written. In the above routine, the offset OFST was not used, and the various offsets were assigned names that correspond to the variables. This approach makes it easier to understand what the program is doing and easier to write the code correctly.
It is important that the contents of the XK register be restored to its initial value when a function call is returned to the calling program. The content of the X and D registers is saved on the stack and the value or XK is then saved. The contents of the stack are placed in the X register, and this register is incremented by two so that it will point to the last data pushed on the stack. The M register is cleared, and the parameter ylen is moved into the E register. This value is used with the rmac instruction to tell how many times the mac instruction is to be repeated. The value of xlen is the length of the buffer that contains input data. This buffer is fed circularly so that when the calculation reaches the last entry in the buffer the pointer into the buffer will be returned to the top of the buffer to get its next entry. The length xlen is the number of entries in the buffer, but the length for the calculation must be the number of bytes in the buffer. Therefore, the value of xlen must be doubled prior to the creation of a mask to be used in XMASK. In this case, XMASK value is calculated, and YMASK which is stored as the B register content of the D register is made zero. There
Digital Signal Processor Operations |
339 |
fore, there will be a circular buffer calculation on X, but there will be no circular calculation on the Y register.
After the MASKs are set up, the two 20-bit pointer registers, X and Y, are assigned the values passed as parameters, and then the product data are put into place. The rmac instruction is then ex ecuted the number of times indicated by the contents of the E register. The register contents are then restored and the result of the singlepoint convolution is moved into the D register before control is returned to the calling program.
A simple program was written to test the operation of this func tion. This function does not attempt to make a digital filter, but it rather sets up to show how the circular convolution works. In this case, it was intended that the program run on an EVB16 board that has a normal serial interface to an RS232 port. Therefore, the data being tested can be sent out of the serial port to a terminal. The purpose of the program is really quite simple. It is to execute a convolution between a short set of coefficients and a long set of data. The coefficient set is 32 integers long, and the data set is 64 integers long. The coefficient data is a descending array of numbers that are in the upper byte of the number. These numbers start at 31 and reduce successively to zero. The data that will be used here are merely the numbers 0 through 63 in the upper byte of the number. The system is set up, the system frequency is set to 16.78 MHz, and the watchdog is disabled. The serial port is set to 9600 baud and the SCI transmitter is enabled.
#include “hc16.h” #include “sim.h” #include “qsm.h”
int putchar(char new_character); void dprint (int c);
void main(void)
{
int circular_conv(char, int*, char, int*); int data[64], coef[32],point[64],i,j,*ip;
/* initialize the SIM */
SYNCR.X=1; /* set the system freq to 16.78 MHz */
SYPCR.SWE=0; /* disable the watchdog */
/* initialize the SCI */
340 Chapter 6 Large Microcontrollers
SCCR0.SCBR=55; /* set baud rate to 9600 */ SCCR2.TE=1; /* enable the transmit of the SCI */
for(i=0;i<64;i++)
data[i]=i*0x100;
for(i=0;i<32;i++) coef[i]=(31-i)*0x100;
for(i=0;i<64;i++)
{
ip=data+i; point[i]=circular_conv(64,ip,32,coef);
}
for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
{
dprint(point[j+8*i]); putchar(‘ ‘);
}
putchar(0xd);
putchar(0xa);
}
}
int putchar(char new_character)
{
while(SCSR.TDRE==0); /* wait until transmit buffer empty */ SCDR=new_character;/* send out byte and reset TDRE */
}
void dprint (int c)
{
if(c<0)
{
putchar(‘-’); c=-c;
}
if ( c/10) dprint(c/10);
putchar(c%10+’0');
}
Listing 6-11: A Test Program for Circular Convolution
Digital Signal Processor Operations |
341 |
Next, the circular convolution program is executed 64 times. These results are stored in the array point[]. This array is then sent out the terminal eight numbers at a time. A few lines of code are ex tracted from Figure 6-2 to make the putchar() function. Finally, the function dprint() sends the data out of the serial port to the terminal. The output from this program is as follows :
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64
This program is merely a test program to show that the circular convolution does indeed work. The fact that the output data is always the same value, decimal 64, shows that the addresses are handled correctly inside of the circular convolution. Let us examine first to determine why the answer should be 64. The coefficients and data are each 0x100. Two of these values are multiplied and summed 32 or 0x20 times. Therefore, one would expect that the result would be 0x20000. However, you must remember that each of the above products is in fact the product of two binary fractional numbers. The binary point in each case is between bit numbers 14 and 15. The product of these numbers will yield 0x1000, but in that case, with the binary point between bits 29 and 30. Actually, the binary point dictated by the microcontroller is between bits 30 and 31. The an swer is corrected to this binary point location in the M register, and the result of the product is then 0x20000. This number is summed 32 times and the final result is 0x400000. When the M register is moved into the E register and then into the D register, the value that is saved is 0x40, or 64, as the test program showed.
Remember Equation 6-1 for the convolution:
n–1
yk = ∑ xk –i hi i=0
342 Chapter 6 Large Microcontrollers
In this expression, it seems possible that the x subscript can have a negative value. Actually, such a case is not possible because a nega tive subscript implies that data is used before it is available. Positive subscripts correspond to time that has already passed. Values of x will be zero for negative subscripts. If the kth sample corresponds to “now,” increasing values of i will get older samples of x. This op eration can lead to a little problem in creating the code for the convolution. The looping construct within the assembly program above selects the different values of i in the above equation. The coefficients hi are placed in memory in successive order so that an increase in the value of i will select the correct next coefficient. However, if the data values x were placed in memory as one would naturally expect, the newest value of data would be at the current array index, and old values of the data would be at lesser index val ues. This arrangement will not work correctly. The data must be placed in the array backwards in order to get the convolution to work. Older data must be at higher indices than the current data sample. Also, when filling the array initially, the program should start at the top of the array rather than the bottom. The routine listed below will store the data properly in the array.
int data[64];
int handle_data(char new_data)
{
static int i=63; if(i<0)
i=63; data[i—]=new_data; return i+1;
}
Listing 6-12: Convenient Data Storage For DSP Use
This routine is integrated into the code shown in Listing 6-11 and used to test the circular convolution. This resultant program is shown in Listing 6-13. In this case, the variables data and coef are moved outside of the function main() to make them global.
Digital Signal Processor Operations |
343 |
#include “hc16.h” #include “sim.h” #include “qsm.h”
int putchar(char new_character); void dprint (int c);
int handle_data(int new_data);
int data[64] @0x2000; int coef[32] @0x2100;
void main(void)
{
int circular_conv(char, int*, |
char, int*); |
||
int point[128],i,j,*ip; |
|||
/* initialize the |
SIM */ |
||
SYNCR.X=1; |
/* set the system |
freq to 16.78 MHz */ |
|
SYPCR.SWE=0; |
/* |
disable the |
watchdog */ |
/* initialize the SCI */
SCCR0.SCBR=55; /* set baud rate to 9600 */ SCCR2.TE=1; /* enable the transmit of the SCI */
for(i=0;i<64;i++)
data[i]=0x00;
for(i=0;i<32;i++)
coef[i]=0x100;
for(i=0;i<64;i++)
{
/* get new data—use 0x100 for this test */
ip=data+handle_data(0x100); point[i]=circular_conv(64,ip,32,coef);
}
for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
{
dprint(point[j+8*i]); putchar(‘ ‘);
}
344 Chapter 6 Large Microcontrollers
putchar(0xd);
putchar(0xa);
}
}
int handle_data(int new_data)
{
static int i=63;
data[i—]=new_data; i &=63;
return (i==63)?0:i+1;
}
Listing 6-13: Test Program For The Circular Convolution
Then the code to send the data to the circular_conv() is modified slightly to that shown below.
for(i=0;i<64;i++)
{
/* get new data — use 0x100 for this test */
ip=data+handle_data(0x100); point[i]=circular_conv(64,ip,32,coef);
}
The value 0x100 is sent into the function handle_data , and the return is the index into the array where the data was stored in the array data. When this integer is added to data, which is a pointer to a type int, the pointer ip will point to the location into the array where the latest value was stored in memory. Under normal circumstances, this piece of code would be entered under control of a clock and the data sent into the routine handle_data() would be new input from an analog to digital converter. Also, the for statement is not expected in a practical application. The output from this program is:
2 |
4 |
6 |
8 10 12 14 16 |
18 |
20 22 24 26 28 30 32 |
||
34 |
36 38 40 42 44 46 48 |
||
50 |
52 54 56 58 60 62 64 |
||
64 |
64 64 64 64 64 64 64 |
||
64 |
64 64 64 64 64 64 64 |
||
64 |
64 64 64 64 64 64 64 |
||
64 |
64 64 64 64 64 64 64 |
||