Vous êtes sur la page 1sur 26

Departamento de Tecnologa

Electrnica
Propuesta de prcticas
Curso 2014-2015

Curso 2014-2015

ndice de contenido
Algunas cosas tiles.............................................................................................................................3
Problemas detectados...........................................................................................................................8
Como empezar, generacin de un tono...............................................................................................12
Practica propuesta...............................................................................................................................14
Retraso en el tiempo...........................................................................................................................15
Prctica propuesta...............................................................................................................................16
Filtrado en el dominio del tiempo......................................................................................................17
Prctica propuesta...............................................................................................................................20
Filtrado en el dominio de la frecuencia..............................................................................................21
Prctica propuesta...............................................................................................................................26

2/26

Curso 2014-2015
Algunas cosas tiles
Antes de empezar hay una serie de funciones que son tiles tener en cuenta para poder trabajar con
la placa de desarrollo.
En el laboratorio hay disponibles en este momento dos sistemas de desarrollo, uno basado en el
DSP TMS320C6713 y el segundo basado en el procesador TMS320C6748.
El sistema de desarrollo proporciona una serie de funciones para acceder a los recursos de la placa.

Para el sistema basado en el 6713


Para acceder al estado de los DIP se usan la funcin DSK6713_dip_get(int).
Para leer el estado del DIP 0 se usa las siguientes instrucciones.
if (DSK6713_dip_get(0) == 0)
{
}
Para cambiar los estados de led disponemos las funciones
DSK6713_led_on(int),DSK6713_led_off(int) y DSK6713_led_toggle(int), que permiten encender
los led, apagarlos y cambiar el estado.
Para inicializar la placa para poder usar estas funciones es necesario antes realizar una llamada a la
funcin DSK6713_init().
Para poder usar los dips es necesario hacer
DSK6713_init();
DSK6713_dip_init();
antes de poder usarlos.
Para poder usar los led la secuencia ser
DSK6713_init();
DSK6713_led_init();
Si instala los ejemplos del libro Digital Signal Processing and Applications with the
TMS320C6713 and TMS320C6416 DSK puede usar las funciones que se encuentran en el
directorio Support en los ficheros c6713dskinit.c y C6713dskinit.h. En estos ficheros se encuentran
algunas de las rutinas usadas en los ejemplos de este documento como comm_intr() que inicializa t
configura la placa para trabajar con interrupciones y llama a la funciones de inicializacin de la
placa.
Otras funciones que proporcionan estos ficheros son
sample_data = input_left_sample(); //lee la muestra de la izquierda, 16 bits
output_left_sample(sample_data); // escribe la muestra de la
izquierda, 16 bits
3/26

Curso 2014-2015
sample_data = input_righ_sample(); //lee la muestra de la derecha,
16 bits
output_righ_sample(sample_data); // escribe la muestra de la
derecha, 16 bits
sample_data = input_sample(); //lee los dos canales, los 16 bits
ms significativos corresponden al canal izquierdo, 32 bits
output_sample(sample_data); // escribe los dos canales, los 16
bits ms significativos corresponden al canal izquierdo, 32 bits
Para acceder fcilmente a los dos canales se puede incluir en el cdigo la siguiente definicin:
typedef union {
Uint32 uint;
short channel[2];
} AICData;
Para usarla:
AICData myData;
myData. uint = input_sample();
y ahora acceder al canal izquierdo es suficiente con hacer
myData. channel [1] y para el derecho myData. Channel [0]
Compruebe que el modelo escogido es far, con esta placa, cualquier otro modelo provoca que las
interrupciones no se ejecuten.

4/26

Curso 2014-2015
En la figura puede observar donde se activa dicha opcin

Para el sistema basado en el 6748


Este sistema no tiene disponible el acceso a los led de la placa actualmente, el acceso debe ser
realizado programando la GPIO del sistema
Para acceder a los dips (interruptores 5 6,7 y 8, se usa la funcin
uint8_t read_LCDK_user_DIP();
que devuelve una mscara binaria con el estado de los DIPS, (dip 5 bit 0 .)
La funcin de inicialiazacin es
L138_init_LCDK_DIP();
Las funciones de acceso a las muestras son idnticas al caso anterior.
sample_data = input_left_sample(); //lee la muestra de la izquierda, 16 bits
output_left_sample(sample_data); // escribe la muestra de la
izquierda, 16 bits
sample_data = input_righ_sample(); //lee la muestra de la derecha,
16 bits
output_righ_sample(sample_data); // escribe la muestra de la
derecha, 16 bits
sample_data = input_sample(); //lee los dos canales, los 16 bits
ms significativos corresponden al canal izquierdo, 32 bits
output_sample(sample_data); // escribe los dos canales, los 16
bits ms significativos corresponden al canal izquierdo, 32 bits
Donde si presenta diferencias es en la funcin de configuracin y en la interrupcin por defecto. Si
en el caso anterior la interrupcin usada es la 11, en este sistema las interrupciones son
completamente configurables y por defecto se usa la interrupcin 4.
Las funciones de configuracion son ahora
Para trabajar con polling
void L138_initialise_poll(int32_t fs, int16_t adc_gain, int16_t
dac_atten, int8_t input_type)
Para trabajar con interrupciones

5/26

Curso 2014-2015
void L138_initialise_intr(int32_t fs, int16_t adc_gain, int16_t
dac_atten, int8_t input_type)
Para trabajar con interrupciones EDMA, en este caso se establece un buffer y la interrupcin se
ejecuta cuando el buffer est lleno.
void L138_initialise_edma(int32_t fs, int16_t adc_gain, int16_t
dac_atten, int8_t input_type)
Todas estas funciones estn configuradas para que la interrupcin se resetea en escritura, lo que
obliga a escribir una muestra siempre, aunque esta muestra sea silencio, (0), es posible modificar
esto fcilmente modificando estas funciones
La configuracin de la entrada y la velocidad de muestreo son ahora parmetros de la funcin en
lugar de variables globales.
Aqui puede ver dos ejemplos de configuracin, uno con velocidad de muestreo de 8KHz y entrada
LINE y el segundo con una velocidad de 16KHz y entrada mic
L138_initialise_intr(FS_8000_HZ,ADC_GAIN_0DB,DAC_ATTEN_0DB,LCDK_LINE_INPUT);
L138_initialise_intr(FS_16000_HZ,ADC_GAIN_0DB,DAC_ATTEN_0DB,LCDK_MIC_INPUT);

CUIDADO
Un aspecto al que hay que prestar atencin, por los posibles problemas que puede provocar, es
fichero de configuracin CMD que establece el mapa de memoria de la placa. Cuando se inicia un
proyecto con Code Composer este establece un fichero de configuracin por defecto que no es
adecuando para ninguno de los sistemas de desarrollo, por lo que debe prestar atencin de que el
fichero de configuracin es correcto. En los ejemplos proporcionados para ambas plataformas, se
incluyen ya el fichero correcto, este fichero est en el directorio support donde tambin se
encuentran el fichero donde se incluye el cdigo con las funciones de configuracin y acceso a las
muestras (input_left_sample ... )que se han comentado anteriormente. El fichero para el
sistema c6713 se denomina C6713dsk.cmd, el fichero para el sistema basado en el c6748 se llama
linker_dsp.cmd
A continuacin se incluyen los contenidos de ambos ficheros.
Observe que el tamao del stack y del heap estn incluidas en el fichero del c6748, aunque ambas
opciones se pueden fijar en las opciones de proyecto.
Es importante que preste atencin al tamao del stack ya que las variables locales de las funciones,
incluidas las variables definidas dentro de la rutina main, se almacenan dentro del stack. Un stack
pequeo o estructuras locales muy grandes pueden provocar un desbordamiento del stack.
/*C6713dsk.cmd
MEMORY
{
IVECS:
IRAM:
SDRAM:
FLASH:

Linker command file*/

org=0h,
len=0x220
org=0x00000220,
len=0x0002FDE0 /*internal memory*/
org=0x80000000, len=0x01000000 /*external memory*/
org=0x90000000, len=0x00020000 /*flash memory*/

6/26

Curso 2014-2015
}
SECTIONS
{
.EXT_RAM
.vectors
.text
.bss
.cinit
.stack
.sysmem
.const
.switch
.far
.cio
.csldata
}

:>
:>
:>
:>
:>
:>
:>
:>
:>
:>
:>
:>

SDRAM
IVECS
IRAM
IRAM
IRAM
IRAM
IRAM
IRAM
IRAM
IRAM
IRAM
IRAM

/*in vector file*/


/*Created by C Compiler*/

/* linker_dsp.cmd */
-stack
-heap

0x00000800
0x00010000

MEMORY
{
dsp_l2_ram:
shared_ram:
external_ram:
}
SECTIONS
{
.text
.const
.bss
.far
.switch
.stack
.data
.cinit
.sysmem
.cio
.vecs
.EXT_RAM
}

>
>
>
>
>
>
>
>
>
>
>
>

ORIGIN = 0x11800000
ORIGIN = 0x80000000
ORIGIN = 0xC0000000

LENGTH = 0x00040000
LENGTH = 0x00020000
LENGTH = 0x08000000

dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
dsp_l2_ram
external_ram

7/26

Curso 2014-2015
Problemas detectados
El objetivo de este apartado es ir incluyendo los problemas detectados en los sistemas de desarrollo,
por lo que esta seccin podr cambiar con el tiempo. Algunos de estos problemas son comunes en
ambas plataformas,
La interrupcin no se ejecuta.
Posibles causas.
Sistema 6713, el modelo de compilacin no es FAR.
Sistema 6748, Se ha detectado un problema con la interrupcin cuando la entrada est configurada
como LINE, este problema no aparece cuando se ejecuta el programa por primera vez pero cuando
se caga veces sucesivas la interrupcin no se ejecuta ms. Si se sufre este problema es necesario
modificar un fichero. En el fichero L138_init_aic3106_ini.c, la funcin
L138_init_aic3106_registers
if (input_type == LCDK_LINE_INPUT)
{
AIC3106_writeRegister( 19, 0x04 );
AIC3106_writeRegister( 22, 0x04 );
}

// power up ADCs
// and connect LINE1L/R to ADC

por
if (input_type == LCDK_LINE_INPUT)
{
AIC3106_writeRegister( 19, 0x04 );
AIC3106_writeRegister( 22, 0x04 );
AIC3106_writeRegister( 17, 0x0F );
0dB
AIC3106_writeRegister( 18, 0xF0 );
0dB
AIC3106_writeRegister( 25, 0x40 );
}

// power up ADCs
// and connect LINE1L/R to ADC
// 17 MIC3L to L ADC (default 0xFF, NC) 0x00
// 18 MIC3R to R ADC (default 0xFF, NC) 0x00
// 25 MICBIAS 0x40 2V, 0x00 OFF

Falta el vector de interrupciones (comn en ambos sistemas). La funcin definida en el vector no es


la misma que la que se desea usar.
Hay un error en el nmero de interrupcin.
No se ha llamado a la funcin de configuracin que habilita las interrupciones (mire los ejemplos).
El breackpoint resetea algunas veces el vector de interrupciones en el sistema 6748, en este caso es
necesario volver a cargar el programa.
El programa finaliza sin hacer nada
Causa ms comn. No hay un bucle infinito en la rutina main, por lo cual el programa finaliza. Este
bucle puede no existir si se est usando DSP/BIOS.
8/26

Curso 2014-2015
Problema de depuracin, la ejecucin paso a paso en el depurador hace cosas raras, entra en
cdigo que no debe y parece que hace algo que no se corresponde con lo que veo en la pantalla
La causa ms comn de esto es que est activa alguna opcin de optimizacin de cdigo, en este
caso se debe deshabilitar. Seleccionar project->propierties. Seleccionar C6000 Compiler y escoger
Basic Options, tal como aparece en la figura. A continuacin en la opcin Optimization level
selecionar disable.
Recuerde que estos cambios son para depuracin, una vez que el cdigo est depurado y se pretenda
su despliegue en campo debe desactivarse las opciones de depuracin y seleccionar las opciones de
optimizacin de cdigo para que este sea ms eficiente y el programa generado sea de menor
tamao

Los resultados en punto fijo no son los que deben


Cuando se opera con nmeros de 32 bits que se deben acumular en un nmero de 64 bits (este
problema es extensible a cualquier operacin cuyo resultado deba ser acumulador en un nmero de
ms de 32 bits) el resultado de la operacin es errneo.
Este es un problema derivado del compilador y la arquitectura. Cuando se multiplican dos enteros,
siempre que estos dos enteros sean de 32 bits o menores, internamente el sistema usa un
acumulador de 32 bits antes de pasar el resultado al acumulador final.
Por ejemplo
9/26

Curso 2014-2015
int64_t resultado;
int32_t val1,val2;
resultado = val1*val2;
Si el resultado de la operacin de multiplicacin es mayor que el mayor valor que puede caber en un
entero de 32 bits se va a producir un truncamiento en el acumulador interno, con lo que el resultado
de la operacin ser errneo. Para evitar este problema se necesita usar casting
resultado = (int64_t)val1*(int64_t)val2;
Con esto, antes de multiplicar, se fuerza a pasar tanto val1 como val2 a enteros de 64 bits, con lo
que el sistema usar un acumulador de 64 bits internamente y se evitar el problema de
truncamiento.
Conversin entre tipos
Algo a lo que hay que prestar atencin, debido a las potenciales problemas que puede causar, es la
conversin entre tipos de datos.
Cuando se va a pasar de un tipo a otro hay tres operaciones a las que hay que prestar atencin por si
es preciso aplicarlas o evitar que ocurran pues pueden provocar errores. Estas operaciones son:
1. Truncamiento. Es la operacin de conversin por defecto cuando se pretende convertir un
nmero entero de mayor tamao (por ejemplo un entero de 32 bits) a uno de menor tamao
(por ejemplo 16 bits), en este caso se eliminan los bits ms significativos.
2. Desplazamiento hacia la derecha. Esta es la operacin que se debe aplicar para conservar la
fidelidad numrica cuando se trabaja con nmeros en formato Q (punto fijo), por ejemplo,
para poder pasar de un nmero en formato Q31 a un nmero en formato Q16, y consiste en
un desplazamiento a la derecha para desechar los bits menos significativos. Se usa en la
multiplicacin.
3. Saturacin. En este caso se comprueba si el valor est comprendido dentro de los mrgenes
que la variable puede almacenar. Si est comprendido se asigna el valor y en caso contrario
se asigna a la variable el valor mximo (o mnimo) que puede almacenar. Esta operacin se
aplica para pasar de valor flotante a entero. En caso de que el valor est fuera de los lmites
y se realice la conversin el resultado ser un valor incorrecto que introducir una fuerte
distorsin en la seal de salida (una distorsin mayor que la introducida por la saturacin).
Cuando se opera con flotantes no es necesario realizar ninguna operacin, los flotantes ajustan
dinmicamente su fidelidad de forma que, excepto la saturacin cuando se pasa a muestras enteras
para enviarlas a los conversores, no es preciso realizar ninguna otra operacin.
Lectura y escrituras de canales mono y estero
Las libreras que estamos usando proporcionan una serie de funciones que nos permiten, bien leer o
escribir un canal, bien los dos canales estreos. Es necesario entender como funciona estas
funciones. En realidad, todas las funciones leen o escriben los dos canales simultneamente, para
ello leen (o escriben) en un registro del DSP. Para la lectura no hay problemas, como es una lectura
de un registro, podemos leer el registro con la funcin input_sample() que leer y presentar el
10/26

Curso 2014-2015
valor del registro completo (los 16 bits menos significativos son de un canal y los 16 mas
significativos del otro canal) o usar las funciones input_right_sample(void) y
input_left_sample(void), estas funciones leeran el registro completo y harn el
truncado/desplazamiento adecuado para presentar cada canal. El problema est en la escritura
estero, si pretendemos escribir output_right_sample() y output_left_sample(), como ambas
funciones escriben sobre el mismo registro, se produce una sobre escritura del registro, con lo que
solo uno de los dos canales se enviar a la salida. En este caso es preciso usar la funcin
output_sample()

11/26

Curso 2014-2015
Como empezar, generacin de un tono
Este programa genera una seal sinusoidal que se enva al conversor. Esto se percibe como un tono
//sine_intr.c Sine generation using sin() function
#include <math.h>
#include "L138_LCDK_aic3106_init.h"

#define SAMPLING_FREQ 8000


#define PI 3.14159265358979
float
float
float
float

frequency = 1000.0;
amplitude = 10000.0;
theta_increment;
theta = 0.0;

interrupt void interrupt4()


{
theta_increment = 2*PI*frequency/SAMPLING_FREQ;
theta += theta_increment;
if (theta > 2*PI) theta -= 2*PI;
output_left_sample((short)(amplitude*sin(theta)));
return;
}
void main()
{
L38_initialise_intr(FS_8000_HZ,ADC_GAIN_0DB,DAC_ATTEN_0DB,LCDK_MIC
_INPUT);
while(1);
}
Cambiando el valor de la frecuencia podemos cambiar la frecuencia de reproduccin de la
sinusoide. Es posible crear composiciones varias sinusoides, en este caso la salida sera.
theta_increment = 2*PI*frequency1/SAMPLING_FREQ;
theta1 += theta_increment;
if (theta1 > 2*PI) theta1 -= 2*PI;
theta_increment = 2*PI*frequency2/SAMPLING_FREQ;
theta2 += theta_increment;
if (theta2 > 2*PI) theta2 -= 2*PI;
12/26

Curso 2014-2015
output_left_sample((short)( (amplitude1*sin(theta1))+
(amplitude2*sin(theta2)));

Como puede observar, el programa usa la funcin para escribir usando una interrupcin en el
conversor. La funcin de inicializacin establece la interrupcin 4 como interrupcin del conversor.
Ahora es necesario relacionar la interrupcin 4 con la funcin de tratamiento de la interrupcin.
Esto se realiza mediante el fichero vector_intr.asm, que se lista a continuacin. Observe como se
indica que cuando se reciba la interrupcin 4 debe ejecutar la funcin _interrup4. Esta funcin est
definida en el cdigo c. Preste atencin que se usa el carcter _ antes del nombre de la funcin. Es
una convencin del ensamblador de TI para indicar que la funcin est definida en otro lenguaje
distinto del ensamblador.
.global _vectors
.global _c_int00
.global _vector1
.global _vector2
.global _vector3
.global _interrupt4 ; funcion de tratamiento de la interrupcion
4
.global
.global
.global
.global
.global
.global
.global
.global
.global
.global
.global

_vector5
_vector6
_vector7
_vector8
_vector9
_vector10
_vector11
_vector12
_vector13
_vector14
_vector15

13/26

Curso 2014-2015
Practica propuesta
Usando los DIP (la funcin para leer el estado de los DIP es read_LCDK_user_DIP ) cree un
programa capaz de generar 4 tonos superpuestos seleccionables a travs de los DIPs 0 a 3 y con
frecuencias 500, 1000, 2000, 3000 Hercios.
Para ver si estn activos los distintos interruptores se puede usar el cdigo que aparece a
continuacin.
uint8_t aux = read_LCDK_user_DIP();
if ( aux & 0x1) // interruptor 1
.
if ( aux & 0x2) // interruptor 2
.
if ( aux & 0x4) // interruptor 3
.
if ( aux & 0x8) // interruptor 4
..
Los interruptores configurables en el sistema C6748 son los que aparecen en la placa numerados del
5 al 8, (el nmero 5 en la placa corresponde al interruptor 1 en el cdigo C).
Tenga en cuenta para esta prctica el ancho de banda impuesto por el conversor para escoger la
frecuencia adecuada

14/26

Curso 2014-2015
Retraso en el tiempo
Un ejercicio sencillo de implementar en sistemas digitales es introducir pequeos retrasos
temporales en la secuencia de los datos.
Para conseguir esto es necesario almacenar los datos en memoria para posteriormente presentarlos.
La manera ms simple de introducir este retraso es usando un buffer circular.
Es fcil calcular el tamao que debe tener el buffer para un determinado retraso temporal en funcin
de la frecuencia de muestreo. Por ejemplo si la frecuencia de muestreo es 8 KHz, el tamao del
buffer para conseguir un retraso de 1 segundo ser 1*8000.
Fjese en el uso de la directiva pragma DATA_SECTION para colocar el buffer en la memoria
externa. El proyecto necesita un fichero CMD para indicar que el segmento EXT_RAM se
encuentra en la memoria SDRAM. Las seccin debe estar definida o bien en fichero de
configuracin de DSPBIOS o en el fichero CMD. En el directorio Support pueden encontrar un
fichero CMD que define una seccin en la memoria externa llamada EXT_RAM. Preste atencin al
carcter . que se aade antes del nombre en la directiva DATA_SECTION.
#define MAX_BUF_SIZE 8000 //set maximum length of delay
short buffer[MAX_BUF_SIZE]; //storage for previous samples
short input;
int i = 0; //index into buffer
#pragma DATA_SECTION(buffer, .EXT_RAM)
#include "L138_LCDK_aic3106_init.h"
interrupt void interrupt4() //interrupt service routine
{
input = input_left_sample(); //read new input sample from ADC
output_left_sample(buffer[i]);
buffer[i] = input; //store new input and
//fraction of delayed value
if(++i >= MAX_BUF_SIZE) //test for end of buffer
i = 0;
return; //return from ISR
}
void main()
{
memset(buffer,0,sizeof(short)* MAX_BUF_SIZE);
L38_initialise_intr(FS_8000_HZ,ADC_GAIN_0DB,DAC_ATTEN_0DB,LCDK_MIC
_INPUT);
while(1); //infinite loop
}

15/26

Curso 2014-2015
Prctica propuesta
Para esta prctica el alumno implementar un ECO funcionando en estreo, la implementacin del
ECO se basa en el uso de un retardo junto con una realimentacin de la muestra actual y la antigua
atenuada.
N

iti
i=0

Donde ti es el valor de la muestra en el instante i * TAM_BUFFER * Frecuencia de muestreo


Una posible implementacin en cdigo C del sumatorio anterior sera la siguiente.
input = input_left_sample();
delayed = buffer[i];
output = input + delayed;
output_left_sample(output);
buffer[i] = input + delayed*alpha;
Estableceremos dos retardos distintos para cada canal. Para el canal derecho se usar un retardo de
1 segundo y para el izquierdo de 2 segundos (esto implica tamao de bufferes distintos para cada
canal)

16/26

Curso 2014-2015
Filtrado en el dominio del tiempo.
Preste atencin que en cdigo presentado en este ejemplo solo se est filtrando un canal.
Sistema C6748
#include "L138_LCDK_aic3106_init.h"
#include "lp33.cof"

//filter coefficient file

float yn;
//filter output
float x[N];
//filter delay line
interrupt void interrupt4()
{
short i;
for (i=(N-1) ; i>0 ; i--)
//shuffle delay line contents
x[i] = x[i-1];
//I know it's inefficient
x[0] = (float)(input_left_sample()); //get new input into delay
line
yn = 0.0;
//initialise filter output
for (i=0 ; i<N ; i++)
//calculate filter output
yn += h[i]*x[i];
output_left_sample((short)(yn));
return;

//output to codec

}
void main()
{
for (i=0 ; i<N ; i++)
//init values
x[i] = 0;
L38_initialise_intr(FS_8000_HZ,ADC_GAIN_0DB,DAC_ATTEN_0DB,LCDK_MIC
_INPUT);
while(1); //infinite loop
}
En estos ejemplos la lnea de retardo se ha realizado mediante un bucle for, que es claramente
ineficiente.
Una ms eficiente implementacin es usando memmove
memmove(x+1,x,sizeof(float)*(N-1));
Finalmente, es posible usar las funciones de la librera DSPLIB, una librera altamente optimizada
en ensamblador.
La funcin base es la siguiente
void DSPF_sp_fir_gen(const float * restrict x, const float *
restrict h, float restrict r, int nh, int nr)
17/26

Curso 2014-2015
donde x son la muestras de entrada, h el filtro (los coeficientes deben estar en orden inverso), r es el
resultado, nh es el tamao de h y nr es el tamao de r, el tamao de las muestras de entrada debe ser
nr+nh1.
Esto implica que esta funcin trabaja con bloques de muestras en lugar de muestra a muestra como
hemos hecho hasta el momento
float x[NR+NH-1];
float h[NH]
float y[NR]
// necesario
#pragma DATA_ALIGN(x, sizeof(float))
#pragma DATA_ALIGN(h, sizeof(float))
#pragma DATA_ALIGN(y, sizeof(float))
void interrupt4()
{
x[cont+NH-1] = (float)(input_left_sample());
output_left_sample((short)(y[cont]));
cont ++;
if (cont > NR)
{
DSPF_sp_fir_gen(x,h,y, NH, NR);
cont = 0;
for (i = 0; i < NH;i++) x[i] = x[NH+i-1];
}
return;
}

Mltiples filtros en paralelo.


Es posible filtrar simultneamente varias bandas de frecuencia. Supongamos que tenemos dos
filtros FIR el primero de orden N1 y el segundo de orden N2, siendo N1>N2, que hace para obtener
el resultado.
float
float
float
float
float
float

x[N1]; // debe ser el mayor de los dos filtros.


h1[N1]; //funcin del filtro 1
h2[N2]; //funcin del filtro 2
y1; // resultado del filtro 1
y2; //
yn;

for (i=(N1-1) ; i>0 ; i--)


x[i] = x[i-1];

//Delay

18/26

Curso 2014-2015
x[0]
y1 =
y2 =
yn =

= (float)(input_left_sample()); //obtengo la nueva muestra


0.0;
//initialise filter output
0.0;
//initialise filter output
0.0;
//initialise filter output

for (i=0 ; i<N1; i++)


{
y1 += h1[i]*x[i];
if (i < N2)
y2 += h2[i]*x[i];
}
yn = (y1*gain1)+(y2*gain2);
output_left_sample((short)(yn));
gain1 y gain2 son dos parmetros que determinan la ganancia de cada una de las bandas. Es posible
usar un mdulo gel para cambiar de forma dinmica estos parmetros.

19/26

Curso 2014-2015
Prctica propuesta
A partir de los ejemplos anteriores cree un programa que sea capaz de actuar como ecualizador de 2
bandas (2 filtros en cascada) en punto fijo. Usando los DIPs 0 y 1 debe ser posible seleccionar que
filtro se activa.
Si est desarrollando en un sistema basado en el DSP C6748 ignore la configuracin de los LED ya
que el sistema no proporciona en este momento funciones para la modificacin de los leds.
Los filtros a usar sern un paso bajo hasta una frecuencia de corte de 2000 Hz y un paso alto con
una frecuencia de corte inferior de 1000Hz.
Para generar los filtros puede usar la herramienta de matlab FDATOOLS, disear el filtro y exportar
los coeficientes del filtro. Exporte los coeficientes tanto en formato int32 como en formato float y
genere un fichero h con cada uno de ellos. Ser necesario editar los ficheros de cabecera y cambiar
los tipos de los datos a float para el caso de los flotantes y a Int32 para el caso de los enteros.
Verifique que los filtros con ambos conjuntos de coeficientes funcionan igual, en caso negativo
justifique la razn.
A continuacin nada mas exportar los coeficientes enteros convierta los enteros de 32 bits a enteros
de 16 bits y compruebe en este caso el resultado. Justifique la razn de la diferencia entre operar
con enteros de 16 y enteros de 32
Para convertir los coeficientes a enteros de 32 es necesario quedarse con la parte mas significativa
para esto haremos la operacin de desplazamiento a la izquierda val >>=16;
Como mejora opcional se puede usar un mdulo gel para controlar la ganancia de cada filtro.

20/26

Curso 2014-2015
Filtrado en el dominio de la frecuencia
Para este ejemplo usaremos las funciones de librera
cfftr2_dit y icfftr2_dit
Estas funciones estn implementadas en ensamblador y est disponibles
Una cosa que hay que hay que tener en cuenta que la FFT debe ser calculada con un nmero par de
muestras y que hasta que no estn todas no es posible calcularlas. Esto implica dos cosas, la
necesidad de trabajar con varios bufferes, un buffer de proceso y un buffer donde se estn
almacenando las muestras, y el algoritmo debe cambiar uno por otro una vez que el buffer de estan
almacenando las muestras se ha llenado. As mimo, esto implica que el resultado se va a presentar
con un cierto retraso, este retraso ser mayor cuanto mayor sea el buffer
#define RADIX 2
#define DELTA (2*PI)/PTS
typedef struct Complex_tag {float real,imag;} COMPLEX;
// Importante, es preciso que los datos estn alineados para poder
pasar la
// informacin a funciones implementadas en ensamblador
//
#pragma DATA_ALIGN(W,sizeof(COMPLEX))
#pragma DATA_ALIGN(samples,sizeof(COMPLEX))
#pragma DATA_ALIGN(h,sizeof(COMPLEX))
COMPLEX W[PTS/RADIX] ;

//twiddle array

COMPLEX samples[PTS];
COMPLEX h[PTS];
COMPLEX bass[PTS], mid[PTS], treble[PTS];
short buffercount = 0;
samples

//buffer count for iobuffer

float iobuffer[PTS/2];

//primary input/output buffer

float overlap[PTS/2];

//intermediate result buffer

short i;

//index variable

short flag = 0;

//set to indicate iobuffer full

float a, b;

//variables for complex multiply

short NUMCOEFFS = sizeof(lpcoeff)/sizeof(float);


21/26

Curso 2014-2015
short iTwid[PTS/2] ;
float bass_gain = 1.0;

//initial gain values

float mid_gain = 0.0;

//change with GraphicEQ.gel

float treble_gain = 1.0;


interrupt void c_int11(void)

//ISR

{
output_left_sample((short)(iobuffer[buffercount]));
iobuffer[buffercount++] = (float)((short)input_left_sample());
// PRESTE ATENCIO, SOLO LA MITAD
if (buffercount >= PTS/2)
iobuffer

//for overlap-add method

//is half size of FFT used


buffercount = 0;
flag = 1;

}
}
main()
{
digitrev_index(iTwid, PTS/RADIX, RADIX);
for( i = 0; i < PTS/RADIX; i++ )
{
W[i].real = cos(DELTA*i);
W[i].imag = sin(DELTA*i);
}
bitrev(W, iTwid, PTS/RADIX);

//bit reverse W

for (i=0 ; i<PTS ; i++)


{
bass[i].real = 0.0;
bass[i].imag = 0.0;
mid[i].real = 0.0;
mid[i].imag = 0.0;
22/26

Curso 2014-2015
treble[i].real = 0.0;
treble[i].imag = 0.0;
}

for (i=0; i<NUMCOEFFS; i++)


filter

//same # of coeff for each

{
bass[i].real = lpcoeff[i];

//lowpass coeff

mid[i].real =

//bandpass coeff

bpcoeff[i];

treble[i].real = hpcoeff[i];

//highpass coef

}
//
//
//Esto es la gracia de la FFT puedo hacer la transformada de los
filtros al principio con lo cual no es necesario hacerlo mas, solo
es preciso hacer la transformada de los datos
cfftr2_dit(bass,W,PTS);

//transform each band

cfftr2_dit(mid,W,PTS);

//into frequency domain

cfftr2_dit(treble,W,PTS);

comm_intr();

//initialise DSK, codec, McBSP

while(1)

//frame processing infinite loop

{
while (flag == 0);
completo

//Espera que el buffer est

flag = 0;
for (i=0 ; i<PTS/2 ; i++)

//iobuffer into samples buffer

{
samples[i].real = iobuffer[i]; // copia los datos de
entrada a al buffer que se va a usar
iobuffer[i] = overlap[i];
overlaped
}
23/26

//los resultados estn en

Curso 2014-2015
for (i=0 ; i<PTS/2 ; i++)
{

//upper-half samples to overlap

overlap[i] = samples[i+PTS/2].real; // ATENCION OVERLAPING de


los datos de la parte superior
samples[i+PTS/2].real = 0.0; //zero-pad input from iobuffer
}
for (i=0 ; i<PTS ; i++)
samples[i].imag = 0.0;

//init samples buffer

cfftr2_dit(samples,W,PTS);
// Como se puede observar, ahora el bucle es menor, no como en
caso del dominio del tiempo donde tenemos tantos bucles como
filtros bucles
for (i=0 ; i<PTS ; i++)
{

//construct freq domain filter


//sum of bass,mid,treble coeffs

h[i].real = bass[i].real*bass_gain + mid[i].real*mid_gain


+ treble[i].real*treble_gain;
h[i].imag = bass[i].imag*bass_gain + mid[i].imag*mid_gain
+ treble[i].imag*treble_gain;
}
for (i=0; i<PTS; i++)
representation
{

//frequency-domain
//complex multiply samples by h

a = samples[i].real;
b = samples[i].imag;
samples[i].real = h[i].real*a - h[i].imag*b;
samples[i].imag = h[i].real*b + h[i].imag*a;
}
icfftr2_dif(samples,W,PTS); // transformada discreta
for (i=0 ; i<PTS ; i++)
samples[i].real /= PTS;
for (i=0 ; i<PTS/2 ; i++)

// ATENCION aade la primera mitad


24/26

Curso 2014-2015
overlap[i] += samples[i].real;
}
}

//end of infinite loop


//end of main()

25/26

Curso 2014-2015
Prctica propuesta
Usando las especificaciones de la prctica anterior realice ahora el filtrado usando la convolucin en
el dominio de la frecuencia. A fin de simplificar el sistema cree ahora los coeficientes en punto
flotante. Si lo desea puede usar la FFT implementada en cdigo C que se puede bajar del campus
virtual.
Verifique la velocidad de operacin y comprela con el filtro FIR en flotante

26/26