Tài liệu Real-Time Digital Signal Processing - Appendix C: Introduction of C Programming for DSP Applications - Pdf 10

Appendix C
Introduction of
C Programming for
DSP Applications
C has become the language of choice for many DSP software developments not only
because of its powerful commands and data structures but also because of its portability
to migrate between DSP platforms and devices. In this appendix, we will cover some of
the important features of C for DSP applications.
The processes of compilation, linking/loading, and execution of C programs differ
slightly among operating environments. To illustrate the process we use a general UNIX
system C compiler shown in Figure C.1 as an example. C compiler translates high-level
C programs into machine language that can be executed by computers or DSP proces-
sors such as the TMS320C55x. The fact that C compilers are available for a wide range
of computer platforms and DSP processors makes C programming the most portable
software for DSP applications. Many C programming environments include debugger
programs, which are useful for identifying errors in source programs. Debugger pro-
grams allow us to see values stored in variables at different points in a program and to
step through the program line by line.
The purpose of DSP programming is to manipulate digital signals for a specific signal
processing application. To achieve this goal, DSP programs must be able to organize
the variables (different data types), describe the actions (operators), control the oper-
ations (program flow), and move data back and forth between the outside world and the
program (input/output). This appendix provides a brief overview of the elements
required for efficient programming of DSP algorithms in C language and introduces
fundamental C programming concepts using C examples, but does not attempt to
cover all the elements in detail. C programming language used throughout this book
is conformed to the ANSI C standard (American National Standard Institute C
Standard).
Real-Time Digital Signal Processing. Sen M Kuo, Bob H Lee
Copyright # 2001 John Wiley & Sons Ltd
ISBNs: 0-470-84137-0 (Hardback); 0-470-84534-1 (Electronic)

#include <math.h>
#define K 1024
void main()
{
float y [K];
int k;
int N  256;
/* Generate unit impulse sequence */
470 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
for(k  1; k < N; k)
{
y[k] 0.0; //Clear array
}
y[0] 1.0; //y(0) 1
}
A program written in C must have a few basic components. We now briefly discuss these
components used in this example C program.
C program comments may contain any message beginning with the characters
sequence /* and ending with the characters sequence */. The comments will be ignored
by the compiler. Program comments may be interspersed within the source code as well
as at the beginning of a function. In above example, the extra asterisks around the
program comments in lines one through three are there only to enhance the appearance
of the comments; they are not necessary. Most of the C compiler nowadays also accepts
the C programming language comments sequence, //. In our example, we mixed
both comment sequences for demonstration purpose. Although program comments are
optional, good programming style requires that they be used throughout a program to
document the operations and to improve its readability. Detailed comments are very
important in maintaining complicated DSP software for new readers, even for the
original programmers after time has passed.
The preprocessor is the first pass of the C compiler. It reads in C source files as input

two types of statements ± statements that define memory locations that will be used in the
program and statements that specify actions to be taken.
C.1.1 Variables and Assignment Operators
Before a variable can be used in a C program, it must be declared to inform the compiler
the name and type of the variable. A variable name is defined by declaring a sequence of
characters (the variable identifier or name) as a particular predefined type of data. C
constants are specific values that are included in the C statements, while variables are
memory locations that are assigned a name or identifier. The variable declaration uses
the following syntax:
data_type name;
For example, in the simple example we have
int k;
The term int indicates that the variable named k will store as integer data value. C also
allows multiple variables to be defined within one statement by separating them with the
commas. For example,
int i,j,k;
An identifier may be any sequence of characters (usually with some length restric-
tions) that starts with a letter or an underscore, and cannot be any of the C compiler
reserved keywords. Note that C is case sensitive; making the variable k different from
the variable K. C language supports several data types that represent: integers numbers,
floating-point numbers, and text data. Arrays of each variable type and pointers of each
type may also be declared and used. Once variables are defined to be a given size and
type, some sort of manipulation can be performed using the variables.
Memory locations must be defined before other statements use them. Initial values
can also be specified at the same time when memory locations are defined. For example,
int N  256;
defines the variable N as an integer, and assigns it with the value 256.
An assignment statement is used to assign a value to an identifier. The most basic
assignment operator in C is the single equal sign, =, where the value to the right of the
equal sign is assigned to the variable on the left. The general form of the assignment

}
Table C.1 Example of integer type limits
Data type Value on 32-bit machine Value on 16-bit machine
Short [À32 768, 32 767] [À32 768, 32 767]
unsigned short [0, 65 535] [0, 65 535]
Int [À2 14 74 83 648, 2 14 74 83 647] [À32 768, 32 767]
unsigned int [0, 4 29 49 67 295] [0, 65 535]
Long [À2 14 74 83 648, 2 14 74 83 647] [À2 14 74 83 648, 2 14 74 83 647]
unsigned long [0, 4 29 49 67 295] [0, 4 29 49 67 295]
A SIMPLE C PROGRAM 473
Instead of using short and long data type, the example code uses Word16 for the 16-
bit integer data type and Word32 for the 32-bit data type. In addition, the three integer
types (int, short, and long) can be declared as unsigned by preceding the declaration
with unsigned. For example,
unsigned int counter;
where counter has a value range from 0 to 65 535.
Statements and expressions using the operators should normally use variables and
constants of the same type. If data types are mixed, C uses two basic rules to auto-
matically make type conversions:
1. If an operation involves two types, the value with a lower rank is converted to the
type of higher rank. This process is called promotion, and the ranking from highest
to lowest type is double, float, long, int, short, and char.
2. In an assignment statement, the result is converted to the type of the variable that is
being assigned. This may result in promotion or demotion when the value is
truncated to a lower ranking type.
Sometimes the conversion must be stated explicitly in order to demand that a con-
version be done in a certain way. A cast operator places the name of the desired type in
parentheses before the variable or expression, thus allowing us to specify a type change
in the value. For example, the data casting(int)used in the following expressions
treats the floating-point number z as an integer:

For example,
float y [5] {1.0, 0.0, 0.0, 0.0, 0.0 };
initializes a 5-point unit impulse response sequence in the floating-point array y[].
Arrays can also be assigned values by means of program statements. For example, the
following example generates an N-point unit impulse sequence.
for(k  1; k < N; k)
{
y[k] 0.0; /* Clear array */
}
y[0] 1.0; /* y(0) 1*/
A more detailed discussion on arrays and loops will be given later.
C.2 Arithmetic and Bitwise Operators
Once variables are defined to be a given size and type, a certain manipulation (operator)
can be performed using the variables. We have discussed assignment operators in C.1.1.
This section will introduce arithmetic and bitwise operators. Logical operators will be
introduced later.
C.2.1 Arithmetic Operators
C supplies arithmetic operators on two operands: + (add), - (subtract), * (multiply), /
(divide), and % (modulus, integer remainder after division). The first four operators are
defined for all types of variables, while the modulus operator is only defined for integer
operands. C does not have an exponential operator. However, the library function
pow(x,y)may be used to compute x
y
. Note that in C, a  pow(x,y)is an expression,
while a  pow(x,y); is a statement. Thus c  b+(apow(x,y)); is a statement. The
result of this statement would be that the result returned by the function pow(x,y)is
assigned to a and ba is assigned to c.
ARITHMETIC AND BITWISE OPERATORS 475
The modulus operator is useful in implementing a circular pointer for signal proces-
sing. For example,

To introduce more features of C programming, an example C program firfltr.c
that implements an FIR filter is included in the software package.
476 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
C.3.1 Command-Line Arguments
The function main can have two parameters, argc and argv [], to catch arguments
passed to main from the command line when the program begins executing. These
arguments could be file names on which the program is to act or options that
influence the logic of the program. The parameter argv []is an array of pointers to
strings, and argc is an int whose value is equal to the number of strings to which
argv []points to. The command-line arguments are passed to the main( ) function as
follows:
void main(int argc, char *argv [])
Suppose that we compile the firfltr.c such that the executable program is generated
and saved as firfltr.exe. We can run the program on a PC under MS-DOS Prompt
by typing
firfltr infile coefile outfile <enter>
The operating system passes the strings on the command line to main. More precisely,
the operating system stores the strings on the command line in memory and sets
argv [0]to the address of the first string (firfltr), the name of the file that holds
the program to be executed on the command line. argv [1]points to the address of the
second string (infile) on the command line, argv [2]to coefile, and argv [3]to
outfile. The argument argc is set to the number of strings on the command line. In
this example, argc  4.
The use of command-line arguments makes the executable program flexible, because
we can run the program with different arguments (data files, parameter values, etc.)
specified at the execution time without modifying the program and re-compiling it
again. For example, the file firfltr.exe can be used to perform FIR filtering
function for different FIR filter with their coefficients defined by coefile. This
program can also be used to filter different input signals contained in the infile.
The flexibility is especially convenient when the parameter values used in the program

main function, such as:
void shift(float *, int, float);
float fir(float *, float *, int);
These functions are sets of statements that typically perform an operation, such as
shift to update data buffers for FIR filters, or as fir to compute an output of the
FIR filter.
To maintain simplicity and readability for more complicated applications, we develop
programs that use a main()function plus additional functions, instead of using one
long main function. In C, any function can call any other function, or be called by any
other function. Breaking a long program into a set of simple functions has following
advantages:
1. A function can be written and tested separately from other parts of the program.
Thus module development can be done in parallel for large projects. Several
engineers can work on the same project if it is separated into modules because the
individual modules can be developed and tested independently of each other.
2. A function is smaller than the complete program, so testing it separately is easier.
3. Once a function has been carefully tested, it can be used in other programs without
being retested. This reusability is a very important issue in the development of large
software systems because it greatly reduces development time.
4. The use of functions frequently reduces the overall length of a program, because
many solutions include steps that are repeated several places in the program.
478 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
A function consists of a definition statement followed by the function body. The
first part of the definition statement defines a function name and the type of value
that is returned by the function. A pair of parentheses containing an optional
argument list and a pair of braces containing the optional executable statements. For
example,
float fir(float *x, float *h, int ntap)
{
float yn  0.0; /* Output of FIR filter */

than by reference, the values of the formal parameters are not moved back to the actual
parameters when the function completes its execution. When the function needs to
modify the variables in the calling program, we must specify the function arguments
as pointers to the beginning of the variables in the calling program's memory. For
example, the main function calls the function shift as
AN FIR FILTER PROGRAM 479
shift(xn_buf, len_imp, xn);
where the address (value) of the first element in xn_buf is passed to the function
shift. Thus in shift, we use
float *x;
which defines the pointer *x points to the first element of xn_buf in the main function.
Therefore the signal buffer array x[i]used in the function shift is actually the same
array xn_buf used by the main function. Consequently, it can be modified by the
function shift.
C.3.4 Files and I/O Operations
To use standard input/output functions provided by the C compiler, we must have the
statement #include <stdio.h> that includes the standard I/O header file for func-
tion declaration. Some functions that perform standard input/output identify the file to
read or write by using a file pointer to store the address of information required to
access the file. To define file pointers, we use
FILE *fpin; /* File pointer to x(n) */
FILE *fpimp; /* File pointer to b(n) */
FILE *fpout; /* File pointer to y(n) */
We can open a file with the function fopen and close it with the function fclose. For
example,
fpin  fopen(argv [1],"rb");
fpout  fopen(argv [3],"wb");
The function fopen requires two arguments ± the name of the file and the mode. The
name of the file is given in the character string argv [], or simply use `file_name'
where file_name is a file name that contains the desired data. For example, to open

pointed to by the file pointer fpin. The fread function returns the number of items
successfully read. The operator sizeof(object)has a value that is the amount of
storage required by an object. The values of sizeof for different data types may vary
from system to system. For example, in a workstation, the value of sizeof(int)is 4
(bytes), whereas on fixed-point DSP systems, the value of sizeof(int)is typically 2
(bytes).
The function fwrite expects four arguments. For example, the statement
fwrite(&yn, sizeof(float), 1, fpout);
writes the binary form float array yn of size sizeof(float)to the file pointed to by
the pointer fpout. The difference between using fwrite to write a floating-point
number to a file and using fprintf with the %f format descriptor is that fwrite
writes the value in binary format using 4 bytes, whereas fprintf writes the value as
ASCII text which usually need more than 4 bytes.
C.4 Control Structures and Loops
C language has a complete set of program control features that allow conditional
execution or repetition of statements in loops based on the result of an expression.
C.4.1 Control Structures
The C language provides two basic methods for executing a statement or series state-
ments conditionally: the if statement and the switch-case statement. The if
statement allows us to test a condition and then execute statements based on whether
the given condition is true or false. The if statement has the following general
format:
CONTROL STRUCTURES AND LOOPS 481
if (condition)
statement1;
If the condition is true, the statement1 is executed; otherwise, this statement will be
skipped. When more than one statement needs to be executed if the condition is true, a
compound statement that consists of a left brace {, some number of statements, and a
right brace }is used as follows:
if (condition)

482 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
Is
Condition
True?
Statements A Statements B
No
Yes
Figure C.3 Flowchart for if/else statement
switch(integer expression)
{
case constant_1:
statements;
break;
case constant_2;
statements;
break;

default:
statements;
}
Program control jumps to the statement if the case label with the constant (an integer
or single character in quotes) matches the result of the integer expression in the
switch statement. If no constant matches the expression value, the control goes to
the statement following the default label. When a match occurs, the statements
following the corresponding case label will be executed. The program execution will
continue until the end of the switch statement is reached, or a break statement that
redirects control to the end of the switch-case statement is reached. A break
statement should be included in each case not required after the last case or default
statement.
C.4.2 Logical Operators

through its elements. For example, the simple C program listed in Section C.1 uses the
following for loop:
for(k  1; k < N; k)
{
y[k] 0.0; /* Clear array */
}
This for statement sets k to 1 (one) first and then checks if k is less than the number N,
If the test condition is true, it executes the statement y[k] 0.0; increments k, and then
repeats the loop until k is equal to N. Note that the integer k is incremented at the end
of the loop if the test condition statement k < N is true. When the loop is completed, the
elements of the array y are set to zero from y[1]up to y[NÀ1].
484 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
The while loop repeats the statements until a test expression becomes false or zero.
The general format of a while loop is
while(condition)
{
statements;
}
The condition is evaluated before the statements within the loop are executed. If the
condition is false, the loop statements are skipped, and execution continues with the
statement following the while loop. If the condition is true, then the loop statements
are executed, and the condition is evaluated again. This repetition continues until the
condition is false. Note that the decision to go through the loop is made before the loop
is ever started. Thus it is possible that the loop is never executed. For example, in the
FIR filter program firfltr.c, the while loop
while((fscanf(fpimp,"%f",&hn))! EOF)
{
bn_coef [len_imp](float)bn; /* Read coefficients */
xn_buf [len_imp] 0.; /* Clear x(n)vector */
len_imp; /* Order of filter */

int 16-bit 2s complement [À32 768, 32 767]
unsigned int 16-bit Binary [0, 65 535]
long 32-bit 2s complement [À2 14 74 83 648, 2 14 74 83 647]
unsigned long 32-bit Binary [0, 42 949 67 295]
float 32-bit IEEE 32-bit [1.175 49 4 10
À38
, 3.402 82 346 10
38
]
double 32-bit IEEE 32-bit [1.175 494 10
À38
, 3.402 82 346 10
38
]
long double 32-bit IEEE 32-bit [1.175 494 10
À38
, 3.402 82 346 10
38
]
enum 16-bit 2s complement [À32 768, 32 767]
data pointer 16-bit Small memory [0x0, 0xFFFF]
data pointer 23-bit Large memory [0x0, 0x7FFFFF]
program pointer 24-bit Function [0x0, 0xFFFFFF]
data type uses 32-bit binary values. The signed data types use 2's-complement notation.
Finally, all floating-point data types are the same and represented by the IEEE single-
precision format. It is the programmer/engineer's responsibility to correctly define and
use the data types while writing the program. When porting applications from one
platform to the other, it is equally important that the correct data type conversion is
applied, such as convert all the data defined as char from 8-bit to 16-bit integer.
References


Nhờ tải bản gốc

Tài liệu, ebook tham khảo khác

Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status